Aller au contenu

Module DS3231 : comment ajouter une horloge temps réel (RTC) à ses projets arduino ? (tutorial avec exemples de code en I²C)

Tutorial DS3231 arduino horloge RTC, avec caractéristiques et adressage I²C, raccordement et pilotage avec microcontrôleur électronique

Que diriez-vous d’ajouter un module d’horloge « temps réel » à votre Arduino Uno, Nano, ou Mega (ou à toute autre carte ou tout autre microcontrôleur dépourvu de cela), afin d’avoir accès à la date et heure courante, à tout moment ?

Si ça vous dit, laissez-moi alors vous présenter le module DS3231, une petite horloge RTC (« Real Time Clock »), avec ses caractéristiques, son brochage, son schéma interne, et son câblage sur le bus I2C. Et comme toujours, le tout accompagné de plusieurs exemples de code arduino, pour passer à la pratique ! Alors en avant 😉

Ce contenu vous plaît ? Alors abonnez-vous à la Newsletter pour ne rien louper !

Cet article est essentiellement une introduction au module DS 3231. Il se limitera donc aux fonctionnalités de base de cette puce montée sur mini-plaquette PCB, et son pilotage via Arduino. Du coup, nous n’aborderons donc pas ici la partie lecture/écriture en mémoire EEPROM (puce externe au DS23231, mais qui est fournie « en bonus » sur le module), afin de rester focalisé sur « l’essentiel » (nota : plus tard, je vous ferai un article à part, dédié aux EEPROM séries externes, pour compléter cette partie). Sur ce, bonne lecture à vous !

ModuleDescriptionLien
Module DS3231 horloge temps réel RTC avec bornier de raccordement format header mâle, vue de face carte électronique avec pile de sauvegardeModule DS3231 monté sur PCB
(sans pile de sauvegarde fournie)

Qu’est-ce qu’une horloge RTC ? Et à quoi ça sert ?

Pour faire simple, une horloge temps réel (ou RTC, en anglais) est tout simplement un compteur date/heure. Et ces date et heure sont consultables et réglables à tout moment, au besoin. Ainsi, si vous souhaitez que votre horloge RTC vous donne la même date et heure que votre PC ou smartphone, il faudra alors la régler (l’initialiser) avec les mêmes date et heure, précisément, afin qu’elle soit synchrone avec eux. À noter que cette action peut être « automatisée » si par exemple vous avez une connexion WiFi.

Mais attention, toutes les horloges temps réel ne se valent pas. En effet, certaines RTC sont vraiment « très précises » (comme c’est le cas du modèle DS3231 que je vais vous présenter ici, qui dispose d’un oscillateur à quartz compensé en température), tandis que d’autres dérivent pas mal dans le temps (comme c’est le cas du temps DS1307, par exemple, qui peut prendre jusqu’à plusieurs minutes de retard parfois, et ce, au bout d’un mois de fonctionnement seulement …).

Bien sûr, la solution à ces « dérives temporelles » est toute trouvée pour les appareils connectés à internet (microcontrôleur avec module WiFi, par exemple). Car ils peuvent se remettre à l’heure à tout moment, en consultant les date et heure « exactes », via un réseau NTP (Network Time Protocol). Par contre, pour tous les appareils non connectés au réseau, la « remise à l’heure » doit être faite manuellement. Du coup, dans ce cas, mieux vaut opter pour une horloge temps réel qui soit la plus précise possible, comme c’est le cas avec du DS 3231, présenté ci-après !

Remarque : les horloges temps réels sont quasiment toujours accompagnées d’une « pile de secours » (format CR2032, par exemple), afin que leur « comptage interne » continue, en cas de coupure de l’alimentation principale. Ainsi, « l’heure reste toujours à l’heure », enfin … tant que la pile de secours a suffisamment d’énergie, pour faire tourner l’horloge RTC 😉

Du reste, certains d’entre vous se demanderont peut-être à quoi sert une horloge RTC, en pratique ? En fait, elle sert le plus souvent à horodater des évènements. C’est pourquoi on la retrouve généralement dans :

  • les enregistreur de données (datalogger), qui enregistrent des mesures de température, pression, tension, courant, ou autre, dans le temps
  • certains systèmes d’alarme (pour mémoriser tout évènement qui a eu lieu dans le temps, remonté par les détecteurs)
  • certains systèmes automatisés (pour déclencher des actions à des moments précis)

À présent, je vous propose de découvrir cette « fameuse » horloge RTC à la fois peu chère et « hyper » stable dans le temps, à savoir : le module DS 3231 !

Découverte du module DS3231 (schéma, pinout, caractéristiques, …)

Avant de vous présenter ce module, juste un mot, quant à son nom. En fait, le terme « DS3231 » provient du nom du circuit intégré « principal », qui équipe ce module. Et par « abus de langage », DS 3231 désigne au final aussi bien cette puce électronique, que le module dans son ensemble.

Au passage, cette puce a été conçue par Maxim Intergrated (entreprise qui a « récemment » été rachetée par Analog Devices, en 2020/2021). Pour ceux que ça intéresse, le datasheet du circuit intégré DS3231 est notamment accessible ici : https://www.analog.com/media/en/technical-documentation/data-sheets/ds3231.pdf

Vue d’ensemble de la plaquette, avec son brochage (DS3231 pinout)

Pour commencer, voici une vue d’ensemble du module DS3231, de face (côté puce) et de dos (côté pile) :

DS3231 pinout face avant et arrière, brochage de la mini carte PCB avec pile et eeprom intégré, sortie 32K SQW puis SCL et SDA pour i²c com

Vous remarquerez que tous les composants électroniques sont positionnés sur « le dessus », et que seule une pile, au format CR2032, figure « dessous ».

Au niveau du pinout du DS3231 (son brochage, donc), on retrouve :

  • la broche « 32K » : c’est une sortie, qui lorsqu’elle est active, délivre un signal carré, de fréquence 32 KHz. Celle-ci permet de vérifier le « bon fonctionnement » de l’oscillateur interne du DS 3231, en extériorisant sa fréquence d’horloge
  • la broche « SQW/INT » : c’est une sortie à double fonction (contrôlé par le bit « INTCN » du registre de contrôle 0x0E du DS3231). Elle peut :
    • soit sortir un signal d’interruption, actif à l’état bas
    • soit sortir un signal carré, de fréquence 1 Hz, 4000 Hz, 8000 Hz, ou 32000 Hz
    • soit être désactivée
  • la broche « SCL » : c’est une entrée qui recevra l’horloge I2C (provenant de votre Arduino, par exemple)
  • la broche « SDA » : c’est une ligne bidirectionnelle, sur laquelle transitent des données, suivant le protocole I2C
  • les broches « VCC » et « GND » : c’est par là qu’on alimente la plaquette (2,7 à 5,5 volts, pour ce module)

À noter que les broches figurant « en doublon » sur la plaquette (SCL / SDA / VCC / GND) sont juste là afin d’assurer le « report » de ces lignes (très utile si par exemple vous souhaitez raccorder un autre module I2C, à la suite de celui-ci ; en anglais, on parle de « daisy chain », pour désigner cela).

Remarque : les lignes « 32K » et « SQW/INT » sont des sorties à drain ouvert ; il est donc nécessaire de mettre en place des pull-up externes. Toutefois, cela est déjà inclus sur le module DS3231 ; il n’est donc pas nécessaire d’en rajouter ! Par ailleurs, si vous n’avez pas besoin de ces sorties, laissez les simplement débranchées 😉

Le schéma interne du module (DS3231 schematic)

À présent, entrons un peu plus dans le détail, et voyons le schéma interne du module DS3231 :

DS3231 schematic interne module PCB, contenant schéma électronique du circuit imprimé équipé d'une mémoire EEPROM et broches de connection

Ici, on retrouve essentiellement :

  • notre « fameuse » puce DS 3231, notée U1 (celle qui donne son nom au module, par « abus de langage »)
  • une mémoire EEPROM série, modèle AT 24C32, notée U2 ; c’est en fait une pièce « bonus », si je puis dire, que vous pouvez soit totalement ignorer, soit utiliser pour y stocker des données, selon vos besoins. Mais pour être bien clair, cet élément est indépendant du reste, et n’influe en rien sur le fonctionnement du DS 3231, en lui-même.
  • des réseaux de résistances pull-up de 4,7 Kohm, notés RP1 et RP2 (pour maintenir au repos à l’état haut les lignes A0, A1, A2, 32K, SQW, SCL, et SDA)
  • un sélecteur d’adresse EEPROM (A0/A1/A2), sous forme de pastilles pouvant recevoir d’éventuels « ponts de soudure », pour sélectionner l’adresse I²C qui nous convient (remarque : cela ne concerne que l’EEPROM, et non la puce DS3231, qui elle, a une adresse fixe)
  • un « circuit de charge », constitué d’une résistance de 200 ohms en série avec une diode 1N414. Ce circuit permet d’alimenter l’entrée « batterie de secours », dont le relais est pris par la « pile de secours », en cas de coupure d’alimentation
  • une pile de secours, dont la tension doit être comprise entre 2,3 et 5,5 volts (ce qui est donc idéal si vous utilisez une pile CR2032 faisant 3 volts, ou une pile LIR2032 de 3,6 volts)
  • une LED signalant la présence de l’alimentation +Vcc, sur la platine (avec résistance de limitation de courant de 1 Kohm)
  • des condensateurs de découplage (100 nF et 1 µF)
  • et des broches pour « header », permettant d’accéder aux différentes lignes (32K, SQW/INT, SCL, SDA, VCC, et GND) via de simple fils dupont

