/*
* ========================================
* SYSTÈME DE CONTRÔLE DE POMPE ESP32
* ========================================
*
* Capteurs:
* - DHT22: Indicateur température ambiante
* - DS18B20 #1: Température réservoir (Pompe_Action_JP)
* - DS18B20 #2: Pic température retour (Retour_Pic_Temp_JP)
*
* Actionneurs:
* - Relay: Contrôle pompe
* - LED Verte: Relay activé
* - LED Rouge: Relay désactivé
* - LED Bleue: WiFi connecté
*
* Interface:
* - LCD I2C: Affichage état système
* - Bouton: Démarrage manuel/arrêt pompe
*/
#include <WiFi.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <time.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
// ========================================
// VARIABLES DE CONFIGURATION
// ========================================
// WiFi Configuration
const char* WIFI_SSID = "TP-Link_DD7F";
const char* WIFI_PASSWORD = "53562244";
IPAddress staticIP(192, 168, 0, 189); // Adresse IP statique
IPAddress gateway(192, 168, 0, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress primaryDNS(8, 8, 8, 8);
// NTP Configuration
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 3600; // GMT+1 pour la Belgique
const int daylightOffset_sec = 3600; // Heure d'été
// Google Script URL
const char* GOOGLE_SCRIPT_URL = "https://script.google.com/macros/s/AKfycbyjog5khCAJohoE8hhBqePivipewDA-jVuLMDrliW8Gip7j8QvvrD_rYKIPXJV9Syq-/exec";
// Paramètres de contrôle pompe
const float TEMP_SEUIL_DHT22 = 12.0; // Température seuil DHT22 (°C)
const unsigned long DUREE_RELAY_MS = 300000; // Durée activation relay (5 min = 300000 ms)
const unsigned long INTERVALLE_CYCLES_MS = 3600000; // Intervalle entre cycles (1h = 3600000 ms)
// Pins GPIO
#define PIN_DHT22 4
#define PIN_DS18B20 5
#define PIN_RELAY 26
#define PIN_BUTTON 27
#define PIN_LED_VERTE 32
#define PIN_LED_ROUGE 33
#define PIN_LED_BLEUE 25
#define PIN_SDA 21
#define PIN_SCL 22
// ========================================
// INITIALISATION COMPOSANTS
// ========================================
DHT dht(PIN_DHT22, DHT22);
OneWire oneWire(PIN_DS18B20);
DallasTemperature sensors(&oneWire);
LiquidCrystal_I2C lcd(0x27, 16, 2); // Adresse I2C à ajuster si nécessaire (0x27 ou 0x3F)
// Adresses DS18B20
DeviceAddress sondePompe = {0x28, 0xEB, 0xEB, 0x69, 0x81, 0x22, 0x0B, 0xDF}; // Pompe_Action_JP
DeviceAddress sondeRetour = {0x28, 0xF1, 0x2E, 0xB3, 0xB1, 0x22, 0x09, 0xC3}; // Retour_Pic_Temp_JP
// ========================================
// VARIABLES D'ÉTAT
// ========================================
bool relayActif = false;
bool boutonPrecedent = HIGH;
unsigned long dernierCycle = 0;
unsigned long debutRelayActif = 0;
float tempDHT22 = 0;
float tempPompe = 0;
float tempRetour = 0;
float tempPicRetour = 0;
bool cycleEnCours = false;
bool arretManuel = false;
unsigned long dernierTestWifi = 0;
const unsigned long INTERVALLE_TEST_WIFI = 30000; // Test WiFi toutes les 30s
enum EtatCycle {
ATTENTE,
DEMARRAGE_POMPE,
POMPE_ACTIVE,
MESURE_PIC,
ARRET_POMPE
};
EtatCycle etatCycle = ATTENTE;
// ========================================
// SETUP
// ========================================
void setup() {
Serial.begin(115200);
Serial.println("\n========================================");
Serial.println("Démarrage système contrôle pompe ESP32");
Serial.println("========================================");
// Configuration pins
pinMode(PIN_RELAY, OUTPUT);
pinMode(PIN_LED_VERTE, OUTPUT);
pinMode(PIN_LED_ROUGE, OUTPUT);
pinMode(PIN_LED_BLEUE, OUTPUT);
pinMode(PIN_BUTTON, INPUT_PULLUP);
// État initial
digitalWrite(PIN_RELAY, LOW);
digitalWrite(PIN_LED_ROUGE, HIGH);
digitalWrite(PIN_LED_VERTE, LOW);
digitalWrite(PIN_LED_BLEUE, LOW);
// Initialisation LCD
Wire.begin(PIN_SDA, PIN_SCL);
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Demarrage...");
// Initialisation capteurs
dht.begin();
sensors.begin();
// Recherche des sondes DS18B20
Serial.println("\nRecherche des sondes DS18B20...");
Serial.print("Nombre de sondes trouvées: ");
Serial.println(sensors.getDeviceCount());
/*
if (sensors.getDeviceCount() >= 2) {
sensors.getAddress(sondePompe, 0);
sensors.getAddress(sondeRetour, 1);
Serial.println("Sondes DS18B20 configurées");
afficherAdresseDS18B20("Pompe", sondePompe);
afficherAdresseDS18B20("Retour", sondeRetour);
} else {
Serial.println("ERREUR: Moins de 2 sondes DS18B20 détectées!");
lcd.clear();
lcd.print("ERR: Sondes DS");
}
*/
// Connexion WiFi
//connecterWiFi();
/* 001 - START - a supprimer */
WiFi.mode(WIFI_STA);
// SSID Wokwi, pas de mot de passe. Le "6" (canal) rend la connexion plus rapide.
WiFi.begin("Wokwi-GUEST", "", 6);
Serial.print("Connexion WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(200);
Serial.print(".");
digitalWrite(PIN_LED_BLEUE, HIGH);
}
Serial.println("\nConnecté !");
Serial.print("IP locale: ");
Serial.println(WiFi.localIP());
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi connecté!");
Serial.print("Adresse IP: ");
Serial.println(WiFi.localIP());
digitalWrite(PIN_LED_BLEUE, HIGH);
lcd.clear();
lcd.print("WiFi OK");
lcd.setCursor(0, 1);
lcd.print(WiFi.localIP());
delay(2000);
} else {
Serial.println("\nÉchec connexion WiFi!");
lcd.clear();
lcd.print("WiFi ERREUR");
}
/* 001 - END - a supprimer */
// Configuration NTP
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
Serial.println("Configuration NTP effectuée");
// Affichage prêt
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Systeme pret");
delay(2000);
Serial.println("========================================");
Serial.println("Système prêt!");
Serial.println("========================================\n");
}
// ========================================
// BOUCLE PRINCIPALE
// ========================================
void loop() {
// Lecture capteurs
lireCapteurs();
// Gestion bouton
gererBouton();
// Test périodique WiFi
testerWiFi();
// Logique de contrôle pompe
gererCyclePompe();
// Mise à jour affichage
mettreAJourAffichage();
delay(100);
}
// ========================================
// FONCTIONS CAPTEURS
// ========================================
void lireCapteurs() {
// Lecture DHT22
tempDHT22 = dht.readTemperature();
if (isnan(tempDHT22)) {
Serial.println("Erreur lecture DHT22");
tempDHT22 = 0;
}
// Lecture DS18B20
sensors.requestTemperatures();
tempPompe = sensors.getTempC(sondePompe);
tempRetour = sensors.getTempC(sondeRetour);
if (tempPompe == DEVICE_DISCONNECTED_C) {
Serial.println("Erreur lecture sonde Pompe");
Serial.println(tempPompe);
tempPompe = 0;
}
if (tempRetour == DEVICE_DISCONNECTED_C) {
Serial.println("Erreur lecture sonde Retour");
tempRetour = 0;
}
}
// ========================================
// GESTION BOUTON
// ========================================
void gererBouton() {
bool boutonActuel = digitalRead(PIN_BUTTON);
// Détection front descendant (bouton pressé)
if (boutonActuel == LOW && boutonPrecedent == HIGH) {
delay(50); // Anti-rebond
if (digitalRead(PIN_BUTTON) == LOW) {
Serial.println("\n>>> BOUTON PRESSÉ <<<");
if (!relayActif) {
// Démarrage manuel
Serial.println("Démarrage manuel de la pompe");
demarrerPompe("bouton");
arretManuel = false;
} else {
// Arrêt manuel
Serial.println("Arrêt manuel de la pompe");
arreterPompe("bouton");
arretManuel = true;
}
}
}
boutonPrecedent = boutonActuel;
}
// ========================================
// GESTION CYCLE POMPE
// ========================================
void gererCyclePompe() {
unsigned long maintenant = millis();
switch (etatCycle) {
case ATTENTE:
// Vérifier si conditions démarrage automatique sont remplies
Serial.println("OK");
if (!arretManuel && tempDHT22 < TEMP_SEUIL_DHT22 && tempDHT22 > 0 && (maintenant - dernierCycle >= INTERVALLE_CYCLES_MS)) {
Serial.println("\n>>> CONDITIONS REMPLIES: DÉMARRAGE AUTO <<<");
demarrerPompe("auto");
}
break;
case DEMARRAGE_POMPE:
// Envoyer données démarrage
envoyerDonnees("Pompe_Action_JP", tempPompe, "auto(on)");
etatCycle = POMPE_ACTIVE;
tempPicRetour = tempRetour; // Initialiser pic
break;
case POMPE_ACTIVE:
// Surveiller pic température retour
if (tempRetour > tempPicRetour) {
tempPicRetour = tempRetour;
}
// Vérifier durée d'activation
if (maintenant - debutRelayActif >= DUREE_RELAY_MS) {
Serial.println("\n>>> FIN DURÉE ACTIVATION <<<");
etatCycle = MESURE_PIC;
}
break;
case MESURE_PIC:
// Envoyer pic température retour
envoyerDonnees("Retour_Pic_Temp_JP", tempPicRetour, "auto(mesure)");
etatCycle = ARRET_POMPE;
break;
case ARRET_POMPE:
// Arrêter pompe et envoyer température finale
arreterPompe("auto");
envoyerDonnees("Pompe_Action_JP", tempPompe, "auto(off)");
etatCycle = ATTENTE;
dernierCycle = maintenant;
arretManuel = false;
break;
}
}
// ========================================
// CONTRÔLE RELAY
// ========================================
void demarrerPompe(String mode) {
relayActif = true;
digitalWrite(PIN_RELAY, HIGH);
digitalWrite(PIN_LED_VERTE, HIGH);
digitalWrite(PIN_LED_ROUGE, LOW);
debutRelayActif = millis();
if (mode == "auto") {
etatCycle = DEMARRAGE_POMPE;
} else {
// Démarrage bouton: envoyer données immédiatement
envoyerDonnees("Pompe_Action_JP", tempPompe, "bouton(on)");
etatCycle = POMPE_ACTIVE;
tempPicRetour = tempRetour;
}
Serial.println("Pompe démarrée - Mode: " + mode);
}
void arreterPompe(String mode) {
relayActif = false;
digitalWrite(PIN_RELAY, LOW);
digitalWrite(PIN_LED_VERTE, LOW);
digitalWrite(PIN_LED_ROUGE, HIGH);
if (mode == "bouton") {
// Arrêt manuel: envoyer pic et température finale
envoyerDonnees("Retour_Pic_Temp_JP", tempPicRetour, "bouton(mesure)");
envoyerDonnees("Pompe_Action_JP", tempPompe, "bouton(off)");
etatCycle = ATTENTE;
dernierCycle = millis();
}
Serial.println("Pompe arrêtée - Mode: " + mode);
}
// ========================================
// COMMUNICATION
// ========================================
void connecterWiFi() {
Serial.print("\nConnexion WiFi à: ");
Serial.println(WIFI_SSID);
lcd.clear();
lcd.print("WiFi...");
WiFi.config(staticIP, gateway, subnet, primaryDNS);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
int tentatives = 0;
while (WiFi.status() != WL_CONNECTED && tentatives < 20) {
delay(500);
Serial.print(".");
tentatives++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi connecté!");
Serial.print("Adresse IP: ");
Serial.println(WiFi.localIP());
digitalWrite(PIN_LED_BLEUE, HIGH);
lcd.clear();
lcd.print("WiFi OK");
lcd.setCursor(0, 1);
lcd.print(WiFi.localIP());
delay(2000);
} else {
Serial.println("\nÉchec connexion WiFi!");
lcd.clear();
lcd.print("WiFi ERREUR");
}
}
void testerWiFi() {
unsigned long maintenant = millis();
if (maintenant - dernierTestWifi >= INTERVALLE_TEST_WIFI) {
dernierTestWifi = maintenant;
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi déconnecté! Reconnexion...");
digitalWrite(PIN_LED_BLEUE, LOW);
connecterWiFi();
} else {
digitalWrite(PIN_LED_BLEUE, HIGH);
}
}
}
void envoyerDonnees(String nomSonde, float temperature, String mode) {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("Erreur: WiFi non connecté, impossible d'envoyer les données");
return;
}
HTTPClient http;
// Obtenir horodatage
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("Erreur obtention heure NTP");
}
char horodatage[30];
strftime(horodatage, sizeof(horodatage), "%Y-%m-%d %H:%M:%S", &timeinfo);
// Construction URL avec paramètres
String url = String(GOOGLE_SCRIPT_URL);
url += "?sonde=" + urlEncode(nomSonde);
url += "&temperature=" + String(temperature, 2);
url += "&mode=" + urlEncode(mode);
url += "×tamp=" + urlEncode(String(horodatage));
Serial.println("\n--- ENVOI DONNÉES ---");
Serial.println("URL: " + url);
http.begin(url);
http.setTimeout(10000);
int httpCode = http.GET();
if (httpCode > 0) {
Serial.print("Code réponse HTTP: ");
Serial.println(httpCode);
if (httpCode == HTTP_CODE_OK) {
String response = http.getString();
Serial.println("Réponse: " + response);
Serial.println("✓ Données envoyées avec succès");
}
} else {
Serial.print("Erreur HTTP: ");
Serial.println(http.errorToString(httpCode));
}
http.end();
Serial.println("---------------------\n");
}
// Encodage URL pour caractères spéciaux
String urlEncode(String str) {
String encodedString = "";
char c;
char code0;
char code1;
for (int i = 0; i < str.length(); i++) {
c = str.charAt(i);
if (c == ' ') {
encodedString += '+';
} else if (isalnum(c)) {
encodedString += c;
} else {
code1 = (c & 0xf) + '0';
if ((c & 0xf) > 9) {
code1 = (c & 0xf) - 10 + 'A';
}
c = (c >> 4) & 0xf;
code0 = c + '0';
if (c > 9) {
code0 = c - 10 + 'A';
}
encodedString += '%';
encodedString += code0;
encodedString += code1;
}
}
return encodedString;
}
// ========================================
// AFFICHAGE
// ========================================
void mettreAJourAffichage() {
static unsigned long dernierMaj = 0;
unsigned long maintenant = millis();
if (maintenant - dernierMaj >= 2000) { // Mise à jour toutes les 2s
dernierMaj = maintenant;
lcd.clear();
// Ligne 1: État relay + température DHT22
lcd.setCursor(0, 0);
if (relayActif) {
lcd.print("POMPE ON ");
} else {
lcd.print("Attente ");
}
lcd.print(tempDHT22, 1);
lcd.print("C");
// Ligne 2: Températures DS18B20
lcd.setCursor(0, 1);
lcd.print("B:");
lcd.print(tempPompe, 1);
lcd.print(" R:");
lcd.print(tempRetour, 1);
}
}
void afficherAdresseDS18B20(String nom, DeviceAddress addr) {
Serial.print("Adresse sonde ");
Serial.print(nom);
Serial.print(": ");
for (uint8_t i = 0; i < 8; i++) {
if (addr[i] < 16) Serial.print("0");
Serial.print(addr[i], HEX);
if (i < 7) Serial.print(":");
}
Serial.println();
}