M5Paperでスケジューラを作りました。
M5PaperのM5EPDライブラリは、日本語等のUTF-8の文字列をTrueTypeフォント(TTF)を使って描画することができますので、スケジューラでもこれを使っています。
最初はSDカードに日本語TTFを置いていたのですが、高々数MBのためにSDカードを専用に差しっぱなしするのも微妙だったので、SPIFFSに置くようにしてみました。以下、その方法です。
ようやくM5Paperを入手したので、予定ビューワーにしてみた。iCal形式のデータをGoogle Calendar等から取得して直近の予定を表示。外部連携で、開始前に音を鳴らしたり、Zoomリンクが登録されてればそれをワンタップするだけで開いてくれるおまけ機能付きw pic.twitter.com/hanxbM6oWO
— NeoCat (@NeoCat) 2021年1月13日
SPIFFS に入るフォントを用意する
SPIFFSは最大で7MBなので、これ以下のフォントを用意する必要があります。ギリギリ過ぎるとメタデータ分も必要なので収まりきりません。
私は比較的自由にダウンロード・利用できるIPAexゴシック(4MB程度)を使ってみました。
SPIFFSにデータをコピーしてM5EPDに読み込ませる (Arduino IDE)
Arduino IDEから以下のコードを書き込みます。
(Arduino IDEでの開発環境は公式ページの導入手順に従ってあらかじめセットアップしておきましょう。)
この時、Arduino IDEのツールメニューにて、ボードとしてM5Stack-Fireを選択した上で、同メニューから Partition Scheme を「Large SPIFFS (7MB)」にしておくことが必要です。
setup()でtryCopySDFile()を呼び出すことで、microSDカードが認識されfont.ttfが存在するかをチェックし、その場合はSPIFFSを初期化して、そこにfont.ttfをコピーさせます。
microSDカードが見つからない場合は、何もしません。
その後、SPIFFSからフォントをM5EPDに読み込ませます。
正しくコピーできていれば、文字が描画できるはずです。
以下では試しにRTCの日付・時刻を描画しています。
うまくいったら、SDカードを抜きます。
#include <M5EPD.h> #include <FS.h> M5EPD_Canvas canvas(&M5.EPD); // SDカードがセットされていれば、SPIFFSを初期化後font.ttfをコピーする bool tryCopySDFile() { const char *path = "/font.ttf"; if (!SD.begin(4) || !SD.exists(path)) { return SPIFFS.begin(false) && SPIFFS.exists(path); } canvas.drawString("Copying font ...", 270, 664); canvas.pushCanvas(0,0,UPDATE_MODE_DU4); Serial.println("Formatting SPIFFS ..."); if (!SPIFFS.format()) { Serial.println("SPIFFS format Failed"); return false; } if (!SPIFFS.begin()) { Serial.println("SPIFFS Mount Failed"); return false; } Serial.println("Copying font ..."); File file = SD.open(path); SPIFFS.remove(path); File dest = SPIFFS.open(path, FILE_WRITE); if (!file || !dest) { Serial.println("failed."); return false; } uint8_t *buf = new uint8_t[4096]; if (!buf) { Serial.println("failed."); return false; } size_t len, size, ret; size = len = file.size(); while (len) { size_t s = len; if (s > 4096) s = 4096; file.read(buf, s); if ((ret = dest.write(buf, s)) < s) { Serial.print("write failed: "); Serial.print(ret); Serial.print(" - "); Serial.println(s); return false; } len -= s; Serial.print(size - len); Serial.print(" / "); Serial.println(size); } delete[] buf; file.close(); dest.close(); if (!SPIFFS.exists(path)) { Serial.println("no file"); return false; } dest = SPIFFS.open(path); len = dest.size(); dest.close(); if (len != size) { Serial.print("size not match : "); Serial.println(dest.size()); return false; } Serial.println("Done."); return true; } void setup() { M5.begin(); M5.TP.SetRotation(90); M5.EPD.SetRotation(90); M5.EPD.Clear(true); M5.RTC.begin(); canvas.createCanvas(540, 960); canvas.setTextDatum(TC_DATUM); canvas.setTextSize(3); canvas.drawString("... Initializing ...", 270, 640); canvas.pushCanvas(0,0,UPDATE_MODE_DU4); if (!tryCopySDFile()) { canvas.drawString("data copy failed !", 270, 254); canvas.pushCanvas(0,0,UPDATE_MODE_DU4); for (;;); } // SPIFFSからフォントを読み込む canvas.drawString("Loading font ...", 270, 230); canvas.pushCanvas(0,0,UPDATE_MODE_DU4); canvas.loadFont("/font.ttf", SPIFFS); canvas.createRender(96, 256); canvas.createRender(32, 256); canvas.drawString("OK!", 270, 254); canvas.pushCanvas(0,0,UPDATE_MODE_DU4); // 時計を描画 drawClock(); M5.EPD.Clear(true); } void loop() { drawClock(); } // 毎分時計を再描画する. RTCが設定されている必要がある void drawClock() { char t[32]; static int last_m = -1; rtc_time_t rtc_time; rtc_date_t rtc_date; M5.RTC.getTime(&rtc_time); M5.RTC.getDate(&rtc_date); if (last_m == rtc_time.min) { delay(1000); return; } last_m = rtc_time.min; const char* weeks[] = {"日", "月", "火", "水", "木", "金", "土"}; canvas.setTextDatum(TL_DATUM); canvas.fillCanvas(0); canvas.drawLine(0, 179, 540, 179, 15); canvas.drawLine(0, 180, 540, 180, 15); snprintf(t, 32, "%d/%d/%d (%s)", rtc_date.year, rtc_date.mon, rtc_date.day, weeks[rtc_date.week]); canvas.setTextSize(32); canvas.drawString(t, 12, 16); snprintf(t, 32, "% 2d:%02d", rtc_time.hour, rtc_time.min); canvas.setTextSize(96); canvas.drawString(t, 12, 64); canvas.pushCanvas(0,0,UPDATE_MODE_GLR16); }