Remarque très importante : la batterie de secours de ce module est branchée sur un circuit de charge. Or, comme vous le savez, on ne recharge JAMAIS une pile non rechargeable. Perso, je trouve que ça ressemble à un défaut de conception, que l’on retrouve pourtant sur toutes les plaquettes (ou tout du moins, toutes celles que j’ai rencontré jusqu’à présent). Du coup, de mon côté, j’ai dessoudé la résistance de 200 ohms (on aurait pu dessouder la diode 1N4148 à la place, ce qui serait revenu au même), afin de ne pas exposer la pile CR2032 (non rechargeable) à une tension supérieure à la sienne. À noter qu’une alternative serait de remplacer cette pile CR 2032 par un accu rechargeable LIR 2032, mais là aussi, sans dispositif de protection contre les surcharges, j’aurais tendance à vous le déconseiller.

Les différents composants/blocs, figurant sur le PCB

Pour ceux qui aiment bien repérer les choses avant tout, voici où figurent « chacun » des composants vus précédemment, sur la plaquette PCB du module DS 3231 :

Puce DS3231
Puce DS 3231 sur mini plaquette pcb, mise en avant du circuit intégré soudé sur ses headers prêt à raccorder sur arduino ou raspberry
La puce DS3231 figure au centre du module. Son oscillateur est interne, c’est pourquoi vous ne trouverez aucun quartz externe, par exemple. Pour info, son oscillateur interne est de type TCXO, c’est à dire que sa fréquence est compensée en température. Ceci fait que cette fréquence est particulièrement stable, et précise (ici à 32 kHz), avec un glissement de « seulement » ± 2 minutes par an.
Eeprom AT24C32
Eeprom AT 24C32 soudée sur module ds3231, empreinte du circuit intégré en bas du circuit imprimé de la mini plaquette prête à l'emploi
L’EEPROM modèle AT 24C32, est un « bonus » intégré à la carte, fourni « en extra ». Il n’est donc pas obligé de s’en servir, à moins d’en avoir besoin. Cette mémoire peut servir au stockage de données, de manière non volatile (avec 1 million de cycles d’écriture possible). Sa capacité est de 32 Kbits, soit 4 Ko. Son adresse i2C est configurable via les couples de pastilles A0, A1, et A2 (nota : je détaillerai cet adressage au dernier chapitre, pour ceux que ça intéresse).
Connecteurs de carte
Connecteurs carte horloge RTC 32K SQW et autre, headers mâles pour branchement de fils dupont du DS 3231 à l'arduino ou au raspberry pi pico
Des connecteurs ou pastilles (headers) figurent à droite et à gauche du module. Les lignes SCL, SDA, VCC, et GND de droite sont directement reliées à celles de gauche ; elles ne sont là que pour faciliter le branchement de modules I2C en cascade (ce qu’on appelle le « daisy chaining », en anglais). À noter que les bornes de gauche sont « idéales » pour le branchement de fils dupont, pour le câblage à un Arduino ou à un Raspberry, par exemple. Par contre, les pastilles de droite sont « à souder ».
Circuit de charge
Circuit de charge horloge RTC d'origine, avec pile CR2032 ou rechargeable LIR 2032, pour garder en mémoire date et heure en temps réel dans puce
Le module est équipé d’un « circuit de charge rudimentaire », composé d’une simple résistance de limitation de courant (200 ohms), et d’une diode anti-retour (modèle 1N4148). Comme vu au chapitre précédent, j’aurais tendance à dessouder l’un ou l’autre de ces composants, afin de ne pas risquer d’endommager la pile/batterie de secours. En effet, si vous utilisez une pile non rechargeable, vous savez certainement qu’on ne doit jamais l’exposer à une tension supérieure à elle (sans quoi vous risqueriez de provoquer des dommages) ; de même, si vous utilisez un accu rechargeable type lithium sans dispositif de fin de charge, vous risquez fort de la surcharger, jusqu’à l’endommager (sans parler des conséquences désastreuses qui peuvent s’en suivre).
Pile de secours
Pile de secours CR 2032 installé au dos du DS 3231, pour garder horloge temps réel active, avec date et heure RTC dans circuit intégré électronique
Un support de pile « de type CR2032 » (20mm de diamètre, 3,2mm d’épaisseur) figure au dos du module. Vous pouvez y insérer une pile, un accu, ou rien du tout, selon si vous souhaitez que l’horloge continue « à tourner » ou pas, lors d’une coupure d’alimentation. Le type de pile/accu que vous pourrez loger ici est un modèle « CR2032 » pour une pile non rechargeable de 3 volts, et un modèle « LIR2032 » pour un accu rechargeable de 3,6 volts.
Composants « annexes »
Résistances pull-up de l'horloge temps réel ds3231, avec condensateurs montés en surface et led témoin d'alimentation, sur kit module soudé
Quelques composants passifs figurent sur la partie « gauche » du module. De haut en bas, on retrouve :
– une LED indiquant si oui ou non le module est alimenté
– une résistance de limitation de courant (1 Kohm), pour la LED
– deux condensateurs, de respectivement 100 nF et 1 µF, assurant le découplage des circuits intégrés
– et deux réseaux de quatre résistances, faisant 4,7 Kohm chacune, servant de pull-up aux lignes SDA, SCL, SQW, 32K, A0, A1, et A2 (eh oui, on se sert que de 7 des 8 résistances présentes !)

Les tensions d’alimentation et de communication I2C

Basiquement, le module DS3231 doit être alimenté avec une tension comprise entre 2,7 et 5,5 volts (courant continu). Pour info, cette plage de tension est tout simplement conditionnée par les circuits intégrés présents sur ce module, à savoir :

  • le circuit intégré DS 3231 en lui-même, qui requiert une tension comprise entre 2,3 et 5,5 Vdc (valeurs « recommandées » par le fabricant)
  • et la mémoire EEPROM AT24C32, qui requiert une tension comprise entre 2,7 et 5,5 Vdc (dans le cas de la famille 24C32 « la plus défavorable »)

Concernant les signaux de communication, quant à eux, ils devront se faire à la même tension ou presque, que celle d’alimentation (donc 0V ou +Vcc). Ce module est donc parfaitement adapté à une utilisation avec un Arduino ou un Raspberry, en s’alimentant/communiquant en +3,3V ou +5V.

Remarque : la plage de tension recommandée pour la pile de secours (sur l’entrée Vbatt de la puce DS3231, donc) va de 2,3 à 5,5V. C’est donc idéal pour l’emploi d’une pile CR 2032 (faisant 3V au nominal) ou d’un accu LIR 2032 (faisant 3,6V au nominal).

La consommation en courant de l’horloge en temps réel

Lorsque l’horloge est alimentée via les broches +Vcc/GND, celle-ci consomme globalement :

  • 170 µA max au repos (sans communication I²C, donc)
  • 300 µA max lorsque une communication i2c a lieu (quand votre microcontrôleur interroge le DS3231, par exemple)
  • et ponctuellement jusqu’à 650 µA, lorsque la puce DS 3231 compense en température son oscillateur interne (pour « garantir » du 32 kHz le plus stable et précis possible)

Et en cas de coupure d’alimentation Vcc, la pile de secours sera alors débitée de :

  • 3,5 µA max au repos (sans communication i2c, bien entendu)
  • et ponctuellement 650 µA max, si le DS3231 compense son cristal interne en température, pour ajuster sa fréquence de référence 32 kHz interne (à raison d’une correction toutes les 64 secondes, au besoin)

À noter que les « 3,5 µA » de consommation (donnés par le fabricant) incluent, de manière moyennée, ces cycles de compensation en température. Vous pouvez donc partir sur cette valeur de consommation en courant, pour estimer la durée de vie de votre pile de secours.

Exemple de calcul de durée de vie d’une pile de secours CR 2032 (pour que son horloge interne continue à tourner, sans alimentation principale aucune) :

  • si vous prenez une pile CR2032 de 3V ayant par exemple une capacité de 210 mAh (soit 210000 µAh)
  • en sachant que le DS3231 soutirera en moyenne 3,5 µA sur cette pile (si aucune alimentation « principale » Vcc est fournie à la puce)
  • alors la durée de vie théorique de la pile (approximative, bien entendu) sera de 210000/3,5/24/365 = 6,85 ans (soit 6 ans et 10 mois environ … en théorie !)

