#include <SPI.h>
#include <MFRC522.h>
#include <FastLED.h>
#include <Arduino.h>
#include <driver/ledc.h>

// RFID引脚定义
#define RFID_RST_PIN 14   // RC522 复位引脚
#define RFID_SS_PIN 10    // RC522 CS/SDA引脚（SPI和I2C共用）
#define RFID_MISO_PIN 13  // MISO 引脚
#define RFID_MOSI_PIN 12  // MOSI 引脚
#define RFID_SCK_PIN 11   // SCK 引脚

// LED定义
#define LED_PIN_1 4      // 1颗WS2812灯珠引脚
#define LED_PIN_2 5      // 160颗WS2812灯带引脚（控制灯珠颜色）
#define LED_PIN_3 48     // 1颗WS2812灯珠引脚（新增）
#define LED_COUNT_1 1    // 1颗灯珠
#define LED_COUNT_2 186  // 160颗灯带
#define LED_COUNT_3 1    // 1颗灯珠（新增）

// PWM定义
#define PWM_PIN 6          // PWM输出（控制输出PWM功率）
#define PWM_CHANNEL 0      // PWM通道
#define PWM_FREQ 1000      // PWM频率(Hz)
#define PWM_RESOLUTION 10  // PWM分辨率(位)
#define DEFAULT_DUTY 819   // 默认占空比(80%)

// 按钮和输入引脚定义
#define BTN0_PIN 15     // 按钮0引脚
#define WAKEUP1_PIN 16  // 唤醒引脚1
#define BTN1_PIN 17     // 按钮1引脚
#define BTN2_PIN 18     // 按钮2引脚

// 任务句柄
TaskHandle_t TaskRFID, TaskLED1, TaskLED2, TaskLED3, TaskPWM, TaskBTN0, TaskWAKEUP1, TaskBTN1, TaskBTN2;

// 双串口架构：
// - Serial     = USB-Serial-JTAG (USB2 口)，连 Windows 做调试日志
// - SerialLinux = UART0 (CH343/USB1 口)，连 Android 开发板收发业务数据
// UART0 默认引脚：TX=GPIO43、RX=GPIO44（对应 CH343P 的 RXD/TXD）
//
// ⚠️ 重要：必须用 #define 别名引用 Arduino core 自带的 Serial0 对象
// 不能自己创建 HardwareSerial(0)，否则 Arduino core 的 UART0 RX 中断会把
// 数据放进 Serial0 的 ring buffer，而自建对象的 available() 读不到 → 命令无响应
#define SerialLinux Serial0

// Serial 输出互斥锁：防止多任务并发写串口导致数据交错/截断
SemaphoreHandle_t serialMutex = NULL;

