Files
jiggly/CHANGELOG.md
swaits 359e693646 chore: repoint remaining links at git.swaits.com
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.
2026-08-31 18:32:19 -06:00

197 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [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 published [`heuropt`][heuropt]
0.8 (with the `parallel` rayon 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.toml` overriding the
firmware's inherited `thumbv6m-none-eabi` build target so it can use
`std`. Invoked via `just tune` or `cargo run --release` from inside
`tuning/`.
### 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_DURATION` 4h00m → 3h51m**, **`YELLOW_AT` 30 → 22**,
**`RED_AT` 25 → 11**, **`FAST_RED_AT` 20 → 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:1512: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_release` 0x0200 → 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-repo `tuning/` crate's NSGA-III
multi-objective search. The new tuner ships with the firmware, builds
reproducibly through `cargo`/`mise`, and is just a normal Rust
dependency on `heuropt` (no separate `uv` invocation).
[heuropt]: https://crates.io/crates/heuropt
## [0.2.0] - 2026-05-01
### Added
- **Composite USB device — mouse + keyboard HID** under one VID/PID. A new
`WakingHost` parent state wraps two substates: `WakingWithKeyboard`
taps **F13** 4× then idles, and a chart timer transitions to
`WakingWithMouse` which 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 intuitive `Shift`
because 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_wake` always sends an all-keys-released
report after its work loop, regardless of which side of the deadline
won. 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 → 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
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 phase `u(t) = t^2.5` drives 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.
- 2 s `Settling` state 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 `i8` HID 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's `hid-logitech-dj` kernel driver matches that
PID and runs ~1020 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 lets `hid-generic` bind immediately on
Linux.
- **`config.device_release = 0x0200`** (was unset / default `0x0010`).
Matches firmware version `0.2.0`.
- **`config.serial_number`** now derived from the RP2040's 64-bit unique
chip ID, rendered as a 16-hex-char `&'static str` in a `StaticCell`.
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) → `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` 8 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 (start `triangular(8.0,
mode=8.5, 9.5)`, lunch 12:0013:00, end `triangular(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-picked `60/30/10` was almost
exactly 50ᵗʰ-percentile; pushing the warning thresholds much closer
to death lifts `P(any lunch sleep)` from 53 % → 74 % and `P(sweet
12:1512: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. See `scripts/tune_runtime.py` for the
simulation.
- 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
(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: AtomicBool` and all `swap`/`store` calls.
- `Phase` enum and the per-tick `phase_for(remaining)` switch — the
breathing color is now read directly from elapsed time inside the
`breathe_color` `during:`.
- `led_task` and `sleep_chunked` helper — both subsumed by the chart.
- `RUN_DURATION = Duration::from_secs(8 * 60 * 60)` and similar
raw-integer-millisecond constants. All timing is now `Duration`-typed
with named constructors (`from_hours`, `from_mins`, `from_millis`).
## [0.1.0] - 2026-04-27
### Added
- Initial Embassy-based `no_std` firmware for the Seeed Studio Xiao RP2040.
- USB HID Boot Mouse, masquerading as a Dell MS116 (VID `0x413c`, PID `0x301a`).
- 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.toml` pinning rust 1.95.0 with explicit components
(`cargo`, `clippy`, `llvm-tools`, `rust-src`, `rust-std`, `rustc`,
`rustfmt`) and the `thumbv6m-none-eabi` target, plus cargo helpers
(`cargo-binutils`, `uf2conv`, `cargo-watch`, `cargo-bloat`,
`cargo-expand`).
- `justfile` recipes for `build`, `release`, `check`, `clippy`, `fmt`,
`lint`, `ci`, `bin`, `uf2`, `flash`, `size`, `bloat`, `expand`, and
`bootstrap`. All recipes execute inside `mise exec -- sh -eu -c` so the
pinned toolchain is used regardless of shell activation state.
[Unreleased]: https://git.swaits.com/swaits/jiggly/compare/v0.3.0...main
[0.3.0]: https://git.swaits.com/swaits/jiggly/compare/v0.2.0...v0.3.0
[0.2.0]: https://git.swaits.com/swaits/jiggly/compare/v0.1.0...v0.2.0
[0.1.0]: https://git.swaits.com/swaits/jiggly/releases/tag/v0.1.0