Take control of your Fitness machines

Charles Anssens
Decathlon Digital
Published in
7 min readJan 16, 2024

In this article we will show you an introduction to the connection to fitness machines to develop a digital experience, you will have the keys to be able to build your own connected sports experience.

A treadmill with a connected application

New possibilities for fitness equipment with connectivity

For several years, fitness machines have been increasingly connectable via a Bluetooth connection. This allows you to retrieve practice data in real time and even take control of the machine to adapt the speed, resistance, etc.

A Bluetooth Low Energy (FTMS) protocol standard has been designed to communicate with fitness machines and supports a wide variety of equipment: treadmills, exercise bikes, elliptical bikes, rowers, steppers, stair machines .

The FTMS (FiTness Machine Service) protocol is supported by a large number of companies like Polar, Wahoo Fitness, Apple, etc. ensuring very good diffusion in the ecosystem of fitness machines.

Why develop new connected sports experiences?

The applications are a complement to the fitness machines allowing new functionalities to be added, this helps to break the monotony of training and make it even more fun or effective.

During the first confinement of 2020 due to the COVID, the Zwift application was very successful because it made it possible to connect cyclists training at home by immersing them in a virtual environment.

The possibilities are immense and you will see that communication between an application and a treadmill, for example, can be achieved very easily.

Introduction to the Bluetooth Low Energy FTMS protocol

The FTMS (FiTness Machine Service) protocol allows you to interact with a multitude of equipment regardless of the brand.

It is a standard defined by the Bluetooth Sig and available online : https://www.bluetooth.com/specifications/specs/fitness-machine-service-1-0/.

The FTMS is a BLE GATT (Generic ATTribute profile) service. GATT defines a method of communicating with a standardized device. The Bluetooth device (in our case a treadmill for example) is the GATT server, the GATT client (in our case the user’s smartphone or computer) executes requests on the GATT server.

A GATT server exposes characteristics. Each characteristic has attributes, the main ones of which are as follows:

  • READ
  • WRITE
  • NOTIFY
  • INDICATE

A characteristic with a READ attribute can therefore be read by the client. Each time the client sends a read request for the characteristic, the server sends the response in return.

A WRITE feature can receive data from the client when the client writes to the characteristic.

The NOTIFY attribute allows the server to send data to the client at the initiative of the server. To enable notifications, the customer has to enable notifications on the characteristic in question. When notifications are enabled, the server can regularly send data to the client.

The INDICATE attribute is associated with the WRITE attribute and allows the server to give a status following writing to a characteristic. Typically a characteristic is used to send commands to the equipment which can give feedback on the execution of this command.

A GATT service contains a certain number of characteristics which have attributes. Services and characteristics are identified by a 128-bit UUID which can be shortened to 16 bits for all those defined in the Bluetooth specification (example: 0x180A for the “Device Information” service).

For our case we will make a connection request to the GATT server to reach the FTMS service, then we will seek to subscribe to the treadmillData characteristic allowing us to receive by notification all the values exposed by a treadmill (speed, distance , elapsed time, etc.)

Sample code: communicate with your fitness device

We will see step by step the connection to a treadmill, then how to collect data from the treadmill.

Flow of exchange to connect and get data from a BLE treadmill

We will present the example in JS using the Bluetooth web capability.

We start by initializing constants which will be used to call the correct service and the correct Bluetooth characteristic.

const serviceFTMS = 0x1826;
const treadmillData = 0x2acd;

We are now launching the detection of devices with the FTMS service by declaring a filter, this avoids having to receive absolutely all Bluetooth devices in the surrounding area.

The connect function is available in several steps, we use a sequence of asynchronous steps knowing that each step can take more or less time.

function connect() {
console.log('Requesting FTMS Bluetooth Device...');
navigator.bluetooth.requestDevice({
filters:[{
services: [serviceFTMS],
}]}).
then(device => {
console.log('Connecting to GATT Server...');
return device.gatt.connect();
})

After making the connection, we access the FTMS service then the characteristics of the treadmill allowing us to retrieve the data.

 .then(server => {
console.log('Getting Service...');
return server.getPrimaryService(serviceFTMS);
})
.then(service => {
console.log('Getting Characteristic...');
return service.getCharacteristic(treadmillData);
})

Finally we subscribe to all updates of the Bluetooth characteristic in order to receive the values ​​as they come.

 .then(characteristic => {
var treadmillDataCharacteristic = characteristic;
return treadmillDataCharacteristic.startNotifications().then(_ => {
console.log('> Notifications started');
treadmillDataCharacteristic.addEventListener('characteristicvaluechanged', handleNotifications);
});
})
.catch(error => {
console.log('Argh! ' + error);
});
}

We are at the end of the connection phase, we are now subscribed to all characteristic updates on the call to the handleNotifications function.

We will now process the characteristic data in order to make it usable.

