Zum Inhalt springen

Lopaka Basics 04: Live-Daten ​

Ein 320×240-ILI9341-Display, das ein Living-room-Dashboard mit rotem LIVE-Punkt, Temperature 36.3 °C, Humidity 67%, einer blauen Feuchtigkeitsleiste und der Bildunterschrift Updated every 0.5 s zeigt

Zwei Screens. Gleiches Board, gleicher Sensor, gleiche Zahlen. Aber einer blinzelt jedes Mal, wenn sich eine Zahl ändert. Verlangsame es, und du siehst warum: Das ganze Bild wird weggewischt und dann Stück für Stück zurückgemalt.

In dieser Episode findest du heraus, was loop() tut, warum ein Farbdisplay flackert, wenn ein OLED es nicht tut, und die drei Regeln, die es stoppen. Dann verwandelst du die Zahlen eines Lopaka-Designs in Variablen, fütterst sie von einem Temperatur- und Feuchtigkeitssensor und führst es auf einem simulierten ESP32 ohne ein einziges Blinken aus.

Was du bauen wirst: einen 320×240-Farb-Raummonitor mit Live-Temperatur- und Feuchtigkeitszahlen und einer Feuchtigkeitsleiste, zweimal pro Sekunde aktualisiert. Was du brauchst: einen Browser. Wir führen den Code auf einem simulierten ESP32 mit einem ILI9341-Display und einem DHT22-Sensor aus; ein echtes Board ist optional.

setup() und loop() ​

Jeder Arduino-Sketch hat zwei Teile:

  • setup() läuft einmal, beim Einschalten.
  • loop() läuft danach, wieder und wieder, solange das Board Strom hat.

Live-Daten leben in loop(): Lies den Sensor, verwandle die Zahlen in Text, zeichne sie und wiederhole. Jede Runde zeichnet den Screen erneut.

setup() läuft einmal, beim Einschalten; loop() ist ein Ring mit drei Stationen, Sensor lesen, Text machen und zeichnen, und der Rundenzähler zeigt 4

Das OLED zeichnet zuerst im Speicher ​

Das OLED aus den ersten Episoden blinkte nie. Seine Bibliothek, U8g2, zeichnet zuerst in den Speicher: einen Frame-Puffer von einem Kilobyte für 128 × 64 Pixel, wie du in Episode 1 gesehen hast. Nur das fertige Bild geht ans Display. Der Page-Modus tut dasselbe in acht Streifen von 128 Bytes, einer nach dem anderen.

Die Grafikbibliothek zeichnet in einen 1.024-Byte-Frame-Puffer im RAM, 128 × 64 Pixel geteilt durch 8, und das OLED bekommt nur fertige Pixel

Ein Farbframe bräuchte 150 KB ​

Ein 320×240-Farbscreen speichert zwei Bytes pro Pixel, also braucht ein Frame 320 × 240 × 2 = 153.600 Bytes, etwa 150 KB. Das ist fast die Hälfte des RAM, den ein ESP32 deinem Sketch gibt. Also malt TFT_eSPI direkt aufs Display, wie Adafruit GFX in Episode 3.

Speicher für einen Frame: Der OLED-Frame ist ein winziger 1-KB-Balken, der Farb-TFT-Frame ist ein langer Balken, 320 × 240 × 2 Bytes = 150 KB pro Frame

Du siehst jeden Schritt ​

Wenn die Bibliothek direkt aufs Display malt, siehst du jeden Schritt des Zeichnens. Wenn der Sketch den Screen zu Beginn jeder Runde löscht, wird das Display zuerst schwarz, und das Bild kommt Stück für Stück zurück.

Zeitlupe des flackernden Screens: Das Display wird zu Schwarz gewischt und das Dashboard Stück für Stück zurückgemalt, bisher nur Titel, Kartenumrisse, die Zahlen 36.3 und 48%

In unserem Test (ESP32 im Simulator, SPI bei 27 MHz) brauchte das Löschen des 320×240-Screens 48 Millisekunden und das Zeichnen des Dashboards weitere 68. Fast 50 Millisekunden Schwarz bei jedem Update reichen, damit dein Auge es bemerkt.

