diff --git a/wiki_ru.md b/wiki_ru.md new file mode 100644 index 0000000..329bc8e --- /dev/null +++ b/wiki_ru.md @@ -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" +```