前書き
実はGUIsliceの記事は書くかどうか迷っていたのですが、使ってみるとかなりの高機能で、とても覚えきれない!ということで備忘録を兼ねて書くことにしました。
果たしてうまくまとめられるかですが・・(^_^;)
目的
機械制御等のマンマシンインターフェースとした利用を目的とするもの。
キーエンス、オムロン、三菱等で市販されているタッチパネルディスプレイに近いことを実現したい。
機械制御等は別のマイコン基板で行い、ESP32とはUART接続する。
システム構成
MPU:ESP32Divkit
Display:ILI9431 パラレル入力ディスプレイ、抵抗膜式タッチパネルスイッチ付
開発ソフト
画面構成等:GUIslice Builder
イメージデータ作成 : GUIslice Image2C
IDE : Arduino IDE 2.0.4
GUIslice BuilderはGithubから入手します。
https://github.com/ImpulseAdventure/GUIslice-Builder
前提条件
以前の記事での準備が終わり正常に動作していること。
SPIタイプのLCDでも大差はないと思います。
とにかくGUIsliceのスケッチ例が動くことが前提となります。
最低でも「daiag_and_touch_calib」は動く必要があります。
キャリブレーションできてないとそもそも使えません。
GUIslice画面と使い方
プロジェクトは [File]→[NEW]で作成します。
すると↓の画面になります。
Tree Viewで、E_PROJECT_OPTIONSを選択し、ライブラリの選択、画面の大きさ等を設定します。
自分の場合Graphics LibraryはTFT_eSPIを指定しています。
実際の作成画面はこんな感じ
ボタンに日本語使っていますが、これはGUIslice Image2Cを使って作成したデータを使っています。
イメージファイルをSDカードに置くやりかたもありますが、表示が遅く使い物にならない場合があります。(用途にもよります)
ボタンをクリックしたら違うページに飛ばせる場合は Property View で [Jamp Page ENUM]に表示させたいPageのENUMをコピペすればOKです。
日本語の表示にこだわらないのであれば、Text Button で作成したほうがメモリ消費量は大幅に減ります。
Tree Viewでページ毎にElement(ボタン等のこと)がまとめられているので、ここクリックすれば目的のPageのコントロールを選択できます。これはデバック時にも役立ちます。
保存とコード作成
各ページの画面が出来上がったらプロジェクトの保存と Generate Code をfileメニューから実行します。
するとプロジェクトフォルダに inoファイルができますのでArduino IDE等で開きます。
ここまでである程度のスケッチを作ってくれます。
あとはコードを見ながら必要なコードを書けばよいわけです。
生成したコードをそのまま実行もできますので、コードを書き込む前に画面構成を作っちゃったほうがいいかもですね。
ユーザーが書き足したコードは、その後でGUIslice Builder で編集を行っても書き足したコードに影響はないみたいです。ただし、削除したエレメントのコードは消えます。
作成中のコードはこんな感じ
hogehoge.ino
//<App !Start!>
// FILE: [hogehoge.ino]
// Created by GUIslice Builder version: [0.17.b20]
//
// GUIslice Builder Generated File
//
// For the latest guides, updates and support view:
// https://github.com/ImpulseAdventure/GUIslice
//
//<App !End!>
// ------------------------------------------------
// Headers to include
// ------------------------------------------------
#include "hogehoge_GSLC.h"
// ------------------------------------------------
// Program Globals
// ------------------------------------------------
// Save some element references for direct access
//<Save_References !Start!>
gslc_tsElemRef* m_pElemInTxt2 = NULL;
gslc_tsElemRef* m_pElemProgress1 = NULL;
gslc_tsElemRef* m_pElemKeyPadAlpha= NULL;
//<Save_References !End!>
// Define debug message function
static int16_t DebugOut(char ch) { if (ch == (char)'\n') Serial.println(""); else Serial.write(ch); return 0; }
// ------------------------------------------------
// Callback Methods
// ------------------------------------------------
// Common Button callback
bool CbBtnCommon(void* pvGui,void *pvElemRef,gslc_teTouch eTouch,int16_t nX,int16_t nY)
{
// Typecast the parameters to match the GUI and element types
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
// ボタンが押されている間、ここが繰り返し呼ばれる
if ( eTouch == GSLC_TOUCH_UP_IN ) {
// From the element's ID we can determine which button was pressed.
// ボタンから指が離れた時に通る
switch (pElem->nId) {
//<Button Enums !Start!>
case E_ELEM_START:
gslc_SetPageCur(&m_gui, E_PG2);
break;
case E_ELEM_MAINTENANCE:
gslc_SetPageCur(&m_gui, E_PG6_MENTENANCE);
Serial1.print('6');
break;
case E_ELEM_SETTING:
gslc_SetPageCur(&m_gui, E_PG5_SETTING);
Serial1.print('4');
break;
case E_ELEM_MANUAL:
gslc_SetPageCur(&m_gui, E_PG4_MANU);
Serial1.print('2');
break;
case E_ELEM_AUTOSTART:
gslc_SetPageCur(&m_gui, E_PG3_AUTO);
break;
case E_ELEM_IMAGEBTN19:
gslc_SetPageCur(&m_gui, E_PG2);
Serial1.print('#');
break;
case E_ELEM_WAITSTART:
gslc_SetPageCur(&m_gui, E_PG7_RUNMONI);
Serial1.print('1');
break;
case E_ELEM_IMAGEBTN20:
gslc_SetPageCur(&m_gui, E_PG2);
Serial1.print('#');
break;
case E_ELEM_BTN1:
gslc_SetPageCur(&m_gui, E_PG8_MANU2);
break;
case E_ELEM_BTN4_SHOOTER:
Serial1.print('4');
break;
case E_ELEM_BTN5_GATE:
gslc_SetPageCur(&m_gui, E_PG9_GATE_CONTORL);
Serial1.print('1');
break;
case E_ELEM_BTN6_CUTTER:
gslc_SetPageCur(&m_gui, E_PG12_CUTTER_CTRL);
Serial1.print('2');
break;
case E_ELEM_BTN7_HOLD:
gslc_SetPageCur(&m_gui, E_PG11_HOLD);
Serial1.print('5');
break;
case E_ELEM_IMAGEBTN21:
gslc_SetPageCur(&m_gui, E_PG2);
Serial1.print('#');
break;
case E_ELEM_IMAGEBTN22:
gslc_SetPageCur(&m_gui, E_PG2);
Serial1.print('#');
break;
case E_ELEM_IMAGEBTN30:
gslc_SetPageCur(&m_gui, E_PG3_AUTO);
break;
case E_ELEM_TEXTINPUT2:
// Clicked on edit field, so show popup box and associate with this text field
gslc_ElemXKeyPadInputAsk(&m_gui, m_pElemKeyPadAlpha, E_POP_KEYPAD_ALPHA, m_pElemInTxt2);
break;
case E_ELEM_IMAGEBTN31:
gslc_SetPageCur(&m_gui, E_PG2);
break;
case E_ELEM_BTN3:
gslc_SetPageCur(&m_gui, E_PG4_MANU);
break;
case E_ELEM_BTN8_FEEDER:
gslc_SetPageCur(&m_gui, E_PG10_FEEDER);
Serial1.print('8');
break;
case E_ELEM_BTN9_INPUT_MONITOR:
break;
case E_ELEM_BTN10_SENSOR:
break;
case E_ELEM_IMAGEBTN32:
gslc_SetPageCur(&m_gui, E_PG4_MANU);
Serial1.print('C');
break;
case E_ELEM_BTN12_GATE_OPEN:
Serial1.print('A');
break;
case E_ELEM_BTN13_GATE_CLOSE:
Serial1.print('B');
break;
case E_ELEM_IMAGEBTN33:
gslc_SetPageCur(&m_gui, E_PG8_MANU2);
Serial1.print('C');
break;
case E_ELEM_BTN14_FEED:
Serial1.print('A');
break;
ボタンが押されるとbool CbBtnCommon(void* pvGui,void *pvElemRef,gslc_teTouch eTouch,int16_t nX,int16_t nY)
が押されている間繰り返しCallされています。
で、ボタンから指が離れると
if ( eTouch == GSLC_TOUCH_UP_IN ) {
の{ }内の処理が実行されます。
指がボタンに触れたタイミングを使いたい場合は
if ( eTouch == GSLC_TOUCH_DOWN_IN ){
とすれば良い。
このプログラムでは主に Serial1.print('A');
という感じで外部のマイコンにUARTで命令を送信しています。
指を押した時、あるいは離した時等、イベント発生タイミングを変えたい場合は GSLC_TOUCH_UP_IN
を変更すれば良さそうです。
(2023/06/06追記)
但し、パネルをタッチしたまま、指がボタン領域からズレ、ズレたまま指がパネルから離れた場合はこのイベントは発生しません。
指がボタン領域からズレた時のイベントを使用する場合は
else if (eTouch == GSLC_TOUCH_MOVE_OUT) {
とし、処理を記述します。
このイベント使用する場合の注意点として、指がボタン領域からズレ、パネルに触れたまま再び押下したボタン領域に戻り、指が離れた場合は
GSLC_TOUCH_MOVE_OUT
が有効になります。
ここまでくると「イジワル操作対策」という感じになりますが、不特定多数が使用するアプリケーションや、機械制御等の安全性が要求されるアプリケーションの場合はこれくらいやっておいたほうが良いです。
イベントの種類は GUIslice_ref.pdf の571ページあたり、
9.45.3.19 gslc_teToch に書いてあります。
あとは配置したボタンのENUMで検索し、該当する case E_ELM_### と break; の間にコード書けば良いといことです。
Visual BASICや等でGUIを使ったプログラミング経験があればわかりやすいかもです。
今回はボタン程度しか使っていませんが、テキスト入力ができたり、アナログメーターやプログレスバーなんかも使えるので、応用範囲は広そうです。
いやぁ、すごい時代になりましたね😁