Files
sunflower-girl/sound_picture_app/sound_picture_app.ino

207 lines
5.5 KiB
C++

#include "Display_ST7789.h"
#include "SD_Card.h"
#include "Display_PNG.h"
#include "Audio_Player.h"
#include <algorithm>
#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
}