How to read audio file from SD card on TFT 1.8" ST7735S SKU:MAR1801

Hi Forum,

Regarding TFT 1.8" ST7735S SKU:MAR1801 screen. Details for the screen can be found at LCD Wiki.

I use it together with an Uno and a DS1302 RTC module, to display an analog clock and some text. That works fine.

This TFT screen also has a SD card reader at the back. I would like to use it to read audio files from an SD card and play them on a tiny speaker for use in a model-train setup.

See image http://www.lcdwiki.com/images/f/f6/MAR1801-003.jpg. I'm assuming a header needs to be put on the P2 location for the 4 pins SD_CS, SD_MOSI, SD_SCK and SD_MISO. At least, without connecting these pins, any attempt to initialize the SD fails.

// Check if SD card is connected and can be initialized.
if (!SD.begin(SD_CS)) {
  Serial.println("SD Card initialization failed!");
  return;
}
  1. How should these 4 SD pins be connected to Uno?
  2. Note that for the TFT screen, DS1302 RTC module, a button and the small speaker, most digital pins on the Uno are already occupied. Can I use A0 to A5 as an alternative for the SD connections?
  3. Is there some example of how to initialize the SD card reader if it is not on standard pins?

Help and suggestions are highly appreciated. Please advise.

These are the connections:

UNO      -      TFT 1.8 inch ST7735S SKU:MAR1801
5V              1 VCC
GND             2 GND
GND             3 GND
                4 NC
                5 NC
                6 NC
13              7 CLK
11              8 SDA
8               9 RS/DC
9               10 RST
10              11 CS
                (solder-on header P2)
?               12 SD_CS
?               13 SD_MOSI
?               14 SD_SCK
?               15 SD_MISO

UNO     -       DS1302 RTC Module
5V              VCC
GND             GND
6               CLK
5               DAT
4               RST

From the project section:

https://projecthub.arduino.cc/HUNMAN/3679efd4-3a86-4eef-87b8-94b5e1c87714

For MP3s:

Thanks for your reply! I've seen such examples. My worry is that they rely on certain pins on my UNO, that are already in use for the TFT screen. I have no more digital pins left open on my UNO. The only available ones are A0 to A5. Would I be able to use them? How would the SD library know that I'm using these pins as an alternative connection for SD_CS, SC_MOSI, SD_SCK and SD_MISO?

If using the SD.h library on UNO, the library expects these defaults defined in SDcard.h:

   SD card attached to ATmel 328P-PU SPI bus as follows:
      MOSI - pin 11
      MISO - pin 12
      CLK  - pin 13
      CS   - pin 10

Reference: SD/Sd2Card.h at master · arduino-libraries/SD · GitHub

Personally, I would re-work what code you already have to free up the default pins; it makes life easier to reuse code when you utilize long-standing wiring conventions: however you can use other digital pins, in theory.
Analog Input Pins | Arduino Documentation | Arduino Documentation

While the main function of the analog pins for most Arduino users is to read analog sensors, the analog pins also have all the functionality of general purpose input/output (GPIO) pins (the same as digital pins 0 - 13).

Suggestion: you really need to put in a bit of effort doing your own search online; you will learn much more conducting your own research.

@mrburnette , thanks for your reply! Sorry for asking such novice questions. Did do a lot of research, but maybe not in the right places. Or, as a novice, I might not directly understand all implications of what I'm reading. I'm aksing these questions to learn more and verify my assumptions are correct.

Regarding reading from SD card, I found e.g.:

All these examples rely on the SD.begin(SD_ChipSelectPin) function. And therefore use the default pins that you mentioned.
If I only needed the SD card I could wire it up on the default pins and it would probably work. Will get a header on the P2, connect it up without TFT screen and verify it works for audio only.

