Files
air-mouse/source/tap.cpp

123 lines
4.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "tap.h"
#ifdef FEATURE_TAP_DETECTION
#include "imu.h"
#include <bluefruit.h>
extern BLEHidAdafruit blehid;
// ─── Tap detection setup ──────────────────────────────────────────────────────
// REG_TAP_THS_6D bits[4:0] = tapThreshold (131); 1 LSB = FS/32 = 62.5 mg at ±2g.
// REG_INT_DUR2 at ODR=416 Hz:
// SHOCK[7:6] = 2 → 38 ms max tap duration
// QUIET[5:4] = 2 → 19 ms refractory after tap
// DUR[3:0] = 6 → 115 ms max inter-tap window for double detection
void applyTapThreshold() {
uint8_t thr = cfg.tapThreshold;
if (thr < 1) thr = 1;
if (thr > 31) thr = 31;
imuWriteReg(REG_TAP_THS_6D, thr & 0x1F);
}
void setupTapDetection() {
imuWriteReg(REG_CTRL1_XL, 0x60); // ODR=416 Hz, FS=±2g
imuWriteReg(REG_TAP_CFG, 0x8E); // TIMER_EN + LIR + TAP_Z/Y/X enabled
applyTapThreshold();
imuWriteReg(REG_INT_DUR2, 0x62); // SHOCK=2(38ms), QUIET=2(19ms), DUR=6(115ms)
imuWriteReg(REG_WAKE_UP_THS, 0x80); // bit7=1 → single + double tap both enabled
imuWriteReg(REG_MD1_CFG, 0x48); // route single-tap(0x08) + double-tap(0x40) → INT1
Serial.print("[TAP] threshold="); Serial.print(cfg.tapThreshold);
Serial.print(" (~"); Serial.print(cfg.tapThreshold * 62.5f, 0); Serial.println(" mg)");
}
// ─── Tap processing ───────────────────────────────────────────────────────────
// Only double-tap is mapped to an action. Single-tap is ignored (it always fires
// before the double is confirmed and cannot be reliably disambiguated on this
// hardware without an unacceptable latency penalty).
//
// The LSM6DS3 sets SINGLE_TAP immediately on first contact — we wait until
// DOUBLE_TAP is set (within the hardware DUR window of 115 ms) before acting.
// An additional TAP_CONFIRM_MS guard ensures the TAP_SRC register has settled.
//
// IMPORTANT: call mouseButtonPress(bitmask) — single arg only. The two-arg
// overload takes (conn_hdl, buttons) and sends the wrong button value.
static enum { TAP_IDLE, TAP_PENDING, TAP_EXECUTING } tapState = TAP_IDLE;
static unsigned long tapPendingMs = 0;
static uint8_t pendingButton = 0; // 0 = key action pending
// After DOUBLE_TAP fires we add a small settle guard before committing.
static const unsigned long TAP_CONFIRM_MS = 20;
static void fireTapAction(unsigned long now) {
switch (cfg.tapAction) {
case TAP_ACTION_LEFT:
blehid.mouseButtonPress(MOUSE_BUTTON_LEFT);
pendingButton = MOUSE_BUTTON_LEFT;
Serial.println("[TAP] Double → LEFT click");
statLeftClicks++;
break;
case TAP_ACTION_RIGHT:
blehid.mouseButtonPress(MOUSE_BUTTON_RIGHT);
pendingButton = MOUSE_BUTTON_RIGHT;
Serial.println("[TAP] Double → RIGHT click");
statRightClicks++;
break;
case TAP_ACTION_MIDDLE:
blehid.mouseButtonPress(MOUSE_BUTTON_MIDDLE);
pendingButton = MOUSE_BUTTON_MIDDLE;
Serial.println("[TAP] Double → MIDDLE click");
statLeftClicks++;
break;
case TAP_ACTION_KEY: {
uint8_t keys[6] = {cfg.tapKey, 0, 0, 0, 0, 0};
blehid.keyboardReport(cfg.tapMod, keys);
pendingButton = 0;
Serial.print("[TAP] Double → KEY 0x"); Serial.println(cfg.tapKey, HEX);
statLeftClicks++;
break;
}
}
clickButtonDown = true; clickDownMs = now;
tapState = TAP_EXECUTING;
}
void processTaps(unsigned long now) {
// ── Release ───────────────────────────────────────────────────────────────
if (tapState == TAP_EXECUTING) {
if (now - clickDownMs >= CLICK_HOLD_MS) {
if (pendingButton) {
blehid.mouseButtonRelease();
} else {
// Key action: release all keys
uint8_t noKeys[6] = {};
blehid.keyboardReport(0, noKeys);
}
clickButton = 0; clickButtonDown = false;
tapState = TAP_IDLE;
}
return;
}
// ── Poll TAP_SRC ──────────────────────────────────────────────────────────
uint8_t tapSrc = imuReadReg(REG_TAP_SRC);
bool tapIA = !!(tapSrc & 0x40);
bool doubleTap = !!(tapSrc & 0x10);
if (tapState == TAP_IDLE) {
if (tapIA && doubleTap) {
tapPendingMs = now;
tapState = TAP_PENDING;
}
return;
}
if (tapState == TAP_PENDING) {
if (now - tapPendingMs >= TAP_CONFIRM_MS) {
fireTapAction(now);
}
}
}
#endif // FEATURE_TAP_DETECTION