【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するようにしたのが下のスクリプト。