#include "Display_ST7789.h" #include "SD_Card.h" #include "Display_PNG.h" #include "Audio_Player.h" #include #define BOOT_KEY_PIN 9 #define PIN_NEOPIXEL 38 // Playback states enum AppState { STATE_IDLE, STATE_PLAYING, STATE_AUTOPLAY, STATE_ERROR }; AppState currentState = STATE_IDLE; bool hasError = false; uint16_t currentIndex = 0; bool lastButtonState = HIGH; uint32_t lastDebounceTime = 0; const uint32_t debounceDelay = 50; // Slide Show timing uint32_t lastSlideTime = 0; const uint32_t slideShowInterval = 5000; // 5 seconds // Pulse control for RGB LED uint32_t lastLedPulseTime = 0; uint8_t pulseBrightness = 0; bool pulseDirection = true; void setup() { Serial.begin(115200); delay(1000); printf("\r\n==========================================\r\n"); printf("Starting Sound and Picture Player...\r\n"); printf("==========================================\r\n"); // Setup NeoPixel pinMode(PIN_NEOPIXEL, OUTPUT); neopixelWrite(PIN_NEOPIXEL, 0, 0, 0); // turn off initially // 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 || Wav_Count == 0) { hasError = true; currentState = STATE_ERROR; printf("ERROR: No media found on microSD card!\r\n"); } else { printf("Displaying initial image (index 0): %s\r\n", Image_Names[0]); Display_Image_By_Index("/", 0); currentState = STATE_IDLE; } } 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 updateLEDStatus() { uint32_t now = millis(); // Pulse animation frequency if (now - lastLedPulseTime > 15) { lastLedPulseTime = now; if (pulseDirection) { pulseBrightness += 2; if (pulseBrightness >= 120) pulseDirection = false; } else { pulseBrightness -= 2; if (pulseBrightness <= 10) pulseDirection = true; } } // Update LED color based on state if (hasError) { // Flashing red if ((now / 250) % 2 == 0) { neopixelWrite(PIN_NEOPIXEL, 64, 0, 0); // Dim Red } else { neopixelWrite(PIN_NEOPIXEL, 0, 0, 0); } } else if (isWavPlaying()) { // Pulsing Green neopixelWrite(PIN_NEOPIXEL, 0, pulseBrightness, 0); } else if (currentState == STATE_AUTOPLAY) { // Pulsing Purple (Red + Blue) neopixelWrite(PIN_NEOPIXEL, pulseBrightness / 2, 0, pulseBrightness); } else { // Solid Blue neopixelWrite(PIN_NEOPIXEL, 0, 0, 32); } } void loop() { bool currentButtonReading = digitalRead(BOOT_KEY_PIN); static uint32_t buttonPressStartTime = 0; static bool buttonWasHeld = false; // Detect changes in button state if (currentButtonReading != lastButtonState) { lastDebounceTime = millis(); if (currentButtonReading == LOW) { buttonPressStartTime = millis(); buttonWasHeld = false; } } if ((millis() - lastDebounceTime) > debounceDelay) { if (currentButtonReading == LOW) { // Button is being held down. Check if it exceeds long-press threshold (1.5 seconds) if (!buttonWasHeld && (millis() - buttonPressStartTime > 1500)) { buttonWasHeld = true; // Mark as held to prevent repeated triggers if (!hasError) { if (currentState == STATE_AUTOPLAY) { currentState = STATE_IDLE; printf("Autoplay Disabled. Manual Mode active.\r\n"); stopWav(); } else { currentState = STATE_AUTOPLAY; printf("Autoplay Enabled! Cycling slides every %d ms.\r\n", slideShowInterval); lastSlideTime = millis(); playCurrentPair(); } } } } else { // Button was released if (lastButtonState == LOW && !buttonWasHeld) { // Single press trigger printf("Single press detected!\r\n"); if (!hasError) { if (currentState == STATE_AUTOPLAY) { currentState = STATE_IDLE; printf("Autoplay Disabled via manual navigation.\r\n"); } playCurrentPair(); currentIndex = (currentIndex + 1) % std::max(Image_Count, Wav_Count); } else { printf("System in error state (missing files). Can't cycle.\r\n"); } } buttonWasHeld = false; } } // Handle slideshow auto-advance in Autoplay mode if (currentState == STATE_AUTOPLAY && !hasError) { // Advance if the audio finished playing and 5 seconds has elapsed if (!isWavPlaying() && (millis() - lastSlideTime > slideShowInterval)) { lastSlideTime = millis(); currentIndex = (currentIndex + 1) % std::max(Image_Count, Wav_Count); playCurrentPair(); } } lastButtonState = currentButtonReading; // Update status RGB LED updateLEDStatus(); delay(10); // Yield to other RTOS tasks }