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 의 블루투스 컨트롤러가 탐색되어 나타난 것을 확인 할 수 있었다.
코드 실습
두기기간의 통신을 확인하기 위해 코드를 실행해보았다. 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 응답을 확인 할 수 있었다!
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
등 여러 세부설정을 확인 할 수 있다.
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>
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/