Lopaka Basics 04: Dane na żywo

Dwa ekrany. Ta sama płytka, ten sam czujnik, te same liczby. Ale jeden z nich miga za każdym razem, gdy zmienia się liczba. Zwolnij, a zobaczysz dlaczego: cały obraz jest czyszczony, a potem malowany z powrotem, kawałek po kawałku.
W tym odcinku dowiesz się, co robi loop(), dlaczego kolorowy wyświetlacz migocze, gdy OLED tego nie robi, oraz trzy zasady, które to zatrzymują. Następnie zamienisz liczby z projektu Lopaka w zmienne, zasilisz je z czujnika temperatury i wilgotności i uruchomisz na symulowanym ESP32 bez ani jednego mrugnięcia.
Co zbudujesz: kolorowy monitor pokojowy 320×240 z liczbami temperatury i wilgotności na żywo oraz paskiem wilgotności, aktualizowany dwa razy na sekundę. Czego potrzebujesz: przeglądarki. Uruchamiamy kod na symulowanym ESP32 z wyświetlaczem ILI9341 i czujnikiem DHT22; prawdziwa płytka jest opcjonalna.
setup() i loop()
Każdy szkic Arduino ma dwie części:
setup()uruchamia się raz, przy włączeniu zasilania.loop()uruchamia się po nim, w kółko, tak długo, jak płytka ma zasilanie.
Dane na żywo żyją w loop(): odczytaj czujnik, zamień liczby w tekst, narysuj je i powtórz. Każde okrążenie rysuje ekran od nowa.

Dlaczego kolorowy ekran miga
OLED najpierw rysuje w pamięci
OLED z pierwszych odcinków nigdy nie migał. Jego biblioteka, U8g2, najpierw rysuje w pamięci: bufor ramki o rozmiarze jednego kilobajta dla 128 × 64 pikseli, jak widziałeś w odcinku 1. Na wyświetlacz trafia tylko gotowy obraz. Tryb stron robi to samo w ośmiu paskach po 128 bajtów, jeden po drugim.

Kolorowa ramka potrzebowałaby 150 KB
Kolorowy ekran 320×240 przechowuje dwa bajty na piksel, więc jedna ramka zajmuje 320 × 240 × 2 = 153 600 bajtów, około 150 KB. To prawie połowa RAM, jaką ESP32 daje twojemu szkicowi. Dlatego TFT_eSPI maluje prosto na wyświetlacz, jak Adafruit GFX w odcinku 3.

Widzisz każdy krok
Gdy biblioteka maluje prosto na wyświetlacz, widzisz każdy krok rysowania. Jeśli szkic czyści ekran na początku każdego okrążenia, wyświetlacz najpierw staje się czarny, a obraz wraca kawałek po kawałku.

W naszym teście (ESP32 w symulatorze, SPI na 27 MHz) wyczyszczenie ekranu 320×240 zajęło 48 milisekund, a narysowanie pulpitu kolejne 68. Prawie 50 milisekund czerni przy każdej aktualizacji wystarcza, aby twoje oko to wychwyciło.

Trzy zasady przeciw migotaniu
- Nie czyść ekranu wewnątrz
loop(). ŻadnegofillScreen()przy każdym okrążeniu. - Nadaj każdej liczbie na żywo kolor tła. Bez niego nowe cyfry są rysowane na starych i piętrzą się w bałagan. Z nim każda cyfra jest rysowana w ramce koloru tła, która wymazuje to, co było pod nią.
- Trzymaj inne kształty z dala spod liczby na żywo. Wypełniona karta pod liczbą byłaby malowana na niej przy każdym okrążeniu, a liczba migałaby. Karta z konturem nie dotyka pikseli za cyframi.
Lopaka dba o dwie pierwsze.

Krok 1: zaprojektuj monitor pokojowy
Nowy projekt TFT_eSPI
Kliknij + Nowy projekt i wybierz platformę TFT_eSPI, M5, Lovyan. TFT_eSPI to jedna z najpopularniejszych bibliotek graficznych dla kolorowych wyświetlaczy na ESP32. Ustaw Tytuł na „Room Monitor”, a Rozmiar ekranu na 320×240, zachowaj czarny Kolor tła i kliknij Zapisz.

