À quels problèmes répond le programme ?
- Comment mesurer les impulsions qui nous arrivent ?
- Quel type d’information nous envoie-t-on ?
- Comment interpréter ces informations ?
- L’heure reçue est-elle bien l’heure exacte ?
Le but du module Arduino est de transformer une information, que l’on constate par un allumage long ou court de la diode, en heure.
1. Comment mesurer les impulsions qui nous arrivent ?
Après étude du signal reçu grâce à un oscilloscope, on obtient ceci :
Les signaux sont plus ou moins larges (100 ms ou 200 ms). Δt = 1,000 seconde. On constate qu’il y a bien une modification de la tension au début de chaque seconde.
On constate bien qu’il y a à chaque seconde un nouveau signal, long ou court, dont la durée Δt ne varie pas :
On va compter le nombre de 0 et de 1, et on pourra dire si l’impulsion est longue ou si elle est courte. Le premier rôle du programme est donc de déterminer la durée des impulsions qu’il reçoit. Il va mesurer le niveau du signal à une vitesse suffisante. Ainsi, si le courant est en haut (par exemple, si la tension est de 5 Volts), il notera un 1, et s’il est en bas (par exemple, si la tension est nulle), il notera un 0. Ainsi, pour pouvoir distinguer entre 1 et 2 ms, on mesurera la tension toutes les 0,1 ms. Puis, on va regarder où est le courant tous les dixièmes de millisecondes. Comme sur le schéma ci-contre. C’est aussi le principe utilisé par l’oscilloscope et la numérisation de la musique.
2. Quel type d’information nous envoie-t-on ?
Le début de chaque minute est signifié par une absence de signal de l’émetteur, on le remarque sur notre vidéo à la 27ème seconde ou encore sur cette photo :
Le module détecte donc une série trop longue de 0. C’est le début du message codé.
Lorsque le programme reçoit un bit (0 ou un 1), il le note dans une mémoire appelée registre. Le registre sera décalé pour noter le bit suivant . Arrivé au huitième 0 ou 1 reçu, le programme va interpréter (nous expliquerons dans un second temps le sens des 0 et des 1). Après avoir interprété les 8 premiers numéros, il les efface, et fait de la place pour les 8 suivants, qu’il reçoit et interprète, jusqu’à la fin de la minute.
Il est intéressant de se figurer ce décalage des données, puis cette interprétation. On interprète, c’est-à-dire que l’on assigne les huit premiers bits à une variable, on efface, puis on passe à la suite. Cette interprétation est faite par la « librairie », que l’on détaillera plus bas. Ainsi, quand le « niveau supérieur » du programme demande l’heure, la librairie lui fournit les variables qu’elle a trouvées.
Il faut ensuite que le programme soit capable de vérifier l’exactitude des données qu’il reçoit. En effet, dans la transmission par les ondes radio, il peut y avoir un parasite industriel, un bruit, qui fausse l’information. Ainsi, après avoir trouvé l’heure une première fois, le programme la compare à l’information transmise par la minute suivante. Si les deux sont semblables, alors l’heure est juste (car on suppose qu’il ne peut pas y avoir deux fois le même parasite au même instant à exactement une minute d’intervalle). Sinon, il faut supprimer une des deux heures obtenues, et calculer encore une fois l’heure. On compare alors le nouveau résultat avec le précédent enregistré. Si ce sont les mêmes, on affiche, sinon, on recommence.
3. Comment interpréter ces informations ?
L’interprétation des 0 et des 1 reçus
La figure ci-contre montre le déroulement des informations au cours d’une minute pour le code de l’émetteur DCF77
Il existe plusieurs méthode de codage des nombres. DCF77 utilise le code BCD ( binary coded decimal / décimal codé binaire).
L’information horaire et la date sont diffusées au cours des secondes 21 à 58 de chaque minute sous forme de trois groupes de signaux BCD minutes, heure et date. Chacun de ces trois groupes se termine par un signal (un bit) de parité, déterminé à l’émission, de façon à ce que le nombre de 1 à l’intérieur de chaque groupe soit toujours pair. La parité est très utile pour vérifier s’il n’y a pas eu d’erreur pendant la réception et pour ne pas afficher une valeur incorrecte.
Exemple :
À la réception, une vérification de la parité pratiquée sur chaque groupe du message pourra servir à rejeter, comme perturbé, tout message qui
comporterait un nombre impair de « 1 » logiques. Les valeurs numériques qui sont diffusées à un instant donné, sont toujours valables pour la minute qui suit immédiatement celle pendant laquelle la diffusion a lieu.
Ainsi, le bip de parité permet de vérifier si le nombre de signaux envoyés est pair ou impair. Soit n le nombre de signaux envoyés (avec 0 ≤ n ≤ 7, sauf pour les années, avec 0 ≤ n ≤ 8). On cherche à savoir si n ≡ 0 [2] ou si n ≡ 1 [2].
Principe du décodage
Le microprocesseur (Arduino), pour être en mesure de traduire correctement l’information, doit détecter deux types d’information : la durée de chaque « top » et l’absence du « top » indiquant le début de la minute.
Lorsque le microprocesseur distingue la durée des « top » il devient possible de le synchroniser avec les secondes, puis de le synchroniser avec les minutes et le décodage peut commencer.
Description du programme :
Il faut télécharger dans le module Arduino un programme de mesure et de décodage. Notre travail a été facilité puisque des informaticiens ont publié des « bibliothèques ». Ce sont des fonctions préparées qui décodent et renvoient les valeurs souhaitées directement, de façon à pouvoir les afficher sur l’écran, pour ensuite les envoyer à un ordinateur. Les bibliothèques font partie de la programmation de haut niveau. Ainsi, nous chercherons seulement à définir ce dont nous nous servons dans notre travail.
Voici le programme, écrit en C/C++ que nous avons pu lire et télécharger dans le module.
/**************************************** Exemple d'écriture sur le display 12C 96 ****************************************/ #include "DCF77.h" #include "Time.h" #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define DCF_PIN 2 // Connection pin to DCF 77 device #define DCF_INTERRUPT 0 // Interrupt number associated with pin time_t time; DCF77 DCF = DCF77 (DCF_PIN,DCF_INTERRUPT) ; #define OLED_RESET 4 Adafruit_SSD1306 display(OLED_RESET); void setup() { Serial.begin (9600) ; Serial.println ("Initialisation") ; display.begin (SSD1306_SWITCHCAPVCC, 0x3C) ; // initialise le display I2C addr 0x3C (128x32) DCF.Start () ; delay (2000) ; // 2 secondes svp display.clearDisplay () ; // clears the screen and buffer display.setTextSize (2) ; display.setTextColor (WHITE) ; display.setCursor (0,0) ; display.println ("Bonjour!") ; display.display () ; } void loop() { delay (1000) ; display.clearDisplay () ; display.setCursor (5,0) ; time_t DCFtime = DCF.getTime () ; // Nouvelle heure DCF77 valide ? if (DCFtime!=0) { display.println("Time is updated"); setTime(DCFtime); } meinberg_usb() ; digitalClockDisplay(); } void digitalClockDisplay(){ // Affichage de date et heure sur display display.setCursor (0,2) ; display.clearDisplay () ; display.setTextSize (1) ; switch(weekday()) { case 1: display.print ("lundi") ; break ; case 2: display.print ("mardi") ; break ; case 3: display.print ("mercredi") ; break ; case 4: display.print ("jeudi") ; break ; case 5: display.print ("vendredi") ; break ; case 6: display.print ("samedi") ; break ; case 7: display.print ("dimanche") ; break ; } display.print (" ") ; display.print (day()) ; display.print (" ") ; display.print (month()) ; display.print (" ") ; display.print (year()) ; display.println ("") ; display.println ("") ; display.setTextSize (2) ; display.print (" ") ; display.print (hour()) ; display_Digits (minute()) ; display_Digits (second()) ; display.println ("") ; display.display () ; } void display_Digits(int digits){ // Utilitaire d'affichage d'un nombre avec 2 chiffres sur le display display.print (":") ; if (digits < 10) display.print ('0') ; display.print(digits); } void printDigits(int digits){ // Utilitaire d'affichage d'un nombre avec 2 chiffres sur le port serie Serial.print (":") ; if(digits < 10) Serial.print ('0') ; Serial.print (digits) ; } void meinberg_usb() { // digital clock display of the time Serial.print ("D:") ; Serial.print (day()) ; Serial.print (".") ; Serial.print (month()) ; Serial.print (".") ; Serial.print (year()) ; Serial.print (";T:") ; Serial.print (weekday()) ; // Day of week Serial.print (";U:") ; Serial.print (hour()) ; Serial.print (".") ; printDigits (minute()) ; Serial.print (".") ; printDigits (second()) ; // current_bit Serial.println () ; }
Dans ce programme, il y a très peu de traitement ; la majeure partie se résume à l’affichage sur l’écran des valeurs obtenues. Ces valeurs, qui semblent surgir de nulle part, proviennent des bibliothèques « DCF77.h » et « Time.h », qui sont inclues au tout début du programme. On fait appel à ces bibliothèques à trois endroits :
time_t time; DCF77 DCF = DCF77 (DCF_PIN,DCF_INTERRUPT) ;
DCF.Start () ;
time_t DCFtime = DCF.getTime () ;
La première fonction, « time_t time; » fait appel à la bibliothèque « Time.h ». Elle retrouve le temps actuel, qu’elle stocke dans une variable « time », qui est réutilisée dans les fonctions DCF. Voici un morceau de la bibliothèque.
int hour(); // the hour now int hour(time_t t); // the hour for the given time int hourFormat12(); // the hour now in 12 hour format int hourFormat12(time_t t); // the hour for the given time in 12 hour format uint8_t isAM(); // returns true if time now is AM uint8_t isAM(time_t t); // returns true the given time is AM uint8_t isPM(); // returns true if time now is PM uint8_t isPM(time_t t); // returns true the given time is PM int minute(); // the minute now int minute(time_t t); // the minute for the given time int second(); // the second now int second(time_t t); // the second for the given time int day(); // the day now int day(time_t t); // the day for the given time int weekday(); // the weekday now (Sunday is day 1) int weekday(time_t t); // the weekday for the given time int month(); // the month now (Jan is month 1) int month(time_t t); // the month for the given time int year(); // the full four digit year: (2009, 2010 etc) int year(time_t t); // the year for the given time
Toutes les variables (hour, minute, second, day, weekday, month et year) sont renseignées automatiquement par la bibliothèque « time_t ». On voit que ces variables sont réutilisées dans l’affichage du Arduino:
void meinberg_usb() { // digital clock display of the time Serial.print ("D:") ; Serial.print (day()) ; Serial.print (".") ; Serial.print (month()) ; Serial.print (".") ; Serial.print (year()) ; Serial.print (";T:") ; Serial.print (weekday()) ; // Day of week Serial.print (";U:") ; Serial.print (hour()) ; Serial.print (".") ; printDigits (minute()) ; Serial.print (".") ; printDigits (second()) ; // current_bit Serial.println () ; }
La fonction « DCF.Start » pose un autre problème : ce n’est qu’une appellation, c’est-à-dire qu’il n’y a aucune fonction DCF.Start dans la bibliothèque « DCF77.h ». On retrouve DCF.Start dans un autre fichier, le fichier « keywords.txt ». C’est un fichier texte, où l’on retrouve tous les morceaux de commandes qui suivent « DCF. », tel que « getTime », « Start », « day », etc.
La fonction « Start » permet de lancer « l’écoute » du signal radio. Cette information est donnée dans un autre fichier texte:
Start(); // Start listening to DCF77 signal
4. L’heure reçue est-elle bien l’heure exacte ?
Enfin, pour la troisième fonction: time_t DCFtime = DCF.getTime ().
Il y a une vérification de la cohérence entre l’heure donnée par « time_t » et l’heure reçue par « DCF.getTime ». Si la valeur de « DCF.getTime » est nulle, la boucle « void loop() » continue à tourner, jusqu’à ce que la variable « DCF.time » s’initialise. Une fois qu’elle s’est initialisée, le programme peut commencer à régler l’affichage.
La programmation de l’affichage change selon le but souhaité, lors de l’obtention des valeurs. Ainsi, si on veut simplement afficher les valeurs sur un écran LED, on va adapter les espacements et les caractères de séparation des heures, minutes et secondes. Si on veut envoyer toutes ces variables vers un ordinateur, de manière à ce que celui-ci les traite, il faudrait transformer toutes ces données sous forme de paquets. Ensuite, il faut les envoyer par USB, comme on le fait avec l’écran. Les bibliothèques inclues au début du programme, telle que <Adafruit_GFX.h>, sont des bibliothèques d’affichage qui permettent de simplifier, encore une fois, les modifications que devrait effectuer un utilisateur de ce programme lors de l’adaptation de celui-ci au matériel utilisé.