// 业务数据输出：只发到 UART0 (给 Android 开发板)
// 调用方：RFID 任务发 SORC_xxx，按键任务发 SO_xxx
// 为什么不发 Serial (USB CDC)：
//   1. 生产场景只连 Android，Windows 不连，Serial 写入+flush 在无 host 时可能阻塞
//   2. 阻塞会拖慢 RFID 响应（之前刷卡不灵敏的潜在原因之一）
//   3. UART0 是广播式通讯，无 host 也能正常写入 FIFO，不阻塞
// 互斥锁保留：虽然只发一个串口，但防止多任务并发调用时缓冲混乱
void serialPrintlnSafe(const String& msg) {
  if (serialMutex && xSemaphoreTake(serialMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
    SerialLinux.println(msg);
    SerialLinux.flush();  // UART0 硬件 FIFO 128 字节，115200 波特率约 1ms 完成
    xSemaphoreGive(serialMutex);
  } else {
    // 降级路径：拿不到锁（极少发生）
    SerialLinux.println(msg);
  }
}

// 全局变量
MFRC522 rfid(RFID_SS_PIN, RFID_RST_PIN);  // 创建RFID实例
CRGB leds1[LED_COUNT_1];                  // 1颗灯珠数组
CRGB leds2[LED_COUNT_2];                  // 160颗灯带数组
CRGB leds3[LED_COUNT_3];                  // 1颗灯珠数组（新增）
CRGB frozenLeds2[LED_COUNT_2];            // 保存冻结时的颜色数据（模式5专用）
uint8_t frozenBrightness = 255;           // 保存冻结时的亮度值，用于计算相对亮度比例

String lastCardData = "";    // 上次读取的RFID卡数据
int ledMode = 1;             // 灯带模式，默认为1(白色)
int pwmDuty = DEFAULT_DUTY;  // PWM占空比
bool btn0State = HIGH;       // 按钮0状态
bool btn0LongPress = false;  // 按钮0长按标志
bool wakeup1State = LOW;     // 唤醒引脚1状态
bool btn1State = LOW;        // 按钮1状态
bool btn2State = LOW;        // 按钮2状态
int singleLedMode = 7;       // 单颗LED模式，默认为7(白色)

// 灯带动画全局变量
static uint8_t rainbowHue = 0;
static int trainPos = 0;
static unsigned long lastUpdate = 0;
static const int TRAIN_LENGTH = 16;                               // 火车灯长度
static int trainPhase = 0;                                        // 火车阶段：0-正向出站，1-正向前进，2-正向进站，3-反向出站，4-反向前进，5-反向进站
static const int VIRTUAL_LED_COUNT = LED_COUNT_2 + TRAIN_LENGTH;  // 虚拟灯带长度


// LED亮度线性映射表 (0~100 → 26~255) - 最小阈值10%
// 用于将用户输入的0-100%亮度值映射到实际的PWM值
// 避免过低亮度导致LED完全不可见的问题
const uint8_t brightnessMapLinear[101] = {
  0, 28, 31, 33, 36, 38, 41, 43, 46, 48,             // 0-9
  51, 54, 56, 59, 61, 64, 66, 69, 71, 74,            // 10-19
  77, 79, 82, 84, 87, 89, 92, 94, 97, 99,            // 20-29
  102, 105, 107, 110, 112, 115, 117, 120, 122, 125,  // 30-39
  128, 130, 133, 135, 138, 140, 143, 145, 148, 150,  // 40-49
  153, 156, 158, 161, 163, 166, 168, 171, 173, 176,  // 50-59
  179, 181, 184, 186, 189, 191, 194, 196, 199, 201,  // 60-69
  204, 207, 209, 212, 214, 217, 219, 222, 224, 227,  // 70-79
  230, 232, 235, 237, 240, 242, 245, 247, 250, 252,  // 80-89
  253, 254, 254, 254, 255, 255, 255, 255, 255, 255,  // 90-99
  255                                                // 100%
};







// 全局选择映射表
const uint8_t* brightnessMap = brightnessMapLinear;

// LED2亮度控制（0-255）
// 用于控制LED灯带的整体亮度，影响模式1、2、4和5
// 模式3使用独立的呼吸算法，基于此值计算动态亮度范围
uint8_t led2Brightness = 102;  // 默认40%左右（102/255≈40%）



// 单颗LED颜色数组
CRGB singleLedColors[8] = {
  CRGB::Black,   // 0: 熄灭
  CRGB::Blue,    // 1: 蓝色
  CRGB::Green,   // 2: 绿色
  CRGB::Orange,  // 3: 橙色
  CRGB::Red,     // 4: 红色
  CRGB::Purple,  // 5: 紫色
  CRGB::Yellow,  // 6: 黄色
  CRGB::White    // 7: 白色
};

// RC522 硬件复位（运行时调用，不依赖 MFRC522 库的 RST 自动判断）
// 用于 setup 初始化和运行时检测到 RC522 异常时恢复
void rc522HardResetRuntime() {
  pinMode(RFID_RST_PIN, OUTPUT);
  digitalWrite(RFID_RST_PIN, LOW);
  delay(10);
  digitalWrite(RFID_RST_PIN, HIGH);
  delay(50);
}

// RFID读取任务
void TaskRFIDcode(void* pvParameters) {
  // 运行时健康检查：每 5 秒读一次 VersionReg，检测到异常自动恢复
  // 防止运行过程中 RC522 因电源波动、WS2812 大电流干扰等原因进入异常状态
  static uint32_t lastHealthCheck = 0;
  const uint32_t HEALTH_CHECK_INTERVAL_MS = 5000;

  for (;;) {
    // 运行时健康检查（和下面的轮询并行）
    uint32_t now = millis();
    if (now - lastHealthCheck > HEALTH_CHECK_INTERVAL_MS) {
      lastHealthCheck = now;
      byte version = rfid.PCD_ReadRegister(MFRC522::VersionReg);
      if (version != 0x91 && version != 0x92) {
        // 通讯异常，自动恢复
        Serial.print("RC522 health check failed (VersionReg=0x");
        Serial.print(version, HEX);
        Serial.println("), reinitializing...");
        rc522HardResetRuntime();
        rfid.PCD_Init();
      }
    }

    // 寻找新卡片
    if (!rfid.PICC_IsNewCardPresent()) {
      delay(10);
      continue;
    }

    // 验证NUID是否可读
    if (!rfid.PICC_ReadCardSerial()) {
      delay(10);
      continue;
    }

    // 读取卡片数据（用户数据区）
    String cardData = "";
    MFRC522::MIFARE_Key key;

    // 准备认证密钥
    for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;

    // 选择卡片
    MFRC522::StatusCode status;
    status = rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 4, &key, &(rfid.uid));
    if (status != MFRC522::STATUS_OK) {
      // 调试日志用普通 Serial.println：失败频率较高时，避免 flush 阻塞拖慢 RFID 响应
      // 偶尔截断可接受（Linux 端用正则 ^SORC_HA\d+$ 过滤业务数据即可）
      Serial.println(String("Authentication failed: ") + rfid.GetStatusCodeName(status));
      rfid.PICC_HaltA();
      rfid.PCD_StopCrypto1();
      delay(30);  // 从 100ms 降到 30ms，提升刷卡响应速度
      continue;
    }

    // 读取数据块
    byte buffer[18];
    byte size = sizeof(buffer);
    status = rfid.MIFARE_Read(4, buffer, &size);
    if (status != MFRC522::STATUS_OK) {
      Serial.println(String("Reading failed: ") + rfid.GetStatusCodeName(status));
      rfid.PICC_HaltA();
      rfid.PCD_StopCrypto1();
      delay(30);  // 从 100ms 降到 30ms
      continue;
    }

    // 转换为ASCII字符串
    for (byte i = 0; i < 16; i++) {
      if (buffer[i] >= 32 && buffer[i] <= 126) {  // 可打印ASCII字符
        cardData += (char)buffer[i];
      }
    }

    // 移除空白字符
    cardData.trim();

    // 卡片数据格式校验：规则 "HA" + 阿拉伯数字
    // 过滤掉卡片读取异常或数据损坏的情况，避免发送无效数据给 Linux
    auto isValidCardData = [](const String& d) -> bool {
      if (d.length() < 3) return false;           // 至少 "HA" + 1 位数字
      if (!d.startsWith("HA")) return false;      // 必须以 HA 开头
      for (size_t i = 2; i < d.length(); i++) {
        if (!isdigit(d[i])) return false;         // HA 后面必须全是数字
      }
      return true;
    };

    // 3 秒去重窗口：同一张卡 3 秒内只发送一次，超过后允许重发
    // 切换到不同卡立即发送
    static String lastSentCard = "";
    static unsigned long lastSentTime = 0;
    const unsigned long DUPLICATE_WINDOW_MS = 3000;

    if (!cardData.isEmpty() && isValidCardData(cardData)) {
      unsigned long now = millis();
      bool isDuplicate = (cardData == lastSentCard) && (now - lastSentTime < DUPLICATE_WINDOW_MS);
      if (!isDuplicate) {
        serialPrintlnSafe("SORC_" + cardData);
        lastSentCard = cardData;
        lastSentTime = now;
      }
    }

    // 使放置在读卡区的IC卡进入休眠状态，不再重复读卡
    rfid.PICC_HaltA();

    // 停止加密PCD
    rfid.PCD_StopCrypto1();

    delay(100);
  }
}

