お盆ですね。あいかわらず仕事の切りが悪い状況が続いているのですが、気を取り直して前回に引き続き、ProcessingからBluetoothシリアル通信経由でLEGO MINDSTORMSを動かしてみたいと思います。
今回は、MINDSTORMSのセンサー情報の取得に挑戦してみます。
MINDSTORMSのセンサー情報の取得方法
Bluetoothシリアル通信を使ったMINDSTORMSのセンサー情報の取得は、次の手順で行います。
SETINPUTMODE
コマンドで、使用する入力ポートとセンサーの種類を指定するGETINPUTVALUE
コマンドでセンサー情報を要求する- MINDSTORMSから返信されたセンサー情報を取得する
MINDSTORMSがサポートするコマンドの種類や送信方法については前回の記事を参考にしてください。
内容を順に説明します。
入力ポートとセンサーの種類の設定
まず、センサーを接続するMINDSTORMSの入力ポート番号とセンサーの種類を指定します。設定にはSETINPUTMODE
コマンドを使用します。
入力ポートはMINDSTORMS本体上で1と表記されている左端のものが、コマンド上では0番と、数字が1つずれているので注意してください。
設定は初期化時に一度だけ行います。
センサー情報の要求
次に、GETINPUTVALUE
コマンドでMINDSTORMSにセンサー情報を要求します。初期化時に設定したポート番号と同じ番号を指定します。
要求を一度行うとセンサー情報が一回分返ってきますので、継続的にセンサー情報を取得したい場合は、定期的にセンサー情報を要求し続ける必要があります。
センサー情報の取得
最後にGETINPUTVALUE
コマンドに対して返信されたセンサー情報を取得します。
Bluetoothを使ったシリアル通信の場合、最初の2バイトでデータ全体のサイズが指定されるので、まず2バイト受信してサイズを確定させた後、指定サイズ分データを受信するという2段階で取得作業を行います。
実際に試してみる
それでは実際にセンサー情報を取得してみます。今回は一番シンプルな接触センサー(Touch Sensor)を使ってみます。
ちなみに接触センサーのセンサー番号は5番になります。
以下コード全文です。
import processing.serial.*; Serial serial = null; int recvSize = 0; byte[] sendData = new byte[256]; byte[] recvData = new byte[256]; void setup() { // シリアルポートを開く String[] ports = Serial.list(); for (String port : ports) { if (port.contains("cu.NXT")) { println("open " + port); serial = new Serial(this, port, 9600); break; } } if (serial == null) { println("can't find ports for NXT"); exit(); } // 0番の入力ポートに接触センサーを設定する sendCommand(0x00, 0x05, 0x00, 0x01, 0x00); // SETINPUTMODE } void draw() { // 0.5秒に1度、0番の入力ポートの情報を要求する if (frameCount % 30 == 0) { sendCommand(0x00, 0x07, 0x00); // GETINPUTVALUES } // MINDSTORMSからのデータを受信する if (recvSize == 0) { // パケットサイズを受信 if (serial.available() > 2) { recvSize = serial.read() + (serial.read() << 8); } } else { // 指定サイズのデータを受信 if (serial.available() >= recvSize) { for (int i = 0; i < recvSize; i++) { recvData[i] = (byte)serial.read(); } receiveCommand(recvData, recvSize); recvSize = 0; } } } void sendCommand(int... data) { // 送信内容を表示 print("send: "); for (int i = 0; i < data.length; i++) { print(hex((byte)data[i]) + " "); } print("\n"); // コマンドを送信する sendData[0] = (byte)(data.length & 0xff); sendData[1] = (byte)(data.length >> 8); for (int i = 0; i < data.length; i ++) { sendData[i + 2] = (byte)data[i]; } serial.write(sendData); } void receiveCommand(byte[] data, int size) { // 受信内容を表示 print("recv: "); for (int i = 0; i < size; i++) { print(hex(data[i], 2) + " "); } print("\n"); // 入力ポート情報の時は接触センサーの値を表示する if (data[0] == 0x02 && data[1] == 0x07) { // GETINPUTVALUES int rawValue = (data[8] & 0xff) + ((data[9] & 0xff) << 8); // 符号を無視させるため、0xffとANDを取ってint型にしている println("touch sensor value = " + rawValue); } }
やることが大分複雑になってきたため、今回はコマンド送信部をsendCommand
、受信部をreceiveCommand
という関数に分け、多少構造化しています。
出力結果
実際の出力結果です。
ちょっとわかりづらいのですが、接触センサーを押した際にターミナルのtouch sensor value
の値が、1023から181に変化しています。
まとめ
2回にわたりProcessingによるMINDSTORMSの操作方法を説明してきましたが、これ以上複雑な操作を行うには専用の補助クラスがないと厳しく、ボリューム的にサンプルの域を越えてしまいそうです。
最近メンテナンスされていないようだったので今回は取り上げませんでしたが、この先はNXTCommのような拡張ライブラリを利用する(あるいは自作する)方が無難そうです。
正直、ProcessingとMINDSTORMSをつなげる必要性のある人が、今日本にどれくらいいるのか疑問なところもあるので、MINDSTORMSの記事は今回で一旦区切りをつけて、次回はまた別の面白そうなことをやってみたいと思います。(その前に仕事の区切りもつけないと…)