feat: Implement initial ESP32 school bell system with web interface, scheduling, and DFPlayer control.

This commit is contained in:
2025-12-16 08:37:36 +08:00
parent f9dc8b0d57
commit 49fabd577c

View File

@@ -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 <Arduino.h>
#include <WiFi.h>
@@ -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
}