Dans ce tutoriel, vous apprendrez comment effectuer un rafraîchissement partiel rapide d’un écran e-Paper. Les principaux avantages des écrans e-Paper sont leur faible consommation d’énergie et leur excellente lisibilité. Mis à part la gamme de couleurs limitée, leur plus grand inconvénient est la lenteur du taux de rafraîchissement.
Il faut généralement 2 à 4 secondes pour qu’un écran e-Paper noir et blanc rafraîchisse l’intégralité de son contenu, et pour les écrans couleur, cela prend beaucoup plus de temps (>20 secondes). Le rafraîchissement complet s’accompagne aussi de beaucoup de scintillements, ce qui est gênant.
Heureusement, vous pouvez effectuer un rafraîchissement partiel, qui est non seulement beaucoup plus rapide (0,3 seconde) mais évite aussi le scintillement de l’écran. Pour toute application pratique qui met à jour le contenu de l’écran plus d’une fois toutes les 10 minutes environ, il est clairement préférable d’utiliser un rafraîchissement partiel.
Cependant, implémenter un rafraîchissement partiel sur un écran e-Paper peut être assez complexe. Ce tutoriel vous montre comment rafraîchir une ou plusieurs zones de l’écran, comment combiner le rafraîchissement partiel avec le mode deep sleep, et comment rafraîchir partiellement des pixels individuels pour du tracé.
Commençons par les pièces nécessaires.
Pièces requises
Pour les pièces requises, j’ai listé un ancien ESP32 lite, qui est obsolète mais que vous pouvez encore trouver à bas prix. Il existe un modèle successeur (Amazon) avec des spécifications améliorées. Mais tout autre ESP32, ESP8266 ou Arduino avec suffisamment de mémoire fonctionnera également.

Écran e-Paper 2,9″

ESP32 lite

Câble USB de données

Jeu de fils Dupont

