零知ESP32——BLE Mesh蓝牙组网红外灯控系统

张开发
2026/5/30 20:31:46 15 分钟阅读
零知ESP32——BLE Mesh蓝牙组网红外灯控系统
✔零知派零知开源是一个专为电子初学者/电子兴趣爱好者设计的开源软硬件平台在硬件上提供超高性价比STM32系列开发板、物联网控制板。取消了Bootloader程序烧录让开发重心从 “配置环境” 转移到 “创意实现”极大降低了技术门槛。零知开源编程软件内置上千个覆盖多场景的示例代码支持项目源码一键下载项目文章在线浏览。零知派(零知开源)平台通过软硬件协同创新让你的创意快速转化为实物来动手试试吧✔访问零知实验室获取更多实战项目和教程资源吧www.lingzhilab.com目录一、系统接线部分1.1 硬件清单1.2 接线方案表1.3 具体接线图1.4 接线实物图二、核心代码讲解2.1 人体感应器初始化2.2 事件处理任务2.3 蓝牙组网回调函数三、项目结果演示3.1 操作流程3.2 视频演示四、常见问题解答FAQQ1初始化后灯光亮起后不熄灭Q2手机App无法扫描到设备项目概述本项目基于零知ESP32和BLE Mesh技术实现了一款智能灯节点。手机通过nRF Mesh App可远程控制灯的开关与亮度同时本地PIR人体感应器能自动开灯并延时关灯。系统采用PWM平滑渐变驱动LEDOLED实时显示配网状态、亮度及运动指示。项目模块化清晰兼顾远程便捷与本地智能适用于智能家居照明场景。项目难点及解决方案问题描述手机控制与人体感应的互斥逻辑解决方案引入全局标志s_pir_enabled和接口set_pir_enabled()手机“开灯”或 “调节亮度到非零” 时调用 set_pir_enabled(false) 禁用 PIR人体感应器。一、系统接线部分1.1 硬件清单元器件型号数量说明主控板零知ESP32ESP32-WROOM-323240MHz双核内置BLE 5.0OLED显示屏SSD1306 0.96寸 128×643I2C接口3.3V供电LED灯珠模块LED限流电阻模块3内置限流电阻数据线USB Type-A to Micro-USB1烧录用杜邦线公对母/母对母若干连接用手机iOS/Android1安装nRF Mesh App人体感应模块HC-SR505 人体感应模块3检测人体或动物的移动热源1.2 接线方案表以下引脚定义严格依据project_config.h中的宏定义三台设备接线完全相同。模块模块引脚ESP32引脚说明OLED SSD1306VCC3.3V注意只能接3.3VOLED SSD1306GNDGND接地OLED SSD1306SDAGPIO 21I2C数据对应OLED_SDA_GPIOOLED SSD1306SCLGPIO 22I2C时钟对应OLED_SCL_GPIOLED模块IN/SIGGPIO 5PWM控制对应LED_GPIOLED模块VCC3.3VLED模块用3.3V供电LED模块GNDGND接地人体感应模块VCC5V默认工作电压4.5~20V人体感应模块OUTGPIO 13输出引脚对应PIR_GPIO人体感应模块GNDGND接地1.3 具体接线图​OLED VCC务必接3.3V引脚ESP32的5V引脚会损坏OLED、GPIO 21和GPIO 22之间无需外接上拉电阻人体感应模块的正极不能接3.3V低于工作电压模块输出引脚数据不正常。1.4 接线实物图​二、核心代码讲解本项目代码基于上篇文章新增了人体传感控灯的功能核心代码仅针对新添加部分做讲解更多可以回顾上篇文章2.1 人体感应器初始化初始化人体感应模块配置引脚、触发电平和防抖延迟时间等esp_err_t pir_sensor_init(int gpio_num, uint8_t trigger_level, uint32_t debounce_ms, uint32_t hold_ms) { if (gpio_num GPIO_NUM_NC) return ESP_ERR_INVALID_ARG; s_gpio_pin gpio_num; s_trigger_level trigger_level; s_debounce_ms debounce_ms; s_hold_ms hold_ms; // 配置 GPIO 输入 gpio_config_t io_conf { .pin_bit_mask (1ULL s_gpio_pin), .mode GPIO_MODE_INPUT, .pull_up_en GPIO_PULLUP_ENABLE, .pull_down_en GPIO_PULLDOWN_DISABLE, .intr_type GPIO_INTR_ANYEDGE, }; esp_err_t ret gpio_config(io_conf); if (ret ! ESP_OK) return ret; // 创建事件队列 s_event_queue xQueueCreate(10, sizeof(uint32_t)); if (!s_event_queue) return ESP_ERR_NO_MEM; // 安装 GPIO ISR 服务 ret gpio_install_isr_service(0); if (ret ! ESP_OK ret ! ESP_ERR_INVALID_STATE) { // ESP_ERR_INVALID_STATE 表示已经安装过可以忽略 return ret; } // 添加中断处理 ret gpio_isr_handler_add(s_gpio_pin, pir_isr_handler, (void*)s_gpio_pin); if (ret ! ESP_OK) return ret; // 创建事件处理任务 xTaskCreate(pir_event_task, pir_event, 4096, NULL, 10, NULL); ESP_LOGI(TAG, Initialized on GPIO %d, trigger level %d, s_gpio_pin, s_trigger_level); return ESP_OK; }intr_type设置为GPIO_INTR_ANYEDGE双边沿如果是GPIO_INTR_POSEDGE 或者 GPIO_INTR_NEGEDGE人离开后不会触发下降沿中断LED也不会熄灭2.2 事件处理任务处理 PIR红外人体感应传感器事件static void pir_event_task(void *arg) { uint32_t io_num; while (1) { if (xQueueReceive(s_event_queue, io_num, portMAX_DELAY)) { int level gpio_get_level((int)io_num); bool triggered (level s_trigger_level); if (triggered !s_is_motion) { // 检测到触发先延时消抖 vTaskDelay(pdMS_TO_TICKS(s_debounce_ms)); if (gpio_get_level(s_gpio_pin) s_trigger_level) { s_is_motion true; ESP_LOGI(TAG, Motion detected); if (s_motion_cb) s_motion_cb(); } } else if (!triggered s_is_motion) { // 信号消失保持 hold_ms 时间再确认空闲 vTaskDelay(pdMS_TO_TICKS(s_hold_ms)); if (gpio_get_level(s_gpio_pin) ! s_trigger_level) { s_is_motion false; ESP_LOGI(TAG, Idle); if (s_idle_cb) s_idle_cb(); } } } } }对 PIR 传感器的信号进行消抖s_debounce_ms和保持时间判断hold_ms从而稳定地检测“有人移动”和“无人空闲状态”2.3 蓝牙组网回调函数static void mesh_generic_server_cb( esp_ble_mesh_generic_server_cb_event_t event, esp_ble_mesh_generic_server_cb_param_t *param) { if (event ! ESP_BLE_MESH_GENERIC_SERVER_STATE_CHANGE_EVT) return; uint32_t op param-ctx.recv_op; /* OnOff Set */ if (op ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET || op ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK) { bool on (param-value.state_change.onoff_set.onoff ! 0); g_local_led_on on; if (on) { uint8_t bri g_local_brightness ? g_local_brightness : 200; g_local_brightness bri; led_pwm_set(true, bri); set_pir_enabled(false); } else { led_pwm_set(false, 0); set_pir_enabled(true); } ESP_LOGI(TAG, OnOff SET: %s bri%d, PIR %s, on ? ON : OFF, g_local_brightness, on ? disabled : enabled); } /* Level SET - absolute value (individual node slider) */ if (op ESP_BLE_MESH_MODEL_OP_GEN_LEVEL_SET || op ESP_BLE_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK) { int16_t lv param-value.state_change.level_set.level; uint8_t bri level_to_bri(lv); g_local_brightness bri; g_local_led_on (bri 0); if (bri 0) { led_pwm_set(g_local_led_on, bri); set_pir_enabled(false); } else{ led_pwm_set(false, 0); set_pir_enabled(true); } ESP_LOGI(TAG, Level SET: lv%d bri%d (%d%%), lv, bri, bri*100/255); } }在“开灯”或者“调整亮度(不为0)”状态下设置set_pir_enabled(false)禁用PIR 自动功能在“关灯”时则打开三、项目结果演示3.1 操作流程编译与烧录①打开 main/project_config.h将 #define NODE_ID 0修改为对应设备编号0~2​②设置目标芯片点击底部任务栏Set Espressif Device Target设置IDF_TARGET芯片为ESP32​③烧录并打开串口监视器通过日志和操作数据进行调试​手机配网①打开nRF Mesh App → Network → 右上角“”扫描通过UUID第三字节00,01,02,03,04识别节点依次配网选择No OOB②配网成功后节点LED闪烁三次OLED显示变为已配网状态为每个节点的OnOff Server和Level Server绑定App Key 1​创建分组与订阅在Groups界面创建Node 0,1、Node 1,2、Node 0~2三个分组为每个节点的OnOff Server和Level Server订阅相应分组功能控制单灯控制在Network中选择节点使用ON/OFF和Level滑块分组控制在Groups中选择分组使用ON/OFF和/-按钮感应控制模拟人体走动触发灯光的开关3.2 视频演示零知派-ESP32 BLE Mesh蓝牙组网红外灯控系统演示本视频展示了 基于ESP32三节点的BLE MESH 红外灯控系统 的操作流程。包括三台ESP32在nRF Mesh iOS App的配网全过程、分组控制下的LED同步开关与PWM渐变调光效果OLED屏显示以及模拟人物走动触发灯的开关。四、常见问题解答FAQQ1初始化后灯光亮起后不熄灭A确认 “人体感应器” 的供电电压是否大于4.5V少于这个电压会导致传感器的引脚输出异常另外查看代码中的传感器的事件处理是否设置为GPIO_INTR_ANYEDGE双边沿如果不是当传感器引脚输出为 0 时并不会触发“关灯”操作Q2手机App无法扫描到设备A①确认ESP32已上电且串口日志显示 Open nRF Mesh App - Scanner - find ESP-BLE-MESH②iOS需要在系统设置中开启nRF Mesh的蓝牙权限③若该设备已被配网需要先在App中将其重置Reset Node才能重新扫描到。项目资源整合ESP-BLE-MESH 架构 ble-mesh-architectureBLE Mesh API bluetooth/esp-ble-mesh

更多文章