Mod existing code to display photos on TFT

Have this code below working successfully now for a couple of years thanks to assistance from the good people here, however it has one drawback. The kids in Thailand can follow the English birthday description to some extent but wife's mum (and others) have no idea what so ever.

So thought perhaps changing the code and building a complete new unit to allow a TFT display to show a photo of the "birthday person" rather than the English text detail on the old 1602 LCD.

Question is, would following the same/similar logical code arrangement and using an SD card with photos which could be displayed on the birthday date work in the same way by monitoring the RTC?
This plus display an analog clock on "no-birthdays today ".

Original 1602 code.

[code]
/* Use Arduino Promini and DS3231 RTC
   Unsolder 201 resistor from DS3231 module if not using rechargable memory battery
   Button on pin 7 is just to show alarm LED and "Birthday Test" text
   Button on pin 9 is to scan through the names and dates stored
   LCD used is 1602 I2C

   Adjustment Required......Go to Line 41 below and replace "xxx" with peoples name (can be capital letters)
   Then Replace the 1,1 with their birthday DAY and then MONTH ...example is shown in the comments alongside
   I had occasion to use the clock for Thailand and so I replaced the hours and the year with the following 
   Added 543 years to current year in Grigorial Calendar and subtracted 3 hours from the current time here to give correct time there
   Example is commented out .....Line 133, 134 and 135 for hours......Line 158, 159 and 160 for the year
*/

#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>
#include "RTClib.h"
RTC_DS1307 rtc;

hd44780_I2Cexp lcd;
const int LCD_COLS = 16;
const int LCD_ROWS = 2;

byte scanButton = 9;
byte buttonState;
byte ledPin = 8;
byte testButton = 7;
byte tbState;
const long interval (200);

