Files
2024-11-24 16:42:52 +01:00

354 lines
12 KiB
C++

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WiFiManager.h>
#include <EEPROM.h>
#include <ESP8266WebServer.h>
#include <ArduinoJson.h>
#include <Bounce2.h>
#include <NTPClient.h>
#include <time.h>
#include <WiFiUdp.h>
// Définition des broches
#define RELAY_PIN 12
#define BUTTON_PIN 0
#define LED_PIN 13
// EEPROM
#define EEPROM_SIZE 8 // Taille nécessaire pour stocker 4 octets pour la durée et 4 octets pour l'heure d'allumage
// Configuration WiFi
const char* ssid = "WifiHome2";
const char* password = "louca2212";
// Variables globales
Bounce buttonDebouncer;
ESP8266WebServer server(80); // Serveur Web sur le port 80
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 3600); // Serveur NTP avec décalage UTC+1
bool relayState = false;
unsigned long relayTimer = 0;
unsigned long relayDuration = 60000; // 60 secondes par défaut
bool saveSuccess = false; // Indique si l'EEPROM a été sauvegardée
int relayStartHour = 0; // Heure d'allumage (par défaut 00)
int relayStartMinute = 0; // Minute d'allumage (par défaut 00)
unsigned long lastNTPUpdate = 0; // Temps de la dernière mise à jour NTP (en ms)
// Fonction pour mettre à jour le client NTP toutes les 30 secondes
void updateNTP() {
unsigned long currentMillis = millis();
if (currentMillis - lastNTPUpdate >= 30000 || lastNTPUpdate == 0) {
timeClient.update();
lastNTPUpdate = currentMillis;
unsigned long rawTime = timeClient.getEpochTime();
Serial.println("Mise à jour NTP effectuée. Temps brut reçu : " + String(rawTime));
Serial.println("Temps formaté (UTC) : " + String(rawTime % 86400 / 3600) + ":" + String(rawTime % 3600 / 60));
}
}
// Fonction pour basculer l'état du relais
void toggleRelay() {
relayState = !relayState;
digitalWrite(RELAY_PIN, relayState);
if (relayState) {
relayTimer = millis();
Serial.println("Relais activé");
} else {
Serial.println("Relais désactivé");
}
}
// Fonction pour sauvegarder les paramètres dans l'EEPROM
void saveSettings() {
EEPROM.write(0, relayDuration / 60000); // Sauvegarde la durée en minutes
EEPROM.write(1, relayStartHour); // Sauvegarde l'heure
EEPROM.write(2, relayStartMinute); // Sauvegarde les minutes
EEPROM.commit();
Serial.println("Paramètres sauvegardés dans l'EEPROM");
}
// Fonction pour charger les paramètres depuis l'EEPROM
void loadSettings() {
relayDuration = EEPROM.read(0) * 60000; // Chargement de la durée en ms
relayStartHour = EEPROM.read(1); // Chargement de l'heure
relayStartMinute = EEPROM.read(2); // Chargement des minutes
// Vérifier les valeurs invalides et appliquer des valeurs par défaut
if (relayDuration == 0 || relayDuration > 10800000) relayDuration = 60000; // Valeur par défaut 60 secondes
if (relayStartHour < 0 || relayStartHour >= 24) relayStartHour = 0; // Heure par défaut 00
if (relayStartMinute < 0 || relayStartMinute >= 60) relayStartMinute = 0; // Minute par défaut 00
Serial.println("Paramètres chargés :");
Serial.println("Durée du relais : " + String(relayDuration / 60000) + " minutes");
Serial.println("Heure d'allumage : " + String(relayStartHour) + ":" + String(relayStartMinute));
}
// Fonction pour mettre à jour la LED en fonction de l'état
void updateLED() {
if (WiFi.status() != WL_CONNECTED) {
digitalWrite(LED_PIN, LOW); // LED éteinte si déconnecté
} else if (relayState) {
digitalWrite(LED_PIN, LOW); // LED allumée si relais activé
} else {
digitalWrite(LED_PIN, HIGH); // LED éteinte si relais désactivé
}
}
// Gestion des clics du bouton
void handleButton() {
if (buttonDebouncer.fell()) {
Serial.println("Bouton pressé");
toggleRelay(); // Bascule le relais
}
}
// Fonction pour obtenir l'heure au format HH:MM
String getFormattedTime() {
unsigned long rawTime = timeClient.getEpochTime();
unsigned long adjustedTime = rawTime ;
int hours = (adjustedTime % 86400L) / 3600; // Heures
int minutes = (adjustedTime % 3600) / 60; // Minutes
char buffer[6];
snprintf(buffer, sizeof(buffer), "%02d:%02d", hours, minutes);
return String(buffer);
}
// Calculer le délai en minutes avant le prochain allumage
int calculateMinutesUntilNextStart() {
unsigned long currentTime = timeClient.getEpochTime();
int currentHour = (currentTime % 86400L) / 3600;
int currentMinute = (currentTime % 3600) / 60;
// Convertir l'heure actuelle et l'heure de démarrage en minutes depuis minuit
int currentTotalMinutes = currentHour * 60 + currentMinute;
int startTotalMinutes = relayStartHour * 60 + relayStartMinute;
// Calculer la différence
int delay = startTotalMinutes - currentTotalMinutes;
if (delay < 0) {
delay += 1440; // Ajouter 24 heures si le délai est négatif (allumage le lendemain)
}
return delay;
}
int calculateMinutesUntilNextStop() {
int delayUntilStart = calculateMinutesUntilNextStart();
if (relayState) {
// Si le relais est déjà activé, calcule le temps restant jusqu'à son extinction
unsigned long elapsedMillis = millis() - relayTimer;
return max(0L, (long)((relayDuration - elapsedMillis) / 60000));
} else {
// Si le relais est désactivé, ajoute la durée d'activation au délai jusqu'au prochain allumage
return delayUntilStart + (relayDuration / 60000);
}
}
// Page principale
void handleMainPage() {
updateNTP(); // Mettre à jour le client NTP si nécessaire
String currentTime = getFormattedTime();
int nextStart = calculateMinutesUntilNextStart();
int nextStop = calculateMinutesUntilNextStop();
String relayStateColor = relayState ? "green" : "red"; // Détermine la couleur du texte
String relayStateText = relayState ? "ACTIVÉ" : "DÉSACTIVÉ";
String html = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Relais Sonoff</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; margin-top: 50px; }
button { padding: 10px 20px; font-size: 16px; margin: 20px; cursor: pointer; }
#relayState { font-weight: bold; color: RELAY_COLOR; }
</style>
<script>
setInterval(() => {
location.reload(); // Rafraîchit la page toutes les secondes
}, 10000);
</script>
</head>
<body>
<h1>Relais Sonoff</h1>
<p>Heure : <span id="currentTime">TIME</span></p>
<p><strong>État du relais :</strong> <span id="relayState">RELAY_STATE</span></p>
<p>Délai avant prochain allumage du relais : <span id="nextStart">NEXT_START</span> minutes</p>
<p>Délai avant prochaine extinction du relais : <span id="nextStop">NEXT_STOP</span> minutes</p>
<p><button onclick="location.href='/settings'">Paramètres</button></p>
</body>
</html>
)rawliteral";
html.replace("TIME", currentTime);
html.replace("RELAY_STATE", relayStateText);
html.replace("RELAY_COLOR", relayStateColor);
html.replace("NEXT_START", String(nextStart));
html.replace("NEXT_STOP", String(nextStop));
server.send(200, "text/html", html);
}
// Page des paramètres
void handleSettingsPage() {
// Charger les paramètres depuis l'EEPROM
loadSettings();
// Générer la page HTML
String html = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Paramètres Relais</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; margin-top: 50px; }
button { padding: 10px 20px; font-size: 16px; margin: 20px; cursor: pointer; }
</style>
<script>
function saveSettings() {
const duration = parseInt(document.getElementById('duration').value);
const startHour = parseInt(document.getElementById('startHour').value);
const startMinute = parseInt(document.getElementById('startMinute').value);
if (isNaN(duration) || duration < 0 || duration > 180) {
alert('Erreur : Durée invalide (entre 0 et 180 minutes).');
return;
}
if (isNaN(startHour) || startHour < 0 || startHour > 23) {
alert('Erreur : Heure invalide (entre 0 et 23).');
return;
}
if (isNaN(startMinute) || startMinute < 0 || startMinute > 59) {
alert('Erreur : Minute invalide (entre 0 et 59).');
return;
}
fetch('/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
relayDuration: duration,
relayStartHour: startHour,
relayStartMinute: startMinute
})
}).then(() => {
location.href = '/';
});
}
function goBack() {
location.href = '/';
}
</script>
</head>
<body>
<h1>Paramètres Relais</h1>
<p>Durée (minutes) : <input type="text" id="duration" value="DURATION"></p>
<p>Heure d'allumage : <input type="text" id="startHour" value="HOUR" size="2"> : <input type="text" id="startMinute" value="MINUTE" size="2"></p>
<p><button onclick="saveSettings()">Enregistrer</button></p>
<p><button onclick="goBack()">Retour</button></p>
</body>
</html>
)rawliteral";
html.replace("DURATION", String(relayDuration / 60000));
html.replace("HOUR", String(relayStartHour));
html.replace("MINUTE", String(relayStartMinute));
server.send(200, "text/html", html);
}
// Sauvegarde des paramètres
void handleSaveSettings() {
JsonDocument doc;
DeserializationError error = deserializeJson(doc, server.arg("plain"));
if (error) {
server.send(400, "text/plain", "Erreur JSON");
return;
}
relayDuration = doc["relayDuration"].as<unsigned long>() * 60000;
relayStartHour = doc["relayStartHour"].as<int>();
relayStartMinute = doc["relayStartMinute"].as<int>();
saveSettings();
server.send(200, "text/plain", "Paramètres sauvegardés.");
}
// Initialisation
void setup() {
Serial.begin(115200);
Serial.println("Démarrage du programme...");
// Initialisation des broches
pinMode(RELAY_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW);
digitalWrite(LED_PIN, LOW);
Serial.println("Broches configurées");
// Configuration du bouton avec Bounce2
buttonDebouncer.attach(BUTTON_PIN);
buttonDebouncer.interval(25);
Serial.println("Bouton configuré");
// Connexion WiFi avec WiFiManager
Serial.println("Connexion au WiFi...");
WiFiManager wifiManager;
wifiManager.autoConnect("SonoffRelay", "password");
Serial.println("Connecté au WiFi : " + String(WiFi.SSID()) + " - IP : " + WiFi.localIP().toString());
EEPROM.begin(EEPROM_SIZE);
loadSettings();
// Initialisation du client NTP
timeClient.begin();
updateNTP();
server.on("/", handleMainPage);
server.on("/settings", handleSettingsPage);
server.on("/save", HTTP_POST, handleSaveSettings);
server.begin();
Serial.println("Serveur web démarré");
}
// Boucle principale
void loop() {
buttonDebouncer.update();
handleButton();
// Mise à jour de l'heure NTP
timeClient.update();
// Gère les requêtes HTTP des clients
server.handleClient();
// Obtenir l'heure actuelle
unsigned long rawTime = timeClient.getEpochTime();
int currentHour = (rawTime % 86400L) / 3600; // Heures actuelles
int currentMinute = (rawTime % 3600) / 60; // Minutes actuelles
// Vérifier si l'heure actuelle correspond à l'heure d'allumage
if (!relayState && currentHour == relayStartHour && currentMinute == relayStartMinute) {
Serial.println("Allumage automatique du relais à l'heure programmée");
toggleRelay(); // Allume le relais
}
// Gestion du relais (arrêt automatique après la durée spécifiée)
if (relayState && millis() - relayTimer >= relayDuration) {
Serial.println("Extinction automatique du relais après la durée configurée");
toggleRelay(); // Éteint le relais
}
// Mise à jour de la LED
updateLED();
}