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

[项目] 基于FireBeetle 2 ESP32-C5 智能环境监测灯光控制

[复制链接]
本帖最后由 腿毛利小五郎 于 2025-10-4 23:36 编辑

展示:
         基于FireBeetle 2 ESP32-C5 智能环境监测灯光控制图4
前言
      参考社区【指南】使用 MQTT 将 ESP32-C5 连接到 Home Assistant DF创客社区大佬的项目,在树莓派上使用mosquitto跑了MQTT服务
利用手头已有的硬件,简单实现灯带的控制系统,使用Freertos管理任务。
      FireBeetle 2 ESP32-C5的资源还有MQTT啥的也不介绍了,社区很多大佬都介绍了很多很多了。



硬件
        FireBeetle 2 ESP32-C5 + WS2812 灯带 + DHT22 温湿度传感器 + OLED 显示屏 + 按键
功能
  • MQTT + Wi-Fi 远程配置与控制
  • 实时温湿度采集上传
  • OLED 实时数据显示(温度、湿度、时间、Wi-Fi 信号、LED 模式)
  • 灯带多种显示模式可本地切换或远程控制
  • 参数持久化(Preferences)

环境搭建:

  • MQTT(mosquitto)

       我在树莓派ubuntu下使用docker部署的Home Assistant,其中Settings -> Devices & Services设置 -> 设备和服务)这一项并没有,如果有的话可以直接在其中安装mosquitto。为图简单,没有找为啥在里面找不到服务。直接使用apt install 去安装mosquitto
      
  1. #更新软件包索引
  2. sudo apt update
  3. #安装 Mosquitto 服务端与命令行客户端工具
  4. sudo apt install -y mosquitto mosquitto-clients
  5. #启用 Mosquitto 服务并立即启动(systemd 管理)
  6. sudo systemctl enable --now mosquitto
  7. #创建 MQTT 用户并生成密码文件(首次使用 -c 会新建文件)
  8. sudo mosquitto_passwd -c /etc/mosquitto/passwd mqttuser
  9. #按提示输入密码并确认(两次必须一致)
  10. #写入 Mosquitto 配置文件,启用认证并禁用匿名访问
  11. sudo tee /etc/mosquitto/conf.d/99-local.conf > /dev/null <<'EOF'
  12. listener 1883                            # 监听端口 1883(默认 MQTT)
  13. allow_anonymous false                    # 禁止匿名连接
  14. password_file /etc/mosquitto/passwd      # 指定密码文件路径
  15. persistence true                         # 启用持久化(保存订阅与消息)
  16. persistence_location /var/lib/mosquitto/ # 持久化数据存储目录
  17. log_dest file /var/log/mosquitto/mosquitto.log  # 日志输出到文件
  18. EOF
  19. #创建持久化与日志目录,确保权限正确
  20. sudo mkdir -p /var/lib/mosquitto /var/log/mosquitto /run/mosquitto
  21. sudo chown -R mosquitto:mosquitto /var/lib/mosquitto /var/log/mosquitto /run/mosquitto
  22. sudo chmod 750 /var/lib/mosquitto /var/log/mosquitto /run/mosquitto
  23. #重启 Mosquitto 服务以应用新配置
  24. sudo systemctl restart mosquitto
  25. #查看服务状态,确认是否启动成功
  26. sudo systemctl status mosquitto --no-pager --full
  27. #测试订阅:监听主题 test/topic 并显示消息内容
  28. mosquitto_sub -h 127.0.0.1 -p 1883 -u mqttuser -P '你的密码' -t test/topic -v
  29. #测试发布:向主题 test/topic 发送一条消息
  30. mosquitto_pub -h 127.0.0.1 -p 1883 -u mqttuser -P '你的密码' -t test/topic -m hello
复制代码
如果收到了类似如下显示,MQTT环境就好了 ,也可以用其他MQTT工具测试发布订阅,这里不用其他工具测试了基于FireBeetle 2 ESP32-C5 智能环境监测灯光控制图1
肯定也有MQTT服务的端口建立
基于FireBeetle 2 ESP32-C5 智能环境监测灯光控制图2

  • 代码库

        DHT22的库里面有异常值处理,我代码里没有对其值获取错误的处理,仅过滤了一下
        基于FireBeetle 2 ESP32-C5 智能环境监测灯光控制图3
       基于FireBeetle 2 ESP32-C5 智能环境监测灯光控制图5
       基于FireBeetle 2 ESP32-C5 智能环境监测灯光控制图6
       其他库都是官方的   