Kein Puffer, direkt aufs Display: Der Screen ist halb neu gemalt, die Zeitleiste darunter zeigt Löschen und Zeichnen

Drei Regeln gegen Flackern ​

  1. Lösche den Screen nicht in loop(). Kein fillScreen() bei jeder Runde.
  2. Gib jeder Live-Zahl eine Hintergrundfarbe. Ohne eine werden die neuen Ziffern über die alten gezeichnet und stapeln sich zu einem Durcheinander. Mit einer wird jede Ziffer in einer Box mit Hintergrundfarbe gezeichnet, die löscht, was darunter war.
  3. Halte andere Formen von unter einer Live-Zahl fern. Eine gefüllte Karte unter der Zahl würde bei jeder Runde über sie gemalt, und die Zahl würde blinken. Eine umrissene Karte berührt die Pixel hinter den Ziffern nicht.

Lopaka kümmert sich um die ersten zwei.

Die drei Regeln: Lösche den Screen nicht in loop(), fillScreen durchgestrichen; ein Hintergrund hinter jeder Live-Zahl, eine schwarze Box löscht die alten Ziffern; nichts anderes unter einer Live-Zahl, eine gefüllte orange Karte ist falsch und eine umrissene ist richtig

Schritt 1: Entwirf den Raummonitor ​

Ein neues TFT_eSPI-Projekt ​

Klicke auf + Neues Projekt und wähle die Plattform TFT_eSPI, M5, Lovyan. TFT_eSPI ist eine der häufigsten Grafikbibliotheken für Farbdisplays auf dem ESP32. Setze den Titel auf „Room Monitor“ und die Screen-Größe auf 320×240, behalte die schwarze Hintergrundfarbe und klicke auf Speichern.

Der Dialog Neues Projekt erstellen mit Plattform TFT_eSPI, M5, Lovyan, dem Titel Room Monitor, Screen-Größe 320×240 und Hintergrundfarbe #000000

Zeichne das Dashboard ​

Zeichne den Screen auf dieselbe Weise wie letztes Mal: einen Titel, zwei umrissene Karten, zwei Beschriftungen, zwei große Zahlen, ein Gradzeichen, eine Leiste für Feuchtigkeit, eine Bildunterschrift und eine rote LIVE-Markierung. Das sind die Werte, die wir nutzten:

EbeneWerkzeugWerte im InspektorSchriftartFarbe
Living roomTextX 12, Y 30FreeSansBold12pt7b#FFFFFF
TemperaturkarteRechteckX 12, Y 46, W 142, H 120, R 10#FF8200
FeuchtigkeitskarteRechteckX 166, Y 46, W 142, H 120, R 10#4C8DFF
TemperatureTextX 24, Y 72FreeSans9pt7b#FF8200
HumidityTextX 178, Y 72FreeSans9pt7b#4C8DFF
23.5TextX 24, Y 140FreeSansBold24pt7b#FFFFFF
48%TextX 178, Y 140FreeSansBold24pt7b#FFFFFF
CTextX 132, Y 122FreeSansBold12pt7b#FFFFFF
GradzeichenEllipseX 124, Y 104, RX 3, RY 3#FFFFFF
LeisteRechteck, GefülltX 12, Y 182, W 142, H 18#4C8DFF
leerer Teil der LeisteRechteck, GefülltX 154, Y 182, W 154, H 18#1F2937
Updated every 0.5 sTextX 12, Y 228FreeSans9pt7b#6B7280
LIVE-PunktEllipse, GefülltX 260, Y 16, RX 4, RY 4#EF4444
LIVETextX 272, Y 27FreeSans9pt7b#EF4444

Der erste Text in einem TFT_eSPI-Projekt startet in der kleinen Schriftart Adafruit 5x7, und jeder Text danach startet in der letzten Schriftart, die du gewählt hast.

Die Karten sind Umrisse und folgen Regel 3. Die Leiste sind zwei Rechtecke Seite an Seite, der gefüllte Teil und der leere Teil, sodass kein Pixel der Leiste zweimal gemalt wird. Wenn du die ganze dunkle Leiste und den blauen Teil darüber zeichnen würdest, würde der blaue Teil bei jeder Runde blinken.

Das Y eines Textes ​

Schau dir die große Temperaturzahl im Code an:

