Environment

pybluez install and basic communications practice

 

랩 구성과 하드웨어 설정 : Intel NUC (CSR4.0 dongle) <-> acer notebook

구성목표는 두 기기에서 간단한 동글을 이용한 블루투스 통신이 파이썬으로 가능한지 눈으로 확인하고, 리눅스툴의 사용에 익숙해지고, 자료가 부족하기때문에 시행착오 성공예 등을 기록해두는것이다. 추후에는 덧붙여 scapy 나 wireshark 등의 패킷분석 및 역공학 연구를 위한 환경을 만드는것을 목표로한다. IoT 기기에는 streaming-based 인 RFCOMM 이 아니라 packet-based 인 L2CAP 가 주로 사용될 것이기때문에 이를 지원하는 리눅스 기반에서 테스트하였다. 

 

미니pc에 다이소 CSR4.0 블루투스 동글을 꽃고 노트북의 내장 블루투스 5.1 로 구성하였다.

각각에 pybluez 와 dependencies 를 설치한다.

 

기본설치후 example 이 제대로 동작하지않아 웹서치를 한 결과,

각 우분투의 ubuntu 의 bluetoothd 를 --compat (-C) 으로 실행하고 권한설정을 같이 해야 잘 동작하며,

device 를 클라이언트만으로 사용하는게 아닌, 기기 탐색과 접속을 가능하게하려면

Page(connect), Inquery(discovery) SCAN 기능을 hciconfig 에서 활성화 해야한다.

pybluez, pybluez[BLE] 설치

sudo apt install -y bluez bluetooth libbluetooth-dev python3-pip \
&& python3 -m pip install pybluez
# BLE (experimental)
sudo apt install -y pkg-config libboost-python-dev \
libboost-thread-dev libglib2.0-dev python3-dev \
&& python3 -m pip install pybluez\[ble\]

bluetooth.service 변경

$ sudo nano /lib/systemd/system/bluetooth.service
...
ExecStart=/usr/lib/bluetooth/bluetoothd -C
...

sudo sdptool add SP
systemctl daemon-reload
service bluetooth restart

SDP: Service Discovery Protocol

echo -e '\nsudo chmod 777 /var/run/sdp' >> ~/.bashrc
sudo reboot

블루투스 컨트롤러

아래 명령을 통해 블루투스 장치명과 MAC 주소를 확인합니다.
hcitool dev
Devices:
    hci0    00:1A:7D:DA:71:13

아래 명령을 통해 블루투스 장치인 hci0의 스캔을 허용할 수 있습니다.
# Enable Page and Inquiry scan
# 이후 hciconfig 시 UP RUNNING 옆에 PSCAN ISCAN 표시가 생깁니다.
$ sudo hciconfig hci0 piscan

bluetoothctl 명령어를 통해 블루투스 장치를 제어할 수 있습니다.
$ bluetoothctl
[NEW] Controller 18:1D:EA:36:DA:89 hhk7734 [default]
Agent registered
[bluetooth]# help

이후 다음과같이 iPhone 에서 미니pc 의 블루투스 컨트롤러가 탐색되어 나타난 것을 확인 할 수 있었다.

NUC 의 CSR4.0 이 탐색된 모습

코드 실습

두기기간의 통신을 확인하기 위해 코드를 실행해보았다. python 없이 간단히 하길원하면 l2ping 을 사용해보자.

pybluez 홈페이지의 L2CAP 예제를 조금 수정하였다.

기능은 간단하게 서버는 접속을 기다리며 클라이언트는 접속후 데이터를 보내면 서버가 echo 를 하는 방식이다.

 

아래는 서버쪽(NUC)의 코드이고 hciconfig 에서 PSCAN ISCAN 을 확인후 실행하면 반응없이 접속을 기다리는 상태가된다.

$ python l2cap-server.py

import bluetooth

server_sock = bluetooth.BluetoothSocket(bluetooth.L2CAP)

port = 0x1001

server_sock.bind(("", port))
server_sock.listen(1)

#uuid = "94f39d29-7d6d-437d-973b-fba39e49d4ef"
#bluetooth.advertise_service(server_sock, "SampleServerL2CAP",
#                            service_id=uuid, service_classes = [uuid])

client_sock, address = server_sock.accept()
print("Accepted connection from", address)

data = client_sock.recv(1024).decode('utf-8')
print("Data received: ", data)

while data:
    client_sock.send("Echo To " + data)
    data = client_sock.recv(1024).decode('utf-8')
    print("Data received: ", data)
    
client_sock.close()
server_sock.close()

아래는 클라이언트(노트북) 쪽의 코드이고 hcitool scan 을 통해 미니pc의 블루트스가 탐색됨을 한번 더 확인 한 후, 

확인한 주소로 코드를 실행하였다.

