You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32-C3深度睡眠唤醒后LED无法立即点亮的问题求助

ESP32-C3深度睡眠唤醒同步点亮LED解决方案

核心问题根因

  1. 唤醒事件未被捕获:深度睡眠唤醒时,按钮处于按下(低电平)状态,但lastButtonState初始值为0,与当前buttonState一致,触发不了计数器递增的状态变化逻辑,必须等按钮松开再按下才会触发。
  2. Preferences未正确持久化:原代码仅在setup中读取计数器后立即写回,未在计数器实际变化时保存,导致深度睡眠后计数器重置。
  3. 状态变量未跨睡眠保留:lastButtonState、ledState等变量未用RTC_DATA_ATTR标记,深度睡眠后会被重置,无法正确追踪按钮状态变化。

关键修改点

  • 在setup中检测GPIO唤醒原因,直接递增计数器并更新LED状态
  • 将需要跨睡眠保留的变量标记为RTC_DATA_ATTR
  • 仅在计数器变化时调用Preferences保存,确保数据持久化
  • 优化按钮状态检测逻辑,避免重复触发

修改后的完整代码

#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <Wire.h>
#include <Preferences.h>

const int buttonPin = 3;  // 状态控制按钮引脚
const int buttonPin2 = 2; // BLE配对按钮引脚
const int ledPin =  5;    // 绿色LED引脚
const int ledPin2 = 6;    // 红色LED引脚
const int ledPin3 = 7;    // 蓝色LED引脚

// 用RTC_DATA_ATTR标记跨睡眠保留的变量
RTC_DATA_ATTR int buttonState = 0;     
RTC_DATA_ATTR int lastButtonState = 0;
int buttonState2 = 0;
int lastButtonState2 = 0;  
RTC_DATA_ATTR int ledState = 0;
RTC_DATA_ATTR int counter = 0; 

#define SERVICE_UUID        "f0e279fc-a979-4ffd-924c-59ced5392948"
#define CHARACTERISTIC_UUID "7f98738e-382d-43d9-b256-3cf8bd3120b7"

BLEServer *pServer = NULL;
BLEService *pService = NULL;
BLECharacteristic *pCharacteristic = NULL;

Preferences preferences;

bool deviceConnected = false;
class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
      Serial.println("Connected");
      delay(3000);
      Serial.println("Entering deep sleep");
      digitalWrite(ledPin3, LOW);  
      esp_deep_sleep_start();  
    };

    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
      Serial.println("Disconnected!");
      Serial.println("Advertising...");
      pServer->startAdvertising(); 
    }
};

class MyCallbacks: public BLECharacteristicCallbacks
{
  void onWrite(BLECharacteristic *pCharacteristic)
  {
    std::string value = pCharacteristic->getValue();

    if (value.length() > 0)
    {
      Serial.println("*********");
      Serial.print("New value: ");
      for (int i = 0; i < value.length(); i++)
      {
        Serial.print(value[i]);
      }
      Serial.println();
      Serial.println("*********");
    }
  }
};

void setupBLEServer(void) {
  Serial.println("Starting BLE Server!");
  BLEDevice::init("Pirouette-IoT-Device");
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  pService = pServer->createService(SERVICE_UUID);

  pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY);

  pCharacteristic->setCallbacks(new MyCallbacks());

  pCharacteristic->setValue("device_state_0_stored");
  pService->start();
  BLEAdvertising *pAdvertising = pServer->getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  
  pAdvertising->setMinPreferred(0x12);
  pAdvertising->start();
}

void pairing(){
  digitalWrite(ledPin3, HIGH);
  setupBLEServer();
}

void print_wakeup_reason() {
  esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();

  switch (wakeup_reason) {
    case ESP_SLEEP_WAKEUP_EXT0:
      Serial.println("Wakeup caused by external signal (RTC_IO)");
      break;
    case ESP_SLEEP_WAKEUP_EXT1:
      Serial.println("Wakeup caused by external signal (RTC_CNTL)");
      break;
    case ESP_SLEEP_WAKEUP_TIMER:
      Serial.println("Wakeup caused by timer");
      break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD:
      Serial.println("Wakeup caused by touchpad");
      break;
    case ESP_SLEEP_WAKEUP_ULP:
      Serial.println("Wakeup caused by ULP program");
      break;
    case ESP_SLEEP_WAKEUP_GPIO:
      Serial.println("Wakeup caused by GPIO button");
      break;
    default:
      Serial.printf("Wakeup not from deep sleep: %d\n", wakeup_reason);
      break;
  }
}