业务逻辑
      1 系统启动与初始化
      设备上电后,会依次完成:
      加载上次保存的配置信息(如 Wi-Fi、MQTT、灯带模式等);
      初始化传感器、OLED、LED 灯带、按键;
      连接 Wi-Fi,并建立 MQTT 通信(若配置可用);
      启动多个任务(如数据采集、显示刷新、MQTT 通信等)。

      2 数据采集与显示      传感器周期性采集温度和湿度;
      系统获取当前时间和 Wi-Fi 信号强度;
      OLED 显示温湿度、时间、Wi-Fi 信号以及当前灯带模式;
      若出现异常(如 Wi-Fi 断开)可通过 OLED 或日志反馈。

      3 灯带控制      用户可通过按键或远程指令切换灯带模式;
      各模式控制灯带显示不同效果(呼吸、跑马、彩虹等);
      当前模式会在 OLED 上同步显示。

      4 网络通信      通过 MQTT 或本地命令接收远程指令(如切换模式、修改参数等);
      上传传感器数据至服务器,实现远程监测与控制。

      5 参数持久化      所有重要参数(如 Wi-Fi 配置、MQTT 参数、灯带模式等)在修改后会写入 Flash 存储(Preferences 或 NVS)
      下次上电时自动读取,无需重新配置;
      保证设备掉电重启后仍能恢复原工作状态。

MQTT工具交互
      修改闪灯模式的:
基于FireBeetle 2 ESP32-C5 智能环境监测灯光控制图7
        修改wifi信息的,略
        修改上传间隔的,略

