#pragma once #include // Feature Flags #define FEATURE_CONFIG_SERVICE #define FEATURE_TELEMETRY #define FEATURE_IMU_STREAM #define FEATURE_TAP_DETECTION #define FEATURE_TEMP_COMPENSATION #define FEATURE_AUTO_RECAL #define FEATURE_BATTERY_MONITOR #define FEATURE_BOOT_LOOP_DETECT #define FEATURE_PHYSICAL_BUTTONS // Debug // #define DEBUG // ATT table size #define _ATT_BASE 900 #ifdef FEATURE_CONFIG_SERVICE #define _ATT_CFG 100 // +20 for cfgGitHash characteristic #else #define _ATT_CFG 0 #endif #ifdef FEATURE_TELEMETRY #define _ATT_TELEM 40 #else #define _ATT_TELEM 0 #endif #ifdef FEATURE_IMU_STREAM #define _ATT_STREAM 30 #else #define _ATT_STREAM 0 #endif #define ATT_TABLE_SIZE_CALC (_ATT_BASE + _ATT_CFG + _ATT_TELEM + _ATT_STREAM) #define ATT_TABLE_SIZE (ATT_TABLE_SIZE_CALC < 1536 ? 1536 : ATT_TABLE_SIZE_CALC) // IMU register addresses #define REG_CTRL1_XL 0x10 #define REG_TAP_CFG 0x58 #define REG_TAP_THS_6D 0x59 #define REG_INT_DUR2 0x5A #define REG_WAKE_UP_THS 0x5B #define REG_MD1_CFG 0x5E #define REG_TAP_SRC 0x1C #define REG_OUT_TEMP_L 0x20 #define REG_OUT_TEMP_H 0x21 // Pins #define PIN_VBAT_ENABLE (14) #define PIN_VBAT_READ (32) #define PIN_CHG (23) #define PIN_HICHG (22) // Persistence #define CONFIG_FILENAME "/imu_mouse_cfg.bin" #define CONFIG_MAGIC 0xDEAD123EUL // Physical button pin assignments (hardcoded - set to 0xFF to disable a button) // Valid pin numbers: 0-10 (Arduino D0-D10 on XIAO nRF52840 Sense) #define BTN_PIN_NONE 0xFF #define BTN_LEFT_PIN BTN_PIN_NONE // e.g. 0 for D0 #define BTN_RIGHT_PIN BTN_PIN_NONE // e.g. 1 for D1 #define BTN_MIDDLE_PIN BTN_PIN_NONE // e.g. 2 for D2 // Runtime feature-override flags (cfg.featureFlags bitmask) // These mirror the compile-time FEATURE_* defines but can be toggled at runtime // via the web UI and persisted in flash. Bits not listed here are reserved = 0. #define FLAG_TAP_ENABLED 0x01 // Tap detection active (requires restart) #define FLAG_TEMP_COMP_ENABLED 0x02 // Temperature gyro-drift compensation #define FLAG_AUTO_RECAL_ENABLED 0x04 // Auto-recalibrate after long idle #define FLAG_ALL_DEFAULT (FLAG_TAP_ENABLED | FLAG_TEMP_COMP_ENABLED | FLAG_AUTO_RECAL_ENABLED) // Enums enum CurveType : uint8_t { CURVE_LINEAR=0, CURVE_SQUARE=1, CURVE_SQRT=2 }; enum ChargeMode : uint8_t { CHARGE_SLOW=0, CHARGE_FAST=1 }; enum ChargeStatus: uint8_t { CHGSTAT_DISCHARGING=0, CHGSTAT_CHARGING=1, CHGSTAT_FULL=2 }; // Tap action types // TAP_ACTION_KEY: fires a raw HID keycode (tapKey) with optional modifier (tapMod). // Modifier byte: bit0=Ctrl, bit1=Shift, bit2=Alt, bit3=GUI (same as HID modifier byte). enum TapAction : uint8_t { TAP_ACTION_LEFT = 0, TAP_ACTION_RIGHT = 1, TAP_ACTION_MIDDLE = 2, TAP_ACTION_KEY = 3, }; // Config (stored in flash) struct Config { uint32_t magic; float sensitivity; float deadZone; float accelStrength; CurveType curve; uint8_t axisFlip; ChargeMode chargeMode; uint8_t tapThreshold; // 1–31 → REG_TAP_THS_6D bits[4:0]; 1 LSB = 62.5 mg at ±2g TapAction tapAction; // what a double-tap does uint8_t tapKey; // HID keycode (used when tapAction == TAP_ACTION_KEY) uint8_t tapMod; // HID modifier byte (used when tapAction == TAP_ACTION_KEY) float jerkThreshold; // jerk² threshold for tap-freeze detection uint8_t tapFreezeEnabled; // 1 = enable jerk-based cursor freeze during taps uint8_t featureFlags; // bitmask of FLAG_* - runtime feature overrides }; extern Config cfg; extern const Config CFG_DEFAULTS; // ConfigBlob (over BLE, 25 bytes) struct __attribute__((packed)) ConfigBlob { float sensitivity; // [0] float deadZone; // [4] float accelStrength; // [8] uint8_t curve; // [12] uint8_t axisFlip; // [13] uint8_t chargeMode; // [14] uint8_t tapThreshold; // [15] 1–31 uint8_t tapAction; // [16] TapAction enum uint8_t tapKey; // [17] HID keycode uint8_t tapMod; // [18] HID modifier uint8_t tapFreezeEnabled; // [19] 1 = enable jerk-based cursor freeze during taps float jerkThreshold; // [20] jerk² tap-freeze threshold uint8_t featureFlags; // [24] FLAG_* bitmask - runtime feature overrides }; static_assert(sizeof(ConfigBlob) == 25, "ConfigBlob must be 25 bytes"); // TelemetryPacket (24 bytes) #ifdef FEATURE_TELEMETRY struct __attribute__((packed)) TelemetryPacket { uint32_t uptimeSeconds; // [0] uint32_t leftClicks; // [4] uint32_t rightClicks; // [8] float tempCelsius; // [12] float biasRmsRadS; // [16] uint16_t recalCount; // [20] uint8_t chargeStatus; // [22] uint8_t _pad; // [23] float battVoltage; // [24] raw battery voltage (V) }; static_assert(sizeof(TelemetryPacket) == 28, "TelemetryPacket must be 28 bytes"); extern TelemetryPacket telem; #endif // ImuPacket (14 bytes) #ifdef FEATURE_IMU_STREAM struct __attribute__((packed)) ImuPacket { int16_t gyroX_mDPS; // [0] pitch axis (nod up/down → cursor Y) int16_t gyroZ_mDPS; // [2] yaw axis (pan left/right → cursor X) int16_t accelX_mg; // [4] int16_t accelY_mg; // [6] int16_t accelZ_mg; // [8] int8_t moveX; // [10] int8_t moveY; // [11] uint8_t flags; // [12] bit0=idle bit1=singleTap bit2=doubleTap uint8_t _pad; // [13] }; static_assert(sizeof(ImuPacket) == 14, "ImuPacket must be 14 bytes"); #endif // Tuning constants extern const float ALPHA; extern const int LOOP_RATE_MS; extern const int BIAS_SAMPLES; extern const int IDLE_FRAMES; extern const unsigned long BATT_REPORT_MS; extern const unsigned long TELEMETRY_MS; extern const unsigned long HEARTBEAT_MS; extern const int HEARTBEAT_DUR; extern const unsigned long BOOT_SAFE_MS; #ifdef FEATURE_IMU_STREAM extern const unsigned long IMU_STREAM_RATE_MS; #endif extern const float BATT_FULL; extern const float BATT_EMPTY; extern const float BATT_CRITICAL; #ifdef FEATURE_TAP_DETECTION extern const unsigned long CLICK_HOLD_MS; #endif #ifdef FEATURE_TEMP_COMPENSATION extern const float TEMP_COMP_COEFF_DPS_C; #endif #ifdef FEATURE_AUTO_RECAL extern const unsigned long AUTO_RECAL_MS; #endif // Global state extern float angleX, angleY; extern float accumX, accumY; extern float gravX, gravY, gravZ; extern float biasGX, biasGY, biasGZ; extern float calTempC; extern float cachedTempC; #ifdef FEATURE_TAP_DETECTION extern bool clickButtonDown; extern uint8_t clickButton; extern unsigned long clickDownMs; extern uint32_t statLeftClicks; extern uint32_t statRightClicks; #endif #ifdef FEATURE_IMU_STREAM extern bool imuStreamEnabled; #endif extern bool pendingCal; extern bool pendingReset; extern ChargeStatus lastChargeStatus; extern int idleFrames; extern unsigned long idleStartMs; extern unsigned long lastTime; extern unsigned long lastBattTime; extern unsigned long lastHeartbeat; extern unsigned long lastTelemetry; extern unsigned long bootStartMs; #ifdef FEATURE_IMU_STREAM extern unsigned long lastImuStream; #endif #ifdef FEATURE_TELEMETRY extern uint16_t statRecalCount; extern float statBiasRms; #endif extern bool safeMode; extern bool bootCountCleared;