void setup() {
  Serial.begin(115200);
 
  // 初始化LED引脚为输出
  pinMode(ledPin, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);
  
  // 初始化按钮引脚为输入
  pinMode(buttonPin, INPUT);
  pinMode(buttonPin2, INPUT_PULLUP);

  // 启用GPIO唤醒(低电平触发)
  esp_deep_sleep_enable_gpio_wakeup(1 << buttonPin, ESP_GPIO_WAKEUP_GPIO_LOW);
  gpio_set_direction((gpio_num_t)buttonPin, GPIO_MODE_INPUT);

  // 从Preferences加载计数器
  preferences.begin("led-state", false);
  counter = preferences.getInt("counter", 0);
  preferences.end();

  // 检测唤醒原因,若为GPIO唤醒则直接递增计数器
  esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
  if (wakeup_reason == ESP_SLEEP_WAKEUP_GPIO || wakeup_reason == ESP_SLEEP_WAKEUP_EXT0) {
    counter++;
    ledState = !ledState;
    Serial.print("Button press count: ");
    Serial.println(counter);

    // 保存更新后的计数器
    preferences.begin("led-state", false);
    preferences.putInt("counter", counter);
    preferences.end();
  }

  // 初始化LED状态
  if(counter == 1){
    digitalWrite(ledPin, ledState);
    if(pCharacteristic != NULL){
      pCharacteristic->setValue("device_state_1_ready");
      pCharacteristic->notify();
    }
  } else if(counter == 3){
    digitalWrite(ledPin2, HIGH);
    digitalWrite(ledPin, LOW);
    if(pCharacteristic != NULL){
      pCharacteristic->setValue("device_state_3_locked");
      pCharacteristic->notify();
    }
  } else if (counter >= 4){
    digitalWrite(ledPin, LOW);
    digitalWrite(ledPin2, LOW);
  }
}

void loop(){
  // BLE配对按钮处理
  buttonState2 = digitalRead(buttonPin2);
  if (lastButtonState2 == HIGH && buttonState2 == LOW) {
    Serial.println("BLE pairing button pressed");
    pairing();
  }
  lastButtonState2 = buttonState2;
  
  // 打印唤醒原因(仅首次进入loop时执行,避免重复打印)
  static bool wakeup_printed = false;
  if(!wakeup_printed){
    print_wakeup_reason();
    wakeup_printed = true;
  }
  
  // 状态按钮处理(处理唤醒后的按钮松开和再次按下)
  buttonState = digitalRead(buttonPin);
  if (buttonState != lastButtonState) {
    // 处理按钮松开事件(高电平)
    if (lastButtonState == LOW && buttonState == HIGH) {
      // 可在此添加松开后的逻辑
    }
    // 处理按钮再次按下事件(低电平)
    else if (lastButtonState == HIGH && buttonState == LOW) {
      counter++;
      ledState = !ledState;
      Serial.print("Button press count: ");
      Serial.println(counter);

      // 保存计数器到Preferences
      preferences.begin("led-state", false);
      preferences.putInt("counter", counter);
      preferences.end();

      // 更新LED状态
      if(counter == 1){
        digitalWrite(ledPin, ledState);
        if(pCharacteristic != NULL){
          pCharacteristic->setValue("device_state_1_ready");
          pCharacteristic->notify();
        }
      } else if(counter == 3){
        digitalWrite(ledPin2, HIGH);
        digitalWrite(ledPin, LOW);
        if(pCharacteristic != NULL){
          pCharacteristic->setValue("device_state_3_locked");
          pCharacteristic->notify();
        }
      } else if (counter >= 4){
        digitalWrite(ledPin, LOW);
        digitalWrite(ledPin2, LOW);
      }
    }
    delay(50); // 消抖
  }
  lastButtonState = buttonState;

  delay(50);
}

修改说明

  1. RTC变量标记:将buttonState、lastButtonState、ledState、counter标记为RTC_DATA_ATTR,确保深度睡眠后这些变量的值不会丢失。
  2. 唤醒时直接处理:在setup中检测到GPIO唤醒时,直接递增计数器并更新LED状态,实现唤醒即点亮LED。
  3. Preferences优化:仅在计数器实际变化时写入Preferences,避免无效操作,同时确保计数器值在深度睡眠后保留。
  4. 按钮逻辑优化:拆分按钮按下和松开的处理逻辑,避免重复触发,添加消抖延迟。

内容的提问来源于stack exchange,提问作者Abhi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 02:08:11