代码
  1. #include <Arduino.h>
  2. #include <Wire.h>
  3. #include <WiFi.h>
  4. #include <PubSubClient.h>
  5. #include <Preferences.h>
  6. #include "ESP32_WS2812_Lib.h"
  7. #include <U8g2lib.h>
  8. #include "DHT22.h"
  9. #include <ArduinoJson.h>
  10. // —— 硬件定义 ——
  11. #define LEDS_COUNT    7
  12. #define LEDS_PIN      2
  13. #define CHANNEL       0
  14. #define BUTTONPIN     7
  15. #define DHT_PIN       28
  16. #define SDA_PIN       9
  17. #define SCL_PIN       10
  18. // —— 默认 Wi-Fi 凭据 ——
  19. const char* WIFI_SSID     = "您的wifi账号";
  20. const char* WIFI_PASSWORD = "您的wifi密码";
  21. // —— MQTT 参数 ——
  22. const char* MQTT_SERVER   = "您的mqtt服务器";
  23. const int   MQTT_PORT     = 1883; // 您的mqtt端口
  24. const char* MQTT_USER     = "mqttuser"; //您的mqtt用户名
  25. const char* MQTT_PASS     = "123456"; // 您的mqtt密码
  26. // —— 上传间隔宏定义 ——
  27. #define UPLOAD_INTERVAL_MS 5000
  28. // —— 全局结构体定义 ——
  29. struct SensorInfo {
  30.   float temperature;
  31.   float humidity;
  32. };
  33. struct WiFiInfo {
  34.   String ssid;
  35.   String password;
  36.   int    rssi;
  37. };
  38. struct SystemConfig {
  39.   SensorInfo sensor;
  40.   WiFiInfo   wifi;
  41.   unsigned long uploadInterval;
  42. };
  43. // —— 全局对象 ——
  44. ESP32_WS2812 strip(LEDS_COUNT, LEDS_PIN, CHANNEL, TYPE_GRB);
  45. U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
  46. DHT22       dht22(DHT_PIN);
  47. Preferences prefs;
  48. WiFiClient  netClient;
  49. PubSubClient mqtt(netClient);
  50. SystemConfig gConfig;
  51. // —— 状态变量 ——
  52. int ledMode = 0;
  53. volatile bool buttonPressed = false;
  54. volatile unsigned long lastPress = 0;
  55. // —— 发布控制 ——
  56. unsigned long lastPublish = 0;
  57. // —— 任务句柄 ——
  58. TaskHandle_t ledTaskHandle;
  59. TaskHandle_t oledTaskHandle;
  60. TaskHandle_t dhtTaskHandle;
  61. TaskHandle_t wifiTaskHandle;
  62. TaskHandle_t mqttTaskHandle;
  63. // —— 清空灯带 ——
  64. void clearStrip() {
  65.   for (int i = 0; i < strip.getLedCount(); i++) {
  66.     strip.setLedColorData(i, 0, 0, 0);
  67.   }
  68.   strip.show();
  69. }
  70. // —— 按键中断 ——
  71. void IRAM_ATTR buttonISR() {
  72.   unsigned long now = millis();
  73.   if (now - lastPress > 200) {
  74.     buttonPressed = true;
  75.     lastPress = now;
  76.   }
  77. }
  78. // —— Wi-Fi 连接函数 ——
  79. void connectWiFi(const String& ssid, const String& pass) {
  80.   if (ssid.isEmpty()) return;
  81.   Serial.printf("→ Wi-Fi connecting: %s\n", ssid.c_str());
  82.   WiFi.disconnect(true);
  83.   WiFi.begin(ssid.c_str(), pass.c_str());
  84.   unsigned long start = millis();
  85.   while (WiFi.status() != WL_CONNECTED && millis() - start < 10000) {
  86.     delay(200);
  87.   }
  88.   if (WiFi.status() == WL_CONNECTED) {
  89.     Serial.printf("→ Wi-Fi IP: %s\n", WiFi.localIP().toString().c_str());
  90.   } else {
  91.     Serial.println("→ Wi-Fi failed");
  92.   }
  93. }
  94. // —— MQTT 回调 ——
  95. void mqttCallback(char* topic, byte* payload, unsigned int length) {
  96.   String msg;
  97.   msg.reserve(length);
  98.   for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];
  99.   Serial.printf("← MQTT [%s]: %s\n", topic, msg.c_str());
  100.   String t = String(topic);
  101.   if (t == "devices/config") {
  102.     StaticJsonDocument<256> doc;
  103.     DeserializationError err = deserializeJson(doc, msg);
  104.     if (!err) {
  105.       if (doc.containsKey("ssid") && doc.containsKey("pass")) {
  106.         gConfig.wifi.ssid = doc["ssid"].as<String>();
  107.         gConfig.wifi.password = doc["pass"].as<String>();
  108.         prefs.putString("wifi_ssid", gConfig.wifi.ssid);
  109.         prefs.putString("wifi_pass", gConfig.wifi.password);
  110.         Serial.printf("→ Saved WiFi: %s / %s\n",
  111.                       gConfig.wifi.ssid.c_str(), gConfig.wifi.password.c_str());
  112.         connectWiFi(gConfig.wifi.ssid, gConfig.wifi.password);
  113.       }
  114.       if (doc.containsKey("interval")) {
  115.         gConfig.uploadInterval = doc["interval"].as<unsigned long>();
  116.         Serial.printf("→ Updated upload interval: %lu ms\n", gConfig.uploadInterval);
  117.       }
  118.       if (doc.containsKey("mode")) {
  119.         ledMode = doc["mode"].as<int>() % 4;
  120.         prefs.putInt("ledMode", ledMode);
  121.         Serial.printf("→ LED mode updated: %d\n", ledMode);
  122.       }
  123.     } else {
  124.       Serial.println("⚠️ JSON parse failed");
  125.     }
  126.   }
  127. }
  128. // —— MQTT 连接保持 ——
  129. void ensureMqttConnected() {
  130.   while (!mqtt.connected()) {
  131.     Serial.print("→ MQTT connect... ");
  132.     if (mqtt.connect("ESP32Client", MQTT_USER, MQTT_PASS)) {
  133.       Serial.println("OK");
  134.       mqtt.subscribe("devices/config");
  135.       Serial.println("→ Subscribed to devices/config");
  136.     } else {
  137.       int rc = mqtt.state();
  138.       Serial.printf("Failed rc=%d\n", rc);
  139.       delay(5000);
  140.     }
  141.   }
  142.   mqtt.loop();
  143. }
  144. // —— LED 任务 ——
  145. void ledTask(void* pv) {
  146.   const TickType_t interval = pdMS_TO_TICKS(20);
  147.   TickType_t nextWake = xTaskGetTickCount();
  148.   int flowIndex = 0, prevLed = -1, breathLevel = 0, breathDir = 1, rainPos = 0;
  149.   while (true) {
  150.     if (buttonPressed) {
  151.       buttonPressed = false;
  152.       ledMode = (ledMode + 1) % 4;
  153.       prefs.putInt("ledMode", ledMode);
  154.       Serial.printf("→ Button: LED mode %d\n", ledMode);
  155.     }
  156.     switch (ledMode) {
  157.       case 0:
  158.         clearStrip();
  159.         break;
  160.       case 1: {
  161.         int ci = (flowIndex / strip.getLedCount()) % 3;
  162.         uint8_t rgb[3] = {0}; rgb[ci] = 255;
  163.         int cur = flowIndex % strip.getLedCount();
  164.         if (prevLed >= 0) strip.setLedColorData(prevLed, 0,0,0);
  165.         strip.setLedColorData(cur, rgb[0],rgb[1],rgb[2]);
  166.         strip.show();
  167.         prevLed = cur; flowIndex++;
  168.         break;
  169.       }
  170.       case 2:
  171.         for (int i = 0; i < strip.getLedCount(); i++) {
  172.           uint8_t v = breathLevel;
  173.           strip.setLedColorData(i,
  174.             ((millis()/3000)%3)==0 ? v : 0,
  175.             ((millis()/3000)%3)==1 ? v : 0,
  176.             ((millis()/3000)%3)==2 ? v : 0);
  177.         }
  178.         strip.show();
  179.         breathLevel += breathDir;
  180.         if (breathLevel == 0 || breathLevel == 255) breathDir = -breathDir;
  181.         break;
  182.       case 3:
  183.         for (int i = 0; i < strip.getLedCount(); i++) {
  184.           int pos = (i * 256 / strip.getLedCount() + rainPos) & 0xFF;
  185.           strip.setLedColorData(i, strip.Wheel(pos));
  186.         }
  187.         strip.show();
  188.         rainPos++;
  189.         break;
  190.     }
  191.     vTaskDelayUntil(&nextWake, interval);
  192.   }
  193. }
  194. // —— OLED 任务 ——
  195. void oledTask(void* pv) {
  196.   const TickType_t interval = pdMS_TO_TICKS(1000);
  197.   TickType_t nextWake = xTaskGetTickCount();
  198.   struct tm ti;
  199.   const char* modeName[] = {
  200.     "Close",
  201.     "blink",
  202.     "breath",
  203.     "Rainbow"
  204.   };
  205.   while (true) {
  206.     if (getLocalTime(&ti)) {
  207.       u8g2.firstPage();
  208.       do {
  209.         u8g2.setFont(u8g2_font_7x14_tf);
  210.         u8g2.setCursor(5, 14);
  211.         u8g2.print("System Show State");
  212.         
  213.         // 传感器数据
  214.         u8g2.setFont(u8g2_font_6x12_tf);
  215.         u8g2.setCursor(0, 30);
  216.         u8g2.printf("Tem: %.1fC", gConfig.sensor.temperature);
  217.         u8g2.setCursor(65, 30);
  218.         u8g2.printf("Hum: %.1f%%", gConfig.sensor.humidity);
  219.         u8g2.setCursor(0, 42);
  220.         u8g2.printf("LED Mode:%s", modeName[ledMode]);
  221.         // WiFi 信号
  222.         u8g2.setCursor(0, 54);
  223.         u8g2.printf("RSSI:%ddBm", WiFi.RSSI());
  224.         // 时间
  225.         u8g2.setFont(u8g2_font_6x10_tf);
  226.         u8g2.setCursor(40, 64);
  227.         u8g2.printf("%02d:%02d:%02d", ti.tm_hour, ti.tm_min, ti.tm_sec);
  228.         // 分隔线
  229.         u8g2.drawLine(0, 16, 128, 16);
  230.       } while(u8g2.nextPage());
  231.     }
  232.     vTaskDelayUntil(&nextWake, interval);
  233.   }
  234. }
  235. // —— DHT22 任务 ——
  236. void dhtTask(void* pv) {
  237.   const TickType_t interval = pdMS_TO_TICKS(2000);
  238.   TickType_t nextWake = xTaskGetTickCount();
  239.   while (true) {
  240.     vTaskDelayUntil(&nextWake, interval);
  241.     float t = dht22.getTemperature();
  242.     float h = dht22.getHumidity();
  243.     if (fabs(t) <= 100.0) gConfig.sensor.temperature = t;
  244.     if (fabs(h) <= 100.0) gConfig.sensor.humidity = h;
  245.   }
  246. }
  247. // —— Wi-Fi & NTP 任务 ——
  248. void wifiTask(void* pv) {
  249.   const TickType_t retry = pdMS_TO_TICKS(5000);
  250.   bool ntpDone = false;
  251.   connectWiFi(gConfig.wifi.ssid, gConfig.wifi.password);
  252.   while (true) {
  253.     if (WiFi.status() != WL_CONNECTED) {
  254.       Serial.println("→ Wi-Fi lost, retry...");
  255.       connectWiFi(gConfig.wifi.ssid, gConfig.wifi.password);
  256.       vTaskDelay(retry);
  257.     } else {
  258.       if (!ntpDone) {
  259.         configTime(8*3600, 0, "pool.ntp.org", "ntp.aliyun.com");
  260.         Serial.println("→ NTP sync");
  261.         ntpDone = true;
  262.       }
  263.       vTaskDelay(pdMS_TO_TICKS(10000));
  264.     }
  265.   }
  266. }
  267. // —— MQTT 任务 ——
  268. void mqttTask(void* pv) {
  269.   mqtt.setServer(MQTT_SERVER, MQTT_PORT);
  270.   mqtt.setCallback(mqttCallback);
  271.   while (true) {
  272.     if (WiFi.status() == WL_CONNECTED) {
  273.       ensureMqttConnected();
  274.       unsigned long now = millis();
  275.       if (now - lastPublish > gConfig.uploadInterval) {
  276.         lastPublish = now;
  277.         gConfig.wifi.rssi = WiFi.RSSI();
  278.         // 构造 JSON 上传包
  279.         StaticJsonDocument<256> doc;
  280.         doc["temp"] = gConfig.sensor.temperature;
  281.         doc["hum"]  = gConfig.sensor.humidity;
  282.         doc["rssi"] = gConfig.wifi.rssi;
  283.         String payload;
  284.         serializeJson(doc, payload);
  285.         mqtt.publish("devices/status", payload.c_str());
  286.         Serial.printf("→ Pub JSON: %s\n", payload.c_str());
  287.       }
  288.     }
  289.     vTaskDelay(pdMS_TO_TICKS(100));
  290.   }
  291. }
  292. // —— setup ——
  293. void setup() {
  294.   Serial.begin(115200);
  295.   Wire.begin(SDA_PIN, SCL_PIN);
  296.   prefs.begin("wifi_cfg", false);
  297.   gConfig.sensor.temperature = 0;
  298.   gConfig.sensor.humidity = 0;
  299.   gConfig.uploadInterval = UPLOAD_INTERVAL_MS;
  300.   gConfig.wifi.ssid = prefs.getString("wifi_ssid", WIFI_SSID);
  301.   gConfig.wifi.password = prefs.getString("wifi_pass", WIFI_PASSWORD);
  302.   strip.begin();
  303.   strip.setBrightness(30);
  304.   u8g2.begin();
  305.   ledMode = prefs.getInt("ledMode", 0);
  306.   Serial.printf("→ Start: LED mode %d\n", ledMode);
  307.   pinMode(BUTTONPIN, INPUT_PULLUP);
  308.   attachInterrupt(BUTTONPIN, buttonISR, FALLING);
  309.   xTaskCreate(ledTask,  "LED",   2048, NULL, 1, &ledTaskHandle);
  310.   xTaskCreate(oledTask, "OLED",  2048, NULL, 1, &oledTaskHandle);
  311.   xTaskCreate(dhtTask,  "DHT22", 2048, NULL, 1, &dhtTaskHandle);
  312.   xTaskCreate(wifiTask, "WiFi",  4096, NULL, 1, &wifiTaskHandle);
  313.   xTaskCreate(mqttTask, "MQTT",  4096, NULL, 1, &mqttTaskHandle);
  314. }
  315. // —— loop ——
  316. void loop() {
  317.   delay(1000);
  318. }
复制代码

视频:https://b23.tv/Mkv6fhc

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

本版积分规则

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

硬件清单

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

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

mail