Zum Inhalt springen

Lopaka Basics 01: Dein erster Screen ​

Ein SSD1306-OLED-Modul, das ein umrahmtes HELLO! mit der Bildunterschrift my first screen zeigt

Thermostate, Smartwatches, 3D-Drucker, Wetterstationen: Fast jedes Gadget hat einen kleinen Screen, und jemand musste ihn zeichnen. Lopaka Basics ist eine zehnteilige Reihe für Leute, die noch nie einen Mikrocontroller angefasst und noch nie Lopaka geöffnet haben.

Jede Episode folgt demselben Weg: zeichne es, hole den Code, sieh es auf einem Screen. In dieser ersten siehst du, was in einem Gadget passiert, wenn es ein Bild zeigt, entwirfst dann einen kleinen Screen in Lopaka und führst den generierten Code auf einem simulierten Arduino aus.

Was du bauen wirst: einen 128×64-OLED-Screen mit einem Rahmen, einem großen „HELLO!“, einer Bildunterschrift, einer Trennlinie und einem kleinen Statuspunkt. Was du brauchst: einen Browser. Ein echtes Board ist optional; wir führen den Code in einem kostenlosen Online-Simulator aus.

Was in einem Gadget steckt ​

Ein winziger Computer, der ein Programm ausführt ​

Die meisten kleinen Geräte haben innen ein Mikrocontroller-Board: ein Arduino UNO, ein ESP32, ein Raspberry Pi Pico oder etwas Ähnliches. Ein Mikrocontroller ist ein winziger Computer. Anders als dein Laptop führt er ein Programm aus, deine Firmware. In einem Arduino-Sketch wiederholt sich die loop()-Funktion dieses Programms, solange das Board Strom hat.

Dein Programm zeichnet alles, was auf dem Screen erscheint. Das Board gibt dir keinen Desktop, keine Fenster und keine fertigen Buttons, mit denen du anfangen könntest.

Ein Arduino UNO, mit vier Drähten an ein 128×64-OLED-Display verdrahtet: GND, VCC, SCL und SDA

Der Screen ist ein Raster aus Pixeln ​

Das Display, das wir in den ersten Episoden durchgehend nutzen, ist eines der häufigsten in der Hobby-Elektronik: ein kleines monochromes OLED, 128 Pixel breit und 64 Pixel hoch. Das gängige Modul ist ein 0,96-Zoll-Board mit dem SSD1306-Controller. Das macht 8.192 Pixel, und jedes ist entweder an oder aus. Ohne Graustufen sieht alles darauf, Text eingeschlossen, gestochen scharf und blockig aus.

Die I2C-Version des Moduls verbindet sich mit vier Drähten mit dem Board: zwei für Strom (GND und VCC) und zwei für Daten (SCL und SDA).

Das 128 × 64-Pixelraster des OLED-Displays: 8.192 Pixel, jedes entweder an oder aus

Jedes Pixel hat eine Adresse ​

Um ein bestimmtes Pixel leuchten zu lassen, muss dein Programm sagen, welches. Jedes Pixel hat eine Adresse aus zwei Zahlen:

  • x zählt von der linken Kante,
  • y zählt nach unten von der oberen Kante.

Also ist (0, 0) die obere linke Ecke. Zwei Details bringen die meisten Anfänger zu Fall:

  1. Y wächst nach unten, das Gegenteil zur Schulmathematik. Ein Pixel bei (40, 20) ist 40 Pixel nach rechts und 20 Pixel nach unten von der Ecke.
  2. Das Zählen beginnt bei null. Auf einem 128×64-Display ist die letzte Spalte 127 und die letzte Zeile 63.

Pixelkoordinaten auf einem 128×64-Display: x läuft von 0 bis 127 nach rechts, y läuft von 0 bis 63 nach unten, mit den markierten Punkten (0, 0) und (40, 20)

Eine Grafikbibliothek verwandelt Befehle in Pixel ​

