Raspberry PiでBLE

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を調べてみる

上で調べた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]>

device name を読んでみる

とりあえず 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サービスを調べてみる

同様に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]>

Heart Rate Control Point を読み書きしてみる

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を有効にしてみる

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に書き込んでみる

書き込み禁止の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 のコマンド一覧と使い方をまとめてみた

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の登録

ボンディングのために、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 offpair したような動作になる模様。

ボンディング解除

ボンディング済みデバイスを登録削除する。
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に戻る