Konashi OTAFUの真実

# はじめに
## 予備知識
Koshianとは[BCM20737ベースのBLEモジュール](http://www.m-pression.com/ja/solutions/boards/koshian)で、980円というお手頃価格で入手できます。購入時には[Konashi](http://konashi.ux-xu.com/)互換のファームウェアが書かれており、提供されるライブラリ等を使えばBLEの知識がなくてもiOS、AndroidやJavaScript上で各種I/Oを叩くことができます。

またBroadcom社の提供するSDKを利用すれば、自分で独自のファームウェアを書き込むことも可能です。SDKについて、ファームウェア書き込みについては前回の[BCM20737SのOTAFU](http://qiita.com/toyoshim/items/d7c5742f32503bb86f7c)の記事で簡単に説明しましたが、ここでKonashiの提供するというOTAFU（Over-the-Air Firmware Upgrade: BLE通信を使った自己ファームアップデート機能）がSDK提供のOTAFUサンプル、書き込みツールと互換性がない事がわかりました。

今回の記事ではKonashi OTAFUについて新たにわかったことをまとめたいと思います。

## 何ができたか
Konashiのフリをする独自ファームウェアをKoshianに書き込み、Konashiのオフィシャルアプリからオリジナルファームウェア（2.0.0-release-04または2.1.3-rc3）に書き戻すことに成功しました。

2016年1月7日追記：Konashiのオフィシャルファームを、Windows用の改変したBroadcom社のOTAツールから書き換えることに成功しました。

# Konashi OTAFUの秘密
## 何が違うのか
ダメ元で購入元[マクニカオンライン](https://store.macnica.co.jp/products/mpression_mp-ksn001b])の問い合わせフォームで質問したところ、その日のうちにお返事をいただき、以下の事がわかりました。

1. Broadcom社のOTAFUと同じ、UUID: 9E5D1E47-5C13-43A0-8635-82AD38A1386Fで公開されているServiceには一部未実装の機能がある
2. 完全動作するOTAFUは別名のServiceとして実装されている

1.が原因でControl PointのCharacteristic Client Configuration設定が常に失敗するようです。このためNotificationの設定ができず、SDK公式ツールでは書き込めません。

2.はUUID: 1B7E8251-2877-41C3-B46E-CF057C562023（2016年1月7日追記：UUIDは固定ではなくランダムに決まるようです）で公開されているServiceについて言っているようです。該当Serviceには2つのCharacteristicがぶら下がっています。本来OTAFUには3つのCharacteristicがぶら下がっていますが、このうち必須ではないAppInfoを取り払った、Control PointとDataの2つが実装されているようです。これら2つはpropertiesを見ればどちらがどちらか容易に想像できます。

1つだけ正体不明のServiceが存在していたのでOTAFUに絡んでいるのかも、とは想像していましたが、事前に機能開放のためのマジックワードでも書き込むのかな、と予想していました。まさか公式OTAFU Serviceがハリボテだったとは予想外です。ヘル・ミッショネルズ級の落とし穴。

2016年1月7日追記：更に気づいた点として、おそらくkonashiファームのバグなのですが、Control Pointのnotificationがなぜかオリジナルサービス側のControl Pointに届くというファンキーな挙動を示すようです（自分も互換ファーム作る際に同じバグを仕込んだので気持ちはわかる）。iOS用のKonashi.jsは、どちらのコントロールポイントにnotificationが届いても反応する模様。

## Service / Characteristic UUID一覧
まとめると以下のようになります。

- Service
 + 1B7E8251-2877-41C3-B46E-CF057C562023
- Control Point Characteristic
 + 8AC32D3F-5CB9-4D44-BEC2-EE689169F626
- Data Characteristic
 + 5E9BF2A8-F93F-4481-A67E-3B2F4A07891A

2016年1月7日追記：UUIDはランダムに決まるようなので、ここでの値は意味を持ちませんでした。適宜Propertiesを見て推測するしかないようです。

2016年1月7日追記：ただしControl Point書き込み後のnotificationは、Broadcom社版OTAFUサービスの下にぶら下がっているControl Point宛てに届きます。Client ConfigurationはKonashi版OTAFUサービス宛てに出しておく必要があり、Broadcom社版宛に出すと不明なエラーが返ります。

# 成功した実験
## オリジナルファームに書き戻してみよう
不明だったServiceの実態が判明したので、konashiのフリをするファームウェアを作る事ができそうです。konashiとほぼ同じGATTを公開し、同じ内容でAdvertiseすれば、公式アプリを騙してオリジナルのファームウェアに書き戻してもらう事ができそうです。

## やらなかった事
かなりの情報をチェックしているようで、やった事よりやらなかった事を書いたほうが早い状況でした。ちなみにkonashi.js 2.2.0を騙した時の記録ですので、今後のアップデートでチェックが厳しくなる可能性は0ではありません。

### UUID: 229BFF00-03FB-40DA-98A7-B0DEF65C2D4B

Konashiのメイン機能を公開している本サービスですが、ここにぶら下がっている大量のCharacteristicsは全て省略できました。がサービス自体は空でも良いのでぶら下げておく必要があるようです。

### Battery Service

2.0.0のファームにはBattery Service以下に3つのCharacteristicsがぶら下がっています。1つはBattery Levelで一般的な物なのですが、残りの2つは128bit UUIDの何か。仕様を詳しく調べたわけじゃないですが、nRF Master Control Panelとかのアプリを使ってもUnknown Characteristicと表示されていました。この未知の2つのCharacteristicsも省略できました。

省略したのはこれだけです。Device Informationなんかも少しくらいは省略できるCharacteristicsもあるかもしれませんが、面倒なので個別には試してはいません。古いファームのバージョンを表示したりしているので、全然見ていないという事はないはずです。その他、BTのアドレスを本来の物を使ったりデバイス名、ローカル名をkonashi2-&lt;BTアドレス後半3バイトのhex&gt;に統一したり、と細かいところでも調整しましたが、必須かどうかは確認していません（2016年1月7日追記：BTアドレスは適当でOK、かつローカル名の命名ルールと矛盾していても問題ない事を確認しています）。

## OTAFU Servicesの実装
SDKのota_firmware_upgradeのサンプルをベースにしています。上記Services/CharacteristicsをGATTに登録して、Konashi版OTAFUのCharacteristicsにアクセスしてきた時にも本家OTAFU版のコードに飛ぶようにwrite_handlerを書き換えました。

2016年1月7日追記：上記の手順で単純に直すとControl Pointに書き込まれた後に呼ばれるnotificationの送信で、本家OTAFU版サービス+キャラクタリクス宛てに通知を飛ばしてしまいます。正しく応答するにはキャラクタリクスのハンドルを持ち回るように修正する必要があります。ただし、Konashi.jsはこのバグに関しては許容してくれるので直さなくても大丈夫（本家ファームも同じバグがある）。

ちなみにサンプルはAdvertise情報のローカル名を登録するところがバグっててlenより1バイト短いデータしかコピーしていません。最後のNULLターミネート分までコピーするように直さないと名前長次第ではクライアントによって変な事が起きます。

## 結果
GATT情報とAdvertise情報を揃えた時点でkonashi.jsから認識されました。Konashi版OTAFUをハンドルするようにして、ようやくファーム更新も正しく終了するようになりました。

ちなみにble_trace*()系の関数を使えば、デバッグ用UARTを経由してログを吐き出すことができます。Data書き込みの際に

```c
for (int i = 0; i < len; ++i) ble_trace1("putchar(0x%02x);", attrPtr[i]);
```

とかしておいて、screen /dev/tty.my-serial-port 115200でログを眺めつつ、ファイルへのログ出力をON/OFFして、

```shell-session
% (echo "#include <stdio.h>"; \
> echo "int main() {"; \
> grep putchar screenlog.0; \
> echo "return 0;}") | gcc -xc - -o foo; ./foo > konashi.ota.bin
```

とかしておくと、何かあっても安心かもしれません。まぁ、やって良いことかは微妙なので、お勧めはしません。いずれにせよ、これでkonashiに戻れる環境ができました。あとは好き勝手ファームをいじるだけです。

# 失敗した実験（2016年1月7日追記：成功しました）
## 公式SDKツールを改変してOTAFU
これができれば、konashiを買ったらハンダ付けなしで遊べるようになって色々と便利なのですが……。例えばOTAFU付きのオリジナルファームと偽konashiファームをバイナリ公開すれば、第三者が気軽にkonashiを別ファームに書き換えて、飽きたら元に戻す、とかできるわけです。

~~で、現在の実験状況なのですが、なぜかうまくいっていません。SDK公式のファーム更新ツールに対してヘッダ上のUUIDを差し替えてみたのですが、最初のコマンドを書いた後にnotificationが届かずに止まってしまいました。ファーム側にはコマンドが届いてnotifyを送っているようなので、たぶんホスト側で何か修正忘れがあるのでしょう。~~

2016年1月7日追記：最後の謎が解けて、無事に改変版公式ツールでKonashiのOTAFUに成功しました。修正は結構面倒になってしまいました。

- UUIDは基本的にはKonashi版に全面的に置き換える
- notificationはBroadcom版に対して見張る

後者のために初期化周りで両方のサービスに対してQueryを回して、サービスとキャラクタリクスの情報を保持、notification登録の際にBroadcom版に対して設定、notificationを受けた際のUUIDチェックの修正、といった処理が必要になります。

# 備考
## ソース公開とか
しても良ければするのですが、WICED Smart IDEのEULAが微妙なので躊躇しています。このSDKで作ったファームについては、製品向けにバイナリを配布するのはOKってなっていて、ソース公開については微妙。詳しく読むのは面倒なので、もし詳しく調べた人がいれば教えて下さい。

自分のソースとは言え、サンプルからのテンプレのコピペもありますし、APIに依存してる部分はグレーです。サンプルコードのヘッダには明示的に再配布を禁止する旨がコメントで書かれているので、きっとNGなのでしょう。もしバイナリ等希望のようでしたら、個別にご相談ください。

## 参考
- KonashiとKoshianについての関連記事
 + [Web Bluetoothを使ってkonashi2.0でLチカしてみる](http://qiita.com/toyoshim/items/74ae7551dc2c9ab9cbf6)
 + [konashi.js向けのコードをChromeで実行](http://qiita.com/toyoshim/items/05b1d14ca925d5df3e43)
 + [Koshianのファームを書き換えてみた](http://qiita.com/toyoshim/items/4dc7ec9dc0345b258804)
- KoshianのOTAについての関連記事
 + [BCM20737SのOTAFU - Koshian](http://qiita.com/toyoshim/items/d7c5742f32503bb86f7c#koshian)