11浏览
查看: 11|回复: 0

[项目] Beetle ESP32-C3:从WiFi配网到低功耗,打造智能LED控制器...

[复制链接]
本帖最后由 云天 于 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并输入密码即可。之后每次启动,它会自动连接保存的网络,无需再次配置。

       在代码中,我们只需几行:

  1. WiFiManager wifiManager;
  2. wifiManager.setConfigPortalTimeout(180);
  3. if (!wifiManager.autoConnect("ESP32-C3-Config")) {
  4.     Serial.println("配网失败,重启...");
  5.     ESP.restart();
  6. }
复制代码
       Beetle ESP32-C3:从WiFi配网到低功耗,打造智能LED控制器...图1

       当设备无法连接(例如首次使用或密码错误)时,它会自动回退到配网模式,非常智能。
       FreeRTOS:多任务让程序更清晰
       ESP32的Arduino核心底层已经运行了FreeRTOS,我们可以直接创建自己的任务。本项目分为两个任务:
        **LED任务**:负责周期性更新LED效果,接收来自网页的命令,并监控空闲超时。
       **主任务**(即`loop()`所在的默认任务):处理ESPUI的后台事务。
       任务间通过**队列**传递控制命令,避免了共享变量冲突。例如,当用户在网页上拖动滑块时,回调函数会将新的颜色值放入队列,LED任务从队列中取出并立即更新效果。这种方式既安全又高效。
       创建任务的代码:
  1. xTaskCreatePinnedToCore(
  2.     ledTask,          // 任务函数
  3.     "LED Task",       // 任务名
  4.     4096,             // 堆栈大小
  5.     NULL,             // 参数
  6.     1,                // 优先级(数字越大优先级越高)
  7.     NULL,             // 任务句柄
  8.     1                 // 运行核心(ESP32-C3单核,填0或1均可)
  9. );
复制代码
      任务函数内部使用`vTaskDelayUntil`实现精确的周期控制,取代`delay()`,避免阻塞。
       Deep Sleep:微安级功耗的秘密
       对于电池供电设备,低功耗至关重要。ESP32-C3的深度睡眠模式功耗仅约5μA,同时可以保留RTC内存中的数据。我们利用`RTC_DATA_ATTR`修饰变量,使其在睡眠期间不丢失。
       本项目设置了一个**空闲超时**:如果30秒内没有收到任何网页控制命令,设备自动进入深度睡眠。唤醒方式采用**定时器唤醒**,每10秒唤醒一次(可以根据实际需求调整)。唤醒后设备从`setup()`重新执行,但RTC内存中的状态得以保留,因此可以快速恢复之前的LED效果并重新连接WiFi。
       核心代码:
  1. void enterDeepSleep() {
  2.     fill_solid(leds, NUM_LEDS, CRGB::Black); // 关闭LED
  3.     FastLED.show();
  4.     esp_sleep_enable_timer_wakeup(10 * 1000000ULL); // 10秒后唤醒
  5.     WiFi.disconnect(true);
  6.     WiFi.mode(WIFI_OFF);
  7.     esp_deep_sleep_start();
  8. }
复制代码
唤醒后,我们可以通过`esp_sleep_get_wakeup_cause()`判断唤醒原因,如果是定时器唤醒,可以选择快速重新连网或保持离线。