In this example we will only show how to display it in the javascript console log of your web browser. It’s up to you to handle these data to build your application.

The characteristic data first contains two bytes dedicated to flags to declare which protocol values ​​are available. We must analyze the bits of the flags field in order to know what data we have and each data has its own size from 1 byte to 4 bytes. (nb: the size of each data is defined in the FTMS service specification)

function handleNotifications(event) {
let value = event.target.value;
let a = [];
// Convert raw data bytes to hex values in order to console log each notification.
for (let i = 0; i < value.byteLength; i++)
{
a.push('0x' + ('00' + value.getUint8(i).toString(16)).slice(-2));
}
console.log('> ' + a.join(' '));

var flags = value.getUint16(0, /*littleEndian=*/true);
// 2octets for flags, 2octets for instant speed, nextPosition is incremented following the number of octets for each value
var nextPosition = 4;


if ((flags & (1 << 1)) != 0) {posAvgSpeed=nextPosition; nextPosition+=2;}
if ((flags & (1 << 2)) != 0) {posTotDistance=nextPosition; nextPosition+=3;}//4
if ((flags & (1 << 3)) != 0) {posInclination=nextPosition; nextPosition+=4;}//8
if ((flags & (1 << 4)) != 0) {posElevGain=nextPosition; nextPosition+=4;}
if ((flags & (1 << 5)) != 0) {posInsPace=nextPosition; nextPosition+=1;}
if ((flags & (1 << 6)) != 0) {posAvgPace=nextPosition; nextPosition+=1;}
if ((flags & (1 << 7)) != 0) {posKcal=nextPosition; nextPosition+=5;}
if ((flags & (1 << 8)) != 0) {posHR=nextPosition; nextPosition+=1;}
if ((flags & (1 << 9)) != 0) {posMET=nextPosition; nextPosition+=1;}
if ((flags & (1 << 10)) != 0) {posElaspedTime=nextPosition; nextPosition+=2;}
if ((flags & (1 << 11)) != 0) {posRemainTime=nextPosition; nextPosition+=2;}
if ((flags & (1 << 12)) != 0) {posForceBelt=nextPosition; nextPosition+=4;}

// instantaneous speed
speed = value.getUint16(2, /*littleEndian=*/true)/100;
console.log('> Speed ' + speed);


//distance
distance = value.getUint16(posTotDistance, /*littleEndian=*/true);
distance_complement = value.getUint8(posTotDistance + 2, /*littleEndian=*/true);
distance_complement = distance_complement << 16;
distance = distance + distance_complement;
console.log('> Distance ' + distance);

if (typeof posInclination != "undefined")
{
inclination = (value.getInt16(posInclination, /*littleEndian=*/true)/10);
console.log('> Inclinaison % ' + inclination );
}



if (typeof posKcal != "undefined")
{
kcal = (value.getUint16(posKcal, /*littleEndian=*/true));
console.log('> Kcal ' + kcal);
}

if (typeof posHR != "undefined")
{
console.log('> HR ' + (value.getUint8(posHR, /*littleEndian=*/true)));
}

if (typeof posElaspedTime != "undefined")
{
console.log('> Elapsed time ' + (value.getUint16(posElaspedTime, /*littleEndian=*/true)));
}
}

That’s it, we have accomplished the connection phase, the handling of data received by notification (analyze the flags, parse the data for each type of values).

You could see below a quick capture of the log output after a connection to a FTMS treadmill, pushing the play button on the treadmill and then changing the speed, etc.

Example of output after a connection to a treadmill

Bluetooth Web Note:

Web Bluetooth is a good opportunity to make a server-side application but there are limitations on the support of this API on different browsers. It is not available on all web browsers, so we invite you to check compatibility by searching for “web bluetooth browser compatibility”.

The primary interest was to have an easily usable and understandable example, it is of course possible to achieve the same thing in other languages ​​(Python, Java, .net, etc.) or other platforms (Raspberry, Arduino, etc.) .

Sharing source code on Github

You can find examples of applications and use cases on this Github link

This is the link of this demo code : https://github.com/Decathlon/domyos-developers/tree/main/ftms-treadmill-web-console

Do not hesitate to contribute by offering your code examples.

Conclusion

We hope that this article has allowed you to understand how communication works between a fitness machine such as a treadmill and an application. This introduction is totally adaptable to other types of equipment like indoor bikes, ellipticals or rowers machines.

If your programming language allows you to manage a Bluetooth Low Energy connection, you can start by creating an immersive experience linked to an indoor sports product.

We invite you to take advantage of this possibility in order to offer an endless variety of combinations that could mix social and community networks with, for example, “leader boards”, multiplayer, 3D technologies and why not even virtual reality…

Do you have experience using FTMS? Do you have some additional recommendations? Let us know in the comments below!

🙏🏼 If you enjoy reading this article, please consider giving it a few 👏👏👏

Article co-written with Tony Ducrocq

--

--