Zwift documentation for interfacing with smart trainers?

I am in the process of constructing a DIY Smart trainer, and am needing a couple questions answered, or at least pointed to appropriate documentation.

The transmission of the wheel-speed and power to Zwift seems pretty straight forward as it should prescribe to standards defined by ANT+ and/or BLE. What isn’t so clear is how resistance is communicated back to the trainer? This is one element that I do not believe is defined in the standard ANT+/BLE profiles.

Also the following would be a question of opinion for those that have done this before… ANT+ or BLE? I have examined both, and it appears the ANT+ libraries are easier to implement, but does require one to be an ANT “adopter”, which isn’t the worst. BLE is an open-standard, but seems much harder programmatically. What option did you use, and why?

I am not too concerned about the electronics or resistance element, this is actually pretty similar to a project I have worked on in the past. At least the way have have envisioned the design.

The ANT+ FE-C protocol includes the bi-directional communication of trainer data to the controller (speed, power, etc), and controller data to the trainer (power target, but also optionally resistance “percentage” and calibration controls). There are github open-source implementations of the ANT+ FE-C profile.

The equivalent BLE profile is FTMS. This protocol is more recent than ANT+ FE-C, so you might have more difficulty putting together a solution from existing pieces.

Did you have success with this?

I’m looking into building my own trainer as well, that uses a brushless DC motor controlled by the open source VESC speed controller which has support for a BLE module through which I would like to communicate to Zwift.

I wonder if anyone else has considered this?

1 Like

I am also trying the implementation with BLE FTMS.
I don’t understand how Zwift controls the load on the bike.
Which characteristic and parameters does Zwift use?

It uses the Fitness Machine profile. However there are shockingly few examples on how to perform this by most every major stack producer. Bluetooth SIG has also removed a major, and very helpful component of their BLE documentation.

I have not had much time to work with lately, but its still an active project for me. I just come away feeling really stupid every time I try to implement FTM. (power alone is actually pretty straight forward, again there are a handfull of code examples).

Out of curiosity, which micro-controller / BLE stack implementation are you planning to use?

DC motor controlled by the open source VESC speed controller

This is similar to my concept.

I got it!

Using micropython Bluetooth LE on ESP32
https://docs.micropython.org/en/latest/library/ubluetooth.html

You must implements FTMS.

snippet

_UUID_FITNESS_MACHINE = bluetooth.UUID(0x1826)
_SERVICE_FITNESS_MACHINE =
_UUID_FITNESS_MACHINE,
(
_CHAR_INDOOR_BIKE_DATA,
_CHAR_FITNESS_MACHINE_CONTROL_POINT,
_CHAR_FITNESS_MACHINE_FEATURE,
_CHAR_FITNESS_MACHINE_STATUS,
_CHAR_SUPPORTED_RESISTANCE_LEVEL_RANGE,
_CHAR_TRAINING_STATUS,
),
)

The app, like Zwift and Kinomap, indices the ‘Set Indoor Bike Simulation Parameters’ (Op:0x11) through a Fitness Machine Control Point.

The sever read like this.

CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xee\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xee\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xee\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xee\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xee\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xee\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xee\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xee\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xee\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xee\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xee\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xa5\xff(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xa3\xff(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xa5\xff(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xa8\xff(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xad\xff(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xb4\xff(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xbd\xff(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xcb\xff(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xdd\xff(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00g\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xa4\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xe5\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\x1e\x01(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00D\x01(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00`\x01(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00j\x01(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00a\x01(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xf8\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xc9\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\xa5\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\x93\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00\x85\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00x\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00o\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00i\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00^\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00]\x00(3’
CP CMD: 0x11 SET INDOOR BIKE SIMULATION b’\x11\x00\x00^\x00(3’