En pratique, bien sûr, il peut y avoir des écarts plus ou moins conséquents, selon des conditions d’utilisation 😉

Précision de l’horloge, et dérive dans le temps

Juste un mot sur la précision de cette horloge RTC DS 3231. En fait, celle-ci sera « juste » à environ ± 2 minutes par an. Vous risquez donc possiblement d’avoir un petit écart entre cette horloge temps réelle, et vos appareils connectés (PC, smartphone, montre, ou tout autre objet connecté sur le net) ; car ces derniers se synchronisent (se remettent à jour) régulièrement, au besoin, via les « serveurs temps » disponibles sur le web (serveurs NTP).

Du coup, vous devrez soit remettre à jour les date et heure de votre horloge temps réel de temps en temps (1 fois par an, par exemple), soit faire appel à un objet connecté pour ce faire (via un ESP8266, ESP32, Raspberry Pico W, ou autre, connecté à internet).

Circuit de charge de la pile de secours

Étant donné qu’il ne faut jamais essayer de recharger une pile non rechargeable (telle que la CR2032), et qu’il ne faut jamais laisser un accu lithium (tel que le LIR2032) branché en permanence sur un dispositif de charge, sans dispositif de coupure en fin de charge, perso je désactive systématiquement le circuit de charge des horloges DS3231, pour ne pas risquer d’endommager la pile/accu de secours.

Pour rappel, le circuit de charge est constitué d’une résistance de 200 ohms, en série avec une diode 1N4148. Il suffit donc de retirer l’un ou l’autre de ces deux composants, pour rendre le circuit de charge inopérant. De mon côté, j’ai simplement dessoudé puis retiré la résistance de 200 ohms, comme visible ci-dessous :

Retrait circuit de charge DS3231 pour ne pas recharger les piles CR 2032 ou les accus LIR 2032 sans protection, neutralisation composants élec

Ainsi, aucun risque de surcharger la pile de secours (que vous utilisiez une pile non rechargeable CR 2032, ou un accu lithium LIR 2032 sans BMS). À noter que retirer ce circuit de charge est une pratique que je vous recommande ici, mais bien sûr, libre à vous de faire comme vous l’entendez 😉

ModuleDescriptionLien
Module DS3231 horloge temps réel RTC avec bornier de raccordement format header mâle, vue de face carte électronique avec pile de sauvegardeModule DS3231 monté sur PCB
(sans pile de sauvegarde fournie)

Comment brancher le DS3231 sur un Arduino Uno, Nano, Mini, Micro, ou Mega ?

Comme vous le constaterez rapidement, brancher un DS3231 sur un Arduino est vraiment simple. D’ailleurs, nous n’avons besoin que de 4 fils de liaisons pour ce faire, et faire ses premiers pas avec. Ces liaisons sont basiquement :

  • 2 fils pour l’alimentation (VCC et GND)
  • 2 fils pour la communication i2c (SDA et SCL)

Par exemple, voici un schéma de raccordement basique d’un module DS 3231 sur un Arduino Uno :

Raccordement DS3231 arduino uno, schéma de branchement avec fils dupont de l'alimentation 5V et des fils de communication I2C que sont SDA et SCL

Bien sûr, il ne s’agit que d’un exemple de câblage de base, pour faire ses premiers pas avec. Car les sorties 32K et SQW sont elles-aussi utilisables, au besoin.

Au passage, suivant quel type d’arduino vous utilisez, il faudra peut-être adapter ce branchement. En effet, pour une communication i2c « standard », voici les raccordements de base à effectuer, selon que vous utilisez un Arduino Uno, Nano, Mini, Micro, ou Mega :

Modèle de carte ArduinoMicrocontrôleurBroche SDABroche SCL
Arduino Nano
Arduino Mini
Arduino Uno
ATmega328PA4A5
Arduino MicroATmega32U4D2D3
Arduino MegaATmega2560D20D21

À présent, voyons quelle librairie utiliser, pour débuter avec cette horloge temps réel DS3231 !

Quelle librairie utiliser pour interagir avec cette horloge temps réel ? (RTClib, Wire, …)

Pour interagir en I2C avec une horloge temps réel DS3231, il existe principalement 2 solutions qui s’offrent à vous :

  • soit utiliser la librairie « générale » Wire, native sous Arduino IDE (pas besoin de la télécharger ni de l’installer, donc)
  • soit utiliser une librairie « spécifique », et dédiée ou adaptée au DS 3231 (mais qui nécessitera par contre de l’importer/installer, pour faire appel à elle ensuite)

Si vous débutez, je vous recommande fortement d’utiliser une librairie spécifique, telle que la librairie RTClib (c’est d’ailleurs celle que je vais utiliser par la suite). Car ce type de librairie est conçu de telle manière que tout vous semblera « vraiment facile » à utiliser. Bien sûr, ce type de librairie est limité aux « opérations de base », mais quand bien même, cela suffit généralement largement lorsqu’on débute, selon moi.

Sinon, pour les plus aguerris (ou téméraires !), vous avez toujours la possibilité d’utiliser la bibliothèque Wire, intégrée de base à l’environnement Arduino, pour communiquer en i²c avec votre DS 3231. Par contre, cela nécessitera de connaître/maîtriser le datasheet du DS3231, afin de savoir exactement quels bits envoyer, et à quel moment les envoyer. Clairement, ce n’est pas la voie la plus facile, surtout si vous êtes débutant en la matière ! Cela étant dit, c’est comme ça qu’on peut approfondir les choses, et optimiser les échanges, si nécessaire 😉

Du coup, pour la suite de ce tuto sur l’horloge RTC DS3231, je m’appuierai sur la bibliothèque RTClib, afin que les programmes/codes restent simples, à lire et à comprendre !

Au passage, pour installer RTClib dans l’environnement de développement Arduino IDE, vous devrez tout d’abord aller dans le menu Outils > Gérer les bibliothèques. Puis taper « RTClib » dans le champ de recherche, et appuyer sur la touche ENTRÉE du clavier, pour effectuer une recherche. Ensuite, il faudra descendre dans la liste des propositions affichées, sélectionner la librairie RTClib (notée « by Adafruit » juste en dessous), et cliquer sur « Installer ». Une fois fait, vous devriez obtenir quelque chose ressemblant à cela :

Installation RTClib sur IDE arduino via gestionnaire de bibliothèques, librairie pour contrôler horloge temps réel RTC depuis ordinateur ou autre

À présent, vous pourrez faire appel à cette bibliothèque, n’importe où dans vos programmes Arduino ! C’est d’ailleurs ce que je vais vous montrer au travers des exemples ci-dessous, sans plus attendre !

Code exemple #1 : programme permettant de mettre à l’heure l’horloge RTC (à chaque upload du programme)

Le premier exemple que je vous propose de découvrir ici est un programme permettant de régler les date et heure de l’horloge temps réel DS3231, via un Arduino Uno ou Nano. En effet, compte tenu du fait que ces arduino n’ont nativement aucune connexion internet, il faut manuellement entrer les date et heure courantes « la première fois », pour que l’horloge RTC soit à l’heure !

Pour tester cela, nous allons partir sur le montage suivant :

Schéma branchement DS 3231 arduino nano, pour test de lecture ou enregistrement de date et heure dans l'horloge RTC, via communication I²C série

Au niveau des composants matériels, voici ce que j’ai utilisé :

À présent, je vais vous montrer 2 façons de régler la date & heure d’un DS3231 :

  • soit en entrant explicitement les dates et heures souhaitées (comme « 2023 07 07 14 35 00 » pour le 07/07/2023 à 14h35 et 0 seconde)
  • soit en laissant le compilateur communiquer renseigner la date/heure courante au moment de la compilation, pour régler le DS 3231 dans la foulée, au moment de l’upload (téléversement du programme PC vers l’Arduino, donc)

Réglage des date et heure du DS 3231 de manière explicite (écrit « en dur » dans le code, comme on dit !)

Alors, voici la première façon de régler date et heure dans l’horloge temps réel DS 3231. Ici, nous allons entrer ces infos de manière explicite, à même le programme. D’ailleurs, le voici :