But what I really need is a combination of displaying text/graphics on the TFT screen and playing the audio from the SD card. I have yet to find an example that uses both together on TFT 1.8” ST7735S SKU:MAR1801.
Also tried to hook the screen up by plugging it into the Uno directly as described in picture 2 in the manual on http://www.lcdwiki.com/res/MAR1801/1.8inch_Arduino_SPI_Module_MAR1801_User_Manual_EN.pdf. But that didn´t work. All I got was a white screen.
I only got the TFT screen to work by connecting it up as described above. And then it uses the hardware SPI of Uno on pin 13 and 11 for the TFT. (See also Using the ST7735 1.8" Color TFT Display with Arduino - Electronics-Lab.com)

Uno             TFT (on 11 pins standard header)

13              7 CLK
11              8 SDA
8               9 RS/DC
9               10 RST
10              11 CS

SD Card would require some of the same pins. Probably need to hook it up like this?

UNO             SD card on header P2

10              12 SD_CS
11              13 SD_MOSI
13              14 SD_SCK
12              15 SD_MISO

So my dilemma is that both the TFT screen and the SD card need to use some of the same SPI pins on the UNO. Looks like both SD and TFT library (Adafruit_GFX.h and Adafruit_ST7735.h) expect to be connected to the same default pins. At least, that is my novice interpretation of what I found sofar. Is there an alternative way of connecting both TFT and SD card reader on TFT 1.8” ST7735S SKU:MAR1801 to Uno, given the restrictions described above?

Maybe I don't fully grasp the SPI bus concept yet. On How to connect multiple SPI sensors/devices with Arduino | Arduino FAQs, they state "Three pins (MOSI,MISO,SCK) must be shared between SPI devices." Not sure if that would apply here and how to proceed on that?

Just noticed this code snippet on Using the ST7735 1.8" Color TFT Display with Arduino - Electronics-Lab.com :

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

// Option 2: use any pins but a little slower!
#define TFT_SCLK 13   // set these to be whatever pins you like!
#define TFT_MOSI 11   // set these to be whatever pins you like!
//Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

The commented out "Option 2" might allow me to use alternative pins for the TFT part. That might allow me to free up the shared pins and to follow the suggestion by @mrburnette to use the default SD pins. Will experiment with that first and come back on it.

Maybe this will help:

Arduino & Serial Peripheral Interface (SPI) | Arduino Documentation | Arduino Documentation

Added:
Arduino - TFT Display of Bitmap Images From an SD Card : 7 Steps - Instructables

Added-2: From the Instructable link (above) & Zip:

// On the SD card use 24 bit color BMP files (be sure they are 24-bit!)
// There are examples images in the sketch folder.

// Change IDE compiler option to -O2 as per Instructable to boost speed 
// http://www.instructables.com/id/Arduino-IDE-16x-compiler-optimisations-faster-code/
// this example then uses 85% of UNO FLASH and 52% of RAM for dynamic storage


// MS Paint can be used to crop, resize and flip and save images in 24bit BMP format

//          ######################## WARNINGS ########################
// ####     Only enable Font 2 or the UNO willl run out of FLASH memory!      ####
// ####  Do not try to use the F_AS_T option in the ILI9341_AS library yet    ####
// #### Change IDE compiler option to -O2 as per Instructable to boost speed  ####


// Updated 31/3/15 to include new drawRAW() function that draws raw 16 bit images.
// Raw images are created with UTFT library tool (libraries\UTFT\Tools\ImageConverter565.exe)
// Save as .raw file as this can be easily piped to the TFT pushColors() function
// without any tedious byte swapping etc
 
#include <Adafruit_GFX_AS.h>     // Core graphics library
#include <Adafruit_ILI9341_AS.h> // Hardware-specific library
//#include <SD.h>                // SD card library a bit slower and bigeger than SdFat
#include <SdFat.h>               // More compact and faster than SD
SdFat SD;                        // For SD compatibility
#include <SPI.h>                 // SPI libray obviously!

// These are the pins I use on an UNO, may need chaging for your setup

// Use hardware SPI lines
#define _sclk 13
#define _miso 12 // Needed for SD card, but does not need to be connected to TFT
#define _mosi 11 // Master Out Slave In to send commands/data to TFT and SD card

// TFT chip select and data/command line
#define _cs 10
#define _dc 9

// SD chip select
#define _sdcs 8

// TFT reset line, can be connected to Arduino reset
#define _rst 7

Adafruit_ILI9341_AS tft = Adafruit_ILI9341_AS(_cs, _dc, _rst); // Invoke custom library

// You can use MS Paint to pick colours off an image and see the RGB values
// This colour is off the mouse image.
#define ILI9341_GREY 0xCE9A // Light grey

// These are used when calling drawBMP() function, see examples in loop()
#define BU_BMP 1 // Temporarily flip the TFT coords for standard Bottom-Up bit maps
#define TD_BMP 0 // Draw inverted Top-Down bitmaps in standard coord frame

uint32_t drawTime = 0; // Variable to save draw times for testing

/***************************************************************************************
** Function name:           setup
** Descriptions:            To set things up
***************************************************************************************/

void setup()
{
  Serial.begin(38400); // For debug messages

  Serial.print(F("Initialising SD card..."));
  if (!SD.begin(_sdcs, SPI_FULL_SPEED)) {  // sdFat library allows speed setting, e.g. SPI_HALF_SPEED
    //if (!SD.begin(_sdcs)) {              // Only needed when standard SD library is used
    Serial.println(F("failed!"));
    //return;
  }

  tft.init(); // Initialise the display (various parameters configured)

  Serial.println(F("Here we go..."));

  // Set text foreground and background colours
  tft.setTextColor(ILI9341_BLACK, ILI9341_WHITE);
}

/***************************************************************************************
** Function name:           loop
** Descriptions:            Infinite loop
***************************************************************************************/

void loop()
{
  // Standard BMP files are usually stored with a raster scan of pixel values from bottom up
  // File names MUST use the 8.3 file name format (8 chars + . + any 3 extension letters)
  // By convention bitmap files end in .bmp
  // Bit maps MUST be in 24 bit colout (not 16 or 8 bit greyscale)
  
  // Landscape mode
  tft.setRotation(1);
  tft.fillScreen(ILI9341_GREY);
  
  // Draws raw image with top left corner pixel at x,y = 0,0
  drawRAW("Inst_1.raw", 0, 0, 320, 240);
  // Using a bmp will be 50% slower and draws from bottom up
  //drawBMP("Inst_1.bmp", 0, 0, BU_BMP);
  
  // Show draw time (time measuring lines can be commented out in function if not needed)
  int xpos = 0;
  xpos += tft.drawNumber(drawTime, xpos, 0, 2);
  tft.drawString("ms to draw", xpos, 0, 2);
      Serial.println(drawTime);
  delay(4000);

  // See how long slear screen takes
  unsigned long timenow = millis();
  tft.fillScreen(ILI9341_BLACK);
  timenow = millis()-timenow;
  Serial.println(timenow);

  // Draw bmp image top left corner at x,y = 40,0 which centres the 240 pixel wide bitmap
  // Image must fit (one day I will add clipping... but it will slow things down)
  drawBMP("240Moon.bmp", 40, 0, BU_BMP);
  xpos = 0;
  xpos += tft.drawNumber(drawTime, xpos, 0, 2);
  tft.drawString("ms to draw", xpos, 0, 2);
  delay(1000);

  // Now draw the raw image for speed comparison
  tft.fillScreen(ILI9341_BLACK);
  // Draw image top left corner at x,y = 40,0 which centres the 240 pixel wide bitmap
  // Image must fit (one day I will add clipping... but it will slow things down)
  drawRAW("240Moon.raw", 40, 0, 240, 240);
  xpos = 0;
  xpos += tft.drawNumber(drawTime, xpos, 0, 2);
  tft.drawString("ms to draw", xpos, 0, 2);
  Serial.println(drawTime);
  delay(1000);

  // Display Terminator image grey was picked off image by using MS Paint
  // and turned into a 565 BGR 16 bit format
  tft.fillScreen(0x94f5);
  // Look out
  drawRAW("225term.raw", 47, 7, 225, 225);
  delay(500);
  // We can draw on an image... plot coords can easily be determined with Paint and a calculator
  tft.fillCircle(47 + 80, 7 + 101, 3, ILI9341_RED);
  tft.fillCircle(47 + 140, 7 + 101, 3, ILI9341_RED);
  delay(1000);

  // To draw top down the image must be flipped vertically and stored, this is
  // easy to do in Paint. We can then use the TFT SGRAM flip features to make drawing faster...
  // May be a pain ;-) but drawing is >25% faster that doing the flip via file pointers!
  // and it only takes 2 clicks in Paint to "Flip Vertical", then can be saved upside down!
  tft.fillScreen(0x94f5);
  drawBMP("225termi.bmp", 47, 7, TD_BMP); // Note we have to set the flag for Top-Down drawing
  // TD_BMP flag invokes SGRAM coord flip in display, orientation is fliiped back by function
  // The x and y coordinates are of the image top left corner are still 0,0 now!
  delay(500);
  // We can draw upon an image... notice coords are the same as bottom up plot above
  // the drawBMP makes sure the inverted image is plotted in the correct relative coords!
  tft.fillCircle(47 + 80, 7 + 101, 3, ILI9341_RED);
  tft.fillCircle(47 + 140, 7 + 101, 3, ILI9341_RED);
  delay(1000);

  // A selfie - what I really look like when writing software!
  tft.fillScreen(ILI9341_WHITE);
  drawBMP("284taz.bmp", 18, 32, BU_BMP);
  delay(1000);

  // Draw the obligatory cute cat from a family holiday... to London!
  tft.setRotation(0); // We must set the orientation so the Tiger image fits!
  // the screen will be blank otherwise! (To do -> clipping to screen!)
  tft.fillScreen(ILI9341_WHITE);
  drawRAW("Tiger.raw", 0, 0, 240, 320);
  delay(1000);

  // Switch rotaion back to landscape
  tft.setRotation(1);
  tft.fillScreen(ILI9341_GREY);
  drawBMP("Inst_1.bmp", 0, 0, BU_BMP);
  delay(4000);

  // Test all standard rotations 0-3 with inverted top-down images
  // This is an orientation and colour encoding test bitmap created with MS Paint
  tft.setRotation(0);
  tft.fillScreen(ILI9341_GREY);
  drawBMP("test_dn.bmp", 0, 0, TD_BMP);
  delay(1000);

  tft.setRotation(1);
  tft.fillScreen(ILI9341_BLACK);
  drawBMP("test_dn.bmp", 0, 0, TD_BMP);
  delay(1000);

  tft.setRotation(2);
  tft.fillScreen(ILI9341_GREY);
  drawBMP("test_dn.bmp", 0, 0, TD_BMP);
  delay(1000);

  tft.setRotation(3);
  tft.fillScreen(ILI9341_BLACK);
  drawBMP("test_dn.bmp", 0, 0, TD_BMP);
  delay(1000);

  // Test all standard rotations 0-3 with small bottom-up images
  // Image used above was shrunk 50% with Paint
  tft.setRotation(0);
  tft.fillScreen(ILI9341_BLACK);
  drawBMP("test_ups.bmp", 0, 0, BU_BMP);
  delay(1000);

  tft.setRotation(1);
  tft.fillScreen(ILI9341_BLACK);
  drawBMP("test_ups.bmp", 0, 0, BU_BMP);
  delay(1000);

  tft.setRotation(2);
  tft.fillScreen(ILI9341_BLACK);
  drawBMP("test_ups.bmp", 0, 0, BU_BMP);
  delay(1000);

  tft.setRotation(3);
  tft.fillScreen(ILI9341_BLACK);
  drawBMP("test_ups.bmp", 0, 0, BU_BMP);
  delay(1000);

  // We could write a function to list and draw all bitmaps...
  // Could add "i" at start of all filenames to indicate an inverted
  // top-down image...
}


/***************************************************************************************
** Function name:           drawBMP
** Descriptions:            draw a BMP format bitmap to the screen
***************************************************************************************/

// This function opens a Windows Bitmap (BMP) file and
// displays it at the given coordinates.  It's sped up
// by reading many pixels worth of data at a time
// (rather than pixel by pixel).  Increasing the buffer
// size makes loading a little faster but the law of
// rapidly diminishing speed improvements applies.
// Suggest 8 minimum and 85 maximum (3 x this value is
// stored in a byte = 255/3 max!)
// A value of 8 is only ~20% slower than 24 or 48!
// Note that 5 x this value of RAM bytes will be needed
// Increasing beyond 48 gives little benefit.
// Use integral division of TFT (or typical often used image)
// width for slightly better speed to avoid short buffer purging

#define BUFF_SIZE 80

void drawBMP(char *filename, int x, int y, boolean flip) {
  if ((x >= tft.width()) || (y >= tft.height())) return;
  File     bmpFile;
  int16_t  bmpWidth, bmpHeight;   // Image W+H in pixels
  //uint8_t  bmpDepth;            // Bit depth (must be 24) but we dont use this
  uint32_t bmpImageoffset;        // Start address of image data in file
  uint32_t rowSize;               // Not always = bmpWidth; may have padding
  uint8_t  sdbuffer[3 * BUFF_SIZE];    // SD read pixel buffer (8 bits each R+G+B per pixel)
  uint16_t tftbuffer[BUFF_SIZE];       // TFT pixel out buffer (16-bit per pixel)
  uint8_t  sd_ptr = sizeof(sdbuffer); // sdbuffer pointer (so BUFF_SIZE must be less than 86)
  boolean  goodBmp = false;            // Flag set to true on valid header parse
  int16_t  w, h, row, col;             // to store width, height, row and column
  //uint8_t  r, g, b;   // brg encoding line concatenated for speed so not used
  uint8_t rotation;     // to restore rotation
  uint8_t  tft_ptr = 0;  // buffer pointer

  // Check file exists and open it
  if ((bmpFile = SD.open(filename)) == NULL) {
    Serial.println(F("File not found")); // Can comment out if not needed
    return;
  }

  drawTime = millis(); // Save current time for performance evaluation, comment out if not needed

  // Parse BMP header to get the information we need
  if (read16(bmpFile) == 0x4D42) { // BMP file start signature check
    read32(bmpFile);       // Dummy read to throw away and move on
    read32(bmpFile);       // Read & ignore creator bytes
    bmpImageoffset = read32(bmpFile); // Start of image data
    read32(bmpFile);       // Dummy read to throw away and move on
    bmpWidth  = read32(bmpFile);  // Image width
    bmpHeight = read32(bmpFile);  // Image height

    //if (read16(bmpFile) == 1) { // Number of image planes -- must be '1'
    // Only proceed if we pass a bitmap file check
    if ((read16(bmpFile) == 1) && (read16(bmpFile) == 24) && (read32(bmpFile) == 0)) { // Must be depth 24 and 0 (uncompressed format)
      //goodBmp = true; // Supported BMP format -- proceed!
      // BMP rows are padded (if needed) to 4-byte boundary
      rowSize = (bmpWidth * 3 + 3) & ~3;
      // Crop area to be loaded
      w = bmpWidth;
      h = bmpHeight;

      // We might need to alter rotation to avoid tedious pointer manipulation
      // Save the current value so we can restore it later
      rotation = tft.getRotation();
      // Use TFT SGRAM coord rotation if flip is set for 25% faster rendering
      if (flip) tft.setRotation((rotation + (flip<<2)) % 8); // Value 0-3 mapped to 4-7

      // We might need to flip and calculate new y plot coordinate
      // relative to top left corner as well...
      switch (rotation) {
        case 0:
          if (flip) y = tft.height() - y - h; break;
        case 1:
          y = tft.height() - y - h; break;
          break;
        case 2:
          if (flip) y = tft.height() - y - h; break;
          break;
        case 3:
          y = tft.height() - y - h; break;
          break;
      }

      // Set TFT address window to image bounds
      // Currently, image will not draw or will be corrputed if it does not fit
      // TODO -> efficient clipping, I don't need it to be idiot proof ;-)
      tft.setAddrWindow(x, y, x + w - 1, y + h - 1);

      // Finally we are ready to send rows of pixels, writing like this avoids slow 32 bit multiply
      for (uint32_t pos = bmpImageoffset; pos < bmpImageoffset + h * rowSize ; pos += rowSize) {
        // Seek if we need to on boundaries and arrange to dump buffer and start again
        if (bmpFile.position() != pos) {
          bmpFile.seek(pos);
          sd_ptr = sizeof(sdbuffer);        }

        // Fill the pixel buffer and plot
        for (col = 0; col < w; col++) { // For each column...
          // Time to read more pixel data?
          if (sd_ptr >= sizeof(sdbuffer)) {
            // Push tft buffer to the display
            if (tft_ptr) {
              // Here we are sending a uint16_t array to the function
              tft.pushColors(tftbuffer, tft_ptr);
              tft_ptr = 0; // tft_ptr and sd_ptr are not always in sync...
            }
            // Finally reading bytes from SD Card
            bmpFile.read(sdbuffer, sizeof(sdbuffer));
            sd_ptr = 0; // Set buffer index to start
          }
          // Convert pixel from BMP 8+8+8 format to TFT compatible 16 bit word
          // Blue 5 bits, green 6 bits and red 5 bits (16 bits total)
          // Is is a long line but it is faster than calling a library fn for this
          tftbuffer[tft_ptr++] = (sdbuffer[sd_ptr++] >> 3) | ((sdbuffer[sd_ptr++] & 0xFC) << 3) | ((sdbuffer[sd_ptr++] & 0xF8) << 8);
        } // Next row
      }   // All rows done

      // Write any partially full buffer to TFT
      if (tft_ptr) tft.pushColors(tftbuffer, tft_ptr);
      drawTime = millis() - drawTime;
    } // End of bitmap access
  }   // End of bitmap file check
  //}     // We can close the file now

  bmpFile.close();
  //if(!goodBmp) Serial.println(F("BMP format not recognized."));
  tft.setRotation(rotation); // Put back original rotation
}

/***************************************************************************************
** Function name:           drawRAW
** Descriptions:            draw a 565 format 16 bit raw image file
***************************************************************************************/

// This function opens a ".raw" image file and displays it at the given coordinates.
// It is faster than plotting BMP images as the file is already in the correct
// format to pipe directly to the display.
// Uses same BUFF_SIZE as BMP function.
// The width and hsight of the image in pixels must be passed to the function
// as these parameters are not in the file

void drawRAW(char *filename, int16_t x, int16_t y, int16_t rawWidth, int16_t rawHeight) {
  File     rawFile;
  uint8_t  sdbuffer[2 * BUFF_SIZE];   // SD read pixel buffer (16 bits per pixel)

  // Check file exists and open it
  if ((rawFile = SD.open(filename)) == NULL) {
    Serial.println(F("File not found"));
    return;
  }

  drawTime = millis(); // Save current time for performance evaluation

  tft.setAddrWindow(x, y, x + rawWidth - 1, y + rawHeight - 1);

  // Work out how many whole buffers to send
  uint16_t nr = ((long)rawHeight * rawWidth)/BUFF_SIZE;
  while(nr--) {
    rawFile.read(sdbuffer, sizeof(sdbuffer));
    tft.pushColors(sdbuffer, BUFF_SIZE);
  }
  
  // Send any partial buffer
  nr = ((long)rawHeight * rawWidth)%BUFF_SIZE;
  if (nr) {
    rawFile.read(sdbuffer, nr<<1); // We load  2 x BUFF_SIZE bytes
    tft.pushColors(sdbuffer, nr);      // We send BUF_SIZE pixels
  }
    
  drawTime = millis() - drawTime;
  rawFile.close();
}

/***************************************************************************************
** Function name:           Support functions for drawBMP()
** Descriptions:            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;
}


Thanks for the links! Will dive into the matter and see what I can get to work. Might be a while, but will come back when I have some results to share.

Tried many things, but can't get the SD card on the back of the TFT 1.8” ST7735S SKU:MAR1801 to work. If someone has succeeded on this, please let me know how.

For now I decided to split the functionality over 2 Uno's. Already had the part to display the graphics. And used another Uno to play the audio. Because with a standard SD card, I can get it to work. My setup below is based on this link provided by @mrburnette.

Used this LM386 audio amplifier module, together with a mini speaker. And this SD card reader

#include <SPI.h>
#include <SD.h>
#include "TMRpcm.h"

TMRpcm audio;

char fragments[][3][20] = {
  {"1.wav", "3.wav", "1.wav"},
  {"1.wav", "1.wav", "2.wav"},
  {"1.wav", "2.wav", "3.wav"},
  {"2.wav", "1.wav", "1.wav"},
  {"2.wav", "2.wav", "1.wav"},
  {"2.wav", "3.wav", "2.wav"}
};
const int numOfFragments = sizeof(fragments) / sizeof(fragments[0]);

// Pin to trigger next message to play
const int cycleMessagePin = 2;

// Schedule messages for OLED display variables
byte msgIndex = 0, msgIndex_old = 9999;

// Pin where the audio module is connected
const int audioPin = 9;

// variable to keep track of cycle message trigger (e.g. a button, a relais, etc.)
int cycleState = 0;
int lastCycleState = 0;

void setup()
{
  audio.speakerPin = audioPin;
  Serial.begin(9600);
  Serial.print("Initializing SD card...");
  if (!SD.begin(4)) {
    Serial.println(" failed!");
    while(true);
  }
  Serial.println(" done.");
  
  audio.setVolume(5);
}

void loop()
{
  // check if the pushbutton is pressed. If it is, the buttonState is HIGH
  cycleState = digitalRead(cycleMessagePin);
  if (cycleState != lastCycleState) {
    if (cycleState == HIGH) {
      // // Switch on LED
      // digitalWrite(ledPin, HIGH); // turn LED on
      // Increase the message index by 1. Jump back to 0 after last message.
      Serial.print("cycleState = HIGH. New index = ");
      Serial.println(msgIndex);
      for (int i = 0; i < 3; i++)
      {
        if (strlen(fragments[msgIndex][i]) > 0) {
          Serial.print("Playing ");
          Serial.println(fragments[msgIndex][i]);
          audio.play(fragments[msgIndex][i]);
          while (audio.isPlaying())
          {
            delay(50);
          }
        }
      }
      msgIndex += 1;
      if (msgIndex >= numOfFragments) msgIndex = 0;
    }
    // Keep track of the last cycle state.
    lastCycleState = cycleState;
  }
}

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