Lopaka Basics 10: Papier elektroniczny i wysyłka

Dwa ekrany pokazują ten sam znak. Wyciągnij wtyczkę, a jasny gaśnie od razu. Drugi zachowuje swój obraz przy zerowym zużyciu energii i nadal będzie tam kilka dni później. To papier elektroniczny.
W tym odcinku dowiesz się, jak atrament elektroniczny utrzymuje obraz bez zasilania i dlaczego ma dwa sposoby jego zmiany. Następnie zaprojektujesz znak sali konferencyjnej w Lopaka dla biblioteki GxEPD2, zaktualizujesz tylko minuty bez błysku, wypróbujesz wersję czerwoną dla panelu trójkolorowego, opublikujesz projekt i udostępnisz do niego link. Przed wysyłką lista kontrolna pięciu błędów, które widzimy najczęściej.
Co zbudujesz: znak pokojowy 296×128 dla wyświetlacza papieru elektronicznego 2,9 cala z dwoma ekranami. Busy pokazuje salę, spotkanie, duży blok BUSY i pozostałe minuty. Free pokazuje następne spotkanie. Czego potrzebujesz: przeglądarki. Aby uruchomić znak, ESP32 i moduł papieru elektronicznego 2,9 cala (Waveshare V2).
Jak działa papier elektroniczny
Odłączony, nadal pokazuje
Ekran TFT taki jak w odcinku 3 świeci własnym światłem, a jego obraz żyje tylko tak długo, jak długo działa zasilanie. Odłącz zasilanie, a jest ciemny.
Papier elektroniczny działa jak atrament na papierze. Gdy obraz zostanie narysowany, pozostaje bez żadnego zasilania. Dzięki temu dobrze nadaje się do znaków, etykiet i metek cenowych, które zmieniają się kilka razy na godzinę i działają na baterii.

Kapsułki z czarnymi i białymi cząsteczkami
Każdy piksel papieru elektronicznego jest pełen malutkich kapsułek. Wewnątrz każdej unoszą się białe cząsteczki i czarne cząsteczki o przeciwnych ładunkach elektrycznych. Między dwiema elektrodami napięcie ściąga jeden kolor na górę, stronę, na którą patrzysz, a piksel staje się biały lub czarny.
Następnie zasilanie znika, a cząsteczki pozostają tam, gdzie są. Do utrzymania obrazu nie jest potrzebne zasilanie.

Pełne odświeżanie i odświeżanie częściowe
Zmiana obrazu wymaga pracy i są dwa sposoby, aby to zrobić:
- Pełne odświeżanie wstrząsa każdym pikselem. Cały panel miga na czarno i biało, aby wymazać stary obraz, a następnie pokazuje nowy. Na panelu czarno-białym 2,9 cala z tego odcinka zajmuje to około 4 sekund.
- Odświeżanie częściowe przesuwa tylko piksele, które się zmieniły, w mniej niż sekundę (około 0,7 s na tym panelu), bez błysku.

Każde odświeżanie częściowe pozostawia słabego ducha starego obrazu, jak stara cyfra za nową powyżej. Po kilku odświeżaniach częściowych wykonaj ponownie pełne odświeżanie, aby wymazać duchy.
Krok 1: nowy projekt papieru elektronicznego
Kliknij + Nowy projekt i otwórz listę Platforma. Wybierz GxEPD2 (e-paper), bibliotekę C++ autorstwa ZinggJM. Jej opis w Lopaka podsumowuje to, co ważne: „opiera się na Adafruit GFX, więc wywołania rysowania i czcionki są znajome, ale musisz nazwać dokładną klasę panelu”, a jej kolor „jest stałym zestawem dziewięciu stałych, takich jak GxEPD_BLACK i GxEPD_RED, a wszystko inne odwzorowuje się na czerń”.
Wypełnij resztę okna:
- Tytuł: „Room Sign”.
- Rozmiar ekranu: 296×128, rozmiar popularnego panelu 2,9 cala.
Kolor tła to już #F8F8F8, prawie biały, jak papier. Kliknij Zapisz.

