Implement display, SD card, I2S WAV audio player, and button sync logic

This commit is contained in:
drjones
2026-06-30 01:08:27 -07:00
parent 23107b98d7
commit d48ad18134
9 changed files with 795 additions and 0 deletions

View File

@@ -0,0 +1,186 @@
#include "Audio_Player.h"
#include <ESP_I2S.h>
#include <algorithm>
I2SClass i2s;
TaskHandle_t audioTaskHandle = NULL;
File audioFile;
volatile bool isAudioPlaying = false;
char currentPlayPath[128];
uint16_t Wav_Count = 0;
char Wav_Names[100][100];
void Search_Wavs(const char* directory) {
Wav_Count = Folder_retrieval(directory, ".wav", Wav_Names, 100);
}
// Parse WAV header and skip meta chunks (like LIST) to find the "fmt " and "data" segments
bool parseWavHeader(File& file, uint32_t& sampleRate, uint16_t& channels, uint16_t& bitsPerSample, uint32_t& dataStartOffset, uint32_t& dataSize) {
char id[4];
// Read RIFF header
file.seek(0);
if (file.readBytes(id, 4) != 4 || memcmp(id, "RIFF", 4) != 0) {
printf("WAV Header Error: RIFF signature not found!\r\n");
return false;
}
file.seek(8);
if (file.readBytes(id, 4) != 4 || memcmp(id, "WAVE", 4) != 0) {
printf("WAV Header Error: WAVE signature not found!\r\n");
return false;
}
uint32_t offset = 12;
bool foundFmt = false;
bool foundData = false;
while (offset < file.size() && (!foundFmt || !foundData)) {
file.seek(offset);
if (file.readBytes(id, 4) != 4) break;
uint32_t chunkSize;
if (file.read((uint8_t*)&chunkSize, 4) != 4) break;
offset += 8; // move past chunk ID and Size field
if (memcmp(id, "fmt ", 4) == 0) {
uint16_t audioFormat;
file.read((uint8_t*)&audioFormat, 2);
if (audioFormat != 1) { // 1 = PCM (uncompressed)
printf("WAV Header Error: Only uncompressed PCM WAV is supported!\r\n");
return false;
}
file.read((uint8_t*)&channels, 2);
file.read((uint8_t*)&sampleRate, 4);
file.seek(offset + 8); // skip byte rate and block align
file.read((uint8_t*)&bitsPerSample, 2);
foundFmt = true;
}
else if (memcmp(id, "data", 4) == 0) {
dataStartOffset = offset;
dataSize = chunkSize;
foundData = true;
}
offset += chunkSize;
}
return foundFmt && foundData;
}
void audioPlayTask(void* parameter) {
uint32_t sampleRate = 44100;
uint16_t channels = 2;
uint16_t bitsPerSample = 16;
uint32_t dataStartOffset = 44;
uint32_t dataSize = 0;
audioFile = SD_MMC.open(currentPlayPath, "r");
if (!audioFile) {
printf("Failed to open audio file: %s\r\n", currentPlayPath);
isAudioPlaying = false;
audioTaskHandle = NULL;
vTaskDelete(NULL);
return;
}
if (!parseWavHeader(audioFile, sampleRate, channels, bitsPerSample, dataStartOffset, dataSize)) {
printf("Failed to parse WAV header\r\n");
audioFile.close();
isAudioPlaying = false;
audioTaskHandle = NULL;
vTaskDelete(NULL);
return;
}
printf("WAV File: Rate=%d Hz, Channels=%d, Bits=%d, Size=%d bytes\r\n",
sampleRate, channels, bitsPerSample, dataSize);
// Configure I2S based on WAV parameters
i2s_data_bit_width_t bitWidth = I2S_DATA_BIT_WIDTH_16BIT;
if (bitsPerSample == 8) bitWidth = I2S_DATA_BIT_WIDTH_8BIT;
else if (bitsPerSample == 24) bitWidth = I2S_DATA_BIT_WIDTH_24BIT;
else if (bitsPerSample == 32) bitWidth = I2S_DATA_BIT_WIDTH_32BIT;
i2s_slot_mode_t slotMode = (channels == 1) ? I2S_SLOT_MODE_MONO : I2S_SLOT_MODE_STEREO;
// Set standard pins
i2s.setPins(I2S_BCLK, I2S_LRCK, I2S_DIN, -1, -1);
// Stop any active driver configuration
i2s.end();
if (!i2s.begin(I2S_MODE_STD, sampleRate, bitWidth, slotMode)) {
printf("Failed to initialize I2S!\r\n");
audioFile.close();
isAudioPlaying = false;
audioTaskHandle = NULL;
vTaskDelete(NULL);
return;
}
// Seek past the parsed headers to raw data
audioFile.seek(dataStartOffset);
const size_t bufferSize = 1024;
uint8_t buffer[bufferSize];
uint32_t bytesRead = 0;
uint32_t totalBytesWritten = 0;
while (isAudioPlaying && totalBytesWritten < dataSize) {
size_t toRead = std::min(bufferSize, (size_t)(dataSize - totalBytesWritten));
bytesRead = audioFile.read(buffer, toRead);
if (bytesRead == 0) {
break;
}
size_t written = i2s.write(buffer, bytesRead);
if (written == 0) {
vTaskDelay(pdMS_TO_TICKS(1));
continue;
}
totalBytesWritten += written;
}
printf("Playback done. Written %d bytes.\r\n", totalBytesWritten);
audioFile.close();
i2s.end();
isAudioPlaying = false;
audioTaskHandle = NULL;
vTaskDelete(NULL);
}
void playWav(const char* filePath) {
if (isAudioPlaying) {
stopWav();
}
strncpy(currentPlayPath, filePath, sizeof(currentPlayPath) - 1);
currentPlayPath[sizeof(currentPlayPath) - 1] = '\0';
isAudioPlaying = true;
xTaskCreatePinnedToCore(
audioPlayTask,
"audioPlayTask",
4096,
NULL,
5,
&audioTaskHandle,
0
);
}
void stopWav() {
if (isAudioPlaying) {
isAudioPlaying = false;
while (audioTaskHandle != NULL) {
vTaskDelay(pdMS_TO_TICKS(5));
}
}
}
bool isWavPlaying() {
return isAudioPlaying;
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include <Arduino.h>
#include "FS.h"
#include "SD_Card.h"
// Define I2S Pins for external DAC (MAX98357A, etc.)
#define I2S_BCLK 1
#define I2S_LRCK 2
#define I2S_DIN 4
void playWav(const char* filePath);
void stopWav();
bool isWavPlaying();
void Search_Wavs(const char* directory);
extern uint16_t Wav_Count;
extern char Wav_Names[100][100];

View File

@@ -0,0 +1,78 @@
#include "Display_PNG.h"
PNG png;
File Image_file;
uint16_t Image_Count = 0;
char Image_Names[100][100];
int16_t xpos = 0;
int16_t ypos = 0;
void * pngOpen(const char *filePath, int32_t *size) {
Image_file = SD_MMC.open(filePath);
*size = Image_file.size();
return &Image_file;
}
void pngClose(void *handle) {
File Image_file = *((File*)handle);
if (Image_file) Image_file.close();
}
int32_t pngRead(PNGFILE *page, uint8_t *buffer, int32_t length) {
if (!Image_file) return 0;
return Image_file.read(buffer, length);
}
int32_t pngSeek(PNGFILE *page, int32_t position) {
if (!Image_file) return 0;
return Image_file.seek(position);
}
static uint16_t lineBuffer[MAX_IMAGE_WIDTH];
void pngDraw(PNGDRAW *pDraw) {
png.getLineAsRGB565(pDraw, lineBuffer, PNG_RGB565_BIG_ENDIAN, 0xffffffff);
uint32_t size = pDraw->iWidth;
for (size_t i = 0; i < size; i++) {
lineBuffer[i] = (((lineBuffer[i] >> 8) & 0xFF) | ((lineBuffer[i] << 8) & 0xFF00));
}
LCD_addWindow(xpos, pDraw->y, xpos + pDraw->iWidth - 1, ypos + pDraw->y, lineBuffer);
}
void Search_Images(const char* directory) {
Image_Count = Folder_retrieval(directory, ".png", Image_Names, 100);
}
void Display_Image_By_Path(const char* filePath) {
printf("Displaying image: %s\r\n", filePath);
int16_t ret = png.open(filePath, pngOpen, pngClose, pngRead, pngSeek, pngDraw);
if (ret == PNG_SUCCESS) {
printf("Image dimensions: %d x %d, %d bpp\r\n", png.getWidth(), png.getHeight(), png.getBpp());
if (png.getWidth() > MAX_IMAGE_WIDTH) {
printf("Image is too wide for line buffer!\r\n");
} else {
ret = png.decode(NULL, 0);
png.close();
}
} else {
printf("Failed to open PNG image! Error code: %d\r\n", ret);
}
}
void Display_Image_By_Index(const char* directory, uint16_t index) {
if (Image_Count == 0) {
Search_Images(directory);
}
if (Image_Count > 0 && index < Image_Count) {
String filePath;
if (String(directory) == "/") {
filePath = String(directory) + Image_Names[index];
} else {
filePath = String(directory) + "/" + Image_Names[index];
}
Display_Image_By_Path(filePath.c_str());
} else {
printf("Invalid image index: %d (Total images: %d)\r\n", index, Image_Count);
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <Arduino.h>
#include <PNGdec.h>
#include "SD_Card.h"
#include "Display_ST7789.h"
#define MAX_IMAGE_WIDTH 172
void Search_Images(const char* directory);
void Display_Image_By_Index(const char* directory, uint16_t index);
void Display_Image_By_Path(const char* filePath);
extern uint16_t Image_Count;
extern char Image_Names[100][100];

View File

@@ -0,0 +1,230 @@
#include "Display_ST7789.h"
SPIClass LCDspi(FSPI);
#define SPI_WRITE(_dat) LCDspi.transfer(_dat)
#define SPI_WRITE_Word(_dat) LCDspi.transfer16(_dat)
void SPI_Init()
{
LCDspi.begin(EXAMPLE_PIN_NUM_SCLK,EXAMPLE_PIN_NUM_MISO,EXAMPLE_PIN_NUM_MOSI);
}
void LCD_WriteCommand(uint8_t Cmd)
{
LCDspi.beginTransaction(SPISettings(SPIFreq, MSBFIRST, SPI_MODE0));
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, LOW);
digitalWrite(EXAMPLE_PIN_NUM_LCD_DC, LOW);
SPI_WRITE(Cmd);
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, HIGH);
LCDspi.endTransaction();
}
void LCD_WriteData(uint8_t Data)
{
LCDspi.beginTransaction(SPISettings(SPIFreq, MSBFIRST, SPI_MODE0));
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, LOW);
digitalWrite(EXAMPLE_PIN_NUM_LCD_DC, HIGH);
SPI_WRITE(Data);
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, HIGH);
LCDspi.endTransaction();
}
void LCD_WriteData_Word(uint16_t Data)
{
LCDspi.beginTransaction(SPISettings(SPIFreq, MSBFIRST, SPI_MODE0));
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, LOW);
digitalWrite(EXAMPLE_PIN_NUM_LCD_DC, HIGH);
SPI_WRITE_Word(Data);
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, HIGH);
LCDspi.endTransaction();
}
void LCD_WriteData_nbyte(uint8_t* SetData,uint8_t* ReadData,uint32_t Size)
{
LCDspi.beginTransaction(SPISettings(SPIFreq, MSBFIRST, SPI_MODE0));
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, LOW);
digitalWrite(EXAMPLE_PIN_NUM_LCD_DC, HIGH);
LCDspi.transferBytes(SetData, ReadData, Size);
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, HIGH);
LCDspi.endTransaction();
}
void LCD_Reset(void)
{
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, LOW);
delay(50);
digitalWrite(EXAMPLE_PIN_NUM_LCD_RST, LOW);
delay(50);
digitalWrite(EXAMPLE_PIN_NUM_LCD_RST, HIGH);
delay(50);
}
void LCD_Init(void)
{
pinMode(EXAMPLE_PIN_NUM_LCD_CS, OUTPUT);
pinMode(EXAMPLE_PIN_NUM_LCD_DC, OUTPUT);
pinMode(EXAMPLE_PIN_NUM_LCD_RST, OUTPUT);
Backlight_Init();
SPI_Init();
LCD_Reset();
//************* Start Initial Sequence **********//
LCD_WriteCommand(0x11);
delay(120);
LCD_WriteCommand(0x36);
if (HORIZONTAL)
LCD_WriteData(0x00);
else
LCD_WriteData(0x70);
LCD_WriteCommand(0x3A);
LCD_WriteData(0x05);
LCD_WriteCommand(0xB0);
LCD_WriteData(0x00);
LCD_WriteData(0xE8);
LCD_WriteCommand(0xB2);
LCD_WriteData(0x0C);
LCD_WriteData(0x0C);
LCD_WriteData(0x00);
LCD_WriteData(0x33);
LCD_WriteData(0x33);
LCD_WriteCommand(0xB7);
LCD_WriteData(0x35);
LCD_WriteCommand(0xBB);
LCD_WriteData(0x35);
LCD_WriteCommand(0xC0);
LCD_WriteData(0x2C);
LCD_WriteCommand(0xC2);
LCD_WriteData(0x01);
LCD_WriteCommand(0xC3);
LCD_WriteData(0x13);
LCD_WriteCommand(0xC4);
LCD_WriteData(0x20);
LCD_WriteCommand(0xC6);
LCD_WriteData(0x0F);
LCD_WriteCommand(0xD0);
LCD_WriteData(0xA4);
LCD_WriteData(0xA1);
LCD_WriteCommand(0xD6);
LCD_WriteData(0xA1);
LCD_WriteCommand(0xE0);
LCD_WriteData(0xF0);
LCD_WriteData(0x00);
LCD_WriteData(0x04);
LCD_WriteData(0x04);
LCD_WriteData(0x04);
LCD_WriteData(0x05);
LCD_WriteData(0x29);
LCD_WriteData(0x33);
LCD_WriteData(0x3E);
LCD_WriteData(0x38);
LCD_WriteData(0x12);
LCD_WriteData(0x12);
LCD_WriteData(0x28);
LCD_WriteData(0x30);
LCD_WriteCommand(0xE1);
LCD_WriteData(0xF0);
LCD_WriteData(0x07);
LCD_WriteData(0x0A);
LCD_WriteData(0x0D);
LCD_WriteData(0x0B);
LCD_WriteData(0x07);
LCD_WriteData(0x28);
LCD_WriteData(0x33);
LCD_WriteData(0x3E);
LCD_WriteData(0x36);
LCD_WriteData(0x14);
LCD_WriteData(0x14);
LCD_WriteData(0x29);
LCD_WriteData(0x32);
LCD_WriteCommand(0x21);
LCD_WriteCommand(0x11);
delay(120);
LCD_WriteCommand(0x29);
}
/******************************************************************************
function: Set the cursor position
parameter :
Xstart: Start uint16_t x coordinate
Ystart: Start uint16_t y coordinate
Xend : End uint16_t coordinates
Yend : End uint16_t coordinatesen
******************************************************************************/
void LCD_SetCursor(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend)
{
if (HORIZONTAL) {
// set the X coordinates
LCD_WriteCommand(0x2A);
LCD_WriteData(Xstart >> 8);
LCD_WriteData(Xstart + Offset_X);
LCD_WriteData(Xend >> 8);
LCD_WriteData(Xend + Offset_X);
// set the Y coordinates
LCD_WriteCommand(0x2B);
LCD_WriteData(Ystart >> 8);
LCD_WriteData(Ystart + Offset_Y);
LCD_WriteData(Yend >> 8);
LCD_WriteData(Yend + Offset_Y);
}
else {
// set the X coordinates
LCD_WriteCommand(0x2A);
LCD_WriteData(Ystart >> 8);
LCD_WriteData(Ystart + Offset_Y);
LCD_WriteData(Yend >> 8);
LCD_WriteData(Yend + Offset_Y);
// set the Y coordinates
LCD_WriteCommand(0x2B);
LCD_WriteData(Xstart >> 8);
LCD_WriteData(Xstart + Offset_X);
LCD_WriteData(Xend >> 8);
LCD_WriteData(Xend + Offset_X);
}
LCD_WriteCommand(0x2C);
}
/******************************************************************************
function: Refresh the image in an area
parameter :
Xstart: Start uint16_t x coordinate
Ystart: Start uint16_t y coordinate
Xend : End uint16_t coordinates
Yend : End uint16_t coordinates
color : Set the color
******************************************************************************/
void LCD_addWindow(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend,uint16_t* color)
{
uint16_t Show_Width = Xend - Xstart + 1;
uint16_t Show_Height = Yend - Ystart + 1;
uint32_t numBytes = Show_Width * Show_Height * sizeof(uint16_t);
uint8_t Read_D[numBytes];
LCD_SetCursor(Xstart, Ystart, Xend, Yend);
LCD_WriteData_nbyte((uint8_t*)color, Read_D, numBytes);
}
// backlight
void Backlight_Init(void)
{
ledcAttach(EXAMPLE_PIN_NUM_BK_LIGHT, Frequency, Resolution);
ledcWrite(EXAMPLE_PIN_NUM_BK_LIGHT, 100);
}
void Set_Backlight(uint8_t Light) //
{
if(Light > 100 || Light < 0)
printf("Set Backlight parameters in the range of 0 to 100 \r\n");
else{
uint32_t Backlight = Light*10;
ledcWrite(EXAMPLE_PIN_NUM_BK_LIGHT, Backlight);
}
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include <Arduino.h>
#include <SPI.h>
#define LCD_WIDTH 172 //LCD width
#define LCD_HEIGHT 320 //LCD height
#define SPIFreq 80000000
#define EXAMPLE_PIN_NUM_MISO -1
#define EXAMPLE_PIN_NUM_MOSI 45
#define EXAMPLE_PIN_NUM_SCLK 40
#define EXAMPLE_PIN_NUM_LCD_CS 42
#define EXAMPLE_PIN_NUM_LCD_DC 41
#define EXAMPLE_PIN_NUM_LCD_RST 39
#define EXAMPLE_PIN_NUM_BK_LIGHT 48
#define Frequency 1000 // PWM frequencyconst
#define Resolution 10
#define VERTICAL 0
#define HORIZONTAL 1
#define Offset_X 34
#define Offset_Y 0
void LCD_SetCursor(uint16_t x1, uint16_t y1, uint16_t x2,uint16_t y2);
void LCD_Init(void);
void LCD_addWindow(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend,uint16_t* color);
void Backlight_Init(void);
void Set_Backlight(uint8_t Light);

View File

@@ -0,0 +1,119 @@
#include "SD_Card.h"
bool SDCard_Flag;
bool SDCard_Finish;
uint16_t SDCard_Size;
uint16_t Flash_Size;
void SD_Init() {
if(!SD_MMC.setPins(SD_CLK_PIN, SD_CMD_PIN, SD_D0_PIN, SD_D1_PIN, SD_D2_PIN, SD_D3_PIN)){
printf("SD MMC: Pin change failed!\r\n");
return;
}
if (SD_MMC.begin("/sdcard", true, true)) {
printf("SD card initialization successful!\r\n");
} else {
printf("SD card initialization failed!\r\n");
}
uint8_t cardType = SD_MMC.cardType();
if(cardType == CARD_NONE){
printf("No SD card attached\r\n");
return;
}
else{
printf("SD Card Type: ");
if(cardType == CARD_MMC){
printf("MMC\r\n");
} else if(cardType == CARD_SD){
printf("SDSC\r\n");
} else if(cardType == CARD_SDHC){
printf("SDHC\r\n");
} else {
printf("UNKNOWN\r\n");
}
uint64_t totalBytes = SD_MMC.totalBytes();
uint64_t usedBytes = SD_MMC.usedBytes();
SDCard_Size = totalBytes/(1024*1024);
printf("Total space: %llu\n", totalBytes);
printf("Used space: %llu\n", usedBytes);
printf("Free space: %llu\n", totalBytes - usedBytes);
}
}
bool File_Search(const char* directory, const char* fileName)
{
File Path = SD_MMC.open(directory);
if (!Path) {
printf("Path: <%s> does not exist\r\n",directory);
return false;
}
File file = Path.openNextFile();
while (file) {
if (strcmp(file.name(), fileName) == 0) {
if (strcmp(directory, "/") == 0)
printf("File '%s%s' found in root directory.\r\n",directory,fileName);
else
printf("File '%s/%s' found in root directory.\r\n",directory,fileName);
Path.close();
return true;
}
file = Path.openNextFile();
}
if (strcmp(directory, "/") == 0)
printf("File '%s%s' not found in root directory.\r\n",directory,fileName);
else
printf("File '%s/%s' not found in root directory.\r\n",directory,fileName);
Path.close();
return false;
}
uint16_t Folder_retrieval(const char* directory, const char* fileExtension, char File_Name[][100],uint16_t maxFiles)
{
File Path = SD_MMC.open(directory);
if (!Path) {
printf("Path: <%s> does not exist\r\n",directory);
return 0;
}
uint16_t fileCount = 0;
char filePath[100];
File file = Path.openNextFile();
while (file && fileCount < maxFiles) {
if (!file.isDirectory() && strstr(file.name(), fileExtension)) {
strncpy(File_Name[fileCount], file.name(), sizeof(File_Name[fileCount]));
if (strcmp(directory, "/") == 0) {
snprintf(filePath, 100, "%s%s", directory, file.name());
} else {
snprintf(filePath, 100, "%s/%s", directory, file.name());
}
printf("File found: %s\r\n", filePath);
fileCount++;
}
file = Path.openNextFile();
}
Path.close();
if (fileCount > 0) {
printf("%d <%s> files were retrieved\r\n",fileCount,fileExtension);
return fileCount;
} else {
printf("No files with extension '%s' found in directory: %s\r\n", fileExtension, directory);
return 0;
}
}
void remove_file_extension(char *file_name) {
char *last_dot = strrchr(file_name, '.');
if (last_dot != NULL) {
*last_dot = '\0';
}
}
void Flash_test()
{
printf("/********** RAM Test**********/\r\n");
uint32_t flashSize = ESP.getFlashChipSize();
Flash_Size = flashSize/1024/1024;
printf("Flash size: %d MB \r\n", flashSize/1024/1024);
printf("/******* RAM Test Over********/\r\n\r\n");
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include "Arduino.h"
#include <cstring>
#include "FS.h"
#include "SD_MMC.h"
#define SD_CLK_PIN 14
#define SD_CMD_PIN 15
#define SD_D0_PIN 16
#define SD_D1_PIN 18
#define SD_D2_PIN 17
#define SD_D3_PIN 21
extern uint16_t SDCard_Size;
extern uint16_t Flash_Size;
void SD_Init();
void Flash_test();
bool File_Search(const char* directory, const char* fileName);
uint16_t Folder_retrieval(const char* directory, const char* fileExtension, char File_Name[][100],uint16_t maxFiles);
void remove_file_extension(char *file_name);

View File

@@ -0,0 +1,100 @@
#include "Display_ST7789.h"
#include "SD_Card.h"
#include "Display_PNG.h"
#include "Audio_Player.h"
#include <algorithm>
#define BOOT_KEY_PIN 9
uint16_t currentIndex = 0;
bool lastButtonState = HIGH;
uint32_t lastDebounceTime = 0;
const uint32_t debounceDelay = 50;
bool buttonWasPressed = false;
void setup() {
Serial.begin(115200);
delay(1000);
printf("\r\n==========================================\r\n");
printf("Starting Sound and Picture Player...\r\n");
printf("==========================================\r\n");
// Initialize peripherals
Flash_test();
SD_Init();
LCD_Init();
Set_Backlight(90);
// Setup button
pinMode(BOOT_KEY_PIN, INPUT_PULLUP);
// Retrieve media files from SD Card
Search_Images("/");
Search_Wavs("/");
printf("Initialization complete!\r\n");
printf("Found %d PNG images and %d WAV soundbites.\r\n", Image_Count, Wav_Count);
if (Image_Count > 0) {
printf("Displaying initial image (index 0): %s\r\n", Image_Names[0]);
Display_Image_By_Index("/", 0);
} else {
printf("WARNING: No PNG images found in SD Card root directory!\r\n");
}
}
void playCurrentPair() {
if (Image_Count == 0 && Wav_Count == 0) {
printf("Cannot play: missing PNG images and WAV soundbites!\r\n");
return;
}
// Stop current playing audio if any
stopWav();
printf("Playing item index %d:\r\n", currentIndex);
// Display the image
if (Image_Count > 0) {
uint16_t pngIndex = currentIndex % Image_Count;
Display_Image_By_Index("/", pngIndex);
}
// Play the soundbite
if (Wav_Count > 0) {
uint16_t wavIndex = currentIndex % Wav_Count;
String soundPath = String("/") + Wav_Names[wavIndex];
printf("Playing sound: %s\r\n", soundPath.c_str());
playWav(soundPath.c_str());
}
}
void loop() {
bool currentButtonReading = digitalRead(BOOT_KEY_PIN);
if (currentButtonReading != lastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (currentButtonReading == LOW) {
if (!buttonWasPressed) {
buttonWasPressed = true;
printf("Button pressed! Cycling to next picture and playing sound...\r\n");
if (Image_Count > 0 || Wav_Count > 0) {
playCurrentPair();
currentIndex = (currentIndex + 1) % std::max(Image_Count, Wav_Count);
} else {
printf("No media found on SD card to cycle!\r\n");
}
}
} else {
buttonWasPressed = false;
}
}
lastButtonState = currentButtonReading;
delay(10); // Yield to other RTOS tasks
}