14 KiB
Описание компонента zh_bh1750
Общие сведения
zh_bh1750 — это компонент ESP-IDF, реализующий драйвер цифрового датчика освещённости BH1750, подключаемого по шине I2C.
Компонент предоставляет простой API для инициализации, чтения освещённости в люксах и диагностики ошибок.
Основные возможности
- Интерфейс I2C — поддержка стандартного и быстрого режимов с частотой до 400 кГц.
- Два I2C-адреса —
0x23(ADDR на GND) и0x5C(ADDR на VCC). - Однократное измерение H-разрешения — шаг 1 лк без внешней калибровки.
- Автоматический пересчёт в люксы — из сырых данных по формуле
lux = raw / 1.2. - Проверка присутствия датчика — I2C-пробирование при инициализации.
- Сбор статистики ошибок — счётчик ошибок драйвера I2C со сбросом для диагностики.
- Несколько датчиков на одном устройстве — через мультиплексор 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() |
Обнуляет все счётчики в структуре статистики. |
Потокобезопасность
Компонент не использует механизмы синхронизации (мьютексы, критические секции, атомарные операции).
- Дескриптор датчика защищён только на уровне инициализации:
zh_bh1750_init()требует*handle == NULLи возвращаетESP_ERR_INVALID_STATEпри повторном вызове, что предотвращает двойную регистрацию устройства. - Глобальная статистика
_statsне имеет защиты — инкрементi2c_driver_errorиз разных задач может привести к гонке данных. - Чтение
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)
- Формируется
i2c_device_config_tс 7-битным адресом и заданной частотой. - Устройство добавляется на шину через
i2c_master_bus_add_device(). - Выполняется
i2c_master_probe()на адресе датчика. - Если пробирование не удалось, только что добавленное устройство снимается с шины (
i2c_master_bus_rm_device), и возвращаетсяESP_ERR_NOT_FOUND— «висящего» устройства на шине не остаётся.
Чтение освещённости (zh_bh1750_read)
- Отправка команды
_bh1750_read_command(0x20). - Ожидание
vTaskDelay(180 / portTICK_PERIOD_MS)— время преобразования для H-разрешения. - Приём 2 байт сырых данных.
- Пересчёт:
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 |
Ограничения
- Отсутствие потокобезопасности — компонент не использует мьютексы; одновременные вызовы
zh_bh1750_read()и работа со статистикой из разных задач приводят к гонке данных. - Глобальная статистика —
_statsобщая для всех экземпляров датчиков: счётчикi2c_driver_errorсуммирует ошибки всех устройств. - Два допустимых адреса — аппаратное ограничение BH1750: принимаются только
0x23и0x5C. - Частота I2C не выше 400 кГц — ограничение Fast Mode datasheet BH1750.
- Блокирующее чтение —
zh_bh1750_read()задерживает задачу примерно на 180 мс (vTaskDelay) ради преобразования H-разрешения. - Только H-разрешение — жёстко зашита команда
0x20(однократное измерение); режимы H-Res 2, L-Res и автоматического измерения не поддерживаются. - Предварительная инициализация шины —
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"