#include "buttons.h" #ifdef FEATURE_PHYSICAL_BUTTONS #include extern BLEHidAdafruit blehid; extern Config cfg; static uint8_t physBtnMask = 0; static uint8_t rawMaskPrev = 0; static unsigned long debounceMs = 0; static const unsigned long DEBOUNCE_MS = 20; // Double-press detection for left button static const unsigned long DOUBLE_PRESS_MS = 400; // max gap between two releases static const unsigned long KEY_HOLD_MS = 60; // how long to hold the key down static unsigned long lastLeftReleaseMs = 0; static unsigned long keyDownUntil = 0; // Setup void setupPhysicalButtons() { // Release any held physical buttons before reconfiguring if (physBtnMask && Bluefruit.connected()) { blehid.mouseButtonRelease(); } physBtnMask = 0; if (BTN_LEFT_PIN != BTN_PIN_NONE) pinMode(BTN_LEFT_PIN, INPUT_PULLUP); if (BTN_RIGHT_PIN != BTN_PIN_NONE) pinMode(BTN_RIGHT_PIN, INPUT_PULLUP); if (BTN_MIDDLE_PIN != BTN_PIN_NONE) pinMode(BTN_MIDDLE_PIN, INPUT_PULLUP); bool any = (BTN_LEFT_PIN != BTN_PIN_NONE) || (BTN_RIGHT_PIN != BTN_PIN_NONE) || (BTN_MIDDLE_PIN != BTN_PIN_NONE); if (any) { Serial.print("[BTN] L="); BTN_LEFT_PIN == BTN_PIN_NONE ? Serial.print("--") : Serial.print(BTN_LEFT_PIN); Serial.print(" R="); BTN_RIGHT_PIN == BTN_PIN_NONE ? Serial.print("--") : Serial.print(BTN_RIGHT_PIN); Serial.print(" M="); BTN_MIDDLE_PIN == BTN_PIN_NONE ? Serial.print("--") : Serial.print(BTN_MIDDLE_PIN); Serial.println(); } } // Poll and report // Called every loop iteration (before rate limiter) for immediate response. // Uses active-low logic: INPUT_PULLUP, button connects pin to GND. void processPhysicalButtons() { if (!Bluefruit.connected()) return; unsigned long now = millis(); // Release held key combo after KEY_HOLD_MS if (keyDownUntil && now >= keyDownUntil) { uint8_t noKeys[6] = {}; blehid.keyboardReport(0, noKeys); keyDownUntil = 0; Serial.println("[BTN] key release"); } uint8_t rawMask = 0; if (BTN_LEFT_PIN != BTN_PIN_NONE && digitalRead(BTN_LEFT_PIN) == LOW) rawMask |= MOUSE_BUTTON_LEFT; if (BTN_RIGHT_PIN != BTN_PIN_NONE && digitalRead(BTN_RIGHT_PIN) == LOW) rawMask |= MOUSE_BUTTON_RIGHT; if (BTN_MIDDLE_PIN != BTN_PIN_NONE && digitalRead(BTN_MIDDLE_PIN) == LOW) rawMask |= MOUSE_BUTTON_MIDDLE; if (rawMask != rawMaskPrev) { rawMaskPrev = rawMask; debounceMs = now; } if (rawMask != physBtnMask && (now - debounceMs >= DEBOUNCE_MS)) { uint8_t newMask = rawMask; uint8_t pressed = newMask & ~physBtnMask; uint8_t released = physBtnMask & ~newMask; physBtnMask = newMask; if (physBtnMask) blehid.mouseButtonPress(physBtnMask); else blehid.mouseButtonRelease(); if (pressed & MOUSE_BUTTON_LEFT) Serial.println("[BTN] L press"); if (pressed & MOUSE_BUTTON_RIGHT) Serial.println("[BTN] R press"); if (pressed & MOUSE_BUTTON_MIDDLE) Serial.println("[BTN] M press"); if (released & MOUSE_BUTTON_LEFT) { unsigned long gap = lastLeftReleaseMs ? (now - lastLeftReleaseMs) : 0; Serial.print("[BTN] L release - gap="); Serial.print(gap); Serial.print("ms (max="); Serial.print(DOUBLE_PRESS_MS); Serial.println("ms)"); // Double-press detection: two short presses → fire key combo if (lastLeftReleaseMs && (gap <= DOUBLE_PRESS_MS)) { uint8_t keys[6] = {cfg.tapKey, 0, 0, 0, 0, 0}; blehid.keyboardReport(cfg.tapMod, keys); keyDownUntil = now + KEY_HOLD_MS; lastLeftReleaseMs = 0; Serial.print("[BTN] Double-press → key 0x"); Serial.print(cfg.tapKey, HEX); Serial.print(" mod 0x"); Serial.println(cfg.tapMod, HEX); } else { lastLeftReleaseMs = now; } } if (released & MOUSE_BUTTON_RIGHT) Serial.println("[BTN] R release"); if (released & MOUSE_BUTTON_MIDDLE) Serial.println("[BTN] M release"); } } #endif // FEATURE_PHYSICAL_BUTTONS