$ python3 l2cap-client.py 00:1A:7D:DA:71:03

import sys

import bluetooth


sock = bluetooth.BluetoothSocket(bluetooth.L2CAP)

if len(sys.argv) < 2:
    print("Usage: l2capclient.py <addr>")
    sys.exit(2)

bt_addr = sys.argv[1]
port = 0x1001

print("Trying to connect to {} on PSM 0x{:02x}...".format(bt_addr, port))

sock.connect((bt_addr, port))

print("Connected. Type something...")
while True:
    data = input()
    if not data:
        break
    sock.send(data)

    data = sock.recv(1024).decode('utf-8')
    print("Data received:", data)

sock.close()

접속이 되면 입력이 가능한 상태가 되고, Hello IoT World! 를 입력해보았다.

서버인 미니pc에서 내가 입력한 문자가 출력되고 노트북에서 Echo 응답을 확인 할 수 있었다!

왼쪽(NUC), 오른쪽(NOTEBOOK) 에서 pybluez 로 블루트스 통신을 성공한 모습

 

TROUBLE SHOOTING

< BLE 설치시 
/usr/bin/ld: cannot find -lboost_python-py34 >
# gattlib 가 python 3.4 버전의 dynamic library 를 찾으면서 오류. 링크를 만든다.

cd /usr/lib/x86_64-linux-gnu/
ls -al libboost_*
sudo ln -s libboost_python38.so libboost_python-py34.so

< bluetooth.btcommon.BluetoothError: no advertisable device >
위 블루투스 컨트롤러 확인.
hciconfig 후 PSCAN ISCAN 가 있는지 확인한다.
$ sudo hciconfig hci0 piscan

< bluetooth.btcommon.BluetoothError: [Errno 2] No such file or directory >
위 bluetooth.service 변경 부분 확인.
블루투스 데몬을 compatibility mode 로 실행해야한다.

< bluetooth.btcommon.BluetoothError: [Errno 13] Permission denied >
위 SDP 참조.
bluetoothd 를 --compat (-C) 로 실행할때 생성되는 파일인 /var/run/sdp 의 퍼미션을 조정해야한다.

reference )
https://stackoverflow.com/questions/36675931/bluetooth-btcommon-bluetootherror-2-no-such-file-or-directory

 

Linux Tools

hciconfig, hcitool, sdptool, hcidump, l2ping, uuidgen
BLE 전용툴은 다른글로 여기선 제외하였음.

 

hciconfig

HCI 는 하나의 장치에서 host (CPU) 와 controller (adapter, dongle) 사이의 통신 HCI Protocol 을 설정하는것이다.

서로다른 두 장치의 무선 연결을 하는 protocol 이 아니다.

 

Inquiry Scan and Page Scan

쉽게말하면 각각은 블루투스 어댑터가 

Inquiry Scan (ISCAN): 근거리의 다른 블루투스 장치들에 의해 탐지가 가능한지

Page Scan (PSCAN): 다른장치의 incoming connect 요청을 받아들일지

를 세팅하는 것이다.

 

# hciconfig hci0 name 'myIoTDevice'

# hciconfig hci0 [up / down / reset]

# hciconfig hci0 [piscan / pscan / iscan / noscan]

# man hciconfig

 

세팅을 reboot 후에 유지되도록 저장하려면 /etc/bluetooth/ .conf 를 변경. 포맷은 manpage 에서 볼수있다.

 

 

hcitool

1. 근처의 장치를 search 하고 detect 한다.

2. low-level 블루투스 연결을 테스트하고 정보를 보여준다.

 

1.

# hcitool inq ( inquire scan으로 BD_ADDR, clock offset, class 출력 )

# hcitool scan ( Inquiry scan으로  장치들의 이름 출력 )

# hcitool lescan ( BLE )

 

예제) miband3 BLE 정보 확인해보기
# hcitool leinfo --random D0:8A:48:4C:CB:41

link9@link9-Swift5:~/installer$ sudo hcitool leinfo --random D0:8A:48:4C:CB:41
Requesting information ... Handle: 75 (0x004b) LMP Version: 4.1 (0x7) LMP Subversion: 0xa
Manufacturer: Dialog Semiconductor B.V. (210)
Features: 0x1d 0x00 0x00 0x00 0x00 0x00 0x00 0x00
# hcidump HCI 호스트가보낸 커맨드 확인

link9@link9-Swift5:~$ sudo hcidump -i hci1 | grep "HCI Command:"
< HCI Command: LE Create Connection (0x08|0x000d) plen 25
< HCI Command: LE Read Remote Used Features (0x08|0x0016) plen 2
< HCI Command: Read Remote Version Information (0x01|0x001d) plen 2
< HCI Command: LE Read Remote Used Features (0x08|0x0016) plen 2
< HCI Command: Disconnect (0x01|0x0006) plen 3

 

