feat: 添加 i2c_master_messager 组件,集成 BH1750 光照传感器支持

This commit is contained in:
Wang Beihong
2026-03-05 20:15:55 +08:00
parent 32f3f9980d
commit bde2df4e13
8 changed files with 362 additions and 2 deletions

View File

@@ -0,0 +1,5 @@
idf_component_register(
SRCS "i2c_master_messager.c"
INCLUDE_DIRS "include"
REQUIRES bh1750 esp_driver_i2c esp_driver_gpio esp_timer
)

View File

@@ -0,0 +1,77 @@
# i2c_master_messager
`i2c_master_messager` 用于统一管理工程中的 I2C 传感器。
当前已接入:
- BH1750 光照传感器(默认地址 `0x23`
设计目标:
- 提供统一的数据结构,方便其他模块读取
- 提供独立采集任务,周期性更新数据
- 为后续新增其他 I2C 传感器预留扩展位置
## 对外数据结构
头文件:`include/i2c_master_messager.h`
- `i2c_master_messager_data_t`
- `bh1750.lux`光照强度lx
- `bh1750.valid`:当前数据是否有效
- `bh1750.last_update_ms`:最后一次成功更新时间(毫秒)
- `bh1750.last_error`:最后一次采集错误码
后续新增传感器时,建议继续在 `i2c_master_messager_data_t` 中增加对应字段。
## API
- `esp_err_t i2c_master_messager_init(const i2c_master_messager_config_t *config);`
- 初始化 I2C 总线和 BH1750
- `esp_err_t i2c_master_messager_start(void);`
- 启动循环采集任务
- `esp_err_t i2c_master_messager_stop(void);`
- 停止采集任务
- `esp_err_t i2c_master_messager_get_data(i2c_master_messager_data_t *out_data);`
- 读取当前缓存数据(线程安全)
- `esp_err_t i2c_master_messager_deinit(void);`
- 释放总线和传感器资源
## 使用示例
```c
#include "esp_check.h"
#include "i2c_master_messager.h"
void app_main(void)
{
i2c_master_messager_config_t cfg = {
.i2c_port = I2C_NUM_0,
.scl_io_num = GPIO_NUM_5,
.sda_io_num = GPIO_NUM_4,
.read_period_ms = 1000,
.bh1750_addr = 0x23,
.bh1750_mode = BH1750_CONTINUE_1LX_RES,
};
ESP_ERROR_CHECK(i2c_master_messager_init(&cfg));
ESP_ERROR_CHECK(i2c_master_messager_start());
while (1) {
i2c_master_messager_data_t data;
if (i2c_master_messager_get_data(&data) == ESP_OK && data.bh1750.valid) {
printf("BH1750: %.2f lx\n", data.bh1750.lux);
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
```
## 扩展建议
新增其他传感器时,建议按以下步骤扩展:
1.`i2c_master_messager_data_t` 中增加该传感器的数据结构
2.`i2c_master_messager_config_t` 中增加该传感器配置项
3.`i2c_master_messager_init()` 中完成驱动初始化
4. 在采集任务中加入周期读取逻辑并更新共享数据
5.`deinit()` 中释放对应资源

View File

@@ -0,0 +1,189 @@
#include <string.h>
#include "driver/i2c_master.h"
#include "esp_check.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "i2c_master_messager.h"
static const char *TAG = "i2c_master_messager";
typedef struct {
bool initialized;
i2c_master_messager_config_t config;
i2c_master_bus_handle_t i2c_bus;
bh1750_handle_t bh1750;
i2c_master_messager_data_t data;
SemaphoreHandle_t lock;
TaskHandle_t task_handle;
} i2c_master_messager_ctx_t;
static i2c_master_messager_ctx_t g_ctx;
static void i2c_master_messager_task(void *arg)
{
(void)arg;
while (1) {
float lux = 0.0f;
esp_err_t ret = bh1750_get_data(g_ctx.bh1750, &lux);
if (xSemaphoreTake(g_ctx.lock, pdMS_TO_TICKS(100)) == pdTRUE) {
g_ctx.data.bh1750.valid = (ret == ESP_OK);
g_ctx.data.bh1750.last_error = ret;
if (ret == ESP_OK) {
g_ctx.data.bh1750.lux = lux;
g_ctx.data.bh1750.last_update_ms = esp_timer_get_time() / 1000;
}
xSemaphoreGive(g_ctx.lock);
}
if (ret != ESP_OK) {
ESP_LOGW(TAG, "bh1750_get_data failed: %s", esp_err_to_name(ret));
}
vTaskDelay(pdMS_TO_TICKS(g_ctx.config.read_period_ms));
}
}
esp_err_t i2c_master_messager_init(const i2c_master_messager_config_t *config)
{
ESP_RETURN_ON_FALSE(config != NULL, ESP_ERR_INVALID_ARG, TAG, "config is null");
ESP_RETURN_ON_FALSE(config->read_period_ms >= I2C_MASTER_MESSAGER_MIN_PERIOD_MS,
ESP_ERR_INVALID_ARG,
TAG,
"read_period_ms too small");
if (g_ctx.initialized) {
return ESP_ERR_INVALID_STATE;
}
memset(&g_ctx, 0, sizeof(g_ctx));
g_ctx.config = *config;
g_ctx.lock = xSemaphoreCreateMutex();
ESP_RETURN_ON_FALSE(g_ctx.lock != NULL, ESP_ERR_NO_MEM, TAG, "failed to create mutex");
const i2c_master_bus_config_t bus_cfg = {
.i2c_port = config->i2c_port,
.sda_io_num = config->sda_io_num,
.scl_io_num = config->scl_io_num,
.clk_source = I2C_CLK_SRC_DEFAULT,
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true,
};
esp_err_t ret = i2c_new_master_bus(&bus_cfg, &g_ctx.i2c_bus);
if (ret != ESP_OK) {
vSemaphoreDelete(g_ctx.lock);
g_ctx.lock = NULL;
return ret;
}
ret = bh1750_create(g_ctx.i2c_bus, config->bh1750_addr, &g_ctx.bh1750);
if (ret != ESP_OK) {
i2c_del_master_bus(g_ctx.i2c_bus);
g_ctx.i2c_bus = NULL;
vSemaphoreDelete(g_ctx.lock);
g_ctx.lock = NULL;
return ret;
}
ESP_GOTO_ON_ERROR(bh1750_power_on(g_ctx.bh1750), err, TAG, "bh1750_power_on failed");
ESP_GOTO_ON_ERROR(bh1750_set_measure_mode(g_ctx.bh1750, config->bh1750_mode),
err,
TAG,
"bh1750_set_measure_mode failed");
g_ctx.data.bh1750.valid = false;
g_ctx.data.bh1750.last_error = ESP_ERR_INVALID_STATE;
g_ctx.initialized = true;
ESP_LOGI(TAG,
"initialized: port=%d scl=%d sda=%d addr=0x%02X period=%u ms",
config->i2c_port,
config->scl_io_num,
config->sda_io_num,
config->bh1750_addr,
config->read_period_ms);
return ESP_OK;
err:
bh1750_delete(g_ctx.bh1750);
g_ctx.bh1750 = NULL;
i2c_del_master_bus(g_ctx.i2c_bus);
g_ctx.i2c_bus = NULL;
vSemaphoreDelete(g_ctx.lock);
g_ctx.lock = NULL;
return ret;
}
esp_err_t i2c_master_messager_start(void)
{
ESP_RETURN_ON_FALSE(g_ctx.initialized, ESP_ERR_INVALID_STATE, TAG, "not initialized");
if (g_ctx.task_handle != NULL) {
return ESP_OK;
}
BaseType_t ok = xTaskCreate(i2c_master_messager_task,
"i2c_msg_task",
4096,
NULL,
5,
&g_ctx.task_handle);
ESP_RETURN_ON_FALSE(ok == pdPASS, ESP_ERR_NO_MEM, TAG, "failed to create task");
return ESP_OK;
}
esp_err_t i2c_master_messager_stop(void)
{
ESP_RETURN_ON_FALSE(g_ctx.initialized, ESP_ERR_INVALID_STATE, TAG, "not initialized");
if (g_ctx.task_handle != NULL) {
vTaskDelete(g_ctx.task_handle);
g_ctx.task_handle = NULL;
}
return ESP_OK;
}
esp_err_t i2c_master_messager_get_data(i2c_master_messager_data_t *out_data)
{
ESP_RETURN_ON_FALSE(g_ctx.initialized, ESP_ERR_INVALID_STATE, TAG, "not initialized");
ESP_RETURN_ON_FALSE(out_data != NULL, ESP_ERR_INVALID_ARG, TAG, "out_data is null");
ESP_RETURN_ON_FALSE(g_ctx.lock != NULL, ESP_ERR_INVALID_STATE, TAG, "lock not ready");
ESP_RETURN_ON_FALSE(xSemaphoreTake(g_ctx.lock, pdMS_TO_TICKS(100)) == pdTRUE,
ESP_ERR_TIMEOUT,
TAG,
"failed to lock shared data");
*out_data = g_ctx.data;
xSemaphoreGive(g_ctx.lock);
return ESP_OK;
}
esp_err_t i2c_master_messager_deinit(void)
{
if (!g_ctx.initialized) {
return ESP_OK;
}
i2c_master_messager_stop();
if (g_ctx.bh1750 != NULL) {
bh1750_delete(g_ctx.bh1750);
g_ctx.bh1750 = NULL;
}
if (g_ctx.i2c_bus != NULL) {
i2c_del_master_bus(g_ctx.i2c_bus);
g_ctx.i2c_bus = NULL;
}
if (g_ctx.lock != NULL) {
vSemaphoreDelete(g_ctx.lock);
g_ctx.lock = NULL;
}
memset(&g_ctx, 0, sizeof(g_ctx));
return ESP_OK;
}

View File

@@ -0,0 +1,45 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "bh1750.h"
#include "driver/gpio.h"
#include "driver/i2c_types.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
i2c_port_num_t i2c_port;
gpio_num_t scl_io_num;
gpio_num_t sda_io_num;
uint16_t read_period_ms;
uint8_t bh1750_addr;
bh1750_measure_mode_t bh1750_mode;
} i2c_master_messager_config_t;
typedef struct {
float lux;
bool valid;
int64_t last_update_ms;
esp_err_t last_error;
} i2c_bh1750_data_t;
typedef struct {
i2c_bh1750_data_t bh1750;
} i2c_master_messager_data_t;
#define I2C_MASTER_MESSAGER_MIN_PERIOD_MS (100)
esp_err_t i2c_master_messager_init(const i2c_master_messager_config_t *config);
esp_err_t i2c_master_messager_start(void);
esp_err_t i2c_master_messager_stop(void);
esp_err_t i2c_master_messager_get_data(i2c_master_messager_data_t *out_data);
esp_err_t i2c_master_messager_deinit(void);
#ifdef __cplusplus
}
#endif

