Messages : 163
Sujets : 13
Inscription : Jun 2024
Hier, 03:46 PM
(Modification du message : Hier, 04:54 PM par tupolev89.)
(Hier, 08:49 AM)Lolo69 a écrit : Petite evolution du code... je suis passé à la version en 2.01 car elle comporte de nombreux gros changements par rapport aux versions 1.x
- L'ensemble des paramètres sont modifiables dans la page Admin => plus besoin de refaire de nombreuses compilations
- 1er Demarrage en point d'accès ESP pour parametrer le wifi depuis la page admin, en cas de perte de la box on repasse en point d'accès ESP pour continuer de pouvoir piloter la vanne localement depuis un telephone connecter sur le ESP_CONFIG à la page http://192.168.4.1/admin
- Liste des destinataires depuis la plage admin permet de rentrer autant de destinataires que l'on souhaite , il suffit de les séparer par un ;
- Mise à jour OTA pour pouvoir t'envoyer des fichiers dejà compilés sans repasser par l'étape compilation par arduino
- dans la page admin le champ déclencheur type contient le mot magique à mettre dans le push de l ecodevice dans l url pour fermer la vanne selon la condition que tu auras choisi par exemple l url sera /?type=inondation quand le push sera activé cela va mettre la gpio à zero et bien sûr envoyer un email aux destinataires
- en cas de soucis avec le wifi il suffit de redemarrer l ESP pour rouvrir la vanne, au demarrage elle est ouverte par défaut
- Dans l'automatisation par sécurité je n'ai pas voulu de fonction pour rouvrir la vanne par un push
- pour la rouvrir tu peux donc soit faire un reset esp mais beaucoup plus commode depuis la page admin tu peux Ouvrir Fermer à volonté
- Par defaut j'ai choisi la GPIO2 car c est celle qui pilote la led sur l ESP ca permet de voir ce qui se passe sans avoir un voltmetre, par la suite il faudra evidemment paramtrer une autre GPIO par exemple la 23
je pense avoir fait le tour des nouveautés
Mode d'emploi
- Compile et charge le code suivant sans faire la moinde modif ( potentieillement il va te dire qu 'il manque des bibliothèques que tu sais maintenant trouver et charger
- A la fin du chargement apres le reboot de l ESP tu vas trouver un reseau wifi nommé ESP_CONFIG
- Connecte ton PC ou ton telephone à ce reseau WIFI ( pas de mot de passe)
- Ouvre un navigateur à l'adresse http://192.168.4.1/admin pour acceder à cette page le user par defaut est admin et le mot de passe est admin cela va ouvrir la page admin dans laquelle tu vas renseigner tous les champs ( evidememnt tu pourras changer le user mot de passe admin d'accès à cette page
- Quand tu as tout bien renseigné les champs tu cliques sur sauvegarde (Attention je n'ai pas fait de bouton magique pour l instant pour remettre le mot de passe admin par defaut , donc prudence, sinon tu seras obligé de reteleverser le programme
Si j ai oublié quelque chose fais moi signe ici
Code : //Version 2.01 22/11/25
/*
Parametrages depuis page /admin
envoi à une liste de destinataires séparés par des ;
GPIO configurable depuis admin, pilotable en temps réel
Trigger configurable pour mise à 0 de GPIO
Ajout OTA via /admin
*/
#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>
#include <LittleFS.h>
#include <WiFiClientSecure.h>
#include <ESP_Mail_Client.h>
#include <Update.h>
#define WIFI_CONNECT_TIMEOUT_MS 10000
#define AP_SSID "ESP32_Config"
// Eviter NULL pour ne pas appeler strlen(NULL)
#define AP_PASSWORD ""
// si tu veux un AP sécurisé par défaut, mets une chaîne >=8 caractères
// #define AP_PASSWORD "esp32admin"
const char* CONFIG_FILE = "/config.txt";
// Valeurs par défaut
String cfg_ssid = "ton SSID";
String cfg_password = "ton mot de passe wifi";
String cfg_smtp = "smtp.orange.fr";
String cfg_email = "tonemail@orange.fr";
String cfg_smtp_pwd = "ton mot de passe Mail";
String cfg_destinataires = "dest1@free.fr;dest2@orange.fr";
int cfg_smtp_port = 465;
int cfg_gpio_number = 2;
String cfg_trigger_type = "inondation";
// Réseau statique
String cfg_local_ip = "192.168.1.119";
String cfg_gateway = "192.168.1.1";
String cfg_subnet = "255.255.255.0";
String cfg_primaryDNS = "192.168.1.1";
String cfg_secondaryDNS = "8.8.4.4";
// Admin auth
String admin_user = "admin";
String admin_pwd = "admin";
// Objets globaux
WebServer server(80);
SMTPSession smtp;
// --- Helpers IP ---
IPAddress ipFromString(const String &s) {
IPAddress ip(0,0,0,0);
int parts[4] = {0,0,0,0}, idx = 0;
String tmp = s; tmp.trim();
int start = 0;
for (int i=0; i<4; ++i) {
int pos = tmp.indexOf('.', start);
String part = (pos==-1) ? tmp.substring(start) : tmp.substring(start,pos);
part.trim(); parts[idx++] = part.toInt();
if (pos==-1) break; start = pos+1;
}
ip = IPAddress(parts[0], parts[1], parts[2], parts[3]);
return ip;
}
// --- LittleFS ---
void saveConfig() {
File f = LittleFS.open(CONFIG_FILE, "w");
if (!f) { Serial.println("Erreur ouverture fichier config"); return; }
f.printf("ssid=%s\n", cfg_ssid.c_str());
f.printf("password=%s\n", cfg_password.c_str());
f.printf("smtp=%s\n", cfg_smtp.c_str());
f.printf("email=%s\n", cfg_email.c_str());
f.printf("smtp_pwd=%s\n", cfg_smtp_pwd.c_str());
f.printf("destinataires=%s\n", cfg_destinataires.c_str());
f.printf("smtp_port=%d\n", cfg_smtp_port);
f.printf("gpio_number=%d\n", cfg_gpio_number);
f.printf("trigger_type=%s\n", cfg_trigger_type.c_str());
f.printf("local_ip=%s\n", cfg_local_ip.c_str());
f.printf("gateway=%s\n", cfg_gateway.c_str());
f.printf("subnet=%s\n", cfg_subnet.c_str());
f.printf("primaryDNS=%s\n", cfg_primaryDNS.c_str());
f.printf("secondaryDNS=%s\n", cfg_secondaryDNS.c_str());
f.printf("admin_user=%s\n", admin_user.c_str());
f.printf("admin_pwd=%s\n", admin_pwd.c_str());
f.close();
Serial.println("Config sauvegardée dans LittleFS");
}
void loadConfig() {
if (!LittleFS.exists(CONFIG_FILE)) { Serial.println("Fichier config absent -> valeurs par défaut"); return; }
File f = LittleFS.open(CONFIG_FILE,"r");
if (!f) { Serial.println("Erreur ouverture fichier config"); return; }
while(f.available()){
String line = f.readStringUntil('\n'); line.trim();
if (line.length()==0) continue;
int eq = line.indexOf('='); if (eq==-1) continue;
String key=line.substring(0,eq), val=line.substring(eq+1); key.trim(); val.trim();
if(key=="ssid") cfg_ssid=val;
else if(key=="password") cfg_password=val;
else if(key=="smtp") cfg_smtp=val;
else if(key=="email") cfg_email=val;
else if(key=="smtp_pwd") cfg_smtp_pwd=val;
else if(key=="destinataires") cfg_destinataires=val;
else if(key=="smtp_port") cfg_smtp_port=val.toInt();
else if(key=="gpio_number") cfg_gpio_number=val.toInt();
else if(key=="trigger_type") cfg_trigger_type=val;
else if(key=="local_ip") cfg_local_ip = val;
else if(key=="gateway") cfg_gateway = val;
else if(key=="subnet") cfg_subnet = val;
else if(key=="primaryDNS") cfg_primaryDNS = val;
else if(key=="secondaryDNS") cfg_secondaryDNS = val;
else if(key=="admin_user") admin_user = val;
else if(key=="admin_pwd") admin_pwd = val;
}
f.close();
Serial.println("Config chargée depuis LittleFS");
}
// --- SMTP ---
void addAllRecipients(SMTP_Message &message, const String &liste) {
String s=liste; int start=0,pos=0;
while((pos=s.indexOf(';',start))!=-1){ String email=s.substring(start,pos); email.trim(); if(email.length()>0) message.addRecipient("Destinataire",email.c_str()); start=pos+1;}
String email=s.substring(start); email.trim(); if(email.length()>0) message.addRecipient("Destinataire",email.c_str());
}
// --- Page root ---
void handleRoot() {
time_t now=time(nullptr); struct tm timeinfo; localtime_r(&now,&timeinfo);
char buffer[20]; strftime(buffer,sizeof(buffer),"%d/%m/%Y %H:%M:%S",&timeinfo);
String type="Non défini";
if(server.hasArg("type")) type=server.arg("type");
// Vérifie le trigger pour GPIO
if(type.equalsIgnoreCase(cfg_trigger_type)){
digitalWrite(cfg_gpio_number, LOW); // GPIO à 0
Serial.println("Trigger activé : GPIO mis à 0");
}
ESP_Mail_Session session;
session.server.host_name=cfg_smtp.c_str();
session.server.port=cfg_smtp_port;
session.login.email=cfg_email.c_str();
session.login.password=cfg_smtp_pwd.c_str();
session.login.user_domain="";
SMTP_Message message;
message.sender.name="ECODEVICE";
message.sender.email=cfg_email.c_str();
addAllRecipients(message,cfg_destinataires);
message.subject="Alerte de type : "+type;
message.text.content="Bonjour ! Email envoyé depuis ESP32 le "+String(buffer);
smtp.debug(1);
String prefixPage="<html><head><meta charset='UTF-8'><meta http-equiv='Cache-Control' content='no-cache, no-store, must-revalidate'><meta name='viewport' content='width=device-width, initial-scale=1'>"
"<style>body{font-family:Arial;padding:16px;} .ok{color:green;} .ko{color:red;}</style></head><body>";
String suffixPage="</body></html>";
String page=prefixPage+suffixPage;
if(!smtp.connect(&session)){
Serial.println("Erreur de connexion SMTP !");
server.send(200,"text/html",prefixPage+"<p class='ko'>Erreur connexion SMTP</p>"+suffixPage);
return;
}
if(!MailClient.sendMail(&smtp,&message)){
page=prefixPage+"<p class='ko'>Erreur d'envoi !</p>"+suffixPage; Serial.println("Erreur d'envoi !");
}else{
page=prefixPage+"<p class='ok'>Email envoyé le "+String(buffer)+"</p>"+suffixPage; Serial.println("Email envoyé !");
}
smtp.closeSession();
server.send(200,"text/html",page);
}
// --- Auth & escape ---
bool checkAuth() { if(!server.authenticate(admin_user.c_str(),admin_pwd.c_str())) { server.requestAuthentication(); return false;} return true; }
String htmlEscape(const String &s){ String r=s; r.replace("&","&"); r.replace("<","<"); r.replace(">",">"); r.replace("\"","""); r.replace("'","'"); return r; }
void handleAdmin(){
if(!checkAuth()) return;
int gpio_state=digitalRead(cfg_gpio_number);
String page="<!DOCTYPE html><html><head><meta charset='UTF-8'><meta http-equiv='Cache-Control' content='no-cache, no-store, must-revalidate'><meta name='viewport' content='width=device-width, initial-scale=1'>"
"<style>body{font-family:Arial;padding:12px;} label{display:block;margin-top:8px;} input[type=text], input[type=password], textarea{width:100%;padding:8px;border-radius:6px;border:1px solid #ccc;box-sizing:border-box;} .row{display:flex;gap:8px;} .col{flex:1;} .btn{display:inline-block;padding:8px 12px;margin-top:10px;border-radius:6px;background:#2196F3;color:#fff;text-decoration:none;}</style>"
"<title>Admin</title></head><body>"
"<h2>Configuration ESP32</h2>"
"<h3>GPIO " + String(cfg_gpio_number) + " : <span id='gpio_state'>" + (gpio_state?"ON":"OFF") + "</span></h3>"
"<button onclick=\"toggleGPIO('on')\">Ouvrir</button> "
"<button onclick=\"toggleGPIO('off')\">Fermer</button><hr>"
"<form method='POST' action='/save'>"
"<label>déclencheur type<input name='trigger_type' type='text' value='"+htmlEscape(cfg_trigger_type)+"'></label>"
"<label>SSID WiFi<input name='ssid' type='text' value='"+htmlEscape(cfg_ssid)+"'></label>"
"<label>Mot de passe WiFi<input name='password' type='password' placeholder='(inchangé)'></label>"
"<label>GPIO Number<input name='gpio_number' type='text' value='"+String(cfg_gpio_number)+"'></label>"
"<label>SMTP host<input name='smtp' type='text' value='"+htmlEscape(cfg_smtp)+"'></label>"
"<label>SMTP port<input name='smtp_port' type='text' value='"+String(cfg_smtp_port)+"'></label>"
"<label>Adresse email (expediteur)<input name='email' type='text' value='"+htmlEscape(cfg_email)+"'></label>"
"<label>Mot de passe SMTP<input name='smtp_pwd' type='password' placeholder='(inchangé)'></label>"
"<label>Destinataires (séparés par ;) <textarea name='destinataires' rows='3'>"+htmlEscape(cfg_destinataires)+"</textarea></label>"
"<h3>Réseau (optionnel)</h3>"
"<label>Local IP<input name='local_ip' type='text' value='"+htmlEscape(cfg_local_ip)+"'></label>"
"<label>Gateway<input name='gateway' type='text' value='"+htmlEscape(cfg_gateway)+"'></label>"
"<label>Subnet<input name='subnet' type='text' value='"+htmlEscape(cfg_subnet)+"'></label>"
"<label>Primary DNS<input name='primaryDNS' type='text' value='"+htmlEscape(cfg_primaryDNS)+"'></label>"
"<label>Secondary DNS<input name='secondaryDNS' type='text' value='"+htmlEscape(cfg_secondaryDNS)+"'></label>"
"<h3>Accès admin</h3>"
"<label>Admin user<input name='admin_user' type='text' value='"+htmlEscape(admin_user)+"'></label>"
"<label>Admin pwd<input name='admin_pwd' type='password' placeholder='(inchangé)'></label>"
"<div class='row'><div class='col'><button class='btn' type='submit'>Sauvegarder</button></div>"
"<div class='col'><a class='btn' href='/testsend'>Tester envoi</a></div></div>"
"</form>"
"<hr><h3>Mise à jour OTA</h3>"
"<form method='POST' action='/update' enctype='multipart/form-data'>"
"<input type='file' name='firmware' accept='.bin' required>"
"<button class='btn' type='submit'>Mettre à jour</button>"
"</form>"
"<script>"
"function toggleGPIO(action){"
" fetch('/gpio?action='+action+'&json=1')"
" .then(response=>response.json())"
" .then(data=>{document.getElementById('gpio_state').innerText=data.state?'ON':'OFF';})"
" .catch(err=>console.error(err));"
"} "
"setInterval(()=>{"
" fetch('/gpio?json=1')"
" .then(r=>r.json())"
" .then(d=>document.getElementById('gpio_state').innerText=d.state?'ON':'OFF');"
"},1000);"
"</script>"
"</body></html>";
server.send(200,"text/html",page);
}
// --- Save ---
void handleSave(){
if(!checkAuth()) return;
if(server.hasArg("ssid")) cfg_ssid = server.arg("ssid");
if(server.hasArg("password") && server.arg("password") != "") cfg_password = server.arg("password");
if(server.hasArg("gpio_number")) {
cfg_gpio_number = server.arg("gpio_number").toInt();
pinMode(cfg_gpio_number, OUTPUT); // Reconfigurer la nouvelle GPIO
digitalWrite(cfg_gpio_number, HIGH); // GPIO ON par défaut après modification
}
if(server.hasArg("trigger_type")) cfg_trigger_type = server.arg("trigger_type");
if(server.hasArg("smtp")) cfg_smtp = server.arg("smtp");
if(server.hasArg("smtp_port")) cfg_smtp_port = server.arg("smtp_port").toInt();
if(server.hasArg("email")) cfg_email = server.arg("email");
if(server.hasArg("smtp_pwd") && server.arg("smtp_pwd") != "") cfg_smtp_pwd = server.arg("smtp_pwd");
if(server.hasArg("destinataires")) cfg_destinataires = server.arg("destinataires");
if(server.hasArg("local_ip")) cfg_local_ip = server.arg("local_ip");
if(server.hasArg("gateway")) cfg_gateway = server.arg("gateway");
if(server.hasArg("subnet")) cfg_subnet = server.arg("subnet");
if(server.hasArg("primaryDNS")) cfg_primaryDNS = server.arg("primaryDNS");
if(server.hasArg("secondaryDNS")) cfg_secondaryDNS = server.arg("secondaryDNS");
if(server.hasArg("admin_user")) admin_user = server.arg("admin_user");
if(server.hasArg("admin_pwd") && server.arg("admin_pwd") != "") admin_pwd = server.arg("admin_pwd");
saveConfig();
server.send(200,"text/html","<html><body><p>Config sauvegardée. Redémarrage...</p></body></html>");
delay(200);
ESP.restart();
}
// --- Test send ---
void handleTestSend(){
if(!checkAuth()) return;
ESP_Mail_Session session;
session.server.host_name=cfg_smtp.c_str();
session.server.port=cfg_smtp_port;
session.login.email=cfg_email.c_str();
session.login.password=cfg_smtp_pwd.c_str();
session.login.user_domain="";
SMTP_Message message;
message.sender.name="ECODEVICE";
message.sender.email=cfg_email.c_str();
addAllRecipients(message,cfg_destinataires);
message.subject="Test d'envoi depuis ESP32 (UI)";
message.text.content="Ceci est un test envoyé depuis /admin";
smtp.debug(1);
String pageStart="<html><body>";
String pageEnd="</body></html>";
if(!smtp.connect(&session)) server.send(200,"text/html",pageStart+"<p style='color:red'>Erreur connexion SMTP</p>"+pageEnd);
else if(!MailClient.sendMail(&smtp,&message)) server.send(200,"text/html",pageStart+"<p style='color:red'>Erreur envoi</p>"+pageEnd);
else server.send(200,"text/html",pageStart+"<p style='color:green'>Email envoyé</p>"+pageEnd);
smtp.closeSession();
}
// --- GPIO route ---
void handleGPIO(){
if(!checkAuth()) return;
if(server.hasArg("action")){
String action=server.arg("action");
if(action=="on") digitalWrite(cfg_gpio_number,HIGH);
else if(action=="off") digitalWrite(cfg_gpio_number,LOW);
}
int state=digitalRead(cfg_gpio_number);
if(server.hasArg("json") && server.arg("json")=="1"){
server.send(200,"application/json","{\"state\":"+String(state)+"}");
return;
}
server.send(200,"text/html","<html><body><h3>GPIO "+String(cfg_gpio_number)+" : "+(state?"ON":"OFF")+"</h3>"
"<a href='/gpio?action=on'>Ouvrir</a> | <a href='/gpio?action=off'>Fermer</a></body></html>");
}
// --- OTA upload handler ---
void handleFirmwareUpload() {
if (!checkAuth()) return;
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
Serial.setDebugOutput(true);
Serial.printf("Mise à jour OTA commencée : %s\n", upload.filename.c_str());
if (!Update.begin(UPDATE_SIZE_UNKNOWN)) Update.printError(Serial);
} else if (upload.status == UPLOAD_FILE_WRITE) {
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) Update.printError(Serial);
} else if (upload.status == UPLOAD_FILE_END) {
if (Update.end(true)) Serial.printf("Mise à jour OTA terminée : %u octets\n", upload.totalSize);
else Update.printError(Serial);
Serial.setDebugOutput(false);
} else if (upload.status == UPLOAD_FILE_ABORTED) {
Update.end();
Serial.println("Mise à jour OTA annulée.");
}
}
void handleUpdatePage() {
if (!checkAuth()) return;
if (Update.hasError()) {
server.send(200, "text/html",
"<html><body><p style='color:red'>Échec de la mise à jour</p></body></html>");
} else {
server.send(200, "text/html",
"<html><body><p style='color:green'>Mise à jour réussie, redémarrage...</p></body></html>");
delay(500);
ESP.restart();
}
}
// --- AP fallback ---
void startAPMode(){
Serial.println("Demarrage AP...");
WiFi.disconnect(true);
delay(200);
WiFi.mode(WIFI_AP_STA);
delay(50);
IPAddress apIP(192,168,4,1);
IPAddress apGateway = apIP;
IPAddress apMask(255,255,255,0);
if(!WiFi.softAPConfig(apIP, apGateway, apMask)) Serial.println("softAPConfig FAILED");
else Serial.println("softAPConfig OK");
delay(50);
if(strlen(AP_PASSWORD) == 0) WiFi.softAP(AP_SSID);
else WiFi.softAP(AP_SSID, AP_PASSWORD);
delay(300);
Serial.print("AP SSID: "); Serial.println(AP_SSID);
Serial.print("AP IP (softAPIP): "); Serial.println(WiFi.softAPIP());
Serial.print("Num stations connected: "); Serial.println(WiFi.softAPgetStationNum());
}
// --- WiFi connect ---
bool attemptWiFiConnect(){
Serial.printf("Connexion a %s\n",cfg_ssid.c_str());
WiFi.mode(WIFI_STA);
WiFi.begin(cfg_ssid.c_str(),cfg_password.c_str());
unsigned long start=millis();
while(WiFi.status()!=WL_CONNECTED && (millis()-start)<WIFI_CONNECT_TIMEOUT_MS){ Serial.print("."); delay(200);}
Serial.println();
if(WiFi.status()==WL_CONNECTED){ Serial.println("WiFi connecté"); Serial.print("IP: "); Serial.println(WiFi.localIP()); return true;}
Serial.println("WiFi non connecté -> fallback AP"); return false;
}
// --- Routes ---
void setupRoutes(){
server.on("/",handleRoot);
server.on("/admin",HTTP_GET,handleAdmin);
server.on("/save",HTTP_POST,handleSave);
server.on("/testsend",HTTP_GET,handleTestSend);
server.on("/gpio",handleGPIO);
server.on("/update", HTTP_POST, handleUpdatePage, handleFirmwareUpload);
server.begin();
Serial.println("Serveur web démarré");
}
// --- Setup ---
void setup(){
Serial.begin(115200);
delay(1000);
if(!LittleFS.begin(true)) Serial.println("LittleFS mount failed!");
else Serial.println("LittleFS OK");
loadConfig();
pinMode(cfg_gpio_number, OUTPUT);
digitalWrite(cfg_gpio_number,HIGH);
if(cfg_local_ip.length()>0){
IPAddress local=ipFromString(cfg_local_ip);
IPAddress gw=ipFromString(cfg_gateway);
IPAddress mask=ipFromString(cfg_subnet);
IPAddress pdns=ipFromString(cfg_primaryDNS);
IPAddress sdns=ipFromString(cfg_secondaryDNS);
if(!WiFi.config(local,gw,mask,pdns,sdns)) Serial.println("Echec config IP statique");
else Serial.println("Config IP statique appliquée");
}
bool wifiOk = attemptWiFiConnect();
if(!wifiOk) {
startAPMode();
setupRoutes();
} else {
setupRoutes();
configTzTime("CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00","fr.pool.ntp.org","time.nist.gov");
unsigned long t0 = millis();
const unsigned long NTPTIMEOUT = 5000;
while (millis() - t0 < NTPTIMEOUT && time(nullptr) < ESP_MAIL_CLIENT_VALID_TS) { delay(100);}
if (time(nullptr) >= ESP_MAIL_CLIENT_VALID_TS) Serial.println("NTP ok");
else Serial.println("NTP non synchro (timeout)");
}
}
void loop(){
server.handleClient();
}
avec cette version ton appli est fonctionnelle à 99.5 % il resterait potentiellement à rendre le texte du mail un peu plus dynamique
et Mike pourra te donner directement des BIN à verser par OTA ,plus besoin d'Arduino ;-)
Bonjour Lolo , de nouveau disponible cette apres-midi , je compile et j'injecte le code de la version 2.01 de suite et te tiens au courant des éventulles souçis ...
Alors tous fonctionne parfaitement , mais comment avoir des pushs differents suivant les differentes fonction de lecodevice, vu quil ny a qu'une seul case pour le declencheur type sur la page web admin? en fait je viens de faire l'essai en laissant la case declencheur type vide les push arrive quand meme. je ne vois pas trop a quoi sert cette cellule en fait
a part a declencher la fermeture de la vanne en reception dun puch contenant le meme texte??
je me réponds à moi même effectivement c'est bien cela, seul le push contenant le mot magique déclenche la fermerture de la vanne et l'envoi d'un mail, pas les autres, ejp ou autres qui se contentent d'envoyer un email sans action sur la vanne.
EN UN MOT PARFAIT
ouverture de la vanne en cas de perte du wifi par reset de lesp cest bien, mais et'il possible d'avoir un bouton physique sur la boite qui contiendra lesp pour le rebouter en apuiyant dessus, plus pratique peuetre? et bien sur la led du gpio 2 allumée quand la vanne est ouverte ?
Messages : 773
Sujets : 4
Inscription : Sep 2024
Pour le reset et pour le fun j aime bien utilisé la fonction capacitive des GPIOs il suffit de relier la GPIO sélectionné à une des vis du boîtier, quand tu touches la vis ça reset . Ca reste plus discret qu un bouton
Pour la led , je ferai comme pour la vanne , une GPIO selectionnable, si tu mets 2 ça sera la led interne si autre chose tu pourras mettre une led externe ce sera plus visible, mais bon l utilité, si tu veux savoir si ta vanne est ouverte ou fermée tu ouvres un robinet non ?
Surtout que l esp ne sait pas vraiment si la vanne est ouverte, il sait juste si lui commande l ouverture mais quid du relais, du fusible, de la vanne…
Messages : 163
Sujets : 13
Inscription : Jun 2024
(Hier, 08:02 PM)Lolo69 a écrit : Pour le reset et pour le fun j aime bien utilisé la fonction capacitive des GPIOs il suffit de relier la GPIO sélectionné à une des vis du boîtier, quand tu touches la vis ça reset . Ca reste plus discret qu un bouton
Pour la led , je ferai comme pour la vanne , une GPIO selectionnable, si tu mets 2 ça sera la led interne si autre chose tu pourras mettre une led externe ce sera plus visible, mais bon l utilité, si tu veux savoir si ta vanne est ouverte ou fermée tu ouvres un robinet non ?
Surtout que l esp ne sait pas vraiment si la vanne est ouverte, il sait juste si lui commande l ouverture mais quid du relais, du fusible, de la vanne…
Merci du retour oui tu as raison pour l'histoire de la led, la chaîne des éléments en série pourrait aller contre l'info de la led
Le bouton reset externe par contre un simple bouton poussoir entre un gpio et une vis du boîtier comment ça ?
Une vis qui touche à la carcasse de lesp ?
Messages : 773
Sujets : 4
Inscription : Sep 2024
Hier, 09:50 PM
(Modification du message : Hier, 09:59 PM par Lolo69.)
Pour être honnête je ne comprends pas le principe de la détection capacitive, mais c est le même principe que les boutons capacitif, genre les plaques qu on touche pour augmenter ou réduire la lumière sur certain variateur.
Dans le monde de l esp tu branches juste le fil qui va à la GPIO sur une pièce métallique ( feuille d Alu. Vis , radiateur , pièce de monnaie….) quand tu effleures cette pièce métallique ça fait varier le micro courant et pour du tout ou rien tu mets un seuil dans le programme.
Des explications ici https://www.upesy.fr/blogs/tutorials/how...ensor-pins
Messages : 163
Sujets : 13
Inscription : Jun 2024
(Hier, 09:50 PM)Lolo69 a écrit : Pour être honnête je ne comprends pas le principe de la détection capacitive, mais c est le même principe que les boutons capacitif, genre les plaques qu on touche pour augmenter ou réduire la lumière sur certain variateur.
Dans le monde de l esp tu branches juste le fil qui va à la GPIO sur une pièce métallique ( feuille d Alu. Vis , radiateur , pièce de monnaie….) quand tu effleures cette pièce métallique ça fait varier le micro courant et pour du tout ou rien tu mets un seuil dans le programme.
Des explications ici https://www.upesy.fr/blogs/tutorials/how...ensor-pins
Ah oui ça paraît très simple en fait on ne boucle pas un contact, mais on touche juste un fil qui créer une sorte d'impulsion électrique, oui il y a plusieurs gpio disponible pour cette fonction, par contre il y aurait une petit bout de code à rajouter au soft apparemment ?
Messages : 773
Sujets : 4
Inscription : Sep 2024
Bah oui un bout de code pour dire si tu touches la vis = reset de l esp dans le cas qui t intéresse ou moins violent juste rouvrir la vanne
On peut aussi faire des resets password si on touche une autre vis…
Les possibilités sont infinies .
Messages : 163
Sujets : 13
Inscription : Jun 2024
Hier, 10:56 PM
(Modification du message : Aujourd’hui, 12:13 AM par tupolev89.)
(Hier, 10:15 PM)Lolo69 a écrit : Bah oui un bout de code pour dire si tu touches la vis = reset de l esp dans le cas qui t intéresse ou moins violent juste rouvrir la vanne
On peut aussi faire des resets password si on touche une autre vis…
Les possibilités sont infinies .
Je voulais juste une possibilité de rèouvrir la vanne si je ne suis pas sur place par exemple.
Imaginons que la vanne coupe pour une fuite, la possibilité de rèouvrir l'eau après la réparation éventuel et possibilité de remettre l'eau sans portable ou connexion internet, lesp se trouvera pas loing de l'arrivée eau de la maison.
Après je ferais manœuvrer la vanne une fois semaine ou plus pour vérifier son fonctionnement.
Apparement en utilisant le gpio En, cela devrait fonctionner pour un reboot
https://www.upesy.fr/blogs/tutorials/esp...EKNvSt5XYs
D'après ce que j'ai compris un contact entre gpio EN et ground permettrait un reboot ?
Messages : 773
Sujets : 4
Inscription : Sep 2024
Il y a 7 heures
(Modification du message : Il y a 5 heures par Lolo69.)
pour le reset , bouton poussoir entre GND et EN c est effectivement OK pas besoin de programmer
Code mis à jour pour piloter en // une deuxieme GPIO, la 2 pour la Led et la 23 par exemple pour la vanne motorisée
Possibilité d'inverser la logique des 2 sorties pour avoir par exemple GPIO 2 led = eclairée quand GPIO23 à 0 ( qui voudra dire relais sous tension car pour rappel la logique de pilotage des relais est inversée 0= Relais activé)
+ quelques modif cosmetique dans la pge Admin pour faire des regroupements plus logique et uniformiser les polices
Code : //Version 2.03 23/11/25
/*
Parametrages depuis page /admin
envoi à une liste de destinataires séparés par des ;
GPIO configurable depuis admin, pilotable en temps réel
Trigger configurable pour mise à 0 de GPIO
Ajout OTA via /admin
Ajout 2ème GPIO pour recopie état de la vanne
rearrangement cosmetiques des sections de la page /admin
Rearrangement cosmetiques dans le code ( alignements commentaires indentation)
Ajout inversion logique indépendante par GPIO
*/
#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>
#include <LittleFS.h>
#include <WiFiClientSecure.h>
#include <ESP_Mail_Client.h>
#include <Update.h>
#define WIFI_CONNECT_TIMEOUT_MS 10000
#define AP_SSID "ESP32_Config"
// Eviter NULL pour ne pas appeler strlen(NULL)
#define AP_PASSWORD ""
// si tu veux un AP sécurisé par défaut, mets une chaîne >=8 caractères
// #define AP_PASSWORD "esp32admin"
const char* CONFIG_FILE = "/config.txt";
// Valeurs par défaut
String cfg_ssid = "ton SSID";
String cfg_password = "ton mot de passe wifi";
String cfg_smtp = "smtp.orange.fr";
String cfg_email = "tonemail@orange.fr";
String cfg_smtp_pwd = "ton mot de passe Mail";
String cfg_destinataires = "dest1@free.fr;dest2@orange.fr";
int cfg_smtp_port = 465;
int cfg_gpio_number = 2;
// ⭐ AJOUT — GPIO clone
int cfg_gpio_number2 = 23;
String cfg_trigger_type = "inondation";
// ⭐ AJOUT — inversion logique indépendante GPIO
bool cfg_invert_gpio1 = false; // GPIO principale
bool cfg_invert_gpio2 = false; // GPIO clone
// Réseau statique
String cfg_local_ip = "192.168.1.119";
String cfg_gateway = "192.168.1.1";
String cfg_subnet = "255.255.255.0";
String cfg_primaryDNS = "192.168.1.1";
String cfg_secondaryDNS = "8.8.4.4";
// Admin auth
String admin_user = "admin";
String admin_pwd = "admin";
// Objets globaux
WebServer server(80);
SMTPSession smtp;
// --- Helpers IP ---
IPAddress ipFromString(const String &s) {
IPAddress ip(0,0,0,0);
int parts[4] = {0,0,0,0}, idx = 0;
String tmp = s;
tmp.trim();
int start = 0;
for (int i=0; i<4; ++i) {
int pos = tmp.indexOf('.', start);
String part = (pos==-1) ? tmp.substring(start) : tmp.substring(start,pos);
part.trim();
parts[idx++] = part.toInt();
if (pos==-1) break;
start = pos+1;
}
ip = IPAddress(parts[0], parts[1], parts[2], parts[3]);
return ip;
}
// --- LittleFS ---
void saveConfig() {
File f = LittleFS.open(CONFIG_FILE, "w");
if (!f) { Serial.println("Erreur ouverture fichier config"); return; }
f.printf("ssid=%s\n", cfg_ssid.c_str());
f.printf("password=%s\n", cfg_password.c_str());
f.printf("smtp=%s\n", cfg_smtp.c_str());
f.printf("email=%s\n", cfg_email.c_str());
f.printf("smtp_pwd=%s\n", cfg_smtp_pwd.c_str());
f.printf("destinataires=%s\n", cfg_destinataires.c_str());
f.printf("smtp_port=%d\n", cfg_smtp_port);
f.printf("gpio_number=%d\n", cfg_gpio_number);
// ⭐ AJOUT — Sauvegarde GPIO2
f.printf("gpio_number2=%d\n", cfg_gpio_number2);
// ⭐ AJOUT — Sauvegarde inversion logique GPIO
f.printf("invert_gpio1=%d\n", cfg_invert_gpio1 ? 1 : 0);
f.printf("invert_gpio2=%d\n", cfg_invert_gpio2 ? 1 : 0);
f.printf("trigger_type=%s\n", cfg_trigger_type.c_str());
f.printf("local_ip=%s\n", cfg_local_ip.c_str());
f.printf("gateway=%s\n", cfg_gateway.c_str());
f.printf("subnet=%s\n", cfg_subnet.c_str());
f.printf("primaryDNS=%s\n", cfg_primaryDNS.c_str());
f.printf("secondaryDNS=%s\n", cfg_secondaryDNS.c_str());
f.printf("admin_user=%s\n", admin_user.c_str());
f.printf("admin_pwd=%s\n", admin_pwd.c_str());
f.close();
Serial.println("Config sauvegardée dans LittleFS");
}
void loadConfig() {
if (!LittleFS.exists(CONFIG_FILE)) {
Serial.println("Fichier config absent -> valeurs par défaut");
return;
}
File f = LittleFS.open(CONFIG_FILE,"r");
if (!f) {
Serial.println("Erreur ouverture fichier config");
return;
}
while(f.available()) {
String line = f.readStringUntil('\n');
line.trim();
if (line.length() == 0) continue;
int eq = line.indexOf('=');
if (eq == -1) continue;
String key = line.substring(0, eq), val = line.substring(eq+1);
key.trim();
val.trim();
if(key=="ssid") cfg_ssid=val;
else if(key=="password") cfg_password=val;
else if(key=="smtp") cfg_smtp=val;
else if(key=="email") cfg_email=val;
else if(key=="smtp_pwd") cfg_smtp_pwd=val;
else if(key=="destinataires") cfg_destinataires=val;
else if(key=="smtp_port") cfg_smtp_port=val.toInt();
else if(key=="gpio_number") cfg_gpio_number=val.toInt();
// ⭐ AJOUT — chargement GPIO2
else if(key=="gpio_number2") cfg_gpio_number2=val.toInt();
// ⭐ AJOUT — chargement inversion logique
else if(key=="invert_gpio1") cfg_invert_gpio1=(val.toInt()!=0);
else if(key=="invert_gpio2") cfg_invert_gpio2=(val.toInt()!=0);
else if(key=="trigger_type") cfg_trigger_type=val;
else if(key=="local_ip") cfg_local_ip = val;
else if(key=="gateway") cfg_gateway = val;
else if(key=="subnet") cfg_subnet = val;
else if(key=="primaryDNS") cfg_primaryDNS = val;
else if(key=="secondaryDNS") cfg_secondaryDNS = val;
else if(key=="admin_user") admin_user = val;
else if(key=="admin_pwd") admin_pwd = val;
}
f.close();
Serial.println("Config chargée depuis LittleFS");
}
// ⭐ AJOUT — helper GPIO avec inversion
void setGPIO(int gpio, bool on, bool invert) {
digitalWrite(gpio, invert ? (on ? LOW : HIGH) : (on ? HIGH : LOW));
}
// --- SMTP ---
void addAllRecipients(SMTP_Message &message, const String &liste) {
String s=liste;
int start=0, pos=0;
while((pos=s.indexOf(';',start))!=-1) {
String email=s.substring(start,pos);
email.trim();
if(email.length()>0) message.addRecipient("Destinataire",email.c_str());
start=pos+1;
}
String email=s.substring(start);
email.trim();
if(email.length()>0) message.addRecipient("Destinataire",email.c_str());
}
// --- Page root ---
void handleRoot() {
time_t now=time(nullptr);
struct tm timeinfo;
localtime_r(&now,&timeinfo);
char buffer[20];
strftime(buffer,sizeof(buffer),"%d/%m/%Y %H:%M:%S",&timeinfo);
String type="Non défini";
if(server.hasArg("type")) type=server.arg("type");
// Vérifie le trigger pour GPIO
if(type.equalsIgnoreCase(cfg_trigger_type)){
setGPIO(cfg_gpio_number, false, cfg_invert_gpio1); // GPIO principale à 0 selon logique
setGPIO(cfg_gpio_number2, false, cfg_invert_gpio2); // GPIO clone
Serial.println("Trigger activé : GPIOs mis à 0 selon logique");
}
ESP_Mail_Session session;
session.server.host_name=cfg_smtp.c_str();
session.server.port=cfg_smtp_port;
session.login.email=cfg_email.c_str();
session.login.password=cfg_smtp_pwd.c_str();
session.login.user_domain="";
SMTP_Message message;
message.sender.name="ECODEVICE";
message.sender.email=cfg_email.c_str();
addAllRecipients(message,cfg_destinataires);
message.subject="Alerte de type : "+type;
message.text.content="Bonjour ! Email envoyé depuis ESP32 le "+String(buffer);
smtp.debug(1);
String prefixPage="<html><head><meta charset='UTF-8'><meta http-equiv='Cache-Control' content='no-cache, no-store, must-revalidate'><meta name='viewport' content='width=device-width, initial-scale=1'>"
"<style>body{font-family:Arial;padding:16px;} .ok{color:green;} .ko{color:red;}</style></head><body>";
String suffixPage="</body></html>";
String page=prefixPage+suffixPage;
if(!smtp.connect(&session)){
Serial.println("Erreur de connexion SMTP !");
server.send(200,"text/html",prefixPage+"<p class='ko'>Erreur connexion SMTP</p>"+suffixPage);
return;
}
if(!MailClient.sendMail(&smtp,&message)){
page=prefixPage+"<p class='ko'>Erreur d'envoi !</p>"+suffixPage;
Serial.println("Erreur d'envoi !");
} else {
page=prefixPage+"<p class='ok'>Email envoyé le "+String(buffer)+"</p>"+suffixPage;
Serial.println("Email envoyé !");
}
smtp.closeSession();
server.send(200,"text/html",page);
}
// --- Auth & escape ---
bool checkAuth() {
if(!server.authenticate(admin_user.c_str(),admin_pwd.c_str())) {
server.requestAuthentication();
return false;
}
return true;
}
String htmlEscape(const String &s){
String r=s;
r.replace("&","&");
r.replace("<","<");
r.replace(">",">");
r.replace("\"",""");
r.replace("'","'");
return r;
}
// --- Page /admin ---
void handleAdmin() {
if(!checkAuth()) return;
int gpio_state = digitalRead(cfg_gpio_number);
int gpio_state2 = digitalRead(cfg_gpio_number2);
String page = "<!DOCTYPE html><html><head><meta charset='UTF-8'><meta http-equiv='Cache-Control' content='no-cache, no-store, must-revalidate'><meta name='viewport' content='width=device-width, initial-scale=1'>"
"<style>body{font-family:Arial;padding:12px;} label{display:block;margin-top:8px;} input[type=text], input[type=password], textarea{width:100%;padding:8px;border-radius:6px;border:1px solid #ccc;box-sizing:border-box;} .row{display:flex;gap:8px;} .col{flex:1;} .btn{display:inline-block;padding:8px 12px;margin-top:10px;border-radius:6px;background:#2196F3;color:#fff;text-decoration:none;}</style>"
"<title>Ecodevice Gateway</title></head><body>"
"<h2>Paramètres Ecodevice Gateway</h2>"
"<h3>GPIO (principale) " + String(cfg_gpio_number) + " : <span id='gpio_state'>" + (gpio_state?"ON":"OFF") + "</span></h3>"
"<button onclick=\"toggleGPIO('on')\">Ouvrir</button> "
"<button onclick=\"toggleGPIO('off')\">Fermer</button><hr>"
"<h3>GPIO2 (secondaire) " + String(cfg_gpio_number2) + " : <span id='gpio_state2'>" + (gpio_state2?"ON":"OFF") + "</span></h3>"
"<p>(Cette sortie suit automatiquement la GPIO principale)</p><hr>"
"<form method='POST' action='/save'>"
"<h3>Paramètres globaux</h3>"
"<label>déclencheur type<input name='trigger_type' type='text' value='"+htmlEscape(cfg_trigger_type)+"'></label>"
"<label>Numéro GPIO principale<input name='gpio_number' type='text' value='"+String(cfg_gpio_number)+"'></label>"
"<label>Numéro GPIO secondaire<input name='gpio_number2' type='text' value='"+String(cfg_gpio_number2)+"'></label>"
"<label>Inverser logique GPIO principale<input type='checkbox' name='invert_gpio1' "+(cfg_invert_gpio1?"checked":"")+"></label>"
"<label>Inverser logique GPIO secondaire<input type='checkbox' name='invert_gpio2' "+(cfg_invert_gpio2?"checked":"")+"></label>"
"<h3>Paramètres Wifi SSID</h3>"
"<label>SSID WiFi<input name='ssid' type='text' value='"+htmlEscape(cfg_ssid)+"'></label>"
"<label>Mot de passe WiFi<input name='password' type='password' placeholder='(inchangé)'></label>"
"<h3>Paramètres Réseau</h3>"
"<label>Local IP<input name='local_ip' type='text' value='"+htmlEscape(cfg_local_ip)+"'></label>"
"<label>Gateway<input name='gateway' type='text' value='"+htmlEscape(cfg_gateway)+"'></label>"
"<label>Subnet<input name='subnet' type='text' value='"+htmlEscape(cfg_subnet)+"'></label>"
"<label>Primary DNS<input name='primaryDNS' type='text' value='"+htmlEscape(cfg_primaryDNS)+"'></label>"
"<label>Secondary DNS<input name='secondaryDNS' type='text' value='"+htmlEscape(cfg_secondaryDNS)+"'></label>"
"<h3>Paramètres SMTP</h3>"
"<label>SMTP host<input name='smtp' type='text' value='"+htmlEscape(cfg_smtp)+"'></label>"
"<label>SMTP port<input name='smtp_port' type='text' value='"+String(cfg_smtp_port)+"'></label>"
"<label>Adresse email (expediteur)<input name='email' type='text' value='"+htmlEscape(cfg_email)+"'></label>"
"<label>Mot de passe SMTP<input name='smtp_pwd' type='password' placeholder='(inchangé)'></label>"
"<label>Destinataires (séparés par ;) <textarea name='destinataires' rows='3'>"+htmlEscape(cfg_destinataires)+"</textarea></label>"
"<h3>Paramètres accès admin</h3>"
"<label>Admin user<input name='admin_user' type='text' value='"+htmlEscape(admin_user)+"'></label>"
"<label>Admin pwd<input name='admin_pwd' type='password' placeholder='(inchangé)'></label>"
"<div class='row'>"
" <div class='col'><button class='btn' type='submit'>Sauvegarder</button></div>"
" <div class='col'><button class='btn' type='button' onclick=\"window.location.href='/testsend'\">Tester envoi</button></div>"
"</div>"
"</form>"
"<hr><h3>Mise à jour OTA</h3>"
"<form method='POST' action='/update' enctype='multipart/form-data'>"
"<input type='file' name='firmware' accept='.bin' required>"
"<button class='btn' type='submit'>Mettre à jour</button>"
"</form>"
"<script>"
"function toggleGPIO(action){"
" fetch('/gpio?action='+action+'&json=1')"
" .then(response=>response.json())"
" .then(data=>{"
" document.getElementById('gpio_state').innerText=data.state?'ON':'OFF';"
" document.getElementById('gpio_state2').innerText=data.state?'ON':'OFF';"
" })"
" .catch(err=>console.error(err));"
"} "
"setInterval(()=>{"
" fetch('/gpio?json=1')"
" .then(r=>r.json())"
" .then(d=>{"
" document.getElementById('gpio_state').innerText=d.state?'ON':'OFF';"
" document.getElementById('gpio_state2').innerText=d.state?'ON':'OFF';"
" });"
"},1000);"
"</script>"
"</body></html>";
server.send(200,"text/html",page);
}
// --- Save ---
void handleSave() {
if(!checkAuth()) return;
if(server.hasArg("ssid")) cfg_ssid = server.arg("ssid");
if(server.hasArg("password") && server.arg("password") != "") cfg_password = server.arg("password");
if(server.hasArg("gpio_number")) {
cfg_gpio_number = server.arg("gpio_number").toInt();
pinMode(cfg_gpio_number, OUTPUT);
setGPIO(cfg_gpio_number, true, cfg_invert_gpio1);
}
if(server.hasArg("gpio_number2")) {
cfg_gpio_number2 = server.arg("gpio_number2").toInt();
pinMode(cfg_gpio_number2, OUTPUT);
setGPIO(cfg_gpio_number2, true, cfg_invert_gpio2);
}
if(server.hasArg("trigger_type")) cfg_trigger_type = server.arg("trigger_type");
if(server.hasArg("smtp")) cfg_smtp = server.arg("smtp");
if(server.hasArg("smtp_port")) cfg_smtp_port = server.arg("smtp_port").toInt();
if(server.hasArg("email")) cfg_email = server.arg("email");
if(server.hasArg("smtp_pwd") && server.arg("smtp_pwd") != "") cfg_smtp_pwd = server.arg("smtp_pwd");
if(server.hasArg("destinataires")) cfg_destinataires = server.arg("destinataires");
if(server.hasArg("local_ip")) cfg_local_ip = server.arg("local_ip");
if(server.hasArg("gateway")) cfg_gateway = server.arg("gateway");
if(server.hasArg("subnet")) cfg_subnet = server.arg("subnet");
if(server.hasArg("primaryDNS")) cfg_primaryDNS = server.arg("primaryDNS");
if(server.hasArg("secondaryDNS")) cfg_secondaryDNS = server.arg("secondaryDNS");
if(server.hasArg("admin_user")) admin_user = server.arg("admin_user");
if(server.hasArg("admin_pwd") && server.arg("admin_pwd") != "") admin_pwd = server.arg("admin_pwd");
// ⭐ AJOUT — lecture inversion logique
cfg_invert_gpio1 = server.hasArg("invert_gpio1");
cfg_invert_gpio2 = server.hasArg("invert_gpio2");
saveConfig();
server.send(200,"text/html","<html><body><p>Config sauvegardée. Redémarrage...</p></body></html>");
delay(200);
ESP.restart();
}
// --- Test send ---
void handleTestSend() {
if(!checkAuth()) return;
ESP_Mail_Session session;
session.server.host_name = cfg_smtp.c_str();
session.server.port = cfg_smtp_port;
session.login.email = cfg_email.c_str();
session.login.password = cfg_smtp_pwd.c_str();
session.login.user_domain = "";
SMTP_Message message;
message.sender.name = "ECODEVICE";
message.sender.email = cfg_email.c_str();
addAllRecipients(message, cfg_destinataires);
message.subject = "Test d'envoi depuis ESP32 (UI)";
message.text.content = "Ceci est un test envoyé depuis /admin";
smtp.debug(1);
String pageStart = "<html><body>";
String pageEnd = "</body></html>";
if(!smtp.connect(&session)) server.send(200,"text/html",pageStart+"<p style='color:red'>Erreur connexion SMTP</p>"+pageEnd);
else if(!MailClient.sendMail(&smtp,&message)) server.send(200,"text/html",pageStart+"<p style='color:red'>Erreur envoi</p>"+pageEnd);
else server.send(200,"text/html",pageStart+"<p style='color:green'>Email envoyé</p>"+pageEnd);
smtp.closeSession();
}
// --- GPIO route ---
void handleGPIO() {
if(!checkAuth()) return;
if(server.hasArg("action")) {
String action = server.arg("action");
if(action == "on") {
setGPIO(cfg_gpio_number, true, cfg_invert_gpio1);
setGPIO(cfg_gpio_number2, true, cfg_invert_gpio2);
} else if(action == "off") {
setGPIO(cfg_gpio_number, false, cfg_invert_gpio1);
setGPIO(cfg_gpio_number2, false, cfg_invert_gpio2);
}
}
int state = digitalRead(cfg_gpio_number);
if(server.hasArg("json") && server.arg("json")=="1") {
server.send(200,"application/json","{\"state\":"+String(state)+"}");
return;
}
server.send(200,"text/html","<html><body><h3>GPIO "+String(cfg_gpio_number)+" : "+(state?"ON":"OFF")+"</h3>"
"<a href='/gpio?action=on'>Ouvrir</a> | <a href='/gpio?action=off'>Fermer</a></body></html>");
}
// --- OTA upload handler ---
void handleFirmwareUpload() {
if (!checkAuth()) return;
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
Serial.setDebugOutput(true);
Serial.printf("Mise à jour OTA commencée : %s\n", upload.filename.c_str());
if (!Update.begin(UPDATE_SIZE_UNKNOWN)) Update.printError(Serial);
} else if (upload.status == UPLOAD_FILE_WRITE) {
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) Update.printError(Serial);
} else if (upload.status == UPLOAD_FILE_END) {
if (Update.end(true)) Serial.printf("Mise à jour OTA terminée : %u octets\n", upload.totalSize);
else Update.printError(Serial);
Serial.setDebugOutput(false);
} else if (upload.status == UPLOAD_FILE_ABORTED) {
Update.end();
Serial.println("Mise à jour OTA annulée.");
}
}
void handleUpdatePage() {
if (!checkAuth()) return;
if (Update.hasError()) {
server.send(200, "text/html",
"<html><body><p style='color:red'>Échec de la mise à jour</p></body></html>");
} else {
server.send(200, "text/html",
"<html><body><p style='color:green'>Mise à jour réussie, redémarrage...</p></body></html>");
delay(500);
ESP.restart();
}
}
// --- AP fallback ---
void startAPMode() {
Serial.println("Demarrage AP...");
WiFi.disconnect(true);
delay(200);
WiFi.mode(WIFI_AP_STA);
delay(50);
IPAddress apIP(192,168,4,1);
IPAddress apGateway = apIP;
IPAddress apMask(255,255,255,0);
if(!WiFi.softAPConfig(apIP, apGateway, apMask)) Serial.println("softAPConfig FAILED");
else Serial.println("softAPConfig OK");
delay(50);
if(strlen(AP_PASSWORD) == 0) WiFi.softAP(AP_SSID);
else WiFi.softAP(AP_SSID, AP_PASSWORD);
delay(300);
Serial.print("AP SSID: "); Serial.println(AP_SSID);
Serial.print("AP IP (softAPIP): "); Serial.println(WiFi.softAPIP());
Serial.print("Num stations connected: "); Serial.println(WiFi.softAPgetStationNum());
}
// --- WiFi connect ---
bool attemptWiFiConnect() {
Serial.printf("Connexion a %s\n", cfg_ssid.c_str());
WiFi.mode(WIFI_STA);
WiFi.begin(cfg_ssid.c_str(), cfg_password.c_str());
unsigned long start = millis();
while(WiFi.status() != WL_CONNECTED && (millis()-start) < WIFI_CONNECT_TIMEOUT_MS) {
Serial.print(".");
delay(200);
}
Serial.println();
if(WiFi.status() == WL_CONNECTED) {
Serial.println("WiFi connecté");
Serial.print("IP: "); Serial.println(WiFi.localIP());
return true;
}
Serial.println("WiFi non connecté -> fallback AP");
return false;
}
// --- Routes ---
void setupRoutes() {
server.on("/", handleRoot);
server.on("/admin", HTTP_GET, handleAdmin);
server.on("/save", HTTP_POST, handleSave);
server.on("/testsend", HTTP_GET, handleTestSend);
server.on("/gpio", handleGPIO);
server.on("/update", HTTP_POST, handleUpdatePage, handleFirmwareUpload);
server.begin();
Serial.println("Serveur web démarré");
}
// --- Setup ---
void setup() {
Serial.begin(115200);
delay(1000);
if(!LittleFS.begin(true)) Serial.println("LittleFS mount failed!");
else Serial.println("LittleFS OK");
loadConfig();
// Configuration des deux GPIO (GPIO2 est un clone)
pinMode(cfg_gpio_number, OUTPUT);
setGPIO(cfg_gpio_number, true, cfg_invert_gpio1);
pinMode(cfg_gpio_number2, OUTPUT);
setGPIO(cfg_gpio_number2, true, cfg_invert_gpio2);
if(cfg_local_ip.length() > 0){
IPAddress local = ipFromString(cfg_local_ip);
IPAddress gw = ipFromString(cfg_gateway);
IPAddress mask = ipFromString(cfg_subnet);
IPAddress pdns = ipFromString(cfg_primaryDNS);
IPAddress sdns = ipFromString(cfg_secondaryDNS);
if(!WiFi.config(local, gw, mask, pdns, sdns)) Serial.println("Echec config IP statique");
else Serial.println("Config IP statique appliquée");
}
bool wifiOk = attemptWiFiConnect();
if(!wifiOk) {
startAPMode();
setupRoutes();
} else {
setupRoutes();
configTzTime("CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00","fr.pool.ntp.org","time.nist.gov");
unsigned long t0 = millis();
const unsigned long NTPTIMEOUT = 5000;
while (millis() - t0 < NTPTIMEOUT && time(nullptr) < ESP_MAIL_CLIENT_VALID_TS) { delay(100);}
if (time(nullptr) >= ESP_MAIL_CLIENT_VALID_TS) Serial.println("NTP ok");
else Serial.println("NTP non synchro (timeout)");
}
}
// --- Loop ---
void loop() {
server.handleClient();
}
Messages : 163
Sujets : 13
Inscription : Jun 2024
Il y a 6 heures
(Modification du message : Il y a 6 heures par tupolev89.)
(Il y a 7 heures)Lolo69 a écrit : pour le reset , bouton poussoir entre GND et EN c est effectivement OK.
Pour la deuxieme GPIO pour la Led , franchement est ce utile ? j ai quand meme rajouté . Veux tu le code source ou directement le fichier BIN à charger par OTA ?
Bonjour Lolo, merci beaucoup pour ta réponse, pour la Led oui ce n'est pas utile vu que lesp sera à proximité d'un robinet donc l'information sera directement transmise par l'arrivée de l'eau en fait.
Messages : 773
Sujets : 4
Inscription : Sep 2024
Il y a 5 heures
(Modification du message : Il y a 5 heures par Lolo69.)
(Il y a 6 heures)tupolev89 a écrit : (Il y a 7 heures)Lolo69 a écrit : pour le reset , bouton poussoir entre GND et EN c est effectivement OK.
Pour la deuxieme GPIO pour la Led , franchement est ce utile ? j ai quand meme rajouté . Veux tu le code source ou directement le fichier BIN à charger par OTA ?
Bonjour Lolo, merci beaucoup pour ta réponse, pour la Led oui ce n'est pas utile vu que lesp sera à proximité d'un robinet donc l'information sera directement transmise par l'arrivée de l'eau en fait.
arf on a posté en meme temps , regarde la mise à jour de mon post précédent
|