feat!: main structure of handle changed to double pointer

This commit is contained in:
aZholtikov committed 2026-09-06 19:49:26 +03:00
1 parent 9749186fc1
commit ffe0c5e376
5 files changed
+143 -129

No files matched your search

+11 -75
View File
@@ -1,25 +1,22 @@
# ESP32 ESP-IDF component for BH1750 ambient light sensor # ESP32 ESP-IDF component for BH1750 ambient light sensor
## Wiki
[EN](WIKI_EN.md) | [RU](WIKI_RU.md)
## Tested on
1. [ESP32 ESP-IDF v6.0.1](https://docs.espressif.com/projects/esp-idf/en/v6.0.1/esp32/index.html)
## SAST Tools ## SAST Tools
[PVS-Studio](https://pvs-studio.com/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code. [PVS-Studio](https://pvs-studio.com/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code.
## Features ## Features
1. Recorded illuminance values from 1 to 65535 lux. 1. I2C interface with configurable frequency up to 400 kHz (Fast Mode).
2. Support some sensors on one device with [zh_pca9548a](https://github.com/aZholtikov/zh_pca9548a). 2. Support for two I2C addresses: 0x23 (ADDR low) and 0x5C (ADDR high).
3. One-time H-resolution measurement with 1 lux step.
4. Built-in error statistics tracking and reset for diagnostics.
5. Automatic lux conversion from raw sensor data.
6. Sensor presence verification during initialization via I2C probe.
7. Support multiple sensors on one device with [zh_pca9548a](https://github.com/aZholtikov/zh_pca9548a).
## Attention ## Note
For correct operation, please enable the following settings in the menuconfig: Enable the following settings in menuconfig:
```text ```text
I2C_ISR_IRAM_SAFE I2C_ISR_IRAM_SAFE
@@ -30,7 +27,7 @@ I2C_MASTER_ISR_HANDLER_IN_IRAM
In an existing project, run the following command to install the component: In an existing project, run the following command to install the component:
```text ```bash
cd ../your_project/components cd ../your_project/components
git clone https://github.com/aZholtikov/zh_bh1750.git git clone https://github.com/aZholtikov/zh_bh1750.git
``` ```
@@ -43,14 +40,12 @@ In the application, add the component:
## Example ## Example
Reading the sensor:
```c ```c
#include "zh_bh1750.h" #include "zh_bh1750.h"
#define I2C_PORT (I2C_NUM_MAX - 1) #define I2C_PORT (I2C_NUM_MAX - 1)
zh_bh1750_handle_t bh1750_handle = {0}; zh_bh1750_handle_t *bh1750_handle = NULL;
void app_main(void) void app_main(void)
{ {
@@ -79,62 +74,3 @@ void app_main(void)
} }
} }
``` ```
3 sensor on one device:
```c
#include "zh_pca9548a.h"
#include "zh_bh1750.h"
#define I2C_PORT (I2C_NUM_MAX - 1)
zh_pca9548a_handle_t pca9548a_handle = {0};
zh_bh1750_handle_t bh1750_handle_chan_0 = {0};
zh_bh1750_handle_t bh1750_handle_chan_1 = {0};
zh_bh1750_handle_t bh1750_handle_chan_2 = {0};
void app_main(void)
{
esp_log_level_set("zh_pca9548a", ESP_LOG_ERROR);
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;
i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
zh_pca9548a_init_config_t config = ZH_PCA9548A_INIT_CONFIG_DEFAULT();
config.i2c_handle = i2c_bus_handle;
config.i2c_address = 0x70;
zh_pca9548a_init(&config, &pca9548a_handle);
zh_bh1750_init_config_t bh1750_config = ZH_BH1750_INIT_CONFIG_DEFAULT();
bh1750_config.i2c_handle = i2c_bus_handle;
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_0);
zh_bh1750_init(&bh1750_config, &bh1750_handle_chan_0);
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_1);
zh_bh1750_init(&bh1750_config, &bh1750_handle_chan_1);
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_2);
zh_bh1750_init(&bh1750_config, &bh1750_handle_chan_2);
float lux = 0.0;
for (;;)
{
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_0);
zh_bh1750_read(&bh1750_handle_chan_0, &lux);
printf("Sensor 1. Lux %0.2f\n", lux);
lux = 0.0;
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_1);
zh_bh1750_read(&bh1750_handle_chan_1, &lux);
printf("Sensor 2. Lux %0.2f\n", lux);
lux = 0.0;
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_2);
zh_bh1750_read(&bh1750_handle_chan_2, &lux);
printf("Sensor 3. Lux %0.2f\n", lux);
lux = 0.0;
vTaskDelay(5000 / portTICK_PERIOD_MS);
}
}
```
+1 -1
View File
@@ -1,6 +1,6 @@
description: ESP32 ESP-IDF component for BH1750 ambient light sensor description: ESP32 ESP-IDF component for BH1750 ambient light sensor
url: https://github.com/aZholtikov/zh_bh1750 url: https://github.com/aZholtikov/zh_bh1750
version: "1.0.0" version: "2.0.0"
license: "Apache-2.0" license: "Apache-2.0"
maintainers: maintainers:
- "Alexey Zholtikov <alexey.zholtikov@yandex.ru>" - "Alexey Zholtikov <alexey.zholtikov@yandex.ru>"
+79 -37
View File
@@ -1,5 +1,21 @@
/** /**
* @file zh_bh1750.h * @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 #pragma once
@@ -20,76 +36,102 @@ extern "C"
#endif #endif
/** /**
* @brief Structure for initial initialization of BH1750 sensor. * @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 typedef struct
{ {
i2c_master_bus_handle_t i2c_handle; /*!< Unique I2C bus handle. */ i2c_master_bus_handle_t i2c_handle; /*!< I2C bus handle for sensor communication */
uint8_t i2c_address; /*!< Sensor I2C address. */ uint8_t i2c_address; /*!< Sensor I2C address (0x23 or 0x5C) */
uint32_t i2c_frequency; /*!< Sensor I2C frequency. */ uint32_t i2c_frequency; /*!< I2C clock frequency in Hz (max 400000) */
} zh_bh1750_init_config_t; } zh_bh1750_init_config_t;
/** /**
* @brief BH1750 sensor handle. * @brief Structure containing sensor error statistics.
*/ */
typedef struct typedef struct
{ {
bool is_initialized; /*!< Sensor initialization flag. */ uint32_t i2c_driver_error; /*!< Counter of I2C driver errors */
i2c_master_dev_handle_t dev_handle; /*!< Unique I2C device handle. */
} zh_bh1750_handle_t;
/**
* @brief Structure for error statistics storage.
*/
typedef struct
{
uint32_t i2c_driver_error; /*!< Number of i2c driver error. */
} zh_bh1750_stats_t; } zh_bh1750_stats_t;
/** /**
* @brief Initialize BH1750 sensor. * @brief Initialize the BH1750 sensor.
* *
* @param[in] config Pointer to BH1750 initialized configuration structure. Can point to a temporary variable. * Allocates a handle, validates the provided configuration, and registers
* @param[out] handle Pointer to unique BH1750 handle. * the sensor as an I2C device. The I2C bus must already be initialized.
* *
* @attention I2C driver must be initialized first. * @param[in] config Pointer to the initialization configuration structure (must not be NULL)
* @param[out] handle Pointer to receive the allocated sensor handle (must be NULL)
* *
* @note Before initialize the sensor recommend initialize zh_bh1750_init_config_t structure with default values. * @return ESP_OK on success
* * @return ESP_ERR_INVALID_ARG if config or handle is NULL, or parameters are invalid
* @code zh_bh1750_init_config_t config = ZH_BH1750_INIT_CONFIG_DEFAULT() @endcode * @return ESP_ERR_NO_MEM if memory allocation for the handle fails
* * @return ESP_FAIL if I2C device registration fails
* @return ESP_OK if success or an error code otherwise. * @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); esp_err_t zh_bh1750_init(const zh_bh1750_init_config_t *config, zh_bh1750_handle_t **handle);
/** /**
* @brief Deinitialize PCF8574 expander. * @brief Deinitialize the BH1750 sensor.
* *
* @param[in] handle Pointer to unique BH1750 handle. * Removes the sensor from the I2C bus, frees the allocated handle,
* and sets the handle pointer to NULL.
* *
* @return ESP_OK if success or an error code otherwise. * @param[in,out] handle Pointer to the sensor handle to deinitialize (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); esp_err_t zh_bh1750_deinit(zh_bh1750_handle_t **handle);
/** /**
* @brief Read BH1750 sensor. * @brief Read ambient light intensity from the BH1750 sensor.
* *
* @param[in] handle Pointer to unique BH1750 handle. * Issues a one-time H-resolution measurement command, waits for the
* @param[out] data Pointer for reading data. * conversion period (~180 ms), and reads the raw data. The raw value
* is converted to lux using the formula: lux = raw_value / 1.2.
* *
* @return ESP_OK if success or an error code otherwise. * @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); esp_err_t zh_bh1750_read(zh_bh1750_handle_t **handle, float *data);
/** /**
* @brief Get error statistics. * @brief Get pointer to sensor error statistics.
* *
* @return Pointer to the statistics structure. * 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); const zh_bh1750_stats_t *zh_bh1750_get_stats(void);
/** /**
* @brief Reset error statistics. * @brief Reset all error statistics to zero.
*
* Clears all error counters tracked in the global statistics structure.
*/ */
void zh_bh1750_reset_stats(void); void zh_bh1750_reset_stats(void);
+1 -1
View File
@@ -1 +1 @@
1.0.0 2.0.0
+51 -15
View File
@@ -13,44 +13,80 @@ static const char *TAG = "zh_bh1750";
return err; \ return err; \
} }
static const uint8_t _bh1750_read_command = 0x20; // Measurement at 1 lx resolution. /**
static zh_bh1750_stats_t _stats = {0}; * @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); 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); 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) esp_err_t zh_bh1750_init(const zh_bh1750_init_config_t *config, zh_bh1750_handle_t **handle) // -V2008
{ {
ZH_LOGI("BH1750 initialization begin."); 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(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(_zh_bh1750_validate_config(config) == ESP_OK, ESP_FAIL, NULL, "BH1750 initialization failed. Initial configuration check failed.");
ZH_ERROR_CHECK(_zh_bh1750_i2c_init(config, handle) == ESP_OK, ESP_FAIL, NULL, "BH1750 initialization failed. Failed to add I2C device."); ZH_ERROR_CHECK(*handle == NULL, ESP_ERR_INVALID_STATE, NULL, "BH1750 initialization failed. BH1750 is already initialized.");
handle->is_initialized = true; *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."); ZH_LOGI("BH1750 initialization completed successfully.");
return ESP_OK; return ESP_OK;
} }
esp_err_t zh_bh1750_deinit(zh_bh1750_handle_t *handle) esp_err_t zh_bh1750_deinit(zh_bh1750_handle_t **handle)
{ {
ZH_LOGI("BH1750 deinitialization started."); ZH_LOGI("BH1750 deinitialization started.");
ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "BH1750 deinitialization failed. Invalid argument."); ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "BH1750 deinitialization failed. Invalid argument.");
ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "BH1750 deinitialization failed. BH1750 not initialized."); ZH_ERROR_CHECK(i2c_master_bus_rm_device((*handle)->dev_handle) == ESP_OK, ESP_FAIL, NULL, "BH1750 deinitialization failed. I2C remove device failed.");
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->is_initialized = false; *handle = NULL;
ZH_LOGI("BH1750 deinitialization completed successfully."); ZH_LOGI("BH1750 deinitialization completed successfully.");
return ESP_OK; return ESP_OK;
} }
esp_err_t zh_bh1750_read(zh_bh1750_handle_t *handle, float *data) // -V2008 esp_err_t zh_bh1750_read(zh_bh1750_handle_t **handle, float *data) // -V2008
{ {
ZH_LOGI("BH1750 read begin."); ZH_LOGI("BH1750 read begin.");
ZH_ERROR_CHECK(data != NULL && handle != NULL, ESP_ERR_INVALID_ARG, NULL, "BH1750 read fail. Invalid argument."); ZH_ERROR_CHECK(handle != NULL && *handle != NULL && data != NULL, ESP_ERR_INVALID_ARG, NULL, "BH1750 read fail. Invalid argument.");
ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_NOT_FOUND, NULL, "BH1750 read fail. BH1750 not initialized.");
uint8_t sensor_data[2] = {0}; uint8_t sensor_data[2] = {0};
ZH_ERROR_CHECK(i2c_master_transmit(handle->dev_handle, &_bh1750_read_command, sizeof(_bh1750_read_command), 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_FAIL, ZH_ERROR_CHECK(i2c_master_transmit((*handle)->dev_handle, &_bh1750_read_command, sizeof(_bh1750_read_command), 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_FAIL,
++_stats.i2c_driver_error, "BH1750 read fail. I2C driver error."); ++_stats.i2c_driver_error, "BH1750 read fail. I2C driver error.");
vTaskDelay(180 / portTICK_PERIOD_MS); vTaskDelay(180 / portTICK_PERIOD_MS);
ZH_ERROR_CHECK(i2c_master_receive(handle->dev_handle, sensor_data, sizeof(sensor_data), 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_FAIL, ZH_ERROR_CHECK(i2c_master_receive((*handle)->dev_handle, sensor_data, sizeof(sensor_data), 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_FAIL,
++_stats.i2c_driver_error, "BH1750 read fail. I2C driver error."); ++_stats.i2c_driver_error, "BH1750 read fail. I2C driver error.");
*data = (sensor_data[0] << 8 | sensor_data[1]) / 1.2; *data = (sensor_data[0] << 8 | sensor_data[1]) / 1.2;
ZH_LOGI("BH1750read completed successfully."); ZH_LOGI("BH1750read completed successfully.");