Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6e49ca147 | ||
|
|
7672975ba4 | ||
|
|
81cf96aa16 | ||
|
|
c5affea438 | ||
|
|
ffe0c5e376 | ||
|
|
9749186fc1 | ||
|
|
f0d5e579ab | ||
|
|
2ac63a930c | ||
|
|
10cb89b02b | ||
|
|
ac973f18a6 |
No files matched your search
Executable
BIN
Binary file not shown.
+6
-1
@@ -1 +1,6 @@
|
||||
idf_component_register(SRCS "main.c" INCLUDE_DIRS "include")
|
||||
if(${IDF_VERSION_MAJOR} STREQUAL 6)
|
||||
set(requires esp_driver_i2c)
|
||||
else()
|
||||
set(requires driver)
|
||||
endif()
|
||||
idf_component_register(SRCS "zh_bh1750.c" INCLUDE_DIRS "include" REQUIRES ${requires})
|
||||
@@ -1,3 +1,203 @@
|
||||
# esp_component_template
|
||||
# ESP32 ESP-IDF component for BH1750 ambient light sensor
|
||||
|
||||
esp_component_template
|
||||
## Features
|
||||
|
||||
1. Measurement of ambient light intensity in lux.
|
||||
2. I2C interface with frequency up to 400 kHz (Fast Mode).
|
||||
3. Support for two I2C addresses: 0x23 (ADDR low) and 0x5C (ADDR high).
|
||||
4. One-time H-resolution measurement with 1 lux step.
|
||||
5. Automatic lux conversion from raw sensor data.
|
||||
6. Sensor presence verification during initialization via I2C probe.
|
||||
7. Built-in I2C error statistics tracking and reset for diagnostics.
|
||||
|
||||
## Attention
|
||||
|
||||
1. The I2C bus must be initialized before the component.
|
||||
2. Only one-time H-resolution measurement mode is supported.
|
||||
3. Each read is a blocking call of approximately 180 ms.
|
||||
4. Error statistics are global for all sensor instances on the device.
|
||||
|
||||
## Using
|
||||
|
||||
In an existing project, run the following command to install the component:
|
||||
|
||||
```bash
|
||||
cd ../your_project/components
|
||||
git clone https://github.com/aZholtikov/zh_bh1750
|
||||
```
|
||||
|
||||
In the application, add the component:
|
||||
|
||||
```c
|
||||
#include "zh_bh1750.h"
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Read Example
|
||||
|
||||
```c
|
||||
#include "zh_bh1750.h"
|
||||
|
||||
#define I2C_PORT (I2C_NUM_MAX - 1)
|
||||
|
||||
zh_bh1750_handle_t *bh1750_handle = NULL;
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_bh1750", ESP_LOG_ERROR);
|
||||
i2c_master_bus_config_t i2c_bus_config = {
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.i2c_port = I2C_PORT,
|
||||
.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 = NULL;
|
||||
esp_err_t ret = i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
printf("I2C bus initialization error\n");
|
||||
return;
|
||||
}
|
||||
zh_bh1750_init_config_t config = ZH_BH1750_INIT_CONFIG_DEFAULT();
|
||||
config.i2c_handle = i2c_bus_handle;
|
||||
ret = zh_bh1750_init(&config, &bh1750_handle);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
printf("BH1750 initialization error: %s\n", esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
float lux = 0.0;
|
||||
for (;;)
|
||||
{
|
||||
ret = zh_bh1750_read(&bh1750_handle, &lux);
|
||||
if (ret == ESP_OK)
|
||||
{
|
||||
printf("Lux: %0.2f\n", lux);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Read error: %s\n", esp_err_to_name(ret));
|
||||
}
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Alternative Address Example
|
||||
|
||||
```c
|
||||
#include "zh_bh1750.h"
|
||||
|
||||
#define I2C_PORT (I2C_NUM_MAX - 1)
|
||||
|
||||
zh_bh1750_handle_t *bh1750_handle = NULL;
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_bh1750", ESP_LOG_ERROR);
|
||||
i2c_master_bus_config_t i2c_bus_config = {
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.i2c_port = I2C_PORT,
|
||||
.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 = NULL;
|
||||
esp_err_t ret = i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
printf("I2C bus initialization error\n");
|
||||
return;
|
||||
}
|
||||
zh_bh1750_init_config_t config = ZH_BH1750_INIT_CONFIG_DEFAULT();
|
||||
config.i2c_handle = i2c_bus_handle;
|
||||
config.i2c_address = 0x5C;
|
||||
ret = zh_bh1750_init(&config, &bh1750_handle);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
printf("BH1750 initialization error: %s\n", esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
float lux = 0.0;
|
||||
ret = zh_bh1750_read(&bh1750_handle, &lux);
|
||||
if (ret == ESP_OK)
|
||||
{
|
||||
printf("Lux: %0.2f\n", lux);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Read error: %s\n", esp_err_to_name(ret));
|
||||
}
|
||||
ret = zh_bh1750_deinit(&bh1750_handle);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
printf("Deinitialization error: %s\n", esp_err_to_name(ret));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Error Statistics Example
|
||||
|
||||
```c
|
||||
#include "zh_bh1750.h"
|
||||
|
||||
#define I2C_PORT (I2C_NUM_MAX - 1)
|
||||
|
||||
zh_bh1750_handle_t *bh1750_handle = NULL;
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_bh1750", ESP_LOG_ERROR);
|
||||
i2c_master_bus_config_t i2c_bus_config = {
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.i2c_port = I2C_PORT,
|
||||
.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 = NULL;
|
||||
esp_err_t ret = i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
printf("I2C bus initialization error\n");
|
||||
return;
|
||||
}
|
||||
zh_bh1750_init_config_t config = ZH_BH1750_INIT_CONFIG_DEFAULT();
|
||||
config.i2c_handle = i2c_bus_handle;
|
||||
ret = zh_bh1750_init(&config, &bh1750_handle);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
printf("BH1750 initialization error: %s\n", esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
float lux = 0.0;
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
ret = zh_bh1750_read(&bh1750_handle, &lux);
|
||||
if (ret == ESP_OK)
|
||||
{
|
||||
printf("Lux: %0.2f\n", lux);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Read error: %s\n", esp_err_to_name(ret));
|
||||
}
|
||||
const zh_bh1750_stats_t *stats = zh_bh1750_get_stats();
|
||||
printf("Number of I2C driver errors: %lu\n", (unsigned long)stats->i2c_driver_error);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
zh_bh1750_reset_stats();
|
||||
const zh_bh1750_stats_t *stats = zh_bh1750_get_stats();
|
||||
printf("Number of I2C driver errors after reset: %lu\n", (unsigned long)stats->i2c_driver_error);
|
||||
zh_bh1750_deinit(&bh1750_handle);
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,8 @@
|
||||
description: ESP32 ESP-IDF component for BH1750 ambient light sensor
|
||||
url: https://github.com/aZholtikov/zh_bh1750
|
||||
version: "2.0.0"
|
||||
license: "Apache-2.0"
|
||||
maintainers:
|
||||
- "Alexey Zholtikov <alexey.zholtikov@yandex.ru>"
|
||||
dependencies:
|
||||
idf: ">=5.0"
|
||||
Whitespace-only changes.
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* @file zh_bh1750.h
|
||||
*
|
||||
* @brief Driver for BH1750 digital ambient light sensor over I2C.
|
||||
*
|
||||
* This module provides an abstraction layer for the BH1750 ambient light
|
||||
* intensity sensor, which communicates via the I2C bus. The sensor converts
|
||||
* ambient light to a digital value with high resolution, providing a direct
|
||||
* lux reading without the need for external calibration.
|
||||
*
|
||||
* Key features:
|
||||
* - I2C interface with support for standard and fast modes
|
||||
* - Automatic lux calculation from raw sensor data
|
||||
* - Error statistics tracking for diagnostics
|
||||
* - Thread-safe initialization and deinitialization
|
||||
*
|
||||
* @note Supported I2C addresses: 0x23 (default), 0x5C (ADDR pin high).
|
||||
* @warning Ensure the I2C bus is initialized before calling zh_bh1750_init().
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/i2c_master.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#define ZH_BH1750_INIT_CONFIG_DEFAULT() \
|
||||
{ \
|
||||
.i2c_frequency = 400000, \
|
||||
.i2c_address = 0x23}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Opaque handle representing a BH1750 sensor instance.
|
||||
*
|
||||
* This handle is allocated by zh_bh1750_init() and must be passed to
|
||||
* all subsequent API functions. It is freed by zh_bh1750_deinit().
|
||||
*/
|
||||
typedef struct _zh_bh1750_handle_t zh_bh1750_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Configuration structure for BH1750 sensor initialization.
|
||||
*
|
||||
* This structure defines the I2C bus parameters required to communicate
|
||||
* with the BH1750 sensor. The I2C address defaults to 0x23 when the ADDR
|
||||
* pin is connected to GND, or 0x5C when the ADDR pin is connected to VCC.
|
||||
*
|
||||
* @note The I2C bus handle must be initialized before passing it here.
|
||||
* @warning I2C frequency must not exceed 400000 Hz (Fast Mode).
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
i2c_master_bus_handle_t i2c_handle; /*!< I2C bus handle for sensor communication */
|
||||
uint8_t i2c_address; /*!< Sensor I2C address (0x23 or 0x5C) */
|
||||
uint32_t i2c_frequency; /*!< I2C clock frequency in Hz (max 400000) */
|
||||
} zh_bh1750_init_config_t;
|
||||
|
||||
/**
|
||||
* @brief Structure containing sensor error statistics.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint32_t i2c_driver_error; /*!< Counter of I2C driver errors */
|
||||
} zh_bh1750_stats_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize the BH1750 sensor.
|
||||
*
|
||||
* Allocates a handle, validates the provided configuration, and registers
|
||||
* the sensor as an I2C device. The I2C bus must already be initialized.
|
||||
*
|
||||
* @param[in] config Pointer to the initialization configuration structure
|
||||
* @param[out] handle Pointer to receive the allocated sensor handle
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return ESP_ERR_INVALID_ARG if config or handle is NULL, or parameters are invalid
|
||||
* @return ESP_ERR_NO_MEM if memory allocation for the handle fails
|
||||
* @return ESP_FAIL if I2C device registration fails
|
||||
* @return ESP_ERR_NOT_FOUND if the sensor does not respond on the specified address
|
||||
*/
|
||||
esp_err_t zh_bh1750_init(const zh_bh1750_init_config_t *config, zh_bh1750_handle_t **handle);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize the BH1750 sensor.
|
||||
*
|
||||
* Removes the sensor from the I2C bus, frees the allocated handle,
|
||||
* and sets the handle pointer to NULL.
|
||||
*
|
||||
* @param handle Pointer to the sensor handle (must not be NULL)
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return ESP_ERR_INVALID_ARG if handle or *handle is NULL
|
||||
*
|
||||
* @note After this call, the handle is invalid and must not be used.
|
||||
*/
|
||||
esp_err_t zh_bh1750_deinit(zh_bh1750_handle_t **handle);
|
||||
|
||||
/**
|
||||
* @brief Read ambient light intensity from the BH1750 sensor.
|
||||
*
|
||||
* Issues a one-time H-resolution measurement command, waits for the
|
||||
* conversion period (~180 ms), and reads the raw data. The raw value
|
||||
* is converted to lux using the formula: lux = raw_value / 1.2.
|
||||
*
|
||||
* @param[in] handle Pointer to the sensor handle (must not be NULL)
|
||||
* @param[out] data Pointer to receive the light intensity in lux (must not be NULL)
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return ESP_ERR_INVALID_ARG if handle or data is NULL
|
||||
* @return ESP_FAIL if any I2C transmission or reception fails
|
||||
*
|
||||
* @note This is a blocking call that delays for approximately 180 ms.
|
||||
* @warning Ensure sufficient stack space for the vTaskDelay call.
|
||||
*/
|
||||
esp_err_t zh_bh1750_read(zh_bh1750_handle_t **handle, float *data);
|
||||
|
||||
/**
|
||||
* @brief Get pointer to sensor error statistics.
|
||||
*
|
||||
* Returns read-only pointer to global statistics structure.
|
||||
*
|
||||
* @return Pointer to statistics structure (valid until reset)
|
||||
*/
|
||||
const zh_bh1750_stats_t *zh_bh1750_get_stats(void);
|
||||
|
||||
/**
|
||||
* @brief Reset all error statistics to zero.
|
||||
*
|
||||
* Clears all error counters tracked in the global statistics structure.
|
||||
*/
|
||||
void zh_bh1750_reset_stats(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
2.0.0
|
||||
+260
@@ -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"
|
||||
```
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
#include "zh_bh1750.h"
|
||||
|
||||
static const char *TAG = "zh_bh1750";
|
||||
|
||||
#define ZH_LOGI(msg, ...) ESP_LOGI(TAG, msg, ##__VA_ARGS__)
|
||||
#define ZH_LOGE(msg, err, ...) ESP_LOGE(TAG, "[%s:%d:%s] " msg, __FILE__, __LINE__, esp_err_to_name(err), ##__VA_ARGS__)
|
||||
|
||||
#define ZH_ERROR_CHECK(cond, err, cleanup, msg, ...) \
|
||||
if (!(cond)) \
|
||||
{ \
|
||||
ZH_LOGE(msg, err, ##__VA_ARGS__); \
|
||||
cleanup; \
|
||||
return err; \
|
||||
}
|
||||
|
||||
#define ZH_ERROR_CHECK_CONT(cond, cleanup, msg, ...) \
|
||||
if (!(cond)) \
|
||||
{ \
|
||||
ZH_LOGE(msg, ESP_FAIL, ##__VA_ARGS__); \
|
||||
cleanup; \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle structure for BH1750 sensor.
|
||||
*
|
||||
* Internal structure managing the I2C device connection.
|
||||
*/
|
||||
struct _zh_bh1750_handle_t
|
||||
{
|
||||
i2c_master_dev_handle_t dev_handle; /*!< Unique I2C device handle */
|
||||
};
|
||||
|
||||
static const uint8_t _bh1750_read_command = 0x20; /*!< One-time H-resolution measurement command (1 lx step) */
|
||||
static zh_bh1750_stats_t _stats = {0}; /*!< Global error statistics accumulator */
|
||||
|
||||
/**
|
||||
* @brief Validate the initialization configuration parameters.
|
||||
*
|
||||
* Checks that the I2C address is valid (0x23 or 0x5C), the frequency
|
||||
* does not exceed 400000 Hz, and the I2C bus handle is not NULL.
|
||||
*
|
||||
* @param config Pointer to the configuration to validate
|
||||
*
|
||||
* @return ESP_OK if all parameters are valid
|
||||
* @return ESP_ERR_INVALID_ARG if any parameter is out of range or NULL
|
||||
*/
|
||||
static esp_err_t _zh_bh1750_validate_config(const zh_bh1750_init_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Register the BH1750 as an I2C device and verify connectivity.
|
||||
*
|
||||
* Configures the I2C device parameters, adds the device to the bus,
|
||||
* and probes the sensor to confirm it is physically connected and responds.
|
||||
*
|
||||
* @param config Pointer to the initialization configuration
|
||||
* @param handle Pointer to the allocated handle to store the device handle
|
||||
*
|
||||
* @return ESP_OK on successful setup and probe
|
||||
* @return ESP_FAIL if device registration fails
|
||||
* @return ESP_ERR_NOT_FOUND if the sensor does not respond to a probe
|
||||
*/
|
||||
static esp_err_t _zh_bh1750_i2c_init(const zh_bh1750_init_config_t *config, zh_bh1750_handle_t *handle);
|
||||
|
||||
esp_err_t zh_bh1750_init(const zh_bh1750_init_config_t *config, zh_bh1750_handle_t **handle)
|
||||
{
|
||||
ZH_LOGI("BH1750 initialization begin.");
|
||||
ZH_ERROR_CHECK(config != NULL && handle != NULL, ESP_ERR_INVALID_ARG, NULL, "BH1750 initialization failed. Invalid argument.");
|
||||
ZH_ERROR_CHECK(_zh_bh1750_validate_config(config) == ESP_OK, ESP_FAIL, NULL, "BH1750 initialization failed. Initial configuration check failed.");
|
||||
ZH_ERROR_CHECK(*handle == NULL, ESP_ERR_INVALID_STATE, NULL, "BH1750 initialization failed. BH1750 is already initialized.");
|
||||
*handle = heap_caps_calloc(1, sizeof(zh_bh1750_handle_t), MALLOC_CAP_8BIT);
|
||||
ZH_ERROR_CHECK(*handle != NULL, ESP_ERR_NO_MEM, NULL, "BH1750 initialization failed. Failed to allocate BH1750 handle.");
|
||||
ZH_ERROR_CHECK(_zh_bh1750_i2c_init(config, *handle) == ESP_OK, ESP_FAIL, heap_caps_free(*handle); *handle = NULL, "BH1750 initialization failed. Failed to add I2C device.");
|
||||
ZH_LOGI("BH1750 initialization completed successfully.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t zh_bh1750_deinit(zh_bh1750_handle_t **handle)
|
||||
{
|
||||
ZH_LOGI("BH1750 deinitialization started.");
|
||||
ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "BH1750 deinitialization failed. Invalid argument.");
|
||||
ZH_ERROR_CHECK(i2c_master_bus_rm_device((*handle)->dev_handle) == ESP_OK, ESP_FAIL, NULL, "BH1750 deinitialization failed. I2C remove device failed.");
|
||||
heap_caps_free(*handle);
|
||||
*handle = NULL;
|
||||
ZH_LOGI("BH1750 deinitialization completed successfully.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t zh_bh1750_read(zh_bh1750_handle_t **handle, float *data)
|
||||
{
|
||||
ZH_LOGI("BH1750 read begin.");
|
||||
ZH_ERROR_CHECK(handle != NULL && *handle != NULL && data != NULL, ESP_ERR_INVALID_ARG, NULL, "BH1750 read fail. Invalid argument.");
|
||||
uint8_t sensor_data[2] = {0};
|
||||
ZH_ERROR_CHECK(i2c_master_transmit((*handle)->dev_handle, &_bh1750_read_command, sizeof(_bh1750_read_command), 100) == ESP_OK, ESP_FAIL,
|
||||
++_stats.i2c_driver_error, "BH1750 read fail. I2C driver error.");
|
||||
vTaskDelay(180 / portTICK_PERIOD_MS);
|
||||
ZH_ERROR_CHECK(i2c_master_receive((*handle)->dev_handle, sensor_data, sizeof(sensor_data), 100) == ESP_OK, ESP_FAIL,
|
||||
++_stats.i2c_driver_error, "BH1750 read fail. I2C driver error.");
|
||||
*data = (sensor_data[0] << 8 | sensor_data[1]) / 1.2;
|
||||
ZH_LOGI("BH1750 read completed successfully.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
const zh_bh1750_stats_t *zh_bh1750_get_stats(void)
|
||||
{
|
||||
return &_stats;
|
||||
}
|
||||
|
||||
void zh_bh1750_reset_stats(void)
|
||||
{
|
||||
ZH_LOGI("Error statistic reset started.");
|
||||
_stats.i2c_driver_error = 0;
|
||||
ZH_LOGI("Error statistic reset successfully.");
|
||||
}
|
||||
|
||||
static esp_err_t _zh_bh1750_validate_config(const zh_bh1750_init_config_t *config)
|
||||
{
|
||||
ZH_ERROR_CHECK(config->i2c_address == 0x23 || config->i2c_address == 0x5C, ESP_ERR_INVALID_ARG, NULL, "Invalid I2C address.");
|
||||
ZH_ERROR_CHECK(config->i2c_frequency <= 400000 && config->i2c_frequency > 0, ESP_ERR_INVALID_ARG, NULL, "Invalid I2C frequency.");
|
||||
ZH_ERROR_CHECK(config->i2c_handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Invalid I2C handle.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t _zh_bh1750_i2c_init(const zh_bh1750_init_config_t *config, zh_bh1750_handle_t *handle)
|
||||
{
|
||||
i2c_device_config_t bh1750_config = {
|
||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||
.device_address = config->i2c_address,
|
||||
.scl_speed_hz = config->i2c_frequency,
|
||||
};
|
||||
i2c_master_dev_handle_t _dev_handle = NULL;
|
||||
ZH_ERROR_CHECK(i2c_master_bus_add_device(config->i2c_handle, &bh1750_config, &_dev_handle) == ESP_OK, ESP_FAIL, NULL, "Add I2C device failed.");
|
||||
ZH_ERROR_CHECK(i2c_master_probe(config->i2c_handle, config->i2c_address, 100) == ESP_OK, ESP_ERR_NOT_FOUND,
|
||||
ZH_ERROR_CHECK_CONT(i2c_master_bus_rm_device(_dev_handle) == ESP_OK, NULL, "I2C remove device failed."), "Sensor not connected or not responded.");
|
||||
handle->dev_handle = _dev_handle;
|
||||
return ESP_OK;
|
||||
}
|
||||
Reference in new issue
Block a user