Mode 7 Hari
This commit is contained in:
136
src/main.cpp
136
src/main.cpp
@@ -76,6 +76,15 @@ bool ledState = false;
|
|||||||
unsigned long lastBuzzerBeep = 0;
|
unsigned long lastBuzzerBeep = 0;
|
||||||
bool buzzerState = false;
|
bool buzzerState = false;
|
||||||
|
|
||||||
|
// MP3 playback error detection (via DFPlayer serial messages)
|
||||||
|
bool playbackError = false;
|
||||||
|
unsigned long playbackErrorEndTime = 0;
|
||||||
|
|
||||||
|
// DFPlayer auto-recovery
|
||||||
|
bool dfPlayerNeedsRecovery = false;
|
||||||
|
unsigned long lastRecoveryAttempt = 0;
|
||||||
|
const unsigned long RECOVERY_INTERVAL_MS = 5000; // Try recovery every 5 seconds
|
||||||
|
|
||||||
// Admin reset blinking effect
|
// Admin reset blinking effect
|
||||||
bool adminResetBlinking = false;
|
bool adminResetBlinking = false;
|
||||||
unsigned long adminResetEndTime = 0;
|
unsigned long adminResetEndTime = 0;
|
||||||
@@ -230,12 +239,117 @@ void saveSchedules() {
|
|||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// DFPlayer
|
// DFPlayer
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Parse DFPlayer messages and return error description
|
||||||
|
String getDFPlayerError(uint8_t type, int value) {
|
||||||
|
switch (type) {
|
||||||
|
case DFPlayerError:
|
||||||
|
switch (value) {
|
||||||
|
case Busy: return "Card not found";
|
||||||
|
case Sleeping: return "Sleeping";
|
||||||
|
case SerialWrongStack: return "Wrong Stack";
|
||||||
|
case CheckSumNotMatch: return "Checksum Error";
|
||||||
|
case FileIndexOut: return "File Index Out";
|
||||||
|
case FileMismatch: return "File Mismatch";
|
||||||
|
case Advertise: return "In Advertise";
|
||||||
|
default: return "Unknown Error";
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag to prevent CardInserted loop after recovery
|
||||||
|
bool recoveryJustDone = false;
|
||||||
|
|
||||||
|
// Check DFPlayer for messages and handle errors
|
||||||
|
void checkDFPlayerMessage() {
|
||||||
|
static bool sdCardPresent = true; // Assume present at start
|
||||||
|
|
||||||
|
if (myDFPlayer.available()) {
|
||||||
|
uint8_t type = myDFPlayer.readType();
|
||||||
|
int value = myDFPlayer.read();
|
||||||
|
|
||||||
|
if (type == DFPlayerError) {
|
||||||
|
String errorMsg = getDFPlayerError(type, value);
|
||||||
|
Serial.printf("[MP3] DFPlayer Error: %s (code: %d)\n", errorMsg.c_str(), value);
|
||||||
|
|
||||||
|
// Trigger error state for SD card related errors
|
||||||
|
if (value == Busy || value == FileIndexOut || value == FileMismatch) {
|
||||||
|
playbackError = true;
|
||||||
|
dfPlayerNeedsRecovery = true;
|
||||||
|
playbackErrorEndTime = millis() + 10000UL;
|
||||||
|
sdCardPresent = false;
|
||||||
|
|
||||||
|
// Notify via WebSocket
|
||||||
|
DynamicJsonDocument d(256);
|
||||||
|
d["type"] = "log";
|
||||||
|
d["msg"] = "⚠️ DFPlayer Error: " + errorMsg;
|
||||||
|
sendJsonWs(d);
|
||||||
|
}
|
||||||
|
} else if (type == DFPlayerCardInserted) {
|
||||||
|
Serial.println("[MP3] SD Card inserted");
|
||||||
|
// Skip if recovery was just done (to prevent loop from begin())
|
||||||
|
if (recoveryJustDone) {
|
||||||
|
recoveryJustDone = false;
|
||||||
|
Serial.println("[MP3] Ignoring CardInserted after recovery");
|
||||||
|
} else if (!sdCardPresent || playbackError) {
|
||||||
|
// Only trigger recovery if card was previously removed or there's an error
|
||||||
|
dfPlayerNeedsRecovery = true;
|
||||||
|
DynamicJsonDocument d(256);
|
||||||
|
d["type"] = "log";
|
||||||
|
d["msg"] = "💾 SD Card terdeteksi, mencoba recovery...";
|
||||||
|
sendJsonWs(d);
|
||||||
|
}
|
||||||
|
sdCardPresent = true;
|
||||||
|
} else if (type == DFPlayerCardRemoved) {
|
||||||
|
Serial.println("[MP3] SD Card removed");
|
||||||
|
sdCardPresent = false;
|
||||||
|
playbackError = true;
|
||||||
|
dfPlayerNeedsRecovery = true;
|
||||||
|
playbackErrorEndTime = millis() + 10000UL;
|
||||||
|
|
||||||
|
DynamicJsonDocument d(256);
|
||||||
|
d["type"] = "log";
|
||||||
|
d["msg"] = "⚠️ SD Card dilepas!";
|
||||||
|
sendJsonWs(d);
|
||||||
|
} else if (type == DFPlayerPlayFinished) {
|
||||||
|
Serial.printf("[MP3] Track %d finished\n", value);
|
||||||
|
isPlaying = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void initDFPlayer() {
|
void initDFPlayer() {
|
||||||
dfSerial.begin(9600, SERIAL_8N1, DFPLAYER_RX_PIN, DFPLAYER_TX_PIN);
|
dfSerial.begin(9600, SERIAL_8N1, DFPLAYER_RX_PIN, DFPLAYER_TX_PIN);
|
||||||
vTaskDelay(pdMS_TO_TICKS(200));
|
vTaskDelay(pdMS_TO_TICKS(200));
|
||||||
myDFPlayer.setTimeOut(600); // Fix for clone MP3 players
|
myDFPlayer.setTimeOut(600); // Fix for clone MP3 players
|
||||||
if (myDFPlayer.begin(dfSerial)) {
|
if (myDFPlayer.begin(dfSerial)) {
|
||||||
myDFPlayer.volume(30); // 0..30 (Max volume)
|
myDFPlayer.volume(30); // 0..30 (Max volume)
|
||||||
|
dfPlayerNeedsRecovery = false;
|
||||||
|
Serial.println("[MP3] DFPlayer initialized successfully");
|
||||||
|
} else {
|
||||||
|
dfPlayerNeedsRecovery = true;
|
||||||
|
Serial.println("[MP3] DFPlayer initialization failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void recoverDFPlayer() {
|
||||||
|
Serial.println("[MP3] Attempting DFPlayer recovery...");
|
||||||
|
myDFPlayer.setTimeOut(600);
|
||||||
|
if (myDFPlayer.begin(dfSerial)) {
|
||||||
|
myDFPlayer.volume(30);
|
||||||
|
dfPlayerNeedsRecovery = false;
|
||||||
|
playbackError = false;
|
||||||
|
recoveryJustDone = true; // Set flag to ignore next CardInserted event
|
||||||
|
Serial.println("[MP3] DFPlayer recovery successful!");
|
||||||
|
// Notify via WebSocket
|
||||||
|
DynamicJsonDocument d(256);
|
||||||
|
d["type"] = "log";
|
||||||
|
d["msg"] = "✅ DFPlayer pulih! Sistem siap kembali.";
|
||||||
|
sendJsonWs(d);
|
||||||
|
} else {
|
||||||
|
Serial.println("[MP3] DFPlayer recovery failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,8 +364,11 @@ void playTrack(uint16_t track, const char* desc) {
|
|||||||
relayHoldUntil = millis() + 5000UL;
|
relayHoldUntil = millis() + 5000UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
myDFPlayer.playMp3Folder(track); // as requested
|
myDFPlayer.playMp3Folder(track);
|
||||||
isPlaying = true;
|
isPlaying = true;
|
||||||
|
playbackError = false; // Reset error state
|
||||||
|
dfPlayerNeedsRecovery = false; // Reset recovery flag for new playback attempt
|
||||||
|
|
||||||
// notify
|
// notify
|
||||||
DynamicJsonDocument doc(256);
|
DynamicJsonDocument doc(256);
|
||||||
doc["type"] = "log";
|
doc["type"] = "log";
|
||||||
@@ -797,6 +914,9 @@ void loop(){
|
|||||||
testButton.tick();
|
testButton.tick();
|
||||||
resetButton.tick();
|
resetButton.tick();
|
||||||
|
|
||||||
|
// Check for DFPlayer messages (errors, card insert/remove, etc.)
|
||||||
|
checkDFPlayerMessage();
|
||||||
|
|
||||||
// Process TEST_BUTTON click: toggle relay manually
|
// Process TEST_BUTTON click: toggle relay manually
|
||||||
if (testBtnClickPending) {
|
if (testBtnClickPending) {
|
||||||
testBtnClickPending = false;
|
testBtnClickPending = false;
|
||||||
@@ -920,8 +1040,18 @@ void loop(){
|
|||||||
relayOn = false;
|
relayOn = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buzzer warning if date < 2025
|
// DFPlayer auto-recovery: reset and set volume after error
|
||||||
if (now.year() < 2025) {
|
if (dfPlayerNeedsRecovery && millis() - lastRecoveryAttempt >= RECOVERY_INTERVAL_MS) {
|
||||||
|
lastRecoveryAttempt = millis();
|
||||||
|
recoverDFPlayer();
|
||||||
|
// Recovery done - stop buzzer
|
||||||
|
playbackError = false;
|
||||||
|
playbackErrorEndTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buzzer warning if date < 2025 OR playback error
|
||||||
|
bool buzzerCondition = (now.year() < 2025) || (playbackError && millis() < playbackErrorEndTime);
|
||||||
|
if (buzzerCondition) {
|
||||||
if (millis() - lastBuzzerBeep >= 2000) {
|
if (millis() - lastBuzzerBeep >= 2000) {
|
||||||
lastBuzzerBeep = millis();
|
lastBuzzerBeep = millis();
|
||||||
buzzerState = true;
|
buzzerState = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user