Pour faire suite à mon precedent article , sur la fabrication d’une horloge numérique . Je vais dans cette deuxième partie illustrer comment la rendre plus dynamique et interagir avec Jeedom.
Pour quelques euros de plus ( 2€ pour le module DHT et 2€ pour le module IR ).
Installation d’un capteur de temperature / humidité :
Pour ce projet j’ai utilisé un capteur de type DHT 11.
Pour le branchement , il suffit de relier la sortie signal à une broche d’entrée de l’Arduino .
Dans notre exemple , j’ai choisi la broche digital numero 2 ( D2 ).
Le VCC au 5v de l’arduino , et le GCC à la broche GCC de l’arduino.
Pour utiliser le capteur , on peut utiliser la bibliothèque , mais pour des raisons de place , j’ai préféré utiliser un script minimaliste que j’ai récupéré sur le site carnet du maker .
La lecture de la temperature et de l’humidité , se fait par la’apppel de la fonction : readDHT11(BROCHE_CAPTEUR, &temperature, &humidity);
On intègre ensuite l’affichage de la temperature à la liste des notifications en cours.
Pour afficher les notifications dans l’ordre , j’ai choisi d’utiliser la fonction Switch , qui permet en fonction d’un compteur de faire defiler les différentes notifications.
- Affichage de la date du jour
- Affichage de la temperature / humidité
Ce qui donne le script complet suivant :
// Projet horloge avec Notif // janvier 2018 // Chargement des librairies // gestion affichage matrice #include <MD_Parola.h> #include <MD_MAX72xx.h> // librairie pour horloge #include <DS3231.h> // Parametrage matrice ( Pin Arduino ou est branché la matrice ) #define MAX_DEVICES 4 // ( nombre de matrice ) #define CLK_PIN 10 #define DATA_PIN 12 #define CS_PIN 11 // initialisation de la matrice MD_Parola P = MD_Parola(DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES); // parametrage horloge DS3231 clock; RTCDateTime dt; // Commande a devalider pour reglage horloge premiere fois Manual (YYYY, MM, DD, HH, II, SS //clock.setDateTime(2017, 10, 27, 16, 19, 00); // Capteur temp / hum -- DHT 11 /** Broche "DATA" du capteur */ const byte BROCHE_CAPTEUR = 2; /* Code d'erreur de la fonction readDHT11() */ const byte DHT_SUCCESS = 0; // Pas d'erreur const byte DHT_TIMEOUT_ERROR = 1; // Temps d'attente dépassé const byte DHT_CHECKSUM_ERROR = 2; // Données reçues erronées float temperature, humidity; // variable pour tempo notif long temps; unsigned int tempoNotif = 40; // temps en secondes //variable systemes #define BUF_SIZE 30 char Message[BUF_SIZE] ; char charVal[BUF_SIZE] ; char charVal2[BUF_SIZE]; int cpt=0; void setup() { //init affichage P.begin(); // init horloge clock.begin(); /* Place la broche du capteur DHT en entrée avec pull-up */ pinMode(BROCHE_CAPTEUR, INPUT_PULLUP); } void loop() { dt = clock.getDateTime(); // Affichage d'un texte simple if (P.displayAnimate()) { P.displayText(clock.dateFormat("H:i", dt), PA_CENTER,0,0, PA_PRINT, PA_NO_EFFECT); // notif auto // temp et date if ((millis()-temps)>( tempoNotif*1000L)) { switch(cpt) { case 0 : { P.displayText(Message, PA_LEFT, 40, 1500, PA_SCROLL_LEFT, PA_SCROLL_LEFT); strcpy(Message,clock.dateFormat(" --> d.m.y", dt)); ++cpt; break; } case 1 : { readDHT11(BROCHE_CAPTEUR, &temperature, &humidity); P.displayText(Message, PA_LEFT, 40, 1500, PA_SCROLL_LEFT, PA_SCROLL_LEFT); dtostrf(temperature, 3, 0, charVal); dtostrf(humidity, 3, 0, charVal2); sprintf(Message," --> temp.: %s C Hum.: %s %%",charVal,charVal2); //sprintf(message,"test temp"); //strcpy(curMessage,message); cpt=0; temps=millis(); break; } } } // fin notif } } /** * Lit la température et le taux d'humidité mesuré par un capteur DHT11. * * @param pin Broche sur laquelle est câblée le capteur. * @param temperature Pointeur vers la variable stockant la température. * @param humidity Pointeur vers la variable stockant le taux d'humidité. * @return DHT_SUCCESS si aucune erreur, DHT_TIMEOUT_ERROR en cas de timeout, ou DHT_CHECKSUM_ERROR en cas d'erreur de checksum. */ byte readDHT11(byte pin, float* temperature, float* humidity) { /* Lit le capteur */ byte data[5]; byte ret = readDHTxx(pin, data, 18, 1000); /* Détecte et retourne les erreurs de communication */ if (ret != DHT_SUCCESS) return ret; /* Calcul la vraie valeur de la température et de l'humidité */ *humidity = data[0]; *temperature = data[2]; /* Ok */ return DHT_SUCCESS; } /** * Fonction bas niveau permettant de lire la température et le taux d'humidité (en valeurs brutes) mesuré par un capteur DHTxx. */ byte readDHTxx(byte pin, byte* data, unsigned long start_time, unsigned long timeout) { data[0] = data[1] = data[2] = data[3] = data[4] = 0; // start_time est en millisecondes // timeout est en microsecondes /* Conversion du numéro de broche Arduino en ports / masque binaire "bas niveau" */ uint8_t bit = digitalPinToBitMask(pin); uint8_t port = digitalPinToPort(pin); volatile uint8_t *ddr = portModeRegister(port); // Registre MODE (INPUT / OUTPUT) volatile uint8_t *out = portOutputRegister(port); // Registre OUT (écriture) volatile uint8_t *in = portInputRegister(port); // Registre IN (lecture) /* Conversion du temps de timeout en nombre de cycles processeur */ unsigned long max_cycles = microsecondsToClockCycles(timeout); /* Evite les problèmes de pull-up */ *out |= bit; // PULLUP *ddr &= ~bit; // INPUT delay(100); // Laisse le temps à la résistance de pullup de mettre la ligne de données à HIGH /* Réveil du capteur */ *ddr |= bit; // OUTPUT *out &= ~bit; // LOW delay(start_time); // Temps d'attente à LOW causant le réveil du capteur // N.B. Il est impossible d'utilise delayMicroseconds() ici car un délai // de plus de 16 millisecondes ne donne pas un timing assez précis. /* Portion de code critique - pas d'interruptions possibles */ noInterrupts(); /* Passage en écoute */ *out |= bit; // PULLUP delayMicroseconds(40); *ddr &= ~bit; // INPUT /* Attente de la réponse du capteur */ timeout = 0; while(!(*in & bit)) { /* Attente d'un état LOW */ if (++timeout == max_cycles) { interrupts(); return DHT_TIMEOUT_ERROR; } } timeout = 0; while(*in & bit) { /* Attente d'un état HIGH */ if (++timeout == max_cycles) { interrupts(); return DHT_TIMEOUT_ERROR; } } /* Lecture des données du capteur (40 bits) */ for (byte i = 0; i < 40; ++i) { /* Attente d'un état LOW */ unsigned long cycles_low = 0; while(!(*in & bit)) { if (++cycles_low == max_cycles) { interrupts(); return DHT_TIMEOUT_ERROR; } } /* Attente d'un état HIGH */ unsigned long cycles_high = 0; while(*in & bit) { if (++cycles_high == max_cycles) { interrupts(); return DHT_TIMEOUT_ERROR; } } /* Si le temps haut est supérieur au temps bas c'est un "1", sinon c'est un "0" */ data[i / 8] <<= 1; if (cycles_high > cycles_low) { data[i / 8] |= 1; } } /* Fin de la portion de code critique */ interrupts(); /* * Format des données : * [1, 0] = humidité en % * [3, 2] = température en degrés Celsius * [4] = checksum (humidité + température) */ /* Vérifie la checksum */ byte checksum = (data[0] + data[1] + data[2] + data[3]) & 0xff; if (data[4] != checksum) return DHT_CHECKSUM_ERROR; /* Erreur de checksum */ else return DHT_SUCCESS; /* Pas d'erreur */ }
La fonction dtostrf(temperature, 3, 0, charVal); permet de convertir une valeur de type float ( temperature ) , en une chaine de caractère ( charval ) autorisant 3 chiffres maximum avant la virgule et zero décimale .
[adsense]Un peu d’interaction …
Comme expliqué plus haut , le but est de rendre cette horloge un peu plus dynamique . Je l’ai conçu dans le but de la mettre dans la salle de jeux des enfants , qui se trouve à l’étage. J’aimerais , pouvoir envoyer une notification afin de les faire descendre quand l’heure du repas ou du gouter arrive.
La salle de jeux est équipée , d’un module Orvibo , afin de pouvoir piloter la TV à distance. J’ai donc décider d’intégrer un module IR à mon horloge , afin de pouvoir déclencher à distance des messages prédéfinis.
Pour cela j’utilise le module IR suivant :
il est equipé de trois broche ( GND , VCC et S pour Signal )
Selon les modèles , les broches sont représentés par G ( gnd ) ,R ( 5v) et Y(S) ou un moins du coté de la broche GND et un S du coté du signal.
Une fois les broches reperés , il suffit de le brancher a l’Arduino sur une Entrée Digital ( ici j’ai choisi la numero 7 ) . si le capteur est bien branché , la led clignote en rouge à chaque reception d’un code infra rouge.
Pour tester votre module IR , et ainsi repérer les différents codes à utiliser , ci dessous un sketch , juste pour contrôler les codes reçus et les afficher dans le moniteur série.
#include "IRremote.h" int receiver = 7; // Singnal PIN du module IR /*-----( Declare objects )-----*/ IRrecv irrecv(receiver); // create instance of 'irrecv' decode_results results; // create instance of 'decode_results' /*-----( Function )-----*/ void translateIR() // Actions en fonction des codes reçus { switch(results.value) { case 0xFFA25D: Serial.println("POWER"); break; case 4001918335: Serial.println("NOTIF 1"); break; case 1255008989: Serial.println("NOTIF 2"); break; case 3533356899: Serial.println("NOTIF 3"); break; case 0xFFFFFFFF: Serial.println(" REPEAT");break; default: Serial.println(" Valeur bouton non reconnu "); }// End Case delay(500); // Do not get immediate repeat } //END translateIR void setup() /*----( SETUP: RUNS ONCE )----*/ { Serial.begin(9600); Serial.println("Module IR : Affichage des valeurs Boutons"); irrecv.enableIRIn(); // Init. du module IR }/*--(end setup )---*/ // Boucle principale void loop() { if (irrecv.decode(&results)) // have we received an IR signal? { translateIR(); Serial.println(results.value); irrecv.resume(); // receive the next value } }
Comment j’ai procédé :
- Tout d’abord , j’ai enregistré dans mon Orvibo , 4 codes d’une télécommande dont je ne me servais pas.
- Puis j’ai installé mon Orvibo face à mon Arduino , avec ce sketch installé .
- J’ai exécuté les 4 codes depuis l’orvibo et les 4 valeurs de ces codes ont été listés dans le Moniteur Série.
- Afin d’être sur que le bon code soit reconnu à chaque appuie , j’ai ensuite ajouté une ligne de code à la fonction case :
case 4001918335: Serial.println(« NOTIF 1« ); break;
A chaque envoie du meme code , avec l’ORVIBO , mon module reconnaissait bien ce code.
Une fois mes codes en mains , il ne reste plus qu’a finaliser mon programme.
Code source complet :
// Projet horloge avec Notif // janvier 2018 // Chargement des librairies // gestion affichage matrice #include <MD_Parola.h> #include <MD_MAX72xx.h> // librairie pour horloge #include <DS3231.h> // librairie infrarouge #include "IRremote.h" // Parametrage matrice ( Pin Arduino ou est branché la matrice ) #define MAX_DEVICES 4 // ( nombre de matrice ) #define CLK_PIN 10 #define DATA_PIN 12 #define CS_PIN 11 // initialisation de la matrice MD_Parola P = MD_Parola(DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES); uint8_t scrollSpeed = 30; // default frame delay value textEffect_t scrollEffect = PA_SCROLL_LEFT; // parametrage horloge DS3231 clock; RTCDateTime dt; // Commande a devalider pour reglage horloge premiere fois Manual (YYYY, MM, DD, HH, II, SS //clock.setDateTime(2017, 10, 27, 16, 19, 00); // Capteur temp / hum -- DHT 11 /** Broche "DATA" du capteur */ const byte BROCHE_CAPTEUR = 2; /* Code d'erreur de la fonction readDHT11() */ const byte DHT_SUCCESS = 0; // Pas d'erreur const byte DHT_TIMEOUT_ERROR = 1; // Temps d'attente dépassé const byte DHT_CHECKSUM_ERROR = 2; // Données reçues erronées float temperature, humidity; // Signal Pin of IR receiver to Arduino Digital Pin 11 int receiver = 7; /*-----( Declare objects )-----*/ IRrecv irrecv(receiver); // create instance of 'irrecv' decode_results results; //LED int LED = 3; // variable pour tempo notif long temps; unsigned int tempoNotif = 600; // temps en secondes //variable systemes #define BUF_SIZE 30 char Notif[BUF_SIZE]; char charVal[BUF_SIZE] ; char charVal2[BUF_SIZE]; int cpt=0; int cpta=0; boolean Alert=false; void setup() { //init affichage P.begin(); // init horloge clock.begin(); /* Place la broche du capteur DHT en entrée avec pull-up */ pinMode(BROCHE_CAPTEUR, INPUT_PULLUP); // init recepteur IR irrecv.enableIRIn(); //init LED pinMode(LED, OUTPUT); // affichage Programme chargé P.print("Ready ..."); delay(1000); } void loop() { dt = clock.getDateTime(); // infrarouge if (irrecv.decode(&results)) // have we received an IR signal? { switch(results.value) { case 4001918335: // Notif1 { Alert=true; sprintf(Notif,"A Table !"); break; } case 4039382595: // Notif 2 { Alert=true; sprintf(Notif,"C'est l'heure de se coucher !"); break; } case 1110255125: // Notif 3 { Alert=true; sprintf(Notif,"Tout le monde descend !!"); break; } case 1386468383: //notif 4 { Alert=true; sprintf(Notif,"On Sonne au Portail !!!"); break; } case 2538093563: break; //Notif 5 case 3238126971: break; //Notif 6 case 0xFFA25D: { Alert=true; sprintf(Notif,"Test Notif !!!"); break; //Notif 6 } } irrecv.resume(); } if (P.displayAnimate()) { if (Alert) { digitalWrite(LED, HIGH); P.setIntensity(MAX_INTENSITY); // intensité au max switch(cpta) { case 0 : { P.displayText("ALERT", PA_LEFT, scrollSpeed, 1000, PA_OPENING_CURSOR, PA_BLINDS); ++cpta; break; } case 1 : { P.displayText(Notif, PA_LEFT, scrollSpeed,2000, PA_SCROLL_LEFT, PA_SCROLL_LEFT); //strcpy(Message,"test message"); ++cpta; break; } case 2 : { P.displayText(Notif, PA_LEFT, scrollSpeed,1000, PA_SCROLL_LEFT, PA_CLOSING_CURSOR); cpta=0; Alert=false; break; } } } else { digitalWrite(LED, LOW); if (dt.hour >6 and dt.hour<23) { P.setIntensity(3); // Affichage heure strcpy(Notif,clock.dateFormat("H:i", dt)); P.displayText(Notif, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); //notif auto if ((millis()-temps)>(tempoNotif*1000L)) { switch(cpt) { case 0 : { P.displayText(Notif, PA_LEFT, scrollSpeed, 1500, scrollEffect, scrollEffect); strcpy(Notif,clock.dateFormat(" l, d F Y", dt)); ++cpt; break; } case 1 : { readDHT11(BROCHE_CAPTEUR, &temperature, &humidity); P.displayText(Notif, PA_LEFT, scrollSpeed, 1500, scrollEffect, scrollEffect); dtostrf(temperature, 3, 0, charVal); dtostrf(humidity, 3, 0, charVal2); sprintf(Notif," --> T:%s C / H:%s %%",charVal,charVal2); cpt=0; temps=millis(); break; } } } } // fin plage horaire else { P.setIntensity(1); P.print(""); } } // fin else } } /** * Lit la température et le taux d'humidité mesuré par un capteur DHT11. * * @param pin Broche sur laquelle est câblée le capteur. * @param temperature Pointeur vers la variable stockant la température. * @param humidity Pointeur vers la variable stockant le taux d'humidité. * @return DHT_SUCCESS si aucune erreur, DHT_TIMEOUT_ERROR en cas de timeout, ou DHT_CHECKSUM_ERROR en cas d'erreur de checksum. */ byte readDHT11(byte pin, float* temperature, float* humidity) { /* Lit le capteur */ byte data[5]; byte ret = readDHTxx(pin, data, 18, 1000); /* Détecte et retourne les erreurs de communication */ if (ret != DHT_SUCCESS) return ret; /* Calcul la vraie valeur de la température et de l'humidité */ *humidity = data[0]; *temperature = data[2]; /* Ok */ return DHT_SUCCESS; } /** * Fonction bas niveau permettant de lire la température et le taux d'humidité (en valeurs brutes) mesuré par un capteur DHTxx. */ byte readDHTxx(byte pin, byte* data, unsigned long start_time, unsigned long timeout) { data[0] = data[1] = data[2] = data[3] = data[4] = 0; // start_time est en millisecondes // timeout est en microsecondes /* Conversion du numéro de broche Arduino en ports / masque binaire "bas niveau" */ uint8_t bit = digitalPinToBitMask(pin); uint8_t port = digitalPinToPort(pin); volatile uint8_t *ddr = portModeRegister(port); // Registre MODE (INPUT / OUTPUT) volatile uint8_t *out = portOutputRegister(port); // Registre OUT (écriture) volatile uint8_t *in = portInputRegister(port); // Registre IN (lecture) /* Conversion du temps de timeout en nombre de cycles processeur */ unsigned long max_cycles = microsecondsToClockCycles(timeout); /* Evite les problèmes de pull-up */ *out |= bit; // PULLUP *ddr &= ~bit; // INPUT delay(100); // Laisse le temps à la résistance de pullup de mettre la ligne de données à HIGH /* Réveil du capteur */ *ddr |= bit; // OUTPUT *out &= ~bit; // LOW delay(start_time); // Temps d'attente à LOW causant le réveil du capteur // N.B. Il est impossible d'utilise delayMicroseconds() ici car un délai // de plus de 16 millisecondes ne donne pas un timing assez précis. /* Portion de code critique - pas d'interruptions possibles */ noInterrupts(); /* Passage en écoute */ *out |= bit; // PULLUP delayMicroseconds(40); *ddr &= ~bit; // INPUT /* Attente de la réponse du capteur */ timeout = 0; while(!(*in & bit)) { /* Attente d'un état LOW */ if (++timeout == max_cycles) { interrupts(); return DHT_TIMEOUT_ERROR; } } timeout = 0; while(*in & bit) { /* Attente d'un état HIGH */ if (++timeout == max_cycles) { interrupts(); return DHT_TIMEOUT_ERROR; } } /* Lecture des données du capteur (40 bits) */ for (byte i = 0; i < 40; ++i) { /* Attente d'un état LOW */ unsigned long cycles_low = 0; while(!(*in & bit)) { if (++cycles_low == max_cycles) { interrupts(); return DHT_TIMEOUT_ERROR; } } /* Attente d'un état HIGH */ unsigned long cycles_high = 0; while(*in & bit) { if (++cycles_high == max_cycles) { interrupts(); return DHT_TIMEOUT_ERROR; } } /* Si le temps haut est supérieur au temps bas c'est un "1", sinon c'est un "0" */ data[i / 8] <<= 1; if (cycles_high > cycles_low) { data[i / 8] |= 1; } } /* Fin de la portion de code critique */ interrupts(); /* * Format des données : * [1, 0] = humidité en % * [3, 2] = température en degrés Celsius * [4] = checksum (humidité + température) */ /* Vérifie la checksum */ byte checksum = (data[0] + data[1] + data[2] + data[3]) & 0xff; if (data[4] != checksum) return DHT_CHECKSUM_ERROR; /* Erreur de checksum */ else return DHT_SUCCESS; /* Pas d'erreur */ }
J’ai opté pour afficher la date sous la forme « vendredi 3 Octobre 2017 » , la date va s’afficher en anglais , pour l’avoir en français , j’ai directement modifié le fichier DS3231.cpp dans la bibliothèque . Ce n’est pas la meilleur méthode , car lors de la mise a jour de la bibliothèque les noms en anglais vont revenir , mais mon code prenant 99,9 % de la memoire , je n’ai pas pu rajouter une fonction permettant de convertir directement depuis mon sketch.
Afin que les notifications soient plus visibles , je met l’intensité à fond , par la commande :
P.setIntensity(MAX_INTENSITY);
J’ai ajouté , sur le dessus de l’horloge une LED Bleue ( relié à la sortie D3 ) , afin que la notification soit plus visible.
Pour allumer la led , il suffit d’entrer la commande :
digitalWrite(LED, HIGH);
Integration dans Jeedom
Une fois en place , dans la pièce choisi , il ne reste plus qu’a intégrer ses notifications dans divers scenarios.
Tout d’abord , nous allons créer un virtuel , reprenant les différentes notifications déclenché par les commandes de l’Orvibo.
Ces notifications peuvent être déclenché manuellement , ou via l’appel de scenario.
Chaque commande est lié à une commande correspondante au module Orvibo .
Exemple de scenario possible :
- L’appuie sur un bouton dans la cuisine , déclenche la notification « A table »
- Une sonnerie au portail , déclenche la notif « On sonne »
- A l’heure du coucher la notification « Bonne Nuit »
- etc …..