Du schaltest die 8.192 Pixel nicht selbst. Dein Programm ruft eine Grafikbibliothek auf und spricht mit ihr in Formen: Zeichne hier einen Rahmen, schreibe dort diesen Text. Die Bibliothek findet heraus, welche Pixel das bedeutet, und sendet sie an das Display.

Für kleine monochrome Displays ist eine der beliebtesten Bibliotheken U8g2. Zwei Zeilen reichen, um den Anfang unseres Screens zu zeichnen:

cpp
u8g2.drawFrame(2, 2, 124, 60);      // a frame: x, y, width, height
u8g2.drawStr(28, 32, "HELLO!");     // text at x = 28, y = 32

Die Grafikbibliothek zeichnet aus zwei Zeilen U8g2-Code einen Rahmen und das Wort HELLO!

Der alte Weg: raten, kompilieren, hochladen, schauen ​

Du schreibst jede Zahl in diesem Code von Hand. Du rätst, wo der Text beginnen sollte, kompilierst den Sketch, lädst ihn auf das Board und schaust auf den Screen. Der Text ist drei Pixel zu weit rechts? Ändere eine Zahl und gehe die Runde noch einmal: kompilieren, hochladen, schauen.

Der alte Weg, einen Screen von Hand zu programmieren: eine Schleife aus Zahlen raten, kompilieren, hochladen und auf den Screen schauen, schon in Runde 8

Jede Runde kostet ein Kompilieren und ein Hochladen, oft Dutzende Sekunden, und du brauchst ein angeschlossenes Board, um überhaupt etwas zu sehen. Selbst ein einfacher Screen wie unserer kann Dutzende Runden brauchen, bis alles ausgerichtet ist.

Lopaka beseitigt diese Schleife. Es ist ein Editor, der in deinem Browser läuft. Du zeichnest den Screen, und Lopaka schreibt den Code für deine Grafikbibliothek, während du zeichnest.

Öffne lopaka.app und melde dich mit E-Mail, Google oder GitHub an. Lopaka hält deine Projekte in der Cloud, sodass du sie von jedem Computer öffnen kannst.

Die Lopaka-Startseite: Meine Projekte mit dem Button Neues Projekt oben und die Community-Projektgalerie darunter

Schritt 1: Erstelle ein neues Projekt ​

Klicke auf + Neues Projekt. Die erste und wichtigste Wahl ist die Plattform, die Grafikbibliothek, die deine Firmware nutzen wird. Das entscheidet, welche Art von Code Lopaka generiert, also wähle die Bibliothek, die du auf dem Board installieren wirst.

Für jede Bibliothek schreibt Lopaka den Code so, wie diese Bibliothek ihn erwartet.

Für ein kleines Schwarz-Weiß-OLED wähle U8g2. Seine Beschreibung nennt es „die Standardwahl für monochrome OLED- und LCD-Panels wie SSD1306, SH1106 und ST7565, bei denen jedes Pixel einfach an oder aus ist“, und der Code lässt sich als Arduino C++ oder ESP-IDF C exportieren.

Fülle dann den Rest des Dialogs aus:

  • Titel: „My First Screen“.
  • Screen-Größe: 128×64. Für U8g2 ist sie bereits ausgewählt; die Liste hat Dutzende andere Größen. Der Schalter Benutzerdefiniert lässt dich jede Größe eintippen, und Rund ist für runde Displays.

Klicke auf Speichern.

Der Dialog Neues Projekt erstellen mit Plattform auf U8g2, dem Titel My First Screen und der Screen-Größe 128×64

Schritt 2: Eine kurze Tour durch den Editor ​

Das ist der Editor. Alles, was du für diese Episode brauchst, passt auf einen Screen:

