feat: Implement initial ESP32 school bell system with web interface, scheduling, and DFPlayer control.
This commit is contained in:
69
src/main.cpp
69
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 <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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user