【M5Stack_Core2】『タッチパネルの実験』として形にしてみた。
ボタンの方は、調整が難しい。
こんなの作ってみたよ。
M5Stack Core2でタッチパネルの実験やってみた pic.twitter.com/oQGu3jFvdZ
— ちよこ (@chiyo_go) 2020年10月18日
【M5Stack_Core2】日本語周り
そういえば、日本語入出力に関して、M5Core2で試してなかったのでやってみた。
過去日本語文字をディスプレーに表示することはしていたが、Serial通信を使って入出力してなかったのでその辺の検証
やっていることPCで日本語手打ち⇔Arduino IDEモニタで転送⇔M5Core2⇔液晶に日本語表示
- 日本語(UTF-8) Serial通信の送受信
- 液晶画面に日本語表示
まずは、こんな感じTeratermだと入力した文字が逐次送信されてしまうため打ち間違いがあるとCore2側で編集しないといけないがまだその環境がまだ整っていない。
逆にArduinoIDEでは、入力BOXを改行キーで1ライン送信なのでその辺は日本語入力やparameter入力としては便利かもしれない。
動かしているスケッチは下記のモノ
// // smoothFont.ino // 日本語フォントを使ってみる for M5Core2 // 2020/10/16 ver0.1 chiyo.h // #define ESP32 #include <M5Core2.h> #include <utility/Sprite.h> #include <stdio.h> // Create Sprite object "img" with pointer to "tft" object // M5.Lcdをsprite化する TFT_eSprite img = TFT_eSprite(&M5.Lcd); int rxBufferClear(){ int sBuf = Serial.available(); int dat; // read dummy. for(int i=0;i<sBuf;i++){ dat = Serial.read(); } return sBuf; } char inKey(){ int rSize = Serial.available(); if(rSize == 0){ return 0; } char c = Serial.read(); rSize--; for(int i=0;i<rSize;i++){ char d = Serial.read(); } return c; } int serialOverflowFlag = false; // Serial通信でオーバーフロー起こしているかどうか // UTF-8のcode並びの先頭であるか? int utf8HeaderCheck(char c){ int cByte; if(((c&0x80)^0x00)==0x00){cByte = 1;} // 1byte else if(((c&0xe0)^0xe0)==0x00){cByte = 2;} // 2bytes else if(((c&0xf0)^0xf0)==0x00){cByte = 3;} // 3bytes else if(((c&0xf8)^0xf8)==0x00){cByte = 4;} // 4bytes //else if((c==0xfe)||(c==0xff)){cByte = 0;} // BOM? else cByte = -1; // 2~4bytes の続きcode return cByte; } // あまり検証できていないUTF-8を使った入力 void readSerial(char* buff){ int cSize=0; // 初期化であるが毎回行われる // rxBuffer確認 int rSize = Serial.available(); //Serial.printf("rxbuff size:%d\n",rSize); if(rSize==0){ buff[0] = 0; //NULL return; // 読み取りデータ無し } // 外部から確認してチェックされるまでそのままアラート出しておく if((serialOverflowFlag == true) || (rSize == 256)) serialOverflowFlag = true; // 残りrxBufferが3bytes while(rSize>3){ buff[cSize] = Serial.read(); cSize++; rSize--; } // 残り3bytes処理 while(rSize>0){ if(utf8HeaderCheck((char)Serial.peek())<=rSize){ buff[cSize] = Serial.read(); cSize++; rSize--; }else{ break; } } buff[cSize] = 0; //NULL return; // 読み取りデータ無し } void setup() { M5.begin(); // 外部I2C off Serial.printf("\n"); Serial.flush(); rxBufferClear(); // serial read clear. delay(50); yield(); // 液晶ディスプレ初期化 M5.Lcd.fillScreen(TFT_WHITE); // 画面塗りつぶし M5.Lcd.setTextColor(BLACK); // printカラー指定 M5.Lcd.setTextSize(1); // printサイズ指定 M5.Lcd.clear(WHITE); // ? M5.Lcd.setCursor(0, 0); // printカーソル指定(初期化:左上) // スプライト初期化 img.createSprite(320,40); // スプライト作成 img.fillSprite(TFT_BLACK); // スプライト塗りつぶし img.setTextColor(WHITE); // printカラー指定 img.setTextSize(1); // printサイズ指定 // "/data/font/NotoSansJPR36.vlw" をSD置きアップロードする String fnt = "data/font/NotoSansJPR36"; //SPIFFS.begin(); //M5.Lcd.loadFont(fnt, SPIFFS); // flash memory M5.Lcd.setTextFont(2); M5.Lcd.setTextSize(2); M5.Lcd.println(" loading japanese font."); M5.Lcd.loadFont(fnt, SD); // SD card img.loadFont(fnt, SD); M5.Lcd.clear(WHITE); // ? M5.Lcd.setCursor(0,4); //Serial.printf("%d,%d\n",M5.Lcd.getCursorX(),M5.Lcd.getCursorY()); M5.Lcd.println(" 日本語のテスト!"); M5.Lcd.println(" Japanese font."); M5.Lcd.setTextSize(1); // printサイズ指定 delay(10); yield(); } void loop() { static char buff[256]; //char c = inKey(); readSerial(buff); if(buff[0] !=0){ img.fillSprite(BLACK); img.setCursor(2,2); img.printf(buff); img.pushSprite(0,160); Serial.printf("%s\n",buff); } delay(1000); yield(); }
まあ、いつものようにその場でガシガシ書いたものなので動作検証用くらいに。
まとめ
- 表示させている日本語Fontは、processingで作ったモノ。ESP32環境ではttf/otfフォントライブラリが無いのでベクトルフォントではなくラスターフォントになる。よって、Fontを変換しているわけ。
- ESP32では、日本語のような扱う文字数が多い文字フォントはメインメモリには入らない。SDカードやSPIFSを使ってフラッシュからファイルアクセスする形式になる。
- 文字数も多いし少し大きめのフォントサイズにするとそれだけのモノクロ無圧縮BMPを作ることになりそれを収めるファイルは大きさになる。
- 今回使った、Noto Sans JP Regularの36ポイントで0~0xFFFFコードで取り込める数が16057/17850グリフで約16MB大きい。元のサイズが4MBなので4倍に膨らんでいる。
- 17850文字に足りないのは、UNICODEにも異体字セレクタを扱える。旧字や異字などと呼ばれるやつ斎藤さんとか渡邊さんとかそういう同じコードで違うグリフも掲載しているフォントだからっぽい。
- 初め、0xFFFF以降のコード化と思ったら、vlwフォーマットは32bits長を対応していることがわかった。しかし、TFT_eSPIのUNICODEの扱いが16bits長っぽいので1面以降を変換することにいまのところ意味(デメリットしか)がない。
- processingのフォント作りは、ツールのやつでなくてTFT_eSPIの作者のスクリプトの改良版を使っている。SJISの文字全部対応させようとするとどの区域なのか大変そうなのでフォントが持っているCMAPをsearchするようにしたのが下のスクリプト。
【M5Stack_Core2】Serial 入力制御
目的を失ってキャレット制御や、BASICやXon/Xoffなどとっ散らかってしまったのでシンプルに入力を
今回わかったこと
M5Core2で使われている、ESP-IDF3.3はRXbufferは256。FIFOでいったん受信したデータは取り出さない限り消えない。
また、bufferをオーバーするとそのデータは消えていく。Serialクラスでは、Xon/Xoffをサポートしていない。ESP32のハードウエアでは
サポートしているっぽい。
// // inkey // // 2020/10/11 chiyo.h // #include <M5Core2.h> #include <utility/Sprite.h> #include <stdio.h> #define ESP32 TFT_eSprite spr = TFT_eSprite(&M5.Lcd); int rxBufferClear(){ int sBuf = Serial.available(); int dat; // read dummy. for(int i=0;i<sBuf;i++){ dat = Serial.read(); } return sBuf; } void setup() { M5.begin(); // 外部I2C off Serial.printf("\n"); Serial.flush(); rxBufferClear(); // serial read clear. delay(50); yield(); M5.Lcd.fillScreen(WHITE); M5.Lcd.setTextColor(BLACK); M5.Lcd.setTextSize(2); M5.Lcd.clear(WHITE); M5.Lcd.setCursor(10, 10); M5.Lcd.printf("Serial Test!"); Serial.println("Serial input test."); Serial.print(">"); // スプライト定義 spr.setColorDepth(8); // カラーモード spr.createSprite(320, 240); // 大きさ spr.fillSprite(TFT_WHITE); delay(50); yield(); } char inKey(){ int rSize = Serial.available(); if(rSize == 0){ return 0; } char c = Serial.read(); rSize--; for(int i=0;i<rSize;i++){ char d = Serial.read(); } return c; } uint32_t color = TFT_BLACK; void selectColor(uint32_t c){ switch(c){ case 'B': color = TFT_BLUE; break; case 'R': color = TFT_RED; break; case 'G': color = TFT_GREEN; break; case 'K': color = TFT_BLACK; break; } } void loop() { char c = inKey(); if(c != 0){ Serial.println(c); selectColor(c); // PC端末の押したキーによって制御を変える spr.drawChar(0,0,c,color,TFT_WHITE,4); spr.pushSprite(64,64); spr.fillSprite(TFT_WHITE); Serial.print('>'); } delay(10); yield(); }
まとめ:毎回parameter変更してコンパイルしなおすよりキーボード制御でモード切替をして動かすほうがいいかな。
【M5Stack_Core2】Serial I/F周り その2
そういえば、昔GPIO0をHにしておけばいいという感じの記事を見た気がした。
しかし、これはRESETを回避する話ではなかったようだ。
今回分かったとこスピーカーを使うとMPUのGPIO0と/DTSがショートレベルになること。あまりよろしくない感じ
【M5Stack_Core2】Serial I/F周り
シリアル入出力を自由に使えないとdebugがはかどらないので少し調べた。
なぜか回路検証をしていた。
test_serial.ino
// // Serial 周り確認 // // 2020/10/11 chiyo.h // #include <M5Core2.h> #include <utility/Sprite.h> #include <stdio.h> #include <touch.h> #define ESP32 TFT_eSprite spr = TFT_eSprite(&M5.Lcd); int gpio0 = 0; void setup() { M5.begin(); // 外部I2C off Serial.printf("\n"); Serial.flush(); delay(50); yield(); M5.Lcd.fillScreen(WHITE); M5.Lcd.setTextColor(BLACK); M5.Lcd.setTextSize(2); M5.Lcd.clear(WHITE); M5.Lcd.setCursor(10, 10); M5.Lcd.printf("Serial Test!"); // スプライト定義 spr.setColorDepth(8); // カラーモード spr.createSprite(320, 240); // 大きさ spr.fillSprite(TFT_WHITE); // Serial動作抑制 Serial.end(); // WDT無効 disableCore0WDT(); disableCore1WDT(); pinMode(gpio0, INPUT); // 入力モード delay(50); yield(); } void loop() { int val = digitalRead(gpio0); spr.drawNumber(val, 0, 0, 4); spr.pushSprite(0,0); // グラフ spr.fillSprite(TFT_WHITE); //Serial.println(""); delay(200); yield(); }
GPIO0は、I2Sスピーカーにもつながっている。 ちょっと調べた感じでは、モノ出力スピーカに対してCTRL信号で、ICのON/OFF及びI2Sの出力R/L切り替えを行う。つまり、ステレオにしたい場合は、このモジュールを2つ用意する。 CTRLを3値制御だがデジタル出力なので右/OFFの2制御になっている。LRCKは、サンプリング周波数及びH/LでR/Lのデータを切り替えを制御。BCLKは、搬送クロック。SADTAはPCMデータ。つまり、スピーカを使っているときにはGPIO0には、サンプリング周波数でMPU側から周波数信号が出ている。
Serial通信に、PC側からデータ溢れチェックのRTSをGPIO0でCTS信号として使えるがそれ用に使っている感じではなさそう。
実際に回路を動かして確認してみる 昔の定番ターミナルにTeraTermというソフトがある。組み込みエンジニアにはおなじみなのかもしれない。昔懐かしいVT100コマンドも対応しているのでArduino IDEモニターではできないような高性能なことがCOMポートに対して可能。WiFi使ってTCP/IP接続の場合もこの端末は使える。で、ここではこいつのマクロを使用してDTS/RTS制御を行う。
serial_check.ttl
; ; Serial I/F周り確認 ; ; ============================================== ; (0) リセットボタン押す ; ============================================== ; ; ============================================== ; (1) まずは、状態確認 ; ============================================== getmodemstatus val sprintf2 str '%d(%x) , result:%d' val val result messagebox str "(1) Modem status" ; ; GPIO0 = 1 ; ; M5側 端末側 ; RTS(NC)->CTS(Low) ; DTR(NC)->DSR(Low) ; ;bit ;1 CTS( 送信可)信号がオンです。 ;2 DSR( データセットレディ)信号がオンです。 ;4 呼び出し信号がオンです。 ;8 RLSD( 受信線信号検出)信号がオンです。 ; ; 値が0ということは、M5側がRTS off DTR offということ ; ; ; ============================================== ; (2) READY PC端末の正常状態(フロー在りのとき) ; ============================================== ; DTR on(端末ON) RTS on(送信要求) setdtr 1 setrts 1 messagebox "DTR1 RTS1" "(2) (II)" ; ; GPIO0 = 1 ; ; 特に変化しない ; ; ; ============================================== ; (3) STOP 送信STOP要求 RTS off ; ============================================== ; ;setdtr 1 setrts 0 messagebox "DTR1 RTS0" "(3) (IV)" ; ; GPIO0 = 0 ; ; GPIO0が0に変化 ; ; ; ============================================== ; (4) 端末OFF DTR off RTS off ; ============================================== ; ※端末OFFなのにRTS on(送信リクエスト)にはならないので ; setdtr 0 setrts 0 messagebox "DTR0 RTS0" "(4) (I)" ; ; GPIO0 = 1 ; ; GPIO0が1に変化 ; ; ; ============================================== ; (5) RESET DTR off RTS on ; ============================================== ; ※異常状態 ; setdtr 0 setrts 1 messagebox "DTR0 RTS1" "(5) (III)" ; ; GPIO0 = 1 ; ; 回路的には、RESETボタンが押されている状態 ; リセットになっているのは、MPUに対してだけなので ; 液晶や周辺回路は変化しない。MPUも立ち上がりedgeで ; リセット動作になるのでここでは変化ない ; ; ; ============================================== ; (6) リセット動作1 DTR off RTS on→off ; ============================================== ; setrts 0 messagebox "DTR0 RTS0" "(6) (III->I)" ; ; GPIO0 = 1 ; ; ; ============================================== ; (7) リセット動作2 DTR off RTS on→off ; ============================================== ; setdtr 1 setrts 1 setdtr 0 messagebox "DTR0 RTS1" "(7) (II->III)" setdtr 1 messagebox "DTR1 RTS1" "(7) (III->II)" ; ; リセット動作 ; ; ; ============================================== ; (8) リセット動作3 download boot ; ============================================== ; (GIO0=0状態でリセット) setdtr 1 setrts 1 setdtr 0 messagebox "DTR0 RTS1" "(8) (II->III)" setdtr 1 setrts 0 messagebox "DTR1 RTS1" "(8) (III->II->IV)" ; ; ; download リセット動作 ; ; ============================================== ; (9) リセットリセットボタンを押す ; ==============================================
log.txt
; ============================================== ; (0) リセットボタン押す ; ============================================== ets Jul 29 2019 12:21:46 rst:0x1 (POWERON_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT) configsip: 0, SPIWP:0xee clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 mode:DIO, clock div:1 load:0x3fff0018,len:4 load:0x3fff001c,len:1044 load:0x40078000,len:8896 load:0x40080400,len:5816 entry 0x400806ac M5Core2 initializing...axp: vbus limit off axp: gpio1 init axp: gpio2 init axp: rtc battery charging enabled axp: esp32 power voltage was set to 3.35v axp: lcd backlight voltage was set to 2.80v axp: lcd logic and sdcard voltage preset to 3.3v axp: vibrator voltage preset to 2v OK ; ============================================== ; (6) リセット動作1 DTR off RTS on→off ; ============================================== ets Jul 29 2019 12:21:46 rst:0x1 (POWERON_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT) configsip: 0, SPIWP:0xee clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 mode:DIO, clock div:1 load:0x3fff0018,len:4 load:0x3fff001c,len:1044 load:0x40078000,len:8896 load:0x40080400,len:5816 entry 0x400806ac M5Core2 initializing...axp: vbus limit off axp: gpio1 init axp: gpio2 init axp: rtc battery charging enabled axp: esp32 power voltage was set to 3.35v axp: lcd backlight voltage was set to 2.80v axp: lcd logic and sdcard voltage preset to 3.3v axp: vibrator voltage preset to 2v OK ; ============================================== ; (7) リセット動作2 DTR off RTS on→off ; ============================================== ets Jul 29 2019 12:21:46 rst:0x1 (POWERON_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT) configsip: 0, SPIWP:0xee clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 mode:DIO, clock div:1 load:0x3fff0018,len:4 load:0x3fff001c,len:1044 load:0x40078000,len:8896 load:0x40080400,len:5816 entry 0x400806ac M5Core2 initializing...axp: vbus limit off axp: gpio1 init axp: gpio2 init axp: rtc battery charging enabled axp: esp32 power voltage was set to 3.35v axp: lcd backlight voltage was set to 2.80v axp: lcd logic and sdcard voltage preset to 3.3v axp: vibrator voltage preset to 2v OK ; ============================================== ; (8) リセット動作3 download boot ; ============================================== ets Jul 29 2019 12:21:46 rst:0x1 (POWERON_RESET),boot:0x7 (DOWNLOAD_BOOT(UART0/UART1/SDIO_REI_REO_V2)) waiting for download
この確認と少し調べてわかったこと
【M5Stack_Core2】virtual button
仮想ボタン1,2,3実験中。もしかしたら、できたかも。
動作しているけど、誤動作も多い。検出アルゴリズムを見直しかな。
【M5Stack_Core2】Arduino-esp32-1.0.4の比較
M5Stack_Core2で使用しているライブラリの話
Arduinoがインストールされている環境やパスを追っていくとesp32\1.0.4
というものを使用していることがわかります。
これは、 github.com を使用しているみたいです。
また、M5Stack_Core2になってからM5Stack社はカスタマイズ版のesp32\1.0.4を独自に展開しているみたいです。
今回は、’ESP32 Arduino 1.0.4 (IDF 3.2)’とgithubにあるファイルとArduinoにインストールされているファイルとM5Stack社はカスタマイズ版の3つについて比較してみたいと思います。
Arduino-esp32-1.0.4の比較
githubから落としてきたTAG1.04のデータ とArduino-esp32フォルダのデータ
- 1.0.4\docsフォルダが無い
- 1.0.4\libraries\AzureIoTがある
- 1.0.4\packageが無い
- 1.0.4\tools\get.exe get.py platformio-build.py が無い
- 1.0.4\ CMakeLists.txt component.mk Kconfig.projbuild libraries LICENSE.md Makefile.projbuild package.json README.mdが無い
- platform.txt 内容が違う versionとtools.esptool_py.pathの値
githubから落としてきたTAG1.04のデータ とM5Stackのカスタマイズ版 Arduino-esp32フォルダのデータ
- 1.0.4\cores\esp32\core_version.h が存在する
- 1.0.4\docsフォルダが無い
- 1.0.4\libraries\AzureIoTがある
- 1.0.4\packageが無い
- 1.0.4\tools\get.exe get.py platformio-build.py が無い
- 1.0.4\variants 他製品のファイルが無い
- 1.0.4\variants のm5stack_core_esp32 m5stack_fire m5stack_cのファイルの改行コードが違う
- 1.0.4\ CMakeLists.txt component.mk Kconfig.projbuild libraries LICENSE.md Makefile.projbuild package.json README.mdが無い
- 1.0.4\boards.txt m5stack製品のみになっている。
- platform.txt 内容が違う versionとtools.esptool_py.pathの値
Arduino-esp32フォルダのデータ とM5Stackのカスタマイズ版 Arduino-esp32フォルダのデータ
- 1.0.4\variants 他製品のファイルが無い
- 1.0.4\variants のm5stack_core_esp32 m5stack_fire m5stack_cのファイルの改行コードが違う
- 1.0.4\boards.txt m5stack製品のみになっている。
- platform.txt 内容が違う name=M5Stack Arduinoになっている
結果的には、
公式?ファイルは、
- ビルドできるファイルがある。
- libraries\AzureIoTがある
- docsフォルダがある
過去にM5Stack_gray等でインストールしたときに追加されたファイルは、
- ビルドできるファイル
- libraries\AzureIoT
- docsフォルダ が削除されている
さらに、M5Stackのカスタマイズ版では
- M5Stack以外のESP32製品情報のカット
- Core2の設定が追加されている