cpp
tft.drawString("23.5", 24, 104);

Der Inspektor sagt Y 140, der Code sagt 104. Wie bei U8g2 ist das Y des Inspektors die Grundlinie, die Linie, auf der die Ziffern stehen. Das drawString von TFT_eSPI nimmt stattdessen den oberen Rand des Textes, also wandelt Lopaka die Zahl für dich um. Farben werden auch umgewandelt: #FF8200 wird zu 0xFC00 in RGB565, dem Zwei-Byte-Farbformat aus Episode 3.

Gerade sind diese Zahlen einfacher Text. Sie werden sich nie ändern.

Schritt 2: Ebenen werden Variablen ​

Benenne die Ebene ​

Um eine Zahl live zu machen, verwandle sie in eine Variable. Benenne zuerst die Ebene: Doppelklicke im Ebenen-Panel auf string 6, den 23.5-Text, und benenne ihn in temp um.

Klicke den Variablen-Button ​

Wähle die Ebene aus und klicke den Variablen-Button am rechten Ende ihres Textfeldes im Inspektor (sein Tooltip sagt Variable hinzufügen). Der Button wird orange, und zwei Dinge ändern sich im Code:

cpp
const char* temp_text = "23.5";
cpp
// temp
tft.drawString(temp_text, 24, 104);

Lopaka deklariert oben im Code eine Variable, benannt nach der Ebene, mit dem Text als Startwert. Der Zeichenaufruf nutzt jetzt die Variable statt des festen Textes. Dein Sketch kann jeden Text in temp_text legen, und der nächste Aufruf der Zeichenfunktion zeigt ihn.

Die temp-Ebene ausgewählt mit eingeschaltetem Variablen-Button neben ihrem Textfeld; der Code beginnt mit const char* temp_text = "23.5"

Feuchtigkeit und die Leiste ​

Mache dasselbe für Feuchtigkeit und gib der Leiste variable Größen:

  1. Benenne string 7, den 48%-Text, in hum um und klicke den Variablen-Button neben seinem Text: hum_text.
  2. Benenne die gefüllte Leiste in hum_bar um und klicke den Variablen-Button neben W: hum_bar_w.
  3. Benenne den leeren Teil der Leiste in hum_rest um und klicke die Variablen-Buttons neben X und W: hum_rest_x und hum_rest_w.

Die Leiste bekommt eine variable Breite und ihr leerer Teil eine variable Position und Breite, sodass der Sketch jedes Pixel der Leiste einmal malt, egal wie die Feuchtigkeit ist.

cpp
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";
cpp
// hum_bar
tft.fillRect(12, 182, hum_bar_w, 18, 0x4C7F);
// hum_rest
tft.fillRect(hum_rest_x, 182, hum_rest_w, 18, 0x1946);

Ein Text wird zu einem const char*, eine Zahl wie eine Breite wird zu einem int. Der Ebenenname und die Eigenschaft ergeben den Variablennamen: hum_bar + w.

Die hum_rest-Ebene ausgewählt mit eingeschalteten Variablen-Buttons für X und W; der Code beginnt mit int hum_bar_w = 142, int hum_rest_w = 154, int hum_rest_x = 154 und den zwei Textvariablen

Ein Hintergrund hinter jeder Zahl ​

Wähle temp aus und setze seine Hintergrund-Farbe auf #000000, Schwarz wie der Screen. Mache dasselbe für hum. Der Code setzt jetzt zwei Farben für den Text, die Ziffern und die Box dahinter:

cpp
// temp
tft.setTextColor(0xFFFF, 0x0);
tft.setFreeFont(&FreeSansBold24pt7b);
tft.drawString(temp_text, 24, 104);
// hum
tft.drawString(hum_text, 178, 104);

0xFFFF ist Weiß, 0x0 ist Schwarz. Die Feuchtigkeitszahl nutzt dieselben Farben, also wiederholt Lopaka den Aufruf nicht. Das ist Regel 2.

Die temp-Ebene mit Hintergrund auf #000000 im Inspektor; der Code zeigt tft.setTextColor(0xFFFF, 0x0) vor drawString(temp_text, 24, 104)

Schalte Display löschen/füllen aus ​

