公告 欢迎您,我的朋友,目前开放免费注册,注册登录后查看更多内容!开通会员送积分,相应文档随便看,积分充值登录后个人中心内按需充值!

🔐 ESP32S3配网和指示灯源码

ESP32S3配网和指示灯ws2812三色指示灯

多种配网方式:串口配网,网络配网,蓝牙配网,热点配网等

ws2812三色指示灯,状态指示灯

后续会陆续更新最新代码

/*
* t_wifi.cpp — WiFi + NTP 校时 + 三级配网兜底(自包含,可单独烧录)
* 烧录:platformio run -e wifi -t upload
 *
* 配网三级兜底(凭据统一存 NVS-无电存储,不硬编码):
*   ① 串口:9IEDITWIFI:SSID,PASSWORD   / 9IPROV(进配网) / 9IRESETCONFIG(清空)
*   ② 网页:无凭据或连不上 → 开热点 ESP32-SETUP,手机连上后浏览器配网
*   ③ BLE/BluFi:后续接入(小程序),逻辑同样写入同一 NVS 列表
*   ④(下一步)MQTT:订阅指令主题,远程 9IEDITWIFI: 预配置到内部
 *
* 主/备逻辑:NVS 中凭据按优先级排序,下标 0 = 主(最后一次连成功者);
*           连接时用 WiFiMulti 按序尝试,连上自动 promote 到队首。
 *
* 状态灯(WS2812@IO48):连接中/掉线=红闪(异常色);联网成功=蓝常亮(唯一正常指示灯);打印时间=绿脉冲(可关)
 */
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include "pins.h"
#include "wifi_creds.h"
#include "prov_ap.h"

static WifiCreds creds;

// 开发期便利:NVS 为空时用这组预设“播种”;量产请把 DEV_SEED_CREDS 设为 0
#define DEV_SEED_CREDS 1
static const char *PRESET_SSID[] = {"aaa", "bbb", "huawei", "xiaomi", "iphone"};
static const char *PRESET_PASS[] = {"123456", "123456", "123456", "123456", "123456"};

static WiFiUDP ntpUDP;
static NTPClient timeClient(ntpUDP, "pool.ntp.org", 8 * 3600, 60000); // UTC+8

// ---------------- 状态指示灯(WS2812@IO48) ----------------
// 设计原则:
//   · 联网成功并正常运行 = 单一常亮指示灯(蓝),不闪烁 —— 这是唯一“正常状态灯”
//   · 仅网络异常(开机连接中 / 在线后掉线)才出现其他颜色(红 闪烁)
//   · 打印时间时叠加一次“绿色脉冲”(短暂事件提示,覆盖在常亮蓝之上;不需要可注释掉调用)
// ledTick() 是“状态机驱动”:被 loop / 连接循环反复调用,按当前 ledState 决定灯色。
enum LedState
{
  LED_CONNECTING,
  LED_ONLINE
};
static LedState ledState = LED_CONNECTING;
static unsigned long ledGreenUntil = 0;           // 绿色脉冲结束时刻(覆盖在状态灯之上)
static uint8_t ledBR = 255, ledBG = 0, ledBB = 0; // 异常时的闪烁基色(红)
static bool ledBlinkOn = false;
static unsigned long ledLastToggle = 0;

static void ledSet(uint8_t r, uint8_t g, uint8_t b) { neopixelWrite(PIN_WS2812, r, g, b); }

static void ledConnecting()
{
  ledState = LED_CONNECTING;
  ledBR = 255;
  ledBG = 0;
  ledBB = 0;
  ledLastToggle = 0;
  ledGreenUntil = 0;
}
static void ledOnline()
{
  ledState = LED_ONLINE;
  ledGreenUntil = 0;
  ledSet(0, 0, 255);
} // 常亮蓝
static void ledPulseGreen(unsigned long ms = 600) { ledGreenUntil = millis() + ms; } // 时间脉冲,可调

static void ledTick()
{
  unsigned long now = millis();
  // 绿色脉冲优先(事件提示):窗口内强制绿,覆盖任何状态色
  if (ledGreenUntil && (long)(now - ledGreenUntil) < 0)
  {
    ledSet(0, 255, 0);
    return;
  }
  if (ledGreenUntil && (long)(now - ledGreenUntil) >= 0)
    ledGreenUntil = 0; // 刚过期,本拍清掉

  if (ledState == LED_ONLINE)
  {
    ledSet(0, 0, 255); // 常亮蓝 —— 稳定单一指示灯,不闪烁
    return;
  }
  // 连接中(网络异常):闪烁,300ms 一拍
  if (now - ledLastToggle >= 300)
  {
    ledLastToggle = now;
    ledBlinkOn = !ledBlinkOn;
    ledSet(ledBlinkOn ? ledBR : 0, ledBlinkOn ? ledBG : 0, ledBlinkOn ? ledBB : 0);
  }
}

// ---------------- 串口配网指令 ----------------
static bool needRestart = false;