const char daysOfTheWeek[7][12] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"}; // changed char to const char to move from SRAM....1/8/2022
const char monthOfYear[13][5]  = {" ", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEPT", "OCT", "NOV", "DEC"};// changed char to const char to move from SRAM....1/8/2022

struct People {        // set up the struct
  char name[10];
  byte day;
  byte month;
};

const People birthdays[] = {
  {"xxx", 1, 1},      // my people name, day and month of birthday (Up to 7 letters in name......next is day....next is month
  {"xxx", 1, 1},      // example Jane on 21 February would be ...   {"Jane",21,2},
  {"xxx", 1, 1},
  {"xxx", 1, 1},
  {"xxx", 1, 1},
  {"xxx", 1, 1},
  {"xxx", 1, 1},
  {"xxx", 1, 1}

};

void setup() {
  Serial.begin(9600);             // for serial monitor
  pinMode(scanButton, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  pinMode(testButton, INPUT_PULLUP);
  lcd.begin(LCD_COLS, LCD_ROWS);  // added this for LCD...hd44780 3/8/2022 ...end of added/changed code for hd44780 library
  lcd.backlight();              // to power ON the back light
  if (! rtc.begin())            // real time clock check
  {
    lcd.print("Couldn't find RTC");
    while (1);
  }
  if (! rtc.isrunning())
  {
    lcd.print("RTC is NOT running!");
  }
  //rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));//auto update from computer time
  //rtc.adjust(DateTime(2022, 10, 9, 6, 28, 0));// to set the time manually............ year (Thai plus 543...month....day.....hour(24) ...minute...second)

  digitalWrite(ledPin, LOW);   // make sure led is off to begin with

}

void loop() {
  buttonState = digitalRead(scanButton); // check if scan button changed
  if (buttonState == LOW) {
    scanBirthdays();
  }

  tbState = digitalRead(testButton);    //check if test button has been pressed
  if (tbState == LOW) {
    testButtonCode();
  }

  DateTime now = rtc.now();      // check rtc

  for (uint8_t x = 0; x < sizeof (birthdays) / sizeof(birthdays[0]); x++) {  // loop through list ....const People birthdays
    if ( birthdays[x].month == now.month() &&
         birthdays[x].day == now.day()) {   // see if any birthdays match todays day and month
      for (int j = 0; j < 10; j++) {
        digitalWrite(ledPin, HIGH);        // flash LED 10 times
        delay(interval);
        digitalWrite(ledPin, LOW);
        delay(interval);
      }

      lcd.setCursor(0, 0);                        //if they do then show on the LCD display
      lcd.print(" HAPPY BIRTHDAY ");
      lcd.setCursor(0, 1);
      lcd.print("                ");
      lcd.setCursor(6, 1);
      lcd.print(birthdays[x].name);
      lcd.print("    ");
      for (int i = 0; i < 10; i++) {     //flash LED 10 times again
        digitalWrite(ledPin, HIGH);
        delay(interval);
        digitalWrite(ledPin, LOW);
        delay(interval);
      }

    }

    else {
      Clock();                                      // jump to display clock
    }                                             // placing clock here gives 8 second clock display
    // if above for loop operates
  }
}

//*************************************end of loop**************************************************
void Clock() {
  // print bottom line on 16 x 2 display
  DateTime now = rtc.now();                // check time on real time clock
  lcd.setCursor(1, 1);                     // display on LCD
  lcd.print("TIME");
  lcd.print(" ");

  if (now.hour() < 10) {
    lcd.print("0");
  }
  
  lcd.print(now.hour());          //comment out to allow time set via pc with 3 hours less for Thailand time
  //int thaiHour = now.hour()-3;  //then comment in these 2 lines 
  //lcd.print(thaiHour);          //similar below for the difference in Thailand years
  
  lcd.print(':');
  if (now.minute() < 10) {                 // "0" leading if less than 10
    lcd.print("0");
  }
  lcd.print(now.minute());
  lcd.print(':');
  if (now.second() < 10) {
    lcd.print("0");
  }
  lcd.print(now.second());
  lcd.print("  ");

  //print top line on 16 x 2 display
  lcd.setCursor(0, 0);
  lcd.print(" ");
  lcd.print(daysOfTheWeek[now.dayOfTheWeek()]);
  lcd.print("  ");
  lcd.print(now.day());
  lcd.print('/');
  lcd.print(now.month());
  lcd.print('/');
  lcd.print(now.year());            // comment out this line for Thailand year
  //int thaiYear = now.year() + 543;  // Then comment in these next 2 lines.....Thai year = +543 years
  //lcd.print(thaiYear);
  
  lcd.print("  ");
  delay(1000);
}

//*******************************end of "clock"******************************************************


void scanBirthdays() {
  for (uint8_t x = 0; x < sizeof (birthdays) / sizeof(birthdays[0]); x++) {

    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(birthdays[x].name); //scan the struct and look for names
    lcd.print(" BIRTHDAY");
    lcd.setCursor(0, 1);
    lcd.print(" ");
    lcd.print(birthdays[x].day);  //select the one that co-incides with the number through the for loop
    lcd.print(" of ");

    int monthValue = birthdays[x].month;

    switch (monthValue) {
      case 1:
        lcd.print("JANUARY");
        break;
      case 2:
        lcd.print("FEBRUARY");
        break;
      case 3:
        lcd.print("March   ");
        break;
      case 4:
        lcd.print("April   ");
        break;
      case 5:
        lcd.print("MAY     ");
        break;
      case 6:
        lcd.print("JUNE     ");
        break;
      case 7:
        lcd.print("JULY     ");
        break;
      case 8:
        lcd.print("AUGUST   ");
        break;
      case 9:
        lcd.print("SEPTEMBER");
        break;
      case 10:
        lcd.print("OCTOBER  ");
        break;
      case 11:
        lcd.print("NOVEMBER ");
        break;
      case 12:
        lcd.print("DECEMBER");
        break;
      default:
        lcd.print("ERROR   ");
        break;

    }
    delay(4000);
  }
}


void testButtonCode() {
  lcd.print("    HAPPY       ");
  lcd.setCursor (0, 1);
  lcd.print(" BIRTHDAY TEST  ");

  for (int i = 0; i < 20; i++) {     //flash LED 20 times
    digitalWrite(ledPin, HIGH);
    delay(interval);
    digitalWrite(ledPin, LOW);
    delay(interval);
  }

}

//************************************program end***********************************************
[/code]

Perhaps something lost in the translation, however, it does have influence on what is displayed on the screen.
Program uses the info from what time/date it is to decide which photo to display.
After all, it is a birthday clock.
I bought a 1.8" display and practiced programming it to display a series of 10 bmp 24 bit images to it as in a "photo frame" but I had more strife than Flash Gordon.
Turned out the display was faulty, so no, I don't have any successful code to show although they are on the web in their 10's with most claiming "easy to do" so don't see that as being a real hurdle.

Was just curious if the same basic approach for the old system would be similar for the TFT display arrangement and it appears you have answered that, thanks.

That was the query....thanks anyhow.

Original display TFT code used on dodgy display.

[code]
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <SPI.h>
#include <SD.h>

#define TFT_CS  10  // Chip select line for TFT display
#define TFT_RST  8  // Reset line for TFT (or see below...)
#define TFT_DC   9  // Data/command line for TFT
#define SD_CS    4  // Chip select line for SD card

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

void setup(void) {
  Serial.begin(9600);

  tft.initR(INITR_BLACKTAB);

  Serial.print("Initializing SD card...");
  if (!SD.begin(SD_CS)) {
    Serial.println("failed!");
    return;
  }
  Serial.println("OK!");

  tft.setRotation(1); // Landscape

}

void loop() {

  bmpDraw("logo.bmp", 0, 0);
  delay(3000);
  bmpDraw("mezapos.bmp",0,0);
  delay(3000);
  bmpDraw("sparti.bmp",0,0);
  delay(3000);
  bmpDraw("mani.bmp",0,0);
  delay(3000);
  bmpDraw("lisbon.bmp",0,0);
  delay(3000);

}


#define BUFFPIXEL 20

void bmpDraw(char *filename, uint8_t x, uint8_t y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // W+H in pixels
  uint8_t  bmpDepth;              // Bit depth (currently must be 24)
  uint32_t bmpImageoffset;        // Start of image data in file
  uint32_t rowSize;               // Not always = bmpWidth; may have padding
  uint8_t  sdbuffer[3*BUFFPIXEL]; // pixel buffer (R+G+B per pixel)
  uint8_t  buffidx = sizeof(sdbuffer); // Current position in sdbuffer
  boolean  goodBmp = false;       // Set to true on valid header parse
  boolean  flip    = true;        // BMP is stored bottom-to-top
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();

  if((x >= tft.width()) || (y >= tft.height())) return;

  Serial.println();
  Serial.print("Loading image '");
  Serial.print(filename);
  Serial.println('\'');

  // Open requested file on SD card
  if ((bmpFile = SD.open(filename)) == NULL) {
    Serial.print("File not found");
    return;
  }

  // Parse BMP header
  if(read16(bmpFile) == 0x4D42) { // BMP signature
    Serial.print("File size: "); Serial.println(read32(bmpFile));
    (void)read32(bmpFile); // Read & ignore creator bytes
    bmpImageoffset = read32(bmpFile); // Start of image data
    Serial.print("Image Offset: "); Serial.println(bmpImageoffset, DEC);
    // Read DIB header
    Serial.print("Header size: "); Serial.println(read32(bmpFile));
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if(read16(bmpFile) == 1) { // # planes -- must be '1'
      bmpDepth = read16(bmpFile); // bits per pixel
      Serial.print("Bit Depth: "); Serial.println(bmpDepth);
      if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed

        goodBmp = true; // Supported BMP format -- proceed!
        Serial.print("Image size: ");
        Serial.print(bmpWidth);
        Serial.print('x');
        Serial.println(bmpHeight);

        // BMP rows are padded (if needed) to 4-byte boundary
        rowSize = (bmpWidth * 3 + 3) & ~3;

        // If bmpHeight is negative, image is in top-down order.
        // This is not canon but has been observed in the wild.
        if(bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }

        // Crop area to be loaded
        w = bmpWidth;
        h = bmpHeight;
        if((x+w-1) >= tft.width())  w = tft.width()  - x;
        if((y+h-1) >= tft.height()) h = tft.height() - y;

        // Set TFT address window to clipped image bounds
        tft.setAddrWindow(x, y, x+w-1, y+h-1);

        for (row=0; row<h; row++) { // For each scanline...

          // Seek to start of scan line.  It might seem labor-
          // intensive to be doing this on every line, but this
          // method covers a lot of gritty details like cropping
          // and scanline padding.  Also, the seek only takes
          // place if the file position actually needs to change
          // (avoids a lot of cluster math in SD library).
          if(flip) // Bitmap is stored bottom-to-top order (normal BMP)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else     // Bitmap is stored top-to-bottom
            pos = bmpImageoffset + row * rowSize;
          if(bmpFile.position() != pos) { // Need seek?
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer); // Force buffer reload
          }

          for (col=0; col<w; col++) { // For each pixel...
            // Time to read more pixel data?
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
            }

            // Convert pixel from BMP to TFT format, push to display
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            tft.pushColor(tft.Color565(r,g,b));
          } // end pixel
        } // end scanline
        Serial.print("Loaded in ");
        Serial.print(millis() - startTime);
        Serial.println(" ms");
      } // end goodBmp
    }
  }

  bmpFile.close();
  if(!goodBmp) Serial.println("BMP format not recognized.");
}

// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.

uint16_t read16(File f) {
  uint16_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read(); // MSB
  return result;
}

uint32_t read32(File f) {
  uint32_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read();
  ((uint8_t *)&result)[2] = f.read();
  ((uint8_t *)&result)[3] = f.read(); // MSB
  return result;
}
[/code]

Ok...you win.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.