feat: add Wahoo KickRun treadmill driver - #4834
Open
cagnulein wants to merge 9 commits into
Open
Conversation
Implements the proprietary Wahoo KickRun BLE protocol reverse-engineered from HCI snoop traces: - Speed command: 02 [uint24-LE μm/s] 00 [flag] on the write+notify char - Challenge-response: device sends FD E0 01 [code] after paddle push, driver echoes E0 [code] immediately without waiting - Init sequence: capability queries 01 10 12 18 80 86 f0; 86 response carries a 4-byte device ID that is echoed back via cmd 87 - Workout unlock: 19 00 sent once before the first speed command - Telemetry: FF 01 [tgt μm/s LE24] 00 [cur μm/s LE24] ... parsed for speed/distance/kcal Removes KICKR RUN from the generic horizonTreadmill branch so the device gets its own driver instead of falling back to FTMS-only control. NOTE: the physical paddle push on the treadmill is still required for each speed change - it is a hardware safety mechanism that generates the BLE challenge code. QZ responds to the challenge automatically, eliminating the ~6 s delay introduced by the Wahoo app. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Owner
Author
|
Stephan Ben. by email |
…etter Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…untreadmill Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The driver picked the first WriteNoResponse+Notify characteristic found during service discovery, which is a026e002 (handle 0x0019) — the Wahoo debug/console stream. The actual Wahoo command channel is a026e03e at handle 0x0023, confirmed from both the QZ char-discovery log and the HCI btsnoop trace of the official Wahoo app. With the wrong char selected, all init/speed/workout-unlock writes were silently accepted by the debug stream but never reached the treadmill controller, so speed stayed zero and QZ entered the auto-pause loop. Fix: match the command characteristic by UUID instead of by property heuristic. The debug-stream UUID (a026e002) also has WriteNoResponse+ Notify, so a property-only search always returns the wrong one first. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Override changeSpeed() to base subsequent rapid presses on the already-pending requestSpeed rather than the stale telemetry speed. This prevents multiple quick taps all sending the same speed value. - After sending a BLE speed command, set Speed = requestSpeed optimistically and set paddlePending = true. This makes the speed tile update immediately in the UI instead of waiting for the belt to physically move. - While paddlePending is true, telemetry speed notifications are ignored so they don't overwrite the displayed commanded speed. paddlePending is cleared when the KICKR RUN issues its paddle-push challenge (FD E0 01). - Changed the speed-equality check to use abs() > 0.01 (km/h) to avoid repeated BLE writes caused by floating-point rounding in the telemetry µm/s conversion. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
wahookickruntreadmilldriver for the Wahoo KickRun treadmill (previously routed throughhorizonTreadmillwhich only supports inclination)01 10 12 18 80 86 f0), device-ID handshake (87 + [4-byte ID]), and workout-mode unlock (19 00)02 [μm/s LE24] 00 [flag]where flag =0xFFfor first command of session,0x0Afor subsequent; confirmed against captured traces at 2.4, 4.8 and 5.0 km/hFD E0 01 [XX YY]after user presses the physical paddle, driver immediately echoesE0 [XX YY]to confirmFF 01 [tgt_b0 b1 b2] 00 …— bytes 2–4 are the commanded speed in μm/s LE24 (confirmed from trace)Test plan
KICKR RUN *is picked up by the new driver (not routed tohorizonTreadmill)connectedAndDiscoveredfiresSpeedmetric correctly🤖 Generated with Claude Code