#define LED_PIN   40

uint8_t keys[] = {4,5,6};
EventBits_t key_events[] = {1,2,4,7};

EventGroupHandle_t key_event;         // 按键事件组
volatile TickType_t keyDeounce = 0;   // 按下按钮的时间
TimerHandle_t flash_timer = NULL;     // 闪烁用的定时器

// 定时器回调函数
void vTimerCallback( TimerHandle_t xTimer ){
  digitalWrite(LED_PIN, !digitalRead(LED_PIN));
}

// 按键时间处理函数
void key_event_task_entry(void *params){
  printf("安检服务程序启动...\n");
  EventBits_t uxBits;
  uint16_t period=0;     // 闪烁间隔时间
  while(1){
    uxBits= xEventGroupWaitBits(key_event,                // 事件组句柄
                                key_events[3],            // 等待事件
                                pdTRUE,                   // 退出后清空所有位
                                pdFALSE,                  // 任何一位到达都触发
                                portMAX_DELAY);           // 无限期等待(50天)
    if(uxBits>0 && ((xTaskGetTickCount() - keyDeounce) < 200)){
      // 如果不是超时
      switch(uxBits){
        case 1:
          period = 1000;
          break;
        case 2:
          period = 500;
          break;
        case 4:
          // 暂停或恢复闪烁
          if(flash_timer !=NULL){
            if(xTimerIsTimerActive(flash_timer)){
              // 启动中,暂停
              if(xTimerStop(flash_timer, 0) == pdPASS){
                printf("定时器停止成功!\n");
              }else{
                printf("错误,定时器无法停止!\n");
              }
            }else{
              // 暂停中,启动
              if(xTimerReset(flash_timer, 0) == pdPASS){  // Reset后会重启,暂停时间也会归零
                printf("定时器重置成功!\n");
              }else{
                printf("错误,定时器无法重置!\n");
              }
            }
          }else{
            printf("还没有创建定时器!\n");
          }
          goto exit;
          break;
      }
      if(flash_timer == NULL){
        // 创建新的定时器
        flash_timer = xTimerCreate("Timer_Flash",   // 定时器的名字
                                  period,           // 调用间隔时间
                                  pdTRUE,           // 是否重复运行
                                  NULL,             // 定时器标识符,相当于参数
                                  vTimerCallback);
        if(xTimerStart(flash_timer,0) == pdPASS){   //立即启动
          printf("开启闪烁,间隔 %d ms\n", period);
        }else{
          printf("错误,启动闪烁失败!");
        }
      }else{
        // 如果定时器存在,则停止并删除定时器
        if(xTimerIsTimerActive(flash_timer)){
          // 定时器正在运行中,先停止,这一步可以不操作,直接删除效果也是一样的
          if(xTimerStop(flash_timer, 0) != pdPASS){
            printf("错误,定时器无法停止!\n");
          }
        }
        if(xTimerDelete(flash_timer,0) == pdPASS){  // 立即删除
          flash_timer = NULL;
          printf("停止闪烁!\n");
          digitalWrite(LED_PIN, LOW);
        }else{
          printf("错误,定时器无法删除!\n");
        }
      }
exit:
      delay(500);
      xEventGroupGetBits(key_event);  // 去抖动
    }
  }
}

// 中断按键
void IRAM_ATTR KEY_ISR(){
  EventBits_t e=0;
  for(int i=0; i<3; i++){
    if(digitalRead(keys[i])==LOW){
      e = key_events[i];
    }
  }
  if(e>0){
    keyDeounce = xTaskGetTickCountFromISR();    // 记录下按下的时间,用于放抖动,正式开发中不要这样写,有Bug
    xEventGroupSetBitsFromISR(key_event, e, NULL);
  }
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("Hello, ESP32-S3!");
  // 初始化LED
  pinMode(LED_PIN, OUTPUT);
  // 初始化事件组
  key_event = xEventGroupCreate();

  // 安装中断
  for(int i=0; i<3; i++){
    pinMode(keys[i], INPUT_PULLUP);
    attachInterrupt(keys[i], KEY_ISR, FALLING);
  }

  // 启动按键服务线程
  xTaskCreate(key_event_task_entry, "KEY_SERVICE", 10240, NULL, 1, NULL);

  vTaskDelete(NULL);
}

void loop() {
}
esp:0
esp:1
esp:2
esp:3
esp:4
esp:5
esp:6
esp:7
esp:8
esp:9
esp:10
esp:11
esp:12
esp:13
esp:14
esp:15
esp:16
esp:17
esp:18
esp:19
esp:20
esp:21
esp:35
esp:36
esp:37
esp:38
esp:39
esp:40
esp:41
esp:42
esp:45
esp:46
esp:47
esp:48
esp:3V3.1
esp:3V3.2
esp:RST
esp:5V
esp:GND.1
esp:GND.2
esp:TX
esp:RX
esp:GND.3
esp:GND.4
btn1:1.l
btn1:2.l
btn1:1.r
btn1:2.r
btn2:1.l
btn2:2.l
btn2:1.r
btn2:2.r
led1:A
led1:C
btn3:1.l
btn3:2.l
btn3:1.r
btn3:2.r