Есть идеи, как декодировать эти маленькие конечные байты в python?

#python #hex #bluetooth-lowenergy #unpack

Вопрос:

Я собираю рекламные данные с устройства BLE. В частности, меня интересуют эти два байта:

 b'x17dx0ex10x0exd7x02x1dx00Gx00Ux01x00'
b'x02xadx02x8dx00x9bx00x0ex01xf6x01xadx00xcfx00Vx01-x01 x00x00x00x00x00'
 

В руководстве по эксплуатации устройства, которое доступно здесь. Они дают структуру для рекламных данных. Я попытался следовать этому и использовать struct.unpack , как показано ниже:

 import struct

bte = b'x17dx0ex10x0exd7x02x1dx00Gx00Ux01x00'
struct.unpack('<BBHHHH', bte)
 

Однако я получаю эту ошибку «для распаковки требуется буфер размером 10 байт». Я думаю, что это как-то связано с первым байтом x17d , так как struct.unpack всегда возвращает эту ошибку, когда у вас есть байт с более чем двумя символами. Также байты x00G и x00U , поскольку я не уверен, что означают U и G. Также этот байт имеет длину 11, в то время как байт формата <BBHHHH должен иметь длину всего 10.

Любая помощь была бы очень признательна.

Комментарии:

1. BBHHHH ожидает 10 байт (1 1 2 2 2 2), но b'x17dx0ex10x0exd7x02x1dx00Gx00Ux01x00' , похоже, 14 байт. Ваша строка формата отключена.

2. кстати, первый байт есть 0x17 , а второй байт 0x64 иначе известен в мире ascii как: d .

3. Да, я уже попадался на это раньше. Обратите внимание, что только две цифры после x являются частью этого литерала. x00G в качестве другого примера можно привести нулевой байт ( 00 ), и "G" ; таким образом, всего два байта.

4. Вы имели в виду: struct.unpack('<BBHHHH', bte[:10]) ?

Ответ №1:

На устройствах Blue Maestro я не думаю, что они помещают данные в формате little endian в данные производителя рекламы.

Глядя на ваши данные, я бы ожидал, что это будет что-то вроде следующего:

 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)
 

Дающий результат:

 {'batt_lvl': 100,
 'dew_point': 7.1,
 'humidity': 54.1,
 'interval': 3600,
 'log_count': 3799,
 'temperature': 8.5,
 'version': 23}
 

Вы можете распаковать вещи за один раз, но вам придется отрегулировать значения влажности точки росы и температуры:

 unpack('>BBHHhhh', pckt[:12])
# (23, 100, 3600, 3799, 541, 71, 85)
 

При выборе отдельных значений может быть более понятным использование int.from_bytes функциональности.

 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)
 

Что дает те же значения:

 {'batt_lvl': 100,
 'dew_point': 7.1,
 'humidity': 54.1,
 'interval': 3600,
 'log_count': 3799,
 'temperature': 8.5,
 'version': 23}
 

x17d , x00G , и x00U — это все два байта. Однако вторые байты являются значениями ASCII, поэтому Python при отображении услужливо отображает букву, а не значение байта.
Чтобы доказать это, мы можем ввести значения байтов и увидеть значение ASCII на выходе:

 >>> b'x64x47x55'
b'dGU'
 

Есть несколько вещей, которые вы можете сделать, чтобы показать фактическое значение байта, чтобы помочь в отладке.

Используйте hexlify:

 >>> binascii.hexlify(bte)
b'17640e100ed7021d004700550100'
 

Преобразуйте байты в список денарных значений:

 >>> list(bte)
[23, 100, 14, 16, 14, 215, 2, 29, 0, 71, 0, 85, 1, 0]
 

Отформатируйте его в виде строки с шестнадцатеричными значениями:

 >>> [f'{n:02X}' for n in bte]
['17', '64', '0E', '10', '0E', 'D7', '02', '1D', '00', '47', '00', '55', '01', '00']
 

Комментарии:

1. Большое вам спасибо, что отлично сработали для меня. Не знал об этом int.from_bytes методе. Также , как указывали другие x17d , x00G , и x00U -это все два байта, как вы сказали, что сбивало меня с толку. Я провел некоторую отладку с помощью приложения на моем телефоне, которое показывает температуру, и я думаю, что у вас могут быть неверно помечены некоторые значения, но в остальном это работает отлично!