2.

# hcitool cc [BD_ADDR]

# hcitool con

 

주의) 이 툴에서의 연결은 low-level piconet 에서의 연결을 의미하고

프로그램작성시 처럼 application level의 data 전송에 대한 연결을 의미하는것이 아니다.

programming 시 운영체제에 의해 자동으로 이루어지는부분.

 

# hcitool info [BD_ADDR]

장치이름, 주소, LMP version (LInk Management Protocol 에 대응하는 블루투스 버전)

RSSI, sniff mode, BR/EDR, LE Support, encryption, simple pairing, encapsulated PDU

등 여러 세부설정을 확인 할 수 있다.

hciconfig, hcitool info

 

sdptool

1. Searching, browsing SDP services by nearby devices

( 휴대폰이나 헤드셋 등이 어떤 서비스를 제공하는지?

classic 은 SDP server 의 service record 에 목록을 저장하고 BLE 에서는 GATT Server.)

2. Local Machine 의 SDP  service 기본 설정

 

1.

# sdptool browse [BD_ADDR/local/None]

# sdptool search [preset string/UUID]

    # sdptool search SP (Serial Port)

    # sdptool search OPUSH (OBEX Object Push)

    # sdptool search 0x1101

 

Preset string 목록은 sdptool -h.

 

2.

# sdptool add <predefined name>

# sdptool del <handle>

 

주변의 iPhone 6s 에 대한 sdp service

hcidump

sudo apt-get install -y bluez-hcidump

low-level debugging 을 위해 모든 블루투스 패킷을 보여줄 수 있다.

어떻게 connection 이 일어나고 또는 어느단계에서 실패하는지 탐구하는데 중요한 툴이다.

root 권한 필요.

 

# hcidump <no argument> ( local 컴퓨터와 adapter 사이의 패킷 )

# hcidump -X

예제) 내 컴퓨터가 adapter 에게 주변의 접속가능한 장치가 있는지 스캔하는 명령을 내리는 low-level 패킷을 출력해보자.
local-term1 $ hcidump
local-term2 $ hcitool scan

두 터미널을 열고 각각 입력하면 1번 터미널에서

HCI sniffer - Bluetooth packet analyzer ver 5.53
device: hci0 snap_len: 1500 filter: 0xffffffffffffffff
> HCI Event: Command Status (0x0f) plen 4
    Inquiry (0x01|0x0001) status 0x00 ncmd 2
> HCI Event: Extended Inquiry Result (0x2f) plen 255
    bdaddr 00:1A:7D:DA:71:03 mode 1 clkoffset 0x3b01 class 0x0c0104 rssi -70
    Complete local name: 'link9-desktop'
    TX power level: 20
    Unknown type 0x10 with 8 bytes data
    Complete service classes: 0x1800 0x1801 0x110e 0x110c 0x110b 0x110a 0x1112 0x1108
> HCI Event: Inquiry Complete (0x01) plen 1
    status 0x00
> HCI Event: Command Status (0x0f) plen 4
    Remote Name Request (0x01|0x0019) status 0x00 ncmd 1
> HCI Event: Remote Name Req Complete (0x07) plen 255
    status 0x00 bdaddr 00:1A:7D:DA:71:03 name 'link9-desktop'

진한 부분까지 출력된 상태로 대기하다가,
2번 터미널에서

Scanning ...
00:1A:7D:DA:71:03 link9-desktop

device 이름출력과 함께 종료되며, 1번 터미널의 연한부분의 나머지 부분이 출력되었다.

일반적으로 controller 는 커맨드를 받으면 Command Status 를 바로 보내는데, task 를 시작했음을 알려서 비 동기적으로 host 가 다른일을 할 수 있도록 하는 메커니즘이다.
hcitool scan 은 2번의 커맨드를 어댑터에 내리는것으로 보이며 두번의 Command Status 와 중간에 Result, 마지막에 complete 이벤트를 받는다.
 
hcitool scan 은
2번째 커맨드의 응답으로 Remote Name Req Complete 이벤트를 받아
포함된 bdaddr 과 name 들을 모두 출력하고 종료하는것으로 보인다.

원하는 프로토콜만 필터링을 할 수 있다. man hcidump 를 참고하자.

 

l2ping

each packet ( L2CAP packet ) 을 주고받음. L2CAP 통신을 테스트하기에 유용하다.

 

# l2ping -c 5 [BD_ADDR]

 

uuidgen

# uuidgen

 

ref )

Essential bluetooth programming for develper book

https://wiki.loliot.net/docs/lang/python/libraries/python-pybluez/

https://stackoverflow.com/questions/36675931/bluetooth-btcommon-bluetootherror-2-no-such-file-or-directory

+ Recent posts