Narysuj pulpit
Narysuj ekran w ten sam sposób co ostatnio: tytuł, dwie karty z konturem, dwie etykiety, dwie duże liczby, znak stopnia, pasek wilgotności, podpis i czerwony znak LIVE. Oto wartości, których użyliśmy:
| Warstwa | Narzędzie | Wartości w inspektorze | Czcionka | Kolor |
|---|---|---|---|---|
Living room | tekst | X 12, Y 30 | FreeSansBold12pt7b | #FFFFFF |
| karta temperatury | prostokąt | X 12, Y 46, W 142, H 120, R 10 | #FF8200 | |
| karta wilgotności | prostokąt | X 166, Y 46, W 142, H 120, R 10 | #4C8DFF | |
Temperature | tekst | X 24, Y 72 | FreeSans9pt7b | #FF8200 |
Humidity | tekst | X 178, Y 72 | FreeSans9pt7b | #4C8DFF |
23.5 | tekst | X 24, Y 140 | FreeSansBold24pt7b | #FFFFFF |
48% | tekst | X 178, Y 140 | FreeSansBold24pt7b | #FFFFFF |
C | tekst | X 132, Y 122 | FreeSansBold12pt7b | #FFFFFF |
| znak stopnia | elipsa | X 124, Y 104, RX 3, RY 3 | #FFFFFF | |
| pasek | prostokąt, Wypełniony | X 12, Y 182, W 142, H 18 | #4C8DFF | |
| pusta część paska | prostokąt, Wypełniony | X 154, Y 182, W 154, H 18 | #1F2937 | |
Updated every 0.5 s | tekst | X 12, Y 228 | FreeSans9pt7b | #6B7280 |
| Kropka LIVE | elipsa, Wypełniony | X 260, Y 16, RX 4, RY 4 | #EF4444 | |
LIVE | tekst | X 272, Y 27 | FreeSans9pt7b | #EF4444 |
Pierwszy tekst w projekcie TFT_eSPI zaczyna się małą czcionką Adafruit 5x7, a każdy kolejny tekst zaczyna się od ostatniej wybranej czcionki.
Karty mają kontury, zgodnie z zasadą 3. Pasek to dwa prostokąty obok siebie, część wypełniona i część pusta, więc żaden piksel paska nie jest malowany dwa razy. Gdybyś narysował cały ciemny pasek, a na nim niebieską część, niebieska część migałaby przy każdym okrążeniu.
Współrzędna Y tekstu
Spójrz na dużą liczbę temperatury w kodzie:
tft.drawString("23.5", 24, 104);Inspektor mówi Y 140, kod mówi 104. Jak w U8g2, Y w inspektorze to linia bazowa, linia, na której stoją cyfry. Funkcja drawString w TFT_eSPI przyjmuje zamiast tego górę tekstu, więc Lopaka konwertuje liczbę za ciebie. Kolory też są konwertowane: #FF8200 staje się 0xFC00 w RGB565, dwubajtowym formacie kolorów z odcinka 3.
Na razie te liczby są zwykłym tekstem. Nigdy się nie zmienią.
Krok 2: warstwy stają się zmiennymi
Nazwij warstwę
Aby ożywić liczbę, zamień ją w zmienną. Najpierw nazwij warstwę: kliknij dwukrotnie string 6, tekst 23.5, w panelu Warstwy i zmień jego nazwę na temp.
Kliknij przycisk zmiennej
Zaznacz warstwę i kliknij przycisk zmiennej na prawym końcu jej pola tekstowego w inspektorze (jego podpowiedź mówi Dodaj zmienną). Przycisk staje się pomarańczowy, a w kodzie zmieniają się dwie rzeczy:
const char* temp_text = "23.5";// temp
tft.drawString(temp_text, 24, 104);Lopaka deklaruje zmienną na górze kodu, nazwaną od warstwy, z tekstem jako wartością początkową. Wywołanie rysowania używa teraz zmiennej zamiast stałego tekstu. Twój szkic może umieścić dowolny tekst w temp_text, a następne wywołanie funkcji rysowania go pokaże.

