Dans ce tutoriel, nous allons construire une horloge analogique en utilisant un écran e-Paper de 4,2″ et un ESP32. L’horloge synchronisera son heure avec un fournisseur de temps sur Internet (serveur SNTP). De plus, l’ESP32 et l’écran passeront en veille entre les mises à jour de l’affichage afin d’augmenter l’autonomie de l’horloge sur batterie.
Commençons par la liste des composants nécessaires.
Composants nécessaires
J’utilise l’ES32 lite comme microcontrôleur, car il est peu cher et dispose d’une interface de charge pour batterie, ce qui permet de faire fonctionner l’horloge sur une batterie LiPo. Tout autre ESP32 ou ESP8266 avec suffisamment de mémoire fonctionnera aussi, mais il est préférable d’en choisir un avec une interface de charge pour batterie.

Écran e-Paper 4,2″

ESP32 lite

Câble USB de données

Jeu de fils Dupont

Breadboard
Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.
Écran e-Paper
L’écran e-Paper utilisé dans ce projet est un module de 4,2″ avec une résolution de 400×300 pixels, 4 niveaux de gris, un temps de rafraîchissement partiel de 0,4 seconde, et un contrôleur intégré avec une interface SPI.

Notez que le module possède un petit pont/switch à l’arrière pour passer du SPI 4 fils au SPI 3 fils. Nous allons utiliser ici le SPI 4 fils par défaut. Vous ne devriez donc rien avoir à changer.

Le module d’affichage fonctionne en 3,3V ou 5V, a un courant de veille très faible de 0,01µA et ne consomme qu’environ 26,4mW lors du rafraîchissement. Pour plus d’informations sur les écrans e-Paper en général, consultez les deux tutoriels suivants : Interfacing Arduino To An E-ink Display et Weather Station on e-Paper Display .
Connexion et test de l’e-Paper
Commençons par connecter et tester le fonctionnement de l’e-Paper. L’image suivante montre tout le câblage pour l’alimentation et le SPI.

Vous trouverez ci-dessous un tableau récapitulatif de toutes les connexions. Notez que vous pouvez alimenter l’écran en 3,3V ou 5V, mais l’ESP32-lite ne fournit que du 3,3V et les lignes de données SPI doivent être en 3,3V !
| Écran e-Paper | ESP32 lite |
|---|---|
| CS/SS | 5 |
| SCL/SCK | 18 |
| SDA/DIN/MOSI | 23 |
| BUSY | 15 |
| RES/RST | 2 |
| DC | 0 |
| VCC | 3.3V |
| GND | G |
Vous pouvez utiliser une breadboard pour tout câbler. Mais j’ai en fait connecté l’écran directement à l’ESP32 avec des fils Dupont et ajouté une batterie LiPo pour tester l’horloge sur batterie. La photo ci-dessous montre à quoi ressemble mon montage.

Installer la bibliothèque GxEPD2
Avant de pouvoir dessiner ou écrire sur l’écran e-Paper, nous devons installer deux bibliothèques. La Adafruit_GFX bibliothèque graphique, qui fournit un ensemble commun de primitives graphiques (texte, points, lignes, cercles, etc.). Et la GxEPD2 bibliothèque, qui fournit le pilote graphique pour l’écran e-Paper.
Installez simplement les bibliothèques comme d’habitude. Après l’installation, elles devraient apparaître dans le Library Manager comme ci-dessous.

Code de test
Une fois la bibliothèque installée, lancez le code de test suivant pour vérifier que tout fonctionne.
#include "GxEPD2_BW.h"
//CS(SS)=5, SCL(SCK)=18, SDA(MOSI)=23, BUSY=15, RES(RST)=2, DC=0
GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> epd(GxEPD2_420_GDEY042T81(5, 0, 2, 15));
void setup() {
epd.init(115200, true, 50, false);
epd.setRotation(1);
epd.setTextColor(GxEPD_BLACK);
epd.setTextSize(2);
epd.setFullWindow();
epd.fillScreen(GxEPD_WHITE);
epd.setCursor(90, 190);
epd.print("Makerguides");
epd.display();
epd.hibernate();
}
void loop() {}
Le code affiche le texte « Makerguides » et après quelques clignotements de l’écran (rafraîchissement complet), vous devriez le voir s’afficher comme ci-dessous :

