✔零知派(零知开源)是一个专为电子初学者/电子兴趣爱好者设计的开源软硬件平台,在硬件上提供超高性价比STM32系列开发板、物联网控制板。取消了Bootloader程序烧录,让开发重心从 "配置环境" 转移到 "创意实现",极大降低了技术门槛。零知开源编程软件,内置上千个覆盖多场景的示例代码,支持项目源码一键下载,项目文章在线浏览。零知派(零知开源)平台通过软硬件协同创新,让你的创意快速转化为实物,来动手试试吧!
✔访问零知实验室,获取更多实战项目和教程资源吧!
目录
[1.1 硬件清单](#1.1 硬件清单)
[1.2 接线方案表](#1.2 接线方案表)
[1.3 具体接线图](#1.3 具体接线图)
[1.4 接线实物图](#1.4 接线实物图)
[2.1 WS2812 驱动实现](#2.1 WS2812 驱动实现)
[2.2 BLE Mesh 控制逻辑](#2.2 BLE Mesh 控制逻辑)
[2.3 PIR 人体感应](#2.3 PIR 人体感应)
[2.4 硬件总开关](#2.4 硬件总开关)
[3.1 操作流程](#3.1 操作流程)
[3.2 视频演示](#3.2 视频演示)
[Q1:WS2812 初始化后显示乱码白光?](#Q1:WS2812 初始化后显示乱码白光?)
[Q2:PIR 触发后颜色恢复不对?](#Q2:PIR 触发后颜色恢复不对?)
项目概述
本项目基于零知ESP32 和BLE Mesh 技术,实现了一款智能灯节点。手机通过nRF Mesh App可远程控制灯的开关 与亮度 ,同时本地PIR人体感应器 能自动开灯并延时关灯。与上个版本不同的是,PIR人体感应器的优先级较高,当识别到有人时,会强制显示白光,并屏蔽手机所有指令。
项目难点及解决方案
问题描述:PIR、BLE、硬件按键三个控制源并发触发时,灯光颜色与开关状态互相覆盖,导致恢复结果错误
解决方案: 为每一层控制源建立独立的状态保存变量(s_saved_xxx / s_sw_saved_xxx),严格按优先级在回调入口处拦截低优先级指令,各层状态互不共用
一、系统接线部分
1.1 硬件清单
| 元器件 | 型号 | 数量 | 说明 |
|---|---|---|---|
| 主控板 | 零知ESP32(ESP32-WROOM-32) | 3 | 240MHz双核,内置BLE 5.0 |
| OLED显示屏 | SSD1306 0.96寸 128×64 | 3 | I2C接口,3.3V供电 |
| RGB彩灯模块 | WS2812灯珠+去耦电容模块 | 3 | 内置去耦电容 |
| 数据线 | USB Type-A to Micro-USB | 1 | 烧录用 |
| 杜邦线 | 公对母/母对母 | 若干 | 连接用 |
| 手机 | iOS/Android | 1 | 安装nRF Mesh App |
| 人体感应模块 | HC-SR505 人体感应模块 | 3 | 检测人体或动物的移动热源 |
| 扩展板 | 零知派 ESP32 通用扩展板 | 1 | 提供OLED接口和按键,节省接线时间 |
1.2 接线方案表
以下引脚定义严格依据
project_config.h中的宏定义,三台设备接线完全相同。
| 模块 | 模块引脚 | ESP32引脚 | 说明 |
|---|---|---|---|
| OLED SSD1306 | VCC | 3.3V | 注意:只能接3.3V |
| OLED SSD1306 | GND | GND | 接地 |
| OLED SSD1306 | SDA | GPIO 21 | I2C数据,对应OLED_SDA_GPIO |
| OLED SSD1306 | SCL | GPIO 22 | I2C时钟,对应OLED_SCL_GPIO |
| WS2812彩灯模块 | DIN | GPIO 5 | PWM控制,对应LED_GPIO |
| WS2812彩灯模块 | VCC | 3.3V | LED模块用3.3V供电 |
| WS2812彩灯模块 | GND | GND | 接地 |
| 人体感应模块 | VCC | 5V | 默认工作电压4.5~20V |
| 人体感应模块 | OUT | GPIO 18 | 输出引脚,对应PIR_GPIO |
| 人体感应模块 | GND | GND | 接地 |
| 总开关按键 | SW | GPIO 12 | 输入引脚,常开 |
1.3 具体接线图

OLED VCC务必接3.3V引脚,ESP32的5V引脚会损坏OLED、GPIO 21和GPIO 22之间无需外接上拉电阻;人体感应模块的正极+不能接3.3V,低于工作电压,模块输出引脚数据不正常;图中按键型号,弯钩相对的两个引脚默认是相连的,如果不确定,可以用万用表测试后再接线
1.4 接线实物图

二、核心代码讲解
本项目代码基于上篇文章新增了WS2812 彩灯的功能,核心代码仅针对新添加部分做讲解,更多可以回顾上篇文章
2.1 WS2812 驱动实现
原项目使用 LEDC PWM 驱动单色 LED,改为 WS2812 后需换用 RMT 外设,利用精确时序发送 24bit 颜色数据(顺序为 G → R → B)
RMT 时序参数
| 信号 | 时间 | Tick 数 (80MHz) |
|---|---|---|
| T0H(bit0 高电平) | 400 ns | 32 |
| T0L(bit0 低电平) | 850 ns | 68 |
| T1H(bit1 高电平) | 800 ns | 64 |
| T1L(bit1 低电平) | 450 ns | 36 |
| RES(复位低电平) | ≥50 μs | 4800 |
时钟务必设置为 80MHz(
resolution_hz = 80 * 1000 * 1000),否则时序错误,WS2812 显示乱码白光。
核心驱动代码
cpp
/* WS2812 时序 Tick 数(80MHz,1 tick = 12.5ns) */
#define WS2812_T0H_TICKS 32
#define WS2812_T0L_TICKS 68
#define WS2812_T1H_TICKS 64
#define WS2812_T1L_TICKS 36
#define WS2812_RES_TICKS 4800
/* 写入颜色到灯珠(WS2812 顺序:G R B) */
static void ws2812_write(uint8_t brightness)
{
uint8_t g = (uint16_t)s_g * brightness / 255;
uint8_t r = (uint16_t)s_r * brightness / 255;
uint8_t b = (uint16_t)s_b * brightness / 255;
uint8_t payload[3] = {g, r, b};
rmt_transmit_config_t tx_cfg = { .loop_count = 0 };
rmt_transmit(s_rmt_chan, s_encoder, payload, sizeof(payload), &tx_cfg);
rmt_tx_wait_all_done(s_rmt_chan, pdMS_TO_TICKS(10));
}
/* 渐变 Tick(在 FreeRTOS 任务中每 15ms 调用一次) */
void led_pwm_fade_tick(void)
{
if (s_current == s_target) return;
if (s_current < s_target) {
uint8_t d = s_target - s_current;
s_current += (d > FADE_STEP) ? FADE_STEP : d;
} else {
uint8_t d = s_current - s_target;
s_current -= (d > FADE_STEP) ? FADE_STEP : d;
}
ws2812_write(s_current);
}
2.2 BLE Mesh 控制逻辑
三档调色实现
将 Generic Level 模型的范围(
-32768 ~ 32767)平均分为三段,每段对应一种色调:
| Level 范围 | 色调 | RGB 值 | 效果 |
|---|---|---|---|
| -32768 ~ -10923 | 冷色调 | (100, 150, 255) | 蓝白冷光 |
| -10922 ~ 10922 | 自然暖白 | (255, 180, 80) | 暖白光 |
| 10923 ~ 32767 | 暖黄色 | (255, 220, 30) | 温暖黄光 |
cpp
static void apply_level_color(int16_t lv)
{
if (lv < -10923) {
led_pwm_set_color(100, 150, 255); // 冷色调
} else if (lv <= 10922) {
led_pwm_set_color(255, 180, 80); // 自然暖白
} else {
led_pwm_set_color(255, 220, 30); // 暖黄色
}
}
优先级控制逻辑
系统存在三层控制,优先级从高到低:
| 优先级 | 控制源 | 行为 |
|---|---|---|
| 🔴 最高 | 硬件总开关 | 关闭时屏蔽 BLE 和 PIR,强制关灯 |
| 🟡 中 | PIR 人体感应 | 有人强制白光,屏蔽手机所有指令 |
| 🟢 最低 | 手机 BLE 控制 | 正常开关 + 调色 |
2.3 PIR 人体感应
PIR 触发时保存当前灯光状态(开关 + 亮度 + 颜色),强制切换白光;人离开后完整恢复之前的状态。
cpp
static void pir_on_motion(void)
{
s_pir_active = true;
/* 保存当前状态 */
s_saved_led_on = g_local_led_on;
s_saved_brightness = g_local_brightness;
led_pwm_get_color(&s_saved_r, &s_saved_g, &s_saved_b);
/* 强制白色开灯 */
led_pwm_set_color(255, 255, 255);
uint8_t bri = g_local_brightness ? g_local_brightness : 200;
led_pwm_set(true, bri);
}
static void pir_on_idle(void)
{
s_pir_active = false;
/* 恢复之前的颜色和状态 */
led_pwm_set_color(s_saved_r, s_saved_g, s_saved_b);
led_pwm_set(s_saved_led_on, s_saved_led_on ? s_saved_brightness : 0);
g_local_led_on = s_saved_led_on;
g_local_brightness = s_saved_brightness;
}
2.4 硬件总开关
使用 FreeRTOS 任务轮询 GPIO,检测下降沿(按下瞬间)触发 Toggle,50ms 防抖。
cpp
// 检测下降沿(按下)
if (last_level == 1 && cur_level == 0) {
vTaskDelay(pdMS_TO_TICKS(50)); // 防抖
if (gpio_get_level(MASTER_SW_GPIO) == 0) {
s_master_on = !s_master_on;
if (!s_master_on) {
/* 关闭:强制关灯 */
led_pwm_set(false, 0);
} else {
/* 开启:恢复正常控制模式 */
led_pwm_set_color(255, 255, 255);
}
}
}
三、项目结果演示
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 App的配网全过程、分组控制下的WS2812三档色调切换(冷色调 / 暖白 / 暖黄)、OLED状态显示、PIR人体感应自动触发白光及离开后恢复原始灯光状态,以及硬件总开关一键切换系统启停。
四、常见问题解答(FAQ)
Q1: WS2812 初始化后显示乱码白光**?**
A:检查 RMT 时钟是否设为 80MHz,时序 Tick 数是否与上文一致。10MHz 时钟会导致时序放大 8 倍,WS2812 无法识别。
Q2: PIR 触发后颜色恢复不对**?**
A:确认
led_pwm_set()内部没有强制重置颜色为白色,颜色应只在
led_pwm_set_color()中修改。
**Q3:**按键抖动导致误触发?
A:轮询任务检测到下降沿后延迟 50ms 再次确认电平,确保稳定后才切换状态。
项目资源整合
ESP-BLE-MESH 架构: ble-mesh-architecture
BLE Mesh API: bluetooth/esp-ble-mesh