#include "Display_ST7789.h" #include "SD_Card.h" #include "Display_PNG.h" #include "Audio_Player.h" #include "Web_Server.h" #define BOOT_KEY_PIN 0 // ── App state ───────────────────────────────────────────────────────────────── enum AppState { STATE_IDLE, STATE_AUTOPLAY, STATE_ERROR }; static AppState currentState = STATE_IDLE; static bool hasError = false; // ── Button gesture state ────────────────────────────────────────────────────── static bool lastButtonState = HIGH; static uint32_t buttonPressStartTime = 0; static uint32_t lastButtonRelease = 0; static uint8_t clickCount = 0; static bool isButtonPressed = false; static bool longPressTriggered = false; // Autoplay static uint32_t lastSlideTime = 0; static const uint32_t SLIDE_INTERVAL = 8000; // 8 s between slides in autoplay // ── Core function: show a memory ────────────────────────────────────────────── void showMemory(int index) { if (g_memoryCount == 0) return; if (index < 0 || index >= g_memoryCount) index = 0; g_currentIndex = index; printf("Showing memory %d: '%s'\r\n", index, g_memories[index].name); stopAudio(); Display_Memory_Photo(index); playMemoryAudio(index); Config_Save(); // persist currentIndex } // ── Button handlers ─────────────────────────────────────────────────────────── void handleSinglePress() { if (hasError || g_memoryCount == 0) return; if (currentState == STATE_AUTOPLAY) currentState = STATE_IDLE; showMemory((g_currentIndex + 1) % g_memoryCount); } void handleDoublePress() { if (hasError) return; pauseResumeAudio(); } void handleTriplePress() { if (hasError || g_memoryCount == 0) return; if (currentState == STATE_AUTOPLAY) currentState = STATE_IDLE; showMemory((g_currentIndex + g_memoryCount - 1) % g_memoryCount); } void handleQuadruplePress() { uint8_t v = getPlayerVolume() + 4; if (v > 21) v = 4; setPlayerVolume(v); printf("Volume: %d/21\r\n", v); } void handleLongPress() { if (hasError) return; if (currentState == STATE_AUTOPLAY) { currentState = STATE_IDLE; stopAudio(); printf("Autoplay OFF\r\n"); } else { currentState = STATE_AUTOPLAY; lastSlideTime = millis(); showMemory(g_currentIndex); printf("Autoplay ON\r\n"); } } // ── Setup ───────────────────────────────────────────────────────────────────── void setup() { Serial.begin(115200); delay(1000); printf("\r\n============================\r\n"); printf(" Memory Frame v2.0\r\n"); printf("============================\r\n"); // Display on first LCD_Init(); Set_Backlight(90); // SD card Flash_test(); SD_Init(); // Load memories from config Config_Load(); // Button pinMode(BOOT_KEY_PIN, INPUT_PULLUP); // Audio player initAudioPlayer(); setPlayerVolume(g_volume); // Show first memory (or error screen) if (g_memoryCount == 0) { printf("No memories found. Add some via the web app.\r\n"); // Screen stays dark — user will add via web } else { showMemory(g_currentIndex); } // Start WiFi + web server initWebServer(); printf("Ready! %d memor%s loaded.\r\n", g_memoryCount, g_memoryCount==1?"y":"ies"); } // ── Loop ────────────────────────────────────────────────────────────────────── void loop() { bool btn = digitalRead(BOOT_KEY_PIN); uint32_t now = millis(); // ── Web display request (triggered by phone) ────────────────────────────── if (g_webDisplayDirty) { g_webDisplayDirty = false; int req = g_webDisplayRequest; g_webDisplayRequest = -1; if (req >= 0 && req < g_memoryCount) { showMemory(req); } } // ── Button edge detection ───────────────────────────────────────────────── if (btn == LOW && lastButtonState == HIGH) { // Pressed down buttonPressStartTime = now; isButtonPressed = true; longPressTriggered = false; } else if (btn == LOW && lastButtonState == LOW && isButtonPressed) { // Held down if (!longPressTriggered && (now - buttonPressStartTime > 1500)) { longPressTriggered = true; handleLongPress(); } } else if (btn == HIGH && lastButtonState == LOW) { // Released isButtonPressed = false; if (!longPressTriggered && (now - buttonPressStartTime > 50)) { clickCount++; lastButtonRelease = now; } } // ── Multi-click gesture resolution (300ms window) ───────────────────────── if (clickCount > 0 && !isButtonPressed && (now - lastButtonRelease > 300)) { if (clickCount == 1) handleSinglePress(); else if (clickCount == 2) handleDoublePress(); else if (clickCount == 3) handleTriplePress(); else handleQuadruplePress(); clickCount = 0; } // ── Autoplay advance ────────────────────────────────────────────────────── if (currentState == STATE_AUTOPLAY && g_memoryCount > 0) { if (!isAudioPlaying() && (now - lastSlideTime > SLIDE_INTERVAL)) { lastSlideTime = now; showMemory((g_currentIndex + 1) % g_memoryCount); } } lastButtonState = btn; // ── Web server ──────────────────────────────────────────────────────────── handleWebServer(); delay(10); }