-
-
Notifications
You must be signed in to change notification settings - Fork 487
Xiaomi Bluetooth Mi Scale
One of my relatives bought a Xiaomi Mi Scale. After a while I had the idea to buy one for myself and give reverse engineering the Bluetooth 4.x protocol (also known as Bluetooth Smart) a try. First of all I searched the internet for information. Luckily I found some information about the Bluetooth protocol by perillamint, and there was already an Android implementation OpenXiaomiScale by Mnkai. A short summary of the information above:
- The connection was not encrypted at all
- The Mi Scale broadcasts the weight in the Bluetooth advertisement frame
- You can enable a notification flag if you want to receive the weight data on every weight change
- The first byte was a config control byte (which includes a stabilized flag, a weight removed flag, and the scale unit)
- If the scale was set to lbs or jin you have to divide the received weight by 100, otherwise by 200
- The scale weight was received in little endian format
I could use these great pieces of information to start my implementation and test the first connection to the Mi Scale. I enabled the notification flag on the Mi Scale and from then on I received the weight data on every weight change.
The raw hex weight data looks like 0x62 0xAC 0x49 0xE0 0x07 0x0C 0x14 0x0D 0x1C 0x04 on every change. I quickly discovered that the date and time of the measurement were also transmitted with the data, so in the end the byte decoding was:
| byte | function |
|---|---|
| 0 | status byte: - Bit 0: lbs unit - Bit 1-3: unknown - Bit 4: jin unit - Bit 5: stabilized - Bit 6: unknown - Bit 7: weight removed |
| 1-2 | weight (little endian) |
| 3-4 | year (little endian) |
| 5 | month |
| 6 | day |
| 7 | hour |
| 8 | minute |
| 9 | second |
But be aware that the date and time of the measurement are only valid if the stabilized flag is true and the weight removed flag is false!
Unfortunately I could only receive the weight data if a user was on the scale and a connection was established. I didn't have access to the RAM of the Mi Scale where the weight history was stored. Now the complicated part of the reverse engineering started. I had to capture and analyse the Bluetooth transmission between the Mi Scale and the original Mi Fit App.
For capturing the transmission I turned on the Bluetooth HCI Snoop Log under the developer options on my smartphone and started the Mi Fit App. Make sure you turn off your Bluetooth before you turn on the log, and if you want to turn the log off again, do it in reverse order. For the analysing step I opened the saved log with Wireshark. Please note that you should use a version > 1.10, because the newer versions support Bluetooth much better!
Analysing initialization process with wireshark
You will find a lot of unnecessary transmissions, but to find the important operations I searched for the hex value 0xE0 0x07 (year 2016 in little endian format), because I knew that every weight measurement contains the date and time. I found a lot of data packages sent from the Mi Scale to the app that looked like the weight data above. The interesting part was that before the packages were sent, the app sent a write command with the value 0x02 to the scale. That must be the command to get the history data.
But I didn't know which Bluetooth service and characteristic it was sent to. So I had to find out the available Bluetooth services and characteristics on the Mi Scale. For this I downloaded the BLE Scanner App by Bluepixel Technology LLP. With the help of this app I could easily discover the services and characteristics and even send/receive data packages to/from the Mi Scale.
| Service | Characteristics |
|---|---|
|
Device Information 0000180a-0000-1000-8000-00805f9b34fb |
Serial Number String 00002a25-0000-1000-8000-00805f9b34fb Software Revision String 00002a28-0000-1000-8000-00805f9b34fb System ID 00002a23-0000-1000-8000-00805f9b34fb PNP ID 00002a50-0000-1000-8000-00805f9b34fb |
|
Generic Access 00001800-0000-1000-8000-00805f9b34fb |
Device Name 00002a00-0000-1000-8000-00805f9b34fb Appearance 00002a01-0000-1000-8000-00805f9b34fb Peripheral Privacy Flag 00002a02-0000-1000-8000-00805f9b34fb Reconnection Address 00002a03-0000-1000-8000-00805f9b34fb |
|
Generic Attribute 00001801-0000-1000-8000-00805f9b34fb |
Service Changed 00002a05-0000-1000-8000-00805f9b34fb |
|
Weight Scale 0000181d-0000-1000-8000-00805f9b34fb |
Current Time 00002a2b-0000-1000-8000-00805f9b34fb Weight Scale Feature 00002a9e-0000-1000-8000-00805f9b34fb Weight Measurement 00002a9d-0000-1000-8000-00805f9b34fb History Weight Measurement [Custom Characteristics] 00002a2f-0000-3512-2118-0009af100700 |
|
Custom Service 00001530-0000-3512-2118-0009af100700 |
Custom Characteristics 00001531-0000-3512-2118-0009af100700 Custom Characteristic 00001532-0000-3512-2118-0009af100700 Peripheral Preferred Connection Parameters 00002a04-0000-1000-8000-00805f9b34fb Custom Characteristic 00001542-0000-3512-2118-0009af100700 Custom Characteristic 00001543-0000-3512-2118-0009af100700 |
You can find further descriptions of the standard services and characteristics on the official Bluetooth specification website.
The weight scale service was of course the interesting part of it. We already know how the weight measurement characteristic works from the information above, but there was also a custom characteristic (0x2a2f) available on which we can enable an indication flag and write some values. So I turned on the notification flag and wrote the value 0x02 to this characteristic. Now I received the complete weight history of the Mi Scale. But I got two measurements at once, so I had to split them up.
To get only the recently saved weight history, send the following command sequence to the custom characteristic (0x2a2f):
- Turn notifications on for the weight measurement history
- Write the value
0x01 0xFF 0xFF 0xFF 0xFFto the characteristic (you will receive0x01 0x?? 0xFF 0xFF 0xFF 0xFFvia notification, where ?? is the amount of new history entries) - Turn notifications off for the weight measurement history
- Turn notifications on for the weight measurement history
- Write the value
0x02to start receiving the recently saved history data
You will be notified with a stop command 0x03, then you have to do the following clean up procedure:
- Send back the stop command by writing the value
0x03to the characteristic - Write the value
0x04 0xFF 0xFF 0xFF 0xFFto the characteristic to acknowledge that you received the recently saved history data
I thought I was finished, but if you take the scale batteries out and put them back in, you have to initialize the Mi Scale again, otherwise the date and time are wrong and the weight measurement history is not saved. To do this I had to capture and analyse the initialization process between the Mi Fit App and the Mi Scale again. After some headache I found out that you can enable the weight measurement history with the following magic bytes 0x01 0x96 0x8a 0xbd 0x62, but beforehand you have to enable the indication flag of the weight measurement history. To set the current date and time on the Mi Scale you have to send a write command to the Current Time characteristic (0x2a2b) with the following byte order:
| byte | value |
|---|---|
| 0-1 | year (little endian) |
| 2 | month |
| 3 | day |
| 4 | hour |
| 5 | minute |
| 6 | second |
| 7 | 0x03 |
| 8 | 0x00 |
| 9 | 0x00 |
Somehow you have to restart the connection to write the current date and time. I don't know why. The last thing that makes me wonder is that you can't delete the weight measurement history on the Mi Scale at all, or at least I haven't found out how yet. If you know how to delete it, please let me know.