Explorar el Código

删除独立固件

moki hace 1 semana
padre
commit
6fb7f7a881
Se han modificado 4 ficheros con 3 adiciones y 1188 borrados
  1. 3 13
      firmware/README.md
  2. 0 0
      firmware/ai_light.ino
  3. 0 498
      firmware/ble/ai_light.ino
  4. 0 677
      firmware/mqtt/ai_light.ino

+ 3 - 13
firmware/README.md

@@ -2,13 +2,9 @@
 
 基于 **ESP32-C3 SuperMini** 的桌面状态灯固件,通过 BLE 蓝牙或 MQTT 接收状态指令,控制红绿灯挂件显示 AI 工作状态。
 
-## 固件版本
+## 固件
 
-| 版本 | 文件 | 通信方式 | 说明 |
-|------|------|----------|------|
-| BLE | `ble/ai_light.ino` | BLE 蓝牙 | 无需 WiFi,适合本地使用 |
-| MQTT | `mqtt/ai_light.ino` | WiFi + MQTT | 远程控制(V2 红黄互换 + 黄绿互换) |
-| **双模式** | `combined/ai_light.ino` | BLE + MQTT | **推荐**,BOOT 按钮切换模式 |
+`ai_light.ino` 支持 BLE + MQTT 双模式,通过 BOOT 按钮切换。
 
 ## Web Bluetooth 配置页面
 
@@ -112,13 +108,7 @@ ESP32 IO4   -> 220Ω -> 黄灯
 4. 打开 `.ino` 文件,点击 Upload
 5. 串口监视器波特率:`115200`
 
-## 依赖库(MQTT 版)
-
-- `WiFi.h`
-- `PubSubClient.h`
-- `ArduinoJson.h`
-
-## 依赖库(双模式版)
+## 依赖库
 
 - `WiFi.h`(ESP32 内置)
 - `PubSubClient.h`

+ 0 - 0
firmware/combined/ai_light.ino → firmware/ai_light.ino


+ 0 - 498
firmware/ble/ai_light.ino

