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

ESP32矩阵按键游戏手柄深度睡眠与唤醒功能实现求助

ESP32游戏手柄深度睡眠与按键唤醒实现方案

核心实现思路

利用ESP32的EXT1外部唤醒功能实现多引脚触发唤醒,适配按键矩阵的任意按键唤醒需求;通过跟踪设备最后操作时间,超时1分钟后触发深度睡眠以节省电量。具体逻辑:

  • 记录每次用户操作(按键按下、编码器转动)的时间戳
  • 配置按键矩阵的行引脚为EXT1唤醒源,任意按键按下时触发电平变化唤醒设备
  • 循环检测时间差,超时则进入深度睡眠

代码修改步骤

1. 添加全局变量

在代码开头添加用于跟踪操作时间和超时阈值的变量:

unsigned long lastActivityTime = 0;
const unsigned long SLEEP_TIMEOUT = 60000; // 1分钟超时,单位毫秒

2. 配置深度睡眠唤醒引脚

在setup()函数末尾添加唤醒配置,将所有行引脚设为EXT1唤醒源:

// 配置EXT1唤醒:任意行引脚变为高电平时唤醒设备
uint64_t wakeupPinMask = 0;
for (uint8_t i = 0; i < ROWS; i++) {
  wakeupPinMask |= (1ULL << rowPins[i]);
}
esp_sleep_enable_ext1_wakeup(wakeupPinMask, ESP_EXT1_WAKEUP_ANY_HIGH);

3. 刷新操作时间戳

在所有用户操作的逻辑中添加时间刷新:

  • 按键矩阵事件中刷新:
    void keypadEvent(KeypadEvent key){
      lastActivityTime = millis(); // 刷新操作时间
      uint8_t keystate = customKeypad.getState();
      if (keystate==PRESSED)  { pressKey(key); }
      if (keystate==RELEASED) { releaseKey(key); }
    }
    
  • 编码器旋转时刷新:
    if (cntr!=prevenccntr[i]) {
      lastActivityTime = millis(); // 刷新操作时间
      // 原编码器处理代码...
    }
    
  • 补充编码器按键检测并刷新时间(原代码未实现):
    // -- ROTARY ENCODERS : PUSH SWITCH -- //
    bool currentPrs = digitalRead(uppPin[i]); // 假设按键接在uppPin,按下为高电平
    if (currentPrs != prevprs[i]) {
      lastActivityTime = millis();
      if (currentPrs) {
        sendKey(28 + i); // 示例按键码,需根据实际需求调整
      }
      prevprs[i] = currentPrs;
    }
    

4. 检测超时并进入深度睡眠

在loop()函数末尾添加超时检测逻辑:

// 检查是否超时无操作
if (millis() - lastActivityTime > SLEEP_TIMEOUT) {
  Serial.println("超时无操作,进入深度睡眠...");
  if (bleGamepad.isConnected()) {
    bleGamepad.end(); // 关闭BLE连接
  }
  esp_deep_sleep_start(); // 触发深度睡眠
}

完整修改后代码

#include <ESP32Encoder.h>
#include <Keypad.h>
#include <BleGamepad.h>

BleGamepad bleGamepad("WirelessGameHub", "breeze", 100);

// 深度睡眠相关全局变量
unsigned long lastActivityTime = 0;
const unsigned long SLEEP_TIMEOUT = 60000; // 1分钟超时

////////////////////// BUTTON MATRIX //////////////////////
#define ROWS 5
#define COLS 4
uint8_t rowPins[ROWS] = {13, 14, 15, 16, 33};
uint8_t colPins[COLS] = {17, 18, 19, 21};
uint8_t keymap[ROWS][COLS] = {
  {0,1,2,3},
  {4,5,6,7},
  {8,9,10,11},
  {12,13,14,15},
  {24,25,26,27}
};
Keypad customKeypad = Keypad( makeKeymap(keymap), rowPins, colPins, ROWS, COLS); 

//////////// ROTARY ENCODERS (WITH PUSH SWITCHES) ////////////
#define MAXENC 4
uint8_t uppPin[MAXENC] = {22, 25, 27, 04};
uint8_t dwnPin[MAXENC] = {23, 26, 32, 05};
uint8_t encoderUpp[MAXENC] = {16,18,20,22};
uint8_t encoderDwn[MAXENC] = {17,19,21,23};
ESP32Encoder encoder[MAXENC];
unsigned long holdoff[MAXENC] = {0,0,0,0};
int32_t prevenccntr[MAXENC] = {0,0,0,0};
bool prevprs[MAXENC] = {0,0,0,0};
#define HOLDOFFTIME 150   

