ESP32-S3 控制 16 颗 WS2812B 灯环:按键切换、亮度调节与断电记忆
ESP32-S3 控制 16 颗 WS2812B 灯环:按键切换、亮度调节与断电记忆
最近我用 ESP32-S3 做了一个 16 颗 WS2812B 灯珠组成的灯环控制器。
最开始的目标只是把灯环点亮,后面逐步加入了按键切换、亮度调节、11 种灯效和断电记忆。项目规模不大,但包含了 GPIO 输入、单总线灯珠控制、按键状态判断、参数持久化和供电稳定处理,很适合作为一个完整的 ESP32-S3 入门项目。
本文记录硬件连接、程序结构和开发过程中遇到的问题。
一、最终实现的功能
- ESP32-S3 控制 16 颗 WS2812B 灯环;
- 短按一个按键切换灯效;
- 长按同一个按键切换亮度;
- 亮度分为 30、60、100、160、255 五档;
- 使用 Preferences 将灯效和亮度保存到 NVS;
- 断电后重新上电,恢复上一次灯效和亮度;
- 支持 11 种灯效;
- ESP32-S3 与灯环共用外部 5V 电源。
二、硬件清单
| 硬件 | 说明 |
|---|---|
| ESP32-S3 开发板 | 主控制器 |
| WS2812B 16 灯珠灯环 | 5V 可编程 RGB 灯珠 |
| 5V 电源 | 本项目使用 5V 5A,给开发板和灯环供电 |
| 330Ω 电阻 | 串联在数据线上,抑制边沿振铃和瞬态冲击 |
| 22μF/10V 电容 | 并联在灯环 5V 与 GND 之间 |
| 独立按键 | 短按切灯效,长按切亮度 |
| 杜邦线 | 原型连接 |
三、接线方式
核心连接如下:
ESP32-S3 GPIO4 -> 330Ω 电阻 -> WS2812B DIN
ESP32-S3 GPIO13 -> 按键一端
ESP32-S3 GND -> 按键另一端
5V 电源正极 -> WS2812B 5V
5V 电源正极 -> ESP32-S3 5V/VIN
5V 电源负极 -> WS2812B GND
5V 电源负极 -> ESP32-S3 GND
22μF 电容并联在灯环输入端的 5V 和 GND 之间。电解电容有极性,正极接 5V,负极接 GND。
这里有两个必须注意的地方:
- ESP32-S3 与 WS2812B 必须共地,否则数据线没有统一的电平参考;
- 外部 5V 只能接开发板的 5V/VIN,不能接 3V3 引脚。
四、为什么数据线上串联 330Ω 电阻
ESP32-S3 输出的数字信号边沿很快。数据线、杜邦线和灯板输入端共同构成一个非理想传输网络,边沿处可能产生振铃、反射和瞬态电流。
在 GPIO 与 WS2812B DIN 之间串联 330Ω 电阻,可以适当降低边沿速度并限制瞬态电流。它不会把正常数字信号简单地“分压成固定电压”,因为 WS2812B 的数据输入端是高阻负载。
电阻应尽量靠近第一颗灯珠的 DIN 输入端放置。
五、为什么在灯环输入端增加电容
WS2812B 在亮度和颜色快速变化时,瞬时电流也会变化。如果电源线较长、线径偏细或者接触不稳定,灯环输入端可能出现电压波动,表现为随机闪烁、颜色异常或者灯效抖动。
本项目在灯环电源输入端并联了 22μF/10V 电容,用于缓冲局部电压变化。实际使用中,加入电容后灯效明显稳定。
需要注意:电容不能代替合格的电源和可靠的共地连接。灯珠数量更多、线更长时,仍需要重新评估电源容量、线径和多点供电。
六、使用 Adafruit_NeoPixel 控制灯环
Arduino 环境中可以直接使用 Adafruit_NeoPixel 库。它帮助我们完成 WS2812B 所需的精确时序输出,不需要在业务代码里手动翻转 GPIO。
核心初始化形式如下:
#include <Adafruit_NeoPixel.h>
constexpr uint8_t LED_PIN = 4;
constexpr uint16_t LED_COUNT = 16;
Adafruit_NeoPixel pixels(
LED_COUNT,
LED_PIN,
NEO_GRB + NEO_KHZ800
);
void setup() {
pixels.begin();
pixels.clear();
pixels.setBrightness(100);
pixels.show();
}
NEO_GRB 表示灯珠按 GRB 顺序解释三个颜色通道,NEO_KHZ800 表示使用常见的 800kHz WS2812B 时序。
七、一个按键实现短按和长按
按键使用 INPUT_PULLUP:未按下时读取 HIGH,按下后与 GND 接通,读取 LOW。
程序需要记录按下时刻和释放时刻:
constexpr uint8_t BUTTON_PIN = 13;
constexpr uint32_t LONG_PRESS_MS = 800;
uint32_t pressStartMs = 0;
bool buttonWasDown = false;
void updateButton() {
bool buttonDown = digitalRead(BUTTON_PIN) == LOW;
if (buttonDown && !buttonWasDown) {
pressStartMs = millis();
}
if (!buttonDown && buttonWasDown) {
uint32_t heldMs = millis() - pressStartMs;
if (heldMs >= LONG_PRESS_MS) {
nextBrightness();
} else {
nextEffect();
}
}
buttonWasDown = buttonDown;
}
实际工程还需要加入 20~50ms 左右的软件消抖,防止一次机械按键动作被识别成多次点击。
八、五档亮度设计
亮度采用固定档位,而不是连续调节:
const uint8_t brightnessTable[] = {30, 60, 100, 160, 255};
长按按键后,亮度索引循环加一:
brightnessIndex = (brightnessIndex + 1) % 5;
pixels.setBrightness(brightnessTable[brightnessIndex]);
pixels.show();
固定档位的好处是:不需要额外屏幕和电位器,单按键也能快速选择常用亮度。
九、使用 Preferences 保存灯效和亮度
ESP32 Arduino 框架提供了 Preferences 类,可以将少量配置保存到芯片的 NVS 非易失性存储中。
#include <Preferences.h>
Preferences preferences;
void loadSettings() {
preferences.begin("ws2812", false);
currentMode = preferences.getUChar("mode", MODE_RAINBOW);
brightnessIndex = preferences.getUChar("brightness", 2);
if (currentMode >= MODE_COUNT) {
currentMode = MODE_RAINBOW;
}
if (brightnessIndex >= 5) {
brightnessIndex = 2;
}
}
MODE_RAINBOW 是键不存在时使用的默认值。第一次运行或 NVS 中尚未保存 mode 时,程序就从彩虹模式开始。
切换灯效和亮度后保存:
preferences.putUChar("mode", currentMode);
preferences.putUChar("brightness", brightnessIndex);
不要在每一帧动画中写 NVS。Flash 擦写次数有限,只在用户确实改变配置后写入即可。
十、当前 11 种灯效
项目中包含以下模式:
- Rainbow:彩虹流动;
- Breath:呼吸灯;
- Color Wipe:逐颗点亮;
- Comet:彗星拖尾;
- Theater:剧院追逐;
- Sparkle:随机闪烁;
- Solid:纯色切换;
- Ring Chase:双点环形追逐;
- Scanner:往返扫描;
- Color Wave:彩色波纹;
- Fireworks:烟花闪烁。
灯带和灯环的物理结构不同。线性灯带适合单向流动,灯环没有明显起点和终点,更适合旋转、追逐和周期换色。因此部分效果针对 16 颗环形结构做了调整,例如彗星转若干圈后换色、双点相向追逐等。
十一、开发过程中遇到的问题
1. 开发板型号选错
普通 ESP32 与 ESP32-S3 是不同芯片。Arduino IDE 中,普通 ESP32 一般选择 ESP32 Dev Module,ESP32-S3 则选择 ESP32S3 Dev Module。选错后可能正常编译,但上传时提示芯片型号不匹配。
2. 只关注程序,忽略供电
WS2812B 是 5V 灯珠。灯珠数量增加或亮度升高后,电源能力、线材压降和共地质量都会直接影响稳定性。软件排查之前,应先确认灯环端的实际电压。
3. 灯效速度不适合灯环
同样的延时参数放在不同数量和不同形状的灯珠上,视觉效果会不同。跑马灯过快会只剩闪烁感,彗星拖尾过短又看不出层次。动画参数需要结合真实结构调节。
十二、后续可以怎样扩展
- 增加 Wi-Fi 网页控制;
- 增加手机调色页面;
- 接入麦克风做音乐律动;
- 增加红外遥控;
- 增加 OLED 显示当前模式;
- 支持多路灯带输出;
- 重新设计 PCB,整合电源、接口和按键。
总结
这个项目真正有价值的地方,不只是点亮 16 颗灯珠,而是把多个常见的嵌入式模块串成了一条完整链路:
按键输入 -> 状态判断 -> 灯效输出 -> 亮度控制 -> 参数保存 -> 断电恢复
项目硬件简单、反馈直观,又能自然引出供电、时序、状态机 等知识,很适合作为 ESP32-S3 的入门实践。
作者:白搞电子
更多推荐


所有评论(0)