Lopaka Basics 10: E-Paper & Versand

Zwei Screens zeigen dasselbe Schild. Ziehe den Stecker, und der helle wird sofort dunkel. Der andere behält sein Bild mit null Strom, und er wird Tage später noch da sein. Das ist E-Paper.
In dieser Episode findest du heraus, wie elektronische Tinte ein Bild ohne Strom hält und warum sie zwei Wege hat, es zu ändern. Dann entwirfst du ein Besprechungsraumschild in Lopaka für die GxEPD2-Bibliothek, aktualisierst nur die Minuten ohne Blitz, probierst eine rote Version für ein Dreifarben-Panel, veröffentlichst das Design und teilst einen Link dazu. Vor dem Versand eine Checkliste der fünf Fehler, die wir am häufigsten sehen.
Was du bauen wirst: ein 296×128-Raumschild für ein 2,9-Zoll-E-Paper-Display mit zwei Screens. Busy zeigt Raum, Besprechung, einen großen BUSY-Block und die verbleibenden Minuten. Free zeigt die nächste Besprechung. Was du brauchst: einen Browser. Um das Schild auszuführen, einen ESP32 und ein 2,9-Zoll-E-Paper-Modul (Waveshare V2).
Wie E-Paper funktioniert
Abgesteckt, zeigt trotzdem
Ein TFT-Screen wie der in Episode 3 leuchtet sein eigenes Licht, und sein Bild lebt nur, solange der Strom da ist. Schneide den Strom ab, und er ist dunkel.
E-Paper funktioniert wie Tinte auf Papier. Sobald das Bild gezeichnet ist, bleibt es ganz ohne Strom. Das macht es zu einer guten Wahl für Schilder, Etiketten und Preisschilder, die sich ein paar Mal pro Stunde ändern und mit Batterie laufen.

Kapseln mit schwarzen und weißen Partikeln
Jedes Pixel von E-Paper ist voll winziger Kapseln. In jeder schweben weiße Partikel und schwarze Partikel mit entgegengesetzten elektrischen Ladungen. Zwischen zwei Elektroden zieht eine Spannung eine Farbe nach oben, die Seite, die du ansiehst, und das Pixel wird weiß oder schwarz.
Dann verschwindet der Strom, und die Partikel bleiben, wo sie sind. Kein Strom wird gebraucht, um das Bild zu halten.

Vollaktualisierung und Teilaktualisierung
Das Bild zu ändern braucht Arbeit, und es gibt zwei Wege, es zu tun:
- Eine Vollaktualisierung schüttelt jedes Pixel. Das ganze Panel blitzt schwarz und weiß, um das alte Bild zu wischen, dann zeigt es das neue. Auf dem 2,9-Zoll-Schwarz-Weiß-Panel dieser Episode braucht sie etwa 4 Sekunden.
- Eine Teilaktualisierung bewegt nur die Pixel, die sich geändert haben, in unter einer Sekunde (etwa 0,7 s auf diesem Panel), ohne Blitz.

Jede Teilaktualisierung lässt einen schwachen Geist des alten Bildes, wie die alte Ziffer hinter der neuen oben. Mache nach ein paar Teilaktualisierungen wieder eine Vollaktualisierung, um die Geister zu wischen.
Schritt 1: Ein neues E-Paper-Projekt
Klicke auf + Neues Projekt und öffne die Liste Plattform. Wähle GxEPD2 (e-paper), eine C++-Bibliothek von ZinggJM. Ihre Beschreibung in Lopaka fasst zusammen, was zählt: Sie „sitzt auf Adafruit GFX, also sind Zeichenaufrufe und Schriftarten die vertrauten, aber du musst deine genaue Panel-Klasse benennen“, und ihre Farbe „ist ein fester Satz von neun Konstanten wie GxEPD_BLACK und GxEPD_RED, und alles andere wird zu schwarz“.
Fülle den Rest des Dialogs aus:
- Titel: „Room Sign“.
- Screen-Größe: 296×128, die Größe eines gängigen 2,9-Zoll-Panels.
Die Hintergrundfarbe ist bereits #F8F8F8, ein Fast-Weiß, wie Papier. Klicke auf Speichern.

