09/26 の Comm Tech Festival で Windows 10 IoT Core の話をしてきたので、その補足をつらつらと書いておきます。
発表スライドは、こちらから。
何を話したかというと、Windows 10 IoT Core を Raspberry Pi に乗せて、Tamiya の Boxing Fighter を動かすというデモです。前半を Windows 10 IoT の概要(かつ、裏事情など諸々)と、後半が Boxing Fighter を動かすデモになっています。発表でも強調しましたが、Boxing Fighter も戦車も、2個のブラシモーター&ギアボックスという組み合わせなので、動作させる部分は切り替え可能です。戦車の場合は本格的に動かそうと思うと、キャタピラで動いてしまうので戦車自体に Raspberry Pi と電源を積まないといけない制限が出てきてしまいますが、Boxing Fighter の場合は、あまりあちこち動かないので線が出てても結構見栄えがします。どっか行っちゃうことがないので、モーターを動かす学習とかにちょうどいいのです。
デモのコード
デモで使ったコードは、github の RPiBoxing からダウンロードできます。MS 品川の会場では Wi-Fi がうまくつながらない可能性があったので、
- 通常のマウス操作のデモ
- SenserTag を使って Bluetooth 経由で操作するデモ
- Windows IoT 側に簡易 HTTP サーバーを起動させて、ブラウザから操作するデモ
になっています。3つ目の HTTP サーバーを起動させるパターンは、クライアント側を iPhone などで作ることも可能です。実際、iPhone 用に Swift で作って持って行ったのですが、Wi-Fiが繋がらないので断念。簡易サーバーへは Web API を利用した GET コマンドを送信するパターンで作って理ます。
以下、ちょっとコードの補足をしておきます。
モータークラスを作る
モーターを正逆で動かすためには、GPIO を2本用いる必要があります。モーターを動かすたびに2本の GPIO を制御してもよいのですが、適当な Motor クラスを作っておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | public class Motor { GpioPin out1 { get ; set ; } GpioPin out2 { get ; set ; } public Motor( int pin1, int pin2) { this .out1 = RPi.gpio.OpenPin(pin1); this .out2 = RPi.gpio.OpenPin(pin2); this .out1.Write(GpioPinValue.Low); this .out2.Write(GpioPinValue.Low); this .out1.SetDriveMode(GpioPinDriveMode.Output); this .out2.SetDriveMode(GpioPinDriveMode.Output); _dir = 0; } int _dir = 0; /// <summary> /// 回転方向を変える /// </summary> public int Direction { get { return _dir; } set { if (_dir != value) { _dir = value; if (_dir == 0) { this .out1.Write(GpioPinValue.Low); this .out2.Write(GpioPinValue.Low); } else if (_dir > 0) { this .out1.Write(GpioPinValue.High); this .out2.Write(GpioPinValue.Low); } else { this .out1.Write(GpioPinValue.Low); this .out2.Write(GpioPinValue.High); } } } } } |
BLE を利用する
リモート制御をするときには、Bluetooth か Wi-Fi を使うとよいのですが、Bluetooth の場合は、ちょっと手順がややこしくなっています。もっと手軽に使えるクラスにすればいいんですけどね。BluetoothGATT から該当箇所を抜き出して使っています。iOS や Android の場合は、特定の BLE デバイスを使うときにはそれを利用するライブラリもワンセットになることが多いので、それを使えばいいのですが、Windows IoT の場合は当然のことながら提供されないので自前で作ることになります。
サンプルコードでは、画面でボタンを押して BLE デバイスを探すことになっていますが、アプリの起動時に自動的に探しに行って接続してもよいですね。
BLE を使うときにはコツがあって、
- Package.appxmanifest に <DeviceCapability Name=”bluetooth.genericAttributeProfile” /> を追加する。
- あらかじめ、該当する BLE デバイスをペアリングしておく。
- BLE のイベントから UI を扱うときは、Dispatcher.RunAsync を使ってスレッドを変える必要がある。
ということになります。実は、3つめの UI スレッドのところが曲者で、確かに BLE などのイベントから UI スレッドにアクセスする(画面表示をさせる)ときには Dispatcher.RunAsync を使ってスレッドを切り替えることは忘れないのですが、MVVM を使って INotifyPropertyChanged で UI アクセスをするときにも Dispatcher.RunAsync が必要です。
具体的には以下のように、ViewModel を媒介するときにも Dispatcher を効かせないといけないってことです。ふつうのストアアプリを使っているときは分からない落とし穴ですが、デバイス系は UI スレッドではないので、見えないところでアクセスすると落ちるってことになります。まあ、これはストアアプリで作ったときにもスレッドで動かすときには注意するわけですが。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { if ((data & 0x01) == 0x01) { _model.BLETap = "Right F" ; KeyRFront.Background = new SolidColorBrush(Colors.Green); motorRight.Direction = 1; } else { _model.BLETap = "" ; KeyRFront.Background = new SolidColorBrush(Colors.Red); motorRight.Direction = 0; } if ((data & 0x02) == 0x02) { _model.BLETap = "Left F" ; KeyLFront.Background = new SolidColorBrush(Colors.Green); motorLeft.Direction = 1; } else { _model.BLETap = "" ; KeyLFront.Background = new SolidColorBrush(Colors.Red); motorLeft.Direction = 0; } }); |
簡易 HTTP サーバーを作る
System.Net.HttpListener がないので、StreamSocketListener を使って自作をします。サンプルとしては、Blinky WebServer を参考にすればよいでしょう。私のデモコードもここから引っ張ってきています。単純な Web API を作りたいときに、こうやっていちいち簡易サーバーのコードを作らないといけないのは面倒なのでライブラリしたいところですね。本来ならば標準で用意してほしいところなのですが、AllJoyn 関係とダブってしまうのか割愛されています。
ただし、ここをクリアすると、ブラウザから制御ができるようになるので是非おさえておきたいところです。ブラウザだけでなくスマートフォンを使っての呼び出しも楽になります。
そんな訳で、このあたりは少しまとめて、近々 hacker.io にアップする予定です。