doc: added wiki

This commit is contained in:
aZholtikov committed 2026-10-18 06:31:28 +03:00
1 parent 7672975ba4
commit b6e49ca147
1 file changed
+260
+260
View File
@@ -0,0 +1,260 @@
# Описание компонента zh_bh1750
## Общие сведения
**zh_bh1750** — это компонент ESP-IDF, реализующий драйвер цифрового датчика освещённости BH1750, подключаемого по шине I2C.
Компонент предоставляет простой API для инициализации, чтения освещённости в люксах и диагностики ошибок.
## Основные возможности
1. **Интерфейс I2C** — поддержка стандартного и быстрого режимов с частотой до 400 кГц.
2. **Два I2C-адреса** — `0x23` (ADDR на GND) и `0x5C` (ADDR на VCC).
3. **Однократное измерение H-разрешения** — шаг 1 лк без внешней калибровки.
4. **Автоматический пересчёт в люксы** — из сырых данных по формуле `lux = raw / 1.2`.
5. **Проверка присутствия датчика** — I2C-пробирование при инициализации.
6. **Сбор статистики ошибок** — счётчик ошибок драйвера I2C со сбросом для диагностики.
7. **Несколько датчиков на одном устройстве** — через мультиплексор [zh_pca9548a](https://github.com/aZholtikov/zh_pca9548a).
---
## Внутреннее устройство
### Структура `zh_bh1750_handle_t`
Непрозрачная структура, описывающая экземпляр датчика. Объявлена как `struct _zh_bh1750_handle_t`, доступна пользователю только через указатель.
| Поле | Тип | Описание |
|------|-----|----------|
| `dev_handle` | `i2c_master_dev_handle_t` | Уникальный дескриптор I2C-устройства, полученный при регистрации на шине |
### Структура `zh_bh1750_init_config_t`
Параметры инициализации, передаваемые пользователем.
| Поле | Тип | Описание |
| ------ | ----- | ---------- |
| `i2c_handle` | `i2c_master_bus_handle_t` | Дескриптор уже инициализированной I2C-шины |
| `i2c_address` | `uint8_t` | I2C-адрес датчика (`0x23` или `0x5C`) |
| `i2c_frequency` | `uint32_t` | Частота шины I2C в герцах (до 400000) |
Для подстановки значений по умолчанию (`400000` Гц, `0x23`) служит макрос `ZH_BH1750_INIT_CONFIG_DEFAULT()`.
### Структура `zh_bh1750_stats_t`
Глобальный накопитель диагностической статистики.
| Поле | Тип | Описание |
|------|-----|----------|
| `i2c_driver_error` | `uint32_t` | Счётчик ошибок передачи/приёма драйвера I2C |
### Управление памятью
- Дескриптор датчика выделяется одной операцией `heap_caps_calloc()` с флагом `MALLOC_CAP_8BIT` и обнуляется. Это гарантирует, что при частичной инициализации все поля равны нулю.
- При любой ошибке после выделения памяти дескриптор освобождается тем же `heap_caps_free()`, а пользовательский указатель обнуляется — утечка исключена.
- `zh_bh1750_deinit()` снимает устройство с шины и освобождает дескриптор.
- Статистика `_stats` — статическая глобальная переменная уровня модуля, не требует аллокации и живёт всё время работы программы.
### Вспомогательные данные и макросы
- `_bh1750_read_command` (`uint8_t` = `0x20`) — команда однократного измерения H-разрешения, отправляемая перед каждым чтением.
- `_stats` — глобальный экземпляр `zh_bh1750_stats_t`, накапливающий ошибки I2C-драйвера.
- Макросы `ZH_ERROR_CHECK` / `ZH_ERROR_CHECK_CONT` — инкапсулируют проверку условия, логирование ошибки с именем функции и строкой, выполнение очистки и возврат кода ошибки.
---
## API (интерфейс)
### Инициализация и освобождение
| Функция | Описание |
|---------|----------|
| `zh_bh1750_init()` | Валидирует конфигурацию, выделяет дескриптор, регистрирует датчик на I2C-шине и проверяет его доступность пробированием. Требует `*handle == NULL`. |
| `zh_bh1750_deinit()` | Удаляет устройство с I2C-шины, освобождает дескриптор и устанавливает пользовательский указатель в `NULL`. |
### Чтение данных
| Функция | Описание |
|---------|----------|
| `zh_bh1750_read()` | Отправляет команду однократного измерения, ожидает ~180 мс, считывает 2 байта и возвращает освещённость в люксах. Блокирующий вызов. |
### Получение информации и сброс статистики
| Функция | Описание |
|---------|----------|
| `zh_bh1750_get_stats()` | Возвращает read-only указатель на глобальную структуру статистики ошибок. |
| `zh_bh1750_reset_stats()` | Обнуляет все счётчики в структуре статистики. |
---
## Потокобезопасность
Компонент **не** использует механизмы синхронизации (мьютексы, критические секции, атомарные операции).
1. **Дескриптор датчика** защищён только на уровне инициализации: `zh_bh1750_init()` требует `*handle == NULL` и возвращает `ESP_ERR_INVALID_STATE` при повторном вызове, что предотвращает двойную регистрацию устройства.
2. **Глобальная статистика** `_stats` не имеет защиты — инкремент `i2c_driver_error` из разных задач может привести к гонке данных.
3. **Чтение** `zh_bh1750_read()` не атомарно: одновременные вызовы из разных задач для одного дескриптора недопустимы.
Подробности — в разделе «Ограничения».
---
## Алгоритмы
### Валидация конфигурации (`_zh_bh1750_validate_config`)
Проверяются три условия, при нарушении любого возвращается `ESP_ERR_INVALID_ARG`:
- `i2c_address` равен `0x23` или `0x5C`;
- `0 < i2c_frequency <= 400000`;
- `i2c_handle != NULL`.
### Регистрация устройства и пробирование (`_zh_bh1750_i2c_init`)
1. Формируется `i2c_device_config_t` с 7-битным адресом и заданной частотой.
2. Устройство добавляется на шину через `i2c_master_bus_add_device()`.
3. Выполняется `i2c_master_probe()` на адресе датчика.
4. Если пробирование не удалось, только что добавленное устройство снимается с шины (`i2c_master_bus_rm_device`), и возвращается `ESP_ERR_NOT_FOUND` — «висящего» устройства на шине не остаётся.
### Чтение освещённости (`zh_bh1750_read`)
1. Отправка команды `_bh1750_read_command` (`0x20`).
2. Ожидание `vTaskDelay(180 / portTICK_PERIOD_MS)` — время преобразования для H-разрешения.
3. Приём 2 байт сырых данных.
4. Пересчёт: `lux = (data[0] << 8 | data[1]) / 1.2`.
---
## Примеры использования
### Базовый пример
```c
#include "zh_bh1750.h"
zh_bh1750_handle_t *bh1750_handle = NULL;
void app_main(void)
{
// Инициализируем I2C-шину
i2c_master_bus_config_t i2c_bus_config = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = I2C_NUM_MAX - 1,
.scl_io_num = GPIO_NUM_22,
.sda_io_num = GPIO_NUM_21,
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true,
};
i2c_master_bus_handle_t i2c_bus_handle;
i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
// Инициализируем датчик конфигурацией по умолчанию
zh_bh1750_init_config_t config = ZH_BH1750_INIT_CONFIG_DEFAULT();
config.i2c_handle = i2c_bus_handle;
zh_bh1750_init(&config, &bh1750_handle);
// Читаем освещённость
float lux = 0.0;
zh_bh1750_read(&bh1750_handle, &lux);
printf("Lux %0.2f\n", lux);
// Освобождаем ресурсы
zh_bh1750_deinit(&bh1750_handle);
}
```
### Работа со статистикой ошибок
```c
#include "zh_bh1750.h"
void check_sensor_health(zh_bh1750_handle_t **handle)
{
float lux = 0.0;
if (zh_bh1750_read(handle, &lux) != ESP_OK) {
// Считываем накопленную статистику сбоев I2C
const zh_bh1750_stats_t *stats = zh_bh1750_get_stats();
printf("I2C driver errors: %lu\n", (unsigned long)stats->i2c_driver_error);
}
// Сбрасываем счётчики перед новым циклом измерений
zh_bh1750_reset_stats();
}
```
### Подключение датчика с адресом 0x5C
```c
#include "zh_bh1750.h"
void init_addr_high(i2c_master_bus_handle_t bus, zh_bh1750_handle_t **handle)
{
// Датчик с ADDR на VCC использует адрес 0x5C
zh_bh1750_init_config_t config = {
.i2c_handle = bus,
.i2c_address = 0x5C,
.i2c_frequency = 100000, // Стандартный режим I2C
};
zh_bh1750_init(&config, handle);
// ... работа с датчиком ...
zh_bh1750_deinit(handle);
}
```
---
## Логирование
Компонент использует ESP-IDF логирование с тегом `"zh_bh1750"`.
Уровень по умолчанию позволяет видеть ход инициализации и чтения (`ESP_LOGI`), а также ошибки с указанием файла, строки и имени функции ошибки (`ESP_LOGE`).
```c
// Приглушить informational-сообщения, оставить только ошибки
esp_log_level_set("zh_bh1750", ESP_LOG_ERROR);
```
---
## Ошибки
| Код | Описание |
| ----- | ---------- |
| `ESP_OK` | Операция выполнена успешно |
| `ESP_ERR_INVALID_ARG` | Неверный аргумент: NULL-указатель, недопустимый адрес/частота, неинициализированная шина |
| `ESP_ERR_INVALID_STATE` | Датчик уже инициализирован (`*handle != NULL` при вызове `zh_bh1750_init()`) |
| `ESP_ERR_NO_MEM` | Не удалось выделить память под дескриптор |
| `ESP_ERR_NOT_FOUND` | Датчик не отвечает на пробировании I2C (не подключён или неверный адрес) |
| `ESP_FAIL` | Ошибка регистрации устройства на шине либо сбой передачи/приёма I2C |
---
## Ограничения
1. **Отсутствие потокобезопасности** — компонент не использует мьютексы; одновременные вызовы `zh_bh1750_read()` и работа со статистикой из разных задач приводят к гонке данных.
2. **Глобальная статистика** — `_stats` общая для всех экземпляров датчиков: счётчик `i2c_driver_error` суммирует ошибки всех устройств.
3. **Два допустимых адреса** — аппаратное ограничение BH1750: принимаются только `0x23` и `0x5C`.
4. **Частота I2C не выше 400 кГц** — ограничение Fast Mode datasheet BH1750.
5. **Блокирующее чтение** — `zh_bh1750_read()` задерживает задачу примерно на 180 мс (`vTaskDelay`) ради преобразования H-разрешения.
6. **Только H-разрешение** — жёстко зашита команда `0x20` (однократное измерение); режимы H-Res 2, L-Res и автоматического измерения не поддерживаются.
7. **Предварительная инициализация шины** — `i2c_handle` в конфигурации должен быть получен через `i2c_new_master_bus()` до вызова `zh_bh1750_init()`.
---
## Требования для сборки
- ESP-IDF версии **5.0** и выше.
- Зависимость от компонента I2C-драйвера: `driver` (ESP-IDF 5.x) либо `esp_driver_i2c` (ESP-IDF 6.x) — выбирается автоматически в `CMakeLists.txt`.
- FreeRTOS (входит в состав ESP-IDF) — используется `vTaskDelay`.
---
## Установка
```bash
cd ../your_project/components
git clone https://github.com/aZholtikov/zh_bh1750.git
```
```c
#include "zh_bh1750.h"
```