- TypeScript 77.2%
- JavaScript 13.3%
- CSS 5.8%
- HTML 2%
- Python 1.4%
- Other 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
scripts/install-shortcut.mjs writes a BikeBeater Start-menu entry (WSL / Windows; a .desktop entry on Linux) whose one click runs `npm start` inside the repo — the repo stays the app, nothing to rebuild after a change. The shortcut's icon and working directory are Windows-local (Explorer won't draw an icon from \\wsl.localhost). Icon: crank on blue, chosen from three (assets/icon.svg → png + ico via scripts/make-icon.mjs, outputs committed). npm start now launches Electron from node_modules on macOS, Linux and native Windows, and uses scripts/app.sh (the Windows Electron) only under WSL. Under WSL it also: adds the Windows system dirs to PATH when a login shell dropped them (the shortcut's console had no powershell.exe — app.sh died silently), tracks the overlay's Windows PID after launch, and closes it with taskkill on Ctrl+C — killing the interop stub alone left the window open. Verified through the real shortcut: launch, Ctrl+C closes both halves, closing the overlay ends npm start. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01CHfdg8t6xCeWiHhAqb9iXX |
||
| assets | ||
| docs | ||
| electron | ||
| relay | ||
| scripts | ||
| server | ||
| src | ||
| .gitignore | ||
| index.html | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| spike.html | ||
| tsconfig.json | ||
| vite.config.ts | ||
BikeBeater
Ride your Rogue Echo Bike (V3 connected console) to a program by ear: the app plays a metronome — one beep per pedal revolution — at the tempo that produces the target console RPM, and distinct audio cues when your actual cadence drifts outside tolerance. The console broadcasts live cadence/power over Bluetooth FTMS; the app compares what you're doing to what the program asks and nudges you back.
A one-time rhythm calibration (Sound card → Calibrate rhythm) maps beep
tempo to console RPM: the console reports flywheel RPM, which is belt-geared
faster than the cranks by an unpublished ratio, so the mapping must be
measured, not assumed. You pedal one revolution per beep through three tempos
(~90 s); the median transmitted cadence per tempo over the beat rate gives the
gear ratio, cross-checked for consistency and snapped to a simple mapping when
it is one (src/engine/calibrate.ts). Until calibrated, the app assumes 1:1
and the metronome will feel wrong — calibrate first.
Run
npm install
npm start # server + desktop overlay, one terminal; close the overlay to stop both
npm start starts the Vite dev server (with the SQLite API) if nothing is
on :5173, waits for it, then launches the overlay; a server already running
is reused. For the pieces separately: npm run dev (server) and
npm run app (overlay).
npm run install-shortcut puts BikeBeater in the Start menu (Windows /
WSL — pin it to the taskbar from there) or in the app launcher (Linux); one
click runs npm start. The repo is the app: a code change is live on the
next launch, nothing to rebuild or reinstall.
Platforms: on macOS, Linux and native Windows the overlay is Electron from
node_modules. Under WSL the overlay must run on the Windows side (a
transparent window doesn't survive WSLg), so install the Windows Electron
build into WSL — npm_config_platform=win32 npm install — and npm start
launches it through scripts/app.sh while the server stays in WSL.
Or open http://localhost:5173 in Chrome or Edge on the Windows side (Web Bluetooth doesn't exist in WSL, and localhost forwarding makes the WSL dev server reachable from Windows). Press the console's Bluetooth/connect state if it needs one, click Connect Echo bike, pick a program, Start ride.
?demo=1— desk demo: fake bike + short program, straight to the ride screen (click the sound chip once; browsers block audio until a gesture).
Hardware spike (do this first on the real bike)
http://localhost:5173/spike.html — connect, pedal a few minutes including
sprints and a full stop, Export JSON. The export answers the open
hardware questions (real notify rate/jitter, flag layout, 0-RPM behavior,
reconnect quirks) and becomes test fixtures in src/fixtures/.
Facts the design leans on
- The Echo's cadence is genuinely measured (reed switch on the flywheel) but the number is flywheel RPM, not crank RPM — fixed belt gearing links them by a constant, unpublished ratio (confirmed on-bike: a 1-beep-per- transmitted-RPM metronome is audibly out of sync with the pedals). Watts are derived from RPM via a fan curve — console-relative, not calibrated power. Program targets and drift detection stay in console units; only the metronome tempo is divided by the calibrated ratio.
- Because watts are a function of RPM, programs target cadence; expected
watts are shown as an estimate once the app has passively learned enough
(rpm, watts) pairs (
src/engine/powermodel.ts, localStorage). - FTMS Indoor Bike Data arrives ~1 Hz as a scalar — beeps set the tempo but cannot phase-lock to crank position.
- Drift alerting is a state machine (
src/engine/drift.ts): EMA smoothing, outer/inner hysteresis bands, dwell times, re-alert cooldown, grace period after interval changes, and separate "stopped" and "stale" states.
Tests
npm test
Covers FTMS frame parsing, the drift state machine (alert/clear/grace/ stopped/stale/overshoot timing), and program stepping. Recorded rides (Download recording / spike export) can be replayed through the detector to tune thresholds without re-riding.