ラズベリーパイPico WでBluetoothを使ってみるでラズベリーパイPico Wでブルートゥースの子機(ペリフェラル)を構築し、
AndroidアプリのSerial Bluetooth Terminalでブルートゥースの動作を確認してみました。
今回はラズベリーパイ等のパソコンでPythonで似たような動作をするコードを作成してみます。
※2024年1月31日時点でクロームブックのクロスティーニではブルートゥースの機能は利用できません
コードの話題に入る前にブルートゥースでのデータ転送の主軸となるGATT(Generic attribute profile)について触れておきます。
親機と子機がブルートゥースで接続された時、お互いの機器はGATTプロファイルに基づくプロファイルで情報のやり取りを行います。
GATTの構造は
のようにProfie、Service(サービス)とCharacteristic(キャラクタリスティック)で構成されている。
ラズベリーパイPico Wを子機とした時を例とすると、サービスがラズベリーパイPico Wに該当し、キャラクタリスティックがラズベリーパイPico Wに組み込んだ機能と捉えると以後のコードの理解が進みます。
キャラクタリスティックにはATTと呼ばれる属性値が含まれていて、
・ハンドル(handle)
・型(type)
・値(value)
・権限(permission)
があります。
ここらへんの話は一旦用語の把握までにしておいて、実際のコードに触れてから改めて調べる事をおすすめします。
Pythonでコードを書く際にbluepyというライブラリを用いる事にします。
GitHub - IanHarvey/bluepy: Python interface to Bluetooth LE on Linux
最初に端末(ターミナル)を開き、下記のコマンドでbluepyを使用できるようにします。
$ sudo apt install python-pip libglib2.0-dev $ sudo pip install bluepy
今回はプロジェクト名をbleにして話を進めます。
下記のコマンドでブルートゥースの親機(セントラル)用のコードを作成する為のディレクトリを作成します。
$ cd ~ $ mkdir workspace $ cd workspace $ mkdir ble $ cd ble
最初に子機に割り当てられたMACアドレスを調べるコードを作成します。
~/workspace/ble/scan.py
from bluepy import btle scanner = btle.Scanner(0) devices = scanner.scan(3.0) for dev in devices: print("[MAC-Adress]:", dev.addr) print("[Address-Type]:", dev.addrType)
上記のコードを実行してみると、
$ python3 ~/workspace/ble/scan.py
[MAC-Adress]: XX:XX:XX:XX:XX:XX [Address-Type]: public
のように出力されます。
どちらの値も重要ですので、記録しておきます。
続いて、子機のUUIDやハンドル(handle)について調べます。
~/workspace/ble/handle.py
from bluepy import btle import time PERIPHERAL_MAC_ADDRESS = "XX:XX:XX:XX:XX:XX" p = btle.Peripheral() # scan.pyで調べたMACアドレスとアドレスタイプを指定します p.connect(PERIPHERAL_MAC_ADDRESS, btle.ADDR_TYPE_PUBLIC) print("connected") chars = p.getCharacteristics(); for char in chars: print("UUID : %s" % char.uuid ) print("Handle %04x: %s" % (char.getHandle(), char.propertiesToString())) p.disconnect() print("disconnected")
上記のコードを実行してみると、
$ python3 ~/workspace/ble/handle.py
UUID : 00002a00-0000-1000-8000-00805f9b34fb Handle 0003: READ UUID : 00002a05-0000-1000-8000-00805f9b34fb Handle 0006: READ UUID : 6e400003-b5a3-f393-e0a9-e50e24dcca9e Handle 0009: READ NOTIFY UUID : 6e400002-b5a3-f393-e0a9-e50e24dcca9e Handle 000c: WRITE NO RESPONSE WRITE
のような値が返ってきます。
今回は子機から送信された値を取得する予定ですので、ATTの権限でREADが含まれている方のUUIDやハンドラ(0003)を記録しておきます。
※以後で作成するコードではUUID等の値は使用しません
得られた値を基にして、Serial Bluetooth Terminalのように動作する為のコードを作成してみます。
~/workspace/ble/main.py
from bluepy import btle PERIPHERAL_MAC_ADDRESS = "XX:XX:XX:XX:XX:XX" class MyDelegate(btle.DefaultDelegate): def __init__(self): btle.DefaultDelegate.__init__(self) def handleNotification(self, cHandle, data): # 子機から得られたデータを端末に出力する print(data) p = btle.Peripheral() p.connect(PERIPHERAL_MAC_ADDRESS, btle.ADDR_TYPE_PUBLIC) print("connected") p.withDelegate(MyDelegate()) # 通信が切れたりSIGINTの割り込みが発生しない限り接続を維持する try: TIMEOUT = 3.0 while True: if p.waitForNotifications(TIMEOUT): continue except: # SIGINTを許可 p.disconnect() print("disconnected")
上記のコードを実行してみると、
$ python3 ~/workspace/ble/main.py
端末にSerial Bluetooth Terminalで出力されたような値が出力され続けます。
出力を止めたい場合はctrl + cで接続を切ることが出来ます。