/*
   ______               _                  _///_ _           _                   _
  /   _  \             (_)                |  ___| |         | |                 (_)
  |  [_|  |__  ___  ___ _  ___  _ __      | |__ | | ___  ___| |_ _ __ ___  _ __  _  ___  _   _  ___
  |   ___/ _ \| __|| __| |/ _ \| '_ \_____|  __|| |/ _ \/  _|  _| '__/   \| '_ \| |/   \| | | |/ _ \
  |  |  | ( ) |__ ||__ | | ( ) | | | |____| |__ | |  __/| (_| |_| | | (_) | | | | | (_) | |_| |  __/
  \__|   \__,_|___||___|_|\___/|_| [_|    \____/|_|\___|\____\__\_|  \___/|_| |_|_|\__  |\__,_|\___|
                                                                                      | |
                                                                                      \_|
  Fichier :       prgArduino-1a-ReglageManuelDateHeure.ino
  
  Description :   Programme permettant de régler la date et l'heure "manuellement",
                  c'est à dire en inscrivant ces valeurs là, précisément, dans le code
                  
  Remarques :     - l'arduino utilisé ici sera un modèle Nano
                  - la librairie utilisée sera RTClib de Adafruit (https://github.com/adafruit/RTClib)
                                    
  Auteur :        Jérôme TOMSKI (https://passionelectronique.fr/)
  Créé le :       25.06.2023

*/

// Inclusion de la librairie RTClib de Adafruit
#include "RTClib.h"

// Instanciation de celle-ci
RTC_DS3231 ds3231;

// ========================
// Initialisation programme
// ========================
void setup() {

  // Initialisation de la liaison série (PC <-> arduino nano)
  Serial.begin(9600);
  Serial.println(F("=========================================================================="));
  Serial.println(F("Exemple DS3231 #1a : réglage 'manuel' de la date/heure d'un module DS 3231"));
  Serial.println(F("=========================================================================="));
  Serial.println("");

  // Initialisation du module DS 3231
  if (!ds3231.begin()) {
    Serial.println("[ERREUR] Impossible de se connecter au module DS3231 (câblage incorrect, peut-être ?)");
    Serial.flush();
    while (1);
  }

  // **********************************************
  // Réglage d'une date/heure, de manière explicite
  // **********************************************
  
        // Exemple permettant de régler la date/heure du "25 juin 2023, à 22h45"
        // (valeurs modifiables comme bon vous semble, dans la limite de ce que les champs peuvent accepter)
        
        uint16_t  Annee     = 2023;     // de 2000 à 2099
        uint8_t   Mois      = 6;        // de 1 à 12
        uint8_t   Jour      = 25;       // de 1 à 31
        uint8_t   Heure     = 22;       // de 0 à 23
        uint8_t   Minutes   = 45;       // de 0 à 59
        uint8_t   Secondes  = 0;        // de 0 à 59
        
  ds3231.adjust(DateTime(Annee, Mois, Jour, Heure, Minutes, Secondes));
  Serial.println("Enregistrement de la nouvelle date/heure réussi !");

}

// =================
// Boucle principale
// =================
void loop() {
  
    // Vide (tout se passe dans la fonction "loop")
    
}

Comme vous le voyez, les infos concernant les année, mois, jour, heure, minutes, et secondes sont manuellement écrites dans le code, de manière explicite. Ce sont donc ces données qui seront inscrites dans l’horloge RTC DS3231, et ce, quelque soit le moment où vous uploaderez ce code..

Remarque très importante : en l’état, il faut bien comprendre que ce sont les date et heure inscrites en dur dans le code, qui seront inscrites dans votre DS3231, via votre Arduino. Et même, réinscrites à chaque RESET (donc redémarrage programme) de votre Arduino. Du coup, la date/heure courante de votre DS 3231 ne sera pas forcément ce à quoi vous vous attendez, selon à quel moment vous uploadez ce programme, et si vous faites des reset ou coupure d’alimentation, entre temps !

Pour vérifier si tout s’est bien passé juste après upload de votre programme, vous pouvez ouvrir le moniteur série de votre IDE Arduino, pour voir le ou les message(s) remonté(s) par le programme. Ainsi, s’il y a eu un problème de communication entre votre Arduino et votre module DS3231, vous devriez obtenir un message d’erreur tel que celui-ci :

Exemple problème de connexion DS 3231, câblage incorrect entraînant un message d'erreur sur le moniteur série de l'IDE Arduino

Sinon, s’il n’y a eu aucun soucis de communication, vous devriez voir un message tel que celui-là :

Exemple enregistrement date et heure fixe dans module ds 3231, via ide arduino, réglage manuel et transmission par lignes SCL et SDA du port série

Maintenant que nous avons vu comment écrire une date et heure de manière explicite, vous voudrez surement savoir comment le faire de manière automatique, et donc, sans avoir à entrer les valeurs de jour, mois, année, heure, minutes, et secondes ! C’est donc ce que nous allons voir à présent 😉

Réglage des date et heure du DS 3231 de manière automatique, au moment de chaque compilation/upload programme

Ici, le programme sera quasiment le même que vu précédemment, mais en plus simple ! En effet, au lieu d’entrer explicitement les date et heure souhaitées dans le code, on va laisser le compilateur les inscrire pour nous, au moment de la compilation.

Pour ce faire, nous allons faire appel aux constantes DATE et TIME, « natives » dans l’environnement de développement Arduino, afin de respectivement récupérer la date et l’heure de votre PC.

Du coup, voici ce que donne le code amélioré, pour le réglage de la date/heure en automatique :

/*
   ______               _                  _///_ _           _                   _
  /   _  \             (_)                |  ___| |         | |                 (_)
  |  [_|  |__  ___  ___ _  ___  _ __      | |__ | | ___  ___| |_ _ __ ___  _ __  _  ___  _   _  ___
  |   ___/ _ \| __|| __| |/ _ \| '_ \_____|  __|| |/ _ \/  _|  _| '__/   \| '_ \| |/   \| | | |/ _ \
  |  |  | ( ) |__ ||__ | | ( ) | | | |____| |__ | |  __/| (_| |_| | | (_) | | | | | (_) | |_| |  __/
  \__|   \__,_|___||___|_|\___/|_| [_|    \____/|_|\___|\____\__\_|  \___/|_| |_|_|\__  |\__,_|\___|
                                                                                      | |
                                                                                      \_|
  Fichier :       prgArduino-1b-ReglageAutomatiqueDateHeure.ino
  
  Description :   Programme permettant de régler la date et l'heure, "automatiquement" après de l'upload de ce code
                  dans votre DS3231, et redémarrage de votre Arduino (la date et heure inscrite sera celle de votre ordinateur)
                  
  Remarques :     - la librairie utilisée ici sera la "RTClib" de Adafruit (https://github.com/adafruit/RTClib)
                  - l'arduino utilisé dans cet exemple sera un Arduino Nano
                                    
  Auteur :        Jérôme TOMSKI (https://passionelectronique.fr/)
  Créé le :       26.06.2023

*/

// Inclusion de la librairie RTClib de Adafruit
#include "RTClib.h"

// Instanciation de celle-ci
RTC_DS3231 ds3231;

// ========================
// Initialisation programme
// ========================
void setup() {

  // Initialisation de la liaison série (PC <-> Arduino Nano)
  Serial.begin(9600);
  Serial.println(F("================================================================================"));
  Serial.println(F("Exemple DS3231 #1b : mise à jour automatique de la date et heure de votre DS3231"));
  Serial.println(F("                     après upload de ce programme / redémarrage de votre arduino"));
  Serial.println(F("                     (la date et heure utilisée sera celle de votre ordinateur)" ));
  Serial.println(F("================================================================================"));
  Serial.println("");


  // Initialisation du module DS 3231
  if (!ds3231.begin()) {
    Serial.println("[ERREUR] Impossible d'établir la connexion avec votre module DS3231 (problème de câblage ?)");
    Serial.flush();
    while (1);
  }
  


  // ************************************************************************************************************
  // Option 1 : réglage systématique de la date/heure du DS3231, après chaque upload/redémarrage de votre arduino
  // ************************************************************************************************************
  ds3231.adjust(DateTime(F(__DATE__), F(__TIME__)));    
 
  
  // *****************************************************************************************
  // Option 2 : mise à jour de la date et heure de votre DS3231, après upload de ce programme,
  //            SEULEMENT si le DS3231 signale qu'il a "perdu l'heure" (ce qui a pu arriver
  //            si sa pile de secours a été défaillante, par exemple)
  // *****************************************************************************************
  // if (ds3231.lostPower()) {
  //  ds3231.adjust(DateTime(F(__DATE__), F(__TIME__)));      
  // }


  // ====================================================================================
  // ----->
  // ----->  désactivez l'une ou autre des 2 options ci-dessus, selon vos besoins  <-----
  //                                                                               <-----
  // ====================================================================================

  Serial.println("Enregistrement de la nouvelle date/heure réussi !");

}

// =================
// Boucle principale
// =================
void loop() {
  
    // Vide (tout se passe dans la fonction "loop", ci-dessus)
    
}

Notez bien que ces heure/date seront celles de votre ordi, au moment de la compilation du programme. En effet, le compilateur va substituer aux constantes TIME et DATE les valeurs courantes d’heure et date, au moment de la compilation. De ce fait, ce seront celles-ci qui seront inscrites en dur dans votre arduino, au moment de l’upload de votre programme.