Sinon, c’est qu’il y a un problème. Le plus probable est que l’écran n’est pas correctement câblé ou que le mauvais pilote d’affichage est sélectionné. La ligne de code critique est celle-ci, où l’on spécifie le pilote pour l’écran 4,2″ :
GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> epd(GxEPD2_420_GDEY042T81(5, 0, 2, 15));
Le Readme de la GxEPD2 bibliothèque liste tous les écrans pris en charge et vous pouvez trouver les détails dans les fichiers header, par exemple GxEPD2.h . Trouvez le pilote spécifique à votre écran. Cela peut demander quelques essais.
Code pour une horloge analogique sur e-Paper
Le code suivant est le code complet pour une horloge analogique qui synchronise automatiquement son heure avec un serveur de temps Internet (SNTP) et affiche l’heure et la date sur un écran e-Paper 4,2″. Entre chaque rafraîchissement, l’écran et l’ESP32 passent en veille pour économiser la batterie. Jetez un œil rapide au code, puis nous verrons les détails.
#include "GxEPD2_BW.h"
#include "Fonts/FreeSans9pt7b.h"
#include "Fonts/FreeSansBold9pt7b.h"
#include "WiFi.h"
#include "esp_sntp.h"
const char* TIMEZONE = "AEST-10AEDT,M10.1.0,M4.1.0/3";
const char* SSID = "SSID";
const char* PWD = "PASSWORD";
const char* DAYSTR[] = {
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
};
// W, H flipped due to setRotation(1)
const int H = GxEPD2_420_GDEY042T81::WIDTH;
const int W = GxEPD2_420_GDEY042T81::HEIGHT;
const int CW = W / 2;
const int CH = H / 2;
const int R = min(W, H) / 2;
const uint16_t WHITE = GxEPD_WHITE;
const uint16_t BLACK = GxEPD_BLACK;
RTC_DATA_ATTR uint16_t wakeups = 0;
GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> epd(GxEPD2_420_GDEY042T81(5, 0, 2, 15));
void initDisplay() {
bool initial = wakeups == 0;
epd.init(115200, initial, 50, false);
epd.setRotation(1);
epd.setTextSize(1);
epd.setTextColor(BLACK);
}
void setTimezone() {
setenv("TZ", TIMEZONE, 1);
tzset();
}
void syncTime() {
if (wakeups % 50 == 0) {
WiFi.begin(SSID, PWD);
while (WiFi.status() != WL_CONNECTED)
;
configTzTime(TIMEZONE, "pool.ntp.org");
}
}
void printAt(int16_t x, int16_t y, const char* text) {
int16_t x1, y1;
uint16_t w, h;
epd.getTextBounds(text, x, y, &x1, &y1, &w, &h);
epd.setCursor(x - w / 2, y + h / 2);
epd.print(text);
}
void printfAt(int16_t x, int16_t y, const char* format, ...) {
static char buff[64];
va_list args;
va_start(args, format);
vsnprintf(buff, 64, format, args);
printAt(x, y, buff);
}
void polar2cart(float x, float y, float r, float alpha, int& cx, int& cy) {
alpha = alpha * TWO_PI / 360;
cx = int(x + r * sin(alpha));
cy = int(y - r * cos(alpha));
}
void drawClockFace() {
int cx, cy;
epd.setFont(&FreeSansBold9pt7b);
epd.drawCircle(CW, CH, R - 2, BLACK);
epd.fillCircle(CW, CH, 8, BLACK);
for (int h = 1; h <= 12; h++) {
float alpha = 360.0 * h / 12;
polar2cart(CW, CH, R - 20, alpha, cx, cy);
printfAt(cx, cy, "%d", h);
polar2cart(CW, CH, R - 40, alpha, cx, cy);
epd.fillCircle(cx, cy, 3, BLACK);
}
for (int m = 1; m <= 12 * 5; m++) {
float alpha = 360.0 * m / (12 * 5);
polar2cart(CW, CH, R - 40, alpha, cx, cy);
epd.fillCircle(cx, cy, 2, BLACK);
}
}
void drawTriangle(float alpha, int width, int len) {
int x0, y0, x1, y1, x2, y2;
polar2cart(CW, CH, len, alpha, x2, y2);
polar2cart(CW, CH, width, alpha - 90, x1, y1);
polar2cart(CW, CH, width, alpha + 90, x0, y0);
epd.drawTriangle(x0, y0, x1, y1, x2, y2, BLACK);
}
void drawClockHands() {
struct tm t;
getLocalTime(&t);
float alphaM = 360 * (t.tm_min / 60.0);
float alphaH = 30 * (t.tm_hour % 12);
drawTriangle(alphaM, 8, R - 50);
drawTriangle(alphaH, 8, R - 65);
}
void drawDateDay() {
struct tm t;
getLocalTime(&t);
epd.setFont(&FreeSans9pt7b);
printfAt(CW, CH+R/3, "%02d-%02d-%02d",
t.tm_mday, t.tm_mon + 1, t.tm_year -100);
printfAt(CW, CH-R/3, "%s", DAYSTR[t.tm_wday]);
}
void drawClock(const void* pv) {
if (wakeups % 120 == 0) {
epd.setFullWindow();
} else {
epd.setPartialWindow(0, 0, W, H);
}
epd.fillScreen(WHITE);
drawClockFace();
drawClockHands();
drawDateDay();
}
void setup() {
initDisplay();
setTimezone();
syncTime();
epd.drawPaged(drawClock, 0);
epd.hibernate();
wakeups = (wakeups + 1) % 1000;
esp_sleep_enable_timer_wakeup(30 * 1000 * 1000);
esp_deep_sleep_start();
}
void loop() {
}
Si vous téléversez le code sur votre ESP32, vous devriez voir l’horloge suivante s’afficher :

