esp-iot-solution sensor_hub_monitor 示例全解析:基于 sensor_hub 的 I2C 传感器动态发现与数据采集
【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution
本示例展示了如何在 ESP32 / ESP32-S2 上使用sensor_hub组件快速构建传感器应用:通过 I2C 总线扫描结果动态发现并加载传感器驱动,以默认参数开启采集并将结果通过事件机制上报。阅读本文后,你将掌握iot_sensor_scan/iot_sensor_create/iot_sensor_start等核心 API 的用法、sensor_hub的轮询与事件架构,以及如何基于板级初始化(iot_board)与menuconfig搭建一套可复用的多传感器监控程序。
示例定位与整体流程
sensor_hub_monitor位于 examples/sensors/sensor_hub_monitor,其核心思路是:让框架替开发者完成"发现驱动 → 创建传感器 → 采集数据 → 上报事件"的完整闭环。开发者只需要三件事:
- 初始化开发板,拿到 I2C 总线句柄;
- 调用
iot_sensor_scan()扫描当前工程中已注册的传感器驱动; - 按驱动名创建传感器、注册事件处理器并启动。
其主程序完整代码见 examples/sensors/sensor_hub_monitor/main/app_main.c,整体执行顺序为:
iot_board_init() → 获取 I2C0 总线句柄 → iot_sensor_scan() → iot_sensor_create() → iot_sensor_handler_register() → iot_sensor_start() → 事件循环上报数据硬件需求与接线
- 本示例默认支持
common_components/boards组件中已收录的全部开发板,以及任意使用 ESP32 或 ESP32-S2 的开发板(使用其他开发板时可能需要按实际引脚简单修改); - 默认支持
sensor_hub中已添加的所有传感器驱动,可以按下面的接线方式同时挂载一个或多个传感器。
接线示意图如下(所有传感器共用同一条 I2C 总线,VCC / SCL / SDA / GND 并联):
ESP32/ESP32S2 BOARD Sensors (1) Sensors (2) .... -- -- | || VCC | || VCC [SCL IO] --------> | || SCL | || SCL [SDA IO] --------> | || SDA | || SDA | || GND | || GND -- --默认开发板为ESP32 DevkitC-V4,其默认SCL引脚为22、默认SDA引脚为21。板级引脚与总线初始化由 examples/common_components/boards 组件统一管理,通过iot_board_init()与iot_board_get_handle(BOARD_I2C0_ID)获取总线句柄,见 app_main.c。
编译与烧写
1. 配置环境变量
export IOT_SOLUTION_PATH=PATH将PATH替换为esp-iot-solution仓库所在路径,例如~/esp-iot-solution。
2. 选择目标开发板
idf.py menuconfig在Board Options -> Choose Target Board中选择目标开发板。默认是ESP32 Devkit-V4。示例默认的sdkconfig.defaults中则预置了CONFIG_BOARD_ESP32_MESHKIT_SENSE=y(即 ESP32-MeshKit-Sense 板),见 examples/sensors/sensor_hub_monitor/sdkconfig.defaults。
3. 编译与烧写
idf.py -p PORT build flash将PORT替换为实际串口端口号。
关键配置项说明
示例级配置(Kconfig)
示例自身的配置定义在 examples/sensors/sensor_hub_monitor/main/Kconfig.projbuild:
| 配置项 | 含义 | 取值范围 | 默认值 |
|---|---|---|---|
CONFIG_SENSOR_EXAMPLE_PERIOD | 传感器最小采集间隔(ms) | 0 ~ 65535 | 200 |
该值通过SENSOR_PERIOD宏(#define SENSOR_PERIOD CONFIG_SENSOR_EXAMPLE_PERIOD)传入每个传感器的min_delay字段,控制轮询采集周期。
组件级配置(sdkconfig.defaults)
sdkconfig.defaults中同时给出了sensor_hub组件与示例相关的完整配置组合:
- 传感器类型裁剪:
CONFIG_SENSOR_INCLUDED_IMU=y、CONFIG_SENSOR_INCLUDED_HUMITURE=y、CONFIG_SENSOR_INCLUDED_LIGHT=y,分别启用 IMU、温湿度、光照三类传感器 HAL 层; - 采集任务:
CONFIG_SENSOR_TASK_PRIORITY_INHERIT=y(继承调用者优先级)、CONFIG_SENSOR_TASK_STACK_SIZE=4096; - 事件循环:
CONFIG_SENSORS_EVENT_QUEUE_SIZE=32、CONFIG_SENSORS_EVENT_STACK_SIZE=4096、CONFIG_SENSOR_EVENT_LOOP_AUTO=y(自动创建事件循环)、CONFIG_SENSOR_DEFAULT_HANDLER未启用(示例自带事件处理器); - 板级:
CONFIG_BOARD_I2C0_INIT=y启用 I2C0 总线自动初始化。
在iot_sensor_hub.c中,传感器类型与驱动实现的绑定同样是编译期剪裁的:s_sensor_impls[]数组仅在对应CONFIG_SENSOR_INCLUDED_*使能时才编译进humiture_create / imu_create / light_sensor_create等实现,见 components/sensors/sensor_hub/iot_sensor_hub.c。
源码剖析:应用层如何驱动 sensor_hub
1. 板级初始化与总线获取
esp_err_t err = iot_board_init(); // 按 menuconfig 选中的板卡完成外设初始化 bus_handle_t i2c0_bus_handle = (bus_handle_t)iot_board_get_handle(BOARD_I2C0_ID); // 取 I2C0 总线句柄任何一步失败都会跳转到error_loop打印ERROR并挂起(while(1) vTaskDelay(1000)),便于串口排障,见 app_main.c。
2. 动态发现驱动:iot_sensor_scan()
iot_sensor_scan();iot_sensor_scan()的实现在 components/sensors/sensor_hub/iot_sensor_hub.c:它遍历链接器段.sensor_hub_detect_fn中所有驱动注册的探测函数,逐一调用并打印Find <name> driver, type: <TYPE>,返回有效驱动数量。
每个传感器驱动通过宏SENSOR_HUB_DETECT_FN(type_id, name_id, impl)自动注册探测函数,该宏定义在 components/sensors/sensor_hub/include/iot_sensor_hub.h。为避免链接器把驱动裁剪掉,驱动侧还需在CMakeLists.txt中通过target_link_libraries(${COMPONENT_LIB} INTERFACE "-u <symbol>")强制保留符号。这就是"按 I2C 扫描结果动态发现驱动"的底层机制:驱动自身声明了名字与类型,扫描时根据驱动实现内部对 I2C 地址的探测结果决定是否加载。
3. 创建传感器:iot_sensor_create()
以温湿度传感器 SHT3x 为例(app_main.c):
sensor_config_t sht3x_config = { .bus = i2c0_bus_handle, // I2C 总线句柄 .addr = 0x44, // 器件 I2C 地址 .type = HUMITURE_ID, // 传感器类型 .mode = MODE_POLLING, // 采集模式:轮询 .min_delay = SENSOR_PERIOD, // 最小采集间隔,默认 200 ms }; iot_sensor_create("sht3x", &sht3x_config, &sht3x_handle);sensor_config_t的完整字段见 components/sensors/sensor_hub/include/iot_sensor_hub.h:
| 字段 | 说明 |
|---|---|
bus | I2C/SPI 总线句柄 |
addr | 传感器 I2C 地址 |
type | 传感器类型,取值见下方枚举 |
mode | 采集模式:MODE_DEFAULT/MODE_POLLING/MODE_INTERRUPT |
range | 量程:RANGE_DEFAULT/RANGE_MIN/RANGE_MEDIUM/RANGE_MAX |
min_delay | 最小采集间隔(ms),轮询模式下即定时器周期 |
intr_pin/intr_type | 中断模式下的 GPIO 引脚与触发类型 |
传感器类型枚举定义在 components/sensors/sensor_hub/include/sensor_type.h:NULL_ID、HUMITURE_ID(温湿度)、IMU_ID(加速度/陀螺仪)、LIGHT_SENSOR_ID(光照/UV/颜色)。
iot_sensor_create()内部(iot_sensor_hub.c)按类型查找到对应 HAL 实现impl后,依次执行:
impl->create()创建驱动实例;impl->control()下发COMMAND_SET_MODE(模式)、COMMAND_SET_RANGE(量程)、COMMAND_SET_ODR(输出频率,此处即min_delay);若驱动不支持对应命令会跳过(返回ESP_ERR_NOT_SUPPORTED不算错误);impl->control()下发COMMAND_SELF_TEST自检,失败则报错;- 将传感器节点加入全局链表,分配事件位(
event_bit); - 按模式初始化资源:
MODE_POLLING创建软件定时器xTimerCreate,MODE_INTERRUPT配置 GPIO 中断; - 若默认采集任务不存在,则创建名为
SENSOR_HUB的任务(sensor_default_task); - 打印
Sensor created, Task name = SENSOR_HUB, Type = ...日志。
4. 注册事件处理器与启动
iot_sensor_handler_register(sht3x_handle, sensor_event_handler, &sht3x_handler_handle); iot_sensor_start(sht3x_handle);iot_sensor_handler_register():为该传感器的事件基(event_base,形如"sht3x_0x44")注册回调,任何事件 ID 都会触发;iot_sensor_start():轮询模式下启动定时器,并发送SENSOR_STARTED事件、将传感器从POWER_MODE_SLEEP唤醒到POWER_MODE_WAKEUP;- 对应地,
iot_sensor_stop()/iot_sensor_delete()会发送SENSOR_STOPED事件、将器件置入睡眠并释放资源(删除最后一个传感器时还会回收事件组与默认任务)。
5. 事件处理器与数据模型
app_main.c中的sensor_event_handler覆盖了示例可能遇到的全部事件 ID,核心代码如下(app_main.c):
case SENSOR_STARTED: ESP_LOGI(TAG, "Timestamp = %llu - %s_0x%x STARTED", ...); case SENSOR_HUMI_DATA_READY: ESP_LOGI(TAG, "... HUMI_DATA_READY - humiture=%.2f", ..., sensor_data->humidity); case SENSOR_TEMP_DATA_READY: ESP_LOGI(TAG, "... TEMP_DATA_READY - temperature=%.2f", ..., sensor_data->temperature); case SENSOR_ACCE_DATA_READY: ESP_LOGI(TAG, "... ACCE_DATA_READY - acce_x=%.2f, acce_y=%.2f, acce_z=%.2f", ...);事件 ID 分为两类(sensor_type.h):
- 通用事件:
SENSOR_STARTED(0)、SENSOR_STOPED(1); - 数据就绪事件:
SENSOR_ACCE_DATA_READY(10)、SENSOR_GYRO_DATA_READY、SENSOR_MAG_DATA_READY、SENSOR_TEMP_DATA_READY、SENSOR_HUMI_DATA_READY、SENSOR_BARO_DATA_READY、SENSOR_LIGHT_DATA_READY、SENSOR_RGBW_DATA_READY、SENSOR_UV_DATA_READY等。
事件数据统一封装在sensor_data_t联合体中(sensor_type.h),包含timestamp(微秒级,来自esp_timer_get_time())、sensor_name、sensor_addr、event_id、min_delay,并通过联合体暴露加速度/陀螺仪(axis3_t)、温湿度(float)、光照(float)、颜色(rgbw_t)、UV(uv_t)等载荷,单位分别为 G、dps、dCelsius、百分比、lux 等。事件的分发由 components/sensors/sensor_hub/sensors_event.c 与 components/sensors/sensor_hub/include/sensor_event.h 提供,底层复用 ESP-IDF 的esp_event循环,队列大小与栈大小由上文CONFIG_SENSORS_EVENT_QUEUE_SIZE/CONFIG_SENSORS_EVENT_STACK_SIZE控制。
6. 轮询采集的底层机制
sensor_default_task(iot_sensor_hub.c)是采集的核心:每个轮询传感器对应一个周期性软件定时器,定时器回调sensors_timer_cb设置事件组中该传感器的 bit(iot_sensor_hub.c);任务通过xEventGroupWaitBits等待事件位后,对命中的传感器调用impl->acquire()采集一组数据,并逐一为sensor_data_t填充时间戳、类型、名字、地址后通过sensors_event_post()发布事件。
因此:无论挂多少轮询传感器,采集与上报都由单一SENSOR_HUB任务统一驱动,采集周期由各传感器的min_delay(即SENSOR_EXAMPLE_PERIOD)决定。若选用MODE_INTERRUPT,则改由 GPIO 中断 ISR(sensors_intr_isr_handler)置位触发采集。
示例输出解读
以同时挂载 VEML6040 光照传感器、SHT31 温湿度传感器、LIS2DH12 加速度计三个模块为例,串口输出分三个阶段:
阶段 1:发现 I2C 设备并推断驱动型号
I (319) Board_Common: Board Init Done ! I (329) SENSOR_HUB: Find lis2dh12 driver, type: IMU I (329) SENSOR_HUB: Find sht3x driver, type: HUMITURE I (339) SENSOR_HUB: Find veml6040 driver, type: LIGHTSENSOR此即iot_sensor_scan()遍历驱动探测函数的输出。
阶段 2:创建并启动传感器
I (349) SENSOR_HUB: Sensor created, Task name = SENSOR_HUB, Type = HUMITURE, Sensor Name = sht3x, Mode = MODE_POLLING, Min Delay = 200 ms I (359) SENSOR_HUB: task: sensor_default_task created! I (369) SENSOR_LOOP: event loop created succeed I (379) SENSOR_LOOP: register a new handler to event loop succeed I (379) Sensors Monitor: Timestamp = 850604 - sht3x_0x44 STARTEDSENSOR_HUB打印创建信息、SENSOR_LOOP打印事件循环与处理器注册信息、Sensors Monitor则是示例应用自己的 TAG(app_main.c中#define TAG "Sensors Monitor")。
阶段 3:周期采集并上报事件
I (579) Sensors Monitor: Timestamp = 1044096 - sht3x_0x44 HUMI_DATA_READY - humiture=45.78 I (779) Sensors Monitor: Timestamp = 1243127 - sht3x_0x44 TEMP_DATA_READY - temperature=25.00 I (779) Sensors Monitor: Timestamp = 1243127 - sht3x_0x44 HUMI_DATA_READY - humiture=45.78 I (979) Sensors Monitor: Timestamp = 1444101 - sht3x_0x44 TEMP_DATA_READY - temperature=25.00每次采集间隔约为 200 ms(Timestamp单位为微秒),与CONFIG_SENSOR_EXAMPLE_PERIOD默认值一致;温湿度事件分别以HUMI_DATA_READY/TEMP_DATA_READY上报。
进一步探索
- 修改采集周期:
idf.py menuconfig中调整Sensor Monitor Example -> sensor min delay(ms); - 更换/增加传感器:在
sensor_hub已支持的驱动中选择type与addr,参照sht3x三段式(create → handler_register → start)扩展; - 深度阅读框架:
sensor_hub组件的完整说明见 components/sensors/sensor_hub/README.md,组件自带的虚拟传感器测试用例(components/sensors/sensor_hub/test_apps)可用于验证各类型驱动的采集与事件上报链路; - 其他传感器示例:仓库中还有 examples/sensors/ntc_temperature_sensor、examples/sensors/power_measure、examples/sensors/sensor_control_led 等单传感器示例可对照学习。
【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考