Messages : 527
Sujets : 5
Inscription : Jun 2024
21-11-2025, 01:10 AM
(Modification du message : 21-11-2025, 01:32 AM par Mike.)
Content que cela fonctionne bien.
Pour les espaces au lieu des tirets du bas tu peux essayer de les remplacer par %20 :
/?type=FUITE%20D%20EAU
Pour ajouter des personnes en cc, il te suffit de rajouter un appel à la fonction addCc après addRecipient :
Code : message.addRecipient("Destinataire", "tondestinataire@domain.fr"); // A mettre à jour
message.addCc("tondestinataire2@domain.fr"); // A mettre à jour ou supprimer si inutile
message.addCc("tondestinataire3@domain.fr"); // A mettre à jour ou supprimer si inutile
//Etc.
Pour la vanne, je vois que Shelly en a une : https://www.shelly.com/fr/products/frank...ve-fk-v02t
Je trouve ça un peu cher mais je te laisse regarder si ça te conviendrait.
On dirait qu'avec la vanne Shelly, l'ecodevice, lolo et moi seront au chômage : https://www.shelly.com/cdn/shop/articles...-case2.jpg
Messages : 163
Sujets : 13
Inscription : Jun 2024
21-11-2025, 01:49 AM
(Modification du message : 21-11-2025, 01:52 AM par tupolev89.)
(21-11-2025, 01:10 AM)Mike a écrit : Content que cela fonctionne bien.
Pour les espaces au lieu des tirets du bas tu peux essayer de les remplacer par %20 :
/?type=FUITE%20D%20EAU
Pour ajouter des personnes en cc, il te suffit de rajouter un appel à la fonction addCc après addRecipient :
Code : message.addRecipient("Destinataire", "tondestinataire@domain.fr"); // A mettre à jour
message.addCc("tondestinataire2@domain.fr"); // A mettre à jour ou supprimer si inutile
message.addCc("tondestinataire3@domain.fr"); // A mettre à jour ou supprimer si inutile
//Etc.
Pour la vanne, je vois que Shelly en a une : https://www.shelly.com/fr/products/frank...ve-fk-v02t
Je trouve ça un peu cher mais je te laisse regarder si ça te conviendrait.
On dirait qu'avec la vanne Shelly, l'ecodevice, lolo et moi seront au chômage : https://www.shelly.com/cdn/shop/articles...-case2.jpg le %20 fonctionne bien parfait
pour la vanne effectivement c’est pas donné, une vanne motorisé piloté depuis un relais sur esp est surement moins couteuse.
j'ai vu des vannes normalement fermée qui s’ouvre des quelles sont alimenté et qui se referme seul , c’est peut être bien , je ne sais pas trop
je mets un lien pour donner une idée
https://fr.aliexpress.com/item/100500556...in_prod%3A
https://fr.aliexpress.com/item/100500633...in_prod%3A
Messages : 774
Sujets : 4
Inscription : Sep 2024
21-11-2025, 07:48 AM
(Modification du message : 21-11-2025, 08:37 AM par Lolo69.)
Pour le relais c est exactement ce que j ai pour démarrer ma pompe filtration piscine ( enfin le contacteur de la pompe évidemment)
Pour la vanne c est ce type de vanne . Après c est du made in Chine c est toi et ta chance en terme de qualité. Au pire elle s ouvre pas tu le verras vite, au pire elle ne se ferme pas tu es dans la situation actuelle
Idéalement il faudra prendre le modèle 3 fils pour éviter d alimenter en permanence pour avoir de l eau car le jour où tu as une panne d elec, tu auras en plus une panne d eau ..pas cool
Arf j avais pas vu que la tête était amovible au pire si problème ou plus d elec tu peux toujours ouvrir fermer à la main.
Donc il faut décortiquer le descriptif de cr02 03 04 qui en première lecture ne sont pas hyper clairs
Le cr04 le plus sécuritaire se ferme même si plus d elec . ( tu gardes la possibilité d ouvrir en démontant la tête)
Le cr03 bon compromis si la commande merdouille la vanne se ferme s il y a encore de l elec mais ça oblige d avoir le relais à on en permanence ( est ce qu il tiendra le coup longtemps?)
Le cr02 offre la meilleure disponibilité mais l eau peut rester ouverte en cas de disfonctionnement esp/relais/ elec
Perso pour piloter un arrosage je prendrai le cr04 , pour ton application je prendrai le cr02 car de toute façon tu recevras l alerte par mail donc en théorie tu vas surveiller.
Dans le fonctionnement il faudra prévoir une réouverture depuis interface web ou au reset de l esp.
Car si fuite on détecte on ferme donc plus de débit il ne faut pas réouvrir automatiquement car ca se mordrait la queue
Si dans ton projet option 3 tu pars sur vanne et relais c est la aussi hyper simple à coder et très facile à câbler aussi
Ps : fonctionnement version 1.05 stable après 14 h de fonctionnement dont 10 sans activité.
Messages : 163
Sujets : 13
Inscription : Jun 2024
(21-11-2025, 07:48 AM)Lolo69 a écrit : Pour le relais c est exactement ce que j ai pour démarrer ma pompe filtration piscine ( enfin le contacteur de la pompe évidemment)
Pour la vanne c est ce type de vanne . Après c est du made in Chine c est toi et ta chance en terme de qualité. Au pire elle s ouvre pas tu le verras vite, au pire elle ne se ferme pas tu es dans la situation actuelle
Idéalement il faudra prendre le modèle 3 fils pour éviter d alimenter en permanence pour avoir de l eau car le jour où tu as une panne d elec, tu auras en plus une panne d eau ..pas cool
Pour moi le modèle CR03 est le bon compromis sécurité / disponibilité
Le CR04 on ne sait pas trop si le power stock ouvre ou ferme la vanne ça serait le plus sécuritaire avec une bonne disponibilité
En tout cas pas le cr02
Arf j avais pas que la tête était amovible au pire si problème ou plus d elec tu peux toujours ouvrir fermer à la main.
Donc modèle CR04 c est parfait Bonjour lolo, oui effectivement la tête est amovible, et d'après ce que j'ai compris elle serait avec le modèle cr04 en mode normalement fermé, c'est à dire qu'il n'y que l'ouverture qui est commandée en faite non ?
Il y a aussi des modèles en laiton au lieu de l'inox, à voir.
Pour ce qui est du contacteur j'en ai en stock (car j'en ai monté un dans mon installation routeur f1atb, pour piloter l'arrêt du ventilateur du triac lorsque le ballon est complètement chaud, et donc le triac s'ouvre à 100% à ce moment le relais coupe le ventilo pour éviter qu'il tourne à fond pour rien). Donc je pense que déjà que l'on pourrait voir pour piloter le relais suivant le push fuite d'eau.
En tout cas plus de problème pour l'instant de déconnexion ou autre, ce matin 7h pile j'ai bien reçu l'alerte mail EJP. Donc le push fonctionne bien.
Bon café du matin, tupolev.
Messages : 774
Sujets : 4
Inscription : Sep 2024
21-11-2025, 08:45 AM
(Modification du message : 21-11-2025, 09:15 AM par Lolo69.)
Le relais est capable de piloter la vanne en direct pas besoin de contacteur intermédiaire
Bon je met le sujet en Stanby ça été très agréable d échanger avec vous.
Réveilles moi quand tu passeras à la phase 3 . On fera la version 2.00
Je suis certain que Mike en moins de 15min aura fait le programme et pour le câblage en moins d une heure c est fonctionnel que ce soir tu as la version 2.0 avec les options 3 et 4 ….
Le truc c est que le relais il faut bien l alimenter en 3.3 pas en 5vdc donc reprendre le 3.3 de l esp et à savoir que la commande est inversée GPIO à 1 = relais désactivé
Pour option 3 et préparer la 4 je verrais bien la création d une route “commande manuelle “ sur laquelle on met un toogle button pour (re)ouvrir / fermer la vanne
Et une route “paramètres” pour configurer tous les paramètres plutôt que de les avoir en dur dans le code
( adresse SSID , IP , credential, paramètre smtp, liste des destinataires….) avec en prime une page d accès en mode point d accès pour le premier parametrage.
Vous avez 12h pour rendre votre copie Mike :-)
Messages : 163
Sujets : 13
Inscription : Jun 2024
(21-11-2025, 08:45 AM)Lolo69 a écrit : Le relais est capable de piloter la vanne en direct pas besoin de contacteur intermédiaire
Bon je met le sujet en Stanby ça été très agréable d échanger avec vous.
Réveilles moi quand tu passeras à la phase 3 . On fera la version 2.00
Je suis certain que Mike en moins de 15min aura fait le programme et pour le câblage en moins d une heure c est fonctionnel que ce soir tu as la version 2.0 avec les options 3 et 4 ….
Le truc c est que le relais il faut bien l alimenter en 3.3 pas en 5vdc donc reprendre le 3.3 de l esp et à savoir que la commande est inversée GPIO à 1 = relais désactivé
Pour option 3 et préparer la 4 je verrais bien la création d une route “commande manuelle “ sur laquelle on met un toogle button pour (re)ouvrir / fermer la vanne
Et une route “paramètres” pour configurer tous les paramètres plutôt que de les avoir en dur dans le code
( adresse SSID , IP , credential, paramètre smtp, liste des destinataires….) avec en prime une page d accès en mode point d accès pour le premier parametrage.
Vous avez 12h pour rendre votre copie Mike :-)
Alors la avec toutes ces évolutions effectivement on attend le graal ????.
Pour ce qui est des raccordement commande et puissance électrique la je doit m'en sortir sans problème.
Dans l'attente de vos suggestions.
Ici la neige ne tombe plus ❄️❄️❄️?
Messages : 527
Sujets : 5
Inscription : Jun 2024
(21-11-2025, 08:45 AM)Lolo69 a écrit : Le relais est capable de piloter la vanne en direct pas besoin de contacteur intermédiaire
Bon je met le sujet en Stanby ça été très agréable d échanger avec vous.
Réveilles moi quand tu passeras à la phase 3 . On fera la version 2.00
Je suis certain que Mike en moins de 15min aura fait le programme et pour le câblage en moins d une heure c est fonctionnel que ce soir tu as la version 2.0 avec les options 3 et 4 ….
Le truc c est que le relais il faut bien l alimenter en 3.3 pas en 5vdc donc reprendre le 3.3 de l esp et à savoir que la commande est inversée GPIO à 1 = relais désactivé
Pour option 3 et préparer la 4 je verrais bien la création d une route “commande manuelle “ sur laquelle on met un toogle button pour (re)ouvrir / fermer la vanne
Et une route “paramètres” pour configurer tous les paramètres plutôt que de les avoir en dur dans le code
( adresse SSID , IP , credential, paramètre smtp, liste des destinataires….) avec en prime une page d accès en mode point d accès pour le premier parametrage.
Vous avez 12h pour rendre votre copie Mike :-)
Pareil que Lolo, ce petit projet était sympa, tout comme les nombreuses pages de discussion avec vous deux.
Tupolev, tiens nous au courant quand tu as la vanne et que tu es prêt pour la suite.
@lolo pour configurer le wifi et les différents paramètres lors du premier lancement de l'ESP, c'est possible de faire ça facilement avec la library WifiManager : https://github.com/tzapu/WiFiManager
Au premier lancement l'ESP est en mode point d’accès, on configure le wifi (on peut rajouter d'autres paramètres) et hop c'est terminé.
Je l'utilise sur certains projets, ça marche très bien.
Messages : 774
Sujets : 4
Inscription : Sep 2024
Hier, 08:49 AM
(Modification du message : Hier, 09:22 AM par Lolo69.)
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 ;-)
Messages : 527
Sujets : 5
Inscription : Jun 2024
Que d'améliorations !
Bravo Lolo, le code a bien évolué et est maintenant facilement utilisable par tout le monde avec en bonus la mise à jour par OTA.
Je ne sais pas si il y a une grosse demande mais Tupolev tu vas pouvoir au moins le proposer à l'oncle de ta femme et à ton ancien collègue
Messages : 63
Sujets : 2
Inscription : Nov 2024
Bonjour @ vous trois,
dommage que ce soit terminé, ce thread était un plaisir à suivre et à lire.
Merci à vous
14 panneaux 425W avec MO IQ8AC Installés par EDF ENR
Routeur F1 ATB v 15.12, - 1 sonde JSY MK 194T sur Esp32 n° 1, - 1 relais SSR en mode demi sinus sur Eps32 n° 2 au niveau du CE 3kW
et - 1 Esp32 Ecran.
Merci André
|