TL;DR
- 電動スマートベッド nerum appを買った
非常に良い。前編 レビュー編は こちらへ - BLE通信をスニッファでキャプチャしてプロトコル解析した。
- スマホアプリでも出来ない、照明の状態と角度の表示、HEAD/FOOT同時操作までできるようになった。イエイ
- サンプルコードは全部 C#
環境
- Windows 11 Pro 21H2
- Bluetoothドングル サンワサプライ MM-BTUD46
- Bluetoothスニッファ Adafruit nRF51822搭載 Bluefruit LE Sniffer(v2) amazonで購入した
- Wireshark v3.6.5
- Python 3.10
- VisualStudio 2022 17.2.3
- スマホアプリ Google Play、App Store
BLE
まずは、スニッファを使わず、PCから普通のBLEのデバイスとして様子を確認する。
アドバタイズ
サンプルコード C#
-
ポイント
- ターゲットフレームワーク
csprojで、次のように指定する
<TargetFramework>net6.0-windows10.0.22000.0</TargetFramework>
- BluetoothLEAdvertisementWatcherクラスを使ってアドバタイズを受信する
- 目的のServiceUuidが分かっていれば、あらかじめフィルタもかけられる
- ターゲットフレームワーク
-
パッシブのアドバタイズ
AdvertisementType:ConnectableUndirected
Address:30E283XXXXXX Type:Public
LocalName:
Flags:GeneralDiscoverableMode, ClassicNotSupported
ManufacturerData
CompanyId:0x0000(Ericsson Technology Licensing)
Data:FF-FF-FF-FF-64-00-FF
ServiceUuids
0000fff0-0000-1000-8000-00805f9b34fb
0000ffb0-0000-1000-8000-00805f9b34fb
DataSections
01(Flags):06
02(Incomplete16bServices):F0-FF-B0-FF
FF(Manufacturer):00-00-FF-FF-FF-FF-64-00-F
- アクティブスキャンのレスポンス
AdvertisementType:ScanResponse
Address:30E283XXXXXX Type:Public
LocalName:OKIN-BLE0008XXXX
Flags:
ManufacturerData
ServiceUuids
DataSections
09(CompleteLocalName):4F-4B-49-4E-2D-42-4C-45-30-30-30-38-XX-XX-XX-XX
12(Unknown):08-00-10-00
0A(TxPower):04
サービスとキャラクタリスティックの列挙
サンプルコード C#
- サンプルコード
- ポイント
- BluetoothLEDeviceクラスのFromBluetoothAddressAsyncメソッドを使ってインスタンスを取得
-
BluetoothLEDevice.GetGattServicesAsyncを使って GATTサービスを列挙する
-
GattDeviceService.GetCharacteristicsAsyncを使って GATTキャラクタリスティックを列挙する
- GattCharacteristic.GetDescriptorsAsyncを使ってGATTディスクリプタを列挙
- GattCharacteristic.ReadValueAsyncを使ってGATTキャラクタリスティックの値を取得
-
GattDeviceService.GetCharacteristicsAsyncを使って GATTキャラクタリスティックを列挙する
サービスとキャラクタリスティックの一覧
サンプルコードで取得できた nerum appの一覧は以下の通り
サービスUUID | キャラクタリスティックUUID | Properties | 値 | Descriptor | DescriptorValue |
---|---|---|---|---|---|
0x1800 (GenericAccess) |
0x2A00 (DeviceName) |
Read | "OKIN-BLE0008XXXX" | - | - |
0x2A01 (Appearance) |
Read | 00-00 | - | - | |
0x2A02 (PeripheralPrivacyFlag) |
Read, Write | 00 | - | - | |
0x2A03 (ReconnectionAddress) |
Write | - | - | - | |
0x2A04 (PeripheralPreferred ConnectionParameters) |
Read | 50-00-A0-00-00-00-E8-03 | - | - | |
0x1801 (GenericAttribute) |
0x2A05 (ServiceChanged) |
Indicate | - | ※2 | 02-00 |
0x180A (DeviceInformation) |
0x2A23 (SystemId) |
Read | D4-46-83-00-00-83-E2-30 | - | - |
0x2A24 (ModelNumberString) |
Read | "GCBT40-1S" | - | - | |
0x2A28 (SoftwareRevisionString) |
Read | "V1.2.5" | - | - | |
0x2A29 (ManufacturerNameString) |
Read | "BLE-4.0 Module" | - | - | |
0xFFE0 | 0xFFE4 | Notify | - | ※2 | 00-00 |
Notify | - | ※1 | UART Channel | ||
0xFFE5 | 0xFFE9 | Write | - | ※1 | BLE Channel |
0xFF20 | 0xFF21 | Write | - | ※1 | Link Interval |
0xFF22 | Write | - | ※1 | Modify Name | |
0xFF23 | Write | - | ※1 | Modify Uart Rate | |
0xFF24 | Write | - | ※1 | Modify RF Power | |
0xFF25 | Write | - | ※1 | Sleep Enable | |
0xFF26 | Write | - | ※1 | Modify Advert Time | |
0xFF2F | Notify | - | ※2 | 00-00 | |
Notify | - | ※1 | Config Notify | ||
0xFF30 | 0xFF31 | Read, Write | "00000" | ※1 | PWM0 |
0xFF32 | Read, Write | "00000" | ※1 | PWM1 | |
0xFF33 | Read, Write | "00000" | ※1 | PWM2 | |
0xFF34 | Read, Write | "00000" | ※1 | PWM3 | |
0xFF35 | Write | - | ※1 | PHASE | |
0xFF36 | Read, Write | "65534" | ※1 | FREQUECY | |
0xFF37 | WriteWithoutResponse | - | ※1 | PWM | |
0xFF3F | Notify | - | ※2 | 00-00 | |
Notify | - | ※1 | PWM Status | ||
0xFF40 | 0xFF41 | Read | 01-62 | ※1 | ADC0 |
0xFF42 | Read | 01-62 | ※1 | ADC1 | |
0xFF50 | 0xFF51 | Write | - | ※1 | GIPO Setting |
0xFF52 | Write | - | ※1 | GPIO Mode Setting | |
0xFF53 | Read | "111111" | ※1 | Read GPIO Status | |
0xFF54 | Read, Write | FF-FF-FF-FF-FF-FF-FF-FF-FF- FF-FF-FF-FF-FF-FF-FF-FF-FF |
※1 | Data Backup | |
0xFF5F | Notify | - | ※2 | 00-00 | |
Notify | - | ※1 | GPIO Notify |
Descriptor
※1 0x2901(CharacteristicUserDescription)
※2 0x2902(ClientCharacteristicConfiguration)
GCBT40というモジュールを使っているらしい
マニュアルっぽいのを見つけて、DeepLで翻訳してみたが、
一部サービスUUIDが異なるなど、リビジョンが違う模様。
BLEスニッファ
GATTキャラクタリスティックだけではどうやって操作するかわからないので、
実際のスマホアプリとベッドの通信を覗き見て、理解を進める。
セットアップ
公式のセットアップだけ見ておけばOK
- 公式のセットアップ
Using with Sniffer V2 and Python3 | Introducing the Adafruit Bluefruit LE Sniffer | Adafruit Learning System - 公式のFAQ
FAQs | Introducing the Adafruit Bluefruit LE Sniffer | Adafruit Learning System - そのほか参考にしたサイト
Bluefruit LE Snifferを使用したBLEプロトコル解析 | TomoSoft
Big SurでAdafruit Bluefruit LE Snifferを使う手順
キャプチャ
- WireSharkを起動し、
nRF Sniffer for Bluetooth LE
を選択する
- アドバタイズが表示される
- 目的のデバイスを選択する
- スマホアプリからBLEデバイスに接続を開始し、 Master/Slaveの組み合わせが出るのを待機する
出てこない場合、スマホアプリで切断⇒接続を繰り返すこと
(アドバタイジングは3つの周波数で行われるので、2/3の確率で別のチャンネルを見ているタイミングで接続を開始したことによるもの。)
- ここまで来たら後は 各GATTとどういうやり取りをしているか、
アプリを動作させつつ観察していくだけです。
フィルタにbtatt
を指定するとGATT通信のみに絞れます
他には、btatt.opcode == 0x12
で送った値だけに絞れたり
btatt.opcode == 0x1b
で通知された値だけに絞れたり
nerum appのプロトコル解析
通知
スマホアプリでは、以下のキャラクタリスティックからのみ、通知を受けている
サービスUUID:0xFFE0
、キャラクタリスティックUUID:0xFFE4
(Description:UART Channel
)
ServiceUuid 0000ffe0-0000-1000-8000-00805f9b34fb
CharacteristicsUuid 0000ffe4-0000-1000-8000-00805f9b34fb
-
操作側を表示中、次のようなNotificationが1秒毎に送信されている
いろいろと操作・観察した結果以下のような構成であることが分かった
- ペイロードは19または20バイト区切りで2つの通知に分割されている
(大抵は合わせて23バイト たまに分割の境界が1バイト分消えることがあった よくわからん)
長さ 意味 値の例 説明 3 マジックワード F4FE16 2 HEAD MOTORの開度 0000 0x0000~0x3338の間
最大値多少の変動あり2 FOOT MOTORの開度 0000 0x0000~0x242Eの間
最大値多少の変動あり12 不明(固定) 0000000010000000000000 1 照明 40 ON:0x40
OFF:0x003 不明(固定) FF0001 1 チェックサム A7 全バイトの合計の下位1バイトをビット反転した値
例:合計 10進数856
=0x0358
0x58(0b0101100)
のビット反転が0xA7(0b10100111)
- ペイロードは19または20バイト区切りで2つの通知に分割されている
-
タイマー設定側を表示中、次のようなNotificationが1秒毎に送信されている
いろいろと操作・観察した結果以下のような構成であることが分かった長さ 意味 値の例 説明 3 マジックワード ED8003 1 アラーム時間(HH) 0F 0x00~0x18の24h 1 アラーム分(MM) 32 0x00~0x3bの60m 1 繰返(曜日) 82 最上位ビットから順に
ON/OFF,土曜日,金曜日,木曜日,水曜日,火曜日,月曜日,日曜日
ONかつ土曜日のみ場合 0b11000000⇒0xC01 モード 12 なし = 0x00
M1 = 0x11
M2 = 0x13
灯り = 0x128 不明(固定) 0000000000000000 1 チェックサム BA (操作側と同じ)
操作
スマホアプリでは、以下のキャラクタリスティックにのみ、書き込みを行っている
サービスUUID:0xFFE5
、キャラクタリスティックUUID:0xFFE9
(Description:BLE Channel
)
ServiceUuid 0000ffe5-0000-1000-8000-00805f9b34fb
CharacteristicsUuid 0000ffe9-0000-1000-8000-00805f9b34fb
ベッドの操作
いろいろと操作・観察した結果以下のような構成であることが分かった
長さ | 意味 | 値の例 | 説明 |
---|---|---|---|
3 | マジックワード | E6FE16 | |
5 | コマンド | 01000000 | 詳細は次の表へ |
1 | チェックサム | 04 | (通知と同じ) |
操作 | マジック | コマンド部 | チェックサム | メモ |
---|---|---|---|---|
HEAD-UP | E6FE16 | 0100000000 | 04 | |
HEAD-DOWN | E6FE16 | 0200000000 | 03 | |
FOOT-UP | E6FE16 | 0400000000 | 01 | |
FOOT-DOWN | E6FE16 | 0800000000 | FD | |
HEAD+FOOT-UP | E6FE16 | 0500000000 | 00 | (おそらく、1バイト目の下位4ビットの組み合わせ) |
HEAD+FOOT-DOWN | E6FE16 | 0A00000000 | FB | 同上 |
メモリ(FLAT) | E6FE16 | 0000000800 | FD | |
メモリ(ANTISNORE) | E6FE16 | 0080000000 | 85 | |
メモリ(不明) | E6FE16 | 0040000000 | C5 | 試しにやってみたら、特定の姿勢になった |
メモリ(READING) | E6FE16 | 0020000000 | E5 | |
メモリ(ZG) | E6FE16 | 0010000000 | F5 | |
メモリ(M1) | E6FE16 | 0000010000 | 04 | |
照明ON|OFF | E6FE16 | 0000020000 | 03 | |
メモリ(M2) | E6FE16 | 0000040000 | 01 | |
停止コマンド? | E6FE16 | 0000000000 | 05 | FLAT等メモリの状態へ姿勢変化中に送信すると、姿勢変化が停止する |
状態通知開始コマンド? | E6FE16 | 0300000000 | 02 | タイマー画面から操作画面へ切り替えた際に、このコマンド送信している。 このコマンド後、F4FE16のNotificationが飛ぶようになる模様(ほかのE6FE16でも同様) 1バイト目の0x03の意味は分かっていない |
タイマー設定
-
タイマー画面に切り替えたタイミング
以下のコマンドを送信している
E28003009A
このコマンド後、ED8003のNotificationが飛ぶようになる -
ON/OFF操作時に以下2個のコマンドを送信している
1.タイマー設定側を表示中のNotificationと同じコマンド
2.時間で変化するコマンド(タイマーの起点となるシステム時刻設定をしている?)長さ 意味 値の例 説明 3 マジックワード E78001 1 年 7A 1900年を0とする値
例:7a=122=2022年1 月 05 1月を0とする値
例:05=6月1 日 07 1日は1とする値
例:07=7日1 時 00 例:0時 1 分 0D 例:13分 1 秒 38 例:56秒 1 チェックサム CC (通知と同じ)
試作アプリの実装
解析したコマンド類の読み書きで実際に操作できるかアプリを試作した。
- サンプルコード
- 試作アプリ
今後
- 操作プロトコルをもっと理解したい(何か隠された機能とかないかな とか
- GooglePlayストアのアプリを逆コンパイルすればいろいろ分かりそう
- スケジュールのダイナミックな操作、オートメーション化
- パラマウントベッド ActiveSleepBedのような入眠補助
- 土日と平日で別スケジュールの実現
- BLEのスタック周りWindowsから脱出したい、RasPi(Linux上)で動かしたい