PowerUp3.0のBLEをHackして自作Appから操作してみた

# What is PowerUp3.0 ?
紙飛行機に装着して，ケータイから操作可能なラジコン化するキット．
プロペラの回転数と垂直尾翼の角度をコントロールして紙飛行機を自在に操縦できるようになる．

↓ 実物はこんなもの，プロペラのついたちっさいUnitを実物の紙飛行機に装着します．
![2017-04-21_010546.JPG](https://qiita-image-store.s3.amazonaws.com/0/129035/25ad8065-0708-1163-f3a3-593422fc8da4.jpeg)

↓ 公式はこちら
https://www.poweruptoys.com/products/powerup-v3

# 何をしたか?
Android/iOSから操作するためのAppは公式から公開されているが，
ケータイとPowerUp3.0とはBluetoothを使って通信する，とのことだったので，
通信ProtocolをHackすれば自作Appからも操作可能になるはず．
Bluetooth/BLEの勉強も兼ねて，PowerUp3.0を自作Appから操作するまでをまとめてみた．

# Step1 : 通信Protocolの特定
Bluetooth通信，と一言でいわれても，ケータイとPowerUp3.0間でどういう通信をしているか，が全く不明なので，解析のとっかかりをつかむためにも，公式App動作中のBluetooth通信をPacket Dumpしてみる．

Androidの開発者設定にHCI Snoop Log有効化のOptionがあるらしく，これを使えば簡単にPacket Logが取れるらしい．

↓ こちらのページが分かりやすかったのでこれを参考に公式AppでPowerUp3.0を操作中のLogを取ってみる．
http://fromnorth.blogspot.jp/2015/02/bluetooth-hci.html

```
AndroidのADBを有効化 + HCI Snoop Log有効化
↓
PowerUp3.0 公式Appを起動して操作
↓
HCI Snoop Log無効化
(この時点でHCI Logファイルが書き出される)
↓
$adb shell pull /sdcard/btsnoop_hci.log .
でLogファイルを回収(Productによってファイルパスが変わるらしい)
```

取ったLogをWiresharkで見ると，↓ のようなPacketが大量に取れている．
プロペラ回転数のみを変更していたため，このPacketはプロペラ回転数制御Packetと思われる．
![ble_log.png](https://qiita-image-store.s3.amazonaws.com/0/129035/3f6efd6a-55f8-7f98-dacd-40aca9cf7042.png)

**Bluetooth HCI ACL Packet**
HCI (Host Controller Interface)のPacket．
Frameの種類が4種類(HCI Command/Event, ACL Data, SCO Data)あり，そのうちの1つ(ACL)らしい．
非同期通信用のL2CAP Packet用に使われるとのこと．
http://www.wdic.org/w/WDIC/HCI%20(Bluetooth)

**Bluetooth L2CAP Protocol**
ACLの説明どおり，L2CAP Protocolが使われている．
CID(Channel ID) = 0x0004 → Attribute Protocol
はBluetooth 4.0(= BLE)以降サポートされているものの模様．
http://www.wdic.org/w/WDIC/L2CAP

**Bluetooth Attribute Protocol**
Opcode: Write Command
によって，
Service UUID = 86c3810ef17140d9a11726b300768cd6
Characteristic UUID = 86c3810e001040d9a11726b300768cd6
に対して，
Value=0a
の書込をしている模様．

# Step2 : Android BT/BLE APIでPowerUp3.0のBLE情報をDumpする
使われているProtocolがBLEのAttribute Protocolなことがわかったので，AndroidのPublic APIでPowerUp3.0デバイスのBLEを情報をDumpしてみる．

**Bluetooth GATT(Generic Attributes)**
https://www.bluetooth.com/ja-jp/specifications/generic-attributes-overview

公式仕様によると，BLEデバイスは↓のような構造で情報を持っている．

```
GATT                         // BLEデバイスは1つのGATT(GATT Server)をもつ
    - Service                // GATTは復数(1つ以上)のServiceをもつ
        - Characteristic     // 1つのServiceは復数(1つ以上)のCharacteristicをもつ
            - Value          // 1つのCharacteristicは1つのValueをもつ
            - Descriptor     // 1つのCharacteristicは復数(1つ以上)のDescriptorをもつ
            - Descriptor
        - Characteristic
            - Value
            - Descriptor
    - Service
        - Service            // 1つのServiceが復数(1つ以上)のServiceを含むことがある
    - Service
```

これをAndroidのBluetooth APIを使ってすべてDumpすればいい．

**Android Developer - Bluetooth Low Energy**
https://developer.android.com/guide/topics/connectivity/bluetooth-le.html
↓のBluetoothGattからService取得→Characteristic/DescriptionのReadリクエスト，でOK.
https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html

GATTがもつ全Service/CharacteristicをDumpすると↓のような感じになるはず．

```text:Service/Characteristic=UUID
service = 00001800-0000-1000-8000-00805f9b34fb
chara   = 00002a00-0000-1000-8000-00805f9b34fb
chara   = 00002a01-0000-1000-8000-00805f9b34fb
chara   = 00002a02-0000-1000-8000-00805f9b34fb
chara   = 00002a03-0000-1000-8000-00805f9b34fb
chara   = 00002a04-0000-1000-8000-00805f9b34fb

service = 00001801-0000-1000-8000-00805f9b34fb
chara   = 00002a05-0000-1000-8000-00805f9b34fb

service = 0000180a-0000-1000-8000-00805f9b34fb
chara   = 00002a23-0000-1000-8000-00805f9b34fb
chara   = 00002a24-0000-1000-8000-00805f9b34fb
chara   = 00002a25-0000-1000-8000-00805f9b34fb
chara   = 00002a26-0000-1000-8000-00805f9b34fb
chara   = 00002a27-0000-1000-8000-00805f9b34fb
chara   = 00002a29-0000-1000-8000-00805f9b34fb

service = 0000180f-0000-1000-8000-00805f9b34fb
chara   = 00002a19-0000-1000-8000-00805f9b34fb

service = 86c3810e-f171-40d9-a117-26b300768cd6
chara   = 86c3810e-0010-40d9-a117-26b300768cd6
chara   = 86c3810e-0020-40d9-a117-26b300768cd6
chara   = 86c3810e-0021-40d9-a117-26b300768cd6
chara   = 86c3810e-0040-40d9-a117-26b300768cd6

service = f000ffc0-0451-4000-b000-000000000000
chara   = f000ffc1-0451-4000-b000-000000000000
chara   = f000ffc2-0451-4000-b000-000000000000
```

PowerUp3.0のFirmwareのVersionによってはズレているものもあるかも．


# Step3 : 各Characteristicの内容を調べる

[UUID=0000xxxx-0000-1000-8000-00805F9B34FB]
のパターンになっているUUIDが多いが，これはBluetooth Service Discovery Protocolで定められているBase UUIDというもので，特定の機能のために標準で予約されている．
https://www.bluetooth.com/specifications/assigned-numbers/service-discovery

**GATT Services**
https://www.bluetooth.com/specifications/gatt/services
**GATT Characteristics**
https://www.bluetooth.com/specifications/gatt/characteristics

[UUID=0451-4000-b000-000000000000]
のパターンが何者なのか不明だったが，TI社製ChipのOAD Service(Over the Air Download)に使うUUIDらしい．
https://e2e.ti.com/support/wireless_connectivity/bluetooth_low_energy/f/538/t/380928

公式App起動時にPowerUp3.0のFirmware Updateが走るときがあり，これに使っているのかな．

各Service/Characteristicをまとめると↓のような感じ

```text:Service/Characteristic=UUID
service = 00001800-0000-1000-8000-00805f9b34fb → 0x1800 Generic Access
chara   = 00002a00-0000-1000-8000-00805f9b34fb → 0x2A00 Device Name
chara   = 00002a01-0000-1000-8000-00805f9b34fb → 0x2A01 Appearance
chara   = 00002a02-0000-1000-8000-00805f9b34fb → 0x2A02 Peripheral Privacy Flag
chara   = 00002a03-0000-1000-8000-00805f9b34fb → 0x2A03 Reconnection Address
chara   = 00002a04-0000-1000-8000-00805f9b34fb → 0x2A04 Peripheral Preferred Connection Parameters

service = 00001801-0000-1000-8000-00805f9b34fb → 0x1801 Generic Attribute
chara   = 00002a05-0000-1000-8000-00805f9b34fb → 0x2A05 Service Changed

service = 0000180a-0000-1000-8000-00805f9b34fb → 0x180A Device Information
chara   = 00002a23-0000-1000-8000-00805f9b34fb → 0x2A23 System ID 
chara   = 00002a24-0000-1000-8000-00805f9b34fb → 0x2A24 Model Number String
chara   = 00002a25-0000-1000-8000-00805f9b34fb → 0x2A25 Serial Number String
chara   = 00002a26-0000-1000-8000-00805f9b34fb → 0x2A26 Firmware Revision String
chara   = 00002a27-0000-1000-8000-00805f9b34fb → 0x2A27 Hardware Revision String
chara   = 00002a29-0000-1000-8000-00805f9b34fb → 0x2A29 Manufacturer Name String

service = 0000180f-0000-1000-8000-00805f9b34fb → 0x180F Battery Service
chara   = 00002a19-0000-1000-8000-00805f9b34fb → 0x2A19 Battery Level

service = 86c3810e-f171-40d9-a117-26b300768cd6 → PowerUp3.0独自のUUID
chara   = 86c3810e-0010-40d9-a117-26b300768cd6
chara   = 86c3810e-0020-40d9-a117-26b300768cd6
chara   = 86c3810e-0021-40d9-a117-26b300768cd6
chara   = 86c3810e-0040-40d9-a117-26b300768cd6

service = f000ffc0-0451-4000-b000-000000000000 → TI ChipのOAD機能
chara   = f000ffc1-0451-4000-b000-000000000000
chara   = f000ffc2-0451-4000-b000-000000000000
```

となり，消去法的に
[UUID=86c3810e-xxxx-40d9-a117-26b300768cd6]
がPowerUp3.0の制御用Service/Characteristicになっている模様．


# Step4 : PowerUp3.0制御用Characteristic UUIDを調べる
[UUID=86c3810e-xxxx-40d9-a117-26b300768cd6]
の xxxx を 0010，0020，0021，0040，の4パターンそれぞれに適当に値を書き込んでみて，
PowerUp3.0の実機の挙動がどうなるか，を見ればどのUUIDがプロペラ回転数か尾翼角度かが分かる．

**プロペラ回転数**
[UUID=86c3810e-0010-40d9-a117-26b300768cd6]
1 byte 範囲で制御

**垂直尾翼角度**
[UUID=86c3810e-0021-40d9-a117-26b300768cd6]
Signed Integerで，
0 = Center
+/- = Right/Left

各CharacteristicのDescriptorを読み出せばHintが書かれているかもしれない．
PowerUp3.0の場合は，

```
86c3810e-0010-40d9-a117-26b300768cd6 → Engine Speed
86c3810e-0020-40d9-a117-26b300768cd6 → Rudder Angle
86c3810e-0021-40d9-a117-26b300768cd6 → Rudder 2 Angle
86c3810e-0040-40d9-a117-26b300768cd6 → Temperature
```

というDescriptorが入っている．
(Rudder AngleじゃなくRudder 2 Angleが制御用に使われているのはなぜだろう．．)

これで後は適当にAndroid AppでSlider UIとか作ればPowerUp3.0を自作Appから制御できる．


#まとめ
WireSharkのBluetooth Packet DumpとAndroidのBT/BLE APIを使って
PowerUp3.0のデータ構造を解析，公式App以外の自作AppからPowerUp3.0のデバイス制御ができた．

///---