// 注意：以下两个函数已被TaskLEDUnifiedCode替代，保留仅供参考
// 实际运行中不会被调用，因为setup()中没有创建对应的任务

// LED1控制任务（已废弃，由TaskLEDUnifiedCode统一处理）
void TaskLED1code(void* pvParameters) {
  // 此函数已被废弃，不再使用
  // LED1的控制已集成到TaskLEDUnifiedCode中
  vTaskDelete(NULL);  // 如果意外创建了此任务，立即删除
}

// LED3控制任务（已废弃，由TaskLEDUnifiedCode统一处理）
void TaskLED3code(void* pvParameters) {
  // 此函数已被废弃，不再使用
  // LED3的控制已集成到TaskLEDUnifiedCode中
  vTaskDelete(NULL);  // 如果意外创建了此任务，立即删除
}

// PWM控制任务
void TaskPWMcode(void* pvParameters) {
  for (;;) {
    // 设置PWM占空比
    ledc_set_duty(LEDC_LOW_SPEED_MODE, (ledc_channel_t)PWM_CHANNEL, pwmDuty);
    ledc_update_duty(LEDC_LOW_SPEED_MODE, (ledc_channel_t)PWM_CHANNEL);

    delay(100);
  }
}

// 按钮0检测任务
void TaskBTN0code(void* pvParameters) {
  static unsigned long pressStartTime = 0;
  // 首次进入任务时读取实际电平作为初始值，避免上电时 GPIO 浮空触发虚假边沿事件
  bool lastState = digitalRead(BTN0_PIN);
  btn0State = lastState;

  for (;;) {
    bool currentState = digitalRead(BTN0_PIN);

    // 检测下降沿(按下)
    if (lastState == HIGH && currentState == LOW) {
      pressStartTime = millis();
      btn0State = LOW;
      serialPrintlnSafe("SO_BT0_HIGH");
      btn0LongPress = false;
    }
    // 检测上升沿(释放)
    else if (lastState == LOW && currentState == HIGH) {
      btn0State = HIGH;
      serialPrintlnSafe("SO_BT0_LOW");
      btn0LongPress = false;
    }
    // 检测长按
    else if (currentState == LOW && millis() - pressStartTime >= 2000 && !btn0LongPress) {
      btn0LongPress = true;
      serialPrintlnSafe("SO_BT0_HIGHL");
    }

    lastState = currentState;
    delay(10);
  }
}

