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 …..