Der Lopaka-Editor mit orange umrissenen Teilen: Projekt, Bibliothek und Größe oben, Zeichenwerkzeuge, Ebenen, Zeichenfläche, Eigenschaften, generierter Code und Code-Optionen

  • Obere Leiste. Der Projektname, gefolgt von zwei Abzeichen: der Bibliothek (u8g2) und der Screen-Größe (128×64). Das Zahnrad-Icon daneben öffnet die Projekteinstellungen. Rechts sind Fork und Teilen.
  • Zeichenwerkzeuge über der Zeichenfläche: Rückgängig und Wiederholen, das Auswahlwerkzeug, dann Pinsel, Text (Aa), Rechteck, Ellipse (das Kreis-Icon), Linie, Dreieck und Polygon.
  • Die Zeichenfläche in der Mitte ist dein Screen mit 128×64, mit Linealen an ihren Rändern. Wenn du eine Form auswählst, markieren die Lineale ihre genauen Ränder in Pixeln.
  • Ebenen links. Jede Form, die du zeichnest, wird hier zu einer Ebene, gruppiert unter ihrem Screen. Screen hinzufügen erstellt einen weiteren Screen im selben Projekt; die Tabs Bilder und Animationen darunter halten deine Bitmaps und Animationen.
  • Eigenschaften rechts, auch Inspektor genannt: die genaue Position, Größe und der Stil von dem, was ausgewählt ist.
  • Generierter Code unten. Er aktualisiert sich live, während du zeichnest. Daneben sind der Schalter Syntax (Arduino C++ oder ESP-IDF C für U8g2) und Schalter, die steuern, was in den Code kommt.

Schritt 3: Zeichne den Screen ​

Ein Rahmen und deine erste Codezeile ​

Wähle das Rechteck-Werkzeug und ziehe über die Zeichenfläche. Ein Rahmen erscheint auf dem Screen, und im selben Moment erscheint eine neue Zeile im Code:

cpp
u8g2.drawFrame(2, 2, 124, 60);

Diese vier Zahlen sind x, y, Breite und Höhe: dieselben Pixeladressen vom Anfang des Artikels. Überspringe präzises Ziehen und tippe exakte Werte in den Inspektor: X 2, Y 2, W 124, H 60. Die Lineale folgen deinen Zahlen und markieren die Ränder der Form bei 2 und 126 quer, 2 und 62 nach unten. Die Lineale zählen die Linien zwischen Pixeln, sodass die rechte Kante bei 126 bedeutet, dass die letzte leuchtende Spalte 125 ist.

Ein auf der Zeichenfläche ausgewähltes Rechteck mit X 2, Y 2, W 124 und H 60 im Inspektor und der passenden drawFrame-Zeile im Code hervorgehoben

Ein Rahmen, der bei x = 2 beginnt und 124 Pixel breit ist, deckt die Spalten 2 bis 125 ab. Das lässt zwei leere Spalten auf jeder Seite des 128-Pixel-Screens und denselben Zwei-Pixel-Rand oben und unten. Von Hand würdest du diese Rechnung vor jedem Kompilieren im Kopf machen.

Die anderen Rechteck-Eigenschaften ändern den Code auch:

  • R rundet die Ecken, und der Aufruf wird zu drawRFrame.
  • Gefüllt verwandelt den Umriss in einen vollen Block, drawBox.
  • Invertiert (XOR) kippt die Pixel unter der Form, statt sie einzuschalten, was für hervorgehobene Menüeinträge praktisch ist.

Text und Schriftarten ​

Wähle das Text-Werkzeug (Aa) und klicke auf die Zeichenfläche. Eine neue Ebene mit dem Wort Text erscheint; wähle sie aus, falls sie noch nicht ausgewählt ist, und tippe HELLO! in das Textfeld im Inspektor. Der erste Text in einem Projekt nutzt die Standard-Schriftart 4x6, die winzig ist: Jedes Zeichen ist vier Pixel breit. Jeder Text, den du danach hinzufügst, startet in der letzten Schriftart, die du gewählt hast.

