Release-prep work for a publish-worthy 0.2.0: - LICENSE: MIT, © 2026 Stephen Waits. - README.md: tagline, what-it-does, hardware, build/flash recipes, ASCII statechart overview, "Why four hours…" runtime-tuning rationale, USB identity section. - Cargo.toml: 0.1.0 → 0.2.0; description, license, repository, readme, keywords, categories; publish = false (firmware, not a library); release profile tightened (lto = "fat", opt-level = "z", panic = "abort"). Flashed binary stays 47 KB; the size knobs are explicit rather than relying on defaults. - CHANGELOG.md: collapse Unreleased → [0.2.0] - 2026-05-01. - scripts/tune_runtime.py: 4-D Monte Carlo over (RUN_DURATION, YELLOW_AT, RED_AT, FAST_RED_AT). PEP 723 inline deps so `uv run` just works. Runtime + LED thresholds re-derived from a typical office workday distribution with a per-minute press-on-warning user model. Joint optimum: RUN_DURATION: 4h00m YELLOW_AT / RED_AT / FAST_RED_AT (min remaining): 30 / 25 / 20 USB identity: VID/PID: 046d:c07d (G502) → 1209:b0b0 (pid.codes) manufacturer: "Logitech" → "swaits.com" product: "G502 Mouse" → "jiggly" bcdDevice: default 0x0010 → 0x0200 (matches firmware version) serial: (none) → RP2040 chip ID as 16 hex chars Bug fix in the descriptor change: an interim version spoofed the Logitech Unifying Receiver (046d:c52b). On Linux, `hid-logitech-dj` matches that exact PID and tries to talk Logitech's HID++ protocol to enumerate paired wireless devices. The firmware doesn't speak HID++, so the driver waits through ~10–20 s of control-transfer timeouts on every plug before unbinding and letting `hid-generic` actually start polling. macOS has no such driver and was always fast. Moving to a pid.codes VID routes the device straight to `hid-generic`. Wake key: tapped Left Shift in early versions to wake the host. In practice that turned out to be exactly the nightmare scenario it sounds like — if the deadline preempted the loop between a Shift-down report and its Shift-up, the host would silently capitalise every keystroke from the user's real keyboard until the device was unplugged. Switched to F13: still wakes any modern OS, but no mainstream OS maps F13 by default, so a stuck F13 has zero visible effect. Also added an unconditional all-keys-released cleanup report at the end of the wake action, bounded by KBD_RELEASE_DEADLINE = 100 ms, so even a deadline that fires mid-press can't leave anything held. Wake refactor: WakingWithKeyboard and WakingWithMouse use oneshot `entry:` actions that race their work against an internal deadline via `embassy_futures::select`; the chart timer (`on(after KBD_PHASE_DURATION)` / `on(after MOUSE_PHASE_DURATION)`) advances. Removes a class of "slow USB ⇒ chart stalls" failure modes from the wake path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
52 lines
2.2 KiB
TOML
52 lines
2.2 KiB
TOML
[package]
|
|
name = "jiggly"
|
|
version = "0.2.0"
|
|
edition = "2024"
|
|
authors = ["Stephen Waits <steve@waits.net>"]
|
|
description = "USB mouse jiggler firmware for the Seeed Studio Xiao RP2040 — keeps your screen awake during the workday, then politely shuts up so you can go home."
|
|
license = "MIT"
|
|
repository = "https://github.com/swaits/jiggly"
|
|
readme = "README.md"
|
|
keywords = ["embedded", "rp2040", "usb-hid", "embassy", "no-std"]
|
|
categories = ["embedded", "no-std"]
|
|
publish = false # firmware binary; not a crates.io library
|
|
|
|
[features]
|
|
default = ["embassy"]
|
|
# `hsmc` emits `#[cfg(feature = "embassy")]` from inside `statechart!` and
|
|
# the cfg is evaluated in this crate's context, so we mirror the flag here
|
|
# and forward it to `hsmc/embassy`.
|
|
embassy = ["hsmc/embassy"]
|
|
|
|
[dependencies]
|
|
embassy-executor = { version = "0.10.0", features = ["platform-cortex-m", "executor-thread"] }
|
|
embassy-time = { version = "0.5.1" }
|
|
embassy-rp = { version = "0.10.0", features = ["rp2040", "time-driver", "critical-section-impl", "unstable-pac"] }
|
|
embassy-sync = "0.8"
|
|
embassy-futures = "0.1"
|
|
embassy-usb = "0.6.0"
|
|
hsmc = { version = "0.5.1", default-features = false }
|
|
usbd-hid = "0.10.0"
|
|
smart-leds = "0.4"
|
|
libm = "0.2"
|
|
|
|
cortex-m = { version = "0.7.6", features = ["inline-asm"] }
|
|
cortex-m-rt = "0.7.5"
|
|
panic-reset = "0.1"
|
|
static_cell = "2.1"
|
|
# Cortex-M0+ has no hardware CAS; static_cell + heapless need this shim.
|
|
portable-atomic = { version = "1.13", features = ["critical-section"] }
|
|
|
|
[lints.rust]
|
|
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(feature, values("tokio", "embassy"))'] }
|
|
|
|
[profile.release]
|
|
lto = "fat" # cross-crate inlining and dead-code elimination
|
|
opt-level = "z" # optimise for size over speed (vs "s")
|
|
codegen-units = 1 # single unit so LTO sees everything
|
|
debug = 2 # symbols stay in the ELF for `just bloat` /
|
|
# `just size`; `cargo objcopy -O binary` strips
|
|
# them when producing the flashable .bin / .uf2
|
|
overflow-checks = false
|
|
panic = "abort" # no unwinding tables; panic-reset just resets
|