Implement advanced gestures (1=next, 2=pause, 3=prev, 4=vol) and ESP32-audioI2S MP3/WAV playback
This commit is contained in:
@@ -32,6 +32,13 @@ uint32_t lastLedPulseTime = 0;
|
||||
uint8_t pulseBrightness = 0;
|
||||
bool pulseDirection = true;
|
||||
|
||||
// Click gesture timers
|
||||
uint32_t lastButtonReleaseTime = 0;
|
||||
uint32_t buttonPressStartTime = 0;
|
||||
uint8_t clickCount = 0;
|
||||
bool isButtonPressed = false;
|
||||
bool longPressTriggered = false;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
@@ -55,12 +62,15 @@ void setup() {
|
||||
|
||||
// Retrieve media files from SD Card
|
||||
Search_Images("/");
|
||||
Search_Wavs("/");
|
||||
Search_Audio("/");
|
||||
|
||||
// Initialize ESP32-audioI2S player
|
||||
initAudioPlayer();
|
||||
|
||||
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 Audio files (.mp3/.wav).\r\n", Image_Count, Audio_Count);
|
||||
|
||||
if (Image_Count == 0 || Wav_Count == 0) {
|
||||
if (Image_Count == 0 || Audio_Count == 0) {
|
||||
hasError = true;
|
||||
currentState = STATE_ERROR;
|
||||
printf("ERROR: No media found on microSD card!\r\n");
|
||||
@@ -72,13 +82,13 @@ void setup() {
|
||||
}
|
||||
|
||||
void playCurrentPair() {
|
||||
if (Image_Count == 0 && Wav_Count == 0) {
|
||||
printf("Cannot play: missing PNG images and WAV soundbites!\r\n");
|
||||
if (Image_Count == 0 && Audio_Count == 0) {
|
||||
printf("Cannot play: missing PNG images and Audio files!\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop current playing audio if any
|
||||
stopWav();
|
||||
stopAudio();
|
||||
|
||||
printf("Playing item index %d:\r\n", currentIndex);
|
||||
|
||||
@@ -88,12 +98,76 @@ void playCurrentPair() {
|
||||
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());
|
||||
// Play the audio bite
|
||||
if (Audio_Count > 0) {
|
||||
uint16_t audioIndex = currentIndex % Audio_Count;
|
||||
String soundPath = String("/") + Audio_Names[audioIndex];
|
||||
printf("Playing audio: %s\r\n", soundPath.c_str());
|
||||
playAudio(soundPath.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void handleSinglePress() {
|
||||
printf("Gesture: Single Click - Next Slide\r\n");
|
||||
if (!hasError) {
|
||||
if (currentState == STATE_AUTOPLAY) {
|
||||
currentState = STATE_IDLE;
|
||||
printf("Autoplay Disabled.\r\n");
|
||||
}
|
||||
currentIndex = (currentIndex + 1) % std::max(Image_Count, Audio_Count);
|
||||
playCurrentPair();
|
||||
}
|
||||
}
|
||||
|
||||
void handleDoublePress() {
|
||||
printf("Gesture: Double Click - Pause / Resume Audio\r\n");
|
||||
if (!hasError) {
|
||||
pauseResumeAudio();
|
||||
}
|
||||
}
|
||||
|
||||
void handleTriplePress() {
|
||||
printf("Gesture: Triple Click - Previous Slide\r\n");
|
||||
if (!hasError) {
|
||||
if (currentState == STATE_AUTOPLAY) {
|
||||
currentState = STATE_IDLE;
|
||||
printf("Autoplay Disabled.\r\n");
|
||||
}
|
||||
uint16_t totalMedia = std::max(Image_Count, Audio_Count);
|
||||
currentIndex = (currentIndex + totalMedia - 1) % totalMedia;
|
||||
playCurrentPair();
|
||||
}
|
||||
}
|
||||
|
||||
void handleQuadruplePress() {
|
||||
if (!hasError) {
|
||||
uint8_t nextVol = getPlayerVolume() + 4;
|
||||
if (nextVol > 20) nextVol = 4; // Loop back
|
||||
setPlayerVolume(nextVol);
|
||||
|
||||
// Quick flash NeoPixel white to indicate volume change
|
||||
for(int i = 0; i < 3; i++) {
|
||||
neopixelWrite(PIN_NEOPIXEL, 64, 64, 64);
|
||||
delay(50);
|
||||
neopixelWrite(PIN_NEOPIXEL, 0, 0, 0);
|
||||
delay(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleLongPress() {
|
||||
printf("Gesture: Long Press - Toggle Autoplay Slideshow\r\n");
|
||||
if (!hasError) {
|
||||
if (currentState == STATE_AUTOPLAY) {
|
||||
currentState = STATE_IDLE;
|
||||
printf("Autoplay Disabled. Manual Mode active.\r\n");
|
||||
stopAudio();
|
||||
} else {
|
||||
currentState = STATE_AUTOPLAY;
|
||||
printf("Autoplay Enabled! Cycling slides every %d ms.\r\n", slideShowInterval);
|
||||
lastSlideTime = millis();
|
||||
playCurrentPair();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +195,7 @@ void updateLEDStatus() {
|
||||
neopixelWrite(PIN_NEOPIXEL, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
else if (isWavPlaying()) {
|
||||
else if (isAudioPlaying()) {
|
||||
// Pulsing Green
|
||||
neopixelWrite(PIN_NEOPIXEL, 0, pulseBrightness, 0);
|
||||
}
|
||||
@@ -137,62 +211,54 @@ void updateLEDStatus() {
|
||||
|
||||
void loop() {
|
||||
bool currentButtonReading = digitalRead(BOOT_KEY_PIN);
|
||||
static uint32_t buttonPressStartTime = 0;
|
||||
static bool buttonWasHeld = false;
|
||||
uint32_t now = millis();
|
||||
|
||||
// Detect changes in button state
|
||||
if (currentButtonReading != lastButtonState) {
|
||||
lastDebounceTime = millis();
|
||||
if (currentButtonReading == LOW) {
|
||||
buttonPressStartTime = millis();
|
||||
buttonWasHeld = false;
|
||||
// Detect state transitions for debouncing and gesture capturing
|
||||
if (currentButtonReading == LOW && lastButtonState == HIGH) {
|
||||
// Button Pressed down
|
||||
lastDebounceTime = now;
|
||||
buttonPressStartTime = now;
|
||||
isButtonPressed = true;
|
||||
longPressTriggered = false;
|
||||
}
|
||||
else if (currentButtonReading == LOW && lastButtonState == LOW && isButtonPressed) {
|
||||
// Button is held down
|
||||
if (!longPressTriggered && (now - buttonPressStartTime > 1500)) {
|
||||
longPressTriggered = true;
|
||||
handleLongPress();
|
||||
}
|
||||
}
|
||||
else if (currentButtonReading == HIGH && lastButtonState == LOW) {
|
||||
// Button Released
|
||||
isButtonPressed = false;
|
||||
uint32_t pressDuration = now - buttonPressStartTime;
|
||||
|
||||
if (!longPressTriggered && pressDuration > 50) { // Debounced release
|
||||
clickCount++;
|
||||
lastButtonReleaseTime = now;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
// Process click gestures after 300ms timeout has elapsed since last release
|
||||
if (clickCount > 0 && !isButtonPressed && (now - lastButtonReleaseTime > 300)) {
|
||||
if (clickCount == 1) {
|
||||
handleSinglePress();
|
||||
} else if (clickCount == 2) {
|
||||
handleDoublePress();
|
||||
} else if (clickCount == 3) {
|
||||
handleTriplePress();
|
||||
} else if (clickCount >= 4) {
|
||||
handleQuadruplePress();
|
||||
}
|
||||
clickCount = 0; // Reset counter
|
||||
}
|
||||
|
||||
// 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);
|
||||
// Only advance when the audio stops playing and slideShowInterval (5s) has passed
|
||||
if (!isAudioPlaying() && (now - lastSlideTime > slideShowInterval)) {
|
||||
lastSlideTime = now;
|
||||
currentIndex = (currentIndex + 1) % std::max(Image_Count, Audio_Count);
|
||||
playCurrentPair();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user