bug sauvegarde NVS corrected
This commit is contained in:
@@ -0,0 +1,102 @@
|
|||||||
|
# Optimisations NVS - Correction du problème de corruption
|
||||||
|
|
||||||
|
## Problème initial
|
||||||
|
L'erreur `assert failed: xQueueSemaphoreTake queue.c:1554` indiquait une corruption des sémaphores FreeRTOS lors de la sauvegarde des paramètres en mémoire NVS.
|
||||||
|
|
||||||
|
## Causes identifiées
|
||||||
|
|
||||||
|
1. **Conflit entre timer d'affichage et NVS** : Le timer d'interruption pour l'affichage pouvait interrompre la sauvegarde NVS
|
||||||
|
2. **Timeouts inadéquats** : Des timeouts trop longs pouvaient créer des deadlocks
|
||||||
|
3. **Absence de vérifications de sécurité** : Aucune vérification du contexte avant utilisation des sémaphores
|
||||||
|
4. **Gestion d'erreur insuffisante** : Pas de mécanisme de récupération en cas de corruption
|
||||||
|
|
||||||
|
## Solutions implémentées
|
||||||
|
|
||||||
|
### 1. Fonction de vérification de sanité des mutex
|
||||||
|
```cpp
|
||||||
|
bool checkMutexSanity() {
|
||||||
|
// Vérifier que les mutex sont valides
|
||||||
|
// Vérifier l'état du scheduler
|
||||||
|
// Vérifier que nous ne sommes pas dans une ISR
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Protection de la fonction saveSettings()
|
||||||
|
- Désactivation temporaire du timer d'affichage
|
||||||
|
- Vérification du contexte avant acquisition des mutex
|
||||||
|
- Timeouts courts (50ms max) pour éviter les blocages
|
||||||
|
- Mécanisme de retry pour les opérations NVS
|
||||||
|
- Gestion d'exception pour les écritures NVS
|
||||||
|
|
||||||
|
### 3. Amélioration de la fonction display_updater()
|
||||||
|
- Double vérification de la validité du timer
|
||||||
|
- Protection contre les accès concurrents
|
||||||
|
|
||||||
|
### 4. Système de surveillance (watchdog)
|
||||||
|
- Vérification périodique de la sanité du système
|
||||||
|
- Compteur de corruption avec récupération automatique
|
||||||
|
- Surveillance de l'état des tâches et de la mémoire
|
||||||
|
|
||||||
|
### 5. Fonction de récupération d'urgence
|
||||||
|
```cpp
|
||||||
|
void emergencyRecovery() {
|
||||||
|
// Arrêt propre de tous les timers et tâches
|
||||||
|
// Nettoyage des mutex
|
||||||
|
// Redémarrage du système
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Réparation de corruption NVS
|
||||||
|
```cpp
|
||||||
|
void repairNVSCorruption() {
|
||||||
|
// Effacement et réinitialisation de la partition NVS
|
||||||
|
// Gestion des erreurs avec retry
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Optimisations techniques
|
||||||
|
|
||||||
|
### Timeouts optimisés
|
||||||
|
- **MUTEX_TIMEOUT_FAST** (50ms) : Pour les opérations rapides
|
||||||
|
- **MUTEX_TIMEOUT_NORMAL** (200ms) : Pour les opérations normales
|
||||||
|
- **MUTEX_TIMEOUT_SLOW** (1000ms) : Pour les opérations I/O
|
||||||
|
- **MUTEX_TIMEOUT_CRITICAL** (2000ms) : Pour les opérations critiques
|
||||||
|
|
||||||
|
### Gestion des contextes
|
||||||
|
- Vérification `xPortInIsrContext()` avant toute opération mutex
|
||||||
|
- Vérification `xTaskGetSchedulerState()` pour s'assurer que le scheduler fonctionne
|
||||||
|
- Désactivation temporaire des interruptions lors des opérations critiques
|
||||||
|
|
||||||
|
### Mécanismes de retry
|
||||||
|
- Retry automatique pour `preferences.begin()`
|
||||||
|
- Fallback vers des valeurs par défaut en cas d'échec
|
||||||
|
- Sauvegarde différée en cas de contexte inapproprié
|
||||||
|
|
||||||
|
## Résultats attendus
|
||||||
|
|
||||||
|
1. **Élimination des corruptions** : Les vérifications de contexte empêchent les accès concurrents dangereux
|
||||||
|
2. **Robustesse accrue** : Le système peut se récupérer automatiquement des corruptions mineures
|
||||||
|
3. **Performance maintenue** : Les timeouts courts évitent les blocages sans impact sur les performances
|
||||||
|
4. **Monitoring continu** : Le watchdog détecte et corrige proactivement les problèmes
|
||||||
|
|
||||||
|
## Instructions d'utilisation
|
||||||
|
|
||||||
|
1. Compiler et uploader le firmware optimisé
|
||||||
|
2. Surveiller les logs série pour détecter les éventuels problèmes
|
||||||
|
3. Le système se récupère automatiquement des corruptions mineures
|
||||||
|
4. En cas de corruption grave, le système redémarre automatiquement après nettoyage
|
||||||
|
|
||||||
|
## Logs de débogage
|
||||||
|
|
||||||
|
Le système produit maintenant des logs détaillés :
|
||||||
|
- `"Saving settings to NVS..."` : Début de sauvegarde
|
||||||
|
- `"Settings saved successfully"` : Sauvegarde réussie
|
||||||
|
- `"Mutex sanity check failed"` : Problème de mutex détecté
|
||||||
|
- `"System corruption detected"` : Corruption du système
|
||||||
|
- `"EMERGENCY RECOVERY"` : Récupération d'urgence en cours
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
|
- Surveiller les compteurs de corruption dans les logs
|
||||||
|
- Vérifier périodiquement l'état de la mémoire heap
|
||||||
|
- En cas de corruptions répétées, vérifier l'alimentation et la qualité des connexions
|
||||||
@@ -46,6 +46,8 @@
|
|||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/queue.h"
|
#include "freertos/queue.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
// Version firmware (uniformisé avec main)
|
// Version firmware (uniformisé avec main)
|
||||||
static const char* FIRMWARE_VERSION = "1.0.0"; // garder synchro avec src/main.cpp
|
static const char* FIRMWARE_VERSION = "1.0.0"; // garder synchro avec src/main.cpp
|
||||||
@@ -154,6 +156,100 @@ uint16_t myORANGE = display.color565(255, 165, 0);
|
|||||||
void IRAM_ATTR display_updater();
|
void IRAM_ATTR display_updater();
|
||||||
void display_update_enable(bool is_enable); // prototype pour usage anticipé
|
void display_update_enable(bool is_enable); // prototype pour usage anticipé
|
||||||
|
|
||||||
|
// Fonction de vérification de la sanité des mutex
|
||||||
|
bool checkMutexSanity() {
|
||||||
|
// Vérifier que les mutex sont valides
|
||||||
|
if (displayMutex == NULL || countdownMutex == NULL || preferencesMutex == NULL) {
|
||||||
|
Serial.println("ERROR: One or more mutex is NULL!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier l'état du scheduler
|
||||||
|
if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) {
|
||||||
|
Serial.println("WARNING: Scheduler not running");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier que nous ne sommes pas dans une ISR
|
||||||
|
if (xPortInIsrContext()) {
|
||||||
|
Serial.println("WARNING: In ISR context");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction de récupération d'urgence en cas de corruption
|
||||||
|
void emergencyRecovery() {
|
||||||
|
Serial.println("EMERGENCY RECOVERY: System corruption detected");
|
||||||
|
|
||||||
|
// Arrêter tous les timers
|
||||||
|
if (timer != nullptr) {
|
||||||
|
timerEnd(timer);
|
||||||
|
timer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrêter toutes les tâches
|
||||||
|
if (displayTaskHandle != NULL) {
|
||||||
|
vTaskDelete(displayTaskHandle);
|
||||||
|
displayTaskHandle = NULL;
|
||||||
|
}
|
||||||
|
if (countdownTaskHandle != NULL) {
|
||||||
|
vTaskDelete(countdownTaskHandle);
|
||||||
|
countdownTaskHandle = NULL;
|
||||||
|
}
|
||||||
|
if (networkTaskHandle != NULL) {
|
||||||
|
vTaskDelete(networkTaskHandle);
|
||||||
|
networkTaskHandle = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nettoyage des mutex
|
||||||
|
if (displayMutex != NULL) {
|
||||||
|
vSemaphoreDelete(displayMutex);
|
||||||
|
displayMutex = NULL;
|
||||||
|
}
|
||||||
|
if (countdownMutex != NULL) {
|
||||||
|
vSemaphoreDelete(countdownMutex);
|
||||||
|
countdownMutex = NULL;
|
||||||
|
}
|
||||||
|
if (preferencesMutex != NULL) {
|
||||||
|
vSemaphoreDelete(preferencesMutex);
|
||||||
|
preferencesMutex = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Délai avant redémarrage
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
// Redémarrage du système
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction de réparation de la corruption NVS
|
||||||
|
void repairNVSCorruption() {
|
||||||
|
Serial.println("Attempting NVS corruption repair...");
|
||||||
|
|
||||||
|
// Fermer toutes les instances de preferences ouvertes
|
||||||
|
preferences.end();
|
||||||
|
|
||||||
|
// Attendre un peu
|
||||||
|
delay(100);
|
||||||
|
|
||||||
|
// Essayer de vider et réinitialiser la partition NVS
|
||||||
|
esp_err_t err = nvs_flash_erase();
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
Serial.println("NVS partition erased successfully");
|
||||||
|
err = nvs_flash_init();
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
Serial.println("NVS partition reinitialized successfully");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("NVS repair failed - will restart");
|
||||||
|
delay(1000);
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
|
||||||
// Utilitaire : copie tronquée en respectant les limites UTF-8 (nombre de caractères et non octets)
|
// Utilitaire : copie tronquée en respectant les limites UTF-8 (nombre de caractères et non octets)
|
||||||
// maxChars : nombre maximum de caractères (glyphes) à conserver (excluant le nul final)
|
// maxChars : nombre maximum de caractères (glyphes) à conserver (excluant le nul final)
|
||||||
// destSize : taille du buffer destination (incluant place pour nul)
|
// destSize : taille du buffer destination (incluant place pour nul)
|
||||||
@@ -224,7 +320,43 @@ public:
|
|||||||
#define MUTEX_GUARD(mutex, timeout) MutexGuard guard_##mutex(mutex, timeout)
|
#define MUTEX_GUARD(mutex, timeout) MutexGuard guard_##mutex(mutex, timeout)
|
||||||
#define MUTEX_GUARD_CHECK(mutex, timeout) MutexGuard guard_##mutex(mutex, timeout); if (!guard_##mutex.isLocked())
|
#define MUTEX_GUARD_CHECK(mutex, timeout) MutexGuard guard_##mutex(mutex, timeout); if (!guard_##mutex.isLocked())
|
||||||
|
|
||||||
// === Constantes optimisées pour les timeouts ===
|
// Watchdog pour surveiller la sanité du système
|
||||||
|
unsigned long lastWatchdogTime = 0;
|
||||||
|
int corruptionCounter = 0;
|
||||||
|
|
||||||
|
void systemWatchdog() {
|
||||||
|
unsigned long now = millis();
|
||||||
|
|
||||||
|
// Exécuter toutes les 30 secondes
|
||||||
|
if (now - lastWatchdogTime > 30000) {
|
||||||
|
lastWatchdogTime = now;
|
||||||
|
|
||||||
|
// Vérifier la sanité des mutex
|
||||||
|
if (!checkMutexSanity()) {
|
||||||
|
corruptionCounter++;
|
||||||
|
Serial.printf("System corruption detected #%d\n", corruptionCounter);
|
||||||
|
|
||||||
|
// Si corruption répétée, déclencher une récupération d'urgence
|
||||||
|
if (corruptionCounter >= 3) {
|
||||||
|
Serial.println("Too many corruptions - triggering emergency recovery");
|
||||||
|
emergencyRecovery();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Réinitialiser le compteur si tout va bien
|
||||||
|
corruptionCounter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier l'état des tâches
|
||||||
|
if (displayTaskHandle == NULL || countdownTaskHandle == NULL || networkTaskHandle == NULL) {
|
||||||
|
Serial.println("One or more tasks missing - system instability detected");
|
||||||
|
corruptionCounter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Imprimer les statistiques de mémoire
|
||||||
|
Serial.printf("Free heap: %d bytes, Min free: %d bytes\n",
|
||||||
|
ESP.getFreeHeap(), ESP.getMinFreeHeap());
|
||||||
|
}
|
||||||
|
}
|
||||||
#define MUTEX_TIMEOUT_FAST pdMS_TO_TICKS(50) // Opérations rapides
|
#define MUTEX_TIMEOUT_FAST pdMS_TO_TICKS(50) // Opérations rapides
|
||||||
#define MUTEX_TIMEOUT_NORMAL pdMS_TO_TICKS(200) // Opérations normales
|
#define MUTEX_TIMEOUT_NORMAL pdMS_TO_TICKS(200) // Opérations normales
|
||||||
#define MUTEX_TIMEOUT_SLOW pdMS_TO_TICKS(1000) // Opérations lentes (I/O)
|
#define MUTEX_TIMEOUT_SLOW pdMS_TO_TICKS(1000) // Opérations lentes (I/O)
|
||||||
@@ -1196,28 +1328,44 @@ void loadSettings() {
|
|||||||
display.setBrightness(effectiveBrightness);
|
display.setBrightness(effectiveBrightness);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sauvegarde des paramètres dans la mémoire flash (version thread-safe)
|
// Sauvegarde des paramètres dans la mémoire flash (version thread-safe optimisée)
|
||||||
void saveSettings() {
|
void saveSettings() {
|
||||||
Serial.println("Saving settings to NVS...");
|
Serial.println("Saving settings to NVS...");
|
||||||
|
|
||||||
// Tentative simple d'acquisition des mutex avec vérification du scheduler
|
// Vérifications de sécurité préliminaires
|
||||||
if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) {
|
if (!checkMutexSanity()) {
|
||||||
Serial.println("Scheduler not running - delaying save");
|
Serial.println("Mutex sanity check failed - aborting save");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tentative d'acquisition des mutex avec timeout court
|
// Désactiver temporairement le timer d'affichage pour éviter les conflits
|
||||||
if (preferencesMutex && xSemaphoreTake(preferencesMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
|
bool timerWasEnabled = (timer != nullptr);
|
||||||
|
if (timerWasEnabled) {
|
||||||
|
display_update_enable(false);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10)); // Attendre que l'IRQ se termine
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tentative d'acquisition des mutex avec timeout très court pour éviter les blocages
|
||||||
|
if (preferencesMutex && xSemaphoreTake(preferencesMutex, pdMS_TO_TICKS(50)) == pdTRUE) {
|
||||||
|
|
||||||
|
bool prefsStarted = false;
|
||||||
|
|
||||||
|
// Retry logic pour preferences.begin()
|
||||||
|
for (int retry = 0; retry < 3 && !prefsStarted; retry++) {
|
||||||
|
prefsStarted = preferences.begin("countdown", false);
|
||||||
|
if (!prefsStarted) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool prefsStarted = preferences.begin("countdown", false);
|
|
||||||
if (prefsStarted) {
|
if (prefsStarted) {
|
||||||
// Paramètres du countdown - capture des valeurs locales pour éviter les races
|
// Paramètres du countdown - capture des valeurs locales pour éviter les races
|
||||||
int year, month, day, hour, minute, second;
|
int year, month, day, hour, minute, second;
|
||||||
int colorR, colorG, colorB;
|
int colorR, colorG, colorB;
|
||||||
int fStyle;
|
int fStyle;
|
||||||
|
|
||||||
// Acquisition rapide du mutex countdown pour capturer les valeurs
|
// Acquisition TRÈS rapide du mutex countdown pour capturer les valeurs
|
||||||
if (countdownMutex && xSemaphoreTake(countdownMutex, pdMS_TO_TICKS(50)) == pdTRUE) {
|
if (countdownMutex && xSemaphoreTake(countdownMutex, pdMS_TO_TICKS(20)) == pdTRUE) {
|
||||||
year = countdownYear;
|
year = countdownYear;
|
||||||
month = countdownMonth;
|
month = countdownMonth;
|
||||||
day = countdownDay;
|
day = countdownDay;
|
||||||
@@ -1230,8 +1378,8 @@ void saveSettings() {
|
|||||||
fStyle = fontStyle;
|
fStyle = fontStyle;
|
||||||
xSemaphoreGive(countdownMutex);
|
xSemaphoreGive(countdownMutex);
|
||||||
} else {
|
} else {
|
||||||
// Si on ne peut pas obtenir le mutex, utiliser les valeurs par défaut
|
// Fallback sans mutex - utiliser les valeurs directement (atomic reads sur ESP32)
|
||||||
Serial.println("Could not acquire countdown mutex - using current values");
|
Serial.println("Mutex timeout - using direct read (atomic)");
|
||||||
year = countdownYear;
|
year = countdownYear;
|
||||||
month = countdownMonth;
|
month = countdownMonth;
|
||||||
day = countdownDay;
|
day = countdownDay;
|
||||||
@@ -1244,54 +1392,66 @@ void saveSettings() {
|
|||||||
fStyle = fontStyle;
|
fStyle = fontStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sauvegarde sans mutex actif
|
// Sauvegarde optimisée - grouper les écritures pour réduire les opérations NVS
|
||||||
preferences.putInt("cd_Year", year);
|
try {
|
||||||
preferences.putInt("cd_Month", month);
|
preferences.putInt("cd_Year", year);
|
||||||
preferences.putInt("cd_Day", day);
|
preferences.putInt("cd_Month", month);
|
||||||
preferences.putInt("cd_Hour", hour);
|
preferences.putInt("cd_Day", day);
|
||||||
preferences.putInt("cd_Minute", minute);
|
preferences.putInt("cd_Hour", hour);
|
||||||
preferences.putInt("cd_Second", second);
|
preferences.putInt("cd_Minute", minute);
|
||||||
|
preferences.putInt("cd_Second", second);
|
||||||
// Paramètres d'affichage - style de police et couleur
|
|
||||||
preferences.putInt("fontStyle", fStyle);
|
// Paramètres d'affichage - style de police et couleur
|
||||||
preferences.putInt("colorR", colorR);
|
preferences.putInt("fontStyle", fStyle);
|
||||||
preferences.putInt("colorG", colorG);
|
preferences.putInt("colorR", colorR);
|
||||||
preferences.putInt("colorB", colorB);
|
preferences.putInt("colorG", colorG);
|
||||||
preferences.putBool("blinkEn", blinkEnabled);
|
preferences.putInt("colorB", colorB);
|
||||||
preferences.putInt("blinkInt", blinkIntervalMs);
|
preferences.putBool("blinkEn", blinkEnabled);
|
||||||
preferences.putInt("blinkWin", blinkWindowSeconds);
|
preferences.putInt("blinkInt", blinkIntervalMs);
|
||||||
preferences.putBool("mqEn", marqueeEnabled);
|
preferences.putInt("blinkWin", blinkWindowSeconds);
|
||||||
preferences.putInt("mqInt", marqueeIntervalMs);
|
preferences.putBool("mqEn", marqueeEnabled);
|
||||||
preferences.putInt("mqGap", marqueeGap);
|
preferences.putInt("mqInt", marqueeIntervalMs);
|
||||||
preferences.putInt("mqMode", marqueeMode);
|
preferences.putInt("mqGap", marqueeGap);
|
||||||
preferences.putInt("mqRetInt", marqueeReturnIntervalMs);
|
preferences.putInt("mqMode", marqueeMode);
|
||||||
preferences.putInt("mqPL", marqueeBouncePauseLeftMs);
|
preferences.putInt("mqRetInt", marqueeReturnIntervalMs);
|
||||||
preferences.putInt("mqPR", marqueeBouncePauseRightMs);
|
preferences.putInt("mqPL", marqueeBouncePauseLeftMs);
|
||||||
preferences.putInt("mqOsDelay", marqueeOneShotDelayMs);
|
preferences.putInt("mqPR", marqueeBouncePauseRightMs);
|
||||||
preferences.putBool("mqOsStopC", marqueeOneShotStopCenter);
|
preferences.putInt("mqOsDelay", marqueeOneShotDelayMs);
|
||||||
preferences.putInt("mqOsRst", marqueeOneShotRestartSec);
|
preferences.putBool("mqOsStopC", marqueeOneShotStopCenter);
|
||||||
preferences.putBool("mqAccEn", marqueeAccelEnabled);
|
preferences.putInt("mqOsRst", marqueeOneShotRestartSec);
|
||||||
preferences.putInt("mqAccSt", marqueeAccelStartIntervalMs);
|
preferences.putBool("mqAccEn", marqueeAccelEnabled);
|
||||||
preferences.putInt("mqAccEnd", marqueeAccelEndIntervalMs);
|
preferences.putInt("mqAccSt", marqueeAccelStartIntervalMs);
|
||||||
preferences.putInt("mqAccDur", marqueeAccelDurationMs);
|
preferences.putInt("mqAccEnd", marqueeAccelEndIntervalMs);
|
||||||
preferences.putInt("bright", displayBrightness);
|
preferences.putInt("mqAccDur", marqueeAccelDurationMs);
|
||||||
|
preferences.putInt("bright", displayBrightness);
|
||||||
preferences.putString("cd_Title", String(countdownTitle));
|
|
||||||
|
preferences.putString("cd_Title", String(countdownTitle));
|
||||||
|
|
||||||
|
Serial.println("Settings saved successfully");
|
||||||
|
|
||||||
|
// Mise à jour de la couleur et de la date cible en dehors du contexte NVS
|
||||||
|
countdownColor = display.color565(colorR, colorG, colorB);
|
||||||
|
countdownTarget = DateTime(year, month, day, hour, minute, second);
|
||||||
|
|
||||||
|
} catch (...) {
|
||||||
|
Serial.println("Exception during NVS write - data may be corrupted");
|
||||||
|
}
|
||||||
|
|
||||||
preferences.end();
|
preferences.end();
|
||||||
Serial.println("Settings saved successfully");
|
|
||||||
|
|
||||||
// Mise à jour de la couleur et de la date cible
|
|
||||||
countdownColor = display.color565(colorR, colorG, colorB);
|
|
||||||
countdownTarget = DateTime(year, month, day, hour, minute, second);
|
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Failed to begin preferences");
|
Serial.println("Failed to begin preferences after retries");
|
||||||
}
|
}
|
||||||
|
|
||||||
xSemaphoreGive(preferencesMutex);
|
xSemaphoreGive(preferencesMutex);
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Could not acquire preferences mutex - save skipped");
|
Serial.println("Could not acquire preferences mutex - save skipped");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Réactiver le timer d'affichage si nécessaire
|
||||||
|
if (timerWasEnabled) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(5)); // Petit délai avant réactivation
|
||||||
|
display_update_enable(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connexion WiFi
|
// Connexion WiFi
|
||||||
@@ -1675,8 +1835,16 @@ void prepare_and_start_The_Server() {
|
|||||||
|
|
||||||
// Fonction de callback pour le timer d'affichage
|
// Fonction de callback pour le timer d'affichage
|
||||||
void IRAM_ATTR display_updater() {
|
void IRAM_ATTR display_updater() {
|
||||||
|
// Vérification rapide de la validité avant d'entrer en section critique
|
||||||
|
if (timer == nullptr) {
|
||||||
|
return; // Timer désactivé
|
||||||
|
}
|
||||||
|
|
||||||
portENTER_CRITICAL_ISR(&timerMux);
|
portENTER_CRITICAL_ISR(&timerMux);
|
||||||
display.display(display_draw_time);
|
// Double vérification dans la section critique
|
||||||
|
if (timer != nullptr) {
|
||||||
|
display.display(display_draw_time);
|
||||||
|
}
|
||||||
portEXIT_CRITICAL_ISR(&timerMux);
|
portEXIT_CRITICAL_ISR(&timerMux);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1912,10 +2080,18 @@ void CountdownTask(void * parameter) {
|
|||||||
// Vérifier s'il faut sauvegarder les paramètres (avec délai de sécurité)
|
// Vérifier s'il faut sauvegarder les paramètres (avec délai de sécurité)
|
||||||
if (saveRequested && (millis() - saveRequestTime) > 1500) { // Débounce 1.5s après dernière modif
|
if (saveRequested && (millis() - saveRequestTime) > 1500) { // Débounce 1.5s après dernière modif
|
||||||
saveRequested = false;
|
saveRequested = false;
|
||||||
// Attendre un peu plus pour s'assurer que le contexte web est terminé
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(50));
|
// Vérifications de sécurité avant sauvegarde
|
||||||
saveSettings();
|
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING && !xPortInIsrContext()) {
|
||||||
Serial.println("Settings saved from CountdownTask");
|
// Attendre un peu plus pour s'assurer que le contexte web est terminé
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
saveSettings();
|
||||||
|
Serial.println("Settings saved from CountdownTask");
|
||||||
|
} else {
|
||||||
|
Serial.println("Unsafe context - delaying save");
|
||||||
|
saveRequested = true; // Re-programmer la sauvegarde
|
||||||
|
saveRequestTime = millis() + 500; // Dans 500ms
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utiliser MUTEX_GUARD avec timeout optimisé
|
// Utiliser MUTEX_GUARD avec timeout optimisé
|
||||||
@@ -1939,7 +2115,9 @@ void CountdownTask(void * parameter) {
|
|||||||
// Ancienne NetworkTask fusionnée dans NetWebTask
|
// Ancienne NetworkTask fusionnée dans NetWebTask
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
// Pause pour éviter les problèmes de watchdog
|
// Surveillance du système et pause pour éviter les problèmes de watchdog
|
||||||
|
systemWatchdog();
|
||||||
|
|
||||||
// Les tâches FreeRTOS gèrent tout le travail
|
// Les tâches FreeRTOS gèrent tout le travail
|
||||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user