#include SPI.h
#include MFRC522.h
#include FastLED.h
#include Arduino.h
#include driverledc.h

 RFID引脚定义
#define RFID_RST_PIN 14    RC522 复位引脚
#define RFID_SS_PIN 10     RC522 片选引脚
#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输出引脚
#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;

 全局变量
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%左右（102255≈40%）



 单颗LED颜色数组
CRGB singleLedColors[8] = {
  CRGBBlack,    0 熄灭
  CRGBBlue,     1 蓝色
  CRGBGreen,    2 绿色
  CRGBOrange,   3 橙色
  CRGBRed,      4 红色
  CRGBPurple,   5 紫色
  CRGBYellow,   6 黄色
  CRGBWhite     7 白色
};

 RFID读取任务
void TaskRFIDcode(void pvParameters) {
  for (;;) {
     寻找新卡片
    if (!rfid.PICC_IsNewCardPresent()) {
      delay(10);
      continue;
    }

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

     读取卡片数据（用户数据区）
    String cardData = ;
    MFRC522MIFARE_Key key;

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

     选择卡片
    MFRC522StatusCode status;
    status = rfid.PCD_Authenticate(MFRC522PICC_CMD_MF_AUTH_KEY_A, 4, &key, &(rfid.uid));
    if (status != MFRC522STATUS_OK) {
      Serial.print(F(Authentication failed ));
      Serial.println(rfid.GetStatusCodeName(status));
      rfid.PICC_HaltA();
      rfid.PCD_StopCrypto1();
      delay(100);
      continue;
    }

     读取数据块
    byte buffer[18];
    byte size = sizeof(buffer);
    status = rfid.MIFARE_Read(4, buffer, &size);
    if (status != MFRC522STATUS_OK) {
      Serial.print(F(Reading failed ));
      Serial.println(rfid.GetStatusCodeName(status));
    } else {
       转换为ASCII字符串
      for (byte i = 0; i  16; i++) {
        if (buffer[i] = 32 && buffer[i] = 126) {   可打印ASCII字符
          cardData += (char)buffer[i];
        }
      }

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

       卡片数据处理
      if (cardData != lastCardData && !cardData.isEmpty()) {
        lastCardData = cardData;
        Serial.println(SORC_ + cardData);
      }
    }

     使放置在读卡区的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;
  static bool lastState = HIGH;

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

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

    lastState = currentState;
    delay(10);
  }
}

 WAKEUP1检测任务
void TaskWAKEUP1code(void pvParameters) {
  static bool lastState = LOW;

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

     检测上升沿
    if (lastState == LOW && currentState == HIGH) {
      wakeup1State = HIGH;
      Serial.println(SO_WAKEUP1);
    }
     检测下降沿
    else if (lastState == HIGH && currentState == LOW) {
      wakeup1State = LOW;
      Serial.println(SO_WAKEUP0);
    }

    lastState = currentState;
    delay(10);
  }
}

 按钮1检测任务
void TaskBTN1code(void pvParameters) {
  static bool lastState = LOW;

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

     检测上升沿
    if (lastState == LOW && currentState == HIGH) {
      btn1State = HIGH;
      Serial.println(SO_BT1_HIGH);
    }
     检测下降沿
    else if (lastState == HIGH && currentState == LOW) {
      btn1State = LOW;
      Serial.println(SO_BT1_LOW);
    }

    lastState = currentState;
    delay(10);
  }
}

 按钮2检测任务
void TaskBTN2code(void pvParameters) {
  static bool lastState = LOW;

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

     检测上升沿
    if (lastState == LOW && currentState == HIGH) {
      btn2State = HIGH;
      Serial.println(SO_BT2_HIGH);
    }
     检测下降沿
    else if (lastState == HIGH && currentState == LOW) {
      btn2State = LOW;
      Serial.println(SO_BT2_LOW);
    }

    lastState = currentState;
    delay(10);
  }
}

 串口命令处理
