06.M5Paper3でBook Reader test2


06.M5Paper3でBook Reader test2

説明

前回の続きです。前回は最初の1頁のみ表示でしたが、今回は、画面タッチで頁をめくります。画面右半分の部分をタッチで次の頁へ、左半分の部分で前の頁を表示します。考え方は、
M5Paperで縦書きテキストビューワを作るよ!(たいへんだったよ!w)
https://note.com/rasen/n/n3d3f056d66f7
を参考にさせていただきました。
確認は1ファイルを数ページ見ただけなので、不具合があるかもしれません。また、画面の一番下の行が1文字だけとなってしまいます。修正中です。

M5GFXフォント一覧

https://docs.m5stack.com/ja/arduino/m5gfx/m5gfx_appendix

他にきれいなフォントが簡単に変更できるかと思いましたがありませんでした。M5GFXライブラリに含まれている日本語のユニコードフォントは、2種類あります。サイズは、lgfxJapanが、8~40px。efontJAが、10~24pxです。今回は、
lgfxJapanGothic_32 or lgfxJapanGothic_36 を使用しています。

&fonts::lgfxJapanMincho_□ (明朝体 固定長)
&fonts::lgfxJapanMinchoP_□ (明朝体 可変長)
&fonts::lgfxJapanGothic_□ (ゴシック体 固定長)
&fonts::lgfxJapanGothicP_□ (ゴシック体 可変長)
* □=8,12,16,20,24,28,32,36,40
* ゴシック体の固定長と可変長の両方を読み込んでも、日本語部分は同じで英数部分しか差がありません。
https://lang-ship.com/blog/work/lovyangfx-3-ja-font/

&fonts::efontJA_□ (標準)
&fonts::efontJA_□_b (太字)
&fonts::efontJA_□_bi (太字斜体)
&fonts::efontJA_□_i (斜体)
* □=10,12,14,16,24

スケッチ


// 05_M5PaperS3_BookReader2.ino
#include <SD.h>                      // SDを使用
File myFile;                         // Fileのインスタンスを作成
#include <epdiy.h>                   // 電子ペーパードライバー
#include <M5GFX.h>                   // M5用グラフィックライブラリ
M5GFX display;                       // M5GFXのインスタンスを作成
#define SD_SPI_CS_PIN GPIO_NUM_47    // SDのpin
#define SD_SPI_SCK_PIN GPIO_NUM_39   // SDのpin
#define SD_SPI_MOSI_PIN GPIO_NUM_38  // SDのpin
#define SD_SPI_MISO_PIN GPIO_NUM_40  // SDのpin
String buf;                          // 表示文章のbuf
int16_t fontS = 32;                  // fontサイズ (32or36)
int16_t gyoukan = 10;                // 行間px
bool edpQ = false;                   // (true)高品質or(false)高速
bool gamenTate = true;               // 画面(true)縦or(false)横
int32_t pageN, pageB[1000];          // 頁番号,頁最初のByte値
lgfx::touch_point_t tp[1];           // タッチポイント

void SetFont() {                                  // Font等初期化関数
  display.init();                                 // パネルの初期化
  display.startWrite();                           // バッファを効率的に使用開始
  if (edpQ) {                                     // 画面の品質設定
    display.setEpdMode(epd_mode_t::epd_quality);  // 高品質 (描画遅)
  } else {
    display.setEpdMode(epd_mode_t::epd_fastest);  // 描画速 (低品質)
  }
  if (gamenTate) {           // 画面向き設定
    display.setRotation(0);  // 縦長
  } else {
    display.setRotation(1);  // 横長
  }
  if (fontS == 36) {                              // フォントサイズ設定
    display.setFont(&fonts::lgfxJapanGothic_36);  // 固定幅ゴシック体 8,12,16,20,24,28,32,36,40
  } else {
    display.setFont(&fonts::lgfxJapanGothic_32);  // 固定幅ゴシック体 8,12,16,20,24,28,32,36,40
  }
  display.fillScreen(TFT_WHITE);               // 全画面白
  display.setTextColor(TFT_BLACK, TFT_WHITE);  // (字,地)
  display.setTextSize(1);                      // 文字サイズの倍数 1-7
  display.setTextWrap(false, false);           // 自動折返し(x,y)
  display.setCursor(0, 0);                     // カーソル位置
  display.setAutoDisplay(false);               // 即表示しない
}

String setBuf(int32_t byteP) {                // bytePの位置から読込みそのbufを返す
  myFile = SD.open("/SDlib.txt", FILE_READ);  // 読込ファイルを開く
  myFile.seek(byteP);                         // 読取開始のByte位置
  int32_t readB = 0;                          // 読取Byte数リセット
  buf = "";                                   // bufリセット
  while (myFile.available()) {                // 読取れるByteがあれば
    buf += char(myFile.read());               // 読取文字
    readB++;                                  // 読取Byte数1加算
    if (readB > ((540 / fontS) * (960 / fontS) * 3)) break;
    // 行間0,全3Byte文字として1頁以上読込んだら抜ける
  }
  myFile.close();  // ファイルを閉じる
  return buf;      // 1頁以上の文字を返す
}

int setView(String buf) {        // 1頁表示関数
  int16_t viewX = 0, viewY = 0;  // 表示x,y位置 リセット
  int16_t i = 0;                 // Byte位置 リセット
  int16_t bufL = buf.length();   // bufの全Byte数 \0は含まず
  display.clear();               // パネルをクリア
  while (i < bufL) {             // buf終わりまでループ
    bool CRon = false;           // 改行 リセット

    // 1頁セット終わり
    if (viewY + gyoukan + fontS > display.height()) break;  // 行が下まで来たら抜ける

    // 1行セット終わり
    if (viewX > (display.width() / fontS - 1) * fontS) {  // 横が1文字分少なく を超えていたら改行
      CRon = true;                                        // 改行
      viewX = 0;                                          // 画面の左端
      viewY += (fontS + gyoukan);                         // 行間スペースを含めて移動へ
    }

    // 改行
    if (buf.charAt(i) == 0x0D) {   // CR+LF だったら
      CRon = true;                 // 改行
      i += 2;                      // CR+LF Windows等
      viewX = 0;                   // 画面の左端
      viewY += (fontS + gyoukan);  // 行間スペースを含めて移動へ
    }

    // LFは無視
    if (buf.charAt(i) == 0x0A) {   // LFだったら 無いはず
      i += 1;                      // 1Byte進める
      viewX = 0;                   // 画面の左端
      viewY += (fontS + gyoukan);  // 行間スペースを含めて移動へ
    }

    //prit
    display.setCursor(viewX, viewY);           // カーソル位置
    if (buf.charAt(i) < 0x80) {                // 1Byte文字なら
      display.print(buf.substring(i, i + 1));  // 1文字セット
      i += 1;                                  // 1Byte進める
      viewX += fontS / 2;                      // 半角
    } else if (buf.charAt(i) < 0xC0) {         // マルチByteの2Byte目以降
      // ここへは来ないはず
    } else if (buf.charAt(i) < 0xE0) {         // 2Byte文字なら
      display.print(buf.substring(i, i + 2));  // 1文字セット
      i += 2;                                  // 2Byte進める
      viewX += fontS;                          // 全角
    } else if (buf.charAt(i) < 0xF0) {         // 3Byte文字なら
      display.print(buf.substring(i, i + 3));  // 1文字セット
      i += 3;                                  // 3Byte進める
      viewX += fontS;                          // 全角
    } else if (buf.charAt(i) < 0xF8) {         // 4Byte文字なら
      display.print(buf.substring(i, i + 4));  // 1文字セット
      i += 4;                                  // 4Byte進める
      viewX += fontS;                          // 全角
    } else if (buf.charAt(i) < 0xFC) {         // 5Byte文字なら
      display.print(buf.substring(i, i + 5));  // 1文字セット
      i += 5;                                  // 5Byte進める
      viewX += fontS;                          // 全角
    } else if (buf.charAt(i) < 0xFE) {         // 6Byte文字なら
      display.print(buf.substring(i, i + 6));  // 1文字セット
      i += 6;                                  // 6Byte進める
      viewX += fontS;                          // 全角
    } else {
      // ここへは来ないはず
    }
  }
  display.display();  // セットしたのを表示
  return (i);         // 処理Byte数を返す
}

void setup() {
  //Serial.begin(115200);  // シリアル通信開始
  SPI.begin(SD_SPI_SCK_PIN, SD_SPI_MISO_PIN,
            SD_SPI_MOSI_PIN, SD_SPI_CS_PIN);
  delay(100);                       // 0.1秒待つ
  SetFont();                        // Font等初期化関数へ
  SD.begin(47);                     // SDライブラリとカードを初期化
  pageN = 0;                        // 頁番号0
  pageB[pageN] = 0;                 // 頁番号の最初のバイト値は0
  buf = setBuf(pageB[pageN]);       // その頁のbufを得る関数へ
  pageB[pageN + 1] = setView(buf);  // 戻り値(Byte値)は次の頁の開始Byte値
}

void loop() {
  bool drawed = false;               // 描画変数=描画していない
  if (display.getTouchRaw(tp, 1)) {  // タッチ情報を取得
    drawed = true;                   // 描画した
    if (tp[0].x > 270) {             // 次頁タッチなら
      pageN++;                       // 頁番号1加算
      buf = setBuf(pageB[pageN]);    // その頁のbufを得る関数へ
      pageB[pageN + 1] = pageB[pageN] + setView(buf);
      // 1頁表示し、(開始+処理)のByte数を次頁の開始Byte数にする
    } else if (pageN > 0) {        // 前頁タッチで頁番号が1以上なら
      pageN--;                     // 頁番号1減算
      buf = setBuf(pageB[pageN]);  // その頁のbufを得る関数へ
      setView(buf);                // 1頁表示関数へ
    }

  } else if (drawed) {  // もし描画していれば
    drawed = false;     // 描画変数を戻す
  }
  vTaskDelay(1);  // 1tickの間タスクをブロック
}
* flash memory(3.1Mbyte)のうち、スケッチが45%使用。RAM(327kbyte)のうち、global変数が7%使用、local変数で302kbyte使用可能。(1000byte=1kbyteで計算)