Claude-skill-registry bluetooth-trainer-expert
Expert guidance on reverse-engineering, testing, and integrating Bluetooth Smart Trainers (Wahoo/FTMS) for fitness applications. Use when building or debugging cycling apps.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/bluetooth-trainer-expert" ~/.claude/skills/majiayu000-claude-skill-registry-bluetooth-trainer-expert && rm -rf "$T"
skills/data/bluetooth-trainer-expert/SKILL.mdBluetooth Trainer Expert
This skill provides a proven workflow for developing biking fitness applications that interact with Smart Trainers (e.g., Wahoo KICKR, FTMS devices). It emphasizes a "Console First" approach to isolate connection and protocol logic before attempting UI integration.
Core Philosophy
- Console First: Always prove the protocol logic in a simple C# Console Application (
) before touching the main UI (WPF/MAUI).BikeFitnessConsole - Physical Verification: Telemetry (Success/Fail responses) is insufficient for brake logic. You MUST ask the user to "Pedal and verify resistance change" to confirm the device actually engaged the magnet/motor.
- Protocol Agnosticism: Support both standard FTMS (
) and legacy Wahoo (0x1826
) protocols.0xA026...
Phase 1: Diagnostic Console App
Create a lightweight console app to handle scanning, connection, and raw command testing.
Key Features to Implement:
- Scanner: Filter for
(FTMS),0x1826
(Wahoo), and0xA026
(Power).0x1818 - Service Explorer: List all available Services and Characteristics to confirm UUIDs.
- Indication Handler: CRITICAL. You must subscribe to
on the Control Point characteristic before sending any write commands. Failure to do so often results in silent failures or "Attribute Not Found" errors on writes.Indicate
Data Format (Endianness)
Bluetooth LE GATT characteristics standardly use Little Endian byte order.
- Reading: When parsing multi-byte values (e.g., Speed, Power, Time), interpret the least significant byte first.
- Example:
->0xE8 0x03
->0x03E8
.1000
- Example:
- Writing: When constructing commands (e.g., Target Watts, Grade), ensure you serialize
/UInt16
values to Little Endian.Int16 - Note: While standard FTMS and common Wahoo OpCodes (
) follow Little Endian, always verify proprietary manufacturer-specific characteristics if behavior is unexpected.0x40-0x45
Phase 2: Protocol Identification & Control Points
Identify the correct Control Point (CP) for the device.
1. FTMS (Fitness Machine Service) - Preferred
- Service UUID:
00001826-0000-1000-8000-00805f9b34fb - Control Point:
(Fitness Machine Control Point)00002ad9-0000-1000-8000-00805f9b34fb - Init Sequence:
- Subscribe to CP Indications.
- Send
(Request Control).0x00 - Send
(Start/Resume).0x07
2. Wahoo Legacy (KICKR / SNAP)
- Service UUID:
a026ee01-0a7d-4ab3-97fa-f1500f9feb8b - Control Point:
(Often hidden inside the Power Servicea026e005-0a7d-4ab3-97fa-f1500f9feb8b
on some firmware).0x1818 - Init Sequence:
- Subscribe to CP Indications.
- Send
(Init/Unlock).0x00
Phase 3: OpCode Discovery Strategy
Different devices support different modes. Use the Console App to "Scan" OpCodes
0x40 through 0x45 by sending [OpCode, 0x00] and observing the Indication response.
- Response Format:
(Where Result01-OpCode-Result
= Success,01
= Not Supported,04
= Fail).40 - Common OpCodes:
Level Mode: 0-9 discrete levels. Basic.0x40
Resistance Mode: 0-100% brake force. Most reliable.0x41
Sim Mode Enable: Activates physics engine (Grade/Wind/Weight). Warning: Many older or lower-end devices (e.g., KICKR SNAP) return "Success" for params but FAIL (0x42
) when trying to Enable this mode.0x40
Sim Parameters: Sets Grade, Weight, Crr, Cw.0x43
Phase 4: "Fake" Simulation Mode (Grade Mapping)
If the device fails to support Native Sim Mode (
0x42), you must implement Grade-to-Resistance Mapping in software to support "Hilly" or "Mountain" workouts.
Recommended Mapping (User Calibrated): Map the visual "Grade %" to the device's "Resistance %" (OpCode
0x41).
| Grade Input | Resistance Output | Feel |
|---|---|---|
| -10% | 0.0% | Coasting / Free spin. |
| 0% | 1.0% | Flat road friction (light). |
| 20% | 40.0% | Steep climb. (Cap at 40% to avoid "brick wall"). |
Logic: Use a piecewise linear function:
- Downhill (-10 to 0): Interpolate 0.0 -> 0.01.
- Uphill (0 to 20): Interpolate 0.01 -> 0.40.
Phase 5: Telemetry
- Power: Standard
(Cycling Power Measurement).0x2A63 - Speed/Distance: Often missing from FTMS on older devices. Calculate locally using Wheel Revolutions from the Power Measurement characteristic (Data bytes
).flags & 0x10Distance = WheelRevs * TireCircumferenceSpeed = DeltaDistance / DeltaTime
- Cadence: Check
(Speed & Cadence) or Crank Data in Power (0x1816
).0x1818
Checklist for Implementation
- Console Test: Can you connect and receive Indications?
- OpCode Scan: Which modes return
(Success)?01 - Physical Test: Does sending
(50% res) make it hard to pedal?0x41 0x32 - Mapping: Does -10% Grade feel like zero resistance?
- UI: Does the UI display Grade (not raw resistance) to the user?