void setup() {
  Serial.begin(115200);

  for (uint8_t i=0; i<MAXENC; i++) {
    encoder[i].clearCount();
    encoder[i].attachSingleEdge(dwnPin[i], uppPin[i]);
    pinMode(uppPin[i], INPUT_PULLDOWN); // 根据硬件接线调整上下拉
  }
  customKeypad.addEventListener(keypadEvent);
  customKeypad.setHoldTime(1);
  bleGamepad.begin();
  Serial.println("Booted!");

  // 配置EXT1唤醒源
  uint64_t wakeupPinMask = 0;
  for (uint8_t i = 0; i < ROWS; i++) {
    wakeupPinMask |= (1ULL << rowPins[i]);
  }
  esp_sleep_enable_ext1_wakeup(wakeupPinMask, ESP_EXT1_WAKEUP_ANY_HIGH);

  lastActivityTime = millis(); // 初始化操作时间
}

void loop() {
  unsigned long now = millis();

  // -- ROTARY ENCODERS : ROTATION -- //
  for (uint8_t i=0; i<MAXENC; i++) {
    int32_t cntr = encoder[i].getCount();
    if (cntr!=prevenccntr[i]) {
      lastActivityTime = now;
      if (!holdoff[i]) {
        if (cntr>prevenccntr[i]) { sendKey(encoderUpp[i]); }
        if (cntr<prevenccntr[i]) { sendKey(encoderDwn[i]); }
        holdoff[i] = now;
        if (holdoff[i]==0) holdoff[i] = 1;
      }
      else if (now-holdoff[i] > HOLDOFFTIME) {
        prevenccntr[i] = encoder[i].getCount();
        holdoff[i] = 0;
      }
    }
    
    // -- ROTARY ENCODERS : PUSH SWITCH -- //
    bool currentPrs = digitalRead(uppPin[i]);
    if (currentPrs != prevprs[i]) {
      lastActivityTime = now;
      if (currentPrs) {
        sendKey(28 + i); // 示例按键码,按需调整
      }
      prevprs[i] = currentPrs;
    }
  }

  customKeypad.getKey();    // 读取按键矩阵

  // 超时检测
  if (now - lastActivityTime > SLEEP_TIMEOUT) {
    Serial.println("进入深度睡眠...");
    if (bleGamepad.isConnected()) bleGamepad.end();
    esp_deep_sleep_start();
  }
}

void keypadEvent(KeypadEvent key){
  lastActivityTime = millis();
  uint8_t keystate = customKeypad.getState();
  if (keystate==PRESSED)  { pressKey(key); }
  if (keystate==RELEASED) { releaseKey(key); }
}

void sendKey(uint8_t key) {
    uint32_t gamepadbutton = pow(2,key);
    Serial.print("pulse	");
    Serial.println(key);
    if(bleGamepad.isConnected()) {
      bleGamepad.press(gamepadbutton);
      delay(150);
      bleGamepad.release(gamepadbutton);
    }
}

void pressKey(uint8_t key) {
    uint32_t gamepadbutton = pow(2,key);
    Serial.print("hold	");
    Serial.println(key);
    if(bleGamepad.isConnected()) {
      bleGamepad.press(gamepadbutton);
    }
}

void releaseKey(uint8_t key) {
    uint32_t gamepadbutton = pow(2,key);
    Serial.print("release	");
    Serial.println(key);
    if(bleGamepad.isConnected()) {
      bleGamepad.release(gamepadbutton);
    }
}

注意事项

  • 引脚上下拉配置需匹配硬件接线:若按键按下时行引脚为低电平,需将唤醒模式改为ESP_EXT1_WAKEUP_ANY_LOW
  • 深度睡眠唤醒后,设备会重新执行setup(),BLE连接需重新建立
  • 编码器按键的检测逻辑需根据实际硬件调整,示例假设按键接在uppPin并使用下拉输入
  • millis()约50天后会溢出,可添加溢出处理逻辑优化超时检测

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 21:02:58