Elle affiche le cadran, les deux aiguilles, ainsi que le jour et la date actuels.
Bibliothèques
Nous commençons par inclure le fichier GxEPD2_BW.h header pour l’écran e-Paper noir et blanc (BW). Si vous avez un écran 3 couleurs, incluez GxEPD2_3C.h , ou GxEPD2_4C.h pour un écran 4 couleurs, et GxEPD2_7C.h pour un écran 7 couleurs, à la place.
#include "GxEPD2_BW.h" #include "Fonts/FreeSans9pt7b.h" #include "Fonts/FreeSansBold9pt7b.h"
Nous incluons aussi deux fichiers pour les polices utilisées pour afficher les labels de l’horloge et la date. Les labels de l’horloge sont en gras ( FreeSansBold9pt7b ) et la date en police plus fine ( FreeSans9pt7b ) – voir la photo de l’horloge ci-dessus. Vous pouvez trouver un aperçu des AdaFruit GFX fonts ici.
Enfin, nous incluons les bibliothèques WiFi.h et esp_snt.h , qui seront nécessaires pour synchroniser l’horloge avec un serveur de temps SNTP via Wi-Fi.
#include "WiFi.h" #include "esp_sntp.h"
Constantes
Ensuite, nous définissons quelques constantes. Le plus important, vous devrez définir le SSID et le PASSWORD pour votre WiFi, ainsi que le TIMEZONE où vous vivez.
const char* SSID = "SSID"; const char* PWD = "PASSWORD"; const char* TIMEZONE = "AEST-10AEDT,M10.1.0,M4.1.0/3";
La spécification de fuseau horaire ci-dessus » AEST-10AEDT,M10.1.0,M4.1.0/3 » est pour l’Australie, ce qui correspond à l’Australian Eastern Standard Time (AEST) avec les ajustements pour l’heure d’été.
Les parties de cette définition de fuseau horaire sont les suivantes
- AEST : Australian Eastern Standard Time
- -10 : Décalage UTC de 10 heures en avance sur le Temps Universel Coordonné (UTC)
- AEDT : Australian Eastern Daylight Time
- M10.1.0 : Passage à l’heure d’été le 1er dimanche d’octobre
- M4.1.0/3 : Retour à l’heure standard le 1er dimanche d’avril, avec un décalage de 3 heures par rapport à l’UTC.
Pour d’autres définitions de fuseaux horaires, consultez le Posix Timezones Database . Copiez-collez simplement la chaîne trouvée et modifiez la constante TIMEZONE en conséquence.
En plus de la date, nous voulons aussi afficher le nom du jour actuel, par exemple jeudi. La constante DAYSTR définit les noms des jours. N’hésitez pas à changer la langue ou à choisir des noms plus courts, mais gardez le même ordre.
const char* DAYSTR[] = {
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
};
Ensuite, nous redéfinissons des constantes pour la largeur W et la hauteur H de l’écran. C’est surtout pour plus de simplicité. Notez que la largeur et la hauteur sont inversées, car nous faisons pivoter l’écran ( setRotation(1) ) dans la fonction initDisplay .
const int H = GxEPD2_420_GDEY042T81::WIDTH; const int W = GxEPD2_420_GDEY042T81::HEIGHT;
Enfin, nous définissons des constantes pour la position centrale ( CW , CH ) sur l’écran, le rayon maximal R d’un cercle sur l’écran, et des noms plus courts pour les couleurs noir et blanc.
const int CW = W / 2; const int CH = H / 2; const int R = min(W, H) / 2; const uint16_t WHITE = GxEPD_WHITE; const uint16_t BLACK = GxEPD_BLACK;
Variables et objets
Après les constantes, nous définissons une variable globale pour compter les réveils et l’objet d’affichage.
RTC_DATA_ATTR uint16_t wakeups = 0; GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> epd(GxEPD2_420_GDEY042T81(5, 0, 2, 15));
La variable wakeups est incrémentée à chaque réveil de l’ESP32 après un deep-sleep. Elle est stockée dans RTC memory pour conserver sa valeur même lorsque l’ESP32 passe en mode deep-sleep. Nous l’utilisons pour décider quand effectuer un rafraîchissement complet ou partiel de l’écran, et à quelle fréquence synchroniser l’heure.
L’objet epd représente l’écran ( e – p aper d isplay). Cette définition d’objet doit correspondre au type d’écran e-Paper que vous avez. Ici, il s’agit d’un écran 4,2 pouces (= _420). Voir le Readme de la GxEPD2 bibliothèque et le fichier GxEPD2.h pour les écrans pris en charge.
Fonction initDisplay
La fonction initDisplay initialise l’écran, définit l’orientation en paysage, la taille du texte à 1 et la couleur du texte en noir.
void initDisplay() {
bool initial = wakeups == 0;
epd.init(115200, initial, 50, false);
epd.setRotation(1);
epd.setTextSize(1);
epd.setTextColor(BLACK);
}
epd.init() provoque généralement un rafraîchissement complet de l’écran lorsqu’elle est exécutée. Cependant, nous voulons un rafraîchissement complet uniquement après un reset de l’ESP32 et un rafraîchissement partiel lors d’un réveil après deep-sleep. Pour cela, nous définissons le paramètre initial de la fonction epd.init() à true lors du wakeups == 0 , c’est-à-dire après un reset, et sinon initial est à false, pour un rafraîchissement partiel.
Fonction setTimezone
La fonction setTimezone définit le TIMEZONE . Il faut l’appeler à chaque fois dans la fonction setup, car après un deep-sleep ou un reset, l’information de fuseau horaire est perdue.
void setTimezone() {
setenv("TZ", TIMEZONE, 1);
tzset();
}
Fonction syncTime
La fonction syncTime crée une connexion WiFi puis synchronise l’horloge interne de l’ESP32 avec le serveur SNTP » pool.ntp.org » en appelant configTzTime() .
Vous pouvez spécifier d’autres serveurs SNTP, ou même plusieurs, à contacter. Consultez le tutoriel How to synchronize ESP32 clock with SNTP server pour plus d’informations.
void syncTime() {
if (wakeups % 50 == 0) {
WiFi.begin(SSID, PWD);
while (WiFi.status() != WL_CONNECTED)
;
configTzTime(TIMEZONE, "pool.ntp.org");
}
}
Cependant, nous voulons synchroniser l’heure seulement de temps en temps. Comme l’ESP32 est réglé pour dormir 30 secondes (voir fonction loop ), l’expression wakeups % 50 == 0 , garantit que la synchronisation ne se fait que toutes les 25 minutes (30sec * 50 / 60sec).
Vous pouvez modifier cela, mais sachez que des synchronisations plus fréquentes consommeront plus de batterie (le Wi-Fi consomme beaucoup). Des synchronisations moins fréquentes signifient que l’horloge réagit plus lentement lors du passage à l’heure d’été.
Fonctions printAt
Les fonctions printAt affichent du texte centré aux coordonnées spécifiées de l’écran.
void printAt(int16_t x, int16_t y, const char* text) {
int16_t x1, y1;
uint16_t w, h;
epd.getTextBounds(text, x, y, &x1, &y1, &w, &h);
epd.setCursor(x - w / 2, y + h / 2);
epd.print(text);
}
void printfAt(int16_t x, int16_t y, const char* format, ...) {
static char buff[64];
va_list args;
va_start(args, format);
vsnprintf(buff, 64, format, args);
printAt(x, y, buff);
}
Alors que printAt() n’affiche que du texte simple, la fonction printfAt() fonctionne comme printf et vous permet de fournir un format, par exemple printfAt(x, y, "%02d-%02d-%02d", m, h, y);
Fonction polar2cart
La fonction polar2cart convertit des polar coordinates données par un rayon r et un angle alpha en cartesian coordinates cx, cy .
void polar2cart(float x, float y, float r, float alpha, int& cx, int& cy) {
alpha = alpha * TWO_PI / 360;
cx = int(x + r * sin(alpha));
cy = int(y - r * cos(alpha));
}
Cette fonction est très pratique, car les données de temps comme 8h15 sont essentiellement des coordonnées polaires sur l’horloge et nous devons les convertir dans le système cartésien utilisé par l’écran.
Fonction drawClockFace
La fonction drawClockFace() , comme son nom l’indique, dessine le cadran de l’horloge. Cela inclut le cadre, les graduations des minutes et les labels et graduations des heures. Comme vous pouvez le voir, elle utilise bien la fonction polar2cart() que nous avons vue précédemment.
void drawClockFace() {
int cx, cy;
epd.setFont(&FreeSansBold9pt7b);
// Frame
epd.drawCircle(CW, CH, R - 2, BLACK);
epd.fillCircle(CW, CH, 8, BLACK);
// Hour ticks and labels
for (int h = 1; h <= 12; h++) {
float alpha = 360.0 * h / 12;
polar2cart(CW, CH, R - 20, alpha, cx, cy);
printfAt(cx, cy, "%d", h);
polar2cart(CW, CH, R - 40, alpha, cx, cy);
epd.fillCircle(cx, cy, 3, BLACK);
}
// Minute ticks
for (int m = 1; m <= 12 * 5; m++) {
float alpha = 360.0 * m / (12 * 5);
polar2cart(CW, CH, R - 40, alpha, cx, cy);
epd.fillCircle(cx, cy, 2, BLACK);
}
}
Fonction drawTriangle
La fonction drawTriangle() dessine un triangle mais en coordonnées polaires. La base du triangle est au centre de l’horloge et le triangle est orienté selon l’angle alpha . width est la largeur de la base et len est la longueur du triangle. Cette fonction sert à dessiner les aiguilles de l’horloge.
void drawTriangle(float alpha, int width, int len) {
int x0, y0, x1, y1, x2, y2;
polar2cart(CW, CH, len, alpha, x2, y2);
polar2cart(CW, CH, width, alpha - 90, x1, y1);
polar2cart(CW, CH, width, alpha + 90, x0, y0);
epd.drawTriangle(x0, y0, x1, y1, x2, y2, BLACK);
}
Vous pouvez passer de epd.drawTriangle() à epd.fillTriangle() , si vous préférez des aiguilles pleines. Elles sont plus visibles mais peuvent masquer l’affichage du jour et de la date.
Fonction drawClockHands
Le dessin réel des aiguilles est effectué par la fonction drawClockHands() . Elle récupère l’heure locale, convertit les minutes et heures en angles puis utilise drawTriangle() pour dessiner les aiguilles.
void drawClockHands() {
struct tm t;
getLocalTime(&t);
float alphaM = 360 * (t.tm_min / 60.0);
float alphaH = 30 * (t.tm_hour % 12);
drawTriangle(alphaM, 8, R - 50);
drawTriangle(alphaH, 8, R - 65);
}
Fonction drawDateDay
En plus de l’heure affichée par les aiguilles, je voulais aussi afficher la date et le jour de la semaine. La fonction drawDateDay() récupère l’heure et la date locales puis affiche le nom du jour, par exemple jeudi, en haut du cadran, et la date, par exemple 05-09-24, en bas. Vous pouvez facilement changer le format pour l’adapter à votre pays.
void drawDateDay() {
struct tm t;
getLocalTime(&t);
epd.setFont(&FreeSans9pt7b);
printfAt(CW, CH+R/3, "%02d-%02d-%02d",
t.tm_mday, t.tm_mon + 1, t.tm_year -100);
printfAt(CW, CH-R/3, "%s", DAYSTR[t.tm_wday]);
}
Fonction drawClock
La fonction drawClock() regroupe tout. Elle efface l’écran puis dessine le cadran, les aiguilles et la date.
void drawClock(const void* pv) {
if (wakeups % 120 == 0) {
epd.setFullWindow();
} else {
epd.setPartialWindow(0, 0, W, H);
}
epd.fillScreen(WHITE);
drawClockFace();
drawClockHands();
drawDateDay();
}
Selon le compteur wakeups , un rafraîchissement complet ou partiel de l’écran est effectué. Un rafraîchissement complet est lent (2-4 secondes) et provoque beaucoup de clignotements. Un rafraîchissement partiel est bien plus rapide (<0,5 sec) et sans clignotement.
Cependant, un rafraîchissement partiel laisse une légère image fantôme de l’ancien contenu. Si vous regardez attentivement la photo ci-dessous, vous pouvez voir les images fantômes de l’aiguille des minutes, lorsqu’elle est passée de 5 à 10.