// WAKEUP1检测任务
void TaskWAKEUP1code(void* pvParameters) {
  // 首次进入任务时读取实际电平作为初始值，避免上电时 GPIO 浮空触发虚假边沿事件
  bool lastState = digitalRead(WAKEUP1_PIN);
  wakeup1State = lastState;

  for (;;) {
    bool currentState = digitalRead(WAKEUP1_PIN);

    // 检测上升沿
    if (lastState == LOW && currentState == HIGH) {
      wakeup1State = HIGH;
      serialPrintlnSafe("SO_WAKEUP1");
    }
    // 检测下降沿
    else if (lastState == HIGH && currentState == LOW) {
      wakeup1State = LOW;
      serialPrintlnSafe("SO_WAKEUP0");
    }

    lastState = currentState;
    delay(10);
  }
}

// 按钮1检测任务
void TaskBTN1code(void* pvParameters) {
  // 首次进入任务时读取实际电平作为初始值，避免上电时 GPIO 浮空触发虚假边沿事件
  bool lastState = digitalRead(BTN1_PIN);
  btn1State = lastState;

  for (;;) {
    bool currentState = digitalRead(BTN1_PIN);

    // 检测上升沿
    if (lastState == LOW && currentState == HIGH) {
      btn1State = HIGH;
      serialPrintlnSafe("SO_BT1_HIGH");
    }
    // 检测下降沿
    else if (lastState == HIGH && currentState == LOW) {
      btn1State = LOW;
      serialPrintlnSafe("SO_BT1_LOW");
    }

    lastState = currentState;
    delay(10);
  }
}

// 按钮2检测任务
void TaskBTN2code(void* pvParameters) {
  // 首次进入任务时读取实际电平作为初始值，避免上电时 GPIO 浮空触发虚假边沿事件
  bool lastState = digitalRead(BTN2_PIN);
  btn2State = lastState;

  for (;;) {
    bool currentState = digitalRead(BTN2_PIN);

    // 检测上升沿
    if (lastState == LOW && currentState == HIGH) {
      btn2State = HIGH;
      serialPrintlnSafe("SO_BT2_HIGH");
    }
    // 检测下降沿
    else if (lastState == HIGH && currentState == LOW) {
      btn2State = LOW;
      serialPrintlnSafe("SO_BT2_LOW");
    }

    lastState = currentState;
    delay(10);
  }
}

// 处理单条完整命令
// 响应统一发到 Serial (USB CDC / Windows 调试口)，不回发给 SerialLinux (Android)
// 原因：Android 高频发灯光命令（音乐律动）时，若每条命令都回响应到 UART0 TX，
//      会堵塞业务数据 SORC_xxx / SO_BTx 的发送，导致刷卡后 Android 收到延迟甚至丢失
// 参数 src 仅用于保留调用兼容性，响应不再写入 src
void processCommand(const String& command, Stream& /*src*/) {
  Stream& resp = Serial;  // 所有命令响应只发 Windows 调试口
  if (command.startsWith("MO_LED_")) {
    String modeStr = command.substring(7);
    int newMode = modeStr.toInt();
    if (newMode >= 0 && newMode <= 7) {
      singleLedMode = newMode;
      resp.print("Single LED set to mode: ");
      resp.println(newMode);
    } else {
      resp.println("Invalid single LED mode command");
    }
  } else if (command.startsWith("MO_LEDN_")) {
    String modeStr = command.substring(8);
    int newMode = modeStr.toInt();
    if (newMode >= 0 && newMode <= 5) {
      if (led2Brightness == 0) {
        resp.println("当前亮度为0，请先将亮度调整至0以上再切换显示模式！");
      } else {
        ledMode = newMode;
        if (newMode == 4) {
          trainPos = -TRAIN_LENGTH;
          trainPhase = 0;
          rainbowHue = random8();
        }
        if (newMode == 5) {
          memcpy(frozenLeds2, leds2, sizeof(leds2));
          frozenBrightness = led2Brightness;
        }
        resp.print("LED strip set to mode: ");
        resp.println(newMode);
      }
    } else {
      resp.println("Invalid LED strip mode command");
    }
  } else if (command.startsWith("MO_PWM_")) {
    String dutyStr = command.substring(7);
    int newDuty = dutyStr.toInt();
    if (newDuty == 1) {
      pwmDuty = 1023;
    } else if (newDuty == 0 || newDuty == 20 || newDuty == 40 || newDuty == 60 || newDuty == 80) {
      pwmDuty = (newDuty * 1023) / 100;
    } else {
      resp.println("Invalid PWM command");
    }
    resp.print("PWM set to: ");
    resp.print((pwmDuty * 100) / 1023);
    resp.println("%");
  } else if (command.startsWith("MO_BRI_")) {
    String levelStr = command.substring(7);
    levelStr.trim();
    if (levelStr.length() == 0) {
      resp.println("错误: 缺少亮度值");
      return;
    }
    bool isNumeric = true;
    for (char c : levelStr) {
      if (!isdigit(c)) { isNumeric = false; break; }
    }
    if (!isNumeric) {
      resp.println("错误: 亮度值必须为整数");
      return;
    }
    int level = levelStr.toInt();
    if (level >= 0 && level <= 100) {
      led2Brightness = brightnessMap[level];
      resp.print("LED亮度: ");
      resp.print(level);
      resp.println("%");
    } else {
      resp.println("错误: 亮度值需在0-100之间");
    }
  } else if (command == "RESET") {
    // 软复位：响应同时发到两个串口（Windows 和 Android 都能看到重启日志）
    Serial.println("System resetting...");
    SerialLinux.println("System resetting...");
    Serial.flush();
    SerialLinux.flush();
    delay(100);
    ESP.restart();
  }
}

