ESP-IoT-Solution BTHome 蓝牙调光器示例全解析:事件触发广播、加密载荷与 RTC IO 低功耗唤醒
2026/9/20 3:42:05 网站建设 项目流程

ESP-IoT-Solution BTHome 蓝牙调光器示例全解析:事件触发广播、加密载荷与 RTC IO 低功耗唤醒

【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution

导读

本文以 esp-iot-solution 仓库中的bthome/dimmer示例为对象,讲解如何基于 ESP-IDF 的bthome组件与ble_hci组件,实现一个符合 BTHome 协议的加密蓝牙调光器:通过按键与旋钮采集输入、仅在事件发生时广播 300 ms 即自动停播,并借助动态调频、tickless idle 与 RTC IO 唤醒实现极低功耗运行。读完本文,你将掌握 BTHome 广播载荷的构建方式、iot_button/iot_knob低功耗驱动的选型逻辑,以及 ESP32-H2/H4 上 light sleep 与 GPIO 引脚约束的落地实践。

BTHome Dimmer 示例概述

该示例位于 examples/bluetooth/ble_adv/bthome/dimmer,是基于仓库内bthome组件(components/bluetooth/ble_adv/bthome)实现的一个通用蓝牙调光器参考实现。它读取按键与旋钮的状态,并通过 BLE 广播发送符合 BTHome 协议的加密广播包,从而被 BTHome 生态(如 Home Assistant 的 BTHome 集成)直接识别。

软件层面实现的效果可概括为三点:

  • 事件采集与上报:读取按键(button)与旋钮(knob)的值,并通过 BLE ADV 发送;
  • 事件触发式广播:仅在按键按压或旋钮旋转时广播,广播在最后一次事件后300 ms 自动停止,避免持续广播耗电;
  • 自动休眠唤醒:支持 light sleep 自动休眠,按键与旋钮通过RTC IO唤醒,保证休眠期间仍能及时响应交互。

工程结构与依赖组件

目录组成

examples/bluetooth/ble_adv/bthome/dimmer/ ├── main/ │ ├── CMakeLists.txt # 组件构建声明 │ ├── Kconfig.projbuild # 例程可调参数(CPU 频率、GPIO 等) │ ├── app_main.c # 全部业务逻辑 │ └── idf_component.yml # 组件依赖清单 ├── CMakeLists.txt ├── README.md / README_CN.md ├── sdkconfig.defaults # 通用默认配置 └── sdkconfig.defaults.esp32h4 # ESP32-H4 低功耗优化配置

依赖关系

main/idf_component.yml 声明了示例的全部依赖,均通过override_path指向仓库内的本地组件:

依赖指向仓库组件作用
bthomecomponents/bluetooth/ble_adv/bthomeBTHome 协议编解码与加密
ble_hcicomponents/bluetooth/ble_hci免 NimBLE/Bluedroid 的底层 HCI 广播接口
buttoncomponents/button按键驱动(含 GPIO/RTC 两种实现)
knobcomponents/knob旋钮(旋转编码器)驱动(含 GPIO/RTC 两种实现)
idf要求 ESP-IDF 版本>=5.0

构建声明见 main/CMakeLists.txt:PRIV_REQUIRES driver bthome ble_hci nvs_flash,其中nvs_flash用于 BTHome 参数的持久化存储。

值得注意,本示例不依赖完整的 BLE 协议栈(如 NimBLE 或 Bluedroid),而是直接使用ble_hci组件通过 HCI 层下发广播参数与广播数据,配合sdkconfig.defaults中的CONFIG_BT_CONTROLLER_ONLY=y,将系统资源占用与功耗降到最低。

支持的芯片与硬件约束

目标芯片

  • ESP32-H2:默认目标,对应 ESP-Dimmer 硬件参考设计;
  • ESP32-H4:低功耗优化版本,选择该目标后会自动加载sdkconfig.defaults.esp32h4

GPIO 引脚约束

这是最容易踩坑的部分,示例在 main/Kconfig.projbuild 中针对不同芯片给出了不同的引脚范围限制:

  • ESP32-H4:按键与旋钮引脚必须为 RTC GPIO 0–5。默认启用 RTC IO 驱动(CONFIG_EXAMPLE_BUTTON_KNOB_USE_RTC_IO),通过 RTC IO 实现 light sleep 唤醒;
  • ESP32-H2:当启用 light sleep 外设掉电(CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP)时,按键与旋钮引脚需使用RTC 唤醒 GPIO 7–14,此时走 GPIO 驱动路径。

