diff --git a/sound_picture_app/Audio_Player.cpp b/sound_picture_app/Audio_Player.cpp new file mode 100644 index 0000000..6637e0a --- /dev/null +++ b/sound_picture_app/Audio_Player.cpp @@ -0,0 +1,186 @@ +#include "Audio_Player.h" +#include +#include + +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; +} diff --git a/sound_picture_app/Audio_Player.h b/sound_picture_app/Audio_Player.h new file mode 100644 index 0000000..350a45d --- /dev/null +++ b/sound_picture_app/Audio_Player.h @@ -0,0 +1,18 @@ +#pragma once +#include +#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]; + diff --git a/sound_picture_app/Display_PNG.cpp b/sound_picture_app/Display_PNG.cpp new file mode 100644 index 0000000..80b7419 --- /dev/null +++ b/sound_picture_app/Display_PNG.cpp @@ -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); + } +} diff --git a/sound_picture_app/Display_PNG.h b/sound_picture_app/Display_PNG.h new file mode 100644 index 0000000..be9dd69 --- /dev/null +++ b/sound_picture_app/Display_PNG.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include +#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]; diff --git a/sound_picture_app/Display_ST7789.cpp b/sound_picture_app/Display_ST7789.cpp new file mode 100644 index 0000000..1e3f089 --- /dev/null +++ b/sound_picture_app/Display_ST7789.cpp @@ -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); + } +} diff --git a/sound_picture_app/Display_ST7789.h b/sound_picture_app/Display_ST7789.h new file mode 100644 index 0000000..2fde54f --- /dev/null +++ b/sound_picture_app/Display_ST7789.h @@ -0,0 +1,28 @@ +#pragma once +#include +#include +#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); diff --git a/sound_picture_app/SD_Card.cpp b/sound_picture_app/SD_Card.cpp new file mode 100644 index 0000000..4d4ac50 --- /dev/null +++ b/sound_picture_app/SD_Card.cpp @@ -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"); +} diff --git a/sound_picture_app/SD_Card.h b/sound_picture_app/SD_Card.h new file mode 100644 index 0000000..f4f22a9 --- /dev/null +++ b/sound_picture_app/SD_Card.h @@ -0,0 +1,22 @@ +#pragma once +#include "Arduino.h" +#include +#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); diff --git a/sound_picture_app/sound_picture_app.ino b/sound_picture_app/sound_picture_app.ino new file mode 100644 index 0000000..9ce46af --- /dev/null +++ b/sound_picture_app/sound_picture_app.ino @@ -0,0 +1,100 @@ +#include "Display_ST7789.h" +#include "SD_Card.h" +#include "Display_PNG.h" +#include "Audio_Player.h" +#include + +#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 +}