夏休みの仕上げとして、XIAOと小型カラー液晶で作ったフォトフレームの動画アップしました。
--
21.04.21 追記
あまりキレイではありませんがスケッチです。無保証です。著作権は留保しますが、改変などご自由にどうぞ。
// 2020.08.19 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
#include <FlashAsEEPROM.h> // XIAOにはEEPROMがないのでflashをEEPROMのように使うライブラリ
#include <FlashStorage.h>
#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 TOUCHSW_1 2
#define TOUCHSW_2 3
#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;
int play;
int interval;
#define MAX_FILE 50
char file_tbl[MAX_FILE][FILENAME_SIZE];
int file_num;
void setup(void) {
// i/oピン設定
pinMode(TOUCHSW_1,INPUT);
pinMode(TOUCHSW_2,INPUT);
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);
}
read_flash(); // 設定値 (play,intervalをflashから読み出す)
ImageReturnCode stat = reader.drawBMP("amazon.bmp", tft, 0, 0);
delay(5000);
// ファイル名一覧を取り出す
int i_file;
char filename[FILENAME_SIZE];
root = SD.open("/"); // rootにあるファイルのみ表示対象
file_num = 0;
while(get_next_file_name(root,filename,FILENAME_SIZE)) {
if (strcmp(filename,"amazon.bmp")!=0) {
strcpy(file_tbl[file_num++],filename);
if (file_num>=MAX_FILE) break;
}
}
// for (int i=0;i<file_num;i++) {
// Serial.print(file_tbl[i]);
// Serial.print("\n");
// }
qsort(file_tbl,file_num,sizeof(file_tbl[0]),compchar);
// Serial.print("Sorted\n");
// for (int i=0;i<file_num;i++) {
// Serial.print(file_tbl[i]);
// Serial.print("\n");
// }
char buff[30];
sprintf(buff,"bmp file number : %d",file_num);
message(buff);
delay(2000);
}
void loop() {
char filename[FILENAME_SIZE];
char buff[B_SIZE];
static int i_file = 0;
if (play==0) i_file = rand()%file_num;
ImageReturnCode stat = reader.drawBMP(file_tbl[i_file], tft, 0, 0);
if (stat) { // 読めないbmpファイルフォーマット
sprintf(buff,"Not support %s",filename);
message(buff);
}
int next = check_touchsw(); // 3秒表示
if (next==1) {
i_file--;
if (i_file<0) i_file = file_num - 1;
}
else {
i_file++;
if (i_file>=file_num) i_file = 0;
}
}
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);
}
int check_touchsw() {
static unsigned int int_time[6] = {0,1000,3000,10000,30000,60000};
unsigned int time_s = millis();
while(1) {
// touch swが押されているか
if (digitalRead(TOUCHSW_1)==1 || digitalRead(TOUCHSW_2)==1) { // 押されている
delay(100); // 同時押し確認
if (digitalRead(TOUCHSW_1)==1 && digitalRead(TOUCHSW_2)==1) { // 同時
select_menu();
return(0);
}
else if (digitalRead(TOUCHSW_1)==1) {
return(1);
}
else if (digitalRead(TOUCHSW_2)==1) {
return(2);
}
}
else {
if (interval!=0) {
if ( (millis() - time_s) > int_time[interval] ) { // interval時間が経過した
return (0);
}
}
}
}
}
void select_menu() {
unsigned int pretime = 0;
menu();
while(digitalRead(TOUCHSW_1)==1 || digitalRead(TOUCHSW_2)==1); // 離されるまで待つ
disp_triangle_play(play,0);
disp_triangle_interval(interval,0);
while(1) {
if ((millis()-pretime)>300) {
disp_triangle_play(play,1); // blink=toggle
pretime = millis();
}
if (digitalRead(TOUCHSW_2)==1) {
play++;
if (play>1) play = 0;
disp_triangle_play(play,1);
while(digitalRead(TOUCHSW_2)==1); // 離されるまで待つ
}
if (digitalRead(TOUCHSW_1)==1) {
while(digitalRead(TOUCHSW_1)==1); // 離されるまで待つ
break;
}
}
disp_triangle_play(play,0);
// disp_triangle_interval(interval,0); // 1:blink
while(1) {
if ((millis()-pretime)>300) {
disp_triangle_interval(interval,1); // blink=toggle
pretime = millis();
}
if (digitalRead(TOUCHSW_2)==1) {
interval++;
if (interval>5) interval = 0;
disp_triangle_interval(interval,1);
while(digitalRead(TOUCHSW_2)==1); // 離されるまで待つ
}
if (digitalRead(TOUCHSW_1)==1) {
while(digitalRead(TOUCHSW_1)==1); // 離されるまで待つ
break;
}
}
disp_triangle_interval(interval,0);
write_flash(); // 設定をflashに書き込む(書き込み回数制限10,000位)
}
int compchar(const void* left, const void* right) {
char *left_char = (char *)left;
char *right_char = (char *)right;
return strcmp( left_char, right_char );
}
void menu() {
tft.fillScreen(ST77XX_WHITE);
tft.setTextColor(ST77XX_BLUE);
tft.setTextSize(1);
tft.setCursor(20,tft.height()-120);
tft.print("PLAY");
tft.setCursor(30,tft.height()-110);
tft.print("random");
tft.setCursor(30,tft.height()-100);
tft.print("sort");
tft.setCursor(20,tft.height()-80);
tft.print("INTERVAL");
tft.setCursor(30,tft.height()-70);
tft.print("manual");
tft.setCursor(30,tft.height()-60);
tft.print(" 1 sec");
tft.setCursor(30,tft.height()-50);
tft.print(" 3 sec");
tft.setCursor(30,tft.height()-40);
tft.print("10 sec");
tft.setCursor(30,tft.height()-30);
tft.print("30 sec");
tft.setCursor(30,tft.height()-20);
tft.print("60 sec");
}
void disp_triangle_play(uint16_t play,int blink) {
static uint16_t preplay = 0;
static int toggle = 0;
uint16_t x1,y1,x2,y2,x3,y3;
toggle ^= blink;
if (blink==0 || toggle==1) {
x1 = 20, y1 = tft.height() - (111 - preplay * 10);
x2 = x1+4, y2 = y1 + 4;
x3 = x1, y3 = y1 + 8;
tft.fillTriangle(x1,y1,x2,y2,x3,y3,ST77XX_WHITE); // clear previous triangle
}
if (blink==0 || toggle==0) {
x1 = 20, y1 = y1 = tft.height() - (111 - play * 10);
x2 = x1+4, y2 = y1 + 4;
x3 = x1, y3 = y1 + 8;
tft.fillTriangle(x1,y1,x2,y2,x3,y3,ST77XX_RED);
}
preplay = play;
}
void disp_triangle_interval(uint16_t interval,int blink) {
static uint16_t preint = 0;
static int toggle = 0;
uint16_t x1,y1,x2,y2,x3,y3;
toggle ^= blink;
if (blink==0 || toggle==1) {
x1 = 20, y1 = tft.height() - (71 - preint * 10);
x2 = x1+4, y2 = y1 + 4;
x3 = x1, y3 = y1 + 8;
tft.fillTriangle(x1,y1,x2,y2,x3,y3,ST77XX_WHITE); // clear previous triangle
}
if (blink==0 || toggle==0) {
x1 = 20, y1 = y1 = tft.height() - (71 - interval * 10);;
x2 = x1+4, y2 = y1 + 4;
x3 = x1, y3 = y1 + 8;
tft.fillTriangle(x1,y1,x2,y2,x3,y3,ST77XX_RED);
}
preint = interval;
}
void read_flash() { // FlashAsEEPROM を利用
if (EEPROM.isValid()) {
play = (int)EEPROM.read(0); // play
interval = (int)EEPROM.read(1); // interval
}
else { // 一度も書き込まれていないので初期値として以下を設定
play = 0; // random
interval = 0; // manual
}
}
void write_flash() { // FlashAsEEPROM を利用
EEPROM.write(0,play);
EEPROM.write(1,interval);
EEPROM.commit();
}