Plaque d’essai (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
Pour ce projet, j’utilise un module d’écran e-Paper 2,9″, avec une résolution de 296×128 pixels et un contrôleur intégré avec interface SPI.

Notez que la plupart des modules d’écran e-Paper avec SPI ont un petit interrupteur ou cavalier qui permet de passer du SPI 4 fils au SPI 3 fils. Nous allons utiliser ici le SPI 4 fils par défaut.

Le module d’écran 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/mise à jour de son contenu. Cela signifie que vous pouvez faire fonctionner un écran e-Paper longtemps même avec une petite batterie.
Pour plus d’informations sur les écrans e-Paper, consultez notre tutoriel Interfacing Arduino To An E-ink Display.
Connexion et test de l’e-Paper
Commençons par connecter et tester le fonctionnement de l’e-Paper. L’image suivante montre le câblage complet pour l’alimentation et le SPI.

Voici un tableau récapitulatif des connexions pour plus de commodité. 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 |
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 est une bibliothèque graphique de base qui fournit un ensemble commun de primitives graphiques (texte, points, lignes, cercles, etc.). Et la GxEPD2 fournit le pilote graphique pour contrôler un écran e-Paper via SPI.
Installez simplement les bibliothèques de la manière habituelle. Après installation, elles devraient apparaître dans le Library Manager comme suit.

Code de test
Voici un code de test simple qui affiche le texte « Makerguides » sur l’écran. Regardez d’abord le code complet, puis nous en discuterons.
#define ENABLE_GxEPD2_GFX 0
#include "GxEPD2_BW.h"
//CS(SS)=5, SCL(SCK)=18, SDA(MOSI)=23, BUSY=15, RES(RST)=2, DC=0
GxEPD2_BW<GxEPD2_290_BS, GxEPD2_290_BS::HEIGHT> epd(GxEPD2_290_BS(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(20, 20);
epd.print("Makerguides");
epd.display();
epd.hibernate();
}
void loop() {}
Décomposons le code en ses composants pour comprendre son fonctionnement.
Constantes et bibliothèques
Le code commence par définir une constante ENABLE_GxEPD2_GFX à 0. Vous pouvez la mettre à 1, ce qui, selon la documentation, permet à la classe de base GxEPD2_GFX de passer des références ou pointeurs à l’instance d’affichage en paramètre. Mais cela ajoute environ 1,2k de code inutile ici, donc elle est mise à 0.
#define ENABLE_GxEPD2_GFX 0
Ensuite, nous incluons le fichier d’en-tête GxEPD2_BW.h pour l’écran e-Paper noir et blanc. Si vous avez un écran 3 couleurs, vous incluriez GxEPD2_3C.h, ou GxEPD2_4C.h pour un écran 4 couleurs, et GxEPD2_7C.h pour un écran 7 couleurs.
#include "GxEPD2_BW.h"
Objet d’affichage
La ligne suivante est importante. Elle crée l’objet d’affichage et dépend du type ou de la marque de l’écran. J’ai essayé un WeAct et un WaveShare, et la ligne suivante fonctionne pour les deux.
GxEPD2_BW<GxEPD2_290_BS, GxEPD2_290_BS::HEIGHT> epd(GxEPD2_290_BS(5, 0, 2, 15));
La bibliothèque Readme for GxEPD2 liste tous les écrans supportés et vous pouvez trouver les détails dans les fichiers d’en-tête, par exemple GxEPD2.h.
Fonction setup
Tout le travail se fait dans la fonction setup. D’abord, on configure les paramètres de l’écran comme la vitesse de communication, la rotation, la police, la couleur du texte et la couleur de fond. Si vous avez des problèmes de rafraîchissement, vous devrez peut-être ajuster les paramètres de la fonction init().
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(20, 20);
epd.print("Makerguides");
epd.display();
epd.hibernate();
}
Ensuite, on remplit l’écran en blanc, on positionne le curseur, on écrit le texte, puis on met le pilote d’affichage en mode hibernation. Cela coupe l’alimentation de l’écran et met le contrôleur en mode deep-sleep (pas l’ESP32, seulement l’écran !).
Fonction loop
La fonction loop() est vide dans cet exemple car le contenu de l’écran est créé dans la fonction setup() et n’a pas besoin d’être mis à jour en continu.
void loop() {}
Téléverser et exécuter le code
Maintenant, nous sommes prêts à téléverser et exécuter le code. Sélectionnez la carte dans le gestionnaire de cartes. Dans mon cas, c’est la WEMOS LOLIN32 Lite listée dans les pièces requises :

Puis appuyez sur téléverser et après quelques scintillements, votre écran devrait afficher le texte suivant :

Rafraîchissement complet
Juste pour montrer à quel point un rafraîchissement complet d’un écran e-Paper est mauvais, nous allons utiliser le code suivant. Il affiche le texte « msec: » suivi des millisecondes écoulées depuis le démarrage du microcontrôleur.
#define ENABLE_GxEPD2_GFX 0
#include "GxEPD2_BW.h"
GxEPD2_BW<GxEPD2_290_BS, GxEPD2_290_BS::HEIGHT> epd(GxEPD2_290_BS(5, 0, 2, 15));
void setup() {
epd.init(115200, true, 50, false);
epd.setRotation(1);
epd.setTextSize(2);
epd.setTextColor(GxEPD_BLACK);
}
void loop() {
epd.fillScreen(GxEPD_WHITE);
epd.setCursor(40, 60);
epd.print("msec: ");
epd.setCursor(150, 60);
epd.print(millis());
epd.display();
epd.hibernate();
}
Le contenu de l’écran est mis à jour et rafraîchi dans la boucle principale aussi vite que possible. La vidéo suivante montre ce que cela donne. Comme vous pouvez le voir, il y a beaucoup de scintillements et le rafraîchissement prend presque 3 secondes.

Donc, pour toute application avec des mises à jour fréquentes, un rafraîchissement complet est trop lent pour être utile.
Rafraîchissement partiel d’une zone unique
Au lieu d’effectuer un rafraîchissement complet à chaque mise à jour de la valeur des millisecondes, on peut faire un rafraîchissement partiel. Le code suivant effectue un rafraîchissement complet une fois au démarrage, puis ne rafraîchit que la zone de l’écran affichant la valeur des millisecondes :
#define ENABLE_GxEPD2_GFX 0
#include "GxEPD2_BW.h"
GxEPD2_BW<GxEPD2_290_BS, GxEPD2_290_BS::HEIGHT> epd(GxEPD2_290_BS(5, 0, 2, 15));
void drawFull(const void* pv) {
epd.setFullWindow();
epd.setCursor(40, 60);
epd.print("msec: ");
}
void drawPartial(const void* pv) {
uint16_t x = 120, y = 50, w = 130, h = 34;
epd.setPartialWindow(x, y, w, h);
epd.setCursor(x + 30, y + 10);
epd.print(millis());
epd.drawRect(x, y, w, h, GxEPD_BLACK);
}
void setup() {
epd.init(115200, true, 50, false);
epd.setRotation(1);
epd.setTextSize(2);
epd.setTextColor(GxEPD_BLACK);
epd.drawPaged(drawFull, 0);
}
void loop() {
epd.drawPaged(drawPartial, 0);
epd.hibernate();
}
La vidéo suivante montre le rafraîchissement partiel. Comme vous pouvez le voir, le scintillement a disparu et la mise à jour des millisecondes est beaucoup plus rapide (< 1 sec).

Un rafraîchissement complet a lieu au démarrage du code, avec le scintillement et la lenteur habituels, mais cela ne se produit qu’une seule fois.
Dessiner par pages
Il existe différentes façons d’implémenter un rafraîchissement complet ou partiel. Utiliser la fonction drawPaged() est probablement la plus élégante et la plus simple. Elle prend une fonction drawCallback et un vecteur de paramètres pv pour effectuer un redessin paginé :
void drawPaged(void (*drawCallback)(const void*), const void* pv)
Dans la fonction drawCallback, on appelle soit etFullWindow() pour un rafraîchissement complet, soit setPartialWindow(x, y, w, h) pour un rafraîchissement partiel.
Utiliser drawPaged() a aussi l’avantage de gérer les opérations de dessin de manière économe en mémoire, ce qui est particulièrement utile pour les grands écrans ou quand la RAM est limitée. Il dessine le contenu en plusieurs petites étapes (pages) au lieu de rafraîchir tout l’écran d’un coup.
Fonction drawFull
Si vous regardez la fonction drawFull(), vous verrez que nous appelons setFullWindow(), positionnons le curseur puis affichons le texte statique "msec: ".
void drawFull(const void* pv) {
epd.setFullWindow();
epd.setCursor(40, 60);
epd.print("msec: ");
}
Dans la fonction setup, on initialise l’écran, définit ses propriétés, puis on appelle drawPaged(drawFull, 0) pour effectuer le rafraîchissement complet.
void setup() {
epd.init(115200, true, 50, false);
epd.setRotation(1);
epd.setTextSize(2);
epd.setTextColor(GxEPD_BLACK);
epd.drawPaged(drawFull, 0);
}
Le vecteur de paramètres pv est mis à 0, car nous ne passons aucun paramètre. Mais vous pourriez fournir la police, la couleur, le texte ou d’autres informations pertinentes à la fonction drawFull ici.
Fonction drawPartial
La principale différence entre drawPartial() et drawFull() est que setPartialWindow(x, y, w, h) est appelé pour limiter la zone d’affichage à rafraîchir.
void drawPartial(const void* pv) {
uint16_t x = 120, y = 50, w = 130, h = 34;
epd.setPartialWindow(x, y, w, h);
epd.setCursor(x + 30, y + 10);
epd.print(millis());
epd.drawRect(x, y, w, h, GxEPD_BLACK);
}
Ensuite, on positionne le curseur, affiche le texte des millisecondes et dessine un rectangle pour marquer la fenêtre de rafraîchissement.
Enfin, la fonction drawPartial() est appelée via drawPaged(drawPartial, 0) dans la boucle principale, ce qui provoque un rafraîchissement répété des millisecondes affichées.
void loop() {
epd.drawPaged(drawPartial, 0);
epd.hibernate();
}
Notez que tout ce que vous dessinez en dehors de la zone partielle ne sera pas affiché. Dans drawPartial(), nous positionnons le curseur à l’intérieur de la zone et affichons la valeur des millisecondes dans cette zone.
Pour montrer la zone de rafraîchissement partiel, nous dessinons un rectangle autour. Cependant, ce n’est pas tout à fait exact ! Plus d’explications dans la section suivante.
Alignement de la fenêtre de rafraîchissement
À cause des limitations d’adressage des contrôleurs d’écran e-Paper, les coordonnées de la fenêtre de rafraîchissement doivent être alignées sur des multiples de 8. Plus précisément,
- x et w doivent être multiples de 8 pour les rotations d’écran 0 ou 2,
- y et h doivent être multiples de 8 pour les rotations 1 ou 3.
La fonction setPartialWindow(x, y, w, h) vous permet de fournir des valeurs arbitraires mais les aligne en interne comme requis. Cela signifie que la fenêtre de rafraîchissement réelle peut être plus grande que celle spécifiée.
C’est important car cela signifie que des parties de l’écran seront mises à jour alors que vous ne le souhaitiez pas forcément. L’image ci-dessous montre la zone de rafraîchissement prévue (rectangle noir) et la zone réelle (rectangle blanc rempli).

Comme vous le voyez, la hauteur de la fenêtre réelle est plus grande que celle spécifiée. Si vous concevez la mise en page de votre contenu, vous devez en tenir compte. Surtout que la fonction drawRect() remplit entièrement le fond de la fenêtre réelle en blanc. Cela signifie qu’elle efface le contenu en dehors de la fenêtre spécifiée mais à l’intérieur de la fenêtre réelle.
Si vous voulez reproduire l’expérience ci-dessus, voici le code. Il inverse simplement la couleur de fond et du texte du redessin complet pour rendre visible la fenêtre réelle de rafraîchissement.
#define ENABLE_GxEPD2_GFX 0
#include "GxEPD2_BW.h"
GxEPD2_BW<GxEPD2_290_BS, GxEPD2_290_BS::HEIGHT> epd(GxEPD2_290_BS(5, 0, 2, 15));
void drawFull(const void* pv) {
epd.setFullWindow();
epd.fillScreen(GxEPD_BLACK);
epd.setCursor(40, 60);
epd.print("msec: ");
}
void drawPartial(const void* pv) {
uint16_t x = 120, y = 50, w = 130, h = 34;
epd.setTextColor(GxEPD_BLACK);
epd.setPartialWindow(x, y, w, h);
epd.setCursor(x + 30, y + 10);
epd.print(millis());
epd.drawRect(x, y, w, h, GxEPD_BLACK);
}
void setup() {
epd.init(115200, true, 50, false);
epd.setRotation(1);
epd.setTextSize(2);
epd.setTextColor(GxEPD_WHITE);
epd.drawPaged(drawFull, 0);
}
void loop() {
epd.drawPaged(drawPartial, 0);
epd.hibernate();
}
Rafraîchissement partiel avec deep sleep
Les écrans e-Paper sont souvent utilisés dans des projets alimentés par batterie, où le microcontrôleur passe généralement en deep-sleep. Comment combiner le deep-sleep d’un ESP32 avec le rafraîchissement partiel d’un écran e-Paper est le sujet de cette section.
La principale difficulté est que l’on veut effectuer un rafraîchissement complet uniquement au premier démarrage (reset) de l’ESP32, mais à chaque réveil du deep-sleep, on veut faire un rafraîchissement partiel. Le code suivant y parvient. Regardez-le rapidement, puis nous détaillons.
#define ENABLE_GxEPD2_GFX 0
#include "GxEPD2_BW.h"
GxEPD2_BW<GxEPD2_290_BS, GxEPD2_290_BS::HEIGHT> epd(GxEPD2_290_BS(5, 0, 2, 15));
RTC_DATA_ATTR bool initial = true;
void drawFull(const void* pv) {
epd.setFullWindow();
epd.setCursor(40, 60);
epd.print("msec: ");
initial = false;
}
void drawPartial(const void* pv) {
uint16_t x = 120, y = 50, w = 130, h = 34;
epd.setPartialWindow(x, y, w, h);
epd.setCursor(x + 30, y + 10);
epd.print(millis());
epd.setCursor(x + 100, y + 10);
epd.print(random(10));
epd.drawRect(x, y, w, h, GxEPD_BLACK);
}
void setup() {
epd.init(115200, initial, 50, false);
epd.setRotation(1);
epd.setTextSize(2);
epd.setTextColor(GxEPD_BLACK);
if (initial)
epd.drawPaged(drawFull, 0);
}
void loop() {
epd.drawPaged(drawPartial, 0);
epd.hibernate();
esp_sleep_enable_timer_wakeup(1000);
esp_deep_sleep_start();
}
Le code est fondamentalement le même que dans l’exemple précédent, avec quelques changements importants.
D’abord, on définit une variable initial stockée en RTC memory, ce qui signifie que sa valeur est conservée en deep-sleep.
RTC_DATA_ATTR bool initial = true;
La variable initial est initialisée à true et après le premier rafraîchissement complet via drawFull() , elle est mise à false. Cela signifie initial=true après un reset, mais false après un réveil du deep-sleep.
Ensuite, il faut indiquer à l’écran qu’il ne doit pas faire de rafraîchissement complet après le deep-sleep. La fonction init(serial_diag_bitrate, initial, reset_duration pulldown_rst_mode) a le paramètre initial pour cela :
epd.init(115200, initial, 50, false);
Nous avons presque fini. Dans la fonction setup, on effectue un redessin complet seulement si initial==true. Ce qui signifie uniquement après un reset, pas après un réveil du deep-sleep.
void setup() {
...
if (initial)
epd.drawPaged(drawFull, 0);
}
Enfin, dans la fonction loop, on met l’ESP32 en veille après avoir effectué un rafraîchissement partiel.
void loop() {
epd.drawPaged(drawPartial, 0);
epd.hibernate();
esp_sleep_enable_timer_wakeup(1000);
esp_deep_sleep_start();
}
Selon la documentation de la fonction init(), il suffit de s’assurer que l’alimentation de l’écran est maintenue pendant le deep-sleep. Appeler hibernate, comme montré ci-dessus, est correct. La vidéo suivante montre le résultat.

Comme millis() se réinitialise pendant le deep-sleep et affiche une valeur constante de 151 millisecondes, j’affiche aussi un nombre aléatoire (0..9) après la valeur msec pour rendre le rafraîchissement visible.
Rafraîchissement partiel de plusieurs zones
Parfois, on veut rafraîchir partiellement plusieurs zones à différents endroits et moments. Par exemple, vous pouvez vouloir rafraîchir l’affichage d’une horloge chaque seconde, mais celui d’une mesure de température toutes les 5 minutes.
Étendre l’exemple précédent pour rafraîchir plusieurs zones est simple. Voici un code complet qui rafraîchit la valeur des millisecondes affichée dans deux zones différentes.
#define ENABLE_GxEPD2_GFX 0
#include "GxEPD2_BW.h"
GxEPD2_BW<GxEPD2_290_BS, GxEPD2_290_BS::HEIGHT> epd(GxEPD2_290_BS(5, 0, 2, 15));
void drawText(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.setPartialWindow(x1, y1, w, h);
epd.setCursor(x, y);
epd.print(text);
epd.drawRect(x1, y1, w, h, GxEPD_BLACK);
}
void drawFull(const void* pv) {
epd.setFullWindow();
epd.setCursor(40, 60);
epd.print("msec: ");
}
void drawPartial1(const void* pv) {
char text[16];
sprintf(text, "1: %8d", millis());
drawText(120, 60, text);
}
void drawPartial2(const void* pv) {
char text[16];
sprintf(text, "2: %8d", millis());
drawText(120, 40, text);
}
void setup() {
epd.init(115200, true, 50, false);
epd.setRotation(1);
epd.setTextColor(GxEPD_BLACK);
epd.setTextSize(2);
epd.drawPaged(drawFull, 0);
}
void loop() {
epd.drawPaged(drawPartial1, 0);
epd.drawPaged(drawPartial2, 0);
epd.hibernate();
}
Comme vous le voyez, on définit simplement deux fonctions drawPartial1() et drawPartial2() qui sont appelées dans la boucle pour effectuer le rafraîchissement partiel de deux zones différentes.
void loop() {
epd.drawPaged(drawPartial1, 0);
epd.drawPaged(drawPartial2, 0);
epd.hibernate();
}
Pour rendre les choses un peu plus intéressantes et pratiques, j’ai ajouté une fonction drawText() qui calcule la boîte englobante d’un texte donné, définit la fenêtre de rafraîchissement aux mêmes dimensions, affiche le texte et dessine la boîte englobante :
void drawText(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.setPartialWindow(x1, y1, w, h);
epd.setCursor(x, y);
epd.print(text);
epd.drawRect(x1, y1, w, h, GxEPD_BLACK);
}
Si vous utilisez ce code, assurez-vous que le texte a toujours la même longueur. Un texte qui rétrécit entre les mises à jour peut laisser des artefacts d’affichage, car la boîte englobante et la zone de rafraîchissement deviennent plus petites. Dans le code ci-dessus, j’utilise le spécificateur de format "%8d" pour garantir une longueur constante malgré les nombres changeants. La vidéo ci-dessous montre le code en action :

Notez que le rafraîchissement prend maintenant deux fois plus de temps, car nous effectuons deux mises à jour indépendantes. Vous ne pouvez pas utiliser deux fenêtres de rafraîchissement différentes (setPartialWindow) dans la même fonction de dessin. Mais vous pourriez spécifier une fenêtre plus grande englobant les deux contenus à mettre à jour. Dans ce cas, vous n’auriez pas trop à vous soucier des différentes longueurs de texte.
Rafraîchissement partiel avec tampon d’écran
Pour finir, je voulais implémenter un simple traceur de données, par exemple pour surveiller la température dans le temps. L’image suivante montre à quoi ressemblera le traceur. Il a un titre fixe « Temperature », un axe x fixe, et des valeurs de température simulées qui changent dynamiquement.

Idéalement, on tracerait simplement des pixels individuels (points de données) avec une fenêtre de rafraîchissement partielle d’un pixel. Cependant, cela ne fonctionne pas, car les dimensions de la fenêtre de rafraîchissement sont limitées aux multiples de 8 (voir plus haut). Si vous essayez, vous verrez que la fenêtre plus grande efface certains pixels tracés précédemment et des parties de l’axe.
Pour contourner ce problème, on dessine d’abord sur une « toile » (canvas). Une toile est essentiellement un tampon d’écran que l’on peut mettre à jour en arrière-plan. Le rafraîchissement partiel prend alors une petite zone de la toile pour mettre à jour l’écran.

Le code suivant contient l’implémentation complète de ce petit traceur de température.
#define ENABLE_GxEPD2_GFX 0
#include "GxEPD2_BW.h"
// W, H flipped due to setRotation(1)
const int W = GxEPD2_290_BS::HEIGHT;
const int H = GxEPD2_290_BS::WIDTH;
const uint16_t WHITE = GxEPD_WHITE;
const uint16_t BLACK = GxEPD_BLACK;
GxEPD2_BW<GxEPD2_290_BS, GxEPD2_290_BS::HEIGHT> epd(GxEPD2_290_BS(5, 0, 2, 15));
GFXcanvas1 canvas(W, H);
void initCanvas() {
canvas.setTextColor(BLACK);
canvas.fillScreen(WHITE);
canvas.setTextSize(2);
canvas.setCursor(10, 10);
canvas.print("Temperature");
for (int i = 0; i < 10; i++) {
int x = 10 + i * W / 10, y = H / 2;
canvas.setTextSize(1);
canvas.setCursor(x - 2, y + 10);
canvas.print(i);
canvas.drawLine(x, y - 5, x, y + 5, BLACK);
}
canvas.drawLine(10, H / 2, W - 10, H / 2, BLACK);
}
void drawCanvas() {
epd.drawBitmap(0, 0, canvas.getBuffer(), W, H, WHITE, BLACK);
}
void drawFull(const void* pv) {
epd.setFullWindow();
drawCanvas();
}
void drawPartial(const void* pv) {
static int16_t x = 10;
x += 1;
if (x > W - 10) x = 10;
int16_t y = (H / 2) + (x / 10.0) * sin(x / 10.0);
canvas.writePixel(x, y, BLACK);
epd.setPartialWindow(x, y, 1, 1);
drawCanvas();
}
void setup() {
initCanvas();
epd.init(115200, true, 50, false);
epd.setRotation(1);
epd.drawPaged(drawFull, 0);
}
void loop() {
epd.drawPaged(drawPartial, 0);
epd.hibernate();
}
D’abord, on définit des constantes d’aide pour les dimensions de l’écran et les couleurs de premier plan et d’arrière-plan. Notez que largeur et hauteur sont inversées, car on fait une rotation de l’écran (setRotation(1)) dans la fonction setup.
// W, H flipped due to setRotation(1) const int W = GxEPD2_290_BS::HEIGHT; const int H = GxEPD2_290_BS::WIDTH; const uint16_t WHITE = GxEPD_WHITE; const uint16_t BLACK = GxEPD_BLACK;
Objet canvas
L’étape suivante importante est la création de l’objet canvas avec les mêmes dimensions que l’écran. Comme mon écran e-Paper n’a que deux couleurs (noir, blanc), on crée un canvas en 1 bit (GFXcanvas1).
GFXcanvas1 canvas(W, H);
Si votre écran a plus de couleurs ou de niveaux de gris, vous devez créer un canvas adapté. Consultez la Adafruit GFX Graphics Library documentation pour plus de détails.
initCanvas et drawCanvas
La fonction initCanvas() écrit simplement le titre et dessine l’axe x sur la toile, sans rien afficher. L’affichage réel est fait par drawCanvas(), qui copie la toile en bitmap sur l’écran.
drawFull et drawPartial
La fonction drawFull() effectue un rafraîchissement complet, tandis que la fonction drawPartial() effectue un rafraîchissement partiel. Lors du rafraîchissement complet, on dessine le titre et l’axe. Lors du rafraîchissement partiel, on dessine les points de données individuels.
La formule suivante crée les données de température simulées y, et x est le temps simulé.
y = (H / 2) + (x / 10.0) * sin(x / 10.0);
L’important est que l’on écrit le point de données (x,y) comme pixel sur la toile, puis on définit une fenêtre de rafraîchissement juste autour de ce pixel et on dessine la toile :
void drawPartial(const void* pv) {
...
canvas.writePixel(x, y, BLACK);
epd.setPartialWindow(x, y, 1, 1);
drawCanvas();
}
Comme la fenêtre de rafraîchissement partielle est fixée à (x,y,1,1), on ne rafraîchit pas tout l’écran mais seulement une petite zone autour du pixel (rappelez-vous la limitation du multiple de 8). Cependant, comme cette zone est prise de la toile, elle contient ce qui a déjà été dessiné et n’efface pas le contenu existant sur l’écran. La vidéo suivante montre le traceur en action :

Vous pouvez voir que la courbe traverse l’axe x sans l’effacer et il n’y a pas de scintillement. Bien que le rafraîchissement ne soit pas très rapide (environ 1 seconde), cela suffit pour un traceur de température.
Notez que le deep-sleep ne peut pas être facilement ajouté à ce code. Il faudrait stocker toute la toile en mémoire RTC, qui est généralement trop petite (8K). Vous pourriez stocker seulement les points de données, mais alors la fonction de redessin partiel serait plus complexe, car il faudrait redessiner toute la courbe. Alternativement, vous pouvez stocker la toile sur une carte SD ou une autre mémoire externe plus grande.
Recommandations
La bibliothèque Waveshare Manual fournit les recommandations suivantes pour l’utilisation d’un écran e-Paper (résumé et reformulé) :
Rafraîchissement complet : L’écran e-Paper scintille plusieurs fois pendant le processus de rafraîchissement (le nombre de scintillements dépend du temps de rafraîchissement), ce qui permet d’éliminer les images fantômes pour obtenir le meilleur rendu.
Rafraîchissement partiel : Dans ce cas, l’écran ne scintille pas pendant le rafraîchissement. Après plusieurs rafraîchissements partiels, un rafraîchissement complet doit être effectué pour éliminer les images résiduelles. Sinon, ces images peuvent devenir permanentes.
Il est recommandé de régler l’intervalle de rafraîchissement de l’écran e-ink à au moins 180 secondes (sauf pour les produits supportant le rafraîchissement partiel).
Après un rafraîchissement, il est conseillé d’éteindre ou d’hiberner l’écran. Cela prolonge sa durée de vie et réduit la consommation d’énergie.
Pour un écran e-ink trois couleurs, il est recommandé de mettre à jour l’écran au moins une fois toutes les 24 heures.
Les écrans e-Paper sont recommandés pour un usage intérieur et non extérieur. Si vous souhaitez l’utiliser en extérieur, placez-le à l’ombre et recouvrez complètement la partie blanche de la bande de connexion de l’écran avec du ruban adhésif 3M.
Conclusions
Dans ce tutoriel, vous avez appris à effectuer des rafraîchissements partiels d’un écran e-Paper pour éviter le scintillement et obtenir des temps de rafraîchissement plus rapides. Nous avons vu comment faire un rafraîchissement partiel pour une ou plusieurs zones, en combinaison avec le deep-sleep et des mises à jour pixel par pixel.
Vous pouvez facilement étendre l’exemple du traceur en ajoutant un vrai capteur de température comme le BME280 au projet. Consultez le tutoriel Weather Station on e-Paper Display pour plus de détails. Notez que vous pouvez utiliser des toiles séparées pour la température, l’humidité et la pression atmosphérique, puis basculer entre elles pour afficher différentes données.
Si vous avez des questions, n’hésitez pas à les poser dans la section commentaires.
Bon bricolage ; )