Öffne die Liste Schriftart. Lopaka bietet 42 eingebaute U8g2-Schriftarten, von 4×6-Pixel-Mikrotext bis zu großen Display-Schriften. Der erste Eintrag in der Liste, Schriftart importieren..., fügt deine eigene hinzu. Wähle profont22.

Die Schriftartliste im Inspektor geöffnet, von 4x6 bis profont29, mit Schriftart importieren oben

Vor dem Text erscheint eine setFont-Zeile im Code, damit die Bibliothek weiß, mit welcher Schriftart sie ihn zeichnen soll:

cpp
u8g2.setFont(u8g2_font_profont22_tr);
u8g2.drawStr(56, 35, "HELLO!");

Ausrichten und den Zahlen beim Wandern zusehen ​

Platziere den Text jetzt mit den Ausrichten-Buttons oben im Inspektor: Die beiden mittleren Buttons legen den Text in die Mitte des Screens, horizontal und vertikal. Oder ziehe ihn irgendwohin.

Beobachte den Code, während du das tust. Jede Bewegung aktualisiert die Zahlen auf einmal, und das Zentrieren setzt X auf 28. Du siehst das Ergebnis, während du es änderst, ohne Kompilieren und ohne Hochladen.

HELLO! mit den Ausrichten-Buttons auf dem Screen zentriert; der Code liest jetzt drawStr(28, 39, "HELLO!")

Schau dir die Lineale in diesem Screenshot an: Sie markieren die Box um die Buchstaben von 25 bis 39, und der Code sagt drawStr(28, 39, ...). In U8g2 ist das y eines Textes seine Grundlinie, die Linie, auf der die Buchstaben stehen, und die Buchstaben füllen die Zeilen darüber. Text, den du von Hand bei y = 0 platzierst, landet über dem Screen, ein klassischer erster Fehler. Der Inspektor von Lopaka nutzt dieselbe Grundlinie für Y, sodass die Zahl im Feld direkt in den Code geht und die Zeichenfläche zeigt, wo die Buchstaben landen werden.

Im fertigen Design schieben wir „HELLO!“ ein wenig höher, auf y = 32, um darunter Platz für Linie und Bildunterschrift zu machen.

Bildunterschrift, Linie und ein Statuspunkt ​

Beende den Screen mit drei weiteren Ebenen:

  • Eine Bildunterschrift. Eine weitere Text-Ebene, my first screen. Sie startet in profont22, der letzten Schriftart, die du genutzt hast, also wechsle sie zur kleineren 6x10, zentriere sie horizontal und setze Y auf 52: drawStr(19, 52, "my first screen"). Weil dieser Text eine andere Schriftart nutzt, fügt Lopaka ein weiteres setFont davor ein. Wenn zwei aufeinanderfolgende Texte dieselbe Schriftart nutzen, wiederholt es den Aufruf nicht.
  • Eine Linie unter „HELLO!“: drawLine(24, 39, 103, 39). Bei Linien sind die vier Zahlen der Startpunkt und der Endpunkt.
  • Ein kleiner Punkt in der oberen rechten Ecke, mit dem Ellipsen-Werkzeug gemacht, mit eingeschaltetem Gefüllt.

Im Inspektor hat der Punkt X 117, Y 10, RX 2 und RY 2: die obere linke Ecke der Box um ihn und seine zwei Radien. Die Ellipsenfunktion von U8g2 nimmt den Mittelpunkt, also sagt der Code drawFilledEllipse(119, 12, 2, 2). Jede Bibliothek hat Konventionen wie diese. Lopaka wandelt sie für dich um, und du arbeitest mit dem, was du auf der Zeichenfläche siehst.

Schau dir jetzt das Ebenen-Panel an: rect 1, string 2, string 3, line 4, ellipse 5. Jede Form ist eine Ebene, und jede Ebene ist eine Codezeile, mit ihrem Namen als Kommentar darüber.