Wilgotność i pasek
Zrób to samo dla wilgotności i nadaj paskowi zmienne rozmiary:
- Zmień nazwę
string 7, tekstu48%, nahumi kliknij przycisk zmiennej obok jego tekstu:hum_text. - Zmień nazwę wypełnionego paska na
hum_bari kliknij przycisk zmiennej obok W:hum_bar_w. - Zmień nazwę pustej części paska na
hum_resti kliknij przyciski zmiennych obok X i W:hum_rest_xihum_rest_w.
Pasek otrzymuje zmienną szerokość, a jego pusta część zmienną pozycję i szerokość, więc szkic nadal maluje każdy piksel paska raz, niezależnie od wilgotności.
int hum_bar_w = 142;
int hum_rest_w = 154;
int hum_rest_x = 154;
const char* hum_text = "48%";
const char* temp_text = "23.5";// hum_bar
tft.fillRect(12, 182, hum_bar_w, 18, 0x4C7F);
// hum_rest
tft.fillRect(hum_rest_x, 182, hum_rest_w, 18, 0x1946);Tekst staje się const char*, liczba taka jak szerokość staje się int. Nazwa warstwy i właściwość tworzą nazwę zmiennej: hum_bar + w.

Krok 3: brak mrugania
Tło za każdą liczbą
Zaznacz temp i ustaw jego kolor Tło na #000000, czarny jak ekran. Zrób to samo dla hum. Kod ustawia teraz dwa kolory dla tekstu, cyfry i ramkę za nimi:
// temp
tft.setTextColor(0xFFFF, 0x0);
tft.setFreeFont(&FreeSansBold24pt7b);
tft.drawString(temp_text, 24, 104);
// hum
tft.drawString(hum_text, 178, 104);0xFFFF to biały, 0x0 to czarny. Liczba wilgotności używa tych samych kolorów, więc Lopaka nie powtarza wywołania. To zasada 2.

