0w0

블루투스 패킷 읽기

목적

블루투스와 GATT 통신하고나서, 패킷값을 보는 방법에 대해 적습니다.

유의할 점은 어디까지나 지금 제가 이해한 수준으로 작성한 것이므로 틀린 내용이 있을 수 있다는 점입니다.

상황

회사에서 만드는 react native 애플리케이션이 블루투스 채혈기 통신하고 그 값을 서버와 연동해야하는 기능이 있으므로, 이를 처리하기 위해서 패킷 내용보는 방법을 알아야 했습니다.

참조

GATT Specification Supplement (PDF)

Core Specification 5.3

GPT와 위 문서를 참조했습니다.

설명

1. 패킷값

먼저 패킷은 길이 최대가 17인 바이트배열입니다. 그리고 그 안에 있는 값은 10진수입니다

[11, 4, 0, 232, 7, 1, 26, 10, 29, 4, 0, 0, 254, 7, 248, 32, 0]

2. flags

바이트배열[0]은 구조체입니다.

여기서는 [11]인데, 이는 2진수로 바꾸면 0000 1011입니다.

단순하게 길이가 8인 비트 배열로 생각했습니다.

그리고 왜 구조체냐면 각 자리가 어떤 의미를 지정하고 있기 때문입니다.

예를 들면 flags[5]번째가 1인데, 이는 Context Information Flag: 1 = This record includes context information 이라 지정되어 있습니다.

3. sequence number

바이트배열[1, 2], uint16 (0 ~ 65535)입니다.

여기서는 [4, 0] 인데, 블루투스는 리틀엔디안이므로 실제로는 [0, 4], 즉 비트 연산 0 | 4 = 4입니다.

Vol 3, Part G
2.4 PROFILE FUNDAMENTALS
...
Multi-octet fields within the GATT profile shall be sent least significant octet
first (little-endian) with the exception of the Characteristic Value field. The
Characteristic Value and any fields within it shall be little-endian unless
otherwise defined in the specification which defines the characteristic.
...

실제 값 4를 보고 무슨 의미일까 이리 저리 추리하다가 혹시 little-endian인가? 싶어서 문서에서 확인했습니다.

4. base time

바이트배열[3 ~ 9], 구조체입니다.

여기서는 [232, 7, 1, 26, 10, 29, 4]입니다.

이를 다시 쓰면 [year1, year2, month, day, hour, min, sec]입니다

year

여기서 year은 uint16이고 [232, 7]입니다. 또한 실제 유효값은 1582 ~ 9999로 지정되어 있습니다.

또한 다시 한 번 말씀드리면 리틀엔디안 이므로 [year1(하위), year2(상위)] 입니다

비트연산 7 | 232 입니다만, 유의할 점은 각 자리가 8비트이고, 7이 앞자리 이므로 8자리 만큼 시프트해야합니다

그래서 0000 0111에서 16비트의 앞자리를 채우기위해서 0000 0111 0000 0000로 만들고나서, 뒷자리 1110 0000를 더합니다.

0000 0111 0000 0000
      1110 0000
-----------------------
0000 0111 1110 0000

이 값은 2024입니다.

자바스크립트로 표현하면 이하와 같습니다.

(7 << 8) | 232 // 틀린 방법

정정

(232 & 255) | ((7 & 15) * 256)

이렇게 작성해야합니다.

15는 2비트 1111 마스킹 용

우연의 일치로 바이트배열[1]에서 앞의 상위 비트 4자리가 0000이면 답이 같게 됩니다

그 외

나머지 바이트배열 [1, 26, 10, 29, 4]은 월, 일, 시, 분, 초입니다.

uint8이고, 위와 같이 값이 특정 범위가 valid되어 있습니다.

Time Offset Present

타임존이고, 바이트배열[10, 11]은 int16입니다.

flags 0000 1011 기준으로 flags[0]이 1이 아니므로 이 값에서는 0 입니다.

실제 값도 [0, 0]입니다.

Glucose Concentration

값입니다. 바이트배열[12, 13]은 [254, 7]이고, SFLOAT입니다

위의 year과 같이 7 << 8 | 254 이렇게 계산합니다.

또한 flags 0000 1011 기준으로 bit[2]가 0입니다.

If Bit 2 of Flags field set to 0,
unit =
org.bluetooth.unit.mass_density.kilogram_per_liter

즉, 단위는 kilogram_per_liter입니다.

Type-Sample Location

바이트배열[14]은 uint8입니다. [248]은 1111 1000입니다

상위 4비트, 하위 4비트를 나눠서 처리합니다.

상위 4비트 1111 0xf 하위 4비트 1000 0x8 인데.........

Present if bit 1 of Flags field is set to 1

flags 0000 1011 flags[1]이 0이므로 설정되어 있지 않는 것 같습니다.

참고로 상위 0xF와 하위 0x8은 의미있는 값이 없습니다.

Sensor Status Annunciation

바이트배열[15, 16]은 [32, 0]이며, boolean[16] 비트배열[16]이라 이해 했습니다.

Present if bit 3 of Flags field is set to 1

flags 0000 1011에서 flags[3]은 0이므로 설정되어 있지 않는 것 같습니다.

다시 한 번 말씀드리면 위의 값도 (0 << 8) | 32 === 32이고, 16비트이므로 0000 0000 0010 0000입니다.

GATT 문서에 16자리에 해당되는 boolean 내용이 있습니다.

위의 경우 표에 따르면 1 = A general device fault has occurred in the sensor device.입니다.

다만 flags[3]가 0이므로 무시합니다.

마무리

이렇게 학습했습니다만, 실제 애플리케이션에서는 정제해서 값과 날짜만 사용했습니다.

제가 제대로 활용하지 못한 것이 이유일 수도 있겠습니다.

읽어주셔서 감사합니다.