I am picking up advertising data from a BLE device. In particular I am interested in these two bytes:
b'\x17d\x0e\x10\x0e\xd7\x02\x1d\x00G\x00U\x01\x00'
b'\x02\xad\x02\x8d\x00\x9b\x00\x0e\x01\xf6\x01\xad\x00\xcf\x00V\x01-\x01+\x00\x00\x00\x00\x00'
In the manual for the device, which is available here. They give a structure for the advertising data. I tried to follow that and use struct.unpack
as shown below:
import struct
bte = b'\x17d\x0e\x10\x0e\xd7\x02\x1d\x00G\x00U\x01\x00'
struct.unpack('<BBHHHH', bte)
However, I get this error "unpack requires a buffer of 10 bytes". I think this has something to do with the first byte \x17d\
as struct.unpack
always returns this error when you have a byte with more than two characters. Also the bytes \x00G\
and \x00U\
as I am not sure what the U and G mean. Also this byte is 11 long while a byte of the format <BBHHHH
should be only 10 long.
Any help would be greatly appreciated.
On the Blue Maestro devices I don't think they put the data in little endian format in the advertisement manufacturer data.
Looking at your data I would expect it to be something like the following:
import binascii
from pprint import pprint
from struct import unpack
pckt = binascii.unhexlify('17640e100ed7021d004700550100')
data = {}
data["version"] = unpack(">B", pckt[0:1])[0]
data["batt_lvl"] = unpack(">B", pckt[1:2])[0]
data["interval"] = unpack(">H", pckt[2:4])[0]
data["log_count"] = unpack(">H", pckt[4:6])[0]
data["humidity"] = unpack(">h", pckt[6:8])[0] / 10
data["dew_point"] = unpack(">h", pckt[8:10])[0] / 10
data["temperature"] = unpack(">h", pckt[10:12])[0] / 10
pprint(data)
Giving an output of:
{'batt_lvl': 100,
'dew_point': 7.1,
'humidity': 54.1,
'interval': 3600,
'log_count': 3799,
'temperature': 8.5,
'version': 23}
You can do the unpack in one go but you will have to adjust the values for dew point humidity, and temperature:
unpack('>BBHHhhh', pckt[:12])
# (23, 100, 3600, 3799, 541, 71, 85)
When picking out individual values it can be clearer to use the int.from_bytes
functionality.
import binascii
from pprint import pprint
pckt = binascii.unhexlify('17640e100ed7021d004700550100')
data = {}
data["version"] = int.from_bytes(pckt[0:1], byteorder='big')
data["batt_lvl"] = int.from_bytes(pckt[1:2], byteorder='big')
data["interval"] = int.from_bytes(pckt[2:4], byteorder='big')
data["log_count"] = int.from_bytes(pckt[4:6], byteorder='big')
data["humidity"] = int.from_bytes(pckt[6:8], byteorder='big', signed=True) / 10
data["dew_point"] = int.from_bytes(pckt[8:10], byteorder='big', signed=True) / 10
data["temperature"] = int.from_bytes(pckt[10:12], byteorder='big', signed=True) / 10
pprint(data)
Which gives the same values:
{'batt_lvl': 100,
'dew_point': 7.1,
'humidity': 54.1,
'interval': 3600,
'log_count': 3799,
'temperature': 8.5,
'version': 23}
The \x17d
, \x00G
, and \x00U
are all two bytes. However the second bytes happen to be ASCII values so Python, when displaying, helpfully displays the letter rather than the byte value.
To prove prove this we can input byte values and see ASCII value in the output:
>>> b'\x64\x47\x55'
b'dGU'
There are a few things you can do to show the actual byte value to help with debugging.
Use hexlify:
>>> binascii.hexlify(bte)
b'17640e100ed7021d004700550100'
Convert the bytes to a list of denary values:
>>> list(bte)
[23, 100, 14, 16, 14, 215, 2, 29, 0, 71, 0, 85, 1, 0]
Format it as a string with the hex values:
>>> [f'{n:02X}' for n in bte]
['17', '64', '0E', '10', '0E', 'D7', '02', '1D', '00', '47', '00', '55', '01', '00']