Schritt 2: Zeichne das Schild
Doppelklicke auf den Namen des Screens über der Zeichenfläche und benenne Screen 1 in Busy um. Zeichne dann das Schild von oben nach unten. Alle Texte nutzen die Adafruit-GFX-Schriftarten aus der Liste Schriftart im Inspektor:
- Der Raumname. Wähle das Text-Werkzeug (
Aa), tippeROOM 3und wähleFreeSansBold12pt7b. Lege es in die obere linke Ecke. - Die Aktualisierungszeit. Füge
Updated 15:25inFreeSans9pt7bin der oberen rechten Ecke hinzu. - Ein Trenner. Wähle das Linien-Werkzeug und zeichne eine Linie unter der Kopfzeile, von x = 10 bis x = 285 bei y = 32.
- Der Statusblock. Wähle das Rechteck-Werkzeug, ziehe ein Rechteck und setze X
10, Y42, W128, H76, R6, dann schalte Gefüllt ein. - Der Status. Füge
BUSYinFreeSansBold18pt7büber dem Block hinzu und setze seine Füllfarbe auf#FFFFFF, Weiß auf Schwarz. - Die Besprechung. Füge rechts vom Block drei Textzeilen hinzu:
Design reviewinFreeSansBold9pt7b,until 15:30inFreeSans9pt7bund5 min leftinFreeSansBold12pt7b.

Die Zahlen behalten denselben 10-Pixel-Rand auf jeder Seite. Die Linie endet bei x = 285, zehn Pixel vor der letzten Spalte, 295. Der Block beginnt 10 Pixel vom linken Rand, und seine untere Zeile ist 117, zehn Zeilen über der letzten, 127.
Neun Farben, und dein Panel zeigt wenige
Öffne die Füllfarbe irgendeiner Form, und du siehst, wie kurz die Palette ist. Diese neun sind die einzigen Farben, die die GxEPD2-Bibliothek kennt: GxEPD_BLACK, GxEPD_WHITE, GxEPD_LIGHTGREY, GxEPD_DARKGREY, GxEPD_RED, GxEPD_YELLOW, GxEPD_BLUE, GxEPD_GREEN und GxEPD_ORANGE.

Dein Panel zeigt nur einige davon. Das Panel dieser Episode ist schwarz-weiß; viele andere fügen eine dritte Farbe hinzu, Rot oder Gelb. Entwirf mit den Farben, die dein Panel hat.
Schritt 3: Dupliziere für „free“
Wenn die Besprechung endet, braucht das Schild einen zweiten Screen. Statt ihn erneut zu zeichnen, wähle den Busy-Screen im Ebenen-Panel aus und klicke im Inspektor auf Duplizieren. Benenne die Kopie in Free um und ändere sie:
BUSYwird zuFREE, mit der Füllfarbe zurück auf#000000.- Wähle
rect 4aus und schalte Gefüllt aus: Der Block wird zu einem Rahmen. Updated 15:25wird zuUpdated 15:30.- Die Besprechungszeilen werden zu
Next: Standup,at 16:00und30 min free.

