無線モジュールTWE-LiteでVFDを遠隔表示してみよう!

最終更新日 2015/12/4
 無線通信を用いて、VFDモジュールを遠隔表示させる方法を解説します。

 小型の無線通信モジュール【TWE-Lite】 を使って、離れたところにあるVFDに表示を出してみました。同じインターフェイスであれば、他の表示器にも応用できます。  

TWE-LiteでGU-7000シリーズを遠隔表示する

デモパネル

 ここでは、Arduinoに書き込んだ「じゃんけんゲームプログラム」を、TWE-Liteを介して、離れたGU-7000シリーズに遠隔表示させる方法を紹介します。漢字表示やスクロール、画像表示の例も含めてご活用いただければ幸いです。

 実機のデモパネルはカホパーツセンター(福岡)様の店舗に展示されていますので、お立ち寄りの際は是非ご覧ください。

準備するもの


TWE-Liteの準備

 TWE-Liteに「シリアル通信用専用アプリ」書き込みます。次の手順で行います。

  1. 開発環境のダウンロード
     TWE-Lite公式サイトの「ToCoNet開発環境」ページから、ToCoNet SDKのWindows版「SDK全部入り」をダウンロードします。

  2. ダウンロードしたファイルの解凍
     ダウンロードされる圧縮ファイルを、説明に従ってCドライブの直下(C:\)に解凍します。 ほかの場所(デスクトップなど)ではいけません。 この中に開発環境とプログラムツールが入っています。

  3. 「シリアル通信専用アプリ」をダウンロード
     TWE-Lite公式サイトの「TWE-Zeroアプリ」ページから、シリアル通信専用アプリの「TWE-Lite用TWE-Regular/Strong用」をダウンロードします。

  4. 半田付け
     TWE-Lite Rの説明にしたがって、14ピンソケットをTWE-Lite Rに半田付けします。 TWE-Liteにもポストヘッダとアンテナを半田付けしておきましょう。
    TWE-Liteの組立て

  5. PCに接続
     TWE-LiteをTWE-Lite R に載せてUSBケーブルでPCにつなぎます。 今回入手したTWE-Lite RはFTDIのチップを使っていましたので、 必要に応じてドライバーをインストールしてください。
    TWE-LiteをPCに接続

  6. プログラマ起動
     解凍しておいたフォルダからプログラマを起動します。

      C:\TWESDK\Tools\TWE-Programmer

  7. 確認
     TWE-Lite Rが認識できると、プログラマにシリアルポート番号とTWE-Liteのシリアル番号が表示されます。 シリアルポート番号は後で使いますのでメモしておきます。
     これで準備が整いました。

  8. 書き込み
     ダウンロードしておいた「シリアル通信専用アプリ」のファイルを解凍してbinファイルをプログラマに読ませます。
    今回のファイル名は次の通りです。

       App_Uart_Master_JN5164L1-0-6S_1_2_15.bin 

    うまく書き込めたら、もう一個のTWE-Lite-DIPも同じファイルを書き込んでおきましょう。

  9. オプション設定(この設定をしなくても使えます)
    このままでも使えますが、混信防止の設定を行いましょう。
    TeraTermというPC用の通信ソフトを使います。

    お持ちでなければWebで検索してTeraTermをダウンロードしてください。

    TWE-Lite RにTWE-Liteを刺したままPCにつないでTeraTermを起動します。
    TWE-Lite Rのつながったポート番号でTeraTermを接続します。ポート番号はプログラマかWindous のデバイスマネージャで知ることができます。
    TeraTermの設定メニューを開いてシリアルポートからボーレートを"115200"に変更します。
    OKをクリックして設定ダイアログを閉じます。
    ゆっくりと"+"を何回かタイプします。次の画面になるまで続けてください。

    TeraTerm画面

    両方のTWE-Liteに同じApplication IDを割り当てます。

    最初の行のSIDがこのTWE-LiteのIDですので、メモしておいてください。頭の0xはC言語で16進数を意味するもので不要です。
    "a"とタイプすると次のように出ますので、メモしたIDをタイプしてEnterキーを押します。

    Input Application ID (HEX:32bit):

    一拍おいて画面が書き直されます。" a: set Application ID()"の括弧の中が先ほど入力したIDに変わっていることを確認します。
    *がついていますが、これは変更が仮のものであることを示します。
    確定するために"S"(大文字のS)をタイプすると、画面が再度書き直されて*が消えます。
    もう1個のTWE-Liteに交換して先ほどメモしたIDを設定します。

    これで同じApplication IDのモジュール同士が通信するようになります。モジュールのSIDは重複がないそうですので、これをApplication IDとして使っておけば別の組との混信が回避されます。

 TWE-Liteのプログラムは以上です。 メニューの他項目はメーカーサイトを参照してください。

