M5PaperでSPIFFSに日本語TTFファイルを置いて描画する

M5Paperでスケジューラを作りました。
M5PaperのM5EPDライブラリは、日本語等のUTF-8の文字列をTrueTypeフォント(TTF)を使って描画することができますので、スケジューラでもこれを使っています。
最初はSDカードに日本語TTFを置いていたのですが、高々数MBのためにSDカードを専用に差しっぱなしするのも微妙だったので、SPIFFSに置くようにしてみました。以下、その方法です。

SPIFFS に入るフォントを用意する

SPIFFSは最大で7MBなので、これ以下のフォントを用意する必要があります。ギリギリ過ぎるとメタデータ分も必要なので収まりきりません。
私は比較的自由にダウンロード・利用できるIPAexゴシック(4MB程度)を使ってみました。

IPAexフォントおよびIPAフォントについて | 一般社団法人 文字情報技術促進協議会

microSDカードにフォントをコピーする

FAT32で初期化したマイクロSDカードに、font.ttf という名前でフォントを格納し、M5Paperに挿入しておきます。

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);
}
f:id:NeoCat:20210410073537j:plain:w400
M5Paperで作ったスケジューラ