M5Stack+M5UnifiedでSDカードから画像表示

概要

M5StackのSDカードから画像を読み出して、表示するサンプルを作ってみました。内容は単純なのですがあまり例が無いので上げておきます。M5Unifiedで組んでいますので、M5Stack BASICやCore2で同じコードで動かすことができます。

スケッチ例

#include "SD.h"
#include <M5Unified.h>

const int maxFile = 100;
String fileList[maxFile];
int fileCount = 0;

void setup() {
  auto cfg = M5.config();
  M5.begin(cfg);
  delay(500);

  // SDカードマウント待ち
  while (false == SD.begin(GPIO_NUM_4, SPI, 15000000)) {
    Serial.println("SD Wait...");
    delay(500);
  }

  File root = SD.open("/");
  if (root) {
    File file = root.openNextFile();
    while (file) {
      if (file.isDirectory()) {
        // Dir skip
      } else {
        // File
        String filename = file.name();
        Serial.println(filename.indexOf(".jpg"));
        if (filename.indexOf(".jpg") != -1 || filename.indexOf(".png") != -1 ) {
          // Find
          fileList[fileCount] = filename;
          fileCount++;
          if (maxFile <= fileCount) {
            break;
          }
        }
      }
      file = root.openNextFile();
    }
  }

  Serial.println("File List");
  for (int i = 0; i < fileCount; i++) {
    Serial.println(fileList[i]);
  }
}

void loop() {
  int index = random(0, fileCount);
  String filename = fileList[index];
  int x = random(0, M5.Lcd.width() * 3 / 4);
  int y = random(0, M5.Lcd.height() * 3 / 4);

  Serial.printf("Index=%d, File=%s, %d, %d\n", index, filename.c_str(), x, y);
  if (filename.indexOf(".jpg") != -1) {
    M5.Lcd.drawJpgFile(SD, filename, x, y);
  } else {
    M5.Lcd.drawPngFile(SD, filename, x, y);
  }
  
  delay(1000);
}

内容的には起動時にSDカードの直下にあるJpegファイルとPNGファイルを検索して一覧に保存しています。loopにてその画像をランダムに表示するだけのサンプルになります。

SDのマウント

  // SDカードマウント待ち
  while (false == SD.begin(GPIO_NUM_4, SPI, 15000000)) {
    Serial.println("SD Wait...");
    delay(500);
  }

GPIO_NUM_4は端末によって変わるはずです。とはいってもM5Stack Core系とM5Paperは全部4で、ATOM TF-CARD KitはCSがGND固定になります。

ファイル検索

  File root = SD.open("/");
  if (root) {
    File file = root.openNextFile();
    while (file) {
      if (file.isDirectory()) {
        // Dir skip
      } else {
        // File
        String filename = file.name();
        Serial.println(filename.indexOf(".jpg"));
        if (filename.indexOf(".jpg") != -1 || filename.indexOf(".png") != -1 ) {
          // Find
          fileList[fileCount] = filename;
          fileCount++;
          if (maxFile <= fileCount) {
            break;
          }
        }
      }
      file = root.openNextFile();
    }
  }

よくある検索処理です。本当はJpegとか4文字拡張子にも対応した方がいい気がします。

読み込み表示部分

  if (filename.indexOf(".jpg") != -1) {
    M5.Lcd.drawJpgFile(SD, filename, x, y);
  } else {
    M5.Lcd.drawPngFile(SD, filename, x, y);
  }

画像はファイル形式により関数が異なるので注意してください。LCDに直接読み込む場合には上記のように非常に単純です。ただこのままだと読み込むまで画像のサイズがわかりません。画像のサイズを取得したい場合にはスプライトに一度読み込んで、widthとheightを取得することで拡大、縮小などの処理やセンター表示などに対応が可能です。

うまく動かない場合

ここが主題でもあるのですが、環境によりうまく動かないと思います。実はArduino IDEでESP32ボードライブラリの2.0.x系を使っている場合には、動きがおかしいことがあります。

まだSD周りはバグがたくさん残っているので、SDを使う場合には安定している1.0.6を使うことをおすすめします。

まとめ

現状のところ2系を使うメリットがあまりないので、ビルドも早い1.6を使い続けたほうがいい気がします。PlatformIOが2系に対応してから移行を考えようかなと思っています。

(追記)現在2.0.1までPlatformIOが対応しました。最新バージョンは2.0.2なのですが、たぶん動きがおかしいのでわざと対応していない可能性があります。

コメント

  1. aka より:

    twitterでタカオ(Takao)さんがM5Stack Basicないし Fireの V2.6で、SDカードの読み取り周波数20MHzならNG、15MHzであれば読めるということを検証しておられたので、SDのマウントに関するサンプルコードも下記の方がいいと思います。
    SD.begin(GPIO_NUM_4, SPI, 15000000)

    ※ツィートを見た直後に当時触っていたarduinoのスケッチは対応したのに、忘れて再度嵌りました…

    https://twitter.com/mongonta555/status/1580944410598002689