組み立て

3端子レギュレータ  いよいよ組み立てに入ります。GU128X32D-7950はUSBからの5V電源で動作できます。 表示器にはコネクタは実装されていませんので、お好みの6ピンコネクタをお使いください。ポストヘッダを半田付けしてブレッドボードに刺してもOKです。

 TWE-Liteに必要な3.3V電源はUSBの5Vから3端子レギュレータで作ります。今回はSMT品を使いましたのでブレッドボード用に3ピンのポストヘッダに半田付けしました。

 これでArduinoのシリアル出力を無線モジュール経由で表示器に送るセットの完成です。

日本語が文字化けするときに(Unicode<>Shift-JIS変換ツール)

 PCの多くはUnicodeで動作していますが、VFDモジュールのフォントテーブルはShift-JISになっています。 文字コードが違うため、PCからモジュールに文字を書き込むと、文字化けすることがあります。
 そこでクリップボードにコピーされているUnicodeのテキストを、Shift-JISの16進数列表記に変換するツールを用意しました。 ダウンロードしてご活用ください。

li_nomal.gif(116 byte) ツールのダウンロードはこちら

サンプルスケッチ(サンプルプログラム)

 Arduinoのホームページから開発環境をダウンロードして、ツールメニューでArduino nanoを選び、ポートを選んでください。書込器装置はArduino as ISPにします。

 下記はじゃんけんゲームのプログラムです。白のタクトスイッチを押すとゲームが始まりますので、黒(グー)緑(チョキ)、橙(パー)のボタンを押して勝負してみましょう。

li_nomal.gif(116 byte) スケッチのダウンロードはこちら