Jetzt Regel 1. Finde in der Liste der Schalter neben dem Code Display löschen/füllen. In einem neuen TFT_eSPI-Projekt ist es an, und die Zeichenfunktion beginnt damit, den ganzen Screen zu füllen:

cpp
void drawScreen_1(void) {
    tft.fillScreen(0x0);

Diese eine Zeile verursacht das Blinken: Die Funktion läuft bei jeder Runde von loop(), also wird der Screen bei jeder Runde schwarz. Schalte Display löschen/füllen aus, und die Zeile ist weg.

Das Code-Panel mit eingeschaltetem Display löschen/füllen: Die Zeichenfunktion beginnt mit tft.fillScreen(0x0); Schriftarten einbeziehen ist aus

Schalte Schriftarten einbeziehen aus ​

Ein weiterer Schalter: Schalte Schriftarten einbeziehen aus. TFT_eSPI bringt diese FreeFonts bereits mit, also braucht der Sketch keine Schriftartdateien.

Schritt 4: Deine eigene Schleife ​

Schreibe um den generierten Block herum ​

Der Rest sind wenige Zeilen von dir. Du kannst den ganzen Sketch direkt hier im Code-Panel schreiben. Lopaka hält deinen Code um seinen generierten Block herum, zwischen // [BEGIN lopaka generated] und // [END lopaka generated], und er bleibt, wenn du das Design änderst oder die Seite neu lädst.

Klicke über die BEGIN-Zeile und tippe die Includes und Objekte:

cpp
#include <TFT_eSPI.h>
#include <DHTesp.h>

TFT_eSPI tft;
DHTesp dht;
char t[8], h[8];

Füge dann unter der END-Zeile setup() und loop() hinzu:

cpp
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);
}

Das Code-Panel nach der END-lopaka-generated-Markierung: void setup() mit tft.begin, setRotation, fillScreen, setTextPadding(94) und dht.setup, dann void loop(), das den Sensor liest; Schriftarten einbeziehen und Display löschen/füllen sind aus

Was setup() tut ​

setup() startet das Display, dreht es mit setRotation(1) ins Querformat und malt es einmal schwarz, dann startet es den Sensor an Pin 27. Das eine fillScreen hier ist in Ordnung: Es läuft beim Einschalten, nicht bei jeder Runde.

setTextPadding(94) gibt jeder Live-Zahl eine feste Box mit 94 Pixeln Breite. TFT_eSPI füllt den Texthintergrund nur so breit wie der neue Text. Wenn „23.5“ sich zu einem schmaleren Wert wie „9.8“ ändert, bliebe das rechte Ende der alten Ziffern auf dem Screen. 94 Pixel ist die Breite eines zweistelligen Werts wie „48%“ in dieser Schriftart, und die Box stoppt vor dem Gradzeichen.

Was loop() tut ​

loop() liest Temperatur und Feuchtigkeit, schreibt sie in die Textvariablen, verwandelt Feuchtigkeit in die Leistenbreite und ruft die Zeichenfunktion auf. Dann wartet es eine halbe Sekunde und macht weiter:

  • snprintf verwandelt eine Zahl in Text: \"%.1f\" behält eine Dezimalstelle, also wird aus 23,46 „23.5“, und \"%.0f%%\" ergibt „48%“. %% druckt das Prozentzeichen.
  • temp_text = t; lässt die Variable auf diesen Text zeigen. Die Puffer t und h sind außerhalb von loop() deklariert, also ist der Text noch da, wenn die Zeichenfunktion ihn liest.
  • Die Leiste ist bei 100 % 296 Pixel lang, von x = 12 bis 308, also ist ein Prozent 2,96 Pixel. Der leere Teil beginnt, wo der gefüllte Teil endet, und nimmt den Rest.
  • delay(500) gibt zwei Updates pro Sekunde.

Schritt 5: Führe ihn aus ​

Im Simulator ​