View File

@@ -1,4 +1,14 @@
dependencies:
espressif/bh1750:
component_hash: e898130f6b2fc4bc0d6022a2e431752bae808b9c94d4cc91596e36ecaf4cb7c6
dependencies:
- name: idf
require: private
version: '>=5.3'
source:
registry_url: https://components.espressif.com/
type: service
version: 2.0.0
espressif/esp_lvgl_port:
component_hash: b6360960f47b6776462e7092861b3ea66477ffb762a01baa0aecbb3d74cd50f4
dependencies:
@@ -25,8 +35,9 @@ dependencies:
type: service
version: 9.5.0
direct_dependencies:
- espressif/bh1750
- espressif/esp_lvgl_port
- idf
manifest_hash: fa314ee0d60a34ffe2dea85313c7369c0c4b16079167ed05ad45e0cadad2199d
manifest_hash: 1fd264456e89adf9729f55472d73dc1ff5c869e32bf39dd97a5d1bb9225c0aff
target: esp32c3
version: 2.0.0

View File

@@ -1,4 +1,4 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS "."
REQUIRES wifi-connect esp_lvgl_port lvgl_st7735s_use
REQUIRES wifi-connect esp_lvgl_port lvgl_st7735s_use i2c_master_messager
)

View File

@@ -15,3 +15,4 @@ dependencies:
# # All dependencies of `main` are public by default.
# public: true
espressif/esp_lvgl_port: ^2.7.2
espressif/bh1750: ^2.0.0

View File

@@ -1,9 +1,16 @@
#include <stdio.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_check.h"
#include "wifi-connect.h"
#include "lvgl_st7735s_use.h"
#include "i2c_master_messager.h"
#define BOTANY_I2C_PORT I2C_NUM_0
#define BOTANY_I2C_SCL_GPIO GPIO_NUM_5
#define BOTANY_I2C_SDA_GPIO GPIO_NUM_4
#define BOTANY_READ_PERIOD_MS 1000
void app_main(void)
{
@@ -13,8 +20,33 @@ void app_main(void)
// 启动 LVGL 演示程序,显示简单的界面
ESP_ERROR_CHECK(start_lvgl_demo());
i2c_master_messager_config_t i2c_cfg = {
.i2c_port = BOTANY_I2C_PORT,
.scl_io_num = BOTANY_I2C_SCL_GPIO,
.sda_io_num = BOTANY_I2C_SDA_GPIO,
.read_period_ms = BOTANY_READ_PERIOD_MS,
.bh1750_addr = 0x23,
.bh1750_mode = BH1750_CONTINUE_1LX_RES,
};
ESP_ERROR_CHECK(i2c_master_messager_init(&i2c_cfg));
ESP_ERROR_CHECK(i2c_master_messager_start());
for (;;)
{
i2c_master_messager_data_t sensor_data = {0};
if (i2c_master_messager_get_data(&sensor_data) == ESP_OK && sensor_data.bh1750.valid) {
char text[32] = {0};
snprintf(text, sizeof(text), "Light: %.1f lx", sensor_data.bh1750.lux);
ESP_ERROR_CHECK(lvgl_st7735s_set_center_text(text));
printf("[BH1750] lux=%.2f update_ms=%" PRId64 "\n",
sensor_data.bh1750.lux,
sensor_data.bh1750.last_update_ms);
} else {
ESP_ERROR_CHECK(lvgl_st7735s_set_center_text("Light: waiting..."));
printf("[BH1750] waiting data, err=0x%x\n", sensor_data.bh1750.last_error);
}
vTaskDelay(pdMS_TO_TICKS(1000));
}