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>
6.3 KiB
6.3 KiB
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
Added
- Composite USB device — mouse + keyboard HID under one VID/PID. A new
WakingHostparent state wraps two substates:WakingWithKeyboard(default) taps Left Shift 4× then idles, and parent'sKBD_WAKE_DURATIONtimeout drives the transition toWakingWithMousewhich 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 secondHidWriteragainst the sameembassy_usb::Builder(no hub simulation; standard USB composite), plusKbdHid/KBD_HID_STATEmachinery and asend_kbdhelper. hsmc0.5.1 statechart drives the entire device lifecycle. The control flow (boot → host wake (kbd → mouse) → settle → spinner → active jiggle/flash → end-of-day spiral → power-down) is expressed declaratively in onestatechart!block; no more atomic flags, no more per-tickPhaseenum, 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 display." - Running (
ShowingRunning): 3 quick clockwise circles (radius 40 px, ~600 ms) — universal "spinner / in progress" cue. - Pre-shutdown (
Ending → ShutdownAnim): eased inward spiral 80 → 2 px over 5 turns (~5 s), starting 30 s before lifetime end. A shared phaseu(t) = t^2.5drives both radius and angle, so the rotation accelerates as the radius collapses — the visual signature of a coin/Euler-disk spinning down. - Warnings (
Active → Warning10 / Warning5): mini versions of the same spiral fire 10 min and 5 min before shutdown — 30 → 2 px over 2 turns (~1.5 s) and 50 → 2 px over 3 turns (~2.5 s) — so the user gets escalating kinetic foreshadowing of the death gesture that's coming.
- Wake-up (
- 2 s
Settlingstate between wake-up and running animations so the display has time to come out of sleep before the spinner draws. - All animations carry sub-pixel residue forward across
i8HID delta reports so the rendered shape matches the intended geometry rather than losing ~half its motion to truncation.
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) →WakingHostparent withWakingWithKeyboardWakingWithMousesubstates;animate_wake→wake_with_mouse, matched by newwake_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 newCtx.kbd: KbdHid);send→send_mouse.
RUN_DURATIONfrom 8 hours to 3 h 50 m. Math-optimal under the user's workday distribution (starttriangular(8.0, mode=8.5, 9.5), lunch 12:00–13:00, endtriangular(16.0, mode=17.5, 19.0)) for "screen goes to sleep during the lunch hour on most days." See/tmp/jiggly_runtime_v3.pyfor the simulation.- HID
poll_ms60 → 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→Bstep 180 ms → 60 ms; 1.5 s pre-animation USB-settle 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.
Removed
JIGGLE_FLAG: AtomicBooland allswap/storecalls.Phaseenum and the per-tickphase_for(remaining)switch — the breathing color is now read directly from elapsed time inside thebreathe_colorduring:.led_taskandsleep_chunkedhelper — both subsumed by the chart.RUN_DURATION = Duration::from_secs(8 * 60 * 60)and similar raw-integer-millisecond constants. All timing is nowDuration-typed with named constructors (from_hours,from_mins,from_millis).
0.1.0 - 2026-04-27
Added
- Initial Embassy-based
no_stdfirmware for the Seeed Studio Xiao RP2040. - USB HID Boot Mouse, masquerading as a Dell MS116 (VID
0x413c, PID0x301a). - Jiggle pattern: pick a random axis from the RP2040 ROSC
RANDOMBIT, nudge +1 px, dwell 25 ms, nudge −1 px, then sleep 4.5 minutes before the next cycle. - 8-hour workday window after boot or
RESET (R)press, then silent idle (watchdog still fed) until the next manual reset. - 8-second hardware watchdog, fed in 5-second chunks during long sleeps.
mise.tomlpinning rust 1.95.0 with explicit components (cargo,clippy,llvm-tools,rust-src,rust-std,rustc,rustfmt) and thethumbv6m-none-eabitarget, plus cargo helpers (cargo-binutils,uf2conv,cargo-watch,cargo-bloat,cargo-expand).justfilerecipes forbuild,release,check,clippy,fmt,lint,ci,bin,uf2,flash,size,bloat,expand, andbootstrap. All recipes execute insidemise exec -- sh -eu -cso the pinned toolchain is used regardless of shell activation state.