無線制御調光器のこと 調光回路の通信部のおはなし
こちらの続き。
今回は通信部分について
前段
通信ですが、ちょろっと前に書いたのですが以下の感じでやっています。
[PC(vvvv)] --UART-- [TWE-Lite(親機)] --ZigBee(無線)-- [TWE-Lite(子機)] --UART-- [Arduino]
ZigBee 部分については TWE-Lite に元から入っているファームウェアがよしなにやってくれるので、自前で実装するのはPC - WTE-Lite間のUART部とTWE-Lite - Arduino間のUART部です。
PC(vvvv) - TWE-Lite(親機) UART通信
ToCoStickについて
今回、TWE-Liteの親機にはToCoStickを使っています。
ToCoStick(トコスティック) - TOCOS-WIRELESS.COM
ToCoStickってのは要は親機設定にしたTWE-LiteとUSB-Serial変換が一緒になってパッケージ化されている製品で、中身については普通のTWE-Liteと変わりありません。
自前で親機設定にしたTWE-Liteを用意してもいいのですが、接続がラクなのでこちらを使っています。
vvvvについて
vvvvっていうのはWindowsで動くビジュアルプログラミング環境です。
以前はopenFrameworksなどを使うことが多かったのですが、vvvvだとラクに早くプロトタイピングできることが多いので最近はよくこちらをつかってます。
今回はvvvv - TWE-Lite間をUART通信で、しかもTWE-Liteファームウェアの決まりにのっとったフォーマットでデータをやり取りしなければ行けないので、そこらへんのデータ変換部分については光らせ方の演出と一緒にvvvv上で自前で組んでます。
vvvv - TWE-Lite 間通信について
普通のプログラミング言語でいうところの”コード”のことをvvvvでは”パッチ”と呼ぶそうなのですが、以下にvvvvからTWE-Liteを使うためのパッチを何となく載せておきます。
データを送信するだけならRS232ノードより上だけでいいのですが、今回は前にちょろっと書いたボタンからの入力などもあったのでこういう感じになってます。
ただし、こちらデータのバッファリングに一部不具合あったり、TWE-Liteの機能の一部しか使えないのでこのまま使うのはちょっと厳しいかも。(パーティーの時は突貫で製作したのでなんとかこのまま乗り切りましたが…)
もうすこしちゃんと実装してTWE-Liteのいろんな機能を使えるようにした物を現在作り直しているところなので、そちらは出来次第、公開しようかなと思っています。
TWE-Liteは本当に入力・出力共にいろいろな使い方ができるので、↑がうまく作れたらもっとこういうハードウェア・PC連携のプロトタイピングが楽になるんじゃないかと思っています!
で、今回 vvvv からはこんな感じのフォーマットでデータを送り出しています。
byte | 0-1 | 2-3 | 4-5 | 6-7 | 8-9 | A-B | C-D | E-F |
---|---|---|---|---|---|---|---|---|
Data | DeviceID | CmdID | UsrCmdID | 調光Data0 | 調光Data1 | 調光Data2 | 調光Data3 | 終端(CRLF) |
上記のうち、DeviceIDとCmdIDはTWE-LiteでUART通信をするうえで必須のデータです。
残りのUsrCmdIDと調光Dataについては今回こちらで定義したデータです。
本当はバイナリデータをそのまま送れれば通信量も少なくていいのですが、TWE-LiteのUART通信の都合上、上記をすべて文字列(ASCIIコード)に変換して送り出しています。
なので調光データは0-255の数値を16進数で表現したうえでそれらを文字列にして送っています。ここら辺の数値変換はvvvvの元から用意されているノードで簡単に実現できるのでラクで良いですね。
PC(vvvv)とTWE-Lite(親機)の通信はざっとこんなかんじです。
TWE-Lite(子機) - Arduino UART通信
TWE-Lite(子機)とArduino間の通信ですが、前述の通り調光データが00 - FFの16進文字列で送られてきます。こちらを解読して元の数値に戻してあげなくてはなりません。
もしかしたら Arduino の標準でそういう変換が楽にできる関数が用意されているカモ知れませんがよくわからなかったのでエイヤっと自前で実装しています。
#define MAX_COUNT (20) // 1 / 50Hz / 2 / 500us #define BYTE2TIM(b) (MAX_COUNT - ((b) * MAX_COUNT / 0xFF)) (中略) byte chex2dec(char c) { if (('0' <= c) && (c <= '9')) { return c - '0'; } else if (('a' <= c) && (c <= 'f')) { return c - 'a' + 10; } else if (('A' <= c) && (c <= 'F')) { return c - 'A' + 10; } return 0; } byte hex2dec(char buff[]) { return (chex2dec(buff[0]) << 4) + chex2dec(buff[1]); } (中略) void loop() { digitalWrite(DEBUG_PIN_1, HIGH); // put your main code here, to run repeatedly: if (Serial.available()) { char c = Serial.read(); if (c != '\r') { if (c == '\n') { // Do if (index >= 5) { T_SERIAL_CMD_HEADER header; header.devID = hex2dec(&buff[1]); header.cmdID = hex2dec(&buff[3]); if (index >= 10) { header.usrCmdID = hex2dec(&buff[5]); if (header.cmdID == 1) { if (header.usrCmdID == 1) { byte data01 = hex2dec(&buff[7]); byte data02 = hex2dec(&buff[9]); byte data03 = hex2dec(&buff[11]); byte data04 = hex2dec(&buff[13]); param[0].waitTim = BYTE2TIM(data01); param[1].waitTim = BYTE2TIM(data02); param[2].waitTim = BYTE2TIM(data03); param[3].waitTim = BYTE2TIM(data04); Serial.println(data01, HEX); } } } } // ~~~~~ index = 0; } else { buff[index] = c; index++; if (index >= sizeof(buff)) { index = 0; } } } } digitalWrite(DEBUG_PIN_1, LOW); }
基本的には
- loop関数の中でシリアル通信を監視
- CRLFを受信したら受信データを解析。
- コマンドID等が合っていれば調光データを文字列から数値にエンコード
- 調光データをトライアック制御のためのタイミングデータに計算し直して、各変数に設定
という感じで作っています。
締め
というわけで、通信部分の解説もこれで終わりです。
無線調光器の部分については大体書き終わったので、次回はプリクラシステムの方かなと思っています。
それでは
追記
つづき書いた