Jeder Screen wird zu seiner eigenen Funktion im Code, drawBusy() und drawFree(), und das Code-Panel hat einen Tab für jeden.
Schritt 4: Der Code
Vertraute Zeichenaufrufe
Öffne den Tab Busy des Code-Panels. Der Code ist dieselbe Art von Zeichenaufrufen wie in den früheren Episoden:
// [BEGIN lopaka generated]
void drawBusy(void) {
display.fillScreen(GxEPD_WHITE);
// string 1
display.setTextColor(GxEPD_BLACK);
display.setTextWrap(false);
display.setFont(&FreeSansBold12pt7b);
display.setCursor(10, 23);
display.print("ROOM 3");
// string 2
display.setFont(&FreeSans9pt7b);
display.setCursor(170, 21);
display.print("Updated 15:25");
// line 3
display.drawLine(10, 32, 285, 32, GxEPD_BLACK);
// rect 4
display.fillRoundRect(10, 42, 128, 76, 6, GxEPD_BLACK);
// string 5
display.setTextColor(GxEPD_WHITE);
display.setFont(&FreeSansBold18pt7b);
display.setCursor(27, 92);
display.print("BUSY");
// string 6
display.setTextColor(GxEPD_BLACK);
display.setFont(&FreeSansBold9pt7b);
display.setCursor(152, 59);
display.print("Design review");
// string 7
display.setFont(&FreeSans9pt7b);
display.setCursor(152, 81);
display.print("until 15:30");
// string 8
display.setFont(&FreeSansBold12pt7b);
display.setCursor(152, 111);
display.print("5 min left");
display.display();
}
// [END lopaka generated]fillScreen(GxEPD_WHITE)löscht das Bild. Der fast weiße Zeichenflächenhintergrund wird auf dem Panel zu Weiß.fillRoundRect(10, 42, 128, 76, 6, …)ist der Block: x, y, Breite, Höhe und der Eckenradius R. Auf dem Free-Screen ist derselbe BlockdrawRoundRect, nur der Umriss.setCursor(x, y)legt Text auf seine Grundlinie, die Linie, auf der die Buchstaben stehen, wie in Episode 1. Für „5 min left“ ist das Zeile 111.display.display()am Ende sendet das ganze Bild mit einer Vollaktualisierung ans Panel.
Der Free-Tab hat dieselben Aufrufe mit den neuen Texten und drawRoundRect, in einer Funktion namens drawFree().
Die Schriftarten kommen mit der Bibliothek
Die vier Schriftarten sind Teil der Adafruit-GFX-Bibliothek, auf der GxEPD2 aufgebaut ist. Du bindest sie selbst ein, mit dem Rest deiner Einrichtung, also schalte Schriftarten einbeziehen in den Schaltern neben dem Code aus.

