前回、SerialポートをBlynkに渡すために、自前の文字出力はLCD画面に向けてみました。毎回、泥縄で出力しているのも辛い。SerialポートのようにLCDでも垂れ流しで文字列出力していけると良い。ついでにWio Terminal の5Wayスイッチも使えると尚善。どこか漁ったら良いライブラリなどあるのかもしれませんが、今回は自前で制作。
※「IoT何をいまさら」投稿順Indexはこちら
まず、Wio Terminalの320×240サイズのLCD画面に表示するテキストについては、サイズ=2で固定としてしまいました。これは、
-
- 老眼の目でもまずまず読めるサイズ
- それでいて、横26文字x15行の表示が可能
という選択であります。ぶっちゃけ1行の高さ16ドットと目出度いキリの良い数字でもあります。後でM5Stackでも同じことをやりたいと思っているのですが、画面の物理的なサイズは似ているのでそのときも有効な筈。一応、色くらいは変更できるようにいたしましたが、単なる文字列出力なので、黒地に白文字で運用の予定。
さて、シリアル端末に文字列を垂れ流すようにLCD画面にも垂れ流すようにするのですが、逆に入力も受け付けたい。Wio Terminal の場合、
-
- 上端の押しボタンスイッチ、A、B、Cの3個
- 正面右下の青色の4方向+PRESSの「5WAYスイッチ」
が使えそうです。A、B、Cの押しボタンはM5Stackにもあるので、こちらは別の用途にとっておくことにいたしました。とりあえずWio Terminal 使った時のUIを少しでも改善すべく、5 way スイッチを使ってみました。これは、簡略なJOYSTICK風というか、ThinkPadのトラックポイント風というか、1つのキーで上下左右を入力できるもの。4方向に加えて押し込むことも検出できるので5way。ソフトウエア的には、それぞれの方向に独立したスイッチが存在しているように見えます。5way switchについての詳しいことはこちら(SeeedStudio社の解説。)
画面上15行使えるので、上の13行を出力文字列の表示に割り当て、下の2行を入力用のUIで使うようにいたしました。最下行がメッセージを表示する「ステータス行」、その上が入力を促す「プロンプト行」であります。
後々、別なプロジェクトでも使えるように、Arduinoのスケッチ上、画面出力とスイッチの入力を行うファイルと、アプリのメインのファイルは分けてみました。どちらも .ino 拡張子のファイル。まずは、画面出力とキー入力側。べたなCスタイル。
LCD画面スクロール出力 と 5 way switch プロンプト
#include "TFT_eSPI.h" #define LCD_BACKLIGHT (72Ul) #define TXT_MAXBUF (28) #define TXT_MAXLEN (26) #define TXT_MAXLINE (13) TFT_eSPI tft; uint16_t txtBGcolor; uint16_t txtFGcolor; uint8_t txtSize; char txtDisplayBuffer[TXT_MAXLINE][TXT_MAXBUF]; uint32_t txtCurrentRow; uint32_t txtCurrentCol; void txtInit(uint16_t fg, uint16_t bg) { txtFGcolor = fg; txtBGcolor = bg; txtSize = 2; tft.begin(); tft.setRotation(3); tft.setTextColor(fg, bg); tft.setTextSize(txtSize); tft.fillScreen(txtBGcolor); txtCLS(); digitalWrite(LCD_BACKLIGHT, HIGH); } void txtTest() { tft.drawString("01234567890123456789012345", 2, 0); for (int y=16; y <= 192; y+=48) { tft.drawString("abcdefghijklmnopqrstuvwxyz", 2, y); tft.drawString("ABCEDFGHIJKLMNOPQRSTUVWXYZ", 2, y+16); tft.drawString("_ ?><]:;[@`\\|~~==)('&%$#!/", 2, y+32); } } void txtCLS() { tft.fillRect(0,0,319,207,txtBGcolor); txtCurrentRow = 0; txtCurrentCol = 0; } void txtPrint(const char *s) { char *ptr; if (txtCurrentRow >= TXT_MAXLINE) { tft.fillRect(0,0,319,207,txtBGcolor); for (int row = 0; row < (TXT_MAXLINE - 1); row++) { strlcpy(txtDisplayBuffer[row],txtDisplayBuffer[row+1], TXT_MAXBUF); tft.drawString(txtDisplayBuffer[row], 2, row * 16); } txtCurrentRow = TXT_MAXLINE - 1; } ptr = strncpy(txtDisplayBuffer[txtCurrentRow], s, TXT_MAXLEN); txtDisplayBuffer[txtCurrentRow][TXT_MAXLEN] = '\0'; tft.drawString(ptr, 2, txtCurrentRow * 16); txtCurrentRow++; } void fiveWaySwitchInit() { pinMode(WIO_5S_UP, INPUT_PULLUP); pinMode(WIO_5S_DOWN, INPUT_PULLUP); pinMode(WIO_5S_LEFT, INPUT_PULLUP); pinMode(WIO_5S_RIGHT, INPUT_PULLUP); pinMode(WIO_5S_PRESS, INPUT_PULLUP); } int fiveWayPrompt() { int retval = 0; tft.fillRect(0,208,319,223,txtBGcolor); tft.drawString("U) D) L) R) P)ress >>>", 2, 13*16); if (digitalRead(WIO_5S_UP) == LOW) { retval = 1; } else if (digitalRead(WIO_5S_DOWN) == LOW) { retval = 2; } else if (digitalRead(WIO_5S_LEFT) == LOW) { retval = 3; } else if (digitalRead(WIO_5S_RIGHT) == LOW) { retval = 4; } else if (digitalRead(WIO_5S_PRESS) == LOW) { retval = 5; } delay(200); return retval; } void statusLine(const char *stat) { tft.fillRect(0,224,319,239,txtBGcolor); tft.drawString(stat, 2, 14*16); }
続いて、それを呼び出すメイン側。例題プログラムの操作としては、
-
- スイッチを上に押すと、数字、英小文字、英大文字、記号などを画面一杯にサンプル表示
- スイッチを下に押すと、下に押したことだけ表示
- スイッチを左に押すと、カウンタをインクリメントしつつ、その数字を画面上垂れ流し(スクロール)出力
- スイッチを右に押すと、カウント停止、垂れ流し出力も停止
- スイッチを押し込むと画面クリア
であります。実際にはループの中でdelayで3秒待っているので、スイッチを認識するのは3秒に1回。スイッチ操作したら反応するまでちょっと押したまま待っている必要あり。また、カウンタの更新も3秒に一回。この辺はループの作り方次第であります。メインの .ino ファイルはこちら。
アプリ・メイン(サンプル)
#include "TFT_eSPI.h" int counter; void setup() { txtInit(TFT_WHITE, TFT_BLACK); fiveWaySwitchInit(); counter = 0; } void loop() { bool countFlag; char buf[28]; switch (fiveWayPrompt()) { case 1: txtCLS(); statusLine("UP) CHAR_TEST_PATTERN"); txtTest(); break; case 2: txtCLS(); statusLine("DOWN) "); break; case 3: txtCLS(); statusLine("LEFT) START COUNTER"); counter = 1; break; case 4: txtCLS(); statusLine("RIGHT) END COUNTER"); counter = 0; break; case 5: txtCLS(); statusLine("PRESS) "); break; } if (counter > 0) { sprintf(buf, "%d", counter++); txtPrint(buf); } delay(3000); }
こんなものでも、画面にダラダラと出力して、入力を受け付けられるようになったので、実験するときは、楽になったかもです。
スクロール出力しているところがこちら。