2020年9月1日火曜日

夏休みの工作、小型フォトフレーム

夏休みの仕上げとして、XIAOと小型カラー液晶で作ったフォトフレームの動画アップしました。



--
21.04.21 追記
あまりキレイではありませんがスケッチです。無保証です。著作権は留保しますが、改変などご自由にどうぞ。
回路(結線)はスケッチと XIAOと1.8インチ小型液晶で小型フォトフレーム をご参照ください。タッチスイッチのピンは、スケッチ内に記載。

//                                                    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();
} 

0 件のコメント:

コメントを投稿