Dein Code darüber: Bibliothek, Schriftarten und dein genaues Panel
Binde über dem generierten Teil die Bibliothek und die vier Schriftarten ein und benenne dein genaues Panel:
#include <GxEPD2_BW.h>
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSansBold9pt7b.h>
#include <Fonts/FreeSansBold12pt7b.h>
#include <Fonts/FreeSansBold18pt7b.h>
// 2.9" e-paper 296x128 (Waveshare V2): CS, DC, RST, BUSY
GxEPD2_BW<GxEPD2_290_T94_V2, GxEPD2_290_T94_V2::HEIGHT>
display(GxEPD2_290_T94_V2(5, 17, 16, 4));
#define MINUTE 60000ULGxEPD2_BW ist die Schwarz-Weiß-Display-Klasse. In den spitzen Klammern:
GxEPD2_290_T94_V2ist der Treiber für das Waveshare-2,9-Zoll-Schwarz-Weiß-Modul, Version 2. GxEPD2 hat eine separate Klasse für fast jedes Panel, und mehrere 2,9-Zoll-Panels sehen gleich aus, brauchen aber unterschiedliche Klassen. Prüfe das Modell, das auf dem Flachbandkabel des Panels gedruckt ist, und finde die Klasse, die in den GxEPD2-Beispielen dazu passt.GxEPD2_290_T94_V2::HEIGHTist, wie viele Zeilen das Programm im Speicher hält. Mit der vollen Höhe passt das ganze Bild in einen Frame-Puffer von 296 × 128 / 8 = 4.736 Bytes, ein Bit pro Pixel.
Die vier Zahlen sind die ESP32-Pins, mit denen das Modul verdrahtet ist: CS 5, DC 17, RST 16 und BUSY 4. MINUTE ist eine Minute in Millisekunden.
Dein Code darunter: voll, teilweise, Schlaf
Zeichne unter dem generierten Teil den Busy-Screen einmal und aktualisiere dann nur die Minuten:
// partial refresh: only the minutes change
void showMinutes(int m) {
display.fillRect(152, 94, 136, 24, GxEPD_WHITE);
display.setFont(&FreeSansBold12pt7b);
display.setTextColor(GxEPD_BLACK);
display.setCursor(152, 111);
display.print(m);
display.print(" min left");
display.displayWindow(152, 94, 136, 24);
}
void setup() {
display.init();
display.setRotation(1);
drawBusy(); // full refresh
for (int m = 4; m >= 1; m--) {
delay(MINUTE);
showMinutes(m); // partial refresh
}
delay(MINUTE);
drawFree(); // full refresh
display.hibernate(); // panel off, the picture stays
}
void loop() {}setRotation(1): Das Panel ist 128 Pixel breit und 296 hoch, wenn es aufrecht steht. Rotation 1 dreht es auf die Seite, sodass es zur 296×128-Zeichenfläche passt.showMinutes()malt ein weißes Rechteck über das alte „5 min left“, von Zeile 94 bis Zeile 117 und von Spalte 152 bis 287, druckt die neue Zahl auf dieselbe Grundlinie, 111, und ruftdisplayWindow()mit demselben Rechteck auf. Nur dieses Fenster wird als Teilaktualisierung ans Panel gesendet, ohne Blitz.setup()zeichnet Busy mit einer Vollaktualisierung, aktualisiert die Minuten viermal mit Teilaktualisierungen und zeichnet dann Free mit einer Vollaktualisierung. Vier Teilaktualisierungen, dann eine volle: Die Geister werden gewischt, wenn die Besprechung endet.hibernate()schaltet das Panel aus und versetzt es in Tiefschlaf. Das Bild bleibt.
Alles passiert einmal in setup(), also ist loop() leer.
Du kannst diese Zeilen direkt in das Code-Panel von Lopaka tippen, über der BEGIN-Markierung und unter der END-Markierung. Klicke dann im Tab Busy auf Kopieren, und der ganze Sketch kommt mit. Füge drawFree() aus dem Free-Tab am Ende ein.
Der ganze Sketch
#include <GxEPD2_BW.h>
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSansBold9pt7b.h>
#include <Fonts/FreeSansBold12pt7b.h>
#include <Fonts/FreeSansBold18pt7b.h>
// 2.9" e-paper 296x128 (Waveshare V2): CS, DC, RST, BUSY
GxEPD2_BW<GxEPD2_290_T94_V2, GxEPD2_290_T94_V2::HEIGHT>
display(GxEPD2_290_T94_V2(5, 17, 16, 4));
#define MINUTE 60000UL
// [BEGIN lopaka generated]
void drawBusy(void) {
display.fillScreen(GxEPD_WHITE);
// string 1
display.setTextColor(GxEPD_BLACK);
display.setTextWrap(false);
display.setFont(&FreeSansBold12pt7b);
display.setCursor(10, 23);
display.print("ROOM 3");
// string 2
display.setFont(&FreeSans9pt7b);
display.setCursor(170, 21);
display.print("Updated 15:25");
// line 3
display.drawLine(10, 32, 285, 32, GxEPD_BLACK);
// rect 4
display.fillRoundRect(10, 42, 128, 76, 6, GxEPD_BLACK);
// string 5
display.setTextColor(GxEPD_WHITE);
display.setFont(&FreeSansBold18pt7b);
display.setCursor(27, 92);
display.print("BUSY");
// string 6
display.setTextColor(GxEPD_BLACK);
display.setFont(&FreeSansBold9pt7b);
display.setCursor(152, 59);
display.print("Design review");
// string 7
display.setFont(&FreeSans9pt7b);
display.setCursor(152, 81);
display.print("until 15:30");
// string 8
display.setFont(&FreeSansBold12pt7b);
display.setCursor(152, 111);
display.print("5 min left");
display.display();
}
// [END lopaka generated]
// partial refresh: only the minutes change
void showMinutes(int m) {
display.fillRect(152, 94, 136, 24, GxEPD_WHITE);
display.setFont(&FreeSansBold12pt7b);
display.setTextColor(GxEPD_BLACK);
display.setCursor(152, 111);
display.print(m);
display.print(" min left");
display.displayWindow(152, 94, 136, 24);
}
void setup() {
display.init();
display.setRotation(1);
drawBusy(); // full refresh
for (int m = 4; m >= 1; m--) {
delay(MINUTE);
showMinutes(m); // partial refresh
}
delay(MINUTE);
drawFree(); // full refresh
display.hibernate(); // panel off, the picture stays
}
void loop() {}
// [BEGIN lopaka generated]
void drawFree(void) {
display.fillScreen(GxEPD_WHITE);
// string 1
display.setTextColor(GxEPD_BLACK);
display.setTextWrap(false);
display.setFont(&FreeSansBold12pt7b);
display.setCursor(10, 23);
display.print("ROOM 3");
// string 2
display.setFont(&FreeSans9pt7b);
display.setCursor(170, 21);
display.print("Updated 15:30");
// line 3
display.drawLine(10, 32, 285, 32, GxEPD_BLACK);
// rect 4
display.drawRoundRect(10, 42, 128, 76, 6, GxEPD_BLACK);
// string 5
display.setFont(&FreeSansBold18pt7b);
display.setCursor(27, 92);
display.print("FREE");
// string 6
display.setFont(&FreeSansBold9pt7b);
display.setCursor(152, 59);
display.print("Next: Standup");
// string 7
display.setFont(&FreeSans9pt7b);
display.setCursor(152, 81);
display.print("at 16:00");
// string 8
display.setFont(&FreeSansBold12pt7b);
display.setCursor(152, 111);
display.print("30 min free");
display.display();
}
// [END lopaka generated]drawFree() kommt nach setup(), das es aufruft. Das ist in einem Arduino-Sketch in Ordnung: Die Arduino-Werkzeuge deklarieren jede Funktion oben im Sketch für dich.
Schritt 5: Führe ihn aus
Verdrahte das Modul mit einem ESP32
Das Modul hat acht Drähte. Verbinde sie so mit einem ESP32 DevKit:
- VCC mit 3V3 und GND mit GND. E-Paper-Panels brauchen 3,3 V, an Strom- und Datenleitungen. Wenn dein Modul einen neunten Pin hat, PWR, verbinde ihn auch mit 3V3.
- DIN mit 23 und CLK mit 18, den Hardware-SPI-Pins des ESP32.
- CS mit 5, DC mit 17, RST mit 16 und BUSY mit 4, den Pins aus dem Konstruktor.
Installiere in der Arduino IDE GxEPD2 von Jean-Marc Zingg aus dem Bibliotheksmanager und akzeptiere Adafruit GFX Library damit. Wähle das Board ESP32 Dev Module und lade hoch. Der Sketch ist klein für einen ESP32:
Sketch uses 322220 bytes (24%) of program storage space. Maximum is 1310720 bytes.
Global variables use 28460 bytes (8%) of dynamic memory, leaving 299220 bytes for local variables. Maximum is 327680 bytes.Der 4.736-Byte-Frame-Puffer ist Teil dieser 28.460 Bytes.
Was du sehen wirst
Im Video läuft das Schild auf einem simulierten ESP32 mit einem 2,9-Zoll-E-Paper-Modul, mit einer „Minute“, die auf zwei Sekunden beschleunigt ist. Schaue den Start an: Das Panel blitzt schwarz und weiß, und das Schild erscheint. Das ist die Vollaktualisierung.
Dann ticken die Minuten herunter, 4, 3, 2, 1, und nur diese Zahl ändert sich, ohne Blitz. Wenn die Besprechung endet, zeichnet eine weitere Vollaktualisierung den Free-Screen, und das Panel schläft ein. Das Schild bleibt.

