Rewrites lingering github.com / *.sr.ht references to this Gitea instance, translating GitHub and Sourcehut URL shapes to Gitea's (/blob/<ref>/ and sr.ht /tree/<ref>/item/ -> /src/branch/<ref>/, /tree/<tag> -> /src/tag/<tag>, /actions/workflows/X -> /actions?workflow=X, /edit/ -> /_edit/). Left pointing at the old hosts on purpose: pull-request links (PRs were not migrated), forks that were never migrated, and GitHub-only surfaces Gitea lacks. OpenPGP identity proofs are untouched by design.
10 KiB
10 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
0.3.0 - 2026-05-07
Added
tuning/crate — a host-side multi-objective NSGA-III tuner for jiggly's four lifecycle constants. Runs against publishedheuropt0.8 (with theparallelrayon feature) and prints the Pareto front, extreme tradeoffs per objective, the firmware's current shipping defaults, and a single weighted-rank recommendation. The crate is its own workspace root with a local.cargo/config.tomloverriding the firmware's inheritedthumbv6m-none-eabibuild target so it can usestd. Invoked viajust tuneorcargo run --releasefrom insidetuning/.
Changed
- Lifecycle timings retuned via the new tuner's 4-objective
(work-time failure, lunch sleep, presses, after-hours waste) Pareto
search, then collapsed by explicit decision weights. New shipping
values:
RUN_DURATION4h00m → 3h51m,YELLOW_AT30 → 22,RED_AT25 → 11,FAST_RED_AT20 → 4 (LED thresholds in minutes-remaining). The 0.2.0 single-composite-score grid had baked the user's weight choices into the search itself; the new approach surfaces the legitimate tradeoffs first and applies preferences afterward. Across 1,000 simulated workdays the new combination averages 26 minutes of lunch sleep, lands in the 12:15–12:45 sweet spot on ~57 % of days, with zero mean work-time failure and ~2 min/day of after-hours waste. The 0.2.0 shipping defaults survive on the new Pareto front but rank well below the new pick under the same weights. config.device_release0x0200 → 0x0300 — matches firmware version 0.3.0.- README section heading "Why four hours…" → "Why these timings…", rewritten to describe the new methodology, the four objectives, the explicit decision weights, and the actual run statistics.
Removed
scripts/tune_runtime.py— the Python single-composite-score grid search is superseded by the in-repotuning/crate's NSGA-III multi-objective search. The new tuner ships with the firmware, builds reproducibly throughcargo/mise, and is just a normal Rust dependency onheuropt(no separateuvinvocation).
0.2.0 - 2026-05-01
Added
- Composite USB device — mouse + keyboard HID under one VID/PID. A new
WakingHostparent state wraps two substates:WakingWithKeyboardtaps F13 4× then idles, and a chart timer transitions toWakingWithMousewhich runs the existing shake animation. Reason: macOS does not reliably wake from raw HID mouse motion alone; a keyboard event does. F13 was picked over the more intuitiveShiftbecause mainstream OSes don't map F13 by default — if a key ever gets stuck on the host (e.g. the wake deadline preempts the loop between a key-down and key-up report), nothing visible happens. Earlier versions used Left Shift and exhibited exactly that nightmare scenario in practice (host typing was capitalised until the device was unplugged). Belt-and-suspenders:keyboard_wakealways sends an all-keys-released report after its work loop, regardless of which side of the deadline won. 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) → pid.codes hobby slot (1209:b0b0). Product string"G502 Mouse"→"jiggly", manufacturer"Logitech"→"swaits.com". Briefly spoofed the Logitech Unifying Receiver (046d:c52b) on the way from G502 → final, but Linux'shid-logitech-djkernel driver matches that PID and runs ~10–20 s of HID++ control-transfer probes the firmware doesn't answer, blocking actual endpoint polling for that long on every plug. macOS doesn't have the driver and was unaffected. Switching to a pid.codes VID letshid-genericbind immediately on Linux. config.device_release = 0x0200(was unset / default0x0010). Matches firmware version0.2.0.config.serial_numbernow derived from the RP2040's 64-bit unique chip ID, rendered as a 16-hex-char&'static strin aStaticCell. Hosts now treat each replug as the same device, and two boards have distinct identities.- 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_DURATION8 h → 4 h 00 m and LED phase boundaries 60 / 30 / 10 → 30 / 25 / 20 (YELLOW_AT/RED_AT/FAST_RED_AT, in minutes-remaining). Joint optimum from a 4-D Monte Carlo over a typical office workday distribution (starttriangular(8.0, mode=8.5, 9.5), lunch 12:00–13:00, endtriangular(16.0, mode=17.5, 19.0)) with a per-minute press-on-warning user model (yellow ~1.5 %/min, red ~4 %/min, fast-red ~6 %/min, plus small bumps at the 10'/5' spiral animations). The hand-picked60/30/10was almost exactly 50ᵗʰ-percentile; pushing the warning thresholds much closer to death liftsP(any lunch sleep)from 53 % → 74 % andP(sweet 12:15–12:45 spot)from 34 % → 52 %. The mechanism is non-obvious — long visible warnings cause more accidental morning RESET-presses that extend the cycle into the afternoon, which is the opposite of what the user wants. Seescripts/tune_runtime.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.