ESP32 BLEのデモにRaspberry Piからアクセスしてみる
ESP32でBLEのデモを動かす ESP32でBLEのデモを動かす 補足 で作ったESP32のBLE peripheral に Raspberry Pi からアクセスしてみる方法についてのメモ。
Androidでアクセスすると、色々とブラックボックスで処理されてどうなってるのか分かり難いので理解を深める意味で試してみた。
[!NOTE] 使用したESP32側のソースはこちら
ここにあるように、BLEでは「ボンディング(参照先ではペアリングと表記)なし」で実行するのが無難と思われる
【サルでもわかるBLE入門】(8) ペアリング
Wiresharkによるパケットキャプチャの解説(たぶん、そんなレイヤでデバッグすることはないと思うけど)
BLEのペアリングをWiresharkでキャプチャしながら学ぶ
ESP側の設定は以下の通り。
まずは接続可能デバイスをスキャンしないと始まらないので、スキャンする。
もちろん、ESP32側はAdvertising 開始状態である必要がある。
$ sudo timeout 5s hcitool lescan ← 5秒間スキャンしてみる
LE Scan ...
48:BA:7E:24:D0:AA (unknown)
50:17:FC:8C:D1:87 ESP_BLE_HR ←見つかった
E4:9A:9F:40:AD:09 (unknown)
C4:49:BB:8A:7F:6C EX-ZR1800-8A7F6B
C4:49:BB:8A:7F:6C (unknown)
hcitool lescan
の実行にはsudo
必須。
timeout 5s
を付けずにCTRL+Cで停止しても良い。
[!NOTE] UUIDについて
16bit UUIDは以下のXXXXの部分(それ以外の部分が一致しないものは128bit UUID)
0000XXXX-0000-1000-8000-00805f9b34fb
16bit UUIDは 以下のページを参照
16-bit UUID Numbers Document
gatttool
の実行にsudo
は不要。
«アドレス»
には上でみつけたアドレスを指定する。
$ gatttool -t random -I -b «アドレス» ← ツールの実行 以下、インタラクティブモードに入る
[50:17:FC:8C:D1:87][LE]> connect ← 接続
Attempting to connect to 50:17:FC:8C:D1:87
Connection successful ← 接続成功
[50:17:FC:8C:D1:87][LE]>
サービスの一覧を取得してみる。
[50:17:FC:8C:D1:87][LE]> primary
attr handle: 0x0001, end grp handle: 0x0005 uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle: 0x0014, end grp handle: 0x001c uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle: 0x0028, end grp handle: 0xffff uuid: 0000180d-0000-1000-8000-00805f9b34fb
[50:17:FC:8C:D1:87][LE]>
それぞれこんな意味
UUID | 種別 | ハンドル範囲 |
---|---|---|
1801 | Generic Attribute | 0x0001 ~ 0x0005 |
1800 | Generic Acces | 0x0014 ~ 0x001c |
180d | Heart Rate | 0x0028 ~ 0xffff |
上で調べたGeneric Acces のハンドル範囲を指定して実行。
表示されるUUIDを
16-bit UUID Numbers Document
で探して右側にメモっておいた。
[50:17:FC:8C:D1:87][LE]> char-desc 0x14 0x1c
handle: 0x0014, uuid: 00002800-0000-1000-8000-00805f9b34fb ← Primary Service
handle: 0x0015, uuid: 00002803-0000-1000-8000-00805f9b34fb ← Characteristic
handle: 0x0016, uuid: 00002a00-0000-1000-8000-00805f9b34fb ← device name
handle: 0x0017, uuid: 00002803-0000-1000-8000-00805f9b34fb ← Characteristic
handle: 0x0018, uuid: 00002a01-0000-1000-8000-00805f9b34fb ← Appearance
handle: 0x0019, uuid: 00002803-0000-1000-8000-00805f9b34fb ← Characteristic
handle: 0x001a, uuid: 00002aa6-0000-1000-8000-00805f9b34fb ← Central Address Resolution
[50:17:FC:8C:D1:87][LE]>
とりあえず devece nameを読んでみる。
[50:17:FC:8C:D1:87][LE]> char-read-hnd 0x016 ← handle 0x16(device name) のリード
Characteristic value/descriptor: 45 53 50 5f 42 4c 45 5f 48 52 ← 結果
[50:17:FC:8C:D1:87][LE]>
このままだとなんだかわからん…
別ウィンドゥで以下を実行。
ただし、nkf
はデフォルトでインストールされていないのでapt
でインストールする。
echo
の中身には上でリードした結果をコピペする。
これをxxd
コマンドでバイナリに変換、
nkf
で文字コード変換を行う(これだとUTF-8→UTF-8なのであんまり意味ない気が…)。
結果は改行されずに、プロンプトが続けて表示されるので注意。
$ echo "45 53 50 5f 42 4c 45 5f 48 52" | xxd -p -r | nkf -WwmQ
ESP_BLE_HR ← おぉ、読めてる
$
同様にHeart Rateサービスについて調べてみる。
UUIDについてのメモも同様。
[50:17:FC:8C:D1:87][LE]> char-desc 0x0028 0xffff
handle: 0x0028, uuid: 00002800-0000-1000-8000-00805f9b34fb ← Primary Service
handle: 0x0029, uuid: 00002803-0000-1000-8000-00805f9b34fb ← Characteristic
handle: 0x002a, uuid: 00002a37-0000-1000-8000-00805f9b34fb ← Heart Rate Measurement
handle: 0x002b, uuid: 00002902-0000-1000-8000-00805f9b34fb ← Client Characteristic Configuration
handle: 0x002c, uuid: 00002803-0000-1000-8000-00805f9b34fb ← Characteristic
handle: 0x002d, uuid: 00002a38-0000-1000-8000-00805f9b34fb ← Body Sensor Location
handle: 0x002e, uuid: 00002803-0000-1000-8000-00805f9b34fb ← Characteristic
handle: 0x002f, uuid: 00002a39-0000-1000-8000-00805f9b34fb ← Heart Rate Control Point
[50:17:FC:8C:D1:87][LE]>
Characteristicの読み書きを試すため、Heart Rate Control Point(ハンドル0x2f;char-desc
の結果から取得)にアクセスしてみる。
[50:17:FC:8C:D1:87][LE]> char-read-hnd 0x2f ← 読んでみる
Characteristic value/descriptor: 00 ← 読めた
[50:17:FC:8C:D1:87][LE]> char-write-req 0x2f 01 ← 書いてみる
Characteristic value was written successfully ← 書けた
[50:17:FC:8C:D1:87][LE]> char-read-hnd 0x2f ← 確認してみる
Characteristic value/descriptor: 01 ← 書けてる
[50:17:FC:8C:D1:87][LE]> char-write-req 0x2f 0x02 ← 書き込み値に0xを付けたらエラーになる
Error: Characteristic Write Request failed: Attribute value length is invalid
[50:17:FC:8C:D1:87][LE]>
NotificationをONにするにはCCCを操作する。
Notificationで受信したデータは自動で表示される。
[50:17:FC:8C:D1:87][LE]> char-read-hnd 0x2b ← CCCを読んでみる
Characteristic value/descriptor: 00 00 ← Notification OFFになってる
[50:17:FC:8C:D1:87][LE]> char-write-req 0x2b 0100 ← Notification ON にしてみる(0100を書き込む)
Characteristic value was written successfully ← 書き込めた
Notification handle = 0x002a value: 50 10 ← Notificationを受信
Notification handle = 0x002a value: 51 10 ← Notificationを受信
Notification handle = 0x002a value: 52 10 ← Notificationを受信
[50:17:FC:8C:D1:87][LE]> char-read-hnd 0x2b ← CCCを読んでみる
Characteristic value/descriptor: 01 00 ← Notification ONになってる
Notification handle = 0x002a value: 53 10 ← Notificationを受信
Notification handle = 0x002a value: 54 10 ← Notificationを受信
Notification handle = 0x002a value: 55 10 ← Notificationを受信
Notification handle = 0x002a value: 56 10 ← Notificationを受信
Notification handle = 0x002a value: 57 10 ← Notificationを受信
[50:17:FC:8C:D1:87][LE]> char-write-req 0x2b 0000 ← Notification OFF にしてみる(0000を書き込む)
Characteristic value was written successfully ← 書き込めた
[50:17:FC:8C:D1:87][LE]> ← 以降、Notificationは停止
書き込み禁止のCharacteristicに書き込んでみるとどうなるか試してみる。
当然エラーになる。
50:17:FC:8C:D1:87][LE]> char-write-req 0x2d 01 ← Body Sensor Locationに書き込んでみる
Error: Characteristic Write Request failed: Attribute can't be written ← エラーになった
操作が終わったら切断する。
[50:17:FC:8C:D1:87][LE]> disconnect ← 切断
(gatttool:1840): GLib-WARNING **: 12:55:27.500: Invalid file descriptor. ← なんか言われるけど無視して良い
[50:17:FC:8C:D1:87][LE]> exit ← インタラクティブモードの終了
$ ← Shellに戻る
「ボンディングする」設定の場合、bluetoothctlでアクセスする必要がある。
ボンディングしない設定で使う分には関係ないが、せっかく調べたのでメモっておく。
ボンディング前後でアドレス違ったりしてイマイチ使いにくい。
どうしても「ボンディングする」にしなければならない場合は、自身のアドレスをパブリックアドレスにしておけばちょっとマシかも。
bluetoothctlのコマンド
bluetoothctl のコマンド一覧と使い方をまとめてみた
bluetoothctlだとGATTへのアクセスができないっぽいので、ボンディングの登録/解除以外は使えないっぽいな…
ESP側の設定は以下の通り。
ツールの起動。
sudo
必要
以下インタラクティブモードで操作。
$ sudo bluetoothctl ← 起動
接続可能デバイスを見つけるためにスキャンする。
[bluetooth]# scan on ← スキャン開始
Discovery started
[CHG] Controller DC:A6:32:70:E7:B2 Discovering: yes
[NEW] Device 70:51:DB:1C:EF:BC 70-51-DB-1C-EF-BC
[NEW] Device 7D:85:86:11:F3:D9 7D-85-86-11-F3-D9
[NEW] Device 62:D1:88:DD:4F:7C リビングルーム
[CHG] Device 94:B9:7E:65:AF:5E RSSI: -38
[CHG] Device 94:B9:7E:65:AF:5E TxPower: 3
・・・
[bluetooth]# scan off ← スキャン停止
Discovery stopped
[CHG] Controller DC:A6:32:70:E7:B2 Discovering: no
[CHG] Device 94:B9:7E:65:AF:5E TxPower is nil
・・・
[bluetooth]# devices ← 接続可能デバイスの表示
Device 5A:E1:9A:05:96:E0 ESP_BLE_HR ← これがESP32(ランダムアドレスなので起動の度に変化する)
Device 70:51:DB:1C:EF:BC 70-51-DB-1C-EF-BC
Device 7D:85:86:11:F3:D9 7D-85-86-11-F3-D9
Device 62:D1:88:DD:4F:7C リビングルーム
ボンディングのために、agentを登録する(数字を入力したり、Yes/Noを選択したりするやつ)。
こちらの設定と相手側の設定の組み合わせで最終的にどの方法が使われるか決定される。
たとえば、こちら側を NoInputNoOutput に設定しておけば、相手側が DisplayYesNo であってもYes/Noの入力は求められない。
逆の設定でも同様。
登録された状態で他のCapabilityで登録しようとしても「既に登録済み」と言われるので、念のため一旦登録解除してから登録しておく。
このあたり、情報少なくてイマイチよく分からない…
[ESP_BLE_HR]# agent off ← 一旦agentの登録解除
Agent unregistered ← 解除された
[ESP_BLE_HR]# agent DisplayYesNo ← DisplayYesNoで登録
Agent registered ← 登録された
接続する。connect
でなく、pair
で実行。
connect
による接続は後述。
[ESP_BLE_HR]# pair 5A:E1:9A:05:96:E0 ← 接続
Attempting to pair with 5A:E1:9A:05:96:E0
[CHG] Device 5A:E1:9A:05:96:E0 Connected: yes
Request confirmation
[agent] Confirm passkey 680456 (yes/no): ← agentがYes/Noを聞いてくる
[NEW] Primary Service ← これが自動的に表示されて上の質問を見失うので注意!!
/org/bluez/hci0/dev_5A_E1_9A_05_96_E0/service0001
00001801-0000-1000-8000-00805f9b34fb
Generic Attribute Profile
[NEW] Characteristic
/org/bluez/hci0/dev_5A_E1_9A_05_96_E0/service0001/char0002
00002a05-0000-1000-8000-00805f9b34fb
Service Changed
・・・
[NEW] Characteristic
/org/bluez/hci0/dev_5A_E1_9A_05_96_E0/service0028/char002e
00002a39-0000-1000-8000-00805f9b34fb
Heart Rate Control Point
[CHG] Device 5A:E1:9A:05:96:E0 UUIDs: 00001800-0000-1000-8000-00805f9b34fb
[CHG] Device 5A:E1:9A:05:96:E0 UUIDs: 00001801-0000-1000-8000-00805f9b34fb
[CHG] Device 5A:E1:9A:05:96:E0 UUIDs: 0000180d-0000-1000-8000-00805f9b34fb
[CHG] Device 5A:E1:9A:05:96:E0 ServicesResolved: yes
yes ← yesを入力(同時に相手側でもyes入力)
[CHG] Device 94:B9:7E:65:AF:5E Address: 94:B9:7E:65:AF:5E
Pairing successful ← 接続された
[ESP_BLE_HR]# paired-devices ← ボンディング結果を確認
Device 94:B9:7E:65:AF:5E ESP_BLE_HR ← advertisingがランダムアドレスでもパブリックアドレスで登録されるので注意!!
[ESP_BLE_HR]# 以降、接続はこのアドレスを使用する!!
切断する。
Characteristicアクセスできないので、実質ボンディングするのみ。
[ESP_BLE_HR]# disconnect ← 切断する
Attempting to disconnect from 94:B9:7E:65:AF:5E ← パブリックアドレスが表示されている
[CHG] Device 94:B9:7E:65:AF:5E ServicesResolved: no
Successful disconnected ← 切断された
[CHG] Device 94:B9:7E:65:AF:5E Connected: no
[bluetooth]#
ボンディング済みのデバイスに再接続する場合の手順。
[bluetooth]# paired-devices ← ボンディング済みデバイスの表示
Device 94:B9:7E:65:AF:5E ESP_BLE_HR
[bluetooth]# connect 94:B9:7E:65:AF:5E ← 表示されたアドレスで接続
Attempting to connect to 94:B9:7E:65:AF:5E
[CHG] Device 94:B9:7E:65:AF:5E Connected: yes
Connection successful ← 接続された
[CHG] Device 94:B9:7E:65:AF:5E ServicesResolved: yes
[ESP_BLE_HR]# disconnect ← 切断
Attempting to disconnect from 94:B9:7E:65:AF:5E
[CHG] Device 94:B9:7E:65:AF:5E ServicesResolved: no
Successful disconnected ← 切断された
[CHG] Device 94:B9:7E:65:AF:5E Connected: no
[bluetooth]#
ボンディングしてない機器に対してconnect
すると、agent off
で pair
したような動作になる模様。
ボンディング済みデバイスを登録削除する。
ESP32側も忘れず登録削除しておくこと。
[bluetooth]# paired-devices ← ボンディング済みデバイスを確認
Device 94:B9:7E:65:AF:5E ESP_BLE_HR
[bluetooth]# remove 94:B9:7E:65:AF:5E ← ボンディング解除
[DEL] Descriptor ← なんか ずらずらっと削除されたと出る
/org/bluez/hci0/dev_5F_76_EF_D2_45_E7/service0001/char0002/desc0004
00002902-0000-1000-8000-00805f9b34fb
Client Characteristic Configuration
・・・
Device has been removed ← 削除された
[bluetooth]# paired-devices ← 確認
[bluetooth]# ← 削除されてる
ツールを終了してShellに戻る。
[bluetooth]# exit ← またはquitまたはCTRL+Dで終了
$ ← Shellに戻る