Kconfig 中通过range指令直接约束可选引脚范围,例如EXAMPLE_GPIO_KNOB_A在 ESP32-H4 上为range 0 5、在 ESP32-H2 上为range 0 27;若选择了非法引脚,menuconfig 会直接报错,从配置源头规避唤醒引脚失效问题。

编译与烧写

进入示例目录后使用idf.py设置目标芯片并编译烧写:

cd ./esp-iot-solution/examples/bluetooth/ble_adv/bthome/dimmer # ESP32-H2(默认硬件参考设计) idf.py set-target esp32h2 # ESP32-H4(低功耗优化,自动加载 sdkconfig.defaults.esp32h4) idf.py set-target esp32h4 # 编译并下载,PORT 替换为实际串口 idf.py -p PORT build flash

设置esp32h4目标时,IDF 会按照命名规则自动加载sdkconfig.defaults.esp32h4,无需手动合并配置。

源码级工作流程解析

初始化链路:app_main()

main/app_main.c 的启动顺序如下:

  1. NVS 初始化nvs_flash_init(),若发生页耗尽或版本更新则先擦除再初始化;
  2. 电源管理配置:在CONFIG_PM_ENABLE下调用esp_pm_configure(),以CONFIG_EXAMPLE_MAX_CPU_FREQ_MHZ/CONFIG_EXAMPLE_MIN_CPU_FREQ_MHZ配置动态调频(DFS)范围;若启用了CONFIG_FREERTOS_USE_TICKLESS_IDLE,则同时开启自动 light sleep(light_sleep_enable = true);
  3. BTHome 对象创建与参数注入bthome_create()创建句柄,注册 NVS 存储/加载回调,设置 16 字节加密密钥(app_main.c#L55)与本地 MAC 地址,最后bthome_load_params()从 NVS 恢复参数;
  4. 创建任务与定时器:创建dimmer task(栈 4096、优先级 10),并创建一次性 300 ms 定时器(app_main.c#L357),定时器到点即关闭广播;
  5. 初始化外设power_ctrl_io_init()将电源控制引脚拉高并gpio_hold_en()保持(app_main.c#L162-L181),随后初始化旋钮与按键并注册回调。

事件采集:ISR 通知 + 任务消费

按键与旋钮的回调均运行在 ISR 上下文,通过xTaskNotifyFromISRdimmer task发送事件(TASK_EVENT_BTN/TASK_EVENT_KNOB,见 app_main.c#L60-L82)。其中按键回调还会读取esp_sleep_get_wakeup_cause(),判断是否由 light sleep 唤醒而来——这是 RTC IO 唤醒路径的关键一环。

dimmer_task主循环(app_main.c#L89-L160)通过xTaskNotifyWait阻塞等待事件,收到事件后根据类型编码事件载荷:

  • 按键事件BUTTON_SINGLE_CLICKbtn_evt_id = 1,否则为 0;此时dim_evt = {0, 0}(无旋转);
  • 旋钮事件:读取iot_knob_get_count_value()的累计值,正值编码为dim_evt = {1, value}(左旋),负值编码为{2, -value}(右旋)。

广播数据构建:BTHome 载荷与加密

dimmer task中先配置广播参数(app_main.c#L98-L108):

  • 广播间隔0x50(约 50 ms);
  • ADV_TYPE_NONCONN_IND(不可连接广播);
  • 随机地址类型(BLE_ADDR_TYPE_RANDOM),使用ble_hci_set_random_address()设置示例中预定义的本地 MAC;
  • 广播信道全开(ADV_CHNL_ALL)。

随后通过 BTHome 组件 API 构建载荷(app_main.c#L142-L144):

payload_length = bthome_payload_adv_add_evt_data(payload_data, payload_length, BTHOME_EVENT_ID_BUTTON, &btn_evt_id, 1); payload_length = bthome_payload_adv_add_evt_data(payload_data, payload_length, BTHOME_EVENT_ID_DIMMER, dim_evt, 2); adv_len = bthome_make_adv_data(s_dimmer->bthome, advertisement_data, name, sizeof(name), info, payload_data, payload_length);

其中BTHOME_EVENT_ID_BUTTON = 0x3ABTHOME_EVENT_ID_DIMMER = 0x3C为事件对象 ID(见 components/bluetooth/ble_adv/bthome/include/bthome_v2.h#L110-L113),name{0x44, 0x49, 0x59},即 ASCII 的"DIY"——这也是手机 APP 中搜索到的设备名。

设备信息字节(bthome_device_info_t,见 bthome_v2.h#L120-L129)通过位域设置:

字段示例取值含义
bit 0encryption_flag1载荷已加密
bit 2trigger_based_flag0非纯触发型设备(携带事件数据)
bit 5–7bthome_version2BTHome 协议版本 v2

bthome_make_adv_data()在内部完成服务数据段(BTHome Service UUID + 设备信息字节)、设备名与加密载荷的拼装,加密基于示例预置的 16 字节密钥与本地 MAC 地址(bthome_set_encrypt_key()/bthome_set_local_mac_addr())。

广播包构建成功后调用ble_hci_set_adv_data()下发数据、ble_hci_set_adv_enable(true)开启广播,并启动/重置 300 ms 定时器(app_main.c#L151-L157)。定时器到点后执行ble_hci_set_adv_enable(false)停播(app_main.c#L84-L87)。由于每次新事件都会xTimerReset,实际效果是最后一个事件后 300 ms 停播

参数持久化:NVS 存储回调

BTHome 组件允许注册store/load回调(bthome_callbacks_t,见 bthome_v2.h#L161-L164)。示例在 app_main.c#L268-L308 中基于 NVS 的nvs_set_blob/nvs_get_blob实现,namespace 为storage,用于保存 BTHome 绑定/配对相关参数,实现重启后免重新配对。

低功耗设计:从配置到驱动的完整链路

两套 sdkconfig 的差异

通用默认配置 sdkconfig.defaults:

CONFIG_BT_ENABLED=y CONFIG_BT_CONTROLLER_ONLY=y # 仅启用控制器,不启用完整协议栈 CONFIG_BT_LE_SLEEP_ENABLE=y # BLE 控制器睡眠 CONFIG_PM_ENABLE=y # 电源管理 CONFIG_PM_DFS_INIT_AUTO=y # 动态调频自动初始化 CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_USE_TICKLESS_IDLE=y # tickless idle(自动 light sleep 前提) CONFIG_LOG_DEFAULT_LEVEL_WARN=y # 默认 WARN 级别,压低日志功耗 CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE=y

ESP32-H4 专属配置 sdkconfig.defaults.esp32h4 在通用配置之上追加:

CONFIG_BT_CTRL_SLEEP_ENABLE=y # 控制器睡眠 CONFIG_BT_CTRL_LP_CLK_SRC_DEFAULT=y CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y # light sleep 外设掉电 CONFIG_EXAMPLE_BUTTON_KNOB_USE_RTC_IO=y # 按键/旋钮走 RTC IO 驱动 CONFIG_RTC_CLK_SRC_EXT_CRYS=y # RTC 慢时钟使用外置 32 kHz 晶振

动态调频与自动 light sleep

CONFIG_PM_ENABLE下,示例通过esp_pm_configure()设定 CPU 频率区间。默认值按芯片区分(见 Kconfig.projbuild#L3-L7 与 Kconfig.projbuild#L41-L46):

芯片最大 CPU 频率最小 CPU 频率
ESP32-H432 MHz16 MHz(依赖 32 MHz XTAL)
ESP32-H296 MHz32 MHz
其他芯片(如 ESP32/S2/S3/C5)80/120/160/240 MHz 可选与 XTAL 对应

最小频率必须为 XTAL 频率或其整数分频,这是 ESP-IDF DFS 的硬件约束。配合CONFIG_FREERTOS_USE_TICKLESS_IDLE,系统在空闲时自动进入 light sleep;再叠加CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP,light sleep 期间外设电源被切断,进一步降低漏电流。

RTC IO 驱动 vs GPIO 驱动

示例通过条件编译在两种驱动之间切换(见 app_main.c#L217-L221 与 app_main.c#L236-L262):

  • CONFIG_EXAMPLE_BUTTON_KNOB_USE_RTC_IO=y时:旋钮走iot_knob_create_rtc()(components/knob/knob_rtc.c),按键走iot_button_new_rtc_device()(components/button/button_rtc.c),二者均通过 RTC IO 在 light sleep 期间保持唤醒能力;
  • 关闭该选项时:走iot_knob_create()iot_button_new_gpio_device()(components/button/button_gpio.c)的 GPIO 驱动路径。

两种路径都注册了按键/旋钮回调,但唤醒机制不同:RTC IO 驱动允许深度 light sleep 后被外设事件唤醒,而普通 GPIO 驱动仅适用于未启用外设掉电的场景。此外,当启用外设掉电且未使用 RTC IO 驱动时,代码会调用validate_wakeup_gpio()校验旋钮两相引脚是否为合法的 light sleep 唤醒引脚(app_main.c#L183-L192),这是 ESP32-H2 上必须选择 RTC 唤醒 GPIO 7–14 的运行时保障。

外置 32 kHz 晶振

CONFIG_RTC_CLK_SRC_EXT_CRYS在 ESP32-H4 上默认启用:RTC 慢时钟使用外置 32 kHz 晶振而非内部 RC 振荡器,休眠电流显著更低。这是 H4 低功耗优化的重要一环。

menuconfig 可调参数速查

全部参数位于 menuconfig 的Example Configuration菜单下(定义见 main/Kconfig.projbuild):

配置项默认值说明
EXAMPLE_MAX_CPU_FREQ_MHZH4: 32 / H2: 96DFS 最大 CPU 频率
EXAMPLE_MIN_CPU_FREQ_MHZH4: 16 / H2: 32DFS 最小 CPU 频率(须为 XTAL 或其整数分频)
EXAMPLE_BUTTON_KNOB_USE_RTC_IOn(H4 配置中开启)按键/旋钮使用 RTC IO 驱动实现低功耗唤醒
EXAMPLE_GPIO_KNOB_A2旋转编码器 A 相引脚(H4 限 0–5,H2 限 0–27)
EXAMPLE_GPIO_KNOB_B3旋转编码器 B 相引脚(范围同上)
EXAMPLE_POWER_CTRL_IO_NUM9电源控制引脚(上电后保持高电平)
EXAMPLE_BUTTON_IO_NUM0按键引脚(范围同旋钮)
EXAMPLE_BUTTON_ACTIVE_LEVEL0按键有效电平(0 低有效,1 高有效)

注意旋钮两个引脚与按键引脚都必须满足对应芯片的 RTC 唤醒约束,否则编译期 Kconfigrange或运行期validate_wakeup_gpio()会给出明确错误。

运行验证与输出

为优化功耗,示例默认把日志级别设为CONFIG_LOG_DEFAULT_LEVEL_WARN(sdkconfig 中为 WARN,且示例内部关键日志使用ESP_LOG_BUFFER_HEX_LEVEL(..., ESP_LOG_WARN)打印广播包内容),常规启动几乎无日志输出。

验证方式:烧写后按压按键或旋转旋钮,使用任意 BLE 扫描工具(如手机 APP)搜索名为DIY的设备,即可观察到加密的 BTHome 广播包;将设备接入 Home Assistant 等支持 BTHome 协议的网关后,事件即可被识别为调光/按键操作。该示例的 BTHome 协议实现细节可进一步查阅 components/bluetooth/ble_adv/bthome/README.md 与其单元测试 components/bluetooth/ble_adv/bthome/test_apps/main/bthome_test.c。

总结

bthome/dimmer示例完整展示了"事件触发广播 + 加密载荷 + 低功耗休眠"三合一的 BLE 外设实现范式:

  • 协议层:复用bthome组件的载荷构造、加密与设备信息编码能力,仅需少量 API 即可产出符合 BTHome v2 规范的广播包;
  • 传输层ble_hci组件绕过完整 BLE 协议栈,以最精简的方式管理非连接广播,配合 300 ms 定时器实现"有事件才广播";
  • 低功耗层:DFS + tickless idle + light sleep 外设掉电 + BLE 控制器睡眠 + RTC IO 唤醒层层叠加,并针对 ESP32-H4 做了外置 32 kHz 晶振的专项优化。

该示例不仅是 BTHome 调光器的参考实现,其"ISR 通知 + 任务编码 + 定时停播 + 唤醒校验"的工程结构,也适合作为其他事件驱动型低功耗 BLE 广播设备(传感器、遥控器、开关面板等)的开发蓝本。

【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询