Wyłącz Wyczyść/Wypełnij wyświetlacz
Teraz zasada 1. Na liście przełączników obok kodu znajdź Wyczyść/Wypełnij wyświetlacz. W nowym projekcie TFT_eSPI jest włączony, a funkcja rysowania zaczyna się od wypełnienia całego ekranu:
void drawScreen_1(void) {
tft.fillScreen(0x0);Ta jedna linia powoduje mruganie: funkcja działa przy każdym okrążeniu loop(), więc ekran staje się czarny przy każdym okrążeniu. Wyłącz Wyczyść/Wypełnij wyświetlacz, a linia znika.

Wyłącz Dołącz czcionki
Jeszcze jeden przełącznik: wyłącz Dołącz czcionki. TFT_eSPI dostarcza już te czcionki FreeFonts, więc szkic nie potrzebuje plików czcionek.
Krok 4: twoja własna pętla
Pisz wokół wygenerowanego bloku
Reszta to kilka linii twojego kodu. Cały szkic możesz napisać tutaj, w panelu kodu. Lopaka przechowuje twój kod wokół wygenerowanego bloku, między // [BEGIN lopaka generated] a // [END lopaka generated], i pozostaje on, gdy zmienisz projekt lub przeładujesz stronę.
Kliknij nad linią BEGIN i wpisz dyrektywy include oraz obiekty:
#include <TFT_eSPI.h>
#include <DHTesp.h>
TFT_eSPI tft;
DHTesp dht;
char t[8], h[8];Następnie, pod linią END, dodaj setup() i loop():
void setup() {
tft.begin();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
tft.setTextPadding(94);
dht.setup(27, DHTesp::DHT22);
}
void loop() {
TempAndHumidity d = dht.getTempAndHumidity();
snprintf(t, sizeof t, "%.1f", d.temperature);
snprintf(h, sizeof h, "%.0f%%", d.humidity);
temp_text = t;
hum_text = h;
hum_bar_w = d.humidity * 2.96;
hum_rest_x = 12 + hum_bar_w;
hum_rest_w = 296 - hum_bar_w;
drawScreen_1();
delay(500);
}
Co robi setup()
setup() uruchamia wyświetlacz, obraca go do orientacji poziomej za pomocą setRotation(1) i maluje go na czarno raz, a następnie uruchamia czujnik na pinie 27. To jedno fillScreen tutaj jest w porządku: działa przy włączeniu zasilania, a nie przy każdym okrążeniu.
setTextPadding(94) nadaje każdej liczbie na żywo stałą ramkę o szerokości 94 pikseli. TFT_eSPI wypełnia tło tekstu tylko na szerokość nowego tekstu. Gdy „23.5” zmieni się na węższą wartość, taką jak „9.8”, prawy koniec starych cyfr pozostałby na ekranie. 94 piksele to szerokość wartości dwucyfrowej, takiej jak „48%”, w tej czcionce, a ramka kończy się przed znakiem stopnia.
Co robi loop()
loop() odczytuje temperaturę i wilgotność, zapisuje je do zmiennych tekstowych, zamienia wilgotność w szerokość paska i wywołuje funkcję rysowania. Następnie czeka pół sekundy i zaczyna od nowa:
snprintfzamienia liczbę w tekst:\"%.1f\"zachowuje jedno miejsce po przecinku, więc 23,46 staje się „23.5”, a\"%.0f%%\"daje „48%”.%%wypisuje znak procenta.temp_text = t;wskazuje zmienną na ten tekst. Buforytihsą zadeklarowane pozaloop(), więc tekst nadal istnieje, gdy funkcja rysowania go odczytuje.- Pasek ma 296 pikseli długości przy 100%, od x = 12 do 308, więc jeden procent to 2,96 piksela. Pusta część zaczyna się tam, gdzie kończy się część wypełniona, i zajmuje resztę.
delay(500)daje dwie aktualizacje na sekundę.
Krok 5: uruchom to
W symulatorze
Kliknij Kopiuj nad kodem. Kopiuje cały panel, łącznie z twoim kodem. W symulatorze z odcinka 1:
- Utwórz nowy projekt ESP32.
- Dodaj wyświetlacz ILI9341 i podłącz go: VCC i LED do 3V3, GND do GND, CS do 15, RST do 2, D/C do 0, MOSI do 13, SCK do 14, MISO do 12.
- Dodaj czujnik DHT22: VCC do 3V3, GND do GND, SDA do 27.
- W Library Manager dodaj TFT_eSPI oraz DHT sensor library for ESPx (bibliotekę z
DHTesp.h). - Wklej kod do
sketch.inoi naciśnij Play.
TFT_eSPI odczytuje swoje piny z pliku ustawień w folderze biblioteki. Symulator używa domyślnych ustawień biblioteki: ILI9341 na tych pinach ESP32.
Kliknij DHT22 i przeciągnij jego suwaki. Liczby i pasek podążają za suwakami bez ani jednego mrugnięcia.

Kompletny kod z przycisku Kopiuj, z blokiem Lopaka pośrodku:
#include <TFT_eSPI.h>
#include <DHTesp.h>
TFT_eSPI tft;
DHTesp dht;
char t[8], h[8];
// [BEGIN lopaka generated]
int hum_bar_w = 142;
int hum_rest_w = 154;
int hum_rest_x = 154;
const char* hum_text = "48%";
const char* temp_text = "23.5";
void drawScreen_1(void) {
// string 1
tft.setTextColor(0xFFFF);
tft.setTextSize(1);
tft.setFreeFont(&FreeSansBold12pt7b);
tft.drawString("Living room", 12, 12);
// rect 2
tft.drawRoundRect(12, 46, 142, 120, 10, 0xFC00);
// rect 3
tft.drawRoundRect(166, 46, 142, 120, 10, 0x4C7F);
// string 4
tft.setTextColor(0xFC00);
tft.setFreeFont(&FreeSans9pt7b);
tft.drawString("Temperature", 24, 58);
// string 5
tft.setTextColor(0x4C7F);
tft.drawString("Humidity", 178, 58);
// temp
tft.setTextColor(0xFFFF, 0x0);
tft.setFreeFont(&FreeSansBold24pt7b);
tft.drawString(temp_text, 24, 104);
// hum
tft.drawString(hum_text, 178, 104);
// string 8
tft.setTextColor(0xFFFF);
tft.setFreeFont(&FreeSansBold12pt7b);
tft.drawString("C", 132, 104);
// ellipse 9
tft.drawEllipse(127, 107, 3, 3, 0xFFFF);
// hum_bar
tft.fillRect(12, 182, hum_bar_w, 18, 0x4C7F);
// hum_rest
tft.fillRect(hum_rest_x, 182, hum_rest_w, 18, 0x1946);
// string 12
tft.setTextColor(0x6B90);
tft.setFreeFont(&FreeSans9pt7b);
tft.drawString("Updated every 0.5 s", 12, 214);
// ellipse 13
tft.fillEllipse(264, 20, 4, 4, 0xEA28);
// string 14
tft.setTextColor(0xEA28);
tft.drawString("LIVE", 272, 13);
}
// [END lopaka generated]
void setup() {
tft.begin();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
tft.setTextPadding(94);
dht.setup(27, DHTesp::DHT22);
}
void loop() {
TempAndHumidity d = dht.getTempAndHumidity();
snprintf(t, sizeof t, "%.1f", d.temperature);
snprintf(h, sizeof h, "%.0f%%", d.humidity);
temp_text = t;
hum_text = h;
hum_bar_w = d.humidity * 2.96;
hum_rest_x = 12 + hum_bar_w;
hum_rest_w = 296 - hum_bar_w;
drawScreen_1();
delay(500);
}Po skompilowaniu dla ESP32 szkic używa 24% przestrzeni programu i 6% RAM.
Ten sam szkic z włączoną opcją Wyczyść/Wypełnij wyświetlacz
Podzielony ekran poniżej uruchamia ten sam szkic dwa razy: po lewej z włączoną opcją Wyczyść/Wypełnij wyświetlacz, po prawej nasz. Jedyna różnica to linia tft.fillScreen(0x0); na początku drawScreen_1(). Lewy ekran jest uchwycony w połowie przerysowywania, z widoczną tylko pierwszą cyfrą. Po prawej zmieniają się tylko cyfry.

Na prawdziwej płytce
Zainstaluj TFT_eSPI oraz DHT sensor library for ESPx z Library Manager w Arduino IDE, podłącz wyświetlacz i czujnik w ten sam sposób i wgraj. Rzeczy do sprawdzenia na prawdziwym sprzęcie:
- Ustawienia TFT_eSPI. Biblioteka pobiera sterownik wyświetlacza i piny z pliku
User_Setup.hwe własnym folderze. Domyślnie jest to ILI9341 z pinami powyżej. W przypadku innego wyświetlacza lub innych pinów zmień ten plik, a nie szkic. - Piny rozruchowe. Na prawdziwym ESP32 GPIO 0 i GPIO 12 decydują też o tym, jak płytka się uruchamia. Jeśli płytka nie uruchamia się przy podłączonym wyświetlaczu, przenieś D/C i MISO na inne piny w
User_Setup.h. - Czujnik. DHT22 daje nowy odczyt co najwyżej co dwie sekundy. Biblioteka zwraca w międzyczasie ostatni odczyt, więc ekran nadal przerysowuje się dwa razy na sekundę. Jeśli liczby pokazują
nan, czujnik nie odpowiedział: sprawdź jego okablowanie.
Podsumowanie
loop()przerysowuje twój ekran w nieskończoność: odczytaj, utwórz tekst, narysuj, powtórz.- Nazwij warstwę, otrzymaj zmienną: przycisk zmiennej zamienia tekst lub rozmiar w zmienną nazwaną od warstwy, a twoja pętla ją zasila.
- Brak czyszczenia i tło oznaczają brak mrugania: wyłącz opcję Wyczyść/Wypełnij wyświetlacz i nadaj każdej liczbie na żywo kolor Tła.
Następny odcinek: Z przeglądarki na płytkę. Zdejmujemy projekt z ekranu i przenosimy na prawdziwą płytkę oraz omawiamy pięć powodów, dla których nowy wyświetlacz pozostaje czarny. Przeczytaj odcinek 5 lub obejrzyj go na YouTube.
Gotowy, aby spróbować? Otwórz Lopaka i ożyw własny ekran.