Linuxでmicro:bitをScratchにつなぐ

(2020/1/18:追記 Chromeでの動作を確認できました)

近所の[CoderDojo](https://coderdojo.jp/)で[Scratch](https://scratch.mit.edu/)と[micro:bit](https://microbit.org/)が流行っています。どちらも教育用プログラミング環境としては有名ですね。この２つをつないでmicro:bitからScratchを操作する、といったこともできるそうです。実際[CoderDojo横浜](https://coderdojo-yokohama.connpass.com/)で、micro:bitをScratchにつないでmicro:bitをゲームパッドにして遊んでいる子がいました。

さっそくmicro:bitを購入し、うちの子どもたちにもScratchとmicro:bitを使って遊ばせてあげようとおもったのですが、micro:bitとScratchを接続する[Scratch Link](https://en.scratch-wiki.info/wiki/Scratch_Link)というプログラムがWindows版とMacOS版しかありません。我が家は諸事情によりWindowsやMacを使えないので、はがゆいところです。ぐぬぬ。さらに、[Scratchコミュニティのディスカッションスレッド](https://scratch.mit.edu/discuss/m/topic/315200/)でScratch Linkのソースコードは公開されていないという書き込みを見つけてがっかりしていました（2019年3月時点）。
![dont_work_on_linux.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337918/84b81cad-2bb9-4fc0-0ed9-a2463f1c9b56.png)
しかし、その後ディスカッションスレッドをもう一度訪れてみると[Scratch LinkのソースコードがGitHub公開](https://github.com/LLK/scratch-link)との書き込みがありました！　中身を読んでみると、自分でもLinux向けに書き直せそうだったので、試しに書き直してみることにしました。

Scratch Linkのしくみ
===================
ScratchはPC上のブラウザで動き、micro:bitはBluetooth Low EnergyでPCとつながっています。Scratch Linkは、PC上の別アプリケーションとして動作し、Scratchとmicro:bitをつないでいます。Scratch側は、micro:bit extensionなどのScratch Extensionを追加することでScratch Linkと通信できるようになります。

![scratch_link_structure.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337918/c1fd4aa7-64a5-4399-d90d-96105074ba73.png)

ScratchとScratch Linkの間の通信プロトコル(Scratch Link Network Protocol)は、[Scratch Link Git Hubに記述があります](https://github.com/LLK/scratch-link/blob/develop/Documentation/NetworkProtocol.md)。要点をいくつかリストします。

 1. Web Socketを使う。Scratch Link側がWeb Socket server。Scratch Extension側がWeb Socket client。
 2. やりとりするデータ構造はJSON RPC 2.0。
 3. INITIAL, DISCOVERY, CONNECTEDの３ステートが定義されている。discover, connectなどのコマンドをScratch ExtensionとScrach Linkがやり取りして状態を遷移していく。
 4. Bluetoothデバイスと Bluetooth Low Energy(BLE)デバイスの２種類のデバイスをサポート。それぞれに応じたプロトコルの追加定義あり。
 5. BLEに関するプロトコルは[こちら](https://github.com/LLK/scratch-link/blob/develop/Documentation/BluetoothLE.md)。serviceやCharacteristicのdiscoveryや、read, write, notificationなどの扱いが定められていて、Scratch ExtensionからBLEデバイスを扱えるようになっている。

おおまかに言うと、Scratch Linkは、Scratch Extensionとやりとりする「Web Socket Server」と、micro:bitなどのBLEデバイス(Bluetooth)デバイスとやりとりする「Bluetooth Application」の２つから構成されていると言えるでしょう。Scratch Extensionから、JSON RPCでラップされたdiscovery, readなどの命令を受け取ると、それをBLEのコマンドに置き換えてBLEデバイスに対して実行し、結果をScratch Extensionに返します。また、BLEデバイスから送られるnotifcationを待ち受け、notificationが届いたらその内容からJSON RPC形式のnotificationを作ってScratch Extensionに送ります。いわば、Scratch Linkは土管のようなもので、Scratch LinkがBLEデバイス(BTデバイス)と通信したい内容を、BLE(Blue Tooth)とJSON RPCの変換をかけながら相互に受け渡しています。

Scratch Link用micro:bitプログラム
================================
Scratch LinkはBLE(Bluetooth)の土管ですから、特にmicro:bitに特化していません。他のBLE(Bluetooth)デバイスも、Scratchにつなげられるそうです。私は持っていないので試していませんが、例えばLego WeDoもサポートしているとのこと。micro:bit固有の処理は、Scratch側のmicro:bit extensionと、micro:bitにダウンロードするscratch-microbit-*.hexが担当しています。

余談になりますが、micro:bit側がBLEで通信するための仕様としては、Lancaster Universityによる[Bluetooth Developer Studio Level 3 Profile Report](https://translate.googleusercontent.com/translate_c?depth=1&hl=en&prev=search&rurl=translate.google.com&sl=zh-CN&sp=nmt4&u=https://lancaster-university.github.io/microbit-docs/resources/bluetooth/bluetooth_profile.html&xid=17259,15700021,15700186,15700190,15700256,15700259,15700262,15700265&usg=ALkJrhg4-gaZVNMNv51jt71LBCzERyYo7w)が有名です。私はてっきりこの仕様を使ってscratch-microbit-*.hexが実装されていると思っていたのですが、実際にBLEのdiscoveryをかけてみると期待したService IDが取得できません。ウェブを漁ってみると、これを解析している人がいて、[scratch-microbit-*.hexのBLE通信プロトコルは、Lancaster Universityの仕様とは異なる](https://translate.google.com/translate?hl=en&sl=zh-CN&u=https://blog.just4fun.site/scratch3-microbit-analysis.html&prev=search)ことがわかりました。Scratch側のmicro:bit extensionにも、この独自仕様に対応したコードがあるそうです。この仕様はmicro:bitが持っている複数センサの状態をbit streamにまとめて一括して渡す仕様になっていて、センサごとに分けて通信するLancaster Univiersityの仕様より通信効率が良さそうです。全く違うプロトコルですから、Scratch Linkを使ってmicro:bitを使うときには、BLEを扱う他のmicro:bitプログラムではだめで、Scratch Link専用のscratch-microbit-*.hexを必ずdownloadしておく必要があります。

Scratch Link for Linux
======================
Linux向けのScratch Linkを実装するにあたり、あまり時間もかけられないので、少しなれているpythonでサクッと書くことにしました。WebSocketについては、[websocketsモジュール](https://websockets.readthedocs.io/en/stable/intro.html)があります。BLEについては、Linux向けBluetooshドライバbluezに対応した[bluepyモジュール](https://github.com/IanHarvey/bluepy)を使うことにしました。

![scratch_link_for_linux.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337918/a7cd7b8b-d678-af20-b6aa-8349af939cd2.png)

BLEデバイスに特化してBluetoothデバイス対応を省くなど、多少手抜きをして360行くらいのpython scriptを書き、Scratchとmicro:bitをつなぐことができました。やっぱりpythonは生産性高いなー。[コードはgithubにあげています](https://github.com/kawasaki/bluepy-scratch-link)。

websocketモジュールのAPIは、[asyncio](https://docs.python.org/3/library/asyncio.html)を使った性能の良さそうなかっこいいAPI。asyncioを知らなかったので、いい勉強になりました。一方bluepyは古めの実装で、asyncioは使われていません。BLEデバイスからbluepy経由でnotificationを受けるために、スレッドを起こすことにしたのですが、このスレッドとwebsocket server側のasyncio処理の間で、BLE通信の競合が起こり、解決に苦労しました。Lockを１つ導入して排他することで競合を避けられました。

Secure WebSocket ServerのCeritificate
=====================================
Scratchはセキュリティもしっかり考えた設計になっていて、Scratch Extensionが接続するWebSocket serverの認証もしっかりやっています。このため、WebSocket serverはSecure Websocket serverとして用意する必要があり、serverのcertificateも用意する必要があります。[MacOSむけにScratch Linkのcertificationを用意する方法](https://translate.google.com/translate?hl=en&sl=ko&u=https://ahnbk.com/%3Fp%3D790&prev=search)を書いている人がいましたので、この記事と[Scratch LinkのREADME](https://github.com/LLK/scratch-link)を参考に、Certificate(pem file)を用意しました。このCertificateでFireFox **とChrome** からWebSocket serverに接続できました~~（ただ、残念ながらChromeから接続できませんでした）~~。

まとめと今後の課題
================
Scratchとmicro:bitをLinux上でつなぐためのpython scriptを書きました。LinuxでもScratchとmicro:bitで遊べます！

![now_linux_is_ok.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337918/a5678117-8ef1-e779-1ed2-8ae9c63dad9c.png)

micro:bitを使うscratch作品を探していくつか遊んでみると、とっても楽しいです！（WindowsやMacではあたりまえなんだけどね）

![microbit_with_scratch_on_linux.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337918/448d7d7b-3f28-8542-6a64-b0e9db36e5ac.jpeg)

今後の課題は、以下の２つでしょうか。
1. ~~Chromeで動かない(Certificateの扱い)~~ (ChromeでLocal Server Certificateを許す方法を、[GitHub issueで教えていただきました](https://github.com/kawasaki/bluepy-scratch-link/issues/3)。)
2. micro:bit以外のデバイス対応(特にBluetooth device)

現場からは以上です。