feat: composite HID mouse+keyboard with WakingHost statechart
Refactor the boot wake sequence into a WakingHost parent state with
WakingWithKeyboard (default, taps Left Shift 4×) timing out after 500 ms
into WakingWithMouse (existing shake). macOS does not reliably wake from
raw HID mouse motion; a keyboard event does — Shift is a no-op modifier
so it has no character side effect. Parent catches WakeDone (emitted by
the mouse path) and exits to Settling.
USB identity: 046d:c07d "G502 Mouse" → 046d:c52b "USB Receiver"
(Logitech Unifying Receiver — a real composite mouse+keyboard device).
Implementation uses two HidWriters on one embassy_usb::Builder; standard
USB composite, no hub simulation.
Naming cleanup throughout the statechart:
- BootSweep → Booting; SweepDone → BootDone; led_rgb_sweep → boot_sweep
- WakingDisplay → WakingHost{WakingWithKeyboard, WakingWithMouse}
- ShowingRunning → Spinning; RunDone → SpinDone; animate_running → animate_spinner
- Ending::ShutdownAnim → Ending::Spiraling; ShutdownAnimDone → SpiralDone
- animate_shutdown → animate_final_spiral; SHUTDOWN_* → FINAL_SPIRAL_*
- Ctx.writer/Writer → Ctx.mouse/MouseHid (+ new Ctx.kbd/KbdHid)
- send → send_mouse (+ new send_kbd)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+36
-7
@@ -9,11 +9,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Added
|
||||
|
||||
- **Composite USB device — mouse + keyboard HID** under one VID/PID. A new
|
||||
`WakingHost` parent state wraps two substates: `WakingWithKeyboard`
|
||||
(`default`) taps Left Shift 4× then idles, and parent's
|
||||
`KBD_WAKE_DURATION` timeout drives the transition to `WakingWithMouse`
|
||||
which runs the existing shake animation. Reason: macOS does not reliably
|
||||
wake from raw HID mouse motion alone; a keyboard event does. Shift is a
|
||||
modifier with no character side effect, so it's safe even with focus on a
|
||||
text field at the moment of wake. Adds a second `HidWriter` against the
|
||||
same `embassy_usb::Builder` (no hub simulation; standard USB composite),
|
||||
plus `KbdHid`/`KBD_HID_STATE` machinery and a `send_kbd` helper.
|
||||
- `hsmc` 0.5.1 statechart drives the entire device lifecycle. The control
|
||||
flow (boot → wake-up animation → settle → running animation → active
|
||||
jiggle/flash → end-of-day shutdown spiral → power-down) is expressed
|
||||
declaratively in one `statechart!` block; no more atomic flags, no more
|
||||
per-tick `Phase` enum, no hand-rolled main loop.
|
||||
flow (boot → host wake (kbd → mouse) → settle → spinner → active
|
||||
jiggle/flash → end-of-day spiral → power-down) is expressed declaratively
|
||||
in one `statechart!` block; no more atomic flags, no more per-tick
|
||||
`Phase` enum, no hand-rolled main loop.
|
||||
- Mouse animations driven by the chart's `during:` activities:
|
||||
- **Wake-up** (`WakingDisplay`): 10 frantic horizontal sin-shaped sweeps
|
||||
(~12 Hz, ±60 px, ~640 ms) — simulates "shake the mouse to wake the
|
||||
@@ -38,6 +48,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Changed
|
||||
|
||||
- **USB descriptor: Logitech G502 (`046d:c07d`, mouse-only) → Logitech
|
||||
Unifying Receiver (`046d:c52b`, real composite mouse+keyboard).** Product
|
||||
string `"G502 Mouse"` → `"USB Receiver"`. Bumping the PID also invalidates
|
||||
the host's cached HID descriptor on first plug after reflash.
|
||||
- **Statechart names cleaned up for symmetry**:
|
||||
- `BootSweep` → `Booting`; `Ev::SweepDone` → `Ev::BootDone`;
|
||||
`led_rgb_sweep` → `boot_sweep`.
|
||||
- `WakingDisplay` (flat) → `WakingHost` parent with `WakingWithKeyboard`
|
||||
+ `WakingWithMouse` substates; `animate_wake` → `wake_with_mouse`,
|
||||
matched by new `wake_with_keyboard`.
|
||||
- `ShowingRunning` → `Spinning`; `Ev::RunDone` → `Ev::SpinDone`;
|
||||
`animate_running` → `animate_spinner`.
|
||||
- `Ending::ShutdownAnim` → `Ending::Spiraling`;
|
||||
`Ev::ShutdownAnimDone` → `Ev::SpiralDone`;
|
||||
`animate_shutdown` → `animate_final_spiral`;
|
||||
`SHUTDOWN_RADIUS_START`/`SHUTDOWN_TURNS`/`SHUTDOWN_FRAMES` →
|
||||
`FINAL_SPIRAL_*`.
|
||||
- `Ctx.writer: Writer` → `Ctx.mouse: MouseHid` (parallel to new
|
||||
`Ctx.kbd: KbdHid`); `send` → `send_mouse`.
|
||||
- **`RUN_DURATION` from 8 hours to 3 h 50 m**. Math-optimal under the
|
||||
user's workday distribution (start `triangular(8.0, mode=8.5, 9.5)`,
|
||||
lunch 12:00–13:00, end `triangular(16.0, mode=17.5, 19.0)`) for "screen
|
||||
@@ -46,12 +75,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- HID `poll_ms` 60 → 8 (125 Hz). At the previous 60 ms poll the host
|
||||
was discarding ~7 of every 8 animation frames the firmware emitted.
|
||||
- Boot LED `R→G→B` step 180 ms → 60 ms; 1.5 s pre-animation USB-settle
|
||||
delay removed. Reset → wake-shake latency dropped from ~2 s to ~240 ms.
|
||||
delay removed. Reset → wake-shake latency dropped from ~2 s to ~240 ms
|
||||
(mouse-shake now ~740 ms after reset since 500 ms of keyboard-wake taps
|
||||
precede it).
|
||||
- Watchdog feeding moved out of the imperative main loop into its own
|
||||
`#[embassy_executor::task]`; the chart owns user-visible state, the
|
||||
watchdog task owns the periodic kick.
|
||||
- USB descriptor unchanged (still spoofs Logitech G502 VID `0x046d` /
|
||||
PID `0xc07d`).
|
||||
|
||||
### Removed
|
||||
|
||||
|
||||
Reference in New Issue
Block a user