Krok 2: narysuj znak
Kliknij dwukrotnie nazwę ekranu nad obszarem rysowania i zmień nazwę Screen 1 na Busy. Następnie narysuj znak od góry do dołu. Wszystkie teksty używają czcionek Adafruit GFX z listy Czcionka w inspektorze:
- Nazwa sali. Wybierz narzędzie tekst (
Aa), wpiszROOM 3i wybierzFreeSansBold12pt7b. Umieść go w lewym górnym rogu. - Czas aktualizacji. Dodaj
Updated 15:25czcionkąFreeSans9pt7b, w prawym górnym rogu. - Linia podziału. Wybierz narzędzie linia i narysuj linię pod nagłówkiem, od x = 10 do x = 285 na y = 32.
- Blok statusu. Wybierz narzędzie prostokąt, przeciągnij prostokąt i ustaw X
10, Y42, W128, H76, R6, a następnie włącz Wypełniony. - Status. Dodaj
BUSYczcionkąFreeSansBold18pt7bna bloku i ustaw jego Kolor wypełnienia na#FFFFFF, biały na czarnym. - Spotkanie. Po prawej stronie bloku dodaj trzy linie tekstu:
Design reviewczcionkąFreeSansBold9pt7b,until 15:30czcionkąFreeSans9pt7boraz5 min leftczcionkąFreeSansBold12pt7b.

Liczby zachowują ten sam 10-pikselowy margines z każdej strony. Linia kończy się na x = 285, dziesięć pikseli przed ostatnią kolumną, 295. Blok zaczyna się 10 pikseli od lewej krawędzi, a jego dolny wiersz to 117, dziesięć wierszy nad ostatnim, 127.
Dziewięć kolorów, a twój panel pokazuje kilka
Otwórz Kolor wypełnienia dowolnego kształtu, a zobaczysz, jak krótka jest paleta. Te dziewięć to jedyne kolory, jakie zna biblioteka GxEPD2: GxEPD_BLACK, GxEPD_WHITE, GxEPD_LIGHTGREY, GxEPD_DARKGREY, GxEPD_RED, GxEPD_YELLOW, GxEPD_BLUE, GxEPD_GREEN i GxEPD_ORANGE.

Twój panel pokazuje tylko niektóre z nich. Panel z tego odcinka jest czarno-biały; wiele innych dodaje trzeci kolor, czerwony lub żółty. Projektuj kolorami, jakie ma twój panel.
Krok 3: zduplikuj dla „free”
Gdy spotkanie się kończy, znak potrzebuje drugiego ekranu. Zamiast rysować go od nowa, zaznacz ekran Busy w panelu Warstwy i kliknij Duplikuj w inspektorze. Zmień nazwę kopii na Free i zmień ją:
BUSYstaje sięFREE, z Kolorem wypełnienia z powrotem na#000000.- Zaznacz
rect 4i wyłącz Wypełniony: blok staje się ramką. Updated 15:25staje sięUpdated 15:30.- Linie spotkania stają się
Next: Standup,at 16:00i30 min free.

Każdy ekran staje się własną funkcją w kodzie, drawBusy() i drawFree(), a panel kodu ma kartę dla każdego.
Krok 4: kod
Znajome wywołania rysowania
Otwórz kartę Busy panelu kodu. Kod to ten sam rodzaj wywołań rysowania co w wcześniejszych odcinkach:
// [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)czyści obraz. Prawie białe tło obszaru rysowania odwzorowuje się na biel na panelu.fillRoundRect(10, 42, 128, 76, 6, …)to blok: x, y, szerokość, wysokość i promień rogu R. Na ekranie Free ten sam blok todrawRoundRect, tylko kontur.setCursor(x, y)umieszcza tekst na jego linii bazowej, linii, na której stoją litery, jak w odcinku 1. Dla „5 min left” jest to wiersz 111.display.display()na końcu wysyła cały obraz na panel z pełnym odświeżaniem.
Karta Free ma te same wywołania z nowymi tekstami i drawRoundRect, w funkcji o nazwie drawFree().
Czcionki są dostarczane z biblioteką
Cztery czcionki są częścią biblioteki Adafruit GFX, na której zbudowany jest GxEPD2. Dołączasz je samodzielnie, z resztą konfiguracji, więc wyłącz opcję Dołącz czcionki w przełącznikach obok kodu.

