Files
zh_bh1750/wiki_ru.md
T
2026-10-18 06:31:28 +03:00

14 KiB
Raw Blame History

Описание компонента 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.

Внутреннее устройство

Структура 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.

Примеры использования

Базовый пример

#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);
}

Работа со статистикой ошибок

#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

#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).

// Приглушить 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.

Установка

cd ../your_project/components
git clone https://github.com/aZholtikov/zh_bh1750.git
#include "zh_bh1750.h"