Pour supprimer les images fantômes, il faut parfois effectuer un rafraîchissement complet de l’écran. Dans la fonction drawClock() , nous le faisons lorsque le compteur wakeup atteint 120 : wakeups % 120 == 0 . Comme le temps de deep-sleep est de 30 secondes, cela fait un rafraîchissement complet toutes les 30sec*120/60sec = 60 minutes.
Fonctions setup et loop
Enfin, nous avons les fonctions classiques setup et loop . La fonction loop est vide, car l’ESP32 passe en deep-sleep à la fin de la fonction setup et la fonction loop n’est donc jamais exécutée.
void setup() {
initDisplay();
setTimezone();
syncTime();
epd.drawPaged(drawClock, 0);
epd.hibernate();
wakeups = (wakeups + 1) % 1000;
esp_sleep_enable_timer_wakeup(30 * 1000 * 1000);
esp_deep_sleep_start();
}
void loop() {
}
La fonction setup commence par initialiser l’écran, définir le fuseau horaire et (éventuellement) synchroniser l’heure (selon le compteur de réveil).
Ensuite, nous appelons drawPaged pour exécuter les fonctions drawClock , puis nous mettons l’écran en hibernation pour économiser l’énergie. Si vous voulez plus de détails, consultez le tutoriel Partial Refresh of e-Paper Display , où nous expliquons la fonction drawPaged plus en détail.
Avant de mettre l’ESP32 en deep-sleep pour 30 secondes (30 * 1000 * 1000 microsecondes), nous incrémentons le compteur wakeup et calculons le modulo 1000 pour éviter un dépassement.
Et voilà ! Avec ce code, vous avez une belle horloge analogique toujours à l’heure et pouvant fonctionner sur batterie.
Conclusions
Dans ce tutoriel, vous avez appris à construire une horloge analogique sur un écran e-Paper 4,2″ qui synchronise son heure avec un serveur SNTP et affiche toujours l’heure et la date exactes. L’horloge utilise la fonction deep-sleep de l’ESP32 pour réduire la consommation et augmenter l’autonomie sur batterie.
Si vous utilisez l’ESP32 LOLIN Lite recommandé, connecter une batterie LiPo rechargeable est simple et vous pouvez utiliser un MAX1704X pour surveiller le niveau de charge.
Enfin, si vous préférez une horloge digitale à une horloge analogique, jetez un œil à notre tutoriel Digital Clock on e-Paper Display .
Bon bricolage ; )