Twój kod powyżej: biblioteka, czcionki i twój dokładny panel
Nad wygenerowaną częścią dołącz bibliotekę i cztery czcionki oraz nazwij dokładny 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 to klasa wyświetlacza czarno-białego. Wewnątrz nawiasów ostrych:
GxEPD2_290_T94_V2to sterownik dla modułu czarno-białego Waveshare 2,9 cala, wersja 2. GxEPD2 ma osobną klasę dla prawie każdego panelu, a kilka paneli 2,9 cala wygląda tak samo, ale potrzebuje innych klas. Sprawdź model wydrukowany na taśmie panelu i znajdź pasującą do niego klasę w przykładach GxEPD2.GxEPD2_290_T94_V2::HEIGHTto liczba wierszy, jaką program przechowuje w pamięci. Przy pełnej wysokości cały obraz mieści się w buforze ramki o rozmiarze 296 × 128 / 8 = 4736 bajtów, jeden bit na piksel.
Cztery liczby to piny ESP32, do których podłączony jest moduł: CS 5, DC 17, RST 16 i BUSY 4. MINUTE to jedna minuta w milisekundach.
Twój kod poniżej: pełne, częściowe, uśpienie
Pod wygenerowaną częścią narysuj ekran zajętości raz, a następnie aktualizuj tylko minuty:
// 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): panel ma 128 pikseli szerokości i 296 wysokości, gdy stoi pionowo. Rotacja 1 obraca go na bok, aby pasował do obszaru rysowania 296×128.showMinutes()maluje biały prostokąt na starym „5 min left”, od wiersza 94 do wiersza 117 i od kolumny 152 do 287, wypisuje nową liczbę na tej samej linii bazowej, 111, i wywołujedisplayWindow()z tym samym prostokątem. Tylko to okno jest wysyłane na panel, jako odświeżanie częściowe, bez błysku.setup()rysuje Busy z pełnym odświeżaniem, aktualizuje minuty cztery razy odświeżeniami częściowymi, a następnie rysuje Free z pełnym odświeżaniem. Cztery odświeżania częściowe, a potem jedno pełne: duchy są wymazywane, gdy spotkanie się kończy.hibernate()wyłącza panel i wprowadza go w głęboki sen. Obraz pozostaje.
Wszystko dzieje się raz w setup(), więc loop() jest puste.
Możesz wpisać te linie wprost do panelu kodu Lopaka, nad znacznikiem BEGIN i pod znacznikiem END. Następnie kliknij Kopiuj na karcie Busy, a cały szkic przyjdzie z nią. Wklej drawFree() z karty Free na końcu.
Cały szkic
#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() znajduje się po setup(), które ją wywołuje. W szkicu Arduino to w porządku: narzędzia Arduino deklarują każdą funkcję na górze szkicu za ciebie.
Krok 5: uruchom to
Podłącz moduł do ESP32
Moduł ma osiem przewodów. Podłącz je do ESP32 DevKit w ten sposób:
- VCC do 3V3 i GND do GND. Panele papieru elektronicznego potrzebują 3,3 V, na zasilaniu i na liniach danych. Jeśli twój moduł ma dziewiąty pin, PWR, podłącz go też do 3V3.
- DIN do 23 i CLK do 18, sprzętowe piny SPI ESP32.
- CS do 5, DC do 17, RST do 16 i BUSY do 4, piny z konstruktora.
W Arduino IDE zainstaluj GxEPD2 autorstwa Jean-Marc Zingg z Library Manager i zaakceptuj wraz z nią Adafruit GFX Library. Wybierz płytkę ESP32 Dev Module i wgraj. Szkic jest mały jak na 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.Bufor ramki o rozmiarze 4736 bajtów jest częścią tych 28 460 bajtów.
Co zobaczysz
W wideo znak działa na symulowanym ESP32 z modułem papieru elektronicznego 2,9 cala, przy czym jedna „minuta” jest przyspieszona do dwóch sekund. Obejrzyj początek: panel miga na czarno i biało, a znak się pojawia. To pełne odświeżanie.
Następnie minuty odliczają w dół, 4, 3, 2, 1, i zmienia się tylko ta liczba, bez błysku. Gdy spotkanie się kończy, jeszcze jedno pełne odświeżanie rysuje ekran Free, a panel zasypia. Znak pozostaje.

