From 49fabd577c6ee7ec9e779d91052df195de431cf2 Mon Sep 17 00:00:00 2001 From: wwartana Date: Tue, 16 Dec 2025 08:37:36 +0800 Subject: [PATCH] feat: Implement initial ESP32 school bell system with web interface, scheduling, and DFPlayer control. --- src/main.cpp | 69 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 05946bf..530cd03 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,8 +7,9 @@ // DFPLAYER_RX_PIN = 15 // DFPLAYER_TX_PIN = 16 // BUSY_PIN = 17 (LOW = busy) -//esp32 sda -//esp32 scl +//esp32 sda 21 +//esp32 scl 22 +//MCU adalah Wemos ESP32 mini #include #include @@ -33,9 +34,9 @@ #define DFPLAYER_RX_PIN 16 //3 df #define DFPLAYER_TX_PIN 17 //2 df #define RELAY_PIN 19 -#define BUSY_PIN 23 +#define BUSY_PIN 5 //must high at boot (strapping pin) #define RESET_PIN 18 -#define BUZZER_PIN 5 +#define BUZZER_PIN 23 #define RESET_HOLD_MS 15000UL #define MAX_SCHEDULES 20 @@ -73,6 +74,10 @@ bool buzzerState = false; bool adminResetBlinking = false; unsigned long adminResetEndTime = 0; +// Button flags +volatile bool btnClickPending = false; +volatile bool btnLongPressPending = false; + // OneButton instance for RESET_PIN OneButton button(RESET_PIN, true); @@ -82,7 +87,7 @@ OneButton button(RESET_PIN, true); int selectBestChannel() { WiFi.mode(WIFI_STA); WiFi.disconnect(); - delay(100); + vTaskDelay(pdMS_TO_TICKS(100)); int n = WiFi.scanNetworks(); int channelCount[14] = {0}; // channels 1-13 @@ -206,14 +211,24 @@ void saveSchedules() { ////////////////////////////////////////////////////////////////////////// void initDFPlayer() { dfSerial.begin(9600, SERIAL_8N1, DFPLAYER_RX_PIN, DFPLAYER_TX_PIN); - delay(200); + vTaskDelay(pdMS_TO_TICKS(200)); + myDFPlayer.setTimeOut(600); // Fix for clone MP3 players if (myDFPlayer.begin(dfSerial)) { - myDFPlayer.volume(25); // 0..30 + myDFPlayer.volume(30); // 0..30 (Max volume) } } void playTrack(uint16_t track, const char* desc) { lastPlayedTrack = track; + + // Force relay ON immediately to protect against BUSY pin latency + digitalWrite(RELAY_PIN, LOW); + relayOn = true; + // Set minimum hold time (5s) to ensure start of playback is heard + if (relayHoldUntil < millis() + 5000UL) { + relayHoldUntil = millis() + 5000UL; + } + myDFPlayer.playMp3Folder(track); // as requested isPlaying = true; // notify @@ -502,20 +517,21 @@ void resetAdminToDefault() { ////////////////////////////////////////////////////////////////////////// void setup(){ // Initialize Task Watchdog Timer (TWDT) to prevent watchdog resets - esp_task_wdt_init(30, true); // 30 seconds timeout, panic on timeout + // Disable panic mode and set longer timeout to prevent resets + esp_task_wdt_init(120, false); // 120 seconds timeout, don't panic on timeout esp_task_wdt_add(NULL); // Add current task to watchdog pinMode(STATUS_LED_PIN, OUTPUT); digitalWrite(STATUS_LED_PIN, LOW); pinMode(RELAY_PIN, OUTPUT); digitalWrite(RELAY_PIN, HIGH); // relay active LOW, so HIGH = off - pinMode(BUSY_PIN, INPUT); // busy pin input (LOW when busy) + pinMode(BUSY_PIN, INPUT_PULLUP); // busy pin input (LOW when busy) pinMode(RESET_PIN, INPUT_PULLUP); pinMode(BUZZER_PIN, OUTPUT); digitalWrite(BUZZER_PIN, LOW); Serial.begin(115200); - delay(200); + vTaskDelay(pdMS_TO_TICKS(200)); if (!LittleFS.begin(true)) { Serial.println("[ERR] LittleFS mount failed"); @@ -582,18 +598,17 @@ void setup(){ }); ArduinoOTA.begin(); + // Setup OneButton callbacks button.setPressMs(15000); // Set long press to 15 seconds button.attachClick([]() { - // Click: play track 1000 - Serial.println("[BUTTON] Click detected: playing track 1000"); - playTrack(1000, "Manual Click"); + // Just set flag, don't do heavy work here + btnClickPending = true; }); button.attachLongPressStart([]() { - // Long press: reset admin credentials - Serial.println("[BUTTON] Long press detected: resetting admin credentials"); - resetAdminToDefault(); + // Just set flag + btnLongPressPending = true; }); @@ -611,6 +626,22 @@ void loop(){ // Handle OneButton button.tick(); + // Process button events outside of callback + if (btnClickPending) { + btnClickPending = false; + Serial.println("[BUTTON] Click detected: playing track 1000"); + playTrack(1000, "Manual Click"); + } + + if (btnLongPressPending) { + btnLongPressPending = false; + Serial.println("[BUTTON] Long press detected: resetting admin credentials"); + resetAdminToDefault(); + } + + // Reset watchdog early in the loop + esp_task_wdt_reset(); + // every second: send time & status if (millis() - lastTimeWS >= 1000) { lastTimeWS = millis(); @@ -674,6 +705,9 @@ void loop(){ relayOn = true; if (relayHoldUntil < millis() + 15000UL) relayHoldUntil = millis() + 15000UL; } + + // Reset watchdog during schedule processing + esp_task_wdt_reset(); } } @@ -702,5 +736,6 @@ void loop(){ // Reset Task Watchdog Timer to prevent timeout esp_task_wdt_reset(); - delay(10); // tiny yield + // Use vTaskDelay instead of delay() to allow other tasks to run + vTaskDelay(pdMS_TO_TICKS(10)); // tiny yield }