Initial public release

This commit is contained in:
2026-07-17 15:29:53 -04:00
commit 2d71ce77a1
81 changed files with 32056 additions and 0 deletions
+184
View File
@@ -0,0 +1,184 @@
#pragma once
#define FW_VERSION "1.1.0"
#define DEVICE_ID "ATOMS3-MACROPAD"
// Universal binary: the same build runs on the AtomS3 (LCD) and the
// AtomS3 Lite (no LCD, SK6812 RGB LED on GPIO 35 — driven by M5.Led,
// which M5Unified wires up from its board pin table). The board is
// detected at boot via M5GFX panel autodetect + M5.getBoard(); these
// names are what cmdPing reports to the host app.
#define BOARD_NAME_ATOMS3 "atoms3"
#define BOARD_NAME_ATOMS3_LITE "atoms3_lite"
// Display (AtomS3; all rendering is gated off on the Lite)
#define SCREEN_W 128
#define SCREEN_H 128
#define IMG_SIZE (SCREEN_W * SCREEN_H * 2) // RGB565 = 32768 bytes
// Default settings
#define DEFAULT_HOLD_MS 500
#define DEFAULT_TYPE_DELAY 15
#define DEFAULT_ORIENTATION 0
// Serial protocol
#define SERIAL_BAUD 115200
#define CMD_BUF_SIZE 4096
#define JSON_DOC_SIZE 8192
// LittleFS paths
#define CONFIG_PATH "/config.json"
#define MACRO_DIR_PREFIX "/m"
// Timing
#define DEBOUNCE_MS 10
#define COMBO_KEY_PRE_DELAY 10 // ms between individual modifier presses within a combo
#define COMBO_KEY_POST_DELAY 25 // ms hold time after all keys pressed before release
// Default timing settings (user-configurable via GUI)
#define DEFAULT_COMBO_PRE_MS 500 // ms delay before sending a key combo
#define DEFAULT_COMBO_POST_MS 500 // ms delay after sending a key combo
#define DEFAULT_PROBE_TIMEOUT_MS 300 // ms to wait for host LED response
#define DEFAULT_MEDIA_HOLD_MS 100 // ms to hold media key before release
#define DEFAULT_TYPE_SHIFT_EXTRA_MS 25 // extra ms per shifted char (uppercase, !@#$ etc.)
#define DEFAULT_TYPE_SETTLE_MS 150 // ms to wait after the last char to let HID reports drain
#define DEFAULT_TYPE_HOLD_MIN_MS 8 // floor for keydown-to-keyup hold; raised when a host needs longer poll-cycle observation
#define DEFAULT_TYPE_INTER_CHAR_MS 5 // floor for gap between consecutive characters; raised for slow/remote hosts that drop fast input
// Pause-screen text margins (host-configurable via GUI). Padding the text
// box from each screen edge — used by drawWrapped() in display_ui.h.
#define DEFAULT_PAUSE_MARGIN_LEFT 4
#define DEFAULT_PAUSE_MARGIN_RIGHT 4
#define DEFAULT_PAUSE_MARGIN_TOP 16
#define DEFAULT_PAUSE_MARGIN_BOTTOM 12
// Max limits
#define MAX_MACROS 40
#define MAX_NODES_PER_MACRO 200
#define MAX_BRANCH_CHOICES 20
#define MAX_LOOPS 16 // per-macro max number of Loop nodes (for iteration tracking)
// RS232 via Atomic RS232 Base (MAX232)
#define RS232_RX_PIN 5
#define RS232_TX_PIN 6
#define RS232_BUF_SIZE 256
// Resume settings
#define DEFAULT_RESUME_DELAY 0 // seconds, 0 = disabled
// Legacy single-file path — kept ONLY so clearExecutionState() can sweep any
// stale file left over from older firmware. The live save path uses the
// A/B double-buffer + sentinel below.
#define RESUME_STATE_PATH "/resume.json"
// Double-buffer + sentinel scheme: each save writes the inactive slot
// (A or B), then flips the 1-byte sentinel. There is no window where zero
// valid resume files exist on disk. Both files carry a monotonic `seq` and
// a CRC32; reader prefers the sentinel-chosen file but falls back to "any
// file that deserializes AND CRC-matches, highest seq wins" if the
// sentinel is missing/garbage.
#define RESUME_STATE_A_PATH "/resume.a.json"
#define RESUME_STATE_B_PATH "/resume.b.json"
#define RESUME_STATE_IDX_PATH "/resume.idx" // 1 byte: 'A' or 'B'
// Sub-routines
#define MAX_SUBROUTINES 20
#define MAX_SUB_CALL_DEPTH 4
#define SUB_DIR_PREFIX "/sub/s"
// BLE variable sync
#define BLE_SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define BLE_VARS_CHAR_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" // host -> device (write)
#define BLE_VARS_NOTIFY_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a9" // device -> host (notify)
// BLE live-keystroke streaming (separate characteristics on the same
// service so a single advertisement covers both protocols; see
// ble_live.py for the binary frame format)
#define BLE_LIVE_KEYS_WRITE_UUID "4fafc202-1fb5-459e-8fcc-c5c9c331914b" // host -> device (WWR)
#define BLE_LIVE_KEYS_NOTIFY_UUID "4fafc203-1fb5-459e-8fcc-c5c9c331914b" // device -> host (notify)
// Advertised service UUID specifically for live mode. The device advertises
// THIS UUID (instead of BLE_SERVICE_UUID) when it's in EX_LIVE so the host's
// var-sync and live-keystroke scanners filter to disjoint device sets —
// they never race for the same connection. The actual GATT characteristics
// still live in the same internal service (Bleak discovers characteristics
// by UUID regardless of advertised service).
#define BLE_LIVE_SERVICE_UUID "4fafc204-1fb5-459e-8fcc-c5c9c331914b"
#define MAX_BLE_VARS 32
#define BLE_VAR_NAME_LEN 32
#define BLE_VAR_VALUE_LEN 256
#define BLE_VAR_BUF_SIZE 512 // max plaintext+overhead per frame
#define BLE_FRAME_BUF_SIZE 768 // tag(<=64) + nonce(12) + ct+tag(BLE_VAR_BUF_SIZE+16)
#define BLE_DEVICE_TAG_PREFIX "M5Stack|"
#define BLE_DEVICE_TAG_MAX 40 // "M5Stack|AA:BB:CC:DD:EE:FF" + slack
#define BLE_DEV_VARS_PATH "/ble_dev_vars.json"
#define BLE_UNI_VARS_PATH "/ble_uni_vars.json"
#define BLE_REPLAY_STATE_PATH "/ble_replay.json"
// 1-byte flag: '1' while a live keystroke session is active. If power is
// lost mid-session it survives to the next boot, which uses it to skip the
// BLE boot grace and immediately re-advertise for the host to reconnect.
#define BLE_LIVE_RESUME_PATH "/live_resume.flag"
// ---------------------------------------------------------------------------
// ESP-NOW mesh (live keyboard transport)
// ---------------------------------------------------------------------------
// All nodes idle-listen on a fixed WiFi channel (STA mode, no AP
// association anywhere). One USB-attached device is switched into hub
// mode by the host app and bridges USB-CDC <-> ESP-NOW broadcast.
#define DEFAULT_MESH_CHANNEL 1 // 1-13, persisted in NVS ("mesh_ch")
// ---------------------------------------------------------------------------
// Live keyboard transport selection
// ---------------------------------------------------------------------------
// Which radio a device brings up when idle to receive a live-keyboard
// session. Only one is ever up at a time (BLE and WiFi/ESP-NOW contend on
// the ESP32-S3). Persisted in NVS ("live_tx") and toggled on-device by
// holding the screen button for MODE_SWITCH_HOLD_MS while idle.
// LIVE_TX_MESH — idle-listen on the ESP-NOW mesh (the hub broadcasts).
// LIVE_TX_BLE — advertise the BLE live service for direct host links.
#define LIVE_TX_MESH 0
#define LIVE_TX_BLE 1
#define DEFAULT_LIVE_TRANSPORT LIVE_TX_MESH
// Screen-button hold time (ms) that toggles the transport while idle. Well
// above the routine-run hold (settings.holdMs, ~500 ms) so the two gestures
// are unambiguous — the run gesture is classified on release, below 5 s.
#define MODE_SWITCH_HOLD_MS 5000
// Transport header (plaintext, precedes the encrypted ble_frame envelope)
#define MESH_MAGIC 0xE5
#define MESH_HDR_LEN 14
// Hub -> nodes (broadcast)
#define MESH_T_DATA 0x01 // reliable lane (group-key ble_frame)
#define MESH_T_DATA_U 0x02 // unreliable lane: pure mouse moves
#define MESH_T_JOIN 0x03 // per-device-key ble_frame (session invite)
#define MESH_T_POLL 0x04 // discovery poll (plaintext)
// Nodes -> hub (unicast)
#define MESH_T_BEACON 0x81 // discovery reply (plaintext identity)
#define MESH_T_ACK 0x82 // cumulative ack (group key)
#define MESH_T_JOIN_ACK 0x83 // join accepted (per-device key)
#define MESH_T_NACK 0x84 // missing-range report (group key)
#define MESH_T_ERR 0x85 // error report (group key)
// Transport flags
#define MESH_F_RETX 0x01 // retransmission
// Reliability tuning (see espnow_manager.h)
#define MESH_RING_FRAMES 128 // hub retransmit ring (power of two)
#define MESH_RING_SLOT 256 // max cached DATA frame size
#define MESH_REORDER_SLOTS 32 // node-side out-of-order buffer
#define MESH_ACK_EVERY_N 16 // ack at least every N frames...
#define MESH_ACK_MAX_DELAY_MS 50 // ...or this long after first unacked
#define MESH_NACK_AFTER_MS 8 // gap age before first NACK
#define MESH_NACK_REPEAT_MS 30 // re-NACK while gap persists
#define MESH_RETX_MIN_GAP_MS 15 // hub per-seq retransmit rate limit
#define MESH_STALL_REBCAST_MS 60 // hub proactive rebroadcast on stall
#define MESH_NODE_OFFLINE_MS 1500 // hub marks node offline after silence
#define MESH_HUB_HOST_TIMEOUT_MS 5000 // hub reverts to node w/o host traffic
#define MESH_BEACON_LABEL_LEN 24
// CDC binary bridge framing (host <-> hub); JSON lines keep working in
// parallel — the dispatcher peeks at the first byte.
#define HUB_MAGIC0 0xC8
#define HUB_MAGIC1 0x35
#define HUB_H2D_SEND 0x01 // payload = complete mesh frame
#define HUB_D2H_RX 0x81 // src_mac[6] + received node frame
#define HUB_D2H_ACKTAB 0x82 // periodic per-node ack table
#define HUB_ACKTAB_PERIOD_MS 250
#define HUB_MAX_FRAME 1500