Schritt 4: Hole den Code ​

Wenn der Screen richtig aussieht, klicke über dem Code auf Kopieren. Du erhältst eine fertige Funktion, benannt nach dem Screen:

cpp
// [BEGIN lopaka generated]
void drawScreen_1(void) {
    u8g2.setFontMode(1);
    u8g2.setBitmapMode(1);
    // rect 1
    u8g2.drawFrame(2, 2, 124, 60);
    // string 2
    u8g2.setFont(u8g2_font_profont22_tr);
    u8g2.drawStr(28, 32, "HELLO!");
    // string 3
    u8g2.setFont(u8g2_font_6x10_tr);
    u8g2.drawStr(19, 52, "my first screen");
    // line 4
    u8g2.drawLine(24, 39, 103, 39);
    // ellipse 5
    u8g2.drawFilledEllipse(119, 12, 2, 2);
}
// [END lopaka generated]

Das Panel mit generiertem Code mit dem Kopieren-Button und Schaltern für PROGMEM, die Wrapper-Funktion, Variablen, Schriftarten, Bilder, Animationshelfer, Kommentare und das Löschen des Displays

Vier Details in diesem Code:

  • Die Schriftarten sind bereits gesetzt. Jeder Text erhält die Schriftart, die du im Editor gewählt hast, mit dem richtigen U8g2-Schriftartnamen. In u8g2_font_profont22_tr bedeutet das t einen transparenten Hintergrund und das r den reduzierten Zeichensatz: die grundlegenden lateinischen Buchstaben, Ziffern und Satzzeichen, was die Schriftart klein im Speicher hält.
  • setFontMode(1) und setBitmapMode(1) machen Text und Bilder transparent, sodass ein über eine Linie gezeichneter Buchstabe sie nicht mit seinem Hintergrund auslöscht.
  • Die BEGIN- und END-Markierungen zeigen, welcher Teil deines Sketches von Lopaka kam. Wenn du das Design änderst, ersetze alles dazwischen.
  • Die Schalter neben dem Code steuern, was in ihn kommt: das Zeichnen in eine Funktion verpacken, Bilder als PROGMEM deklarieren, Schriftarten und Bilddaten einbeziehen, Kommentare mit Ebenennamen und mehr. Die Standardeinstellungen sind für diese Episode richtig.

Schritt 5: Führe ihn aus ​

Wohin die Funktion gehört: der Frame-Puffer ​

Dein Sketch muss die kopierte Funktion aufrufen. Ein minimaler Arduino-Sketch für unser Display:

cpp
#include <U8g2lib.h>
#include <Wire.h>

// 128x64 SSD1306 OLED on I2C, full frame buffer
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

// Paste the Lopaka code here

void setup() {
  u8g2.begin();
}

void loop() {
  u8g2.clearBuffer();
  drawScreen_1();
  u8g2.sendBuffer();
}

Der lange Name des Display-Objekts liest sich von links nach rechts: der SSD1306-Controller, ein 128X64-Panel, ein generisches (NONAME) Modul, ein Full-Frame-Puffer, verbunden über HardWare-I2C. U8G2_R0 bedeutet keine Rotation, und U8X8_PIN_NONE sagt, dass das Modul keinen verdrahteten Reset-Pin hat.

Rufe in loop() deine Funktion zwischen clearBuffer() und sendBuffer() auf:

  1. clearBuffer() wischt das Bild weg.
  2. drawScreen_1() zeichnet alles im Speicher, nicht auf dem Display.
  3. sendBuffer() sendet das fertige Bild in einem Zug an das Display.

