Christian
Christian

Reputation: 43

Get weight data from bluetooth le scale by disassemble manucaftures library

a friend has a body scale with Bluetooth Le. To save his weight measurements he has to open the Manufactures App on his phone. So we try to use his raspberry pi for the Job. We find out, that the scale sends the data over advertisments. The scale is never connectable, and you can use different phones at the same time to receive the data. During the measurement the app(s) shows the same values as the display of the scale does. You know, if you stand on the scale the value will go up and down until the measurement is finished. This 'going up and down' is also visible on the phone, or phones if you use more than one at the same time. So i think it is a fact, that the data is transmitted over BLe advertisments. They can only be in the manufacturer data, what for example looks like this:

starts always with:
0f ff ac a0 db 58 e2 53 91 a0

and then:

88,30kg
20 c9 f8 42 0d b0
20 c9 f8 78 0d a6

88,35kg
20 c9 f9 b4 0d a3
20 c9 f9 88 0d b7

88,40kg
20 c9 f9 e6 0d b5
20 c9 f9 e6 0d b5

2,15kg
a0 c8 ab 40 0d a0

0,00kg
a0 c8 a0 a0 0d b5

But no matter how i have tried, i can't calculate this data to the given weight. I tried it in big endian, little endian, other untis like pound, ounces or even stone. I can not find a formula to calculate the data to every known weight. Interesting is, that zero is "a0 c8 a0 a0 0d b5". The first byte is 20 during the measurement, after finishing it, the scale sends many advertisment-packages with a0 in the first byte. I think this will say "final value". The fifth byte is always 0d, no matter what. The second byte changes from c8 to c9 on higher weights. I think at 50 or 60 kg. Because i can't find the solution for this i tried something else. I decompiled the android app an look for the part, where the advertisment-package is parsed. Unfortunately the app loads a native library to do this. Looks like the Manufacture really wanna keep it`s secret. I tried to use their library for my one purpose. But it looks like i need a Header-File to use the library in my own Java-Application. So my last chance is to disassemble the Library, what i have done. I used objdump. I know from the Java-Code, the function that i need is

private native List<Map<String, Object>> decode(byte[] bArr, int i, int i2, Object obj);

I can find this function/subroutine in the dump file.. but i can not understand how to work with it. I watched a video tutorial series for assembler, what was really interesting, but it doesn't really help me with this. I assumed you can somehow see where the data comes in (parameters for decode), how they be processed and returned (saved to memory for example). I also found a "BleBroadcastScaleProtocol16decodeWeightData"-Function but have still the same problem. My questions is now: Is there a real chance, that this can be done this way? The dump file has 10MB of text. Do someone know how to do this, or have done something like this before? I'm working for months on this and ever step forward brings me two steps back.

I put the dump file on my Cloud drive, maybe someone will have a look.

Dump File

Edit: I use my Raspberry Pi to send advertisements to the Manufactures App. I use the example-advertisement script from bluez. If i send adverts with the Service-ID FFB0 and "aca0" on the first two bytes of the manufacturer data the app recognize the pi as an "Insmart" scale. I'm also able to send weight data to the app. If i send "(ac a0 db 58 e2 53 91 a0) a0 c8 a0 a0 0d b5" the app show 0. I played around and find out the last four bits are a checksum, calculated by adding all hex-values together and take the last four bits of the summary. The weight data must be somewhere in the 2. to 4. bit (c8 - second a0). I wrote a script to take 16 bits from an offset and print out the hex value. As input data i use c8a0a0 (=0). With an offset of 1 (take 16 bits, starting from bit 2) it prints out "0x9141". In a second script i can input a hex value and it will merge it with the default value of "c8a0a0". So i can enter 9142 and put it together to data i can send as manufacuter data. 9142 is c8a120, what does nothing but 9143 is c8a1a0. This value is 0,25kg. When i increase the first value (9143) by 2 it will add 0,25kg in the app. Sometimes it adds 0,3kg. This works to 917f / c8bfa0 / 7,95kg. If i go to 9181 / c8c0a0 it jumps to 24,55kg. The i can continue in 0,25kg steps to 9195 / c8caa0 / 27,15kg. On 9197 / c8cba0 it changes unit and shows 4:4.4st:lb. If i count up by 1 the app crashs most of the time with the other values (9144, 9146, 9148...) Does anyone recognize a pattern in it? I'm also starting to try different offsets, but it doesn't look very promising.

Edit2: 4:4.4st:lb is equal to 27.397kg. Next step is 4:5.0st:lb what is 27,67kg. So it is linear at this point. But the range 8 - 24kg is missing

Here are the scripts that i use

Upvotes: 2

Views: 3878

Answers (2)

Christian
Christian

Reputation: 43

We finally figured out how to get the weight data. As i suggested the weight data is in the second to fourth byte, like this: c9 f8 42. But to get the right data you have to do something weird: You have to replace the third and fifth 'character' of the hex string like this:

0 1 2 3 4 5 6 7 8 9 a b c d e f Normal
a b 8 9 e f c d 2 3 0 1 6 7 4 5 Icomon

For example, c8a0a0 (=0,00kg) will become 0xC80000. Convert this to an integer and substract 0xC80000, divide by 1000 and there you have it.In this example its just 0, but i works with every values we tried.

I really don't know if this have anything to do with Big/little Endian or MSB/LSB shenanigans or if it is just a very simple 'encrypten' but it totally works.

Thanks anyone for helping us, we got some new ideas here what led us to the answer

Here is our python-file to decode the data:

#!/usr/bin/env python3
import sys
# 0 1 2 3 4 5 6 7 8 9 a b c d e f normal people
# a b 8 9 e f c d 2 3 0 1 6 7 4 5 Icomon
def main():
  hex_code = sys.argv[1]
  ic = ['a','b','8','9','e','f','c','d','2','3','0','1','6','7','4','5']

  i=0
  out=''
  for byte in hex_code:
    if i in [2, 4]:
      index = int(f'0x{byte}', 16)
      out = out+ic[index]
    else: out=out+byte  
    i=i+1
  value = round((int(f'0x{out}', 16)-0xc80000)/1000,2)
  value=round05(value)
  print(value)

def round05(number):
  return (round(number * 20) / 20)

main()

Edit: Well, now i know the 'shenanigans' are just XOR.

Upvotes: 2

d_air
d_air

Reputation: 641

Update: Basing on the data that you provided, I think that you already found a pattern that can lead to finding the weight value. It looks like the weight value can be extracted from the 2nd to the 4th byte with C8A0A0 as the starting value(0kg).

To get the weight value, the 2nd-4th byte values can be substracted by C8A0A0. Then divide the answer by 1000.

The examples below are based on your data. The one inside the parenthesis is your observed kg value.

(0.25kg) C8A1A0 - C8A0A0 = 100HEX => 256DEC => 0.256kg

(7.95kg) C8BFA0 - C8A0A0 = 1F00HEX => 7936DEC => 7.936kg

(88.3kg) C9F842 - C8A0A0 = 157A2HEX => 87970DEC => 87.97kg C9F878 - C8A0A0 = 158D8HEX => 88280DEC => 88.28kg

(88.35kg) C9F9B4 - C8A0A0 = 15914HEX => 88340DEC => 88.34kg C9F988 - C8A0A0 = 158E8HEX => 88296DEC => 88.296kg

(88.40kg) C9F9E6 - C8A0A0 = 15946HEX => 88390DEC => 88.39kg

(2.15kg) C8AB40 - C8A0A0 = AA0HEX => 2720DEC => 2.72kg

Please try more data like simulating to send C9B210 and see if you can get a weight value that is closer to 70kg.

Previous answer: Perhaps the weight data is not in the advertisement. It is not clear to me in your description that you attempted to connect to the bluetooth le scale in a standard way. You can do it using the nRF or the Lightblue app. This is the general procedure:

  1. Open the app. Let it detect the bluetooth scale.
  2. Connect to the device. Make sure that the scale is not connected to other apps(such as it's own app).
  3. You will see the UUIDs for the services and characterics.
  4. Find the uuids that has a 'notify' and 'indicate' characteristics and subscribe to all that has that characteristics.
  5. Look for the logs of those characteristics while the value in the scale is changing. It is most probably where you see the weight data.

Depending on the functionality of your scale, it may be required that you'll be doing all these steps while the scale is active and have not yet settled on the final value so the bluetooth communication is ongoing. You can do this by constantly stepping in/out to the scale while operating the app.

Upvotes: 0

Related Questions