@@ -1,498 +0,0 @@
-#include <BLEDevice.h>
-#include <BLEServer.h>
-#include <BLEUtils.h>
-#include <BLE2902.h>
-
-// =====================================================
-// ESP32-C3 SuperMini + 原玩具公共正极灯板:BLE 蓝牙控制增强版
-//
-// 接线方式:
-// ESP32 3.3V  -> 原灯板 + / 原电池正极
-// ESP32 IO2   -> 220Ω -> L1 控制点 = 绿灯
-// ESP32 IO3   -> 220Ω -> L2 控制点 = 红灯
-// ESP32 IO4   -> 220Ω -> L3 控制点 = 黄灯
-//
-// 注意:
-// 1. 原灯板 - / 原电池负极 第一版先不要接。
-// 2. 公共正极:GPIO LOW = 灯亮,GPIO HIGH = 灯灭。
-// 3. 默认开机模式:init
-// 4. 除 off、traffic 外,其他模式最多运行 5 分钟,然后自动进入 traffic。
-// 5. traffic 最多运行 10 分钟,然后自动 off。
-// =====================================================
-
-const char* BLE_DEVICE_NAME = "AI-Light";
-
-#define SERVICE_UUID        "b8b7e001-7a6b-4f4f-9a8b-11c0ffee0001"
-#define MODE_CHAR_UUID      "b8b7e002-7a6b-4f4f-9a8b-11c0ffee0001"
-
-// 你的实测:L1=绿灯,L2=红灯,L3=黄灯
-const int GREEN_PIN = 2;   // IO2 -> L1 绿灯
-const int RED_PIN = 3;     // IO3 -> L2 红灯
-const int YELLOW_PIN = 4;  // IO4 -> L3 黄灯
-const int WIFI_LED_PIN = 8; // IO8 -> 板载LED
-
-const int PWM_FREQ = 5000;
-const int PWM_RESOLUTION = 8;
-
-// 红灯偏弱,所以红灯单独增强
-const int RED_MAX = 255;
-const int YELLOW_MAX = 220;
-const int GREEN_MAX = 220;
-
-const unsigned long NORMAL_MODE_TIMEOUT_MS = 5UL * 60UL * 1000UL;   // 5 分钟
-const unsigned long TRAFFIC_MODE_TIMEOUT_MS = 10UL * 60UL * 1000UL; // 10 分钟
-
-String currentMode = "init";
-unsigned long modeStart = 0;
-
-BLEServer* pServer = nullptr;
-BLECharacteristic* pModeCharacteristic = nullptr;
-bool deviceConnected = false;
-
-
-// =====================================================
-// 基础工具函数:公共正极反相输出
-// =====================================================
-
-void writeLed(int pin, int value) {
-  value = constrain(value, 0, 255);
-  int pwmValue = 255 - value; // 公共正极反相
-  ledcWrite(pin, pwmValue);
-}
-
-void allOff() {
-  writeLed(RED_PIN, 0);
-  writeLed(YELLOW_PIN, 0);
-  writeLed(GREEN_PIN, 0);
-}
-
-void setOnly(int red, int yellow, int green) {
-  writeLed(RED_PIN, constrain(red, 0, RED_MAX));
-  writeLed(YELLOW_PIN, constrain(yellow, 0, YELLOW_MAX));
-  writeLed(GREEN_PIN, constrain(green, 0, GREEN_MAX));
-}
-
-int triWave(unsigned long t, unsigned long period, int maxValue) {
-  unsigned long x = t % period;
-  if (x < period / 2) {
-    return map(x, 0, period / 2, 0, maxValue);
-  } else {
-    return map(x, period / 2, period, maxValue, 0);
-  }
-}
-
-int fadeInOutBrightness(
-  unsigned long t,
-  unsigned long fadeIn,
-  unsigned long hold,
-  unsigned long fadeOut,
-  unsigned long offTime,
-  int maxValue
-) {
-  unsigned long total = fadeIn + hold + fadeOut + offTime;
-  unsigned long x = t % total;
-
-  if (x < fadeIn) {
-    return map(x, 0, fadeIn, 0, maxValue);
-  }
-
-  x -= fadeIn;
-  if (x < hold) {
-    return maxValue;
-  }
-
-  x -= hold;
-  if (x < fadeOut) {
-    return map(x, 0, fadeOut, maxValue, 0);
-  }
-
-  return 0;
-}
-
-void fadeToStatic(int targetRed, int targetYellow, int targetGreen, int fadeMs = 80) {
-  allOff();
-
-  int steps = 12;
-  int delayPerStep = max(1, fadeMs / steps);
-
-  for (int i = 0; i <= steps; i++) {
-    float p = (float)i / steps;
-    setOnly(targetRed * p, targetYellow * p, targetGreen * p);
-    delay(delayPerStep);
-  }
-}
-
-
-// =====================================================
-// 模式处理
-// =====================================================
-
-bool isValidMode(String mode) {
-  return (
-    mode == "red" ||
-    mode == "yellow" ||
-    mode == "green" ||
-    mode == "busy" ||
-    mode == "error" ||
-    mode == "thinking" ||
-    mode == "ai" ||
-    mode == "success" ||
-    mode == "traffic" ||
-    mode == "alarm" ||
-    mode == "init" ||
-    mode == "idle" ||
-    mode == "off"
-  );
-}
-
-void notifyMode() {
-  if (pModeCharacteristic) {
-    pModeCharacteristic->setValue(currentMode.c_str());
-    if (deviceConnected) {
-      pModeCharacteristic->notify();
-    }
-  }
-}
-
-void setMode(String mode) {
-  mode.trim();
-  mode.toLowerCase();
-
-  if (!isValidMode(mode)) {
-    Serial.print("Unknown mode: ");
-    Serial.println(mode);
-    return;
-  }
-
-  if (mode == "idle") {
-    mode = "traffic";
-  }
-
-  currentMode = mode;
-  modeStart = millis();
-
-  Serial.print("Mode changed to: ");
-  Serial.println(currentMode);
-
-  if (mode == "red") {
-    fadeToStatic(RED_MAX, 0, 0, 80);
-  } else if (mode == "yellow") {
-    fadeToStatic(0, YELLOW_MAX, 0, 80);
-  } else if (mode == "green") {
-    fadeToStatic(0, 0, GREEN_MAX, 80);
-  } else if (mode == "success") {
-    setOnly(0, 0, GREEN_MAX);
-  } else if (mode == "off") {
-    allOff();
-  }
-
-  notifyMode();
-}
-
-void autoTimeoutCheck() {
-  unsigned long elapsed = millis() - modeStart;
-
-  if (currentMode == "off") {
-    return;
-  }
-
-  if (currentMode == "traffic") {
-    if (elapsed >= TRAFFIC_MODE_TIMEOUT_MS) {
-      Serial.println("Traffic timeout -> off");
-      setMode("off");
-    }
-    return;
-  }
-
-  if (elapsed >= NORMAL_MODE_TIMEOUT_MS) {
-    Serial.println("Normal mode timeout -> traffic");
-    setMode("traffic");
-  }
-}
-
-
-// =====================================================
-// 灯效模式
-// =====================================================
-
-void updateBusy() {
-  unsigned long t = millis() - modeStart;
-  int y = fadeInOutBrightness(t, 80, 500, 120, 500, YELLOW_MAX);
-  setOnly(0, y, 0);
-}
-
-void updateError() {
-  unsigned long t = millis() - modeStart;
-  int r = fadeInOutBrightness(t, 40, 180, 80, 180, RED_MAX);
-  setOnly(r, 0, 0);
-}
-
-// thinking:连贯跑马灯,按实物从上到下:L1绿 -> L2黄 -> L3红
-void updateThinking() {
-  unsigned long t = millis() - modeStart;
-  const unsigned long period = 1050;
-  unsigned long x = t % period;
-
-  int g = 0;
-  int y = 0;
-  int r = 0;
-
-  if (x < 350) {
-    g = map(x, 0, 350, GREEN_MAX, 70);
-    y = map(x, 0, 350, 20, YELLOW_MAX);
-    r = 0;
-  } else if (x < 700) {
-    unsigned long p = x - 350;
-    g = map(p, 0, 350, 70, 0);
-    y = map(p, 0, 350, YELLOW_MAX, 70);
-    r = map(p, 0, 350, 20, RED_MAX);
-  } else {
-    unsigned long p = x - 700;
-    g = map(p, 0, 350, 20, GREEN_MAX);
-    y = map(p, 0, 350, 70, 0);
-    r = map(p, 0, 350, RED_MAX, 70);
-  }
-
-  setOnly(r, y, g);
-}
-
-// ai:柔和版跑马灯,比 thinking 更慢、更柔和、亮度低一点
-void updateAi() {
-  unsigned long t = millis() - modeStart;
-  const unsigned long period = 1800;
-  unsigned long x = t % period;
-
-  unsigned long gx = (x + 0) % period;
-  unsigned long yx = (x + period / 3) % period;
-  unsigned long rx = (x + 2 * period / 3) % period;
-
-  int g = triWave(gx, period, 150);
-  int y = triWave(yx, period, 140);
-  int r = triWave(rx, period, 170);
-
-  setOnly(r, y, g);
-}
-
-void updateSuccess() {
-  setOnly(0, 0, GREEN_MAX);
-}
-
-// alarm:红黄交替警灯,带短渐变
-void updateAlarm() {
-  unsigned long t = millis() - modeStart;
-  const unsigned long phaseMs = 260;
-  int phase = (t / phaseMs) % 2;
-  unsigned long inside = t % phaseMs;
-
-  int brightness;
-  if (inside < 60) {
-    brightness = map(inside, 0, 60, 0, 255);
-  } else if (inside < 180) {
-    brightness = 255;
-  } else {
-    brightness = map(inside, 180, phaseMs, 255, 0);
-  }
-
-  if (phase == 0) {
-    setOnly(brightness, 0, 0);
-  } else {
-    setOnly(0, min(brightness, YELLOW_MAX), 0);
-  }
-}
-
-// traffic:绿灯变黄前绿闪;黄灯;红灯变绿前红闪
-void updateTraffic() {
-  unsigned long t = (millis() - modeStart) % 15000;
-
-  if (t < 5000) {
-    setOnly(0, 0, GREEN_MAX);
-  }
-
-  else if (t < 6500) {
-    unsigned long phase = (t - 5000) % 500;
-    int g = 0;
-    if (phase < 60) g = map(phase, 0, 60, 0, GREEN_MAX);
-    else if (phase < 230) g = GREEN_MAX;
-    else if (phase < 320) g = map(phase, 230, 320, GREEN_MAX, 0);
-    else g = 0;
-    setOnly(0, 0, g);
-  }
-
-  else if (t < 8500) {
-    setOnly(0, YELLOW_MAX, 0);
-  }
-
-  else if (t < 13500) {
-    setOnly(RED_MAX, 0, 0);
-  }
-
-  else {
-    unsigned long phase = (t - 13500) % 500;
-    int r = 0;
-    if (phase < 60) r = map(phase, 0, 60, 0, RED_MAX);
-    else if (phase < 230) r = RED_MAX;
-    else if (phase < 320) r = map(phase, 230, 320, RED_MAX, 0);
-    else r = 0;
-    setOnly(r, 0, 0);
-  }
-}
-
-// init:三色一起呼吸
-void updateInit() {
-  unsigned long t = millis() - modeStart;
-  const unsigned long period = 2500;
-  unsigned long x = t % period;
-  
-  int brightness;
-  if (x < 800) {
-    brightness = map(x, 0, 800, 0, 200);
-  } else if (x < 1200) {
-    brightness = 200;
-  } else if (x < 2000) {
-    brightness = map(x, 1200, 2000, 200, 0);
-  } else {
-    brightness = 0;
-  }
-  
-  setOnly(brightness, brightness, brightness);
-}
-
-
-// =====================================================
-// BLE 回调
-// =====================================================
-
-class ServerCallbacks : public BLEServerCallbacks {
-  void onConnect(BLEServer* pServer) {
-    deviceConnected = true;
-    Serial.println("BLE client connected.");
-  }
-
-  void onDisconnect(BLEServer* pServer) {
-    deviceConnected = false;
-    Serial.println("BLE client disconnected. Restart advertising.");
-    BLEDevice::startAdvertising();
-  }
-};
-
-class ModeCharacteristicCallbacks : public BLECharacteristicCallbacks {
-  void onWrite(BLECharacteristic* pCharacteristic) {
-    String value = pCharacteristic->getValue();
-    value.trim();
-
-    Serial.print("BLE write: ");
-    Serial.println(value);
-
-    setMode(value);
-  }
-
-  void onRead(BLECharacteristic* pCharacteristic) {
-    pCharacteristic->setValue(currentMode.c_str());
-  }
-};
-
-
-// =====================================================
-// 初始化
-// =====================================================
-
-void setup() {
-  Serial.begin(115200);
-  delay(500);
-
-  ledcAttach(RED_PIN, PWM_FREQ, PWM_RESOLUTION);
-  ledcAttach(YELLOW_PIN, PWM_FREQ, PWM_RESOLUTION);
-  ledcAttach(GREEN_PIN, PWM_FREQ, PWM_RESOLUTION);
-
-  pinMode(WIFI_LED_PIN, OUTPUT);
-  digitalWrite(WIFI_LED_PIN, HIGH);
-
-  allOff();
-
-  currentMode = "init";
-  modeStart = millis();
-
-  Serial.println();
-  Serial.println("Power on. Default mode: init");
-  Serial.println("Common anode BLE enhanced version.");
-  Serial.println("BLE device name: AI-Light");
-
-  BLEDevice::init(BLE_DEVICE_NAME);
-
-  pServer = BLEDevice::createServer();
-  pServer->setCallbacks(new ServerCallbacks());
-
-  BLEService* pService = pServer->createService(SERVICE_UUID);
-
-  pModeCharacteristic = pService->createCharacteristic(
-    MODE_CHAR_UUID,
-    BLECharacteristic::PROPERTY_READ |
-    BLECharacteristic::PROPERTY_WRITE |
-    BLECharacteristic::PROPERTY_NOTIFY
-  );
-
-  pModeCharacteristic->setCallbacks(new ModeCharacteristicCallbacks());
-  pModeCharacteristic->setValue(currentMode.c_str());
-  pModeCharacteristic->addDescriptor(new BLE2902());
-
-  pService->start();
-
-  BLEAdvertising* pAdvertising = BLEDevice::getAdvertising();
-  pAdvertising->addServiceUUID(SERVICE_UUID);
-  pAdvertising->setScanResponse(true);
-  pAdvertising->setMinPreferred(0x06);
-  pAdvertising->setMinPreferred(0x12);
-
-  BLEDevice::startAdvertising();
-
-  Serial.println("BLE advertising started.");
-  Serial.println("Supported modes:");
-  Serial.println("init / thinking / ai / busy / success / error / alarm / traffic / off / red / yellow / green");
-}
-
-
-// =====================================================
-// 主循环
-// =====================================================
-
-unsigned long lastBleLedToggle = 0;
-bool bleLedState = false;
-
-void updateBleLed() {
-  if (deviceConnected) {
-    digitalWrite(WIFI_LED_PIN, LOW);
-  } else {
-    if (millis() - lastBleLedToggle >= 500) {
-      bleLedState = !bleLedState;
-      digitalWrite(WIFI_LED_PIN, bleLedState ? LOW : HIGH);
-      lastBleLedToggle = millis();
-    }
-  }
-}
-
-void loop() {
-  updateBleLed();
-  autoTimeoutCheck();
-
-  if (currentMode == "busy") {
-    updateBusy();
-  } else if (currentMode == "error") {
-    updateError();
-  } else if (currentMode == "thinking") {
-    updateThinking();
-  } else if (currentMode == "ai") {
-    updateAi();
-  } else if (currentMode == "success") {
-    updateSuccess();
-  } else if (currentMode == "traffic") {
-    updateTraffic();
-  } else if (currentMode == "alarm") {
-    updateAlarm();
-  } else if (currentMode == "init") {
-    updateInit();
-  } else if (currentMode == "off") {
-    allOff();
-  }
-
-  delay(5);
-}

