Most treadmills have a Bluetooth chip and a companion app nobody enjoys using. I wanted to drive the treadmill - speed, incline, live stats - from a clean SwiftUI app instead. That became Treadmill Pro, an iOS app that talks to the treadmill directly over Bluetooth Low Energy with CoreBluetooth. Here's what building a BLE hardware controller actually involves.
BLE in one paragraph
A BLE device exposes services, each containing characteristics you can read, write, or subscribe to. Fitness equipment often speaks FTMS (the Fitness Machine Service), a standard set of characteristics for treadmills, bikes and rowers. In practice, support is uneven - some machines follow the spec, some ship their own quirks - so you discover services at runtime and adapt.
The connection lifecycle
The unglamorous truth of BLE is that most of the work is state management:
- Scan for peripherals advertising the right service.
- Connect and discover services and characteristics.
- Subscribe to the ones that stream data (speed, distance, time).
- Write to the control characteristic to change speed or incline.
- Handle disconnects - devices drop, users walk out of range, sessions end.
Every one of these steps can fail or stall, so the app is really a state machine with a treadmill on the other end.
Reading stats vs sending commands
Two directions, two different concerns:
- Reading is a subscription: the treadmill pushes notifications and you decode the bytes into speed, distance, time, calories. The decoding has to match the characteristic's data layout exactly.
- Writing is a control request: you send a command to set speed or incline. Many machines require a "take control" handshake before they accept commands, and you have to respect their allowed ranges.
Lessons from talking to hardware
- Assume the spec is a suggestion. Real devices deviate. Log raw packets and be defensive when decoding.
- Make the state machine explicit. Scanning, connecting, connected, reconnecting - model them directly instead of juggling booleans.
- Never trust the connection. Design for the treadmill vanishing mid-run; recover gracefully instead of hanging.
- Keep the UI responsive during BLE work. Connection and discovery are async; the interface should always show what's happening.
Takeaways
BLE hardware apps are less about the flashy UI and more about robust connection handling and correct byte decoding. Get the state machine and the parsing right and the rest is easy.
The app is at https://treadmillpro.app
If you've worked with CoreBluetooth or FTMS, I'm curious how you handle devices that ignore the standard - do you maintain per-device quirks, or try to stay generic?
Top comments (0)