10連休もあっという間に過ぎて、明日は最終日です。毎日の猛暑で家の雑用ToDoリストは、半分くらいしかこなせませんでした。毎日、午前中の作業でヘタバッテしまい午後は昼寝でした。昼食をとると条件反射のように昼寝してしまいます。こんなことで来週から仕事に復帰できるのか心配になってきました。
あまりの暑さのため物理的な工作はできず、エアコンの効いた部屋でプログラム書きをするのが精いっぱい。前回の記事 で書きましたが、少しだけ大きな1.8インチカラー液晶(160x128)にSDカードスロットが付いていたので、小さなフォトフレーム(デジタルサイネージ)を作ってみました。
SDカードに格納した bmpファイルを順次表示するという単純なものです。制御は Seeeduino XIAO です。使う部品は液晶モジュールとXIAO、SDメモリカードだけ(他にブレッドボードとジャンパワイヤは必要ですが)。手持ちのSDカードを探したら16MBという骨董品のようなカードが見つかりました。Canonのラベルが付いていたので、その昔キヤノンのデジカメに付いてきたものだと思います。撮影のために液晶モジュールをブレッドボードから外してありますが、ブレッドボードに挿したまま、SDカードの抜き差しができます。SDカードはスロットから結構出っ張る。短いSDカードってあるのかな? microSDカードのアダプタなら短いものがあった。
処理は、SDカードのルートディレクトリに置いてある拡張子がbmpのファイルを順次読んで表示しています。XIAOはRAMが32KBしかないので、画像1枚をRAMに置いておくことができません。SDカードから読みながら順次液晶モジュールに転送して表示します。下の写真は空がモアレがかっていますが、撮影したiPhoneのカメラとの相性のように思います。肉眼だともう少しキレイ。
表示するファイル名の長さは拡張子を含めて31文字までにしています。また、ディレクトリを順次読んでいますので、表示順序はディレクトリへの格納順かと思います。ファイル数が少なければディレクトリを丸ごと読んで、ファイル名でのソート表示などもできます(プログラムを書けば)。
bmpファイルのサイズは液晶画面と同じ160x128pixelで、フォーマットはWindows形式の24ビットカラーです。16ビットカラーだとライブラリが対応していないようで読めませんでした。bmp拡張子でも読めないファイル形式の場合は、以下のようなメッセージを出すようにしています。表示されている画像は、ひとつ前の画像です。
接続は以下としました。間違いがあるかもしれません、無保証です。
XIAO(3V3)- LCD(VCC) - LCD(LED)
XIAO(GND)- LCD(GND)
XIAO(4) - LCD(CS)
XIAO(5) - LCD(RESET)
XIAO(6) - LCD(A0)
XIAO(7) - LCD(SD_CS)
XIAO(8) - LCD(SCK) - LCD(SD_SCK)
XIAO(9) - LCD(SD_MISO)
XIAO(10) - LCD(SDA) - LCD(SD_MOSI)
スケッチは以下です。こちらもバグがあるかも知れません。一応動きましたが無保証です。
// 2020.08.15 naka
// Small Digital Photo frame (Signage)
//
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <SdFat.h> // SD card & FAT filesystem library
#include <Adafruit_ImageReader.h> // Image-reading functions
#define SD_CS 7 // SD card chip select
#define TFT_CS 4 // TFT select pin
#define TFT_DC 6 // TFT data/command pin
#define TFT_RST 5 // Or set to -1 and connect TFT RST to Arduino reset pin
#define FILENAME_SIZE 32
#define B_SIZE 32
SdFat SD; // SD card filesystem
Adafruit_ImageReader reader(SD); // Image-reader object, pass in SD filesys
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
File root;
void setup(void) {
tft.initR(INITR_BLACKTAB); // Initialize screen
tft.fillScreen(ST77XX_WHITE);
tft.setRotation(3);
if(!SD.begin(SD_CS, SD_SCK_MHZ(10))) {
message("SD card error");
while(1);
}
root = SD.open("/"); // rootにあるファイルのみ表示対象
}
void loop() {
char filename[FILENAME_SIZE];
char buff[B_SIZE];
if (get_next_file_name(root,filename,FILENAME_SIZE)) {
ImageReturnCode stat = reader.drawBMP(filename, tft, 0, 0);
if (stat) { // 読めないbmpファイルフォーマット
sprintf(buff,"Not support %s",filename);
message(buff);
}
}
else {
root.rewind(); // 最後のファイルまで読んだので最初に戻る
}
delay(3000); // 3秒表示
}
int get_next_file_name(File dir,char *buff,int size) {
File entry;
while(1) {
entry = root.openNextFile();
if (!entry) { // お終い
return(0);
}
if (!entry.isDirectory()) { // ファイルなら拡張子を確認してbmpならbreak
entry.getName(buff,size);
String fileName = String(buff);
String ext = fileName.substring(fileName.lastIndexOf('.'));
if (ext==".bmp" || ext==".BMP") {
break;
}
}
else { // dirならクローズして次のエントリ
entry.close();
}
}
entry.close();
return (1);
}
void message(char* text) {
tft.fillRect(0,tft.height()-71, 159,10, ST77XX_WHITE); // 消去
tft.setCursor(3,tft.height()-70);
tft.setTextColor(ST77XX_RED);
tft.setTextWrap(true);
tft.setTextSize(1);
tft.print(text);
}
100均で買える小さな写真立てに組み込んだら見栄えもよさげですね。液晶モジュール(Aliexpress)とXIAO(秋月電子)、写真立てを合わせて1,000円以下で作れる工作です。夏休みの工作に如何ですか、といってもAliexpressは注文してから1か月くらいかかるので夏休み中には間に合わないか。