本帖最后由 云天 于 2026-2-23 11:22 编辑
玩转FireBeetle ESP32-C3:从WiFi配网到低功耗,打造你的智能LED控制器
本文通过一个完整的项目实例,带你体验FireBeetle ESP32-C3的三大核心特性:一键WiFi配网、FreeRTOS多任务、深度睡眠低功耗。我们将用网页控制12颗WS2812B灯珠,实现多种炫彩效果,并让设备在无人操作时自动休眠,极大延长电池寿命。
【引言】
FireBeetle ESP32-C3(DFR0868)是一款基于RISC-V架构的超小体积物联网控制器,板载WiFi和Bluetooth 5 (LE),并集成了锂电池充电管理。它虽然身材小巧,但能力强大:支持WiFi配网、FreeRTOS实时操作系统,以及深度睡眠模式,非常适合制作电池供电的无线智能设备。
本次项目我们将制作一个**智能LED灯环控制器**,通过手机或电脑浏览器访问ESP32-C3生成的网页,即可调节灯带的颜色、亮度,切换彩虹、呼吸、流星等多种效果。更重要的是,当长时间无操作时,设备会自动进入深度睡眠,功耗低至微安级别,唤醒后能瞬间恢复之前的状态。
在实现过程中,我们将深入体验FireBeetle ESP32-C3的三个核心特性:
**WiFi配网**:使用`WiFiManager`库,无需硬编码WiFi密码,手机扫码即可配置。
**FreeRTOS**:利用ESP32原生支持的FreeRTOS,将LED效果更新和网页后台分离为独立任务,保证响应流畅。
**Deep Sleep**:通过RTC内存保存状态,并配置定时器自动唤醒,实现超低功耗待机。
【硬件准备】
FireBeetle ESP32-C3 主控板 ×1
WS2812B 12位LED灯环(或其他任意数量的可寻址灯带) ×1
3.7V锂电池 ×1(可选,用于演示低功耗)
杜邦线若干
如果使用电池供电,建议准备一个电源开关
接线非常简单:
灯带 **VCC** → 锂电池正极(或ESP32-C3的3.3V,但WS2812B在3.3V下亮度稍低,推荐直接用电池)
灯带 **GND** → ESP32-C3 **GND**(必须共地)
灯带 **DIN** → ESP32-C3 **GPIO5**(可根据需要修改)
注意:如果电池电压高于5V,需加稳压模块。本项目使用3.7V锂电池,可直接为Beetle ESP32-C3的VIN供电,同时为灯带供电(部分WS2812B在3.7V下仍可工作,但亮度会稍低;如需满亮度,建议使用5V升压模块)。Beetle ESP32-C3板载TP4057充电管理芯片,插入USB即可为电池充电,Charge指示灯清晰显示状态。
【软件准备】
Arduino IDE 环境配置
1. 安装ESP32开发板支持包:
打开Arduino IDE,进入`文件`→`首选项`,在“附加开发板管理器网址”中添加:
`https://raw.githubusercontent.co ... e_esp32_index.json`
然后打开`工具`→`开发板`→`开发板管理器`,搜索`esp32`,安装最新版本(推荐2.0.17+)。
2. 选择开发板:`工具`→`开发板`→`ESP32 Arduino`→`ESP32C3 Dev Module`。
3. **关键设置**:为了使用USB串口打印,必须将`USB CDC On Boot`设置为`Enabled`。
安装所需库
在`项目`→`加载库`→`管理库`中搜索并安装:
**WiFiManager** by tzapu(用于一键配网)
**ESPUI** by s00500(用于生成网页控制界面)
**FastLED** by Daniel Garcia(用于驱动LED灯带)
此外,我们还会用到ESP32内置的FreeRTOS和深度睡眠库,这些无需额外安装。
【三大特性详解】
WiFi配网:告别硬编码密码
传统IoT项目通常需要将WiFi密码写在代码里,一旦更换网络就得重新编译烧录。而`WiFiManager`库彻底解决了这个问题:首次运行时,ESP32会创建一个名为`ESP32-C3-Config`的WiFi热点,手机连接后会自动弹出配置页面,选择家里的WiFi并输入密码即可。之后每次启动,它会自动连接保存的网络,无需再次配置。
在代码中,我们只需几行:
- WiFiManager wifiManager;
- wifiManager.setConfigPortalTimeout(180);
- if (!wifiManager.autoConnect("ESP32-C3-Config")) {
- Serial.println("配网失败,重启...");
- ESP.restart();
- }
复制代码
当设备无法连接(例如首次使用或密码错误)时,它会自动回退到配网模式,非常智能。
FreeRTOS:多任务让程序更清晰
ESP32的Arduino核心底层已经运行了FreeRTOS,我们可以直接创建自己的任务。本项目分为两个任务:
**LED任务**:负责周期性更新LED效果,接收来自网页的命令,并监控空闲超时。
**主任务**(即`loop()`所在的默认任务):处理ESPUI的后台事务。
任务间通过**队列**传递控制命令,避免了共享变量冲突。例如,当用户在网页上拖动滑块时,回调函数会将新的颜色值放入队列,LED任务从队列中取出并立即更新效果。这种方式既安全又高效。
创建任务的代码:
- xTaskCreatePinnedToCore(
- ledTask, // 任务函数
- "LED Task", // 任务名
- 4096, // 堆栈大小
- NULL, // 参数
- 1, // 优先级(数字越大优先级越高)
- NULL, // 任务句柄
- 1 // 运行核心(ESP32-C3单核,填0或1均可)
- );
复制代码 任务函数内部使用`vTaskDelayUntil`实现精确的周期控制,取代`delay()`,避免阻塞。
Deep Sleep:微安级功耗的秘密
对于电池供电设备,低功耗至关重要。ESP32-C3的深度睡眠模式功耗仅约5μA,同时可以保留RTC内存中的数据。我们利用`RTC_DATA_ATTR`修饰变量,使其在睡眠期间不丢失。
本项目设置了一个**空闲超时**:如果30秒内没有收到任何网页控制命令,设备自动进入深度睡眠。唤醒方式采用**定时器唤醒**,每10秒唤醒一次(可以根据实际需求调整)。唤醒后设备从`setup()`重新执行,但RTC内存中的状态得以保留,因此可以快速恢复之前的LED效果并重新连接WiFi。
核心代码:
- void enterDeepSleep() {
- fill_solid(leds, NUM_LEDS, CRGB::Black); // 关闭LED
- FastLED.show();
- esp_sleep_enable_timer_wakeup(10 * 1000000ULL); // 10秒后唤醒
- WiFi.disconnect(true);
- WiFi.mode(WIFI_OFF);
- esp_deep_sleep_start();
- }
复制代码 唤醒后,我们可以通过`esp_sleep_get_wakeup_cause()`判断唤醒原因,如果是定时器唤醒,可以选择快速重新连网或保持离线。
【项目实战:网页控制LED灯】
硬件连接
按前文所述连接好硬件,确保共地。如果使用锂电池,将电池正极接ESP32-C3的`VIN`,负极接`GND`;灯带电源可直接取自电池正极(需注意灯带工作电压),或通过升压模块得到5V。
完整代码解析
下面给出完整代码,并对关键部分进行注释。请将代码上传至FireBeetle ESP32-C3。
- #include <WiFi.h>
- #include <WiFiManager.h> // 一键配网
- #include <ESPUI.h> // 网页界面
- #include <FastLED.h> // LED控制
- #include <freertos/FreeRTOS.h>
- #include <freertos/task.h>
- #include <freertos/queue.h>
- #include "esp_sleep.h" // 深度睡眠
- // ------------------- 硬件配置 -------------------
- const int LED_PIN = 10; // 板载LED(GPIO10)
- const int NUM_LEDS = 12; // 灯珠数量
- const int DATA_PIN = 5; // LED数据引脚
- CRGB leds[NUM_LEDS]; // LED数组
- // ------------------- 数据结构(用于队列和RTC内存)-------------------
- typedef struct {
- uint8_t red;
- uint8_t green;
- uint8_t blue;
- uint8_t brightness;
- uint8_t mode; // 0:静态,1:彩虹,2:呼吸,3:流星
- uint8_t speed; // 0-100
- } LedCommand;
- // RTC内存:保存唤醒后恢复的状态
- RTC_DATA_ATTR LedCommand rtcLastCommand = {255, 0, 0, 50, 0, 50};
- RTC_DATA_ATTR bool rtcWifiConnected = false;
- RTC_DATA_ATTR uint32_t rtcWakeupCount = 0;
- LedCommand currentCmd = rtcLastCommand; // 当前命令
- // ------------------- 队列句柄 -------------------
- QueueHandle_t commandQueue;
- // ------------------- UI控件ID(全局,供回调使用)-------------------
- uint16_t redSliderId, greenSliderId, blueSliderId;
- uint16_t brightnessSliderId;
- uint16_t modeSelectId;
- uint16_t speedSliderId;
- // ------------------- 函数声明 -------------------
- void enterDeepSleep();
- void ledTask(void *pvParameters);
- void onColorChange(Control *sender, int type);
- void onBrightnessChange(Control *sender, int type);
- void onModeChange(Control *sender, int type);
- void onSpeedChange(Control *sender, int type);
- // =================== UI回调函数 ===================
- void onColorChange(Control *sender, int type) {
- LedCommand cmd = currentCmd;
- if (sender->id == redSliderId) cmd.red = sender->value.toInt();
- else if (sender->id == greenSliderId) cmd.green = sender->value.toInt();
- else if (sender->id == blueSliderId) cmd.blue = sender->value.toInt();
- xQueueSend(commandQueue, &cmd, 0); // 发送到队列
- rtcLastCommand = cmd; // 保存到RTC内存
- }
- void onBrightnessChange(Control *sender, int type) {
- LedCommand cmd = currentCmd;
- cmd.brightness = sender->value.toInt();
- xQueueSend(commandQueue, &cmd, 0);
- rtcLastCommand = cmd;
- }
- void onModeChange(Control *sender, int type) {
- LedCommand cmd = currentCmd;
- cmd.mode = sender->value.toInt();
- xQueueSend(commandQueue, &cmd, 0);
- rtcLastCommand = cmd;
- }
- void onSpeedChange(Control *sender, int type) {
- LedCommand cmd = currentCmd;
- cmd.speed = sender->value.toInt();
- xQueueSend(commandQueue, &cmd, 0);
- rtcLastCommand = cmd;
- }
-
- // =================== LED任务 ===================
- void ledTask(void *pvParameters) {
- FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
- FastLED.setBrightness(currentCmd.brightness);
- uint8_t hue = 0;
- int breathStep = 0;
- bool breathDirection = true;
- int meteorPos = 0;
- int meteorDir = 1;
- CRGB meteorColor = CRGB::Red;
- TickType_t xLastWakeTime = xTaskGetTickCount();
- unsigned long lastActivity = millis(); // 最后活动时间,用于空闲检测
- while (1) {
- // 接收新命令(非阻塞)
- LedCommand newCmd;
- if (xQueueReceive(commandQueue, &newCmd, 0) == pdTRUE) {
- currentCmd = newCmd;
- FastLED.setBrightness(currentCmd.brightness);
- lastActivity = millis(); // 有操作,更新活动时间
- }
- // 根据模式更新LED
- switch (currentCmd.mode) {
- case 0: // 静态
- fill_solid(leds, NUM_LEDS, CRGB(currentCmd.red, currentCmd.green, currentCmd.blue));
- break;
- case 1: // 彩虹
- fill_rainbow(leds, NUM_LEDS, hue, 255 / NUM_LEDS);
- hue++;
- break;
- case 2: // 呼吸
- {
- if (breathDirection) {
- breathStep += 2;
- if (breathStep >= 255) { breathStep = 255; breathDirection = false; }
- } else {
- breathStep -= 2;
- if (breathStep <= 0) { breathStep = 0; breathDirection = true; }
- }
- uint8_t b = map(breathStep, 0, 255, 0, currentCmd.brightness);
- fill_solid(leds, NUM_LEDS, CRGB(currentCmd.red, currentCmd.green, currentCmd.blue));
- FastLED.setBrightness(b);
- }
- break;
- case 3: // 流星
- {
- for (int i = 0; i < NUM_LEDS; i++) leds[i].fadeToBlackBy(64);
- leds[meteorPos] = meteorColor;
- meteorPos += meteorDir;
- if (meteorPos >= NUM_LEDS || meteorPos < 0) {
- meteorDir = -meteorDir;
- meteorPos += meteorDir;
- meteorColor = CHSV(random(256), 255, 255);
- }
- }
- break;
- }
- if (currentCmd.mode != 2) {
- FastLED.setBrightness(currentCmd.brightness);
- }
- FastLED.show();
- // 空闲超时检测:30秒无操作进入深度睡眠
- if (millis() - lastActivity > 30000) {
- vTaskDelay(pdMS_TO_TICKS(100)); // 确保队列消息处理完
- enterDeepSleep();
- }
- // 根据速度计算更新间隔
- int delayMs = map(currentCmd.speed, 0, 100, 200, 10);
- vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(delayMs));
- }
- }
- // =================== 进入深度睡眠 ===================
- void enterDeepSleep() {
- Serial.println("准备进入深度睡眠,仅定时器唤醒...");
- fill_solid(leds, NUM_LEDS, CRGB::Black);
- FastLED.show();
- esp_sleep_enable_timer_wakeup(10 * 1000000ULL); // 10秒后唤醒
- WiFi.disconnect(true);
- WiFi.mode(WIFI_OFF);
- Serial.println("即将进入深度睡眠...");
- delay(100);
- esp_deep_sleep_start();
- }
- // =================== Arduino初始化 ===================
- void setup() {
- Serial.begin(115200);
- pinMode(LED_PIN, OUTPUT);
- digitalWrite(LED_PIN, LOW);
- // 打印唤醒原因
- esp_sleep_wakeup_cause_t wakeupCause = esp_sleep_get_wakeup_cause();
- rtcWakeupCount++;
- Serial.printf("唤醒次数: %d, 唤醒原因: %d\n", rtcWakeupCount, wakeupCause);
- if (wakeupCause == ESP_SLEEP_WAKEUP_TIMER) {
- Serial.println("由定时器唤醒");
- } else {
- Serial.println("首次启动或其他唤醒");
- }
- // 恢复保存的命令
- if (rtcLastCommand.red != 0 || rtcLastCommand.green != 0 || rtcLastCommand.blue != 0) {
- currentCmd = rtcLastCommand;
- }
- // 创建命令队列
- commandQueue = xQueueCreate(5, sizeof(LedCommand));
- // WiFi配网(如果之前已连接且未断开,可跳过)
- if (!rtcWifiConnected || WiFi.status() != WL_CONNECTED) {
- WiFiManager wifiManager;
- wifiManager.setConfigPortalTimeout(180);
- if (!wifiManager.autoConnect("ESP32-C3-Config")) {
- Serial.println("配网失败,重启...");
- ESP.restart();
- }
- rtcWifiConnected = true;
- }
- Serial.println("WiFi连接成功");
- Serial.print("IP地址: ");
- Serial.println(WiFi.localIP());
- digitalWrite(LED_PIN, HIGH); // 板载LED常亮表示WiFi已连接
- // 创建LED任务(固定到核心1)
- xTaskCreatePinnedToCore(
- ledTask,
- "LED Task",
- 4096,
- NULL,
- 1,
- NULL,
- 1
- );
- // ------------------- 构建ESPUI界面 -------------------
- redSliderId = ESPUI.addControl(ControlType::Slider, "红色", String(currentCmd.red), ControlColor::Alizarin, Control::noParent, onColorChange);
- greenSliderId = ESPUI.addControl(ControlType::Slider, "绿色", String(currentCmd.green), ControlColor::Emerald, Control::noParent, onColorChange);
- blueSliderId = ESPUI.addControl(ControlType::Slider, "蓝色", String(currentCmd.blue), ControlColor::Peterriver, Control::noParent, onColorChange);
- ESPUI.addControl(ControlType::Min, "", "0", ControlColor::None, redSliderId);
- ESPUI.addControl(ControlType::Max, "", "255", ControlColor::None, redSliderId);
- ESPUI.addControl(ControlType::Min, "", "0", ControlColor::None, greenSliderId);
- ESPUI.addControl(ControlType::Max, "", "255", ControlColor::None, greenSliderId);
- ESPUI.addControl(ControlType::Min, "", "0", ControlColor::None, blueSliderId);
- ESPUI.addControl(ControlType::Max, "", "255", ControlColor::None, blueSliderId);
- brightnessSliderId = ESPUI.addControl(ControlType::Slider, "亮度", String(currentCmd.brightness), ControlColor::Sunflower, Control::noParent, onBrightnessChange);
- ESPUI.addControl(ControlType::Min, "", "0", ControlColor::None, brightnessSliderId);
- ESPUI.addControl(ControlType::Max, "", "255", ControlColor::None, brightnessSliderId);
- modeSelectId = ESPUI.addControl(ControlType::Select, "效果模式", "静态", ControlColor::Wetasphalt, Control::noParent, onModeChange);
- ESPUI.addControl(ControlType::Option, "静态", "0", ControlColor::None, modeSelectId);
- ESPUI.addControl(ControlType::Option, "彩虹", "1", ControlColor::None, modeSelectId);
- ESPUI.addControl(ControlType::Option, "呼吸", "2", ControlColor::None, modeSelectId);
- ESPUI.addControl(ControlType::Option, "流星", "3", ControlColor::None, modeSelectId);
- speedSliderId = ESPUI.addControl(ControlType::Slider, "速度", String(currentCmd.speed), ControlColor::Carrot, Control::noParent, onSpeedChange);
- ESPUI.addControl(ControlType::Min, "", "0", ControlColor::None, speedSliderId);
- ESPUI.addControl(ControlType::Max, "", "100", ControlColor::None, speedSliderId);
- ESPUI.begin("LED控制器");
- }
- void loop() {
- // loop基本空闲,交给FreeRTOS调度
- vTaskDelay(pdMS_TO_TICKS(1000));
- }
复制代码
代码关键点说明
**WiFiManager配网**:`autoConnect("ESP32-C3-Config")`会创建一个开放热点,手机连接后自动弹出配置页面。若已有保存的WiFi信息,则直接尝试连接。
**队列通信**:`commandQueue`用于从UI回调传递命令到LED任务。回调函数运行在默认任务中,LED任务运行在独立任务,通过队列安全交换数据。
**LED效果**:使用FastLED库实现四种效果,通过`mode`切换。速度滑块控制更新间隔(10~200ms)。
**深度睡眠**:`lastActivity`记录最后收到命令的时间,超过30秒则调用`enterDeepSleep()`。睡眠前关闭LED、断开WiFi,仅保留定时器唤醒。唤醒后从`setup()`重新执行,但RTC内存中的`rtcLastCommand`保留了上次的状态,因此LED能恢复之前的效果。
**RTC内存**:`RTC_DATA_ATTR`修饰的变量在深度睡眠期间不会丢失。我们将最后命令保存下来,以便唤醒后恢复。
【效果演示与调试】
1. 上传代码后打开串口监视器(波特率115200),观察输出。
2. 首次运行时,ESP32-C3会创建名为`ESP32-C3-Config`的WiFi热点。用手机连接此热点,片刻后会自动弹出配置页面(若未弹出,可手动访问`192.168.4.1`)。选择你的家庭WiFi并输入密码。
3. 配网成功后,串口会打印设备获得的IP地址。在浏览器中输入该IP,即可看到控制界面。
4. 拖动滑块,LED灯环会实时变化;切换模式可看到不同效果。
5. 停止操作30秒后,设备进入深度睡眠,LED熄灭,串口停止输出。10秒后设备自动唤醒,重新连接WiFi,LED恢复之前的状态。你可以用手机再次访问网页,确认状态已保留。
小技巧:如果希望修改空闲超时时间,可以调整`lastActivity`判断中的`30000`(毫秒);唤醒间隔修改`esp_sleep_enable_timer_wakeup`的参数(微秒)。
【演示视频】
【总结与扩展】
通过这个项目,我们完整地体验了FireBeetle ESP32-C3的三个重要特性:
**WiFiManager**让设备配网变得无比简单,用户无需接触代码。
**FreeRTOS**使得程序结构清晰,任务间通信安全可靠。
**Deep Sleep**将待机功耗降至极致,非常适合电池供电的物联网节点。
在此基础上,你可以轻松扩展:
增加更多LED效果,如火焰、波浪等。
接入温湿度传感器,在网页上实时显示环境数据。
使用MQTT协议,实现远程云端控制。
添加外部按钮,结合唤醒,实现按键开机。
FireBeetle ESP32-C3虽小,却蕴藏着无限可能。希望本文能激发你的创作灵感,打造出更多有趣的物联网项目!
|