Klicke über dem Code auf Kopieren. Es kopiert das ganze Panel, deinen Code eingeschlossen. Im Simulator aus Episode 1:

  1. Erstelle ein neues ESP32-Projekt.
  2. Füge ein ILI9341-Display hinzu und verbinde es: VCC und LED mit 3V3, GND mit GND, CS mit 15, RST mit 2, D/C mit 0, MOSI mit 13, SCK mit 14, MISO mit 12.
  3. Füge einen DHT22-Sensor hinzu: VCC mit 3V3, GND mit GND, SDA mit 27.
  4. Füge im Bibliotheksmanager TFT_eSPI und DHT sensor library for ESPx (die Bibliothek mit DHTesp.h) hinzu.
  5. Füge den Code in sketch.ino ein und drücke Play.

TFT_eSPI liest seine Pins aus einer Einstellungsdatei im Bibliotheksordner. Der Simulator nutzt die Standardeinstellungen der Bibliothek: ein ILI9341 an diesen ESP32-Pins.

Klicke auf den DHT22 und ziehe seine Schieberegler. Zahlen und Leiste folgen den Reglern ohne ein einziges Blinken.

Der Simulator, der den Sketch ausführt: Die DHT22-Regler sind auf 18,2 °C und 31,0 % gesetzt, und der ILI9341 neben dem ESP32 zeigt Temperature 18.2 °C und Humidity 31% mit einer kurzen blauen Leiste

Der vollständige Code vom Kopieren-Button, mit dem Lopaka-Block in der Mitte:

cpp
#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);
}

Für einen ESP32 kompiliert, nutzt der Sketch 24 % des Programmplatzes und 6 % des RAM.

Derselbe Sketch mit Display löschen/füllen ​

Der geteilte Screen unten führt denselben Sketch zweimal aus: links mit eingeschaltetem Display löschen/füllen, rechts unseren. Der einzige Unterschied ist die Zeile tft.fillScreen(0x0); am Anfang von drawScreen_1(). Der linke Screen ist mitten im Neuzeichnen erwischt, mit nur der ersten Ziffer zurück. Rechts ändern sich nur die Ziffern.

Zwei simulierte Displays Seite an Seite: Mit eingeschaltetem Display löschen/füllen ist der linke Screen mitten im Neuzeichnen mit nur der Ziffer 3 sichtbar; mit ausgeschaltetem zeigt der rechte Screen 36.3 °C und 48%

Auf einem echten Board ​

Installiere TFT_eSPI und DHT sensor library for ESPx aus dem Bibliotheksmanager in der Arduino IDE, verdrahte Display und Sensor auf dieselbe Weise und lade hoch. Dinge, die du auf echter Hardware prüfen solltest:

  • TFT_eSPI-Einstellungen. Die Bibliothek nimmt Display-Treiber und Pins aus User_Setup.h in ihrem eigenen Ordner. Der Standard ist ein ILI9341 mit den obigen Pins. Ändere für ein anderes Display oder andere Pins diese Datei, nicht den Sketch.
  • Boot-Pins. An einem echten ESP32 entscheiden GPIO 0 und GPIO 12 auch, wie das Board startet. Wenn das Board mit angeschlossenem Display nicht bootet, verschiebe D/C und MISO in User_Setup.h auf andere Pins.
  • Der Sensor. Ein DHT22 gibt höchstens jede zwei Sekunden eine neue Messung. Die Bibliothek gibt die letzte Messung dazwischen zurück, also zeichnet der Screen trotzdem zweimal pro Sekunde neu. Wenn die Zahlen nan zeigen, antwortete der Sensor nicht: Prüfe seine Verdrahtung.

Zusammenfassung ​

  1. loop() zeichnet deinen Screen für immer neu: Lies, mache Text, zeichne, wiederhole.
  2. Benenne eine Ebene, erhalte eine Variable: Der Variablen-Button verwandelt einen Text oder eine Größe in eine Variable, benannt nach der Ebene, und deine Schleife füttert sie.
  3. Kein Löschen plus ein Hintergrund bedeutet kein Blinken: Schalte Display löschen/füllen aus und gib jeder Live-Zahl eine Hintergrundfarbe.

Nächste Episode: Vom Browser zum Board. Wir bringen ein Design vom Screen auf ein echtes Board und gehen fünf Gründe durch, warum ein neues Display schwarz bleibt. Lies Episode 5 oder schaue sie auf YouTube.

Bereit, es zu versuchen? Öffne Lopaka und mache deinen eigenen Screen live.