Et comme pour l’exemple précédent, si vous coupez puis remettez l’alimentation de votre Arduino, ou faites un reset de votre Arduino, alors ces date et heure de compilation seront automatiquement réinscrites dans le DS 3231, via l’arduino. Gardez toujours cela à l’esprit, avec ces « programmes de base » !

Au passage, comme pour tout à l’heure, un message d’erreur apparaîtra sur le moniteur série de votre IDE Arduino, en cas de problème de communication (cf. ci-dessous).

Problème de communication i2c entre DS3231 et Arduino détecté par logiciel, avec affichage du message sur moniteur série de l'interface arduino

Et si tout ce passe bien, voici ce que vous devriez voir apparaître :

Enregistrement date et heure dans DS3231, affichage du résultat du code de programmation de l'horloge temps réel depuis montage arduino

Voilà ! Maintenant que nous avons vu comment écrire l’heure et la date dans le DS3231, voyons comment les lire, afin de vérifier que tout soit bien enregistré !

ModuleDescriptionLien
Module DS3231 horloge temps réel RTC avec bornier de raccordement format header mâle, vue de face carte électronique avec pile de sauvegardeModule DS3231 monté sur PCB
(sans pile de sauvegarde fournie)

Code exemple #2 : programme permettant de lire la date et l’heure actuelle, du DS 3231

À présent, pour lire l’heure et la date courante d’un DS 3231, nous allons procéder à quelques modifications au niveau du code logiciel. Car au niveau du câblage et du côté matériel, rien ne change (nous allons donc conserver le montage précédent !).

Pour rappel, le câblage du montage devrait ressembler à cela, lorsque réalisé sur breadboard :

Grosso modo, vous aurez besoin :

Concernant la partie logicielle (code de programmation arduino), la voici :

/*
   ______               _                  _///_ _           _                   _
  /   _  \             (_)                |  ___| |         | |                 (_)
  |  [_|  |__  ___  ___ _  ___  _ __      | |__ | | ___  ___| |_ _ __ ___  _ __  _  ___  _   _  ___
  |   ___/ _ \| __|| __| |/ _ \| '_ \_____|  __|| |/ _ \/  _|  _| '__/   \| '_ \| |/   \| | | |/ _ \
  |  |  | ( ) |__ ||__ | | ( ) | | | |____| |__ | |  __/| (_| |_| | | (_) | | | | | (_) | |_| |  __/
  \__|   \__,_|___||___|_|\___/|_| [_|    \____/|_|\___|\____\__\_|  \___/|_| |_|_|\__  |\__,_|\___|
                                                                                      | |
                                                                                      \_|
  Fichier :       prgArduino-2-LectureDateHeureDS3231.ino
  
  Description :   Programme permettant de lire la date et heure "en cours" dans le DS3231
                  
  Remarques :     - la librairie utilisée ici sera la "RTClib" de Adafruit (https://github.com/adafruit/RTClib)
                  - l'arduino utilisé pour les essais sera un Nano
                                    
  Auteur :        Jérôme TOMSKI (https://passionelectronique.fr/)
  Créé le :       26.06.2023

*/

// Inclusion de la librairie RTClib de Adafruit
#include "RTClib.h"

// Instanciation de celle-ci
RTC_DS3231 ds3231;

// Constante(s)
char joursDeLaSemaine[7][12] = {"Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"};


// ========================
// Initialisation programme
// ========================
void setup() {

  // Initialisation de la liaison série (PC <-> Arduino Nano)
  Serial.begin(9600);
  Serial.println(F("========================================================================"));
  Serial.println(F("Exemple DS3231 #2 : lecture de la date et heure contenue dans le DS3231,"));
  Serial.println(F("                    avec rafraîchissement toutes les 2 secondes         "));
  Serial.println(F("========================================================================"));
  Serial.println("");


  // Initialisation du module DS3231
  if (!ds3231.begin()) {
    Serial.println("[ERREUR] Impossible d'établir la connexion avec votre module DS3231 (problème de câblage ?)");
    Serial.flush();
    while (1);
  }

}


// =================================================================================
// Fonction : formateValeurSurDeuxChiffresMinimum
// (permet d'avoir une homogénéïté, au niveau de l'affichage, sur le moniteur série)
// =================================================================================
String formateValeurSurDeuxChiffresMinimum(int nombre)
{
  if (nombre < 10)
    return "0" + String(nombre);
  else
    return String(nombre);
}


// =================
// Boucle principale
// =================
void loop() {

  // Lecture de la date/heure actuelle, de notre horloge temps réel DS 3231
  DateTime dateHeureDuDS3231 = ds3231.now();

  // Affichage de ça sur le moniteur série
  Serial.print("Date/heure actuelle (DS3231) : ");
  Serial.print(joursDeLaSemaine[dateHeureDuDS3231.dayOfTheWeek()]);
  Serial.print(" ");
  Serial.print(formateValeurSurDeuxChiffresMinimum(dateHeureDuDS3231.day()));
  Serial.print("/");
  Serial.print(formateValeurSurDeuxChiffresMinimum(dateHeureDuDS3231.month()));
  Serial.print("/");
  Serial.print(formateValeurSurDeuxChiffresMinimum(dateHeureDuDS3231.year()));
  Serial.print(" ");
  Serial.print(formateValeurSurDeuxChiffresMinimum(dateHeureDuDS3231.hour()));
  Serial.print(":");
  Serial.print(formateValeurSurDeuxChiffresMinimum(dateHeureDuDS3231.minute()));
  Serial.print(":");
  Serial.print(formateValeurSurDeuxChiffresMinimum(dateHeureDuDS3231.second()));
  Serial.println();

  // Attente de 2 secondes, avant relecture de l'heure (histoire de vérifier que l'horloge RTC "avance bien" !)
  delay(2000);
    
}

Vous retrouvez ici essentiellement 3 parties :

  • la partie setup, où on initialise la liaison série PC ↔ arduino, et arduino ↔ DS 3231
  • une fonction de formatage de nombre sur 2 chiffres (permettant par exemple d’afficher « 12:08:03 » pour 12h 8min et 3 secondes, au lieu d’afficher « 12:8:3 », … ce qui serait moins parlant !)
  • et la fonction loop, qui lira à intervalles réguliers (toutes les 2 secondes, ici), les dates et heures courantes du DS3231

Une fois uploadé dans votre Arduino, il vous suffira d’ouvrir le moniteur série de votre IDE Arduino pour voir le résultat.

En cas de problème, vous devriez vous retrouver avec un message d’erreur du type :

Echec connexion i2c du ds3231 affiché sur moniteur série de l'Arduino IDE, avec indication par message d'erreur sur console retour

Et si tout se passe bien, vous devriez voir la date/heure courante de votre horloge temps réel s’afficher à l’écran ; avec un rafraîchissement (relecture des infos, plutôt) toutes les deux secondes, comme visible ci-dessous :

Exemple lecture toutes les 2 secondes des date et heure d'une horloge RTC DS 3231, avec affichage moniteur série IDE Arduino temps réel

