FTMS lab: decode treadmill, bike and rower Bluetooth data
An FTMS machine data value is a Flags field followed only by the fields its bits announce, so the same characteristic arrives in different lengths and you cannot read it at fixed offsets. In Treadmill Data (0x2ACD), Instantaneous Speed is present when bit 0 (“More Data”) is 0, every other field when its own bit is 1, and the Bluetooth SIG’s structure gives each field’s size and resolution: speed in 1/100 km/h, Indoor Bike cadence in 1/2 rpm. This lab connects a treadmill, indoor bike, rower or cross trainer over Web Bluetooth and decodes every value against that structure, or decodes hex you paste, with no machine at all.
Paste a value
Bytes as hex, in the order they arrive (spaces, commas or 0x prefixes are fine). The samples are synthetic, built from the SIG’s structure by the same module that decodes them.
Sample: Instantaneous Speed 1050 (×0.01 km/h), Total Distance 1234 m, Inclination 15 and Ramp Angle 9 (×0.1), Elapsed Time 600 s.
Treadmill Data (0x2ACD) · 13 octets · little-endian
| Field | Present when | Octets | Raw | Value |
|---|---|---|---|---|
| Flagsboolean[16] | alwayspresent | 0–1 | 1036 | 0x040C |
| Instantaneous Speeduint16 | bit 0 of Flags is 0present | 2–3 | 1050 | 10.5 kilometre per hour× 0.01 from Represented values: M = 1, d = -2, b = 0beside Unit is 1/100 of a kilometer per hour |
| Average Speeduint16 | bit 1 of Flags is 1absent | — | — | — |
| Total Distanceuint24 | bit 2 of Flags is 1present | 4–6 | 1234 | 1234 metre |
| Inclinationsint16 | bit 3 of Flags is 1present | 7–8 | 15 | 1.5 percentage× 0.1 from Represented values: M = 1, d = -1, b = 0beside Unit is 1/10 of a percent |
| Ramp Angle Settingsint16 | bit 3 of Flags is 1present | 9–10 | 9 | 0.9 degree× 0.1 from Represented values: M = 1, d = -1, b = 0beside Unit is 1/10 of a degree |
| Positive Elevation Gainuint16 | bit 4 of Flags is 1absent | — | — | — |
| Negative Elevation Gainuint16 | bit 4 of Flags is 1absent | — | — | — |
| Instantaneous Pace (Time per 500 meters)uint16 | bit 5 of Flags is 1absent | — | — | — |
| Average Pace (Time per 500 meters)uint16 | bit 6 of Flags is 1absent | — | — | — |
| Total Energyuint16 | bit 7 of Flags is 1absent | — | — | — |
| Energy Per Houruint16 | bit 7 of Flags is 1absent | — | — | — |
| Energy Per Minuteuint8 | bit 7 of Flags is 1absent | — | — | — |
| Heart Rateuint8 | bit 8 of Flags is 1absent | — | — | — |
| Metabolic Equivalentuint8 | bit 9 of Flags is 1absent | — | — | — |
| Elapsed Timeuint16 | bit 10 of Flags is 1present | 11–12 | 600 | 600 second |
| Remaining Timeuint16 | bit 11 of Flags is 1absent | — | — | — |
| Force On Beltsint16 | bit 12 of Flags is 1absent | — | — | — |
| Power Outputsint16 | bit 12 of Flags is 1absent | — | — | — |
Flags bit by bit 0x040C
| Bit | Value | The SIG’s description |
|---|---|---|
| 0 | 0 | More Data |
| 1 | 0 | Average Speed present |
| 2 | 1 | Total Distance present |
| 3 | 1 | Inclination and Ramp Angle Setting present |
| 4 | 0 | Elevation Gain present |
| 5 | 0 | Instantaneous Pace present |
| 6 | 0 | Average Pace present |
| 7 | 0 | Expended Energy present |
| 8 | 0 | Heart Rate present |
| 9 | 0 | Metabolic Equivalent present |
| 10 | 1 | Elapsed Time present |
| 11 | 0 | Remaining Time present |
| 12 | 0 | Force On Belt and Power Output present |
| 13–15 | 000b | Reserved for Future Use |
The structures it decodes against
Each table is the layout the lab compiled from the Bluetooth SIG’s GATT Specification Supplement files at commit 408f4a0 of the SIG’s public repository (read 2026-10-17), through this site’s generated copy. The machine data characteristics, then the Supported Range characteristics the lab reads once after connecting:
Treadmill Data (0x2ACD) · 19 fields
| Field | Type | Size | Present when | Unit and resolution |
|---|---|---|---|---|
| Flags | boolean[16] | 2 | always | flags |
| Instantaneous Speed | uint16 | 0 or 2 | bit 0 of Flags is 0 | kilometre per hour, × 0.01 (Represented values: M = 1, d = -2, b = 0) |
| Average Speed | uint16 | 0 or 2 | bit 1 of Flags is 1 | kilometre per hour, × 0.01 (Represented values: M = 1, d = -2, b = 0) |
| Total Distance | uint24 | 0 or 3 | bit 2 of Flags is 1 | metre |
| Inclination | sint16 | 0 or 2 | bit 3 of Flags is 1 | percentage, × 0.1 (Represented values: M = 1, d = -1, b = 0) |
| Ramp Angle Setting | sint16 | 0 or 2 | bit 3 of Flags is 1 | degree, × 0.1 (Represented values: M = 1, d = -1, b = 0) |
| Positive Elevation Gain | uint16 | 0 or 2 | bit 4 of Flags is 1 | metre, × 0.1 (Represented values: M = 1, d = -1, b = 0) |
| Negative Elevation Gain | uint16 | 0 or 2 | bit 4 of Flags is 1 | metre, × 0.1 (Represented values: M = 1, d = -1, b = 0) |
| Instantaneous Pace (Time per 500 meters) | uint16 | 0 or 2 | bit 5 of Flags is 1 | second |
| Average Pace (Time per 500 meters) | uint16 | 0 or 2 | bit 6 of Flags is 1 | second |
| Total Energy | uint16 | 0 or 2 | bit 7 of Flags is 1 | kilogram calorie |
| Energy Per Hour | uint16 | 0 or 2 | bit 7 of Flags is 1 | kilogram calorie |
| Energy Per Minute | uint8 | 0 or 1 | bit 7 of Flags is 1 | kilogram calorie |
| Heart Rate | uint8 | 0 or 1 | bit 8 of Flags is 1 | beats per minute |
| Metabolic Equivalent | uint8 | 0 or 1 | bit 9 of Flags is 1 | metabolic equivalent |
| Elapsed Time | uint16 | 0 or 2 | bit 10 of Flags is 1 | second |
| Remaining Time | uint16 | 0 or 2 | bit 11 of Flags is 1 | second |
| Force On Belt | sint16 | 0 or 2 | bit 12 of Flags is 1 | newton |
| Power Output | sint16 | 0 or 2 | bit 12 of Flags is 1 | watt |
Treadmill Data on the Bluetooth GATT reference · the SIG’s file gss/org.bluetooth.characteristic.treadmill_data.yaml
Cross Trainer Data (0x2ACE) · 21 fields
| Field | Type | Size | Present when | Unit and resolution |
|---|---|---|---|---|
| Flags | boolean[24] | 3 | always | flags |
| Instantaneous Speed | uint16 | 0 or 2 | bit 0 of Flags is 0 | kilometre per hour, × 0.01 (Represented values: M = 1, d = -2, b = 0) |
| Average Speed | uint16 | 0 or 2 | bit 1 of Flags is 1 | kilometre per hour, × 0.01 (Represented values: M = 1, d = -2, b = 0) |
| Total Distance | uint24 | 0 or 3 | bit 2 of Flags is 1 | metre |
| Steps Per Minute | uint16 | 0 or 2 | bit 3 of Flags is 1 | per minute |
| Average Step Rate | uint16 | 0 or 2 | bit 3 of Flags is 1 | per minute |
| Stride Count | uint16 | 0 or 2 | bit 4 of Flags is 1 | —, × 0.1 (Represented values: M = 1, d = -1, b = 0) |
| Positive Elevation Gain | uint16 | 0 or 2 | bit 5 of Flags is 1 | metre |
| Negative Elevation Gain | uint16 | 0 or 2 | bit 5 of Flags is 1 | metre |
| Inclination | sint16 | 0 or 2 | bit 6 of Flags is 1 | percentage, × 0.1 (Represented values: M = 1, d = -1, b = 0) |
| Ramp Setting | sint16 | 0 or 2 | bit 6 of Flags is 1 | degree, × 0.1 (Represented values: M = 1, d = -1, b = 0) |
| Resistance Level | uint8 | 0 or 1 | bit 7 of Flags is 1 | —, × 10 (Represented values: M = 1, d = 1, b = 0) — the same field also says Unit is 1 |
| Instantaneous Power | sint16 | 0 or 2 | bit 8 of Flags is 1 | watt |
| Average Power | sint16 | 0 or 2 | bit 9 of Flags is 1 | watt |
| Total Energy | uint16 | 0 or 2 | bit 10 of Flags is 1 | kilogram calorie |
| Energy Per Hour | uint16 | 0 or 2 | bit 10 of Flags is 1 | kilogram calorie |
| Energy Per Minute | uint8 | 0 or 1 | bit 10 of Flags is 1 | kilogram calorie |
| Heart Rate | uint8 | 0 or 1 | bit 11 of Flags is 1 | beats per minute |
| Metabolic Equivalent | uint8 | 0 or 1 | bit 12 of Flags is 1 | metabolic equivalent, × 0.1 (Represented values: M = 1, d = -1, b = 0) |
| Elapsed Time | uint16 | 0 or 2 | bit 13 of Flags is 1 | second |
| Remaining Time | uint16 | 0 or 2 | bit 14 of Flags is 1 | second |
Cross Trainer Data on the Bluetooth GATT reference · the SIG’s file gss/org.bluetooth.characteristic.cross_trainer_data.yaml
Step Climber Data (0x2ACF) · 13 fields
| Field | Type | Size | Present when | Unit and resolution |
|---|---|---|---|---|
| Flags | boolean[16] | 2 | always | flags |
| Floors | uint16 | 0 or 2 | bit 0 of Flags is 0 | — |
| Step Count | uint16 | 0 or 2 | bit 0 of Flags is 0 | — |
| Steps Per Minute | uint16 | 0 or 2 | bit 1 of Flags is 1 | per minute |
| Average Step Rate | uint16 | 0 or 2 | bit 2 of Flags is 1 | per minute |
| Positive Elevation Gain | uint16 | 0 or 2 | bit 3 of Flags is 1 | metre |
| Total Energy | uint16 | 0 or 2 | bit 4 of Flags is 1 | kilogram calorie |
| Energy Per Hour | uint16 | 0 or 2 | bit 4 of Flags is 1 | kilogram calorie |
| Energy Per Minute | uint8 | 0 or 1 | bit 4 of Flags is 1 | kilogram calorie |
| Heart Rate | uint8 | 0 or 1 | bit 5 of Flags is 1 | beats per minute |
| Metabolic Equivalent | uint8 | 0 or 1 | bit 6 of Flags is 1 | metabolic equivalent, × 0.1 (Represented values: M = 1, d = -1, b = 0) |
| Elapsed Time | uint16 | 0 or 2 | bit 7 of Flags is 1 | second |
| Remaining Time | uint16 | 0 or 2 | bit 8 of Flags is 1 | second |
Step Climber Data on the Bluetooth GATT reference · the SIG’s file gss/org.bluetooth.characteristic.step_climber_data.yaml
Stair Climber Data (0x2AD0) · 13 fields
| Field | Type | Size | Present when | Unit and resolution |
|---|---|---|---|---|
| Flags | boolean[16] | 2 | always | flags |
| Floors | uint16 | 0 or 2 | bit 0 of Flags is 0 | — |
| Steps Per Minute | uint16 | 0 or 2 | bit 1 of Flags is 1 | per minute |
| Average Step Rate | uint16 | 0 or 2 | bit 2 of Flags is 1 | per minute |
| Positive Elevation Gain | uint16 | 0 or 2 | bit 3 of Flags is 1 | metre |
| Stride Count | uint16 | 0 or 2 | bit 4 of Flags is 1 | — |
| Total Energy | uint16 | 0 or 2 | bit 5 of Flags is 1 | kilogram calorie |
| Energy Per Hour | uint16 | 0 or 2 | bit 5 of Flags is 1 | kilogram calorie |
| Energy Per Minute | uint8 | 0 or 1 | bit 5 of Flags is 1 | kilogram calorie |
| Heart Rate | uint8 | 0 or 1 | bit 6 of Flags is 1 | beats per minute |
| Metabolic Equivalent | uint8 | 0 or 1 | bit 7 of Flags is 1 | metabolic equivalent, × 0.1 (Represented values: M = 1, d = -1, b = 0) |
| Elapsed Time | uint16 | 0 or 2 | bit 8 of Flags is 1 | second |
| Remaining Time | uint16 | 0 or 2 | bit 9 of Flags is 1 | second |
Stair Climber Data on the Bluetooth GATT reference · the SIG’s file gss/org.bluetooth.characteristic.stair_climber_data.yaml
Rower Data (0x2AD1) · 17 fields
| Field | Type | Size | Present when | Unit and resolution |
|---|---|---|---|---|
| Flags | boolean[16] | 2 | always | flags |
| Stroke Rate | uint8 | 0 or 1 | bit 0 of Flags is 0 | per minute, × 0.5 (Represented values: M = 1, d = 0, b = -1) |
| Stroke Count | uint16 | 0 or 2 | bit 0 of Flags is 0 | — |
| Average Stroke Rate | uint8 | 0 or 1 | bit 1 of Flags is 1 | per minute, × 0.5 (Represented values: M = 1, d = 0, b = -1) |
| Total Distance | uint24 | 0 or 3 | bit 2 of Flags is 1 | metre |
| Instantaneous Pace (Time per 500 meters) | uint16 | 0 or 2 | bit 3 of Flags is 1 | second |
| Average Pace (Time per 500 meters) | uint16 | 0 or 2 | bit 4 of Flags is 1 | second |
| Instantaneous Power | sint16 | 0 or 2 | bit 5 of Flags is 1 | watt |
| Average Power | sint16 | 0 or 2 | bit 6 of Flags is 1 | watt |
| Resistance Level | uint8 | 0 or 1 | bit 7 of Flags is 1 | —, × 10 (Represented values: M = 1, d = 1, b = 0) — the same field also says Unit is 1 |
| Total Energy | uint16 | 0 or 2 | bit 8 of Flags is 1 | kilogram calorie |
| Energy Per Hour | uint16 | 0 or 2 | bit 8 of Flags is 1 | kilogram calorie |
| Energy Per Minute | uint8 | 0 or 1 | bit 8 of Flags is 1 | kilogram calorie |
| Heart Rate | uint8 | 0 or 1 | bit 9 of Flags is 1 | beats per minute |
| Metabolic Equivalent | uint8 | 0 or 1 | bit 10 of Flags is 1 | metabolic equivalent, × 0.1 (Represented values: M = 1, d = -1, b = 0) |
| Elapsed Time | uint16 | 0 or 2 | bit 11 of Flags is 1 | second |
| Remaining Time | uint16 | 0 or 2 | bit 12 of Flags is 1 | second |
Rower Data on the Bluetooth GATT reference · the SIG’s file gss/org.bluetooth.characteristic.rower_data.yaml
Indoor Bike Data (0x2AD2) · 16 fields
| Field | Type | Size | Present when | Unit and resolution |
|---|---|---|---|---|
| Flags | boolean[16] | 2 | always | flags |
| Instantaneous Speed | uint16 | 0 or 2 | bit 0 of Flags is 0 | kilometre per hour, × 0.01 (Represented values: M = 1, d = -2, b = 0) |
| Average Speed | uint16 | 0 or 2 | bit 1 of Flags is 1 | kilometre per hour, × 0.01 (Represented values: M = 1, d = -2, b = 0) |
| Instantaneous Cadence | uint16 | 0 or 2 | bit 2 of Flags is 1 | revolution per minute, × 0.5 (Represented values: M = 1, d = 0, b = -1) |
| Average Cadence | uint16 | 0 or 2 | bit 3 of Flags is 1 | revolution per minute, × 0.5 (Represented values: M = 1, d = 0, b = -1) |
| Total Distance | uint24 | 0 or 3 | bit 4 of Flags is 1 | metre |
| Resistance Level | uint8 | 0 or 1 | bit 5 of Flags is 1 | —, × 10 (Represented values: M = 1, d = 1, b = 0) — the same field also says Unit is 1 |
| Instantaneous Power | sint16 | 0 or 2 | bit 6 of Flags is 1 | watt |
| Average Power | sint16 | 0 or 2 | bit 7 of Flags is 1 | watt |
| Total Energy | uint16 | 0 or 2 | bit 8 of Flags is 1 | kilogram calorie |
| Energy Per Hour | uint16 | 0 or 2 | bit 8 of Flags is 1 | kilogram calorie |
| Energy Per Minute | uint8 | 0 or 1 | bit 8 of Flags is 1 | kilogram calorie |
| Heart Rate | uint8 | 0 or 1 | bit 9 of Flags is 1 | beats per minute |
| Metabolic Equivalent | uint8 | 0 or 1 | bit 10 of Flags is 1 | metabolic equivalent, × 0.1 (Represented values: M = 1, d = -1, b = 0) |
| Elapsed Time | uint16 | 0 or 2 | bit 11 of Flags is 1 | second |
| Remaining Time | uint16 | 0 or 2 | bit 12 of Flags is 1 | second |
Indoor Bike Data on the Bluetooth GATT reference · the SIG’s file gss/org.bluetooth.characteristic.indoor_bike_data.yaml
Supported Speed Range (0x2AD4) · 3 fields
| Field | Type | Size | Present when | Unit and resolution |
|---|---|---|---|---|
| Minimum Speed | uint16 | 2 | always | kilometre per hour, × 0.01 (Represented values: M = 1, d = -2, b = 0) |
| Maximum Speed | uint16 | 2 | always | kilometre per hour, × 0.01 (Represented values: M = 1, d = -2, b = 0) |
| Minimum Increment | uint16 | 2 | always | kilometre per hour, × 0.01 (Represented values: M = 1, d = -2, b = 0) |
Supported Speed Range on the Bluetooth GATT reference · the SIG’s file gss/org.bluetooth.characteristic.supported_speed_range.yaml
Supported Inclination Range (0x2AD5) · 3 fields
| Field | Type | Size | Present when | Unit and resolution |
|---|---|---|---|---|
| Minimum Inclination | sint16 | 2 | always | percentage, × 0.1 (Represented values: M = 1, d = -1, b = 0) |
| Maximum Inclination | sint16 | 2 | always | percentage, × 0.1 (Represented values: M = 1, d = -1, b = 0) |
| Minimum Increment | uint16 | 2 | always | percentage, × 0.1 (Represented values: M = 1, d = -1, b = 0) |
Supported Inclination Range on the Bluetooth GATT reference · the SIG’s file gss/org.bluetooth.characteristic.supported_inclination_range.yaml
Supported Resistance Level Range (0x2AD6) · 3 fields
| Field | Type | Size | Present when | Unit and resolution |
|---|---|---|---|---|
| Minimum Resistance Level | uint8 | 1 | always | —, × 10 (Represented values: M = 1, d = 1, b = 0) — the same field also says Unit is 1 |
| Maximum Resistance Level | uint8 | 1 | always | —, × 10 (Represented values: M = 1, d = 1, b = 0) — the same field also says Unit is 1 |
| Minimum Increment | uint8 | 1 | always | —, × 10 (Represented values: M = 1, d = 1, b = 0) — the same field also says Unit is 1 |
Supported Resistance Level Range on the Bluetooth GATT reference · the SIG’s file gss/org.bluetooth.characteristic.supported_resistance_level_range.yaml
Supported Heart Rate Range (0x2AD7) · 3 fields
| Field | Type | Size | Present when | Unit and resolution |
|---|---|---|---|---|
| Minimum Heart Rate | uint8 | 1 | always | beats per minute |
| Maximum Heart Rate | uint8 | 1 | always | beats per minute |
| Minimum Increment | uint8 | 1 | always | beats per minute |
Supported Heart Rate Range on the Bluetooth GATT reference · the SIG’s file gss/org.bluetooth.characteristic.supported_heart_rate_range.yaml
Supported Power Range (0x2AD8) · 3 fields
| Field | Type | Size | Present when | Unit and resolution |
|---|---|---|---|---|
| Minimum Power | sint16 | 2 | always | watt |
| Maximum Power | sint16 | 2 | always | watt |
| Minimum Increment | uint16 | 2 | always | watt |
Supported Power Range on the Bluetooth GATT reference · the SIG’s file gss/org.bluetooth.characteristic.supported_power_range.yaml
Fitness Machine Feature (0x2ACC) · no structure: shown raw
The SIG's public repository has no gss structure for this characteristic. The lab shows its bytes as hex.
Training Status (0x2AD3) · no structure: shown raw
The SIG's public repository has no gss structure for this characteristic. The lab shows its bytes as hex.
Fitness Machine Control Point (0x2AD9) · no structure: shown raw
The SIG's public repository has no gss structure for this characteristic. The lab shows its bytes as hex.
Fitness Machine Control Point on the Bluetooth GATT reference
Fitness Machine Status (0x2ADA) · no structure: shown raw
The SIG's public repository has no gss structure for this characteristic. The lab shows its bytes as hex.
How to read an FTMS value
- Flags first. Treadmill, Indoor Bike and Rower Data open with a two-octet Flags field (
boolean[16]); Cross Trainer Data’s is three octets (boolean[24]). Read it as a little-endian number. - Walk the fields in order. For each field, its “Present if” line names one bit and the value that makes it present. Present: read its size and move on. Absent: it takes no octets. Several fields can share one bit (Total Energy, Energy Per Hour and Energy Per Minute all hang on one), and bit 0 is the odd one out:
Present if bit 0 of Flags field is set to 0
. - Scale each value. The SIG gives a
Represented values: M = …, d = …, b = …
line where the raw number is not the unit, and the lab multiplies by M × 10d × 2b. Instantaneous Speed:Represented values: M = 1, d = -2, b = 0
, besideUnit is 1/100 of a kilometer per hour
. Instantaneous Cadence:Represented values: M = 1, d = 0, b = -1
, besideUnit is 1/2 of a revolution per minute
. - Merge across notifications. A machine with more to say than fits in one value can set bit 0 (“More Data”), which drops Instantaneous Speed from that value. The lab’s “Latest values” panel keeps the newest reading of each field from whichever notification carried it; the treadmill replay sends such a split packet every fifth second.
Where the decoder reads past the SIG’s file
- Byte order. The YAML gives sizes, not byte order. The lab reads little-endian, as the Web Bluetooth specification’s heart-rate example does (
result.heartRate = data.getUint16(index, /*littleEndian=*/true);, read 2026-10-31). - The scale formula. The files do not spell out how M, d and b combine. The lab uses M × 10d × 2b, and its test suite checks that product against every “Unit is” sentence in the dataset it can parse (b = −10 beside “1/1024 second”, M = 5 and d = −3 beside “0.005 kilogram”, and so on): 60 lines, all agreeing except 8.
- Resistance Level. 6 of those 8 are FTMS resistance fields, where the SIG’s own lines disagree:
Represented values: M = 1, d = 1, b = 0
(a factor of 10) sits besideUnit is 1
. The lab shows the value both ways and picks neither. - No structure, no decoding. At this commit the SIG’s repository has no gss file for Fitness Machine Feature (0x2ACC), Training Status (0x2AD3), Fitness Machine Control Point (0x2AD9), Fitness Machine Status (0x2ADA). The lab shows their bytes as hex and does not decode them from memory. That is also why it has no control panel: setting a target speed or power means writing op codes to the Control Point, and the layout of those op codes is not in the files this site reads. The lab never writes to the machine.
Which browsers can connect
Web Bluetooth is a Chromium feature. MDN's compatibility data lists it in Chrome (requestDevice since version 56), Edge, Opera and Samsung Internet, with the note that Chrome's Linux support is not enabled by default, and marks the API experimental. It lists no support in Firefox, in Safari on macOS or iOS, or in Android's WebView.
Source: MDN browser-compat-data, api/Bluetooth.json, read 2026-11-05. Elsewhere the paste box and replays still work.
What the lab cannot tell you
- Accuracy. The bytes say what the machine reports, not whether its belt speed or power reading is right.
- Which fields a model supports before it sends them. Fitness Machine Feature declares that, and it is one of the characteristics shown raw here.
- Anything about a machine that does not advertise the Fitness Machine service: the browser’s chooser only lists devices that do. For any other device, use the BLE GATT explorer.
The integration side is in what FTMS is, treadmill app integration, indoor bike trainer integration and rowing machine data; the radio question in ANT+ vs Bluetooth. Power meters and cadence sensors have their own cycling power and cadence lab.
How this works, and what it sends
Nothing leaves your device. The browser shows its own device chooser, and the page can reach only the machine you pick and only its Fitness Machine service. It subscribes to the characteristics that notify (except the Control Point), reads the Supported Range and Feature characteristics once, and writes nothing. Values are decoded in this tab and held in memory until you close it; this site has no server code behind the page and stores nothing.
The samples and replays are generated, not recorded from a machine: their numbers are made up, and they are built by the same module that decodes them, from the field names in the SIG’s structure.
The structures come from the Bluetooth SIG’s public repository (https://bitbucket.org/bluetooth-SIG/public), read by this site’s generator on 2026-10-17. The SIG’s text is quoted with attribution: Copyright © Bluetooth SIG, Inc., which describes these files as proprietary to it. This site is not affiliated with Bluetooth SIG.
A free tool from AIFitnessAPI. More at free tools for health-app builders.