// グローバル変数の宣言 #define _Gu_leng 137 #define _Choki_leng 137 #define _Pa_leng 137 unsigned char _Gu_code[ _Gu_leng ] = // グーの画像データ { /*** -----OpCode----- xL xH yL yH 01 d1 d2 d3 . . . (d?:BmpData 0<=d?<=255) *****/ 0x1f, 0x28, 0x66, 0x11, 0x20, 0x00, 0x04, 0x00, 0x01, 0x00, 0x1f, 0xff, 0x80, 0x00, 0x20, 0x00, 0x40, 0x00, 0x20, 0x00, 0x40, 0x07, 0xc0, 0x00, 0x20, 0x18, 0x40, 0x00, 0x20, 0x20, 0x40, 0x20, 0x20, 0x20, 0x40, 0x20, 0x1f, 0x40, 0x60, 0x20, 0x01, 0x40, 0x50, 0x20, 0x00, 0x40, 0x48, 0x20, 0x00, 0x40, 0x48, 0x40, 0x00, 0x20, 0x48, 0x40, 0x00, 0x1f, 0xc8, 0x40, 0x00, 0x30, 0x48, 0xa0, 0x00, 0x60, 0x49, 0x10, 0x00, 0x40, 0x3e, 0x10, 0x00, 0x40, 0x02, 0x10, 0x00, 0x40, 0x02, 0x10, 0x00, 0x40, 0x01, 0x20, 0x00, 0x20, 0x00, 0xc0, 0x00, 0x1f, 0xff, 0xc0, 0x1f, 0x08, 0x02, 0x20, 0x20, 0x10, 0x04, 0x20, 0x20, 0x20, 0x04, 0x20, 0x20, 0x20, 0x02, 0x40, 0x20, 0x20, 0x0f, 0xc0, 0x40, 0x20, 0xf2, 0x40, 0x40, 0x1f, 0x04, 0x20, 0x80, 0x08, 0x04, 0x21, 0x00, 0x08, 0x04, 0x22, 0x00, 0x08, 0x02, 0x4c, 0x00, 0x07, 0xff, 0xf0, 0x00, }; unsigned char _Choki_code[ _Choki_leng ] = //チョキの画像データ { /*** -----OpCode----- xL xH yL yH 01 d1 d2 d3 . . . (d?:BmpData 0<=d?<=255) *****/ 0x1f, 0x28, 0x66, 0x11, 0x20, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x08, 0x70, 0x3f, 0xff, 0xf8, 0x18, 0x40, 0x00, 0x08, 0x0c, 0x80, 0x00, 0x08, 0x06, 0x80, 0x00, 0x08, 0x03, 0x80, 0x00, 0x08, 0x21, 0x80, 0x00, 0x0e, 0x21, 0x40, 0x00, 0x0a, 0x21, 0x3f, 0xff, 0xca, 0x21, 0x40, 0x00, 0x0a, 0x21, 0x80, 0x00, 0x0a, 0x21, 0x80, 0x00, 0x0a, 0x41, 0x80, 0x00, 0x0a, 0x81, 0x80, 0x00, 0x0f, 0x01, 0x40, 0x00, 0x7f, 0x81, 0x3f, 0xff, 0x84, 0x41, 0x00, 0x04, 0x04, 0x41, 0x00, 0x04, 0x04, 0x41, 0x00, 0x04, 0x05, 0x81, 0x00, 0x03, 0xfe, 0x02, 0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x84, 0x00, 0x01, 0x04, 0x88, 0x00, 0x01, 0x04, 0x90, 0x00, 0x00, 0x85, 0x20, 0x00, 0x00, 0x66, 0x40, 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; unsigned char _Pa_code[ _Pa_leng ] = //パーの画像データ { /*** -----OpCode----- xL xH yL yH 01 d1 d2 d3 . . . (d?:BmpData 0<=d?<=255) *****/ 0x1f, 0x28, 0x66, 0x11, 0x20, 0x00, 0x04, 0x00, 0x01, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x20, 0x7c, 0x00, 0x00, 0x20, 0x03, 0xe0, 0x00, 0x20, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x04, 0x00, 0x07, 0x00, 0x02, 0x00, 0x00, 0xf0, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x03, 0x01, 0x3f, 0xff, 0xfe, 0x01, 0x40, 0x00, 0x00, 0x01, 0x80, 0x00, 0x01, 0x01, 0x80, 0x00, 0x00, 0x81, 0x80, 0x00, 0x04, 0x7d, 0x80, 0x00, 0x04, 0x01, 0x7f, 0xff, 0xc2, 0x01, 0x40, 0x00, 0x01, 0x01, 0x80, 0x00, 0x00, 0x81, 0x80, 0x00, 0x00, 0x41, 0x80, 0x00, 0x0c, 0x21, 0x40, 0x00, 0x02, 0x11, 0x3f, 0xff, 0xc1, 0x09, 0x40, 0x00, 0x00, 0x85, 0x40, 0x00, 0x00, 0x41, 0x40, 0x00, 0x00, 0x41, 0x40, 0x00, 0x00, 0x41, 0x3f, 0xff, 0xe0, 0x41, 0x04, 0x00, 0x00, 0x41, 0x08, 0x00, 0x00, 0x41, 0x08, 0x00, 0x00, 0x42, 0x07, 0xff, 0xff, 0xfc, }; int i; // 表示パターンを憶える変数 int swOrg = 10; //ポートのピン割り当て int swGrn = 9; int swBlk = 4; int swWht = 3; boolean swOrgPush = false; // ボタンが押されたことを記憶するフラグ boolean swGrnPush = false; boolean swBlkPush = false; boolean swWhtPush = false; unsigned long ms; // 時間待ちタイマーの基点を記憶する変数 void setup() { // put your setup code here, to run once: // 初期化 Serial.begin(38400); i = 0; pinMode(swOrg, INPUT_PULLUP); pinMode(swGrn, INPUT_PULLUP); pinMode(swBlk, INPUT_PULLUP); pinMode(swWht, INPUT_PULLUP); ms = millis(); // 初期化終わり } void loop() { // put your main code here, to run repeatedly: int j; // 4個のスイッチが10mSec間隔で2回LOWなら押されたと判定してフラグをたてる if (digitalRead(swOrg) == LOW) { delay(10); if (digitalRead(swOrg) == LOW) { swOrgPush = true; } } if (digitalRead(swGrn) == LOW) { delay(10); if (digitalRead(swGrn) == LOW) { swGrnPush = true; } } if (digitalRead(swBlk) == LOW) { delay(10); if (digitalRead(swBlk) == LOW) { swBlkPush = true; } } if (digitalRead(swWht) == LOW) { delay(10); if (digitalRead(swWht) == LOW) { swWhtPush = true; } } loop: // いづれかのボタンが押されていたときの処理 // スクロール開始から2秒くらいはスクロール中かもしれないのでやらない。 if (millis() > (ms + 2000)) { if (swWhtPush) { // 白ボタンが押されていたらじゃんけんゲームを行う。 JankenPon(); } if (swBlkPush) // 黒が押されていた時の処理。 { // グーを書いてみる vfdCursor(0, 0); vfdCls(); for (i = 0; i < _Gu_leng; i++) { Serial.write(_Gu_code[i]); // delay(1); } swBlkPush = false; while (digitalRead(swBlk) == LOW) {} ms = millis(); } if (swGrnPush) // 緑が押されていた時の処理。 { swGrnPush = false; while (digitalRead(swGrn) == LOW) {} ms = millis(); } if (swOrgPush) // 橙が押されていた時の処理。 { swOrgPush = false; while (digitalRead(swOrg) == LOW) {} ms = millis(); } } // // loop()は待たずに繰り返しており,3秒毎にif以下を実行して表示を更新する。 // if ( millis() > (ms + 3000)) { // 白ボタンを押して、じゃんけんしましょ。 unsigned char text000[ 39] = { 0x94, 0x92, 0x83, 0x7b, 0x83, 0x5e, 0x83, 0x93, 0x82, 0xf0, 0x89, 0x9f, 0x82, 0xb5, 0x82, 0xc4, 0x81, 0x41, 0x82, 0xb6, 0x82, 0xe1, 0x82, 0xf1, 0x82, 0xaf, 0x82, 0xf1, 0x82, 0xb5, 0x82, 0xdc, 0x82, 0xb5, 0x82, 0xe5, 0x81, 0x42, 0x00 }; vfdHScrollMode(); vfdHSSpeed(1); vfdCls(); vfdKanjiOn(); delay(20); vfdCursor(0, 1); delay(10); j = 0; while (text000[j] != 00) { Serial.write(text000[j++]); delay(55); if (digitalRead(swWht) == LOW) { delay(10); // 10mSec待ってもう一度読んでみる if (digitalRead(swWht) == LOW) { swWhtPush = true; if ((j % 2) == 1) { Serial.write(text000[j]); // 2バイト文字を完結しておく。 goto loop; //JankenPon(); } } } } ms = millis(); //3秒タイマーの再セット } // Loopはここまでです。 } void JankenPon() { // すべてのボタンが押されていたときの、じゃんけんゲーム int MyTe; // 自分の手 0=グー、1=チョキ、2=パー unsigned int k = 0; // 自分の手を決める乱数用変数 int j; //作業用 bool Gu = false, Choki = false, Pa = false; // 相手の手 黒:グー、緑:チョキ、橙:パー int syousya; mouitido: // 引き分けでもう一度のときここに戻る vfdCursor(0, 0); vfdCls(); //vfdKanjiOn(); //じゃんけん unsigned char text010[ 11] = { 0x82, 0xb6, 0x82, 0xe1, 0x82, 0xf1, 0x82, 0xaf, 0x82, 0xf1, 0x00 }; j = 0; while (text010[j] != 00) { Serial.write(text010[j++]); delay(1); } j = 0; // すべてのボタンを離すのを待つ jはチャタリングの除去用 while ( j < 10) { if (digitalRead(swBlk) == LOW or digitalRead(swGrn) == LOW or digitalRead(swOrg) == LOW ) { j = 0; } else { delay(1); j++; } k++; delay(1); } // 自分の手を決める MyTe = k % 3; syousya = 0; ms = millis(); //どれかのボタンを押すのを待つ for (j = 0; j < 10; ) { Gu = digitalRead(swBlk) == LOW; Choki = digitalRead(swGrn) == LOW; Pa = digitalRead(swOrg) == LOW; if (Gu or Choki or Pa) { delay(1); j++; } else { j = 0; } delay(1); if (millis() > (ms + 10000)) { // ボタンが押されず10秒経過したら終わる。 j = 20; MyTe = 3; syousya = 3; } } // 自分の手の表示 vfdCursor(0, 0); vfdCls(); if (MyTe == 0) { vfdCursor(0, 0); vfdCls(); for (i = 0; i < _Gu_leng; i++) { Serial.write(_Gu_code[i]); // delay(1); } } else if (MyTe == 1) { vfdCursor(0, 0); vfdCls(); for (i = 0; i < _Choki_leng; i++) { Serial.write(_Choki_code[i]); // delay(1); } } else if (MyTe == 2) { vfdCursor(0, 0); vfdCls(); for (i = 0; i < _Pa_leng; i++) { Serial.write(_Pa_code[i]); // delay(1); } } if ( MyTe < 3) { // 相手の手の表示 vfdCursor(96, 0); if (Gu) { for (i = 0; i < _Gu_leng; i++) { Serial.write(_Gu_code[i]); // delay(1); } } else if (Choki) { for (i = 0; i < _Choki_leng; i++) { Serial.write(_Choki_code[i]); // delay(1); } } else if (Pa) { for (i = 0; i < _Pa_leng; i++) { Serial.write(_Pa_code[i]); // delay(1); } } } // "ポン!"と表示 unsigned char text101[ 6] = { // SJISでポン!のコード 0x83, 0x7c, 0x83, 0x93, 0x81, 0x49 }; vfdCursor(40, 1); // 表示位置を決めて for (j = 0; j < 6; j++) // 書く { Serial.write(text101[j]); } delay(1000); // タメを作ります。 // 勝敗判定 //syousya = 0; // 1:私、2:あなた、0:引き分け switch (MyTe) { case 0: // グー if (Gu) { syousya = 0; //引き分け } else if (Choki) { syousya = 1 ; //私 } else if (Pa) { syousya = 2; // あなた } break; case 1: // チョキ if (Gu) { syousya = 2; // あなた } else if (Choki) { syousya = 0; // 引き分け } else if (Pa) { syousya = 1; //私 } break; case 2: // パー if (Gu) { syousya = 1; //私 } else if (Choki) { syousya = 2; // あなた } else if (Pa) { syousya = 0; //引き分け } break; } //勝者表示 // Serial.print("引き分け"); unsigned char text001[9] = { 0x88, 0xf8, 0x82, 0xab, 0x95, 0xaa, 0x82, 0xaf, 0x00}; // Serial.print("私の勝ち"); unsigned char text002[9] = { 0x8e, 0x84, 0x82, 0xcc, 0x8f, 0x9f, 0x82, 0xbf, 0x00}; // Serial.print("貴方の勝ち"); unsigned char text003[11] = { 0x8b, 0x4d, 0x95, 0xfb, 0x82, 0xcc, 0x8f, 0x9f, 0x82, 0xbf, 0x00}; vfdKanjiOn(); delay(20); vfdCursor(35, 1); delay(10); j = 0; switch (syousya) { case 0: while (text001[j] != 00) { Serial.write(text001[j++]); delay(1); } break; case 1: while (text002[j] != 00) { Serial.write(text002[j++]); delay(1); } break; case 2: vfdCursor(20, 1); delay(10); while (text003[j] != 00) { Serial.write(text003[j++]); delay(1); } break; } if (MyTe != 3) { delay(1000); } //終了の処理 // どれかのボタンが押されている間待つ。(=すべて離すのを待つ) while ( (digitalRead(swWht) == LOW) or (digitalRead(swBlk) == LOW) or (digitalRead(swGrn) == LOW) or (digitalRead(swOrg) == LOW)) {} delay(50); swOrgPush = false; swGrnPush = false; swBlkPush = false; swWhtPush = false; ms = millis(); //タイマー初期化 if (syousya == 0) { goto mouitido; } //引き分けはもう一度 // ゲームルーチンはここまで } // 以下色々な関数です。 // VFDに1バイト書き込む void vfdWrite(unsigned char onebyte) { // while( digitalRead(RTS) == LOW){} Serial.write( onebyte); } // 文字の横スクロール速度を設定する void vfdHSSpeed(int n) { Serial.write(0x1f); Serial.write(0x73); Serial.write(n); } // VFD を文字の横スクロール設定にする void vfdHScrollMode() { Serial.write(0x1f); Serial.write(0x03); } // VFDをSJIS漢字表示モードにする。文字寸は16x16ドット void vfdKanjiOn() { Serial.write(0x1f); Serial.write(0x28); Serial.write(0x67); Serial.write(0x01); Serial.write(0x02); Serial.write(0x1f); Serial.write(0x28); Serial.write(0x67); Serial.write(0x02); Serial.write(0x01); Serial.write(0x1f); Serial.write(0x28); Serial.write(0x67); Serial.write(0x0f); Serial.write(0x00); delay(10); } // VFDのユーザーウインドウをベースウインドウにする。 void vfdBaseWin() { Serial.write(0x1f); Serial.write(0x28); Serial.write(0x77); Serial.write(0x01); Serial.write(0x00); } void vfdUserWin() { Serial.write(0x1f); Serial.write(0x28); Serial.write(0x77); Serial.write(0x02); Serial.write(0x01); Serial.write(0x01); Serial.write(0x28); Serial.write(0x00); Serial.write(0x00); Serial.write(0x00); Serial.write(0x3c); Serial.write(0x00); Serial.write(0x04); Serial.write(0x00); Serial.write(0x1f); Serial.write(0x28); Serial.write(0x77); Serial.write(0x01); Serial.write(0x01); } // スクロール表示アクションの実行(左方向) void vfdScroll() { Serial.write(0x1f); Serial.write(0x28); Serial.write(0x61); Serial.write(0x10); Serial.write(0x20); Serial.write(0x00); Serial.write(0x10); Serial.write(0x00); Serial.write(0x01); } // スクロール表示アクションの実行(右方向) void vfdScroll_R() { Serial.write(0x1f); Serial.write(0x28); Serial.write(0x61); Serial.write(0x10); Serial.write(0xe0); Serial.write(0x03); Serial.write(0x10); Serial.write(0x00); Serial.write(0x01); } // VFDのカーソル位置設定・移動 void vfdCursor(int X, int Y) { Serial.write(0x1f); Serial.write(0x24); Serial.write(X % 256); Serial.write(0); Serial.write(Y % 256); Serial.write(0); } // VFDのカーソルをホームポジション(左上)に移動 void vfdHome() { Serial.write(0x0b); } // FlashROMに登録された画像を呼び出す。 // n : F-ROMアドレス/512 x:画像の横方向画素数 void callBmp(int n, int x) { Serial.write(0x1f); Serial.write(0x28); Serial.write(0x66); Serial.write(0x10); Serial.write(0x01); Serial.write(0x00); Serial.write(n * 2); Serial.write(0x00); Serial.write(0x04); Serial.write(0x00); Serial.write(x % 256); Serial.write(x / 256); Serial.write(0x04); Serial.write(0x00); Serial.write(0x01); } // VFD画像消去 実行後はカーソルは左上に移動する void vfdCls() { Serial.write(0x0c); }