Remarque : les dates et heures affichées ici ont été initialisées (enregistrées, si vous préférez) à l’occasion de l’exemple précédent (code #1b). Si vous regardez attentivement l’image ci-dessus, vous noterez qu’il y a environ 10 secondes d’écart, entre l’heure du PC affichée sur le moniteur série (tout à gauche de l’image), et celle retournée par le DS3231 (tout à droite de l’image, dans le cadre vert). En fait, cela vient non pas du fait que le DS 3231 a pris 10 secondes de retard depuis lors, mais que 10 secondes se sont passée entre la compilation du programme précédent, et son exécution sur le microcontrôleur. D’où le décalage 😉

ModuleDescriptionLien
Module DS3231 horloge temps réel RTC avec bornier de raccordement format header mâle, vue de face carte électronique avec pile de sauvegardeModule DS3231 monté sur PCB
(sans pile de sauvegarde fournie)

Code exemple #3 : programme permettant d’ajouter une fonction heure d’été / heure d’hiver

Une chose, dont je n’ai pas vraiment parlé jusqu’à présent, concerne les heures d’été / hiver.

Si comme moi vous habitez en France, vous savez que l’on change d’heure deux fois par an (pour le passage d’heure d’hiver à heure d’été, puis d’heure d’été à heure d’hiver). Et même s’il est question d’en terminer « un jour », pour l’instant, c’est toujours d’actualité !

Or le DS 3231 ne gère pas nativement le passage d’heure d’hiver à été, et vice-versa. Du coup : soit vous faites avec et aurez 1h de décalage 6 mois par an, soit vous implémentez un bout de code, pour « corriger » cela. Et c’est ce que je vous propose de découvrir ici.

Remarque : concernant la partie matérielle, pour tester ce 3ème exemple de code, nous utilisons encore le même câblage arduino/ds3231 (comme vu en détail au code exemple #1). N’hésitez pas à revenir dessus, au besoin !

Ah oui … concernant les « règles » de changement d’heure en France, laissez moi d’abord vous les présenter (si cela ne change pas entre-temps !) :

  • le passage à l’heure d’été s’effectue à 2h du matin, le dernier dimanche du mois de mars (on ajoute alors 1h à ce moment là ; à 2h du mat, il sera alors 3h)
  • le passage à l’heure d’hiver s’effectue à 3h du matin, le dernier dimanche du mois d’octobre (on enlève alors 1h à ce moment là ; à 3h du mat, il sera alors 2h)

Comme vous vous en doutez, d’un point de vue logiciel, gérer ces heures d’été/hiver ne sera pas vraiment simple ; surtout qu’il y a plusieurs façons de faire, avec pour chaque, des avantages et inconvénients à la clef.

Ici, dans le programme que je vais vous présenter, j’ai fais le choix d’enregistrer la date/heure UTC dans l’horloge temps réel DS3231 (UTC venant de l’anglais « Universal Time Coordinated », et servant de « fuseau horaire servant de référence pour le reste du globe », parmi d’autres). L’heure UTC couvre entre autre le Royaume Uni, l’Espagne, le Portugal, etc, mais pas la France). En fait, pour faire simple, nous sommes en France à l’heure UTC+1 en hiver, et UTC+2 en été. Du coup, suivant quel jour nous sommes et l’heure qu’il est, il suffit simplement de rajouter 1 ou 2 heures à l’heure UTC, pour savoir quelle heure il est en France. C’est d’ailleurs ce principe qui est repris dans le code qui suit.

Remarque : toute cette mécanique de changement d’heure n’est pas forcément simple à comprendre, surtout une fois codé. Du coup, ne vous prenez pas la tête si jamais vous ne comprenez pas tout du premier coup !

Voici le code, permettant de simuler la gestion de l’heure d’été/hiver avec le module DS 3231 :

/*
   ______               _                  _///_ _           _                   _
  /   _  \             (_)                |  ___| |         | |                 (_)
  |  [_|  |__  ___  ___ _  ___  _ __      | |__ | | ___  ___| |_ _ __ ___  _ __  _  ___  _   _  ___
  |   ___/ _ \| __|| __| |/ _ \| '_ \_____|  __|| |/ _ \/  _|  _| '__/   \| '_ \| |/   \| | | |/ _ \
  |  |  | ( ) |__ ||__ | | ( ) | | | |____| |__ | |  __/| (_| |_| | | (_) | | | | | (_) | |_| |  __/
  \__|   \__,_|___||___|_|\___/|_| [_|    \____/|_|\___|\____\__\_|  \___/|_| |_|_|\__  |\__,_|\___|
                                                                                      | |
                                                                                      \_|
  Fichier :       prgArduino-3-ExempleAvecHeureEteHiver.ino
  
  Description :   Programme permettant de régler la date et l'heure d'un DS3231 à l'upload de ce programme,
                  au format "UTC" (c'est à dire : sans tenir compte des fuseaux horaires)
                  
  Remarques :     - la librairie qui sera utilisée ici sera la "RTClib" de Adafruit (https://github.com/adafruit/RTClib)
                  - l'arduino utilisé pour tester ce programme sera un Arduino Nano
                  - ce programme analysera la date/heure locale du PC, fera une conversion vers de l'UTC, sur la base du
                    fuseau horaire "France" (étant entendu qu'en France nous sommes en "UTC+1" en hiver, et en "UTC+2" en été)
                  - ce programme peut mal fonctionner si vous l'uploadez juste au moment ou aux alentours d'un changement d'heure (été ↔ hiver, j'entends)
                  - ATTENTION : chaque "reset" de l'arduino reprogramme la dernière heure connue du PC, et faussera donc ensuite l'heure (en fait, ici,
                                il ne s'agit que d'un programme exemple de base, pour illustrer une potentielle fonction "heure d'été / heure d'hiver"
                                    
  Auteur :        Jérôme TOMSKI (https://passionelectronique.fr/)
  Créé le :       29.06.2023

*/

// Inclusion de la librairie RTClib d'Adafruit
#include "RTClib.h"

// Instanciation de celle-ci
RTC_DS3231 ds3231;

// Constantes
const int nombreDheuresArajouterOuEnleverEnHiver  = 1;      // En France, en heure d'été, nous somme en "UTC + 1 heure" (d'où le "1" ici)
const int nombreDheuresArajouterOuEnleverEnEte    = 2;      // En France, en heure d'été, nous somme en "UTC + 2 heures" (d'où le "2" ici)

const char joursDeLaSemaine[7][12]                = {"dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"};

const int nombreDeSecondesAcompenser              = 10;     // Pour compenser le temps d'upload de ce programme, lors de l'enregistrement
                                                            // de la date/heure dans cette horloge RTC

// ========================
// Initialisation programme
// ========================
void setup() {

  // Initialisation de la liaison série (PC <-> Arduino Nano)
  Serial.begin(9600);
  Serial.println(F("==============================================================================="));
  Serial.println(F("Exemple DS3231 #3 : mise à jour de la date/heure de votre DS3231, au format UTC"));
  Serial.println(F("                   (valable seulement 'en France', et doit être adapté au delà)"));
  Serial.println(F("==============================================================================="));
  Serial.println("");


  // Initialisation du module DS 3231
  if (!ds3231.begin()) {
    Serial.println("[ERREUR] Impossible d'établir la connexion avec votre module DS3231 (problème de câblage ?)");
    Serial.flush();
    while (1);
  }

  
  // Affichage des paramètres saisis (heures UTC, été/hiver)
  Serial.println(F("Paramètres entrés (pour compensation en heures, conversion UTC <-> heure locale du pays) : "));
  Serial.print(F("   → pour l'hiver : "));
  if(nombreDheuresArajouterOuEnleverEnHiver>0)
    Serial.print(F("+"));
  Serial.print(nombreDheuresArajouterOuEnleverEnHiver);
  Serial.print(F(" H"));
  Serial.println();
  Serial.print(F("   → pour l'été   : "));
  if(nombreDheuresArajouterOuEnleverEnEte>0)
    Serial.print(F("+"));
  Serial.print(nombreDheuresArajouterOuEnleverEnEte);
  Serial.print(F(" H"));
  Serial.println();
  Serial.println();


  // Récupération de la date/heure de votre PC (depuis où vous uploadez ce programme, vers votre arduino)
  DateTime dateHeurePC (F(__DATE__), F(__TIME__));       // Récupération de la date et heure de votre PC (on peut être ici en heure d'été ou d'hiver)
  DateTime dateHeureLocale (dateHeurePC + TimeSpan(0,0,0,nombreDeSecondesAcompenser));    // Ajoute 0 jour, 0 heures, 0 minutes, et x secondes


  // Affichage de cette date/heure là sur le moniteur série, de l'IDE Arduino
  Serial.println(F("Date/heure (locale) lue sur le PC : "));
  Serial.print(F("   → "));
  afficheUneDateHeureSurLeMoniteurSerie(dateHeureLocale);
  Serial.println();

  // Affichage du fait qu'il s'agisse ou non d'une heure d'été/hiver
  Serial.print(F("   → remarque : on est en heure d'"));
  if(estOnEnHeureDeEte(dateHeureLocale))
    Serial.print(F("été"));
  else
    Serial.print(F("hiver"));
  Serial.println();
  Serial.println();

  // Calcul du nombre d'heures à retrancher ou rajouter, pour passer en heure UTC
  int nbreDheuresAretrancherOuAjouter = estOnEnHeureDeEte(dateHeureLocale) ? nombreDheuresArajouterOuEnleverEnEte : nombreDheuresArajouterOuEnleverEnHiver;    

  // Conversion de la date/heure locale au format UTC
  Serial.println(F("Date/heure (UTC) d'après la date/heure locale :"));
  DateTime dateHeureUTC     (dateHeureLocale - TimeSpan(0,nbreDheuresAretrancherOuAjouter,0,0));
  Serial.print(F("   → "));
  afficheUneDateHeureSurLeMoniteurSerie(dateHeureUTC);
  Serial.println();
  Serial.println();

  // Enregistrement de cette date/heure UTC dans le DS3231
  Serial.println(F("Enregistrement de cette date/heure UTC dans le DS3231 :"));
  ds3231.adjust(dateHeureUTC);
  Serial.println(F("   → effectué"));
  Serial.println();


  // Lecture de la date/heure contenue dans le DS 3231
  Serial.println(F("Lecture de la date/heure contenue dans le DS 3231 :"));
  DateTime dateHeureDuDS3231 = ds3231.now();
  Serial.print(F("   → tel que lu dans l'horloge  : "));
  afficheUneDateHeureSurLeMoniteurSerie(dateHeureDuDS3231);
  Serial.println(" (UTC)");

  // Calcul du nombre d'heures à rajouter ou enlever, pour arriver à l'heure de "notre fuseau horaire (+ compensation été/hiver)
  int nbreDheuresAajouterOuRetirer = estOnEnHeureDeEte(dateHeureLocale) ? nombreDheuresArajouterOuEnleverEnEte : nombreDheuresArajouterOuEnleverEnHiver;
  
  // Conversion de la date/heure UTC contenue dans le DS3231 en date/heure locale, correspondant à notre fuseau horaire (avec compensation heure d'été ou hiver)
  DateTime dateHeureDuDS3231Corrige (dateHeureDuDS3231 + TimeSpan(0,nbreDheuresAajouterOuRetirer,0,0));    // On ajoute ou retire 0 jour, x heure, 0 minute, et 0 seconde
  Serial.print(F("   → au format \"local\" (France) : "));
  afficheUneDateHeureSurLeMoniteurSerie(dateHeureDuDS3231Corrige);
  Serial.println();
  
}

// =================
// Boucle principale
// =================
void loop() {
  
  // Vide (tout se passe dans la section "setup")
    
}


// ======================================================================
// Fonction "afficheUneDateHeureSurLeMoniteurSerie"
// (permet d'afficher "proprement" une date/heure, sur le moniteur série)
// ======================================================================
void afficheUneDateHeureSurLeMoniteurSerie(DateTime dt) {
  Serial.print(joursDeLaSemaine[dt.dayOfTheWeek()]);
  Serial.print(" ");
  Serial.print(formateValeurSurDeuxChiffresMinimum(dt.day()));
  Serial.print("/");
  Serial.print(formateValeurSurDeuxChiffresMinimum(dt.month()));
  Serial.print("/");
  Serial.print(formateValeurSurDeuxChiffresMinimum(dt.year()));
  Serial.print(" ");
  Serial.print(formateValeurSurDeuxChiffresMinimum(dt.hour()));
  Serial.print(":");
  Serial.print(formateValeurSurDeuxChiffresMinimum(dt.minute()));
  Serial.print(":");
  Serial.print(formateValeurSurDeuxChiffresMinimum(dt.second()));
}


// =================================================================================
// Fonction : formateValeurSurDeuxChiffresMinimum
// (permet d'avoir une homogénéïté, au niveau de l'affichage, sur le moniteur série)
// =================================================================================
String formateValeurSurDeuxChiffresMinimum(int nombre)
{
  if (nombre < 10)
    return "0" + String(nombre);
  else
    return String(nombre);
}


// =================================================================================
// Fonction : estOnEnHeureDeEte
// (permet de dire si oui ou non nous serions actuellement en heure d'été)
// =================================================================================
boolean estOnEnHeureDeEte(DateTime dateHeureAanalyser) {

/*
 * En France, le passage à l'heure d'hiver s'effectue le dernier dimanche d'octobre,
 * et le passage à l'heure d'été, le dernier dimanche de mars.
 * 
 * À la fin mars, on ajoute 1 heure, dans la nuit de samedi à dimanche.
 * Le changement se fait à 2 heures du matin, où il sera alors 3h du matin.
 * 
 * À la fin d'octobre, on retire 1 heure, dans la nuit de samedi à dimanche. 
 * Le changement se fait à 3 heures du matin, où il sera alors 2h du matin.
 * 
 * À noter que ce changement d'heure va possiblement disparaître, dans les années à venir.
 * Mais là pour l'instant, en 2023, il est toujours "en cours".
 * 
*/

  // Variables temporaires de cette fonction
  boolean estAvantLeDernierDimancheDeMars;
  boolean estApresLeDernierDimancheDeOctobre;
  boolean conditionValidite1;
  boolean conditionValidite2;
  boolean conditionValidite3;

  // Récupération temporaire des données dont nous aurons besoin
  int anneeDeLaDateHeureAanalyser = dateHeureAanalyser.year();
  int moisDeLaDateHeureAanalyser = dateHeureAanalyser.month();
  int jourDeLaDateHeureAanalyser = dateHeureAanalyser.day();
  int heuresDeLaDateHeureAanalyser = dateHeureAanalyser.hour();
  int minutesDeLaDateHeureAanalyser = dateHeureAanalyser.minute();

  // Recherche du "numéro" de jour correspondant au dernier dimanche du mois de mars (de l'année de la date à analyser)
  int dernierDimancheDuMoisDeMars;
  for(int i=31; i >= 1; i--) {
    DateTime jourDeMars (anneeDeLaDateHeureAanalyser, 3, i, 0, 0, 0);       // Paramètres = année, mois, jour, heures, minutes, secondes
    if(jourDeMars.dayOfTheWeek() == 0) {
      dernierDimancheDuMoisDeMars = i;  // "dayOfTheWeek == 0" signifiant que nous sommes un "dimanche", avec la librairie RTClib,
      break;                            // et le "break" permet alors de quitter cette boucle for, comme nous avons trouvé le dernier dimanche du mois
    }
  }

  // Recherche du "numéro" de jour correspondant au dernier dimanche du mois d'octobre (de l'année de la date à analyser)
  int dernierDimancherDuMoisDeOctobre;
  for(int i=31; i >= 1; i--) {
    DateTime jourDeOctobre (anneeDeLaDateHeureAanalyser, 10, i, 0, 0, 0);   // Paramètres = année, mois, jour, heures, minutes, secondes
    if(jourDeOctobre.dayOfTheWeek() == 0) {
      dernierDimancherDuMoisDeOctobre = i;  // "dayOfTheWeek == 0" signifiant que nous sommes un "dimanche", avec la librairie RTClib,
      break;                                // et le "break" permet alors de quitter cette boucle for, comme nous avons trouvé le dernier dimanche du mois
    }
  }

  // On teste pour savoir si on est avant le dernier dimanche de mars, et avant 3h du matin
  conditionValidite1 = moisDeLaDateHeureAanalyser < 3;
  conditionValidite2 = (moisDeLaDateHeureAanalyser == 3) && (jourDeLaDateHeureAanalyser < dernierDimancheDuMoisDeMars);
  conditionValidite3 = (moisDeLaDateHeureAanalyser == 3) && (jourDeLaDateHeureAanalyser == dernierDimancheDuMoisDeMars) && (heuresDeLaDateHeureAanalyser < 3);  
  if(conditionValidite1 || conditionValidite2 || conditionValidite3)
    estAvantLeDernierDimancheDeMars = true;
  else
    estAvantLeDernierDimancheDeMars = false;

  // On teste pour savoir si on est après le dernier dimanche d'octobre, et après 3h du matin
  // (remarque : surtout ne pas faire l'upload entre 2 et 3h du mat, ce jour là en particulier, sinon ça va dysfonctionner !)
  conditionValidite1 = moisDeLaDateHeureAanalyser > 10;
  conditionValidite2 = (moisDeLaDateHeureAanalyser == 10) && (jourDeLaDateHeureAanalyser > dernierDimancherDuMoisDeOctobre);
  conditionValidite3 = (moisDeLaDateHeureAanalyser == 10) && (jourDeLaDateHeureAanalyser == dernierDimancheDuMoisDeMars) && (heuresDeLaDateHeureAanalyser >= 3);  
  if(conditionValidite1 || conditionValidite2 || conditionValidite3)
    estApresLeDernierDimancheDeOctobre = true;
  else
    estApresLeDernierDimancheDeOctobre = false;

  // Et on retourne le résultat
  if(estAvantLeDernierDimancheDeMars || estApresLeDernierDimancheDeOctobre)
    return false;       // Car là, on serait en "heure d'hiver"
  else
    return true;        // Car là, on serait en "heure d'été"

}

Dans les grandes lignes, ce programme :

  • lit l’heure locale du PC (qui peut être de l’UTC+1 en heure d’hiver, ou de l’UTC+2 en heure d’été, si comme moi vous habitez en France)
  • détermine si l’heure locale est une heure d’été ou heure d’hiver
  • convertie cette heure locale UTC+x en heure UTC
  • enregistre cette heure UTC dans l’horloge temps réel DS3231
  • puis lit l’heure actuelle du DS 3231 (pour voir si l’heure UTC précédente a bien été enregistrée)
  • et reconvertit cette heure du DS3231 en heure d’été/hiver, pour voir si celle-ci correspond bien à l’heure de votre PC

Vous pourrez visualiser toutes ces étapes, en ouvrant le moniteur série de votre IDE Arduino. Voici d’ailleurs ce que cela donne, de mon côté :

Conversion GMT UTC horloge RTC DS3231 via programme arduino, exemple de code pour gestion fuseau horaire, et lecture écriture date et heure

Comme vous le voyez, c’est lourd et pas facile à comprendre, de prime abord. Cela étant dit, gardez bien à l’esprit qu’il s’agit là d’un programme de test, vous montrant chaque étape du raisonnement que l’on pourrait avoir, pour gérer les heures d’été-hiver, avec une horloge RTC de type DS3231. Il peut donc grandement être épuré/amélioré !

Bien sûr, en l’état, ce programme n’a que peu d’utilité. Par contre, libre à vous de vous en inspirer, comme « brique de base », à la réalisation d’un projet plus grand (comme un datalogger, par exemple !).

Nota : encore une fois, si cette gestion d’heure été hiver et/ou ce programme vous semble trop complexe, laissez-les tout simplement de côté, pour l’instant. N’hésitez pas à reprendre tout cela plus tard, à tête reposée 😉

Un mot sur l’adressage I²C (0x68 pour la puce DS 3231, et de 0x50 à 0x57 pour l’eeprom AT 24C32)

Juste une parenthèse sur le module DS 3231 et son adressage I2C, car il y a une source de confusion, ici ! En effet, il faut tout d’abord bien comprendre que ce module embarque :

  • une puce DS3231
  • une mémoire EEPROM 24C32
  • et un sélecteur d’adresse I2C (là où il y a noté A0, A1, et A2, sur le pcb)

Ici, on pourrait croire que la sélection d’adresse concerne la puce DS3231 ; mais en fait, il n’en est rien. En effet, cela concerne uniquement l’adresse I²C de l’EEPROM, qui n’a, pour ainsi dire, aucun lien direct avec la puce DS 3231 (pour rappel, cette mémoire eeprom est fournie « en bonus », et peut être totalement ignorée).

Du coup, comment changer l’adresse I2C du DS3231, me direz-vous, si ce n’est pas via A0/A1/A2 ? Eh bien, c’est tout simple : la puce DS 3231 ne possède qu’une seule adresse i2c (0x68), et ne peut être changée ! Vous ne pouvez donc pas choisir d’autres adresse I²C que 0x68.

En clair : pour résumer, le DS3231 a une seule adresse i²c, qui est 0x68, tandis que la mémoire eeprom peut adopter 1 des 8 adresses disponibles, entre 0x50 et 0x57 (inclus tous deux).

Au niveau des bits d’adresses i2C, ceux-ci sont prototypés de la manière suivante :

  • Horloge temps réel DS3231 : 1 1 0 1 0 0 0 R/W (bit 7 à bit 0)
  • Eeprom 24C32 : 1 0 1 0 A2 A1 A0 R/W (bit 7 à bit 0)

En sachant que R/W=1, lorsqu’on veut lire (« R »ead) sur le bus i²c, et R/W=0 lorsqu’on souhaite écrite (« W »rite) sur le bus. Voici ce que cela donne, si je synthétise cela dans un tableau :

ÉlémentA2A1A0Adresse 7 bits I²CAdresse 8 bits écriture I²CAdresse 8 bits lecture I²C
Puce DS32310x68 (0b1101 000)0xD0 (0b1101 0000)0xD1 (0b1101 0001)
Eeprom 24C320000x50 (0b1010 000)0xA0 (0b1010 0000)0xA1 (0b1010 0001)
0010x51 (0b1010 001)0xA2 (0b1010 0010)0xA3 (0b1010 0011)
0100x52 (0b1010 010)0xA4 (0b1010 0100)0xA5 (0b1010 0101)
0110x53 (0b1010 011)0xA6 (0b1010 0110)0xA7 (0b1010 0111)
1000x54 (0b1010 100)0xA8 (0b1010 1000)0xA9 (0b1010 1001)
1010x55 (0b1010 101)0xAA (0b1010 1010)0xAB (0b1010 1011)
1100x56 (0b1010 110)0xAC (0b1010 1100)0xAD (0b1010 1101)
1110x57 (0b1010 111)0xAE (0b1010 1110)0xAF (0b1010 1111)

Nota : A2, A1, et A0 sont les 3 entrées de sélection d’adresse de la mémoire EEPROM. Leur valeur équivaut à « 0 » lorsqu’un cavalier/pont de soudure est en place à leur niveau, ou « 1 » s’il n’y a aucun cavalier ou pont de soudure à leur niveau.

Cela étant dit, cette histoire d’adresse i2c est souvent gérée « en coulisse » au niveau des librairies Arduino. Du coup, vous n’aurez pas trop à vous en soucier, si vous n’utilisez pas l’EEPROM embarquée !

Liens (datasheets, codes de programmation, …)

En synthèse, voici les liens de cet article :

Nota : pour rappel, pour les programmes Arduino, ceux-ci doivent être enregistrés dans un répertoire portant le même nom, pour qu’ils puissent être exécutés.

ModuleDescriptionLien
Module DS3231 horloge temps réel RTC avec bornier de raccordement format header mâle, vue de face carte électronique avec pile de sauvegardeModule DS3231 monté sur PCB
(sans pile de sauvegarde fournie)

Tuto DS3231 Arduino : conclusion !

Voilà ! J’espère avoir fait le tour de cette horloge RTC DS3231, afin que vous puissiez faire vos premiers pas avec. Ainsi, vous pourrez avoir accès aux date et heure en « temps réel », et ce, sans requérir à la moindre connexion internet !

Bien sûr, certains microcontrôleurs modernes, équipés de modules WiFi, rendraient obsolète une telle horloge. Toutefois, si jamais vous prévoyez de faire tourner votre montage électronique loin de toute connexion internet (en dehors de chez vous, par exemple), alors rien de tel qu’une telle horloge ! En fait, chaque objet a ses atouts et ses inconvénients, et du coup, il faut simplement savoir les utiliser à bon escient 😉

Sur ce, je vous laisse ! Et vous dit à bientôt, pour un prochain article !
Jérôme.

À découvrir aussi : le PCF 8574, ou comment rajouter des entrées / sorties à son Arduino !

Ce contenu vous plaît ? Alors abonnez-vous à la Newsletter pour ne rien louper !

(*) Mis à jour le 25/04/2024

12 commentaires sur “Module DS3231 : comment ajouter une horloge temps réel (RTC) à ses projets arduino ? (tutorial avec exemples de code en I²C)”

  1. Site passionelectronique.fr

    Merci Jérôme pour ce tuto très détaillé, mais vous ne faites pas mention qu’il peut aussi servir de thermomètre !

    Et c’est vrai que pour une pile ou une batterie, il faut impérativement supprimer le circuit de charge. Ça ne devrait pas être une option.

    1. Site passionelectronique.fr

      Salut Pierre !

      En fait, sauf erreur de ma part, le capteur de température du DS3231 permet seulement la mesure de la température « intérieure » du circuit intégré (pour la partie TCXO, afin de compenser l’horloge en température afin d’assurer sa stabilité), et non la mesure de la température ambiante (extérieure à la puce, donc). Du coup, difficile d’en faire un véritable thermomètre 😉

      Par contre, tu as parfaitement raison : c’est quelque chose que je n’ai pas abordé ici. En fait, l’explication tient du fait que l’article commençait à devenir long, et que j’ai préféré rester focalisé sur l’essentiel (à savoir le réglage et la consultation des date et heure, de cette horloge temps réel).

      Voilà ! Bonne journée à toi !
      Jérôme.

  2. Site passionelectronique.fr

    Merci Jérôme pour la réponse.

    Effectivement, c’est la température de l’intérieur du chip mais comme il consomme très peu, il n’a pas d’échauffement propre et si on prend quelques précautions, on peut afficher la température ambiante avec une certaine inertie à cause de cette masse d’enrobage.
    Bien sûr, rien ne vaut un bon LM35 !

    En tout cas un grand merci pour ces tutos et on espère en avoir beaucoup d’autres.
    Cordialement, Pierre

  3. Site passionelectronique.fr

    Bonjour,

    Il y a une petite coquille dans la fonction « estOnEnHeureDeEte ».

    En effet, les vérifications individuelles sur la date, le mois et l’heure, telles qu’elles sont réalisées ne sont pas opérationnelles. La conversion des 3 datetimes (analyse, mars et octobre) en timestamp unix avec la fonction .unixtime() règle le problème et rétablit le fonctionnement attendu. Avec les corrections ci-dessous, le code est de nouveau fonctionnel.

    Merci pour votre travail et votre partage, c’était très instructif 🙂

    1. Site passionelectronique.fr

      Salut Ludo !

      Merci beaucoup, pour cette remontée de coquille ! En effet, il y avait 2 conditions mal écrites, au niveau de la fonction « estOnEnHeureDeEte ».

      Là, c’est bon, c’est corrigé ! Par contre, j’ai laissé les datetime comme auparavant, afin que le code soit le plus explicite possible (pour ceux qui débutent, j’entends).

      Encore merci à toi,
      Jérôme.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Symbole danger point d'exclamation site PSE, triangle jaune avec contour noir, texte noir alerte au milieuAfin de filtrer au maximum les messages de type "spam" ou "inappropriés", chaque commentaire est soumis à modération, et validé manuellement. Du coup, il se peut que certains commentaires ne soient pas publiés, ou sinon, avec un peu de retard. Par ailleurs, j'ai malheureusement plus de messages à traiter que de temps pour y répondre ; c'est pourquoi je ne pourrais pas répondre à tout le monde. Désolé …