Recently I got hold of an FG2508WB smart body fat scale — an Ant Afu co-branded ICOMON (Wolai) unit. Normally the data can only be viewed through the Ant Afu app. Since the device is right here at hand, I figured I might as well use Python to reverse-engineer the Bluetooth protocol and write a reading script that doesn’t need a phone.
Device and Goal
- Device: Ant Afu × ICOMON FG2508WB smart body fat scale
- Connection: Bluetooth Low Energy (BLE)
- Goal: read the weight directly with Python +
bleak, and parse body-composition data as far as possible


Environment Setup
On Linux, as long as the system Bluetooth works, you just need to install bleak:
|
Check the local Bluetooth controller:
|
Scanning and Connecting
To save power the scale doesn’t advertise normally — it only wakes up when you step on it lightly. Scan with BleakScanner; the device name is usually AFU-WL-TZ-A1:
|
After connecting, the GATT service structure turned out to be:
| Service | Characteristic | Properties |
|---|---|---|
0000ffb0-... |
0000ffb1-... |
write-without-response |
0000ffb0-... |
0000ffb2-... |
notify |
00001530-... |
00001531-... |
write, notify |
The actual data comes out of the notify on 0000ffb2.
Packet Structure
After connecting, write the user parameters (sex/age/height) to trigger a measurement:
|
Once you step on the scale, it continuously sends 20-byte packets over notify. The common types are:
0x00: real-time weight0x80: stable but not yet locked0x02: final result locked
A typical final-result packet looks like this:
|
Parsing the Weight
After comparing several sets of data (empty scale, a person standing on it, milk cartons as ballast), I found that the weight is stored in bytes[10:13] as a 24-bit big-endian integer:
|
The conversion formula:
|
Verification
| Scenario | Real Weight | bytes[10:13] |
Calculated Result |
|---|---|---|---|
| Milk | 13.75 kg | 68 35 b6 |
13.75 kg |
| Human | 67.30 kg | 69 06 b2 |
67.25 kg |
Both matched, with an error within 0.05 kg — exactly the scale’s nominal graduation value. Home body fat scales always fluctuate slightly from one weighing to the next; even the same person measured repeatedly can vary between 0.05 and 0.1 kg, which is normal.

Impedance and Body Composition
In the packet, bytes[4:5] and bytes[6:7] look like impedance values at two frequencies:
|
Metrics like body fat percentage, water percentage, BMI, bone mass, and muscle mass are not directly encoded in the Bluetooth packet — they’re calculated on the app side from “weight + impedance + height/age/sex” using a BIA algorithm. Different manufacturers use different algorithm coefficients; the results I got from plugging into the public BIA formulas deviated from the Ant Afu app, so for now the script only outputs weight and raw impedance values.
The Complete Script
The full script is also in this repository at tools/afu_scale_reader.py; copy it, change DEVICE_ADDRESS, and run it.
|
Run it:
|
Here’s what it actually looks like when running — from scanning to weighing to locking takes about 3-5 seconds:

Summary
- Weight can be parsed straight out of the BLE packet, with the formula
(bytes[10:13] - 0x680000) / 1000. - Body composition data — body fat, water, BMI, etc. — requires an additional BIA algorithm and is still being calibrated.
- Different batches of ICOMON / Ant Afu scales may differ in protocol details, but the service UUID
0000FFB0and the 20-byte packet structure are common features.
If you’ve also reverse-engineered this scale, or you know the specific coefficients of the body-fat algorithm, please leave a comment — I’d love to exchange notes.
评论
0 条评论