Le but de cet article , est d’améliorer le projet Horloge Arduino.
en apportant les plus suivants :
- Mise à jour en WIFI
- Envoi des notification en WIFI
- Gestion de la luminosité
- Gestion et mise à jour de l’heure via service NTP
- Ecran plus grand
- Paramétrable via interface Web
- Interopérabilité domotique
Pour cela nous aurons besoin de :
- Wemos d1
- Deux matrices LED de quatre éléments
- Une photorésistance et une résistance
On va partir du sketch final de l’article : WIFI et OTA
Afin de gérer , l’affichage des différentes fonctionnalités ( horloge , notification , message information …. ) nous allons utiliser deux matrices LED ( composées de 4 matrices LED 8×8 ( basées sur Max7219 ).
Il suffira de souder les deux modules , ce qui amène un affichage sur 8 x 64.
Une fois les deux modules assemblés ( soudures entre les deux circuits , à prévoir ).Nous allons relier la matrice au Wemos de la façon suivante :
- Vcc -> 5v
- Gnd -> Gnd
- DIN -> D7
- CS -> D6
- CLK -> D5
Nous allons maintenant intégrer au Sketch , les bibliothèques permettant de piloter la matrice ( ne pas hésiter à se reporter à l’article horloge Arduino , pour plus de détails ).
// gestion de l’affichage matricielle
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
Puis à définir les PIN sur lesquels sont reliés cette dernière , ainsi que le nombre d’afficheur. Puis à Initialiser la matrice.
// Parametrage matrice ( Pin Arduino où est branchée la matrice )
#define MAX_DEVICES 8 // ( nombre de matrice )
#define CLK_PIN D5
#define DATA_PIN D7
#define CS_PIN D6
// initialisation de la matrice
MD_Parola P = MD_Parola(DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
Afin de pouvoir afficher des messages il faut démarrer le service d’affichage , dans la fonction Setup.
//démarrage Display
P.begin();
A partir de maintenant , nous sommes en mesure de pouvoir afficher un message sur la matrice avec la fonction : P.print(« votre message« );
Le message à afficher peut être soit de type String ou Char(buffer).
Si vous aves des soucis d’affichage , des caractères à l’envers , des effets miroir ou autres ( penser à configurer dans la bibliothèque master : le fichier src/MD_MAX72xx_lib.h , comme expliqué dans cet article )
Ce qui donne :
#include <ESP8266WiFi.h> #include <ESP8266WebServer.h> // WIFI Manager , afin de gérer la connexion au WIFI de façon plus intuitive #include <WiFiManager.h> //https://github.com/tzapu/WiFiManager ///includes nécessaires au fonctionnement de l'OTA : #include <WiFiUdp.h> #include <ArduinoOTA.h> // gestion de l'affichage matricielle #include <MD_Parola.h> #include <MD_MAX72xx.h> ESP8266WebServer server(80); // serveur WEB sur port 80 // définition du numéro de LED interne int led = 2; // led built IN // Paramétrage matrice ( Pin Arduino où est branchée la matrice ) #define MAX_DEVICES 8 // ( nombre de matrice ) #define CLK_PIN D5 #define DATA_PIN D7 #define CS_PIN D6 // initialisation de la matrice MD_Parola P = MD_Parola(DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES); //variable systemes #define BUF_SIZE 60 char Notif[BUF_SIZE]; // fonction callback Wifimanager quand Mode AP en fonction void configModeCallback (WiFiManager *myWiFiManager) { P.print("Choisir AP.."); delay(3000); P.print(WiFi.softAPIP().toString()); delay(3000); P.print(myWiFiManager->getConfigPortalSSID()); //Serial.println(WiFi.softAPIP()); //if you used auto generated SSID, print it //Serial.println(myWiFiManager->getConfigPortalSSID()); } void setup() { //démarrage Display P.begin(); //******** WiFiManager ************ //Local intialization. Once its business is done, there is no need to keep it around WiFiManager wifiManager; //Si besoin de fixer une adresse IP //wifiManager.setAPStaticIPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0)); //Forcer à effacer les données WIFI dans l'eprom , permet de changer d'AP à chaque démarrage ou effacer les infos d'une AP dans la mémoire ( à valider , lors du premier lancement ) //wifiManager.resetSettings(); //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode wifiManager.setAPCallback(configModeCallback); //Recupère les identifiants ssid et Mot de passe dans l'eprom et essaye de se connecter //Si pas de connexion possible , il demarre un nouveau point d'accés avec comme nom , celui defini dans la commande autoconnect ( ici : AutoconnectAP ) // wifiManager.autoConnect("AutoConnectAP"); //Si rien indiqué le nom par défaut est ESP + ChipID //wifiManager.autoConnect(); if(!wifiManager.autoConnect("AP_ESP")) { P.print("erreur AP");; delay(3000); //reset and try again, or maybe put it to deep sleep ESP.reset(); delay(5000); } // ****** Fin config WIFI Manager ************ //******* OTA *************** // Port defaults to 8266 // ArduinoOTA.setPort(8266); // Hostname defaults to esp8266-[ChipID] ArduinoOTA.setHostname("EspTestOTA"); // No authentication by default // ArduinoOTA.setPassword("admin"); // Password can be set with it's md5 value as well // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3 // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3"); ArduinoOTA.onStart([]() { String type; if (ArduinoOTA.getCommand() == U_FLASH) type = "sketch"; else // U_SPIFFS type = "filesystem"; digitalWrite(led, HIGH); // allume led au début du transfert P.print("Update ..."); // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() //Serial.println("Start updating " + type); }); ArduinoOTA.onEnd([]() { P.print("Reboot ..."); digitalWrite(led, LOW); // éteint à la fin de la mise à jour }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { sprintf(Notif,"Upload %u%%", (progress / (total / 100))); P.print(Notif); //Serial.printf("Progress: %u%%\r", (progress / (total / 100))); }); ArduinoOTA.onError([](ota_error_t error) { //Serial.printf("Error[%u]: ", error); }); ArduinoOTA.begin(); //********* Fin OTA *************** // on attend d'être connecté au WiFi avant de continuer while (WiFi.status() != WL_CONNECTED) { delay(500); P.print("Wait ..."); } // on affiche l'adresse IP attribuée pour le serveur DSN //Serial.print("IP address: "); //Serial.println(WiFi.localIP()); P.print("Wifi Ready ..."); delay(300); P.print(WiFi.localIP().toString()); delay(2000); // on définit les points d'entrée (les URL à saisir dans le navigateur web) et on affiche un simple texte server.on("/", [](){ server.send(200, "text/plain", "Page d'accueil"); }); server.on("/led", []() { String Etat=server.arg("etat"); if (Etat == "on") digitalWrite(led, LOW); else if (Etat == "off") digitalWrite(led, HIGH); server.send(200, "text/plain", "la led est " + Etat); }); // on démarre le serveur web server.begin(); pinMode(led, OUTPUT); // Initialise la broche "led" comme une sortie digitalWrite(led, HIGH); P.print("OK ..."); } void loop() { // à chaque iteration, la fonction handleClient traite les requêtes server.handleClient(); // Surveillance des demandes de mise à jour en OTA ArduinoOTA.handle(); }
Qu’apporte ce script , par rapport à la version précédente : Des messages d’informations sur les différentes étapes de sketch.
- Phase WifiManager : Une fonction de callback à été ajoutée , afin d’informer l’utilisateur de configurer son module en allant sur l’AP créé par le module ( le nom de l’AP et l’adresse IP du module s’affiche sur le Display ).
P.print(WiFi.softAPIP().toString());
P.print(myWiFiManager->getConfigPortalSSID()); - Phase OTA : Une information sur chaque étape : Start , End et Progress qui permet d’afficher un compteur en pourcentage sur le téléchargement en cours .
sprintf(Notif, »Upload %u%% », (progress / (total / 100)));
P.print(Notif); - Phase 3 : Information sur la connexion à votre reseau WIFI , affichage de l’adresse IP obtenue.
P.print(« Wifi Ready … »);
delay(300);
P.print(WiFi.localIP().toString()); - Phase 4 : Fin de la boucle setup
Fonction Temps et affichage de l’heure
Nous pourrions utiliser un module RTC , comme dans le premier article , sur la conception de l’horloge avec le module Arduino. Mais étant donné que ce module dispose d’une connexion WIFI permanente , nous allons synchroniser l’heure système sur un serveur de temps afin de récupérer l’heure universelle régulièrement , et ainsi permettre d’être toujours à l’heure.
Pour cela nous allons utiliser , les deux librairies suivantes :
time et NTPclient . A ajouter par l’intermédiaire du gestionnaire de bibliothèque ou à télécharger directement sur leurs github associés.
On définit ensuite les paramètres NTP suivants :
// Paramètre NTP
// Serveur NTP
const char ntpserver[] = « pool.ntp.org »;
// TimeZone
int8_t timeZone = 1;
// Le fuseau utilise les horaires été / hiver
bool DLS = true;
Dans la boucle Setup() , on va démarrer le service NTP avec les instructions suivantes :
//******* Service NTP ********
// Démarrage du processus NTP
NTP.begin(ntpserver, timeZone, DLS);
// Intervalle de synchronisation en seconde , 10 s au départ pour forcer une mise à jour rapide dès le démarrage.
NTP.setInterval (10);
Dans la boucle loop() , on définit la fonction de callback dédiée au process NTP , afin d’agir en fonction des différents évènements.
NTP.onNTPSyncEvent ([](NTPSyncEvent_t event) {
processNtpEvent(event);
});
La fonction processNTPEvent , est définie en dehors des deux boucles :
void processNtpEvent (NTPSyncEvent_t ntpEvent) {
if (ntpEvent) {
if (ntpEvent == noResponse)
// action à définir si aucune réponse ou réponse erronée en retour
else if (ntpEvent == invalidAddress)
// action à definir si adresse serveur invalide
} else {
// Réponse valide et serveur accessible — Tout est OK
NTP.setInterval (1800); // Apres première synchro , le temps de synchronisation est augmenté à 30 mn , afin d’éviter d’envoyer trop de requête )
}
}
Une fois le service NTP synchronisé , l’heure peut être affichée à l’aide de la librairie Time.
par le biais des fonctions suivantes :
hour(); // L'heure (0-23) minute(); // Minute (0-59) second(); // Seconde (0-59) day(); // Le jour(1-31) weekday(); // Numéro de jour (1-7), Dimanche est le jour 1 month(); // Numéro du mois (1-12) year(); // l'année sur 4 digit: (2017,2018 , etc )
Pour afficher l’heure , nous allons inclure dans la boucle loop , les instructions suivantes :
if (P.displayAnimate()) // Boucle pour contrôler les animations de l’afficheur
{
digitalClockDisplay().toCharArray(Notif,BUF_SIZE); // fonction pour récupérer l’heure et la formater.
P.displayText(Notif, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT);
// affichage de l’horloge sur le display avec effet central
}
Pour le formatage de l’heure , on utilise la fonction suivante :
// Fonction clockformat
String digitalClockDisplay()
{
String message;
// si date pas récupérée affiche init …
if ( year() == 1970) { message= »Init.. »;
}
else {
message=hour();
message+=printDigits(minute());
// Affiche les secondes si variable DisSec sur true
if ( DisSec ) { message+=printDigits(second()); }
}
return message;
}
Fonction qui permet d’ajouter les deux points et le zéro avant les minutes et les secondes.
String printDigits(int digits)
{
String Digital;
Digital= »: »;
if (digits < 10) Digital+= »0″;
Digital+=digits;
return Digital;
}
Ce qui donne le script complet suivant :
#include <ESP8266WiFi.h> #include <ESP8266WebServer.h> // WIFI Manager , afin de gérer la connexion au WIFI de façon plus intuitive #include <WiFiManager.h> //https://github.com/tzapu/WiFiManager ///includes nécessaires au fonctionnement de l'OTA : #include <WiFiUdp.h> #include <ArduinoOTA.h> // gestion de l'affichage matricielle #include <MD_Parola.h> #include <MD_MAX72xx.h> // librairie temps #include <TimeLib.h> #include <NtpClientLib.h> ESP8266WebServer server(80); // serveur WEB sur port 80 // définition du numéro de LED interne int led = 2; // led built IN // Paramétrage matrice ( Pin Arduino où est branchée la matrice ) #define MAX_DEVICES 8 // ( nombre de matrice ) #define CLK_PIN D5 #define DATA_PIN D7 #define CS_PIN D6 // initialisation de la matrice MD_Parola P = MD_Parola(DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES); //variable systemes #define BUF_SIZE 60 char Notif[BUF_SIZE]; boolean DisSec = true; // Parametre NTP // Serveur NTP const char ntpserver[] = "pool.ntp.org"; // TimeZone int8_t timeZone = 1; // Le fuseau utilise les horaires été / hiver bool DLS = true; void configModeCallback (WiFiManager *myWiFiManager) { P.print("Choisir AP.."); delay(3000); P.print(WiFi.softAPIP().toString()); delay(3000); P.print(myWiFiManager->getConfigPortalSSID()); //Serial.println(WiFi.softAPIP()); //if you used auto generated SSID, print it //Serial.println(myWiFiManager->getConfigPortalSSID()); } void processNtpEvent (NTPSyncEvent_t ntpEvent) { if (ntpEvent) { if (ntpEvent == noResponse) P.print ("Serveur NTP injoignable"); else if (ntpEvent == invalidAddress) P.print ("Adresse du serveur NTP invalide"); } else { //P.print ("Récupération du temps NTP: "); //P.print (NTP.getTimeDateString (NTP.getLastNTPSync ())); NTP.setInterval (1800); } } void setup() { //démarrage Display P.begin(); //******** WiFiManager ************ //Local intialization. Once its business is done, there is no need to keep it around WiFiManager wifiManager; //Si besoin de fixer une adresse IP //wifiManager.setAPStaticIPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0)); //Forcer à effacer les données WIFI dans l'eprom , permet de changer d'AP à chaque démarrage ou effacer les infos d'une AP dans la mémoire (à valider , lors du premier lancement) //wifiManager.resetSettings(); //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode wifiManager.setAPCallback(configModeCallback); //Récupère les identifiants ssid et Mot de passe dans l'eprom et essaye de se connecter //Si pas de connexion possible , il démarre un nouveau point d'accès avec comme nom , celui définit dans la commande autoconnect ( ici : AutoconnectAP ) // wifiManager.autoConnect("AutoConnectAP"); //Si rien indiqué le nom par défaut est ESP + ChipID //wifiManager.autoConnect(); if(!wifiManager.autoConnect("AP_ESP")) { P.print("erreur AP");; delay(3000); //reset and try again, or maybe put it to deep sleep ESP.reset(); delay(5000); } // ****** Fin config WIFI Manager ************ //******* OTA *************** // Port defaults to 8266 // ArduinoOTA.setPort(8266); // Hostname defaults to esp8266-[ChipID] ArduinoOTA.setHostname("EspTestOTA"); // No authentication by default // ArduinoOTA.setPassword("admin"); // Password can be set with it's md5 value as well // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3 // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3"); ArduinoOTA.onStart([]() { String type; if (ArduinoOTA.getCommand() == U_FLASH) type = "sketch"; else // U_SPIFFS type = "filesystem"; digitalWrite(led, HIGH); // allume led au début du transfert P.print("Update ..."); // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() //Serial.println("Start updating " + type); }); ArduinoOTA.onEnd([]() { P.print("Reboot ..."); digitalWrite(led, LOW); // éteint à la fin de la mise à jour }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { sprintf(Notif,"Upload %u%%", (progress / (total / 100))); P.print(Notif); //Serial.printf("Progress: %u%%\r", (progress / (total / 100))); }); ArduinoOTA.onError([](ota_error_t error) { //Serial.printf("Error[%u]: ", error); }); ArduinoOTA.begin(); //********* Fin OTA *************** // on attend d'être connecté au WiFi avant de continuer while (WiFi.status() != WL_CONNECTED) { delay(500); P.print("Wait ..."); } // on affiche l'adresse IP attribuée pour le serveur DSN //Serial.print("IP address: "); //Serial.println(WiFi.localIP()); P.print("Wifi Ready ..."); delay(300); P.print(WiFi.localIP().toString()); delay(2000); //************************* // on définit les points d'entrée (les URL à saisir dans le navigateur web) et on affiche un simple texte server.on("/", [](){ server.send(200, "text/plain", "Page d'accueil"); }); server.on("/led", []() { String Etat=server.arg("etat"); if (Etat == "on") digitalWrite(led, LOW); else if (Etat == "off") digitalWrite(led, HIGH); server.send(200, "text/plain", "la led est " + Etat); }); // on démarre le serveur web server.begin(); //******* Service NTP ******** // Démarrage du processus NTP NTP.begin(ntpserver, timeZone, DLS); // Intervalle de synchronisation en seconde , 10 s au départ pour forcer une mise à jour rapide NTP.setInterval (10); pinMode(led, OUTPUT); // Initialise la broche "led" comme une sortie digitalWrite(led, HIGH); P.print("OK ..."); } void loop() { NTP.onNTPSyncEvent ([](NTPSyncEvent_t event) { processNtpEvent(event); }); // à chaque iteration, la fonction handleClient traite les requêtes server.handleClient(); // Surveillance des demandes de mise à jour en OTA ArduinoOTA.handle(); if (P.displayAnimate()) { digitalClockDisplay().toCharArray(Notif,BUF_SIZE); P.displayText(Notif, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); } } // Fonction clockformat String digitalClockDisplay() { String message; if ( year() == 1970) { message="Init.."; } else { message=hour(); message+=printDigits(minute()); if ( DisSec ) { message+=printDigits(second()); } } return message; } String printDigits(int digits) { String Digital; Digital=":"; if (digits < 10) Digital+="0"; Digital+=digits; return Digital; }
Avec la variable suivante :
boolean DisSec = true;
vous pouvez afficher les secondes ( true ) ou ne pas les afficher ( false ).
Ajout des Notifications
Les messages vont être envoyés par le biais du Wifi , en appelant l’adresse Web du serveur de notre module.
Sous la forme : http://ip_module/Notification?msg=test
Pour cela, nous allons mettre en place une page Web , dans notre programme sous la forme suivante :
server.on(« /Notification », [](){
// on récupère le paramètre msg dans l’url
message=server.arg(« msg »);
// on affecte le message à l’écran et on passe la variable Alert a true , afin de signaler une demande d’affichage
message.toCharArray(Notif,BUF_SIZE);
Alert=true;
// on répond au client , en affichant le message récupéré
server.send(200, « text/plain », « message received: » + message);
});
Dans la boucle loop , au niveau de la boucle qui contrôle l’affichage du display , on va apporter les modifications suivantes :
if (Alert) {
P.print(« »);
P.setIntensity(MAX_INTENSITY); // intensité au max
P.displayText(Notif, PA_LEFT, 40, 1000, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
Alert=false;
}
else {
// Affichage heure
P.setIntensity(Intensite);
digitalClockDisplay().toCharArray(Notif,BUF_SIZE);
P.displayText(Notif, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT);
}
Si Alert est a True , on ajuste l’intensité de l’écran au Max, et on fait défiler le texte vers la gauche à une vitesse de défilement de 40 et une pause de 1000 Millisecondes.
Puis le paramètre Alert passe à False , afin d’afficher l’heure à la prochaine boucle. L’intensité du display , récupère sa valeur d’origine.
Gestion de la luminosité
Afin d’éviter que l’écran soit trop lumineux le soir , j’ai ajouté une fonction qui permet toutes les 5 secondes de contrôler la luminosité ambiante ( via une résistance sensible à la lumière ) et ajuster ainsi l’intensité des leds.
int Intensite=5; const long interval = 5000; // intervalle pour mesure luminosite réglé sur 5 s unsigned long previousMillis=0 ; // fonction réglage auto luminosité void luminosite() { sensorValue = analogRead(A0); // read analog input pin 0 Intensite =round((sensorValue*1.5)/100); if (Intensite < 1 ) Intensite = 1 ; if (Intensite > 15 ) Intensite = MAX_INTENSITY; } void loop () { ........... if( millis() - previousMillis >= interval) { previousMillis = millis(); luminosite(); } .......... }
Pour mesurer l’intensité lumineuse , on utilise une résistance sensible à la lumière ( Photo résistance ). Cette résistance varie en fonction de la luminosité ambiante.
Pour lire cette variation , on va réaliser le branchement suivant :
Attention l’entrée Analogique A0 du module ESP8266 , ne doit pas dépasser 3,3 V .
Pour lire la valeur A0 , on utilise la fonction analogRead(A0) , qui en fonction de la variation de la Photo-résistance donnera une valeur comprise entre 0 ( Nuit ) et 1023 ( en pleine lumière ).
Sachant que l’intensité du display , varie de 0 à 15 ( 0 étant éteint ) , on applique la formule suivante :
round((sensorValue*1.5)/100);
Qui permet de récupérer une valeur entre 0 et 15. Afin d’éviter d’éteindre le display ou d’envoyer une valeur trop haute , le retour est compris entre 1 et 15 par les deux conditions if.
Ce qui donne le script final complet :
#include <ESP8266WiFi.h> #include <ESP8266WebServer.h> // WIFI Manager , afin de gerer la connexion au WIFI de façon plus intuitive #include <WiFiManager.h> //https://github.com/tzapu/WiFiManager ///includes nécessaires au fonctionnement de l'OTA : #include <WiFiUdp.h> #include <ArduinoOTA.h> // gestion de l'affichage matricielle #include <MD_Parola.h> #include <MD_MAX72xx.h> // librairie temps #include <TimeLib.h> #include <NtpClientLib.h> ESP8266WebServer server(80); // serveur WEB sur port 80 // définition du numéro de LED interne int led = 2; // led built IN // variable pour stocker la valeur du photoresistor int sensorValue; // Paramétrage matrice ( Pin Arduino où est branchée la matrice ) #define MAX_DEVICES 8 // ( nombre de matrice ) #define CLK_PIN D5 #define DATA_PIN D7 #define CS_PIN D6 // initialisation de la matrice MD_Parola P = MD_Parola(DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES); //variable systemes #define BUF_SIZE 60 char Notif[BUF_SIZE]; boolean DisSec = true; boolean Alert=false; int Intensite=5; const long interval = 5000; // interval pour mesure luminosite réglé sur 5 s unsigned long previousMillis=0 ; String message=""; // Paramètre NTP // Serveur NTP const char ntpserver[] = "pool.ntp.org"; // TimeZone int8_t timeZone = 1; // Le fuseau utilise les horaires été / hiver bool DLS = true; void configModeCallback (WiFiManager *myWiFiManager) { P.print("Choisir AP.."); delay(3000); P.print(WiFi.softAPIP().toString()); delay(3000); P.print(myWiFiManager->getConfigPortalSSID()); //Serial.println(WiFi.softAPIP()); //if you used auto generated SSID, print it //Serial.println(myWiFiManager->getConfigPortalSSID()); } void processNtpEvent (NTPSyncEvent_t ntpEvent) { if (ntpEvent) { if (ntpEvent == noResponse) P.print ("Serveur NTP injoignable"); else if (ntpEvent == invalidAddress) P.print ("Adresse du serveur NTP invalide"); } else { //P.print ("Récupération du temps NTP: "); //P.print (NTP.getTimeDateString (NTP.getLastNTPSync ())); NTP.setInterval (1800); } } // fonction réglage auto luminosité void luminosite() { sensorValue = analogRead(A0); // read analog input pin 0 Intensite =round((sensorValue*1.5)/100); if (Intensite < 1 ) Intensite = 1 ; if (Intensite > 15 ) Intensite = MAX_INTENSITY; } void setup() { //démarrage Display P.begin(); //******** WiFiManager ************ //Local intialization. Once its business is done, there is no need to keep it around WiFiManager wifiManager; //Si besoin de fixer une adresse IP //wifiManager.setAPStaticIPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0)); //Forcer à effacer les données WIFI dans l'eprom , permet de changer d'AP à chaque démarrage ou effacer les infos d'une AP dans la mémoire (à valider , lors du premier lancement ) //wifiManager.resetSettings(); //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode wifiManager.setAPCallback(configModeCallback); //Récupère les identifiants ssid et Mot de passe dans l'eprom et essaye de se connecter //Si pas de connexion possible , il démarre un nouveau point d'accès avec comme nom , celui défini dans la commande autoconnect ( ici : AutoconnectAP ) // wifiManager.autoConnect("AutoConnectAP"); //Si rien indiqué le nom par défaut est ESP + ChipID //wifiManager.autoConnect(); if(!wifiManager.autoConnect("AP_ESP")) { P.print("erreur AP");; delay(3000); //reset and try again, or maybe put it to deep sleep ESP.reset(); delay(5000); } // ****** Fin config WIFI Manager ************ //******* OTA *************** // Port defaults to 8266 // ArduinoOTA.setPort(8266); // Hostname defaults to esp8266-[ChipID] ArduinoOTA.setHostname("EspTestOTA"); // No authentication by default // ArduinoOTA.setPassword("admin"); // Password can be set with it's md5 value as well // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3 // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3"); ArduinoOTA.onStart([]() { String type; if (ArduinoOTA.getCommand() == U_FLASH) type = "sketch"; else // U_SPIFFS type = "filesystem"; digitalWrite(led, HIGH); // allume led au début du transfert P.print("Update ..."); // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() //Serial.println("Start updating " + type); }); ArduinoOTA.onEnd([]() { P.print("Reboot ..."); digitalWrite(led, LOW); // éteint à la fin de la mise à jour }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { sprintf(Notif,"Upload %u%%", (progress / (total / 100))); P.print(Notif); //Serial.printf("Progress: %u%%\r", (progress / (total / 100))); }); ArduinoOTA.onError([](ota_error_t error) { //Serial.printf("Error[%u]: ", error); }); ArduinoOTA.begin(); //********* Fin OTA *************** // on attend d'être connecté au WiFi avant de continuer while (WiFi.status() != WL_CONNECTED) { delay(500); P.print("Wait ..."); } // on affiche l'adresse IP attribuée pour le serveur DSN //Serial.print("IP address: "); //Serial.println(WiFi.localIP()); P.print("Wifi Ready ..."); delay(300); P.print(WiFi.localIP().toString()); delay(2000); //************************* // on définit les points d'entrée (les URL à saisir dans le navigateur web) et on affiche un simple texte server.on("/", [](){ server.send(200, "text/plain", "Page d'accueil"); }); server.on("/Notification", [](){ // on récupère le paramètre msg dans l'url message=server.arg("msg"); message.toCharArray(Notif,BUF_SIZE); Alert=true; //P.print(message); // on repond au client server.send(200, "text/plain", "message received:" + message); }); server.on("/led", []() { String Etat=server.arg("etat"); if (Etat == "on") digitalWrite(led, LOW); else if (Etat == "off") digitalWrite(led, HIGH); server.send(200, "text/plain", "la led est " + Etat); }); // on demarre le serveur web server.begin(); //******* Service NTP ******** // Démarrage du processus NTP NTP.begin(ntpserver, timeZone, DLS); // Intervalle de synchronisation en seconde , 10 s au départ pour forcer une mise à jour rapide NTP.setInterval (10); pinMode(led, OUTPUT); // Initialise la broche "led" comme une sortie digitalWrite(led, HIGH); P.print("OK ..."); } void loop() { if( millis() - previousMillis >= interval) { previousMillis = millis(); luminosite(); } NTP.onNTPSyncEvent ([](NTPSyncEvent_t event) { processNtpEvent(event); }); // à chaque iteration, la fonction handleClient traite les requêtes server.handleClient(); // Surveillance des demandes de mise à jour en OTA ArduinoOTA.handle(); if (P.displayAnimate()) { if (Alert) { P.setIntensity(MAX_INTENSITY); // intensité au max P.displayText(Notif, PA_LEFT, 40, 1000, PA_SCROLL_LEFT, PA_SCROLL_LEFT); Alert=false; } else { // Affichage heure P.setIntensity(Intensite); digitalClockDisplay().toCharArray(Notif,BUF_SIZE); //P.print(Intensite); Debug pour afficher valeur luminosité P.displayText(Notif, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); } } } // Fonction clockformat String digitalClockDisplay() { message; if ( year() == 1970) { message="Init.."; } else { message=hour(); message+=printDigits(minute()); if ( DisSec ) { message+=printDigits(second()); } } return message; //Serial.print(day()); //Serial.print("."); //Serial.print(month()); //Serial.print("."); //Serial.print(year()); //Serial.println(); } String printDigits(int digits) { String Digital; Digital=":"; if (digits < 10) Digital+="0"; Digital+=digits; return Digital; }[adsense]
Integration Jeedom
Afin de permettre à Jeedom ( ou toute autre solution domotique ) , d’envoyer des notifications sur notre horloge . Nous allons créer un nouvel élément à l’aide du plugin Script de jeedom.
Aller dans Scipt , et ajouter . Choisisser un nom.
Puis dans l’onglet commande , ajouter une commande script.
Cette commande sera de type HTTP
Choisissez dans type : Action et type d’action : message.
Dans le champs requête :
Ip_de_votre_module/Notification?msg=#message#
Sauvegarder puis tester , vous devriez voir défiler sur votre écran : Ceci est un test de message pour la commande notification.
test depuis le Widget Jeedom.
L’info titre n’est pas utilisé dans cet exemple.
Dans le script j’ai ajouté une variable type , qui peut être utilisée avec le mot clé « alert » afin de mettre l’intensité au max.
Il suffit alors de modifier la requête http de la commande script créée :
IP_Module_horloge/Notification?msg=#message#&type=#title#
Les notifications peuvent être appelées depuis un scénario.
Cela se présente sous la forme suivante :
Dans jeedom , il n’y a pas de limite aux types de notification. Elles peuvent servir à afficher :
- Un message d’information
- Un message d’alerte ( en indiquant alert dans l’onglet titre )
- la date du jour et la saint du jour ( #sjour# , #jour# #smois# #annee# Saint du jour : #[Admin / Info][INFO][Saint du jour]# )
- des flash news
- la météo du jour
- etc ……
pour terminer cet article , il ne reste plus qu’a décrire , comment implanter une interface web pour le paramétrage du module
Paramétrage depuis interface WEB
Dans le cadre de cet article , nous allons mettre une page WEB , qui affichera les informations suivantes :
- Etat du système
- Parametrage de l’affichage des secondes
- Parametrage Intensité
Avec ces différents exemples , vous serez en mesure d’apporter des paramètres supplémentaires , que vous jugerez utile , dans le cas d’une amélioration de l’horloge.
Voici la page WEB qui sera dédié aux réglages :
Pour cela nous allons modifier le script de la manière suivante :
.......... // dans la partie definition de notre serveur web server.on("/", handleRoot); // on ajoute la fonction handleRoot server.on("/Notification", [](){ // on recupere les parametre dans l'url message=server.arg("msg"); message.toCharArray(Notif,BUF_SIZE); if (server.arg("type")=="alert") { P.setIntensity(MAX_INTENSITY); // intensité au max } Alert=true; //P.print(message); // on repond au client server.send(200, "text/plain", "message received:" + message + server.arg("type")); }); ........
La fonction handleroot sera en charge d’intercepter tous les appels effectué à la racine du site WEB du module.
Avant la section setup() , nous allons insérerez les deux nouvelles fonctions :
handleRoot : qui sera en charge d’afficher la page HTML du formulaire ou de rediriger vers handleSubmit , si des données sont présentes dans le formulaire.
handleSubmit : Qui sera en charge de récupérer les variables soumis dans le formulaire et de les traiter.
// ****************** // Gestiuon page WEB //****************** void handleRoot(){ if ( server.hasArg("SEC") ) { handleSubmit(); } else { server.send ( 200, "text/html", getPage() ); } } void handleSubmit() { String SEC; SEC = server.arg("SEC"); if ( server.arg("Autolum") =="manu" ) { AutoIn=false; Intensite=server.arg("ReglageInt").toInt(); } else { AutoIn=true;} if ( server.arg("HOR") =="1" ) { TimeOn=true; } else { TimeOn=false;} if ( SEC == "1" ) { DisSec=true; server.send ( 200, "text/html", getPage() ); } else if ( SEC == "0" ) { DisSec=false; server.send ( 200, "text/html", getPage() ); } else { // Serial.println("Err Led Value"); } } //***********************
Dans cette exemple , j’ai mis en place un formulaire permettant de :
- Afficher les secondes
- Gérer la luminosité
- Afficher l’horloge ( écran vide , juste un point indique que l’horloge est active )
Quand à la fonction getPage , elle me permet d’aller chercher ma page WEB.
La fonction est écrite en fin de script , après la fonction loop().
........ //************************************ // Page WEB // *********************************** String getPage(){ String page = "<html lang=fr-FR><head><meta http-equiv='refresh' content='120'/>"; page += "<title>Horloge / Notification - Byfeel.info</title>"; page += "<style> table.steelBlueCols { border: 3px solid #555555;background-color: #555555;width: 95%;text-align: left;border-collapse: collapse;}"; page +="table.steelBlueCols td, table.steelBlueCols th { border: 1px solid #555555;padding: 4px 9px;}"; page +="table.steelBlueCols tbody td {font-size: 1em;font-weight: bold;color: #FFFFFF;} table.steelBlueCols td:nth-child(even) {background: #398AA4;}"; page +="fieldset { padding:0 20px 20px 20px; margin-bottom:10px;border:1px solid #398AA4;width:95%;} div.bloc {float:left;width:450px}"; //page +="table.steelBlueCols tfoot .links a{display: inline-block;background: #FFFFFF;color: #398AA4;padding: 2px 8px;border-radius: 5px;" page +="}</style>"; page += "</head><body><h1><span style='background-color: #398AA4; color: #ffffff; padding: 0 5px;'>Horloge - Notification</span></h1>"; page += "<div class='bloc'><form action='/Notification' method='GET'>"; page +="<fieldset><legend>Envoyer une Notification :</legend>"; page += "<INPUT type='text' name='msg' id='msg'maxlength='59' style='width:400px;' placeholder='Votre Message - 60 caracteres max -'/>"; page +="</fieldset>"; page += "<INPUT type='submit' value='Envoyer Message' /></div>"; page += "</form></div>"; page +="<div style='clear:both;'></div>"; page += "<div class='bloc'><h3>Systeme</h3>"; page += "<table class='steelBlueCols'><tbody>"; page += "<tr><td>Mise en Marche </td><td>"+ NTP.getUptimeString() + "</td></tr>"; page += "<tr><td>Serveur NTP </td><td>"; page += ntpserver; page +="</td></tr>"; page += "<tr><td>Premiere synchro NTP </td><td>"+NTP.getTimeDateString(NTP.getFirstSync())+"</td></tr>"; page += "<tr><td>Derniere synchro NTP </td><td>"+NTP.getTimeDateString(NTP.getLastNTPSync())+"</td></tr>"; page +="</tbody></table></div>"; page += "<div class='bloc'><h3>Resau</h3>"; page += "<table class='steelBlueCols'><tbody>"; page += "<tr><td>mac adress : </td><td>"; page += WiFi.macAddress().c_str(); page +="</td></tr>"; page += "<tr><td>IP</td><td>"+ WiFi.localIP().toString() + "</td></tr>"; page += "<tr><td>Masque </td><td>"+ WiFi.subnetMask().toString() + "</td></tr>"; page += "<tr><td>Passerelle</td><td>"+ WiFi.gatewayIP().toString() + "</td></tr>"; page += "<tr><td>DNS primaire</td><td>"+ WiFi.dnsIP().toString() + "</td></tr>"; page += "<tr><td>DNS secondaire</td><td>"+ WiFi.dnsIP(1).toString() + "</td></tr>"; page +="</tbody></table></div>"; page +="<div class='bloc'><h3>Wifi</h3>"; page += "<table class='steelBlueCols'><tbody>"; page += "<tr><td>Hostname</td><td>"+ WiFi.hostname() + "</td></tr>"; page += "<tr><td>SSID</td><td>"+ WiFi.SSID() + "</td></tr>"; page += "<tr><td>Signal WIFI</td><td>"; page += WiFi.RSSI(); page +=" dBm</td></tr>"; page += "<tr><td>BSSID </td><td>"; page += WiFi.BSSIDstr().c_str(); page +="</td></tr>"; page +="</tbody></table></div>"; page +="<div style='clear:both;'></div>"; page += "<div class='bloc'><h3>Parametres :</h3>"; page += "<form action='/' method='POST'>"; page +="<fieldset><legend>Affichage des Secondes :</legend>"; page += "<INPUT type='radio' name='SEC' value='1' id='on'"; if (DisSec==true) page +=" checked "; page += "><label for='on'>ON</label>"; page +="<INPUT type='radio' name='SEC' value='0' id='off'"; if (DisSec==false) page +=" checked "; page += "><label for='off'>OFF</label></fieldset>"; page +="<fieldset><legend>Affichage Horloge :</legend>"; page += "<INPUT type='radio' name='HOR' value='1' id='horon'"; if (TimeOn==true) page +=" checked "; page += "><label for='horon'>ON</label>"; page +="<INPUT type='radio' name='HOR' value='0' id='horoff'"; if (TimeOn==false) page +=" checked "; page += "><label for='horoff'>OFF</label></fieldset>"; page +="<fieldset><legend>Gestion de la luminositée : </legend>"; page += "<INPUT type='radio' name='Autolum' value='auto' id='auto'"; if (AutoIn==true) page +=" checked "; page += "><label for='auto'>AUTO</label>"; page +="<INPUT type='radio' name='Autolum' value='manu' id='manu'"; if (AutoIn==false) page +=" checked "; page += "><label for='manu'>Manuel</label></P>"; if (AutoIn==false) { page +="<p>Valeur luminositée : "; page +=Intensite; page +="</p>"; page += "<p><input id='slider1' type='range' min='0' max='15' step='1' name='ReglageInt' list='tickmarks' value='"; page +=Intensite; page +="'/>"; page +="<datalist id='tickmarks'>"; page +="<option value='0' label='0'>"; page +="<option value='1'>"; page +="<option value='2'>"; page +="<option value='3'>"; page +="<option value='4'>"; page +="<option value='5' label='5'>"; page +="<option value='6'>"; page +="<option value='7'>"; page +="<option value='8'>"; page +="<option value='9'>"; page +="<option value='10' label='10'>"; page +="<option value='11'>"; page +="<option value='12'>"; page +="<option value='13'>"; page +="<option value='14'>"; page +="<option value='15' label='15'>"; page +="</datalist></p>"; }; page +="</fieldset>"; page += "<INPUT type='submit' value='Actualiser'name='parametres'/></div>"; page += "</form>"; page += "<br><br>"; page += "</body></html>"; return page; } ........
Pour , vous aider à écrire votre page WEB , vous pouvez vous aider d’éditeurs en ligne comme Online HTML Editor .
Dans cette page , je gére deux formulaires :
- le premier : Envoie d’une notification avec le formulaire « <form action=’/Notification’ method=’GET’> » , par la methode Get et appelle de la page /Notification ( celle utilisé par jeedom , pour l’envoie des notifications ).
- Le second : Paramètres , utilise la méthode POST, « <form action=’/’ method=’POST’> » , afin d’être traité par la fonction hndleSubmit .
Le script final au complet :
#include <ESP8266WiFi.h> #include <ESP8266WebServer.h> // WIFI Manager , afin de gerer la connexion au WIFI de façon plus intuitive #include <WiFiManager.h> //https://github.com/tzapu/WiFiManager ///includes necessaires au fonctionnement de l'OTA : #include <WiFiUdp.h> #include <ArduinoOTA.h> // gestion de l'affichage matricielle #include <MD_Parola.h> #include <MD_MAX72xx.h> // librairie temps #include <TimeLib.h> #include <NtpClientLib.h> ESP8266WebServer server(80); // serveur WEB sur port 80 // definition du numero de LED interne int led = 2; // led built IN // variable pour stocker la valeur du photoresistor int sensorValue; // Parametrage matrice ( Pin Arduino ou est branché la matrice ) #define MAX_DEVICES 8 // ( nombre de matrice ) #define CLK_PIN D5 #define DATA_PIN D7 #define CS_PIN D6 // initialisation de la matrice MD_Parola P = MD_Parola(DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES); //variable systemes const char nom_module[] ="ESP_tuto-horloge"; #define BUF_SIZE 60 char Notif[BUF_SIZE]; boolean DisSec = true; boolean Alert=false; boolean AutoIn=true; boolean TimeOn=true; int Intensite=5; const long interval = 5000; // interval pour mesure luminosite réglé sur 5 s unsigned long previousMillis=0 ; String message=""; // Parametre NTP // Serveur NTP const char ntpserver[] = "pool.ntp.org"; // TimeZone int8_t timeZone = 1; // Le fuseau utilise les horaires été / hiver bool DLS = true; boolean syncEventTriggered = false; // True if a time even has been triggered NTPSyncEvent_t ntpEvent; // Last triggered event void configModeCallback (WiFiManager *myWiFiManager) { P.print("Choisir AP.."); delay(3000); P.print(WiFi.softAPIP().toString()); delay(3000); P.print(myWiFiManager->getConfigPortalSSID()); //Serial.println(WiFi.softAPIP()); //if you used auto generated SSID, print it //Serial.println(myWiFiManager->getConfigPortalSSID()); } //void processNtpEvent (NTPSyncEvent_t ntpEvent) { void processSyncEvent (NTPSyncEvent_t ntpEvent) { if (ntpEvent) { if (ntpEvent == noResponse) P.print ("Serveur NTP injoignable"); else if (ntpEvent == invalidAddress) P.print ("Adresse du serveur NTP invalide"); } else { if ( year() != 1970 or year() != 2036 ) { NTP.setInterval (1200); } } } // fonction reglage auto luminosite void luminosite() { sensorValue = analogRead(A0); // read analog input pin 0 Intensite =round((sensorValue*1.5)/100); if (Intensite < 1 ) Intensite = 0 ; if (Intensite > 15 ) Intensite = MAX_INTENSITY; } // ****************** // Gestiuon page WEB //****************** void handleRoot(){ if ( server.hasArg("SEC") ) { handleSubmit(); } else { server.send ( 200, "text/html", getPage() ); } } void handleSubmit() { String SEC; SEC = server.arg("SEC"); if ( server.arg("Autolum") =="manu" ) { AutoIn=false; Intensite=server.arg("ReglageInt").toInt(); } else { AutoIn=true;} if ( server.arg("HOR") =="1" ) { TimeOn=true; } else { TimeOn=false;} if ( SEC == "1" ) { DisSec=true; server.send ( 200, "text/html", getPage() ); } else if ( SEC == "0" ) { DisSec=false; server.send ( 200, "text/html", getPage() ); } else { // Serial.println("Err Led Value"); } } //*********************** void setup() { //demarrage Display P.begin(); //******** WiFiManager ************ //Local intialization. Once its business is done, there is no need to keep it around WiFiManager wifiManager; //Si besoin de fixer une adresse IP //wifiManager.setAPStaticIPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0)); //Forcer à effacer les donnees WIFI dans l'eprom , permet de changer d'AP à chaque demmarrage ou effacer les infos d'une AP dans la memoire ( a valider , lors du premier lancement ) //wifiManager.resetSettings(); //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode wifiManager.setAPCallback(configModeCallback); //Recupere les identifiants ssid et Mot de passe dans l'eprom et essayes de se connecter //Si pas de connexion possible , il demarre un nouveau point d'accés avec comme nom , celui definit dans la commande autoconnect ( ici : AutoconnectAP ) // wifiManager.autoConnect("AutoConnectAP"); //Si rien indiqué le nom par defaut est ESP + ChipID //wifiManager.autoConnect(); if(!wifiManager.autoConnect("AP_ESP")) { P.print("erreur AP");; delay(3000); //reset and try again, or maybe put it to deep sleep ESP.reset(); delay(5000); } // ****** Fin config WIFI Manager ************ //******* OTA *************** // Port defaults to 8266 // ArduinoOTA.setPort(8266); // Hostname defaults to esp8266-[ChipID] ArduinoOTA.setHostname(nom_module); // No authentication by default // ArduinoOTA.setPassword("admin"); // Password can be set with it's md5 value as well // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3 // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3"); ArduinoOTA.onStart([]() { String type; if (ArduinoOTA.getCommand() == U_FLASH) type = "sketch"; else // U_SPIFFS type = "filesystem"; digitalWrite(led, HIGH); // allume led au debut du transfert P.print("Update ..."); // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() //Serial.println("Start updating " + type); }); ArduinoOTA.onEnd([]() { P.print("Reboot ..."); digitalWrite(led, LOW); // eteint a la fin de la mise a jour }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { sprintf(Notif,"Upload %u%%", (progress / (total / 100))); P.print(Notif); //Serial.printf("Progress: %u%%\r", (progress / (total / 100))); }); ArduinoOTA.onError([](ota_error_t error) { //Serial.printf("Error[%u]: ", error); }); ArduinoOTA.begin(); //********* Fin OTA *************** // on attend d'etre connecte au WiFi avant de continuer P.print("Wait ..."); while (WiFi.status() != WL_CONNECTED) { } // on affiche l'adresse IP attribuee pour le serveur DSN et on affecte le nom reseau WiFi.hostname(nom_module); P.print("Wifi Ready ..."); delay(300); P.print(WiFi.localIP().toString()); delay(2000); //************************* // on definit les points d'entree (les URL a saisir dans le navigateur web) et on affiche un simple texte server.on("/", handleRoot); server.on("/Notification", [](){ // on recupere les parametre dans l'url message=server.arg("msg"); message.toCharArray(Notif,BUF_SIZE); if (server.arg("type")=="alert") { P.setIntensity(MAX_INTENSITY); // intensité au max } Alert=true; //P.print(message); // on repond au client server.send(200, "text/plain", "message received:" + message + server.arg("type")); }); // on demarre le serveur web server.begin(); //******* Service NTP ******** NTP.onNTPSyncEvent ([](NTPSyncEvent_t event) { ntpEvent = event; syncEventTriggered = true; }); // Démarrage du processus NTP NTP.begin(ntpserver, timeZone, DLS); // Interval de synchronisation en seconde , 10 s au depart pour forcer une mise a jour rapide NTP.setInterval (10); pinMode(led, OUTPUT); // Initialise la broche "led" comme une sortie digitalWrite(led, HIGH); P.print("OK ..."); } void loop() { // Si gestion Auto luminositée activé if (AutoIn) { if( millis() - previousMillis >= interval) { previousMillis = millis(); luminosite(); } } // NTP.onNTPSyncEvent ([](NTPSyncEvent_t event) { // processNtpEvent(event); //}); if (syncEventTriggered) { processSyncEvent (ntpEvent); syncEventTriggered = false; } // a chaque iteration, la fonction handleClient traite les requetes server.handleClient(); // Surveillance des demandes de mise a jour en OTA ArduinoOTA.handle(); if (P.displayAnimate()) { if (Alert) { P.displayText(Notif, PA_LEFT, 40, 1000, PA_SCROLL_LEFT, PA_SCROLL_LEFT); Alert=false; } else { // Affichage heure if (TimeOn) { P.setIntensity(Intensite); digitalClockDisplay().toCharArray(Notif,BUF_SIZE); //P.print(Intensite); Debug pour afficher valeur luminosite P.displayText(Notif, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); } else P.print("."); } } } // Fonction clockformat String digitalClockDisplay() { message=hour(); message+=printDigits(minute()); if ( DisSec ) { message+=printDigits(second()); } return message; //Serial.print(day()); //Serial.print("."); //Serial.print(month()); //Serial.print("."); //Serial.print(year()); //Serial.println(); } String printDigits(int digits) { String Digital; Digital=":"; if (digits < 10) Digital+="0"; Digital+=digits; return Digital; } //************************************ // Page WEB // *********************************** String getPage(){ String page = "<html lang=fr-FR><head><meta http-equiv='refresh' content='120'/>"; page += "<title>Horloge / Notification - Byfeel.info</title>"; page += "<style> table.steelBlueCols { border: 3px solid #555555;background-color: #555555;width: 95%;text-align: left;border-collapse: collapse;}"; page +="table.steelBlueCols td, table.steelBlueCols th { border: 1px solid #555555;padding: 4px 9px;}"; page +="table.steelBlueCols tbody td {font-size: 1em;font-weight: bold;color: #FFFFFF;} table.steelBlueCols td:nth-child(even) {background: #398AA4;}"; page +="fieldset { padding:0 20px 20px 20px; margin-bottom:10px;border:1px solid #398AA4;width:95%;} div.bloc {float:left;width:450px}"; //page +="table.steelBlueCols tfoot .links a{display: inline-block;background: #FFFFFF;color: #398AA4;padding: 2px 8px;border-radius: 5px;" page +="}</style>"; page += "</head><body><h1><span style='background-color: #398AA4; color: #ffffff; padding: 0 5px;'>Horloge - Notification</span></h1>"; page += "<div class='bloc'><form action='/Notification' method='GET'>"; page +="<fieldset><legend>Envoyer une Notification :</legend>"; page += "<INPUT type='text' name='msg' id='msg'maxlength='59' style='width:400px;' placeholder='Votre Message - 60 caracteres max -'/>"; page +="</fieldset>"; page += "<INPUT type='submit' value='Envoyer Message' /></div>"; page += "</form></div>"; page +="<div style='clear:both;'></div>"; page += "<div class='bloc'><h3>Systeme</h3>"; page += "<table class='steelBlueCols'><tbody>"; page += "<tr><td>Mise en Marche </td><td>"+ NTP.getUptimeString() + "</td></tr>"; page += "<tr><td>Serveur NTP </td><td>"; page += ntpserver; page +="</td></tr>"; page += "<tr><td>Premiere synchro NTP </td><td>"+NTP.getTimeDateString(NTP.getFirstSync())+"</td></tr>"; page += "<tr><td>Derniere synchro NTP </td><td>"+NTP.getTimeDateString(NTP.getLastNTPSync())+"</td></tr>"; page +="</tbody></table></div>"; page += "<div class='bloc'><h3>Resau</h3>"; page += "<table class='steelBlueCols'><tbody>"; page += "<tr><td>mac adress : </td><td>"; page += WiFi.macAddress().c_str(); page +="</td></tr>"; page += "<tr><td>IP</td><td>"+ WiFi.localIP().toString() + "</td></tr>"; page += "<tr><td>Masque </td><td>"+ WiFi.subnetMask().toString() + "</td></tr>"; page += "<tr><td>Passerelle</td><td>"+ WiFi.gatewayIP().toString() + "</td></tr>"; page += "<tr><td>DNS primaire</td><td>"+ WiFi.dnsIP().toString() + "</td></tr>"; page += "<tr><td>DNS secondaire</td><td>"+ WiFi.dnsIP(1).toString() + "</td></tr>"; page +="</tbody></table></div>"; page +="<div class='bloc'><h3>Wifi</h3>"; page += "<table class='steelBlueCols'><tbody>"; page += "<tr><td>Hostname</td><td>"+ WiFi.hostname() + "</td></tr>"; page += "<tr><td>SSID</td><td>"+ WiFi.SSID() + "</td></tr>"; page += "<tr><td>Signal WIFI</td><td>"; page += WiFi.RSSI(); page +=" dBm</td></tr>"; page += "<tr><td>BSSID </td><td>"; page += WiFi.BSSIDstr().c_str(); page +="</td></tr>"; page +="</tbody></table></div>"; page +="<div style='clear:both;'></div>"; page += "<div class='bloc'><h3>Parametres :</h3>"; page += "<form action='/' method='POST'>"; page +="<fieldset><legend>Affichage des Secondes :</legend>"; page += "<INPUT type='radio' name='SEC' value='1' id='on'"; if (DisSec==true) page +=" checked "; page += "><label for='on'>ON</label>"; page +="<INPUT type='radio' name='SEC' value='0' id='off'"; if (DisSec==false) page +=" checked "; page += "><label for='off'>OFF</label></fieldset>"; page +="<fieldset><legend>Affichage Horloge :</legend>"; page += "<INPUT type='radio' name='HOR' value='1' id='horon'"; if (TimeOn==true) page +=" checked "; page += "><label for='horon'>ON</label>"; page +="<INPUT type='radio' name='HOR' value='0' id='horoff'"; if (TimeOn==false) page +=" checked "; page += "><label for='horoff'>OFF</label></fieldset>"; page +="<fieldset><legend>Gestion de la luminositée : </legend>"; page += "<INPUT type='radio' name='Autolum' value='auto' id='auto'"; if (AutoIn==true) page +=" checked "; page += "><label for='auto'>AUTO</label>"; page +="<INPUT type='radio' name='Autolum' value='manu' id='manu'"; if (AutoIn==false) page +=" checked "; page += "><label for='manu'>Manuel</label></P>"; if (AutoIn==false) { page +="<p>Valeur luminositée : "; page +=Intensite; page +="</p>"; page += "<p><input id='slider1' type='range' min='0' max='15' step='1' name='ReglageInt' list='tickmarks' value='"; page +=Intensite; page +="'/>"; page +="<datalist id='tickmarks'>"; page +="<option value='0' label='0'>"; page +="<option value='1'>"; page +="<option value='2'>"; page +="<option value='3'>"; page +="<option value='4'>"; page +="<option value='5' label='5'>"; page +="<option value='6'>"; page +="<option value='7'>"; page +="<option value='8'>"; page +="<option value='9'>"; page +="<option value='10' label='10'>"; page +="<option value='11'>"; page +="<option value='12'>"; page +="<option value='13'>"; page +="<option value='14'>"; page +="<option value='15' label='15'>"; page +="</datalist></p>"; }; page +="</fieldset>"; page += "<INPUT type='submit' value='Actualiser'name='parametres'/></div>"; page += "</form>"; page += "<br><br>"; page += "</body></html>"; return page; }
Il ne reste plus qu’à vous trouver une petite boîte , ou en fabriquer une pour mettre votre horloge dedans.
37 commentaires sur “DIY Horloge et Notification Domotique ( Matrix LED ) avec Jeedom”
J’ai modifié le code initial pour enlever les accents… Si cela interesse quelqu’un.
Bonjour, personnellement je n’ai utilisé que des wemos mini pour mes afficheurs et ca fonctionne super bien
Bonjour,
Merci pour vos réponses et précisions, je vais donc adapter pour travailler sur le Wemos D1 Mini, j’essaie de limiter l’encombrement dans le boitier.
Et pour le RGB, je vais du coup attendre de voir si qqch bouge à ce niveau là.
Merci bien et belle suite de soirée à tous et bon weekend.
Hello, merci pour ce super tuto, je vais m y mettre tout bientôt.
J’ai juste 2 petites question :
1. Est-ce qu’il est possible de faire le montage avec un Wemos D1 mini ? Ce qui serait intéressant pour le gain de place.
2. J’ai mes LED Matrix depuis longtemps du coups me rappelle pas vraiment, est-ce qu’ils sont multi couleur ? Ou c’est un affichage en une seule couleur ?
Dans le cas d’une seule couleur, est-ce qu’il est envisageable de faire le même principe de fonctionnement mais avec du RGB ? J’aimerai vraiment me rapprocher au mieux de LaMetrics que je trouves excellent mais dont le budget reste important.
Merci d’avance et belle suite de journée à tous.
bonjour steve ,
pour répondre à vos questions , il est tout a fait possible d’utiliser un autre wemos , ou tout autre module à base de chip ESP8266 ( nodeMCU , etc … ) . Il faudra juste vérifier le branchement au niveau des broches , et au moment de la compilation avec l’interface ARduino , de choisir la carte adéquate .
pour la question 2 , j’utilise des led monochrome avec la bibliothèque max72xx , mais il est possible d’adapter le script avec une matrice RGB, il faudra pour cela utiliser une bibliothèque adéquate ( comme celle proposé par adafruit ).
A ce jour , je ne me suis pas encore intéressé aux matrices RGB ( cher et consomme beaucoup plus ) , le but est de faire un notificateur le plus économe possible.
Mais aux vus des évolutions , il se peut que dans quelques temps je m’y intéresse.
Super, fonctionne nickel.
Y a t-il un moyen de mettre l’horloge sur OFF depuis l’URL?
Bonjour , oui bien sur il suffit de taper l’adresse ip du module suivi de la commande ?HOR=off
soit : http://192.168.X.X/HOR=off et on pour reactiver
je fais un screen lors du prochain upload 🙂
Merci pour ce super tuto. Tout fonctionne direct.
Tu as un github pour héberger tes sources ?
Non pas encore , mais c’est une bonne idée .
Je vais regarder ça.
Merci à vous tous , la V2 avance vite. Elle ne devrait plus tarder .
Merci pour le good job….
Que de bonnes nouvelles ! j’ai complètement adopté le système et ai déjà recommandé le matériel pour un deuxième afficheur voir même un réveil comme Thyer l’a suggéré ! merci pour le taff en tout cas
Au top Merci
Quelqu’un a t’il trouvé la solution pour gérer les accents pour les messages a travers la bibliothèque MAX 72xx ? Je rame …
Bonjour julien ,
oui j’ai la solution , je suis entrain de finaliser les modifs , j’ai fait quelques test ça fonctionne .
Pour info suite aux remontées de Thyer , j’ai commencé à ajouter aussi la gestion d’un bouton , pour différentes actions.
Le temps de finaliser tout ça , et je penses pouvoir publier les modifications d’ici fin de semaine , début de semaine prochaine.
L’avantage , avec ce type d’afficheur , c’est que son role est uniquement d’afficher un texte ou une horloge . Toute l’intelligence est dans la box domotique ( ici jeedom ) qui permet tout un tas de scenario :
– Horloge
– notification domotique
– Reveil
– Affichage de la temperature ( exterieur / interieur )
– Affichage info diverses ( saint du jour , horoscope / alerte météo ) via des scenario et des moments de la journée ….
– … etc …
Lol j’ai déjà commandé un Pi Zero + une Carte son USB pour la partie Squeezebox, et deux Wemos + 4 matrix led pour l’affichage :D, ouai je me fais chier en ce moment 😀
Donc deux afficheur en un si je comprend bien ?
C’est exactement à cela qu’il me sert actuellement mais l’idée du réveil me plaît bien
Carrément, avec une partie Squeezebox pour gérer le réveil. 🙂
bon la tu m’intéresse carrement, je pense que je vais directement recommander des matrix led 😀
Chez moi , j’ai mis en place trois afficheurs ( dans le salon , le bureau et la salle de jeux ) , afin d’être notifié ( de la sonnette , message important , alerte … ) pour les alertes j’ai couplé avec une sortie son sur une enceinte Sonos.
J’ai dans l’idée de faire un radio reveil , couplé à Jeedom/telephone, d’où les boutons.
Dans le Script proposé sur le site de sarakha6 les accents fonctionnes, il y a une partie UTF8 dans le code.
Encore merci
C’est vrai qu’un radio réveil ca le ferait bien en fait 😀
effectivement dans le cas d’une chambre à coucher , il peut être plus rapide d’appuyer sur un bouton , plutôt que de lancer l’interface.
Pour les accents , j’ai repris mon script , et en effet j’ai trouvé l’option permettant de prendre en charge l’UTF8. Des que j’aurais eu le temps de faire les tests de mettrais à jour le script.
Par curiosité , j’irai voir le script sur le site de sarakha …
Super quand même quand je vois commen j’ai galérer avec l’autre, ici ca passe quand même tout seul ! je n’utilise pas l’interface graphique car tout passe via scenarios par jeedom mais j’y ai jeter un oeil et elle est assez sympa.
Pareil si on peut recupérer quelques boutons ca serait sympa, je pourrais aussi via script http dans jeedom gérer directement la luminosité de l’écran et la ca devient chouette car on pourrai utiliser directement un capteur externe style gateway xiaomi 🙂
En effet , j’ai pensé a mettre un bouton au debut . Mais dans un premier temps je préférais passer via l’interface WEB ou par le biais des script dans jeedom.
D’ou la création des variables , permettant l’affichage ou non du réveil , la luminosité en mode manuel ou auto et encore l’affichage des secondes ( qui peut être énervant dans certaines pieces , de voir les secondes s’engrenaient ).
Salut,
merci pour votre Tuto, au top et bien détaillé, par contre même erreur que DCJona j’ai du supprimer le If ainsi que le else pour pouvoir le compiler.
Juste un problème avec les accents, ils ne n’affichent pas. (éàè° etc..)
Y serais intéressent d’ajouter un bouton ou deux.
1) Un appui : ça diminue l’intensité et désactive le capteur.
2) Deux appui : ça réactive le capteur de luminosité
2) Trois appui : ça coupe l’horloge
3) Quatre appui : ça rallume l’horloge
Merci , pour les infos . Bizarre pour la partie OTA ? Peut être est ce un problème de carte ou de configuration dans le contrôleur IDE Arduino ( j’utilise la version 1.8.5 ) . Ou version de bibliothèque ? A creuser.
Pour les accents , j’ai vu , que les bibliothèques MAX 72xx , gérés les accents , il y a aussi une option dans la bibliothèque Parola qui permet de convertir des caractères pour prendre en charge l’UTF 8 , mais je ne suis pas encore arrivé , à bien exploiter cette fonction . Il faut que je découpe caractère par caractère , les tester un par un , les convertir si besoins , puis les recoller dans une chaine de caractère afin de les afficher. ( pour l’instant ça bug ).
Pour la partie bouton , j’y ai pensé , cela viendra , avec la version 2 , une fois que j’aurais résolu les problèmes d’affichage des accents.
Il est possible que l’erreur vienne de chez moi. Peut être une librairie manquante … Je n’utilise pas la maj via ota donc cela ne me pose vraiment aucun soucis. Le sketch ne sera installé qu’une seule fois de toute façon et ça tourne parfaitement depuis plusieurs jours. Merci beaucoup ça améliore pas mal à mes yeux et ça simplifie la version que le site de sarakha. De plus l’horloge donne un vrai plus au système !
Merci pour votre retour. Si vous voyez des ameliorations à apporter , je suis preneur .
Oula je ne me souviens plus mais de mémoire c’était cette ligne la : if (ArduinoOTA.getCommand() == U_FLASH)
Merci , pour l’info . Apres verification , je n’ai pas trouvé d’erreur , mais le if , peut être supprimé car il permet de detecter si la mise a jour correspond au sketch ou à la memoire interne du Wemos D1.
Si on ne souhaite mettre que le sketch à jour , il suffit d’enlever le if et son else , et d’indiquer uniquement type = « sketch »;
Superbe tuto, c afait longtemps que je galère avec le mqtt et les matrix led pour jeedom et la aucune encombre! ca passe tout seul, juste une erreur de OTA dans mon sketch mais j’ai supprimé car j’utilise pas. Mercii
Merci.
Par contre cela m’intéresserait de savoir quel erreur vous avez sur l’OTA ? Car cela fait plusieurs modules que je monte et je n’ai jamais eu de soucis ?