【项目实战:网页控制LED灯】

       硬件连接
       按前文所述连接好硬件,确保共地。如果使用锂电池,将电池正极接ESP32-C3的`VIN`,负极接`GND`;灯带电源可直接取自电池正极(需注意灯带工作电压),或通过升压模块得到5V。
       完整代码解析
       下面给出完整代码,并对关键部分进行注释。请将代码上传至FireBeetle ESP32-C3。
  1. #include <WiFi.h>
  2. #include <WiFiManager.h>          // 一键配网
  3. #include <ESPUI.h>                 // 网页界面
  4. #include <FastLED.h>               // LED控制
  5. #include <freertos/FreeRTOS.h>
  6. #include <freertos/task.h>
  7. #include <freertos/queue.h>
  8. #include "esp_sleep.h"             // 深度睡眠
  9. // ------------------- 硬件配置 -------------------
  10. const int LED_PIN = 10;             // 板载LED(GPIO10)
  11. const int NUM_LEDS = 12;             // 灯珠数量
  12. const int DATA_PIN = 5;              // LED数据引脚
  13. CRGB leds[NUM_LEDS];                 // LED数组
  14. // ------------------- 数据结构(用于队列和RTC内存)-------------------
  15. typedef struct {
  16.   uint8_t red;
  17.   uint8_t green;
  18.   uint8_t blue;
  19.   uint8_t brightness;
  20.   uint8_t mode;      // 0:静态,1:彩虹,2:呼吸,3:流星
  21.   uint8_t speed;     // 0-100
  22. } LedCommand;
  23. // RTC内存:保存唤醒后恢复的状态
  24. RTC_DATA_ATTR LedCommand rtcLastCommand = {255, 0, 0, 50, 0, 50};
  25. RTC_DATA_ATTR bool rtcWifiConnected = false;
  26. RTC_DATA_ATTR uint32_t rtcWakeupCount = 0;
  27. LedCommand currentCmd = rtcLastCommand;  // 当前命令
  28. // ------------------- 队列句柄 -------------------
  29. QueueHandle_t commandQueue;
  30. // ------------------- UI控件ID(全局,供回调使用)-------------------
  31. uint16_t redSliderId, greenSliderId, blueSliderId;
  32. uint16_t brightnessSliderId;
  33. uint16_t modeSelectId;
  34. uint16_t speedSliderId;
  35. // ------------------- 函数声明 -------------------
  36. void enterDeepSleep();
  37. void ledTask(void *pvParameters);
  38. void onColorChange(Control *sender, int type);
  39. void onBrightnessChange(Control *sender, int type);
  40. void onModeChange(Control *sender, int type);
  41. void onSpeedChange(Control *sender, int type);
  42. // =================== UI回调函数 ===================
  43. void onColorChange(Control *sender, int type) {
  44.   LedCommand cmd = currentCmd;
  45.   if (sender->id == redSliderId)   cmd.red = sender->value.toInt();
  46.   else if (sender->id == greenSliderId) cmd.green = sender->value.toInt();
  47.   else if (sender->id == blueSliderId)  cmd.blue = sender->value.toInt();
  48.   xQueueSend(commandQueue, &cmd, 0);      // 发送到队列
  49.   rtcLastCommand = cmd;                   // 保存到RTC内存
  50. }
  51. void onBrightnessChange(Control *sender, int type) {
  52.   LedCommand cmd = currentCmd;
  53.   cmd.brightness = sender->value.toInt();
  54.   xQueueSend(commandQueue, &cmd, 0);
  55.   rtcLastCommand = cmd;
  56. }
  57. void onModeChange(Control *sender, int type) {
  58.   LedCommand cmd = currentCmd;
  59.   cmd.mode = sender->value.toInt();
  60.   xQueueSend(commandQueue, &cmd, 0);
  61.   rtcLastCommand = cmd;
  62. }
  63. void onSpeedChange(Control *sender, int type) {
  64.   LedCommand cmd = currentCmd;
  65.   cmd.speed = sender->value.toInt();
  66.   xQueueSend(commandQueue, &cmd, 0);
  67.   rtcLastCommand = cmd;
  68. }
  69. // =================== LED任务 ===================
  70. void ledTask(void *pvParameters) {
  71.   FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
  72.   FastLED.setBrightness(currentCmd.brightness);
  73.   uint8_t hue = 0;
  74.   int breathStep = 0;
  75.   bool breathDirection = true;
  76.   int meteorPos = 0;
  77.   int meteorDir = 1;
  78.   CRGB meteorColor = CRGB::Red;
  79.   TickType_t xLastWakeTime = xTaskGetTickCount();
  80.   unsigned long lastActivity = millis();      // 最后活动时间,用于空闲检测
  81.   while (1) {
  82.     // 接收新命令(非阻塞)
  83.     LedCommand newCmd;
  84.     if (xQueueReceive(commandQueue, &newCmd, 0) == pdTRUE) {
  85.       currentCmd = newCmd;
  86.       FastLED.setBrightness(currentCmd.brightness);
  87.       lastActivity = millis();                // 有操作,更新活动时间
  88.     }
  89. // 根据模式更新LED
  90.     switch (currentCmd.mode) {
  91.       case 0: // 静态
  92.         fill_solid(leds, NUM_LEDS, CRGB(currentCmd.red, currentCmd.green, currentCmd.blue));
  93.         break;
  94.       case 1: // 彩虹
  95.         fill_rainbow(leds, NUM_LEDS, hue, 255 / NUM_LEDS);
  96.         hue++;
  97.         break;
  98.       case 2: // 呼吸
  99.         {
  100.           if (breathDirection) {
  101.             breathStep += 2;
  102.             if (breathStep >= 255) { breathStep = 255; breathDirection = false; }
  103.           } else {
  104.             breathStep -= 2;
  105.             if (breathStep <= 0) { breathStep = 0; breathDirection = true; }
  106.           }
  107.           uint8_t b = map(breathStep, 0, 255, 0, currentCmd.brightness);
  108.           fill_solid(leds, NUM_LEDS, CRGB(currentCmd.red, currentCmd.green, currentCmd.blue));
  109.           FastLED.setBrightness(b);
  110.         }
  111.         break;
  112.       case 3: // 流星
  113.         {
  114.           for (int i = 0; i < NUM_LEDS; i++) leds[i].fadeToBlackBy(64);
  115.           leds[meteorPos] = meteorColor;
  116.           meteorPos += meteorDir;
  117.           if (meteorPos >= NUM_LEDS || meteorPos < 0) {
  118.             meteorDir = -meteorDir;
  119.             meteorPos += meteorDir;
  120.             meteorColor = CHSV(random(256), 255, 255);
  121.           }
  122.         }
  123.         break;
  124.     }
  125.     if (currentCmd.mode != 2) {
  126.       FastLED.setBrightness(currentCmd.brightness);
  127.     }
  128.     FastLED.show();
  129.     // 空闲超时检测:30秒无操作进入深度睡眠
  130.     if (millis() - lastActivity > 30000) {
  131.       vTaskDelay(pdMS_TO_TICKS(100));   // 确保队列消息处理完
  132.       enterDeepSleep();
  133.     }
  134.     // 根据速度计算更新间隔
  135.     int delayMs = map(currentCmd.speed, 0, 100, 200, 10);
  136.     vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(delayMs));
  137.   }
  138. }
  139. // =================== 进入深度睡眠 ===================
  140. void enterDeepSleep() {
  141.   Serial.println("准备进入深度睡眠,仅定时器唤醒...");
  142.   fill_solid(leds, NUM_LEDS, CRGB::Black);
  143.   FastLED.show();
  144.   esp_sleep_enable_timer_wakeup(10 * 1000000ULL);   // 10秒后唤醒
  145.   WiFi.disconnect(true);
  146.   WiFi.mode(WIFI_OFF);
  147. Serial.println("即将进入深度睡眠...");
  148.   delay(100);
  149.   esp_deep_sleep_start();
  150. }
  151. // =================== Arduino初始化 ===================
  152. void setup() {
  153.   Serial.begin(115200);
  154.   pinMode(LED_PIN, OUTPUT);
  155.   digitalWrite(LED_PIN, LOW);
  156. // 打印唤醒原因
  157. esp_sleep_wakeup_cause_t wakeupCause = esp_sleep_get_wakeup_cause();
  158. rtcWakeupCount++;
  159. Serial.printf("唤醒次数: %d, 唤醒原因: %d\n", rtcWakeupCount, wakeupCause);
  160. if (wakeupCause == ESP_SLEEP_WAKEUP_TIMER) {
  161. Serial.println("由定时器唤醒");
  162. } else {
  163. Serial.println("首次启动或其他唤醒");
  164. }
  165. // 恢复保存的命令
  166. if (rtcLastCommand.red != 0 || rtcLastCommand.green != 0 || rtcLastCommand.blue != 0) {
  167. currentCmd = rtcLastCommand;
  168. }
  169. // 创建命令队列
  170. commandQueue = xQueueCreate(5, sizeof(LedCommand));
  171. // WiFi配网(如果之前已连接且未断开,可跳过)
  172. if (!rtcWifiConnected || WiFi.status() != WL_CONNECTED) {
  173. WiFiManager wifiManager;
  174. wifiManager.setConfigPortalTimeout(180);
  175. if (!wifiManager.autoConnect("ESP32-C3-Config")) {
  176. Serial.println("配网失败,重启...");
  177. ESP.restart();
  178. }
  179. rtcWifiConnected = true;
  180. }
  181. Serial.println("WiFi连接成功");
  182. Serial.print("IP地址: ");
  183. Serial.println(WiFi.localIP());
  184. digitalWrite(LED_PIN, HIGH);   // 板载LED常亮表示WiFi已连接
  185. // 创建LED任务(固定到核心1)
  186. xTaskCreatePinnedToCore(
  187. ledTask,
  188. "LED Task",
  189. 4096,
  190. NULL,
  191. 1,
  192. NULL,
  193. 1
  194. );
  195. // ------------------- 构建ESPUI界面 -------------------
  196. redSliderId = ESPUI.addControl(ControlType::Slider, "红色", String(currentCmd.red), ControlColor::Alizarin, Control::noParent, onColorChange);
  197. greenSliderId = ESPUI.addControl(ControlType::Slider, "绿色", String(currentCmd.green), ControlColor::Emerald, Control::noParent, onColorChange);
  198. blueSliderId = ESPUI.addControl(ControlType::Slider, "蓝色", String(currentCmd.blue), ControlColor::Peterriver, Control::noParent, onColorChange);
  199. ESPUI.addControl(ControlType::Min, "", "0", ControlColor::None, redSliderId);
  200. ESPUI.addControl(ControlType::Max, "", "255", ControlColor::None, redSliderId);
  201. ESPUI.addControl(ControlType::Min, "", "0", ControlColor::None, greenSliderId);
  202. ESPUI.addControl(ControlType::Max, "", "255", ControlColor::None, greenSliderId);
  203. ESPUI.addControl(ControlType::Min, "", "0", ControlColor::None, blueSliderId);
  204. ESPUI.addControl(ControlType::Max, "", "255", ControlColor::None, blueSliderId);
  205. brightnessSliderId = ESPUI.addControl(ControlType::Slider, "亮度", String(currentCmd.brightness), ControlColor::Sunflower, Control::noParent, onBrightnessChange);
  206. ESPUI.addControl(ControlType::Min, "", "0", ControlColor::None, brightnessSliderId);
  207. ESPUI.addControl(ControlType::Max, "", "255", ControlColor::None, brightnessSliderId);
  208. modeSelectId = ESPUI.addControl(ControlType::Select, "效果模式", "静态", ControlColor::Wetasphalt, Control::noParent, onModeChange);
  209. ESPUI.addControl(ControlType::Option, "静态", "0", ControlColor::None, modeSelectId);
  210. ESPUI.addControl(ControlType::Option, "彩虹", "1", ControlColor::None, modeSelectId);
  211. ESPUI.addControl(ControlType::Option, "呼吸", "2", ControlColor::None, modeSelectId);
  212. ESPUI.addControl(ControlType::Option, "流星", "3", ControlColor::None, modeSelectId);
  213. speedSliderId = ESPUI.addControl(ControlType::Slider, "速度", String(currentCmd.speed), ControlColor::Carrot, Control::noParent, onSpeedChange);
  214. ESPUI.addControl(ControlType::Min, "", "0", ControlColor::None, speedSliderId);
  215. ESPUI.addControl(ControlType::Max, "", "100", ControlColor::None, speedSliderId);
  216. ESPUI.begin("LED控制器");
  217. }
  218. void loop() {
  219. // loop基本空闲,交给FreeRTOS调度
  220. vTaskDelay(pdMS_TO_TICKS(1000));
  221. }