static void parseSerialLine(const String &line)
{
  if (line.startsWith("9IEDITWIFI:"))
  {
    String body = line.substring(11);
    int comma = body.indexOf(',');
    if (comma < 0)
    {
      Serial.println("[PROV] 格式错:9IEDITWIFI:SSID,PASSWORD");
      return;
    }
    String ssid = body.substring(0, comma);
    String pass = body.substring(comma + 1);
    // 注意:不再以 $ 结尾,回车/换行即结束,密码可包含 $ 等任意字符
    ssid.trim();
    pass.trim();
    if (ssid.length() == 0)
    {
      Serial.println("[PROV] SSID 空");
      return;
    }
    creds.set(ssid.c_str(), pass.c_str());
    Serial.printf("[PROV] 已保存 WiFi: %s,即将重连\n", ssid.c_str());
    needRestart = true;
  }
  else if (line == "9IPROV")
  {
    Serial.println("[PROV] 收到 9IPROV,重启后进入网页配网");
    needRestart = true;
  }
  else if (line == "9IRESETCONFIG")
  {
    creds.clearAll();
    Serial.println("[PROV] 已清空全部 WiFi 凭据,重启后进入网页配网");
    needRestart = true;
  }
}

static void checkSerial()
{
  static String buf;
  while (Serial.available())
  {
    char c = Serial.read();
    if (c == '\n' || c == '\r')
    {
      if (buf.length())
      {
        parseSerialLine(buf);
        buf = "";
      }
    }
    else
      buf += c;
  }
}

// ---------------- 连接(主/备依次尝试) ----------------
static bool attemptConnect(unsigned long timeoutMs)
{
  WiFiMulti wm;
  for (int i = 0; i < creds.count(); i++)
    wm.addAP(creds.get(i)->ssid, creds.get(i)->pass);
  Serial.print("[WiFi] 连接中");
  ledConnecting();
  unsigned long t = millis();
  int dots = 0;
  while (wm.run() != WL_CONNECTED && millis() - t < timeoutMs)
  {
    ledTick();
    checkSerial();
    if (needRestart)
      return false;
    if ((int)((millis() - t) / 200) > dots)
    {
      Serial.print(".");
      dots++;
    }
    delay(20);
  }
  Serial.println();
  if (WiFi.status() == WL_CONNECTED)
  {
    String cur = WiFi.SSID();
    for (int i = 0; i < creds.count(); i++)
      if (cur == creds.get(i)->ssid)
      {
        creds.promote(i);
        creds.save();
        break;
      }
    ledOnline();
    Serial.printf("[WiFi] 联网成功 SSID=%s IP=%s RSSI=%ddBm (主)\n",
                  WiFi.SSID().c_str(), WiFi.localIP().toString().c_str(), WiFi.RSSI());
    return true;
  }
  return false;
}

// 初始化
void setup()
{
  Serial.begin(115200);
  Serial.setTxTimeoutMs(0);
  delay(1000);
  Serial.println("=== WiFi + NTP + 三级配网 测试 ===");
  creds.begin();
  Serial.printf("[WiFi] NVS 已有 %d 条凭据\n", creds.count());

  if (creds.empty())
  {
#if DEV_SEED_CREDS
    Serial.println("[WiFi] NVS 为空,先用开发预设播种(量产请关 DEV_SEED_CREDS)");
    for (int i = 0; i < 5; i++)
      creds.set(PRESET_SSID[i], PRESET_PASS[i]);
#else
    Serial.println("[PROV] NVS 为空,进入网页配网");
    startApProvisioning(creds, 180000);
    ESP.restart();
#endif
  }

  WiFi.mode(WIFI_STA);
  if (!attemptConnect(40000))
  {
    Serial.println("[WiFi] 主/备均失败,进入网页配网(180s 超时)");
    startApProvisioning(creds, 180000);
    ESP.restart();
  }

  timeClient.begin();
  for (int k = 0; k < 5 && !timeClient.update(); k++)
  {
    ledTick();
    delay(300);
  }
  if (timeClient.isTimeSet())
    Serial.printf("[NTP] 校时成功: %s\n", timeClient.getFormattedTime().c_str());
  else
    Serial.println("[NTP] 校时失败(无网络时间)");
}

// 循环体
void loop()
{
  ledTick();     // 状态指示灯
  checkSerial(); // 串口监测
  if (needRestart)
  {
    delay(200);
    ESP.restart();
  }

  static unsigned long last = 0;
  if (WiFi.status() == WL_CONNECTED)
  {
    if (millis() - last > 5000)
    {
      last = millis();
      // 更新时钟
      timeClient.update();
      if (timeClient.isTimeSet())
      {
        time_t t = timeClient.getEpochTime();
        struct tm *ti = localtime(&t);
        Serial.printf("[时间] %04d-%02d-%02d %02d:%02d\n",
                      ti->tm_year + 1900, ti->tm_mon + 1, ti->tm_mday, ti->tm_hour, ti->tm_min);
        // ledPulseGreen();   // 打印时间时绿脉冲一下;不需要此提示可注释掉本行
      }
    }
  }
  else
  {
    Serial.println("[WiFi] 在线后掉线,重新连接");
    WiFi.mode(WIFI_STA);
    attemptConnect(40000); // 内部会切到 ledConnecting()(红闪=网络异常色)
  }
  delay(50);
}

该文档需要「VIP会员」身份

请先登录后继续阅读。