Dieses Bild im Speicher heißt Frame-Puffer. Das Display erhält nur vollständige Frames, sodass du nie einen halb gezeichneten Screen siehst. Der Preis ist Speicher: 128 × 64 Pixel mit je einem Bit sind 1.024 Bytes, die Hälfte der 2 KB RAM, die ein Arduino UNO hat. Unser ganzer Sketch belegt 12.218 Bytes Flash (37 %) und 1.578 Bytes RAM (77 %) auf einem UNO, und der größte Teil dieses RAM ist der Frame-Puffer. Für einen so kleinen Sketch ist das in Ordnung. Auf einem ESP32 nutzt derselbe Sketch 7 % des RAM. Wir kommen später in der Reihe auf Speicher zurück, und darauf, mit weniger zu zeichnen.

Der Sketch mit erklärtem Frame-Puffer: clearBuffer wischt das Bild weg, drawScreen_1 zeichnet im Speicher, sendBuffer sendet es an das Display; 1.024 Bytes sind die Hälfte des RAM eines Arduino UNO

Noch kein Board? Führe ihn in einem Simulator aus ​

Du kannst deinen Screen ohne jede Hardware arbeiten sehen. Wokwi ist ein kostenloser Online-Simulator, der echten Arduino-Code kompiliert und ausführt:

  1. Erstelle ein neues Arduino-UNO-Projekt.
  2. Füge ein SSD1306-OLED-Display hinzu und verbinde vier Drähte: GND mit GND, VCC mit 5V, SCL mit A5 und SDA mit A4.
  3. Füge die U8g2-Bibliothek im Tab Bibliotheksmanager hinzu.
  4. Füge den Sketch oben in sketch.ino ein, mit dem Lopaka-Code an seiner Stelle.
  5. Drücke Play.

Das Display zeigt, was du gezeichnet hast, ausgeführt als echter Arduino-Code. Wir fügten den Code aus Lopaka ein, ohne ein Zeichen zu ändern, und das simulierte Display stimmte Pixel für Pixel mit der Lopaka-Zeichenfläche überein.

Der Simulator, der den Sketch ausführt: Das simulierte OLED, verbunden mit einem Arduino UNO, zeigt HELLO! und my first screen

Auf einem echten Board ​

Weil es echter Code ist, läuft derselbe Sketch auch auf einem echten Board. Verdrahte das Display auf dieselbe Weise, installiere U8g2 aus dem Bibliotheksmanager in der Arduino IDE und lade hoch. Zwei Dinge, die du auf echter Hardware prüfen solltest:

  • Der Controller. Die meisten 0,96-Zoll-Module nutzen den SSD1306. Viele 1,3-Zoll-Module sehen gleich aus, nutzen aber den SH1106; ändere bei diesen im Konstruktor SSD1306 zu SH1106: U8G2_SH1106_128X64_NONAME_F_HW_I2C.
  • Strom. Die meisten dieser Module akzeptieren sowohl 3,3 V als auch 5 V, aber manche reinen Boards sind nur 3,3 V. Prüfe die Artikelbeschreibung deines Moduls.

Eine spätere Episode der Reihe geht diesen Schritt ausführlich durch, einschließlich dessen, was du prüfen solltest, wenn der Screen schwarz bleibt.

Zusammenfassung ​

Der Workflow bleibt für jeden Screen, den du machst, derselbe:

  1. Wähle deine Bibliothek: Die Plattform, die du beim Erstellen eines Projekts wählst, entscheidet, welchen Code du erhältst. Beginne für ein monochromes OLED mit U8g2.
  2. Zeichne deinen Screen: Jede Form ist eine Ebene, jede Ebene ist eine Codezeile, und der Inspektor zeigt die genauen Zahlen.
  3. Kopiere den Code: Rufe die Funktion zwischen clearBuffer() und sendBuffer() auf und führe sie im Simulator oder auf einem echten Board aus.

Nächste Episode: Text, Schriftarten & Icons. Wir bauen eine Mini-Wetterstation und finden heraus, warum jede Schriftart Speicher kostet. Lies Episode 2 oder schaue sie auf YouTube.

Bereit, es zu versuchen? Öffne Lopaka und zeichne deinen ersten Screen.