前回、bluepy モジュールを使ってラズパイ上のPython3のスクリプトでBBC micro:bit のBLEアトリビュート内の「特性(characteristic)」のハンドルをリストいたしました。ハンドルが分かれば実際の値にアクセスできます。今回は、読み出すだけの特性を中心に「読んで」みたいと思います。
※「ブロックを積みながら」投稿順 index はこちら
今回も、前回に引き続き micro:bit側は、同じオブジェクトHEXファイルを使っているのでブロックは積みません。作業は、Raspberry Pi 3 model B+上のPython3スクリプトであります。今回使用したスクリプト(とりあえず版)は末尾に全文を置いてあります。
さて、micro:bitがPeriphal、ラズパイがCentralというBLE上の役割分担です。micro:bit内のアトリビュートにアクセスするのに参照しなくてはならないのは、bluepy ドキュメンテーション内の以下のクラスの部分です。
添付のスクリプトは、
- 知りたいことをコマンドライン引数としてスクリプトに与える
- 指定のBLEアドレスへconnectできれば、その知りたいことを標準出力にダンプ
- connectできなければエラーで終わる
という単純なものです。ペアリングおよび、BLEアドレスのSCANは済との前提です。SCANが不要なので、sudo する必要はありません。とりあえずテストに使っている手元のmicro:bitのBLEデバイスアドレスをデフォルト値として内部にハードコードしてあるので以下の試行ではBLEアドレスを与えていません。引数に
--ADR xx:xx:xx:xx:xx:xx
などとBLEアドレスを与えれば他のmicro:bitでも動作するんじゃないかと思います(とりあえず版なので確かめてません、すみません。)
UUIDの一覧
まずは先週調べた、UUIDとハンドル、プロパティの一覧の取得です。以下のように -uuid オプションを与えれば一覧が出力されます。こんな感じ。
$ python3 microbitBLEutil.py -uuid microbitBLEutil v0.1 CONNECTED: F5:D1:5F:02:68:25 UUID: 00002a00-0000-1000-8000-00805f9b34fb Handle: 0x0003 Prop: READ WRITE UUID: 00002a01-0000-1000-8000-00805f9b34fb Handle: 0x0005 Prop: READ UUID: 00002a04-0000-1000-8000-00805f9b34fb Handle: 0x0007 Prop: READ UUID: 00002a05-0000-1000-8000-00805f9b34fb Handle: 0x000a Prop: INDICATE UUID: e95d93b1-251d-470a-a062-fa1922dfa9a8 Handle: 0x000e Prop: READ WRITE UUID: e97d3b10-251d-470a-a062-fa1922dfa9a8 Handle: 0x0011 Prop: WRITE NO RESPONSE NOTIFY UUID: 00002a24-0000-1000-8000-00805f9b34fb Handle: 0x0015 Prop: READ UUID: 00002a25-0000-1000-8000-00805f9b34fb Handle: 0x0017 Prop: READ UUID: 00002a26-0000-1000-8000-00805f9b34fb Handle: 0x0019 Prop: READ UUID: e95d9775-251d-470a-a062-fa1922dfa9a8 Handle: 0x001c Prop: READ NOTIFY UUID: e95d5404-251d-470a-a062-fa1922dfa9a8 Handle: 0x001f Prop: WRITE NO RESPONSE WRITE UUID: e95d23c4-251d-470a-a062-fa1922dfa9a8 Handle: 0x0021 Prop: WRITE UUID: e95db84c-251d-470a-a062-fa1922dfa9a8 Handle: 0x0023 Prop: READ NOTIFY UUID: 6e400002-b5a3-f393-e0a9-e50e24dcca9e Handle: 0x0027 Prop: INDICATE UUID: 6e400003-b5a3-f393-e0a9-e50e24dcca9e Handle: 0x002a Prop: WRITE NO RESPONSE WRITE DISCONNECTED.
デバイス固有の情報など
ここで調べたHandleの値が変わらぬものとして、デバイス名など「読み取れば意味がとれる特性」を読み出すのには -info オプションが使えます。末尾のコードを見ればわかりますが、先週調べたハンドルが変わらぬ前提でハードコードしてあるので、装置によっては異なる可能性も無きにしもあらず。BLEの規定により、少なくとも手元のmicro:bitとラズパイの「ペア」に関してはこの番号で変化しない筈。
デバイス名、シリアル番号、ファームウエアのリビジョンなどリストされていますが、それらしい感じなので多分良さそう(いい加減な。)
$ python3 microbitBLEutil.py -info microbitBLEutil v0.1 CONNECTED: F5:D1:5F:02:68:25 DeviceName = BBC micro:bit ModelNumber = BBC micro:bit SerialNumber = 6239613436 FirmwareRevision = 2.1.1--g DISCONNECTED.
サービスの列挙
前回も書きましたが、GATTサーバーのアトリビュートは複数のサービスを含み、サービスは複数の特性を含み、特性にはデスクリプタというものを含めることができるという階層構造をなしています。このmicro:bit(MakeCodeエディタで明示的に置いたのは UARTサービスのみ)においてサービスを列挙するには -service オプションを使います。サービス毎にハンドルがまとめられていることが分かります。こんな感じ。
$ python3 microbitBLEutil.py -service microbitBLEutil v0.1 CONNECTED: F5:D1:5F:02:68:25 Service <uuid=Generic Access handleStart=1 handleEnd=7> Service <uuid=Generic Attribute handleStart=8 handleEnd=11> Service <uuid=e95d93b0-251d-470a-a062-fa1922dfa9a8 handleStart=12 handleEnd=14> Service <uuid=e97dd91d-251d-470a-a062-fa1922dfa9a8 handleStart=15 handleEnd=18> Service <uuid=Device Information handleStart=19 handleEnd=25> Service <uuid=e95d93af-251d-470a-a062-fa1922dfa9a8 handleStart=26 handleEnd=36> Service <uuid=6e400001-b5a3-f393-e0a9-e50e24dcca9e handleStart=37 handleEnd=65535> DISCONNECTED.
デスクリプタの列挙
特性(characteristic)は先ほどの -uuid で列挙できるので、その下の階層、デスクリプタレベルで列挙するのは、-desc オプションです。デスクリプタは数が多いのでリストを途中で打ち切っています。全部列挙しても使い道に困るので、特性かサービス毎に取得する方が良さそうです。
$ python3 microbitBLEutil.py -desc microbitBLEutil v0.1 CONNECTED: F5:D1:5F:02:68:25 Descriptor <Primary Service Declaration> Descriptor <Characteristic Declaration> Descriptor <Device Name> Descriptor <Characteristic Declaration> Descriptor <Appearance> Descriptor <Characteristic Declaration> Descriptor <Peripheral Preferred Connection Parameters> Descriptor <Primary Service Declaration> Descriptor <Characteristic Declaration> Descriptor <Service Changed> Descriptor <Client Characteristic Configuration> Descriptor <Primary Service Declaration> Descriptor <Characteristic Declaration> Descriptor <e95d93b1-251d-470a-a062-fa1922dfa9a8> Descriptor <Primary Service Declaration> Descriptor <Characteristic Declaration> Descriptor <e97d3b10-251d-470a-a062-fa1922dfa9a8> Descriptor <Client Characteristic Configuration> Descriptor <Primary Service Declaration> Descriptor <Characteristic Declaration> Descriptor <Model Number String> ~以下略~
まあこれで、一方通行のデータ読み出しはできることが確認できたので、次は双方向でのBLEデータ通信ですかね。そろそろ「ブロックを積まないと」羊頭狗肉です。
ブロックを積みながら(18) BLE、ハンドル、UUID、CHARACTERISTICS へ戻る
ブロックを積みながら(20) micro:bit、BLE UARTへPythonから書き出し へ進む
micro:bit のBLE「特性」など読み出しユーティリティ(初版)
#! /usr/bin/python3 import argparse import bluepy import sys import time versionSTR = "microbitBLEutil v0.1" class microbitBLE: def __init__(self, adr): self.devadr = adr self.pobj = None def conPobj(self): try: self.pobj = bluepy.btle.Peripheral() self.pobj.connect(self.devadr, bluepy.btle.ADDR_TYPE_RANDOM) except: print("ERROR: connect.") return False return True def listCharacteristics(self): chrLis = self.pobj.getCharacteristics() for item in chrLis: print(" UUID: {0} Handle: 0x{1:04x} Prop: {2}".format(item.uuid, item.getHandle(), item.propertiesToString())) def getDeviceInfo(self): work = dict() work['DeviceName'] = str(self.pobj.readCharacteristic(0x0003), 'utf-8') work['ModelNumber'] = str(self.pobj.readCharacteristic(0x0015), 'utf-8') work['SerialNumber'] = str(self.pobj.readCharacteristic(0x0017), 'utf-8') work['FirmwareRevision'] = str(self.pobj.readCharacteristic(0x0019), 'utf-8') return work def getServiceInfo(self): return self.pobj.getServices() def getDescInfo(self): return self.pobj.getDescriptors() def close(self): self.pobj.disconnect() def main(): parser = argparse.ArgumentParser(description='microbitBLEutil.') parser.add_argument('--ADR', nargs=1, help='BLE address.') parser.add_argument('-info', dest='info', help='device Info.', action='store_true', default=False) parser.add_argument('-service', dest='service', help='available service list.', action='store_true', default=False) parser.add_argument('-desc', dest='descriptor', help='available descriptor list.', action='store_true', default=False) parser.add_argument('-uuid', dest='uuid', help='list all UUIDs.', action='store_true', default=False) parser.add_argument('-V', dest='VERSION', help='Show Version, then exit', action='store_true', default=False) args = parser.parse_args() print(versionSTR) if args.VERSION: sys.exit(0) devadr = "F5:D1:5F:02:68:25" # Default BLE address if args.ADR is not None: devadr = args.ADR[0] mb = microbitBLE(devadr) if mb.conPobj(): print("CONNECTED: ", devadr) else: sys.exit(1) if args.uuid: mb.listCharacteristics() if args.info: for key, value in mb.getDeviceInfo().items(): print(key, "=", value) if args.service: for srv in mb.getServiceInfo(): print(str(srv)) if args.descriptor: for desc in mb.getDescInfo(): print(str(desc)) mb.close() print("DISCONNECTED.") if __name__ == "__main__": main()