Schritt 6: Mache es rot
Willst du etwas Rot? Viele Panels fügen eine dritte Farbe hinzu. Wähle den Busy-Screen, wähle sein rect 4 und setze die Füllfarbe auf #FF0000, Rot aus der Palette. Der Code ändert ein Wort:
display.fillRoundRect(10, 42, 128, 76, 6, GxEPD_RED);
Ändere für ein Dreifarben-Panel den oberen Teil des Sketches zur Dreifarben-Klasse und dem Treiber eines roten 2,9-Zoll-Panels:
#include <GxEPD2_3C.h>
// 2.9" three-colour e-paper 296x128 (black, white, red): CS, DC, RST, BUSY
GxEPD2_3C<GxEPD2_290_C90c, GxEPD2_290_C90c::HEIGHT>
display(GxEPD2_290_C90c(5, 17, 16, 4));Auf einem Dreifarben-Panel kommt der Block rot heraus. Aber Dreifarben-Panels sind langsam: Eine Vollaktualisierung dieses Panels braucht etwa 26 Sekunden, und es hat keine schnelle Teilaktualisierung, also zeichnet jedes Update den ganzen Screen neu. Zeichne das Schild einmal und überspringe den Minutencountdown:
void setup() {
display.init();
display.setRotation(1);
drawBusy(); // full refresh, about 26 s on a real panel
display.hibernate();
}Das rote Bild braucht einen zweiten Puffer: Der Sketch braucht 33.196 Bytes RAM (10 %) statt 28.460.