+ 0 - 677
firmware/mqtt/ai_light.ino

@@ -1,677 +0,0 @@
-#include <WiFi.h>
-#include <PubSubClient.h>
-#include <ArduinoJson.h>
-
-// =====================================================
-// ESP32-C3 SuperMini + 原玩具公共正极灯板:MQTT 蓝牙控制增强版 V2
-//
-// 接线方式(V2 版本:红黄互换 + 黄绿互换):
-// ESP32 3.3V  -> 原灯板 + / 原电池正极
-// ESP32 IO2   -> 220Ω -> L1 控制点 = 黄灯
-// ESP32 IO3   -> 220Ω -> L2 控制点 = 红灯
-// ESP32 IO4   -> 220Ω -> L3 控制点 = 绿灯
-//
-// 注意:
-// 1. 原灯板 - / 原电池负极 第一版先不要接。
-// 2. 公共正极:GPIO LOW = 灯亮,GPIO HIGH = 灯灭。
-// 3. 默认开机模式:init
-// 4. 除 off、traffic 外,其他模式最多运行 5 分钟,然后自动进入 traffic。
-// 5. traffic 最多运行 10 分钟,然后自动 off。
-// =====================================================
-
-// =====================================================
-// WiFi & MQTT 配置(请根据你的网络修改)
-// =====================================================
-#define WIFI_SSID        "your_wifi_ssid"
-#define WIFI_PASSWORD    "your_wifi_password"
-#define MQTT_BROKER      "your_mqtt_broker"
-#define MQTT_PORT        1883
-#define MQTT_USERNAME    "your_mqtt_username"
-#define MQTT_PASSWORD    "your_mqtt_password"
-#define MQTT_CLIENT_ID   "AI-Light"
-#define MQTT_TOPIC       "opencode/status"
-#define MQTT_STATUS_TOPIC "openCodeLight/status"
-
-// V2 版本:红黄互换 + 黄绿互换
-const int GREEN_PIN = 2;   // IO2 -> L1 绿灯
-const int RED_PIN = 3;     // IO3 -> L2 红灯
-const int YELLOW_PIN = 4;  // IO4 -> L3 黄灯
-const int WIFI_LED_PIN = 8; // IO8 -> 板载LED
-
-const int PWM_FREQ = 5000;
-const int PWM_RESOLUTION = 8;
-
-// 红灯偏弱,所以红灯单独增强
-const int RED_MAX = 255;
-const int YELLOW_MAX = 220;
-const int GREEN_MAX = 220;
-
-const unsigned long NORMAL_MODE_TIMEOUT_MS = 5UL * 60UL * 1000UL;   // 5 分钟
-const unsigned long TRAFFIC_MODE_TIMEOUT_MS = 10UL * 60UL * 1000UL; // 10 分钟
-
-String currentMode = "init";
-unsigned long modeStart = 0;
-
-WiFiClient wifiClient;
-PubSubClient mqttClient(wifiClient);
-
-
-// =====================================================
-// 基础工具函数:公共正极反相输出
-// =====================================================
-
-void writeLed(int pin, int value) {
-  value = constrain(value, 0, 255);
-  int pwmValue = 255 - value; // 公共正极反相
-  ledcWrite(pin, pwmValue);
-}
-
-void allOff() {
-  writeLed(RED_PIN, 0);
-  writeLed(YELLOW_PIN, 0);
-  writeLed(GREEN_PIN, 0);
-}
-
-void setOnly(int red, int yellow, int green) {
-  writeLed(RED_PIN, constrain(red, 0, RED_MAX));
-  writeLed(YELLOW_PIN, constrain(yellow, 0, YELLOW_MAX));
-  writeLed(GREEN_PIN, constrain(green, 0, GREEN_MAX));
-}
-
-int triWave(unsigned long t, unsigned long period, int maxValue) {
-  unsigned long x = t % period;
-  if (x < period / 2) {
-    return map(x, 0, period / 2, 0, maxValue);
-  } else {
-    return map(x, period / 2, period, maxValue, 0);
-  }
-}
-
-int fadeInOutBrightness(
-  unsigned long t,
-  unsigned long fadeIn,
-  unsigned long hold,
-  unsigned long fadeOut,
-  unsigned long offTime,
-  int maxValue
-) {
-  unsigned long total = fadeIn + hold + fadeOut + offTime;
-  unsigned long x = t % total;
-
-  if (x < fadeIn) {
-    return map(x, 0, fadeIn, 0, maxValue);
-  }
-
-  x -= fadeIn;
-  if (x < hold) {
-    return maxValue;
-  }
-
-  x -= hold;
-  if (x < fadeOut) {
-    return map(x, 0, fadeOut, maxValue, 0);
-  }
-
-  return 0;
-}
-
-void blinkLed(int pin, int times = 3, int intervalMs = 200) {
-  for (int i = 0; i < times; i++) {
-    writeLed(pin, 200);
-    delay(intervalMs);
-    writeLed(pin, 0);
-    delay(intervalMs);
-  }
-}
-
-void fadeToStatic(int targetRed, int targetYellow, int targetGreen, int fadeMs = 80) {
-  allOff();
-
-  int steps = 12;
-  int delayPerStep = max(1, fadeMs / steps);
-
-  for (int i = 0; i <= steps; i++) {
-    float p = (float)i / steps;
-    setOnly(targetRed * p, targetYellow * p, targetGreen * p);
-    delay(delayPerStep);
-  }
-}
-
-
-// =====================================================
-// 模式处理
-// =====================================================
-
-bool isValidMode(String mode) {
-  return (
-    mode == "red" ||
-    mode == "yellow" ||
-    mode == "green" ||
-    mode == "busy" ||
-    mode == "error" ||
-    mode == "thinking" ||
-    mode == "ai" ||
-    mode == "success" ||
-    mode == "traffic" ||
-    mode == "alarm" ||
-    mode == "init" ||
-    mode == "off" ||
-    mode == "idle"
-  );
-}
-
-void publishStatus() {
-  mqttClient.publish(MQTT_STATUS_TOPIC, currentMode.c_str(), true);
-}
-
-void setMode(String mode) {
-  mode.trim();
-  mode.toLowerCase();
-
-  if (!isValidMode(mode)) {
-    Serial.print("Unknown mode: ");
-    Serial.println(mode);
-    return;
-  }
-
-  if (mode == "idle") {
-    mode = "traffic";
-  }
-
-  currentMode = mode;
-  modeStart = millis();
-
-  Serial.print("Mode changed to: ");
-  Serial.println(currentMode);
-
-  if (mode == "red") {
-    fadeToStatic(RED_MAX, 0, 0, 80);
-  } else if (mode == "yellow") {
-    fadeToStatic(0, YELLOW_MAX, 0, 80);
-  } else if (mode == "green") {
-    fadeToStatic(0, 0, GREEN_MAX, 80);
-  } else if (mode == "success") {
-    setOnly(0, 0, GREEN_MAX);
-  } else if (mode == "off") {
-    allOff();
-  }
-
-  publishStatus();
-}
-
-void autoTimeoutCheck() {
-  unsigned long elapsed = millis() - modeStart;
-
-  if (currentMode == "off") {
-    return;
-  }
-
-  if (currentMode == "traffic") {
-    if (elapsed >= TRAFFIC_MODE_TIMEOUT_MS) {
-      Serial.println("Traffic timeout -> off");
-      setMode("off");
-    }
-    return;
-  }
-
-  if (elapsed >= NORMAL_MODE_TIMEOUT_MS) {
-    Serial.println("Normal mode timeout -> traffic");
-    setMode("traffic");
-  }
-}
-
-
-// =====================================================
-// 灯效模式
-// =====================================================
-
-void updateBusy() {
-  unsigned long t = millis() - modeStart;
-  int y = fadeInOutBrightness(t, 80, 500, 120, 500, YELLOW_MAX);
-  setOnly(0, y, 0);
-}
-
-void updateError() {
-  unsigned long t = millis() - modeStart;
-  int r = fadeInOutBrightness(t, 40, 180, 80, 180, RED_MAX);
-  setOnly(r, 0, 0);
-}
-
-// thinking:连贯跑马灯,按实物从上到下:L1绿 -> L2黄 -> L3红
-void updateThinking() {
-  unsigned long t = millis() - modeStart;
-  const unsigned long period = 1050;
-  unsigned long x = t % period;
-
-  int g = 0;
-  int y = 0;
-  int r = 0;
-
-  if (x < 350) {
-    g = map(x, 0, 350, GREEN_MAX, 70);
-    y = map(x, 0, 350, 20, YELLOW_MAX);
-    r = 0;
-  } else if (x < 700) {
-    unsigned long p = x - 350;
-    g = map(p, 0, 350, 70, 0);
-    y = map(p, 0, 350, YELLOW_MAX, 70);
-    r = map(p, 0, 350, 20, RED_MAX);
-  } else {
-    unsigned long p = x - 700;
-    g = map(p, 0, 350, 20, GREEN_MAX);
-    y = map(p, 0, 350, 70, 0);
-    r = map(p, 0, 350, RED_MAX, 70);
-  }
-
-  setOnly(r, y, g);
-}
-
-// ai:柔和版跑马灯,比 thinking 更慢、更柔和、亮度低一点
-void updateAi() {
-  unsigned long t = millis() - modeStart;
-  const unsigned long period = 1800;
-  unsigned long x = t % period;
-
-  unsigned long gx = (x + 0) % period;
-  unsigned long yx = (x + period / 3) % period;
-  unsigned long rx = (x + 2 * period / 3) % period;
-
-  int g = triWave(gx, period, 150);
-  int y = triWave(yx, period, 140);
-  int r = triWave(rx, period, 170);
-
-  setOnly(r, y, g);
-}
-
-void updateSuccess() {
-  setOnly(0, 0, GREEN_MAX);
-}
-
-// alarm:红黄交替警灯,带短渐变
-void updateAlarm() {
-  unsigned long t = millis() - modeStart;
-  const unsigned long phaseMs = 260;
-  int phase = (t / phaseMs) % 2;
-  unsigned long inside = t % phaseMs;
-
-  int brightness;
-  if (inside < 60) {
-    brightness = map(inside, 0, 60, 0, 255);
-  } else if (inside < 180) {
-    brightness = 255;
-  } else {
-    brightness = map(inside, 180, phaseMs, 255, 0);
-  }
-
-  if (phase == 0) {
-    setOnly(brightness, 0, 0);
-  } else {
-    setOnly(0, min(brightness, YELLOW_MAX), 0);
-  }
-}
-
-// traffic:绿灯变黄前绿闪;黄灯;红灯变绿前红闪
-void updateTraffic() {
-  unsigned long t = (millis() - modeStart) % 15000;
-
-  if (t < 5000) {
-    setOnly(0, 0, GREEN_MAX);
-  }
-
-  else if (t < 6500) {
-    unsigned long phase = (t - 5000) % 500;
-    int g = 0;
-    if (phase < 60) g = map(phase, 0, 60, 0, GREEN_MAX);
-    else if (phase < 230) g = GREEN_MAX;
-    else if (phase < 320) g = map(phase, 230, 320, GREEN_MAX, 0);
-    else g = 0;
-    setOnly(0, 0, g);
-  }
-
-  else if (t < 8500) {
-    setOnly(0, YELLOW_MAX, 0);
-  }
-
-  else if (t < 13500) {
-    setOnly(RED_MAX, 0, 0);
-  }
-
-  else {
-    unsigned long phase = (t - 13500) % 500;
-    int r = 0;
-    if (phase < 60) r = map(phase, 0, 60, 0, RED_MAX);
-    else if (phase < 230) r = RED_MAX;
-    else if (phase < 320) r = map(phase, 230, 320, RED_MAX, 0);
-    else r = 0;
-    setOnly(r, 0, 0);
-  }
-}
-
-// init:三色一起呼吸
-void updateInit() {
-  unsigned long t = millis() - modeStart;
-  const unsigned long period = 2500;
-  unsigned long x = t % period;
-  
-  int brightness;
-  if (x < 800) {
-    brightness = map(x, 0, 800, 0, 200);
-  } else if (x < 1200) {
-    brightness = 200;
-  } else if (x < 2000) {
-    brightness = map(x, 1200, 2000, 200, 0);
-  } else {
-    brightness = 0;
-  }
-  
-  setOnly(brightness, brightness, brightness);
-}
-
-// 工具执行完成:绿灯快闪三次
-void updateCompletedFlash() {
-  unsigned long t = millis() - modeStart;
-  const int flashCount = 3;
-  const unsigned long flashDuration = 150;  // 每次闪烁150ms
-  const unsigned long totalDuration = flashCount * 2 * flashDuration;  // 3次闪烁总时长
-  
-  if (t >= totalDuration) {
-    // 闪烁完成,保持灭灯
-    setOnly(0, 0, 0);
-    return;
-  }
-  
-  // 计算当前闪烁状态
-  unsigned long flashPhase = t % (2 * flashDuration);
-  if (flashPhase < flashDuration) {
-    // 亮灯阶段
-    setOnly(0, 0, GREEN_MAX);
-  } else {
-    // 灭灯阶段
-    setOnly(0, 0, 0);
-  }
-}
-
-// 会话完成:绿灯呼吸闪烁(3次后自动切换到idle)
-void updateBreathing() {
-  unsigned long t = millis() - modeStart;
-  const unsigned long period = 2500;  // 2.5秒一个呼吸周期
-  const int breathCount = 3;  // 呼吸3次
-  const unsigned long totalDuration = breathCount * period;  // 总时长
-  
-  // 检查是否完成3次呼吸
-  if (t >= totalDuration) {
-    // 3次呼吸完成,自动切换到idle
-    setMode("idle");
-    return;
-  }
-  
-  unsigned long x = t % period;
-  
-  int brightness;
-  if (x < 800) {
-    // 吸入阶段:0 -> 最大
-    brightness = map(x, 0, 800, 0, GREEN_MAX);
-  } else if (x < 1200) {
-    // 保持阶段:最大亮度
-    brightness = GREEN_MAX;
-  } else if (x < 2000) {
-    // 呼出阶段:最大 -> 0
-    brightness = map(x, 1200, 2000, GREEN_MAX, 0);
-  } else {
-    // 停顿阶段:0亮度
-    brightness = 0;
-  }
-  
-  setOnly(0, 0, brightness);
-}
-
-
-// =====================================================
-// MQTT 回调
-// =====================================================
-
-void mqttCallback(char* topic, byte* payload, unsigned int length) {
-  String message = "";
-  for (unsigned int i = 0; i < length; i++) {
-    message += (char)payload[i];
-  }
-
-  Serial.print("MQTT message on ");
-  Serial.print(topic);
-  Serial.print(": ");
-  Serial.println(message);
-
-  JsonDocument doc;
-  DeserializationError error = deserializeJson(doc, message);
-
-  if (error) {
-    Serial.print("JSON parse failed, treating as raw mode: ");
-    Serial.println(error.c_str());
-    setMode(message);
-    return;
-  }
-
-  const char* code = doc["code"];
-  if (code) {
-    String codeStr = String(code);
-    
-    // 状态码映射到灯效
-    if (codeStr == "idle") {
-      setMode("idle");
-    } else if (codeStr == "busy" || codeStr == "running") {
-      setMode("busy");
-    } else if (codeStr == "retry" || codeStr == "permission") {
-      setMode("alarm");
-    } else if (codeStr == "pending") {
-      setMode("yellow");
-    } else if (codeStr == "reasoning") {
-      setMode("thinking");
-    } else if (codeStr == "using_tool") {
-      setMode("ai");
-    } else if (codeStr == "error") {
-      setMode("error");
-    } else {
-      // 直接使用原始模式名
-      setMode(codeStr);
-    }
-  } else {
-    Serial.println("MQTT message missing 'code' field");
-  }
-}
-
-unsigned long lastWifiLedToggle = 0;
-bool wifiLedState = false;
-
-void updateWifiLed() {
-  if (WiFi.status() == WL_CONNECTED) {
-    digitalWrite(WIFI_LED_PIN, LOW);
-  } else {
-    if (millis() - lastWifiLedToggle >= 500) {
-      wifiLedState = !wifiLedState;
-      digitalWrite(WIFI_LED_PIN, wifiLedState ? LOW : HIGH);
-      lastWifiLedToggle = millis();
-    }
-  }
-}
-
-void connectWiFi() {
-  Serial.print("Connecting to WiFi");
-  WiFi.mode(WIFI_STA);
-  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
-
-  int maxRetries = 5;
-  int attemptsPerRetry = 60;
-
-  for (int retry = 1; retry <= maxRetries; retry++) {
-    Serial.print("\nWiFi attempt ");
-    Serial.print(retry);
-    Serial.print("/");
-    Serial.println(maxRetries);
-
-    int attempts = 0;
-    while (WiFi.status() != WL_CONNECTED && attempts < attemptsPerRetry) {
-      delay(5);
-      setOnly(100, 100, 100);
-      updateWifiLed();
-      Serial.print(".");
-      attempts++;
-    }
-
-    if (WiFi.status() == WL_CONNECTED) {
-      break;
-    }
-
-    Serial.println("\nWiFi connection failed, retrying...");
-    delay(2000);
-  }
-
-  if (WiFi.status() != WL_CONNECTED) {
-    Serial.println("WiFi failed after 5 retries. Restarting...");
-    delay(1000);
-    ESP.restart();
-  }
-
-  Serial.println();
-  Serial.print("WiFi connected. IP: ");
-  Serial.println(WiFi.localIP());
-  digitalWrite(WIFI_LED_PIN, LOW);
-}
-
-void breathingGreen(int times) {
-  const unsigned long period = 2500;
-  for (int i = 0; i < times; i++) {
-    unsigned long start = millis();
-    while (millis() - start < period) {
-      unsigned long t = millis() - start;
-      int brightness;
-      if (t < 800) {
-        brightness = map(t, 0, 800, 0, GREEN_MAX);
-      } else if (t < 1200) {
-        brightness = GREEN_MAX;
-      } else if (t < 2000) {
-        brightness = map(t, 1200, 2000, GREEN_MAX, 0);
-      } else {
-        brightness = 0;
-      }
-      setOnly(0, 0, brightness);
-      delay(5);
-    }
-  }
-  allOff();
-}
-
-void connectMQTT() {
-  mqttClient.setServer(MQTT_BROKER, MQTT_PORT);
-  mqttClient.setCallback(mqttCallback);
-
-  Serial.print("Connecting to MQTT broker");
-  int attempts = 0;
-  bool ledState = false;
-  while (!mqttClient.connected()) {
-    ledState = !ledState;
-    if (ledState) {
-      setOnly(RED_MAX, 0, 0);
-    } else {
-      setOnly(0, YELLOW_MAX, 0);
-    }
-
-    if (mqttClient.connect(MQTT_CLIENT_ID, MQTT_USERNAME, MQTT_PASSWORD)) {
-      Serial.println();
-      Serial.println("MQTT connected.");
-
-      mqttClient.subscribe(MQTT_TOPIC);
-      Serial.print("Subscribed to: ");
-      Serial.println(MQTT_TOPIC);
-
-      allOff();
-      breathingGreen(3);
-      publishStatus();
-    } else {
-      Serial.print(".");
-      delay(500);
-      attempts++;
-      if (attempts > 60) {
-        Serial.println("\nMQTT connection failed!");
-        attempts = 0;
-      }
-    }
-  }
-}
-
-void checkMQTTConnection() {
-  if (!mqttClient.connected()) {
-    Serial.println("MQTT disconnected. Reconnecting...");
-    connectMQTT();
-  }
-}
-
-
-// =====================================================
-// 初始化
-// =====================================================
-
-void setup() {
-  Serial.begin(115200);
-  delay(500);
-
-  ledcAttach(RED_PIN, PWM_FREQ, PWM_RESOLUTION);
-  ledcAttach(YELLOW_PIN, PWM_FREQ, PWM_RESOLUTION);
-  ledcAttach(GREEN_PIN, PWM_FREQ, PWM_RESOLUTION);
-
-  pinMode(WIFI_LED_PIN, OUTPUT);
-  digitalWrite(WIFI_LED_PIN, LOW);  // 初始状态关闭
-
-  allOff();
-
-  Serial.println();
-  Serial.println("Power on.");
-  Serial.println("Common anode MQTT enhanced version V2 (Red/Yellow + Yellow/Green swapped).");
-  Serial.print("Device name: ");
-  Serial.println(MQTT_CLIENT_ID);
-
-  currentMode = "init";
-  modeStart = millis();
-
-  connectWiFi();
-  connectMQTT();
-
-  setMode("traffic");
-
-  Serial.println("Supported modes:");
-  Serial.println("init / thinking / ai / busy / success / error / alarm / traffic / off / red / yellow / green");
-}
-
-
-// =====================================================
-// 主循环
-// =====================================================
-
-void loop() {
-  updateWifiLed();
-  checkMQTTConnection();
-  mqttClient.loop();
-
-  autoTimeoutCheck();
-
-  if (currentMode == "busy") {
-    updateBusy();
-  } else if (currentMode == "error") {
-    updateError();
-  } else if (currentMode == "thinking") {
-    updateThinking();
-  } else if (currentMode == "ai") {
-    updateAi();
-  } else if (currentMode == "success") {
-    updateSuccess();
-  } else if (currentMode == "traffic") {
-    updateTraffic();
-  } else if (currentMode == "alarm") {
-    updateAlarm();
-  } else if (currentMode == "init") {
-    updateInit();
-  } else if (currentMode == "off") {
-    allOff();
-  }
-
-  delay(5);
-}