Implement UX improvements: status LED, long press slideshow toggle
This commit is contained in:
@@ -5,12 +5,32 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#define BOOT_KEY_PIN 9
|
#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;
|
uint16_t currentIndex = 0;
|
||||||
bool lastButtonState = HIGH;
|
bool lastButtonState = HIGH;
|
||||||
uint32_t lastDebounceTime = 0;
|
uint32_t lastDebounceTime = 0;
|
||||||
const uint32_t debounceDelay = 50;
|
const uint32_t debounceDelay = 50;
|
||||||
bool buttonWasPressed = false;
|
|
||||||
|
// 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() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
@@ -20,6 +40,10 @@ void setup() {
|
|||||||
printf("Starting Sound and Picture Player...\r\n");
|
printf("Starting Sound and Picture Player...\r\n");
|
||||||
printf("==========================================\r\n");
|
printf("==========================================\r\n");
|
||||||
|
|
||||||
|
// Setup NeoPixel
|
||||||
|
pinMode(PIN_NEOPIXEL, OUTPUT);
|
||||||
|
neopixelWrite(PIN_NEOPIXEL, 0, 0, 0); // turn off initially
|
||||||
|
|
||||||
// Initialize peripherals
|
// Initialize peripherals
|
||||||
Flash_test();
|
Flash_test();
|
||||||
SD_Init();
|
SD_Init();
|
||||||
@@ -36,11 +60,14 @@ void setup() {
|
|||||||
printf("Initialization complete!\r\n");
|
printf("Initialization complete!\r\n");
|
||||||
printf("Found %d PNG images and %d WAV soundbites.\r\n", Image_Count, Wav_Count);
|
printf("Found %d PNG images and %d WAV soundbites.\r\n", Image_Count, Wav_Count);
|
||||||
|
|
||||||
if (Image_Count > 0) {
|
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]);
|
printf("Displaying initial image (index 0): %s\r\n", Image_Names[0]);
|
||||||
Display_Image_By_Index("/", 0);
|
Display_Image_By_Index("/", 0);
|
||||||
} else {
|
currentState = STATE_IDLE;
|
||||||
printf("WARNING: No PNG images found in SD Card root directory!\r\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,31 +97,110 @@ void playCurrentPair() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() {
|
void loop() {
|
||||||
bool currentButtonReading = digitalRead(BOOT_KEY_PIN);
|
bool currentButtonReading = digitalRead(BOOT_KEY_PIN);
|
||||||
|
static uint32_t buttonPressStartTime = 0;
|
||||||
|
static bool buttonWasHeld = false;
|
||||||
|
|
||||||
|
// Detect changes in button state
|
||||||
if (currentButtonReading != lastButtonState) {
|
if (currentButtonReading != lastButtonState) {
|
||||||
lastDebounceTime = millis();
|
lastDebounceTime = millis();
|
||||||
|
if (currentButtonReading == LOW) {
|
||||||
|
buttonPressStartTime = millis();
|
||||||
|
buttonWasHeld = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((millis() - lastDebounceTime) > debounceDelay) {
|
if ((millis() - lastDebounceTime) > debounceDelay) {
|
||||||
if (currentButtonReading == LOW) {
|
if (currentButtonReading == LOW) {
|
||||||
if (!buttonWasPressed) {
|
// Button is being held down. Check if it exceeds long-press threshold (1.5 seconds)
|
||||||
buttonWasPressed = true;
|
if (!buttonWasHeld && (millis() - buttonPressStartTime > 1500)) {
|
||||||
printf("Button pressed! Cycling to next picture and playing sound...\r\n");
|
buttonWasHeld = true; // Mark as held to prevent repeated triggers
|
||||||
|
if (!hasError) {
|
||||||
if (Image_Count > 0 || Wav_Count > 0) {
|
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();
|
playCurrentPair();
|
||||||
currentIndex = (currentIndex + 1) % std::max(Image_Count, Wav_Count);
|
currentIndex = (currentIndex + 1) % std::max(Image_Count, Wav_Count);
|
||||||
} else {
|
} else {
|
||||||
printf("No media found on SD card to cycle!\r\n");
|
printf("System in error state (missing files). Can't cycle.\r\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
buttonWasHeld = false;
|
||||||
buttonWasPressed = 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;
|
lastButtonState = currentButtonReading;
|
||||||
|
|
||||||
|
// Update status RGB LED
|
||||||
|
updateLEDStatus();
|
||||||
|
|
||||||
delay(10); // Yield to other RTOS tasks
|
delay(10); // Yield to other RTOS tasks
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user