Krok 6: zrób to na czerwono
Chcesz trochę czerwieni? Wiele paneli dodaje trzeci kolor. Zaznacz ekran Busy, zaznacz jego rect 4 i ustaw Kolor wypełnienia na #FF0000, czerwień z palety. Kod zmienia jedno słowo:
display.fillRoundRect(10, 42, 128, 76, 6, GxEPD_RED);
W przypadku panelu trójkolorowego zmień górę szkicu na klasę trójkolorową i sterownik czerwonego panelu 2,9 cala:
#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));Na panelu trójkolorowym blok wychodzi czerwony. Ale panele trójkolorowe są powolne: pełne odświeżanie tego zajmuje około 26 sekund i nie ma szybkiego odświeżania częściowego, więc każda aktualizacja przerysowuje cały ekran. Narysuj znak raz i pomiń odliczanie minut:
void setup() {
display.init();
display.setRotation(1);
drawBusy(); // full refresh, about 26 s on a real panel
display.hibernate();
}Czerwony obraz potrzebuje drugiego bufora: szkic zajmuje 33 196 bajtów RAM (10%) zamiast 28 460.

Krok 7: opublikuj to
Teraz wyślij to. Kliknij ikonę koła zębatego obok nazwy projektu, aby otworzyć ustawienia projektu, włącz Opublikuj w Galerii i kliknij Zapisz.

Opublikowany projekt pokazuje swoje ekrany tylko wtedy, gdy upublicznisz je. Zaznacz ekran Busy i kliknij Upublicznij w inspektorze. Ekran otrzymuje plakietkę Publiczny, a przycisk zamienia się w Uczyń prywatnym.
Krok 8: udostępnij link
Kliknij Udostępnij na górnym pasku i wybierz Kopiuj publiczny link. Link wskazuje ten ekran w galerii: https://lopaka.app/gallery/<project>/<screen>.

Wyślij link każdemu: członkowi zespołu, klientowi, wątkowi na forum. Otwierają oni projekt i jego kod wprost w przeglądarce. Za pomocą Gwiazdka zachowują go w ulubionych, a za pomocą Kopia otrzymują własną kopię w Moich projektach, aby na niej budować, podczas gdy twoja pozostaje nietknięta.

Zanim wyślesz: pięć błędów
Zanim wyślesz jakikolwiek ekran, przejdź tę listę kontrolną. Oto pięć błędów, które widzimy najczęściej na ekranach wbudowanych:
- Tekst zbyt mały. Czytaj ekran z miejsca, w którym ludzie będą stać: w przypadku znaku pokojowego są to drzwi.
- Kolory, których panel nie może pokazać. Projektuj prawdziwą paletą panelu: czarny i biały oraz czerwony lub żółty, jeśli twój panel go ma.
- Tekst, który się nie mieści. Przetestuj najdłuższą nazwę, jaką kiedykolwiek otrzymasz, a nie ładną krótką. „Design review” mieści się obok bloku; „Quarterly planning” wychodzi poza krawędź znaku.
- Zbyt częste odświeżanie. Przerysowuj tylko to, co się zmieniło: jest to szybsze, a na papierze elektronicznym oszczędza baterię.
- Martwe urządzenie nadal pokazuje swój ostatni obraz. To podstępny przypadek na papierze elektronicznym: znak z rozładowaną baterią wygląda jak działający. Zawsze pokazuj, kiedy został zaktualizowany, aby nikt nie ufał nieaktualnemu znakowi.

Znak w tym odcinku obejmuje piąty: „Updated 15:25” w rogu mówi wszystkim, jak stary jest obraz.
Podsumowanie
- Papier elektroniczny zachowuje obraz bez zasilania: cząsteczki pozostają tam, gdzie umieściło je ostatnie napięcie, więc panel potrzebuje zasilania tylko do zmiany obrazu.
- Pełne odświeżanie, odświeżanie częściowe, uśpienie:
display()przerysowuje wszystko z błyskiem,displayWindow()zmienia jeden obszar bez niego, ahibernate()wyłącza panel, podczas gdy obraz pozostaje. - Sprawdź, a potem wyślij: przejdź listę kontrolną pięciu punktów, opublikuj projekt, upublicznij ekran i udostępnij link.
Narysuj to, pobierz kod, zobacz to na ekranie. Otwórz Lopaka i zaprojektuj własny znak na papierze elektronicznym.