Schritt 7: Veröffentliche es
Jetzt versende es. Klicke das Zahnrad-Icon neben dem Projektnamen, um die Projekteinstellungen zu öffnen, schalte In Galerie veröffentlichen ein und klicke auf Speichern.

Ein veröffentlichtes Projekt zeigt seine Screens nur, wenn du sie öffentlich machst. Wähle den Busy-Screen aus und klicke im Inspektor auf Öffentlich machen. Der Screen bekommt ein Öffentlich-Abzeichen, und der Button wird zu Privat machen.
Schritt 8: Teile den Link
Klicke in der oberen Leiste auf Teilen und wähle Öffentlichen Link kopieren. Der Link zeigt auf diesen Screen in der Galerie: https://lopaka.app/gallery/<project>/<screen>.

Sende den Link an jeden: einen Teamkollegen, einen Kunden, einen Forum-Thread. Sie öffnen das Design und seinen Code direkt im Browser. Mit Stern behalten sie es in ihren Favoriten, und mit Fork bekommen sie ihre eigene Kopie in Meine Projekte, um darauf aufzubauen, während deine unberührt bleibt.

Vor dem Versand: fünf Fehler
Gehe vor dem Versand irgendeines Screens diese Checkliste durch. Das sind die fünf Fehler, die wir bei Embedded-Screens am häufigsten sehen:
- Zu kleiner Text. Lies den Screen von dort, wo die Leute stehen werden: bei einem Raumschild die Tür.
- Farben, die das Panel nicht zeigen kann. Entwirf mit der echten Palette des Panels: Schwarz und Weiß, plus Rot oder Gelb, wenn dein Panel es hat.
- Text, der nicht passt. Teste den längsten Namen, den du je bekommen wirst, nicht den schönen kurzen. „Design review“ passt neben den Block; „Quarterly planning“ läuft über den Rand des Schilds.
- Zu oft aktualisieren. Zeichne nur neu, was sich geändert hat: Es ist schneller, und auf E-Paper spart es die Batterie.
- Ein totes Gerät zeigt noch sein letztes Bild. Das ist der heimtückische auf E-Paper: Ein Schild mit leerer Batterie sieht aus wie ein funktionierendes. Zeige immer, wann es aktualisiert wurde, damit niemand einem veralteten Schild vertraut.

Das Schild in dieser Episode deckt den fünften ab: „Updated 15:25“ in der Ecke sagt jedem, wie alt das Bild ist.
Zusammenfassung
- E-Paper behält sein Bild ohne Strom: Die Partikel bleiben, wo die letzte Spannung sie hingelegt hat, also braucht das Panel nur Strom, um das Bild zu ändern.
- Vollaktualisierung, Teilaktualisierung, Schlaf:
display()zeichnet alles mit einem Blitz neu,displayWindow()ändert einen Bereich ohne ihn, undhibernate()schaltet das Panel aus, während das Bild bleibt. - Prüfe, dann versende: Gehe die Fünf-Punkte-Checkliste durch, veröffentliche das Projekt, mache den Screen öffentlich und teile den Link.
Zeichne es, hole den Code, sieh es auf dem Screen. Öffne Lopaka und entwirf dein eigenes E-Paper-Schild.