复制代码

       代码关键点说明

        **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`修饰的变量在深度睡眠期间不会丢失。我们将最后命令保存下来,以便唤醒后恢复。
Beetle ESP32-C3:从WiFi配网到低功耗,打造智能LED控制器...图2



Beetle ESP32-C3:从WiFi配网到低功耗,打造智能LED控制器...图3

【效果演示与调试】

       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`的参数(微秒)。
Beetle ESP32-C3:从WiFi配网到低功耗,打造智能LED控制器...图4



Beetle ESP32-C3:从WiFi配网到低功耗,打造智能LED控制器...图5


【演示视频】


【总结与扩展】

       通过这个项目,我们完整地体验了FireBeetle ESP32-C3的三个重要特性:
        **WiFiManager**让设备配网变得无比简单,用户无需接触代码。
        **FreeRTOS**使得程序结构清晰,任务间通信安全可靠。
       **Deep Sleep**将待机功耗降至极致,非常适合电池供电的物联网节点。

       在此基础上,你可以轻松扩展:
       增加更多LED效果,如火焰、波浪等。
       接入温湿度传感器,在网页上实时显示环境数据。
       使用MQTT协议,实现远程云端控制。
        添加外部按钮,结合唤醒,实现按键开机。

       FireBeetle ESP32-C3虽小,却蕴藏着无限可能。希望本文能激发你的创作灵感,打造出更多有趣的物联网项目!



您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2026 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail