01.M5Tab5でjpgの一部を自動送り表示


01.M5Tab5でjpgの一部を自動送り表示

M5Stack Tab5をスイッチサイエンスで購入しました。
https://www.switch-science.com/collections/m5stack/products/10378

電池は別売りなので、アマゾンで購入。NP-F550互換バッテリー2900mAhが2個とUSB充電器のセットで3399円でした。

A4程度の大きさの本をスキャンしてpdfにした手持ちがたくさんあったのでそれをM5Stack Tab5で見れるようにします。今回は初期セットから1頁分を順番に表示するまでです。

スケッチ概要

PCでpdfをjpgに変換し、短辺2432pxのjpgとします。それをTab5横置き(1280x720px)で部分表示させます。
左上1タイル目 → 右上1タイル目 → 左2タイル目 → ・・・・・ → 右上6タイル目 の順に
1頁12タイル(横2x縦6タイル)を0.5秒ごとに自動送りで表示させます。(今回はここまで)
表示の重なりは、タイルの左右は128px、上下は100px以上としました。

製品仕様

スイッチサイエンス製品紹介

https://www.switch-science.com/collections/m5stack/products/10378

本家製品紹介の一部

https://docs.m5stack.com/en/core/Tab5

電源に関する注意
電源を切ったり、バッテリーを交換したりする前に、まずシャットダウンを実行。電源が直接切断されている場合は、5秒待ってから再度電源を入れます。そうしないと、異常電圧によりIMUセンサーが正しく初期化されない事があります。

電源on/off
USBケーブル or バッテリーで駆動時、電源ボタンを1回押すと、電源がoffになっている時にonになります。onの時は、2回押してoffします。

ダウンロードモード
USBケーブルを接続するか、バッテリーに電力を供給した状態で、内部の緑LEDが点滅し始めるまで、リセットボタンを押します(2秒)。離すと、ダウンロードモードに入ります。

初回起動方法

https://docs.m5stack.com/ja/arduino/m5tab5/program

1.準備

1-1.済

1-2.ボードマネージャーをインストール
ボードマネージャー > M5Stack by M5Stack 3.2.2(最新)をインストール
Arduino-IDE 2.3.6 > ツール > ボード > M5Stack > M5Tab5 を選択

1-3.ライブラリのインストール (すべてをインストール)
M5Unified by M5Stack 0.2.7(最新)
M5GFX by M5Stack 0.2.9(最新)

2.ダウンロードモード

USBケーブル接続時に、下面左端のリセットボタンを2秒長押し、内部の緑LEDが点滅したら放します。

3. ポートの選択

Arduino-IDE > ツール > ポート > COM□を選択

4.スケッチで動作確認

Arduino-IDE > ファイル > スケッチ例 > (カスタムライブラリのスケッチ例) M5GFX > Basic > BarGraph を書込み実行 > バーグラフを表示 > 動作確認終了

スケッチについて

元のpdfはA4程度以下の横書きとします。本の字より大きな表示をします。
・jpg1枚をPSRAMのキャンバスに展開し、マイナスのオフセットで一部をpushSpriteします。
・画面表示は全画面にしないと、数倍表示が遅くなってしまったので部分表示は止めました。
・最初にSDから読込むのが一番時間がかかります。

1.準備

1-1.pdf→jpgにオンライン変換(無料)
https://www.ilovepdf.com/ja/pdf_to_jpg
* jpgは、pngより容量が小さいです。

1-2.フリーソフトIrfanView 4.62にて
横2432pxに一括変換。今回縦は3653pxになりました。

1-3.SDに保存
SDのルートに保存。今回は"09.jpg"としました。

2.overlap

横は128px固定で,縦は100px以上とします。


pdfからjpgにする時、jpg横2432pxにします。横は2タイルとするので、重なりは、1280x2-2432=128pxになります。

縦 (overlapH)
最低100px(overlapHmin)とし、jpgの縦px(imgH)により変わります。
重なりを引いたタイル縦pxは、
lcdH - overlapHmin = 720 - 100 = 620px以下
となります。タイルの縦数は、重なりが1少ないので
(imgH - lcdH) / 620 + 1 = (3653-720)/620+1 = 5.73以上 = 6タイル
で、その時、重なりを引いたタイル縦pxは、
(imgH - lcdH) / (6 - 1) = (3653-720)/5 = 586.6 ≒ 587px
よって、
overlapH = lcdH - 587 = 720-587 = 133px
ただし、一番下の重なりを引いたタイル縦pxは、
3653-587x5-133=585px
になります。

2.表示位置

今回のjpg位置でのタイル座標は以下になります。
(0,000)┌─┐(x1,000) (x2,000)┌─┐(x3,000)
(0,y01)└─┘(x1,y01) (x2,y01)└─┘(x3,y01)
(0,y02)┌─┐(x1,y02) (x2,y02)┌─┐(x3,y02)
(0,y03)└─┘(x1,y03) (x2,y03)└─┘(x3,y03)
(0,y04)┌─┐(x1,y04) (x2,y04)┌─┐(x3,y04)
(0,y05)└─┘(x1,y05) (x2,y05)└─┘(x3,y05)
(0,y06)┌─┐(x1,y06) (x2,y06)┌─┐(x3,y06)
(0,y07)└─┘(x1,y07) (x2,y07)└─┘(x3,y07)
(0,y08)┌─┐(x1,y08) (x2,y08)┌─┐(x3,y08)
(0,y09)└─┘(x1,y09) (x2,y09)└─┘(x3,y09)
(0,y10)┌─┐(x1,y10) (x2,y10)┌─┐(x3,y10)
(0,y11)└─┘(x1,y11) (x2,y11)└─┘(x3,y11)

x1=1280,x2=1152,x3=2432
y01=720,y02=587,y03=y02+720,y04=1174,y05=y04+720,y06=1761,
y07=y06+720,y08=2348,y09=y08+720,y10=2933,y11=y10+720=3653
単純計算ではy10=2935ですが、縦720px以下となるため、
下に余白ができ前回の表示が見えてしまうので、
2933(=3653-720)にして全画面表示とします。

スケッチ


// jpg1枚をPSRAMのキャンバスに展開し、
// マイナスのオフセットで一部をpushSprite
#include <SD.h>                       // SDを使用 SDの方が上に書く
#include <M5Unified.h>                // M5統合ライブラリ
#define SD_SPI_CS_PIN 42              // SD CS pin
#define SD_SPI_SCK_PIN 43             // SD SCK pin
#define SD_SPI_MOSI_PIN 44            // SD MOSI pin
#define SD_SPI_MISO_PIN 39            // SD MISO pin
static const char* imgF = "/09.jpg";  // 読込file
static const int imgW = 2432;         // jpg横(=38x64) tileW*2(=2560)以下
// 横重なりは、lcdW x 2 - imgW = 1280x2-2432 = 128px
static const int imgH = 3653;        // jpg縦 1.41倍程度 スキャン切抜きにより異なる
static const int lcdW = 1280;        // 表示横 画面横いっぱい
static const int lcdH = 720;         // 表示縦 全画面表示で無いと数倍遅い。一番下のタイル表示は我慢
static const int overlapHmin = 100;  // 縦重なり
int overlapH;
M5Canvas canvas(&M5.Display);  // canvasをM5.Displayに描画

void setup() {
  M5.begin();                                                                  // M5の初期化
  SPI.begin(SD_SPI_SCK_PIN, SD_SPI_MISO_PIN, SD_SPI_MOSI_PIN, SD_SPI_CS_PIN);  // SPIの初期化

  M5.Display.setRotation(3);                       // 画面回転 (0-3,4-7)
  M5.Display.fillScreen(TFT_DARKGREEN);            // 全画面を深緑 起動確認のため
  M5.Display.setBrightness(100);                   // 明るさ (0暗-255明)
  M5.Display.clear();                              // 画面クリア
  M5.Display.setFont(&fonts::lgfxJapanGothic_40);  // 固定幅ゴシック 8,12,16,20,24,28,32,36,40
  if (!SD.begin(SD_SPI_CS_PIN, SPI, 25000000)) {
    // SDの初期化に失敗時 or SDが存在しない時(CSpin,SPI通信,SPI25MHz)
    M5.Display.print("\n SD cardが見つかりません");  // 画面表示
    for (;;) delay(1000);                            // 永久ループ
  }
  M5.Display.println("\n SD cardを検出\n");  // 画面表示
  canvas.setPsram(true);                     // キャンバスはPSRAMを使用
  canvas.setColorDepth(16);                  // 色の深さ(1,2,4,8,16,24,32)
  if (!canvas.createSprite(imgW, imgH)) {    // 1頁丸ごとキャンバスが作れなければ
    // 2400×3604×RGB565≈16.5MB程度を確保。PSRAM断片化で失敗したら再起動や初期確保
    M5.Display.println(" キャンバス枠作成 失敗");  // 画面表示
    for (;;) delay(1000);                          // 永久ループ
  }
  //M5.Display.println("キャンバス枠作成 成功\n");       // 画面表示
  M5.Display.println(" jpgをキャンバスに描画中\n");     // 画面表示
  if (!canvas.drawJpgFile(SD, imgF, 0, 0)) {            // キャンバスにjpgを描画できなければ
    M5.Display.println(" jpgをキャンバスに描画 失敗");  // 画面表示
    for (;;) delay(1000);                               // 永久ループ
  }
  overlapH = ((imgH * 1.0 - lcdH) / (lcdH - overlapHmin) + 1 + 0.999);
  overlapH = ((imgH * 1.0 - lcdH) / (overlapH - 1) + 0.5);
  overlapH = lcdH - overlapH;                         // 縦のoverlap(px)
  M5.Display.printf(" overlapH= %3dpx\n\n", overlapH);  // overlapHを表示
  M5.Display.println(" 表示 開始");                   // 画面表示
  delay(1000);                                        // 1秒待つ
}
void loop() {
  bool viewEnd = 0;                                                // 表示する
  for (int32_t offY = 0; offY > -imgH; offY -= lcdH - overlapH) {  // 最後はbreakで抜ける
    if (lcdH > imgH + offY) {                                      // 一番下のブロック(表示が余る)なら
      offY = -(imgH - lcdH);                                       // 全画面表示するよう重なりを増やす
      viewEnd = 1;                                                 // 表示終了フラグ
    }
    canvas.startWrite();  // バッファを効率的に使用開始
    //M5.Display.fillScreen(TFT_BLACK);// 全画面黒 ちらつくのでやめ
    canvas.pushSprite(0, offY);  // 左キャンバスをプッシュ(左上基準点)
    canvas.endWrite();           // バッファを効率的に使用終了
    delay(500);                  // 0.5秒待つ
    canvas.startWrite();         // バッファを効率的に使用開始
    //M5.Display.fillScreen(TFT_BLACK);       // 全画面黒 ちらつくのでやめ
    canvas.pushSprite(-(imgW - lcdW), offY);  // 右キャンバスをプッシュ(左上基準点)
    canvas.endWrite();                        // バッファを効率的に使用終了
    delay(500);                               // 0.5秒待つ
    if (viewEnd) break;                       // 表示終了
  }
}
* flash memory(6.5Mbyte)のうち、スケッチが16%使用。
RAM(327kbyte)のうち、global変数が7%使用、
local変数で301kbyte使用可能。(1000byte=1kbyteで計算)