void handleSerialCommand() {
  static String command = ;

  while (Serial.available()) {
     先检查命令长度（新增的防护代码）
    if (command.length()  64) {
      Serial.println(错误 命令过长（最大64字符）);
      command = ;                               清空当前命令
      while (Serial.available()) Serial.read();   清空串口缓冲区
      continue;
    }

    char c = Serial.read();
    if (c == 'n') {
       处理命令
      if (command.startsWith(MO_LED_)) {
        String modeStr = command.substring(7);
        int newMode = modeStr.toInt();

         控制单颗LED
        if (newMode = 0 && newMode = 7) {
          singleLedMode = newMode;
          Serial.print(Single LED set to mode );
          Serial.println(newMode);
        } else {
          Serial.println(Invalid single LED mode command);
        }
      } else if (command.startsWith(MO_LEDN_)) {
        String modeStr = command.substring(8);
        int newMode = modeStr.toInt();

         控制灯带（只有亮度不为0时才允许）
        if (newMode = 0 && newMode = 5) {
          if (led2Brightness == 0) {
            Serial.println(当前亮度为0，请先将亮度调整至0以上再切换显示模式！);
          } else {
            ledMode = newMode;
             重置火车灯状态
            if (newMode == 4) {
              trainPos = -TRAIN_LENGTH;
              trainPhase = 0;
              rainbowHue = random8();
            }

             新增：切换到模式5时，复制当前LED2状态和亮度
            if (newMode == 5) {
              memcpy(frozenLeds2, leds2, sizeof(leds2));
              frozenBrightness = led2Brightness;   保存冻结时的亮度
            }

            Serial.print(LED strip set to mode );
            Serial.println(newMode);
          }
        } else {
          Serial.println(Invalid LED strip mode command);
        }
      } else if (command.startsWith(MO_PWM_)) {
        String dutyStr = command.substring(7);
        int newDuty = dutyStr.toInt();

         检查PWM百分比
        if (newDuty == 1) {
          pwmDuty = 1023;   100%
        } else if (newDuty == 0  newDuty == 20  newDuty == 40  newDuty == 60  newDuty == 80) {
          pwmDuty = (newDuty  1023)  100;   转换为实际占空比
        } else {
          Serial.println(Invalid PWM command);
        }

        Serial.print(PWM set to );
        Serial.print((pwmDuty  100)  1023);
        Serial.println(%);
      }

      else if (command.startsWith(MO_BRI_)) {
         提取亮度参数（跳过MO_BRI_前缀）
        String levelStr = command.substring(7);
        levelStr.trim();
        command = ;   清空命令缓冲区

         空参数检查
        if (levelStr.length() == 0) {
          Serial.println(错误 缺少亮度值);
          return;   终止处理
        }

         严格数字验证（拒绝非数字字符）
        bool isNumeric = true;
        for (char c  levelStr) {
          if (!isdigit(c)) {
            isNumeric = false;
            break;   发现非数字立即退出
          }
        }

         非数字错误处理
        if (!isNumeric) {
          Serial.println(错误 亮度值必须为整数);
          return;
        }

         转换为整数并验证范围
        int level = levelStr.toInt();
        if (level = 0 && level = 100) {
           更新亮度值（映射到PWM范围）
          led2Brightness = brightnessMap[level];   使用预定义映射表
          Serial.print(LED亮度 );
          Serial.print(level);
          Serial.println(%);

              亮度为0时输出警告
             if (level == 0) {
               Serial.println(亮度已设置为0，所有灯光将熄灭！);
             }
        } else {
          Serial.println(错误 亮度值需在0-100之间);
        }
        command = ;   清空命令
      }

      command = ;   清空命令
    } else {
       累积非换行符字符
      command += c;
    }
  }
}




 统一LED控制任务（防闪烁优化版本）
 集中管理LED1（单颗）、LED2（灯带）、LED3（强制关闭）的显示逻辑
 优化特性：
 1. 30FPS稳定更新频率，防止闪烁
 2. 修复模式5的双重亮度衰减问题
 3. 统一亮度处理机制
 4. 内存优化，减少不必要的数据拷贝
 5. 防闪烁机制，确保LED显示稳定
void TaskLEDUnifiedCode(void pvParameters) {
  static unsigned long lastLEDUpdate = 0;
  const unsigned long LED_UPDATE_INTERVAL = 33;  ~30FPS，降低更新频率减少闪烁
  
  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] = CRGBBlue;
    }

     ---- LED3 控制（熄灭）----
    leds3[0] = CRGBBlack;

     ---- LED2 控制（灯带）----
    switch (ledMode) {
      case 0   模式0：全部熄灭，关闭所有LED灯珠
        fill_solid(leds2, LED_COUNT_2, CRGBBlack);
        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, CRGBBlack);

          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, CRGBBlack);   亮度为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() {
   初始化串口
  Serial.begin(115200);
  Serial.println(System starting...);

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

   初始化RFID
  rfid.PCD_Init();
  Serial.println(RFID initialized.);

   初始化LED
  FastLED.addLedsWS2812, LED_PIN_1, GRB(leds1, LED_COUNT_1);
  FastLED.addLedsWS2812, LED_PIN_2, GRB(leds2, LED_COUNT_2);
  FastLED.addLedsWS2812, LED_PIN_3, GRB(leds3, LED_COUNT_3);   新增LED3

   初始化LED状态
  fill_solid(leds1, LED_COUNT_1, singleLedColors[singleLedMode]);
  fill_solid(leds2, LED_COUNT_2, CHSV(0, 0, led2Brightness));   初始化白色
  fill_solid(leds3, LED_COUNT_3, CRGBBlack);                  强制GPIO48的灯珠熄灭
  FastLED.show();
  Serial.println(LED initialized.);

   初始化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.);

   创建任务
  xTaskCreatePinnedToCore(
    TaskRFIDcode,  任务函数 
    TaskRFID,    任务名称 
    4096,          任务栈大小 
    NULL,          传递给任务的参数 
    1,             任务优先级 
    &TaskRFID,     任务句柄 
    1);            运行在核心1上 

  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() {
   处理串口命令
  handleSerialCommand();

   让出CPU时间
  delay(1);
}