// 从指定流读取命令字节，累积到完整一行后交给 processCommand 处理
// 每个流需要独立的命令缓冲区（通过引用传入 static 变量保持状态）
void handleCommandFromStream(Stream& src, String& cmdBuf) {
  while (src.available()) {
    // 命令长度保护：防止恶意或异常数据撑爆内存
    if (cmdBuf.length() > 64) {
      src.println("错误: 命令过长（最大64字符）");
      cmdBuf = "";
      while (src.available()) src.read();
      return;
    }

    char c = src.read();
    if (c == '\n') {
      cmdBuf.trim();  // 去掉末尾 \r 兼容不同行尾符
      if (cmdBuf.length() > 0) {
        processCommand(cmdBuf, src);
      }
      cmdBuf = "";
    } else {
      cmdBuf += c;
    }
  }
}




// 统一LED控制任务（防闪烁优化版本）
// 集中管理LED1（单颗）、LED2（灯带）、LED3（强制关闭）的显示逻辑
// 优化特性：
// 1. 30FPS稳定更新频率，防止闪烁
// 2. 修复模式5的双重亮度衰减问题
// 3. 统一亮度处理机制
// 4. 内存优化，减少不必要的数据拷贝
// 5. 防闪烁机制，确保LED显示稳定
void TaskLEDUnifiedCode(void* pvParameters) {
  static unsigned long lastLEDUpdate = 0;
  // 20FPS 更新频率：兼顾视觉流畅度和 RFID 稳定性
  // 每秒 20 次 WS2812 传输（每次 ~5.6ms 关中断），相比 30FPS 减少 33% 干扰窗口
  const unsigned long LED_UPDATE_INTERVAL = 50;
  
  for (;;) {
    unsigned long currentTime = millis();
    
    // 控制更新频率，避免过度占用CPU和闪烁问题
    if (currentTime - lastLEDUpdate < LED_UPDATE_INTERVAL) {
      delay(5);  // 增加延时，确保任务调度稳定
      continue;
    }
    lastLEDUpdate = currentTime;
    
    // ---- LED1 控制（单颗 LED）----
    if (singleLedMode >= 0 && singleLedMode <= 7) {
      leds1[0] = singleLedColors[singleLedMode];
    } else {
      leds1[0] = CRGB::Blue;
    }

    // ---- LED3 控制（熄灭）----
    leds3[0] = CRGB::Black;

    // ---- LED2 控制（灯带）----
    switch (ledMode) {
      case 0:  // 模式0：全部熄灭，关闭所有LED灯珠
        fill_solid(leds2, LED_COUNT_2, CRGB::Black);
        break;

      case 1:  // 模式1：纯白色静态光，亮度可通过led2Brightness调节
        fill_solid(leds2, LED_COUNT_2, CHSV(0, 0, led2Brightness));
        break;

      case 2:  // 模式2：彩虹流水灯，颜色沿灯带流动，速度和亮度可调
        for (int i = 0; i < LED_COUNT_2; i++) {
          leds2[i] = CHSV(rainbowHue + i * 256 / LED_COUNT_2, 255, led2Brightness);
        }
        rainbowHue++;
        break;
      case 3: // 模式3：彩虹呼吸灯（优化版本），缓慢变色配合呼吸效果
        {
          static unsigned long lastHueUpdate = 0;
          static unsigned long lastBreathUpdate = 0;
          static uint8_t breathingHue = 0;
          static uint8_t breathPhase = 0;
          
          unsigned long currentTime = millis();
          
          // 每300ms更新一次色相，实现非常缓慢的颜色变化
          if (currentTime - lastHueUpdate > 300) {
            breathingHue += 1;
            lastHueUpdate = currentTime;
          }
          
          // 每30ms更新一次呼吸相位，控制亮度变化节奏
          if (currentTime - lastBreathUpdate > 30) {
            breathPhase += 2;
            lastBreathUpdate = currentTime;
          }
          
          // 计算呼吸亮度：基于led2Brightness的60%-100%范围，避免过暗
          uint8_t minBrightness = led2Brightness * 60 / 100;
          uint8_t maxBrightness = led2Brightness;
          uint8_t breathValue = map(sin8(breathPhase), 0, 255, minBrightness, maxBrightness);
          
          for(int i = 0; i < LED_COUNT_2; i++) {
            leds2[i] = CHSV(breathingHue, 200, breathValue);
          }
        }
        break;


      case 4:  // 模式4：彩虹火车灯，模拟火车往返运行的动态效果
        if (millis() - lastUpdate > 30) {  // 30ms更新间隔，控制火车移动速度
          lastUpdate = millis();
          fill_solid(leds2, LED_COUNT_2, CRGB::Black);

          switch (trainPhase) {
            case 0:  // 阶段0：正向出站，火车从起点逐渐显现
              for (int i = 0; i < TRAIN_LENGTH; i++) {
                int pos = trainPos + i;
                if (pos >= 0 && pos < LED_COUNT_2) {
                  uint8_t hue = rainbowHue + (i * 256 / TRAIN_LENGTH);
                  leds2[pos] = CHSV(hue, 255, led2Brightness);
                }
              }
              trainPos++;
              if (trainPos >= 0) {
                trainPhase = 1;  // 切换到正向前进阶段
                trainPos = 0;
              }
              break;

            case 1:  // 阶段1：正向前进，火车完整显示并向终点移动
              for (int i = 0; i < TRAIN_LENGTH; i++) {
                int pos = trainPos + i;
                if (pos >= 0 && pos < LED_COUNT_2) {
                  uint8_t hue = rainbowHue + (i * 256 / TRAIN_LENGTH);
                  leds2[pos] = CHSV(hue, 255, led2Brightness);
                }
              }
              trainPos++;
              if (trainPos >= LED_COUNT_2 - TRAIN_LENGTH) {
                trainPhase = 2;  // 切换到正向进站阶段
                trainPos = LED_COUNT_2 - TRAIN_LENGTH;
              }
              break;

            case 2:  // 阶段2：正向进站，火车从尾部开始消失
              for (int i = 0; i < TRAIN_LENGTH; i++) {
                int displayPos = LED_COUNT_2 - 1 - i;
                if (displayPos >= trainPos) {
                  uint8_t hue = rainbowHue + (i * 256 / TRAIN_LENGTH);
                  leds2[displayPos] = CHSV(hue, 255, led2Brightness);
                }
              }
              trainPos++;
              if (trainPos >= LED_COUNT_2) {
                trainPhase = 3;  // 切换到反向出站阶段
                trainPos = 0;
                rainbowHue += 64;  // 改变彩虹颜色，增加视觉变化
              }
              break;

            case 3:  // 阶段3：反向出站，火车从终点逐渐显现
              for (int i = 0; i < trainPos + 1; i++) {
                int pos = LED_COUNT_2 - 1 - i;
                if (pos >= 0) {
                  uint8_t hue = rainbowHue + ((TRAIN_LENGTH - 1 - i) * 256 / TRAIN_LENGTH);
                  leds2[pos] = CHSV(hue, 255, led2Brightness);
                }
              }
              trainPos++;
              if (trainPos >= TRAIN_LENGTH) {
                trainPhase = 4;  // 切换到反向前进阶段
                trainPos = TRAIN_LENGTH;
              }
              break;

            case 4:  // 阶段4：反向前进，火车完整显示并向起点移动
              for (int i = 0; i < TRAIN_LENGTH; i++) {
                int pos = LED_COUNT_2 - trainPos + i;
                if (pos >= 0 && pos < LED_COUNT_2) {
                  uint8_t hue = rainbowHue + ((TRAIN_LENGTH - 1 - i) * 256 / TRAIN_LENGTH);
                  leds2[pos] = CHSV(hue, 255, led2Brightness);
                }
              }
              trainPos++;
              if (trainPos >= LED_COUNT_2) {
                trainPhase = 5;  // 切换到反向进站阶段
                trainPos = 0;
              }
              break;

            case 5:  // 阶段5：反向进站，火车从头部开始消失
              for (int i = 0; i < TRAIN_LENGTH - trainPos; i++) {
                int pos = i;
                if (pos < LED_COUNT_2) {
                  uint8_t hue = rainbowHue + ((TRAIN_LENGTH - 1 - i) * 256 / TRAIN_LENGTH);
                  leds2[pos] = CHSV(hue, 255, led2Brightness);
                }
              }
              trainPos++;
              if (trainPos >= TRAIN_LENGTH) {
                trainPhase = 0;  // 重新开始正向出站，形成循环
                trainPos = -TRAIN_LENGTH;
                rainbowHue += 64;  // 再次改变彩虹颜色
              }
              break;
          }
        }
        break;

      case 5:  // 模式5：冻结当前灯效，保持切换时的图像但允许调节亮度（内存优化版本）
        if (led2Brightness == 0) {
          fill_solid(leds2, LED_COUNT_2, CRGB::Black);  // 亮度为0时完全熄灭
        } else {
          // 计算相对亮度比例，避免双重衰减问题
          uint16_t brightnessRatio = (uint16_t)led2Brightness * 255 / frozenBrightness;
          if (brightnessRatio > 255) brightnessRatio = 255;
          
          // 直接计算并设置像素颜色，内存优化，避免使用memcpy
          for (int i = 0; i < LED_COUNT_2; i++) {
            leds2[i].r = (frozenLeds2[i].r * brightnessRatio) >> 8;
            leds2[i].g = (frozenLeds2[i].g * brightnessRatio) >> 8;
            leds2[i].b = (frozenLeds2[i].b * brightnessRatio) >> 8;
          }
        }
        break;
    }

    // ---- 最终统一刷新LED ----
    // 添加FastLED刷新保护，确保数据稳定后再显示
    FastLED.show();
    
    // ---- 稳定的延时机制 ----
    // 使用固定延时确保LED显示稳定，避免闪烁
    delay(10);  // 10ms延时，确保LED数据传输完成
  }
}


void setup() {
  // 初始化 USB CDC 串口（Windows 调试）
  // 增大 TX 缓冲区到 4KB：防止多任务并发写串口时 USB CDC 默认缓冲区溢出导致数据截断
  Serial.setTxBufferSize(4096);
  Serial.begin(115200);

  // 初始化 UART0（CH343/USB1 → Linux 业务通讯）
  // 默认引脚：TX=GPIO43、RX=GPIO44，波特率与 Linux 端保持一致
  SerialLinux.begin(115200);

  // 创建 Serial 输出互斥锁
  serialMutex = xSemaphoreCreateMutex();

  Serial.println("System starting...");
  SerialLinux.println("System starting...");

  // 初始化SPI总线
  SPI.begin(RFID_SCK_PIN, RFID_MISO_PIN, RFID_MOSI_PIN, RFID_SS_PIN);

  // 初始化RFID：显式硬件复位 + 版本校验 + 失败重试
  // 背景：MFRC522 库的 PCD_Init 自动判断 RST 电平，冷启动时 GPIO14 浮空可能读到 HIGH，
  //       库只做软件复位但 RC522 尚未完成上电 → 芯片卡在异常状态 → 刷卡永远失败。
  //       显式拉低再拉高 RST 可避免这个坑。GitHub miguelbalboa/rfid #229 #269 记录此问题。
  //       复用顶部定义的 rc522HardResetRuntime() 函数

  // 最多重试 3 次初始化，直到版本寄存器返回合法值
  bool rfidReady = false;
  for (uint8_t attempt = 1; attempt <= 3; attempt++) {
    rc522HardResetRuntime();  // 显式拉低再拉高 RST
    rfid.PCD_Init();
    byte version = rfid.PCD_ReadRegister(MFRC522::VersionReg);
    Serial.print("RC522 init attempt ");
    Serial.print(attempt);
    Serial.print(", VersionReg=0x");
    Serial.println(version, HEX);
    // 0x91=v1.0, 0x92=v2.0 为合法；0x00/0xFF 表示通讯异常
    if (version == 0x91 || version == 0x92) {
      rfidReady = true;
      break;
    }
    delay(100);  // 重试前等待
  }

  if (rfidReady) {
    Serial.println("RFID initialized.");
  } else {
    Serial.println("RFID initialization FAILED after 3 attempts! Check wiring/power.");
  }

  // 初始化LED
  FastLED.addLeds<WS2812, LED_PIN_1, GRB>(leds1, LED_COUNT_1);
  FastLED.addLeds<WS2812, LED_PIN_2, GRB>(leds2, LED_COUNT_2);
  FastLED.addLeds<WS2812, LED_PIN_3, GRB>(leds3, LED_COUNT_3);  // 新增LED3

  // 启动时先全黑，避免 186 颗 LED 同时点亮产生瞬时 4.5A 大电流
  // 冲击 3.3V/5V 电源导致刚初始化好的 RC522 进入异常状态
  // TaskLEDUnified 启动后会根据 ledMode/led2Brightness 自动恢复默认显示
  fill_solid(leds1, LED_COUNT_1, CRGB::Black);
  fill_solid(leds2, LED_COUNT_2, CRGB::Black);
  fill_solid(leds3, LED_COUNT_3, CRGB::Black);
  FastLED.show();
  Serial.println("LED initialized (dark startup, task will restore default).");

  // 初始化PWM
  // 创建LED控制器配置
  ledc_timer_config_t ledc_timer = {
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .duty_resolution = (ledc_timer_bit_t)PWM_RESOLUTION,
    .timer_num = (ledc_timer_t)PWM_CHANNEL,
    .freq_hz = PWM_FREQ,
    .clk_cfg = LEDC_AUTO_CLK
  };
  ledc_timer_config(&ledc_timer);

  // 创建LED通道配置
  ledc_channel_config_t ledc_channel = {
    .gpio_num = PWM_PIN,
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .channel = (ledc_channel_t)PWM_CHANNEL,
    .intr_type = LEDC_INTR_DISABLE,
    .timer_sel = (ledc_timer_t)PWM_CHANNEL,
    .duty = 0,
    .hpoint = 0
  };
  ledc_channel_config(&ledc_channel);

  // 设置初始占空比
  ledc_set_duty(LEDC_LOW_SPEED_MODE, (ledc_channel_t)PWM_CHANNEL, pwmDuty);
  ledc_update_duty(LEDC_LOW_SPEED_MODE, (ledc_channel_t)PWM_CHANNEL);

  Serial.println("PWM initialized.");

  // 初始化输入引脚
  pinMode(BTN0_PIN, INPUT_PULLUP);
  pinMode(WAKEUP1_PIN, INPUT);
  pinMode(BTN1_PIN, INPUT);
  pinMode(BTN2_PIN, INPUT);
  Serial.println("Inputs initialized.");

  // 创建任务
  // TaskRFID 放 Core 0：避开 Core 1 上 WS2812 bit-banging 关中断窗口，SPI 通讯更稳定
  xTaskCreatePinnedToCore(
    TaskRFIDcode, /* 任务函数 */
    "TaskRFID",   /* 任务名称 */
    4096,         /* 任务栈大小 */
    NULL,         /* 传递给任务的参数 */
    2,            /* 任务优先级（提高到 2，避免被按键任务频繁抢占）*/
    &TaskRFID,    /* 任务句柄 */
    0);           /* 运行在核心0上（与 LED 任务物理隔离）*/

  xTaskCreatePinnedToCore(
    TaskLEDUnifiedCode,
    "TaskLEDUnified",
    8192,  // 建议栈大一点
    NULL,
    3,  // 提高优先级，确保LED更新不被其他任务干扰
    NULL,
    1);

  xTaskCreatePinnedToCore(
    TaskPWMcode,
    "TaskPWM",
    1024,
    NULL,
    1,
    &TaskPWM,
    1);

  xTaskCreatePinnedToCore(
    TaskBTN0code,
    "TaskBTN0",
    2048,
    NULL,
    1,
    &TaskBTN0,
    0);

  xTaskCreatePinnedToCore(
    TaskWAKEUP1code,
    "TaskWAKEUP1",
    2048,
    NULL,
    1,
    &TaskWAKEUP1,
    0);

  xTaskCreatePinnedToCore(
    TaskBTN1code,
    "TaskBTN1",
    2048,
    NULL,
    1,
    &TaskBTN1,
    0);

  xTaskCreatePinnedToCore(
    TaskBTN2code,
    "TaskBTN2",
    2048,
    NULL,
    1,
    &TaskBTN2,
    0);

  Serial.println("Tasks created. System ready.");
}

void loop() {
  // 同时处理两个串口的命令输入（双端都能下发控制命令）
  // - Serial (USB-Serial-JTAG)：Windows 调试发命令
  // - SerialLinux (UART0 / CH343)：Android 开发板发命令
  // 两个缓冲区独立保存，避免一方半发命令被另一方打断
  static String cmdFromSerial = "";
  static String cmdFromLinux = "";
  handleCommandFromStream(Serial, cmdFromSerial);
  handleCommandFromStream(SerialLinux, cmdFromLinux);

  // 让出CPU时间
  delay(1);
}