From bde2df4e137f162a32c7f6f918f27c3a82b12fd9 Mon Sep 17 00:00:00 2001 From: Wang Beihong Date: Thu, 5 Mar 2026 20:15:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20i2c=5Fmaster=5Fmes?= =?UTF-8?q?sager=20=E7=BB=84=E4=BB=B6=EF=BC=8C=E9=9B=86=E6=88=90=20BH1750?= =?UTF-8?q?=20=E5=85=89=E7=85=A7=E4=BC=A0=E6=84=9F=E5=99=A8=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/i2c_master_messager/CMakeLists.txt | 5 + components/i2c_master_messager/README.md | 77 +++++++ .../i2c_master_messager/i2c_master_messager.c | 189 ++++++++++++++++++ .../include/i2c_master_messager.h | 45 +++++ dependencies.lock | 13 +- main/CMakeLists.txt | 2 +- main/idf_component.yml | 1 + main/main.c | 32 +++ 8 files changed, 362 insertions(+), 2 deletions(-) create mode 100644 components/i2c_master_messager/CMakeLists.txt create mode 100644 components/i2c_master_messager/README.md create mode 100644 components/i2c_master_messager/i2c_master_messager.c create mode 100644 components/i2c_master_messager/include/i2c_master_messager.h diff --git a/components/i2c_master_messager/CMakeLists.txt b/components/i2c_master_messager/CMakeLists.txt new file mode 100644 index 0000000..dbe9bc9 --- /dev/null +++ b/components/i2c_master_messager/CMakeLists.txt @@ -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 +) diff --git a/components/i2c_master_messager/README.md b/components/i2c_master_messager/README.md new file mode 100644 index 0000000..e52befc --- /dev/null +++ b/components/i2c_master_messager/README.md @@ -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()` 中释放对应资源 diff --git a/components/i2c_master_messager/i2c_master_messager.c b/components/i2c_master_messager/i2c_master_messager.c new file mode 100644 index 0000000..f591d48 --- /dev/null +++ b/components/i2c_master_messager/i2c_master_messager.c @@ -0,0 +1,189 @@ +#include + +#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; +} diff --git a/components/i2c_master_messager/include/i2c_master_messager.h b/components/i2c_master_messager/include/i2c_master_messager.h new file mode 100644 index 0000000..c56dfe1 --- /dev/null +++ b/components/i2c_master_messager/include/i2c_master_messager.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +#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 diff --git a/dependencies.lock b/dependencies.lock index 0af6716..a21ba95 100644 --- a/dependencies.lock +++ b/dependencies.lock @@ -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 diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 6125fb2..f75861c 100755 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -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 ) diff --git a/main/idf_component.yml b/main/idf_component.yml index 2c48139..0026c33 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -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 diff --git a/main/main.c b/main/main.c index 0c0fb54..dff3edf 100755 --- a/main/main.c +++ b/main/main.c @@ -1,9 +1,16 @@ #include +#include #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)); }