Lopaka Basics 09: Über Arduino hinaus

C++. YAML. Python. Noch mehr Python. Und C für einen Flipper Zero. Fünf verschiedene Programme zeichnen denselben Screen, und Lopaka schrieb den Zeichencode für jedes davon.
In dieser Episode findest du heraus, warum Boards Code in so unterschiedlichen Sprachen ausführen und was kompilierte Firmware von einem Python-Interpreter trennt. Dann zeichnest du einen kleinen Pflanzenmonitor in Lopaka für MicroPython, baust denselben Screen für CircuitPython, ESPHome und Flipper Zero und führst drei davon auf simulierten Boards aus.
Was du bauen wirst: einen 128×64-Pflanzenmonitor-Screen: einen Topf mit drei Blättern, den Namen der Pflanze, eine Feuchtigkeitsleiste und eine Erinnerung, sie zu gießen, auf vier Plattformen. Was du brauchst: einen Browser und den Online-Simulator aus Episode 1. Boards sind optional: ein Raspberry Pi Pico oder ein ESP32 mit einem 128×64-SSD1306-OLED oder ein Flipper Zero.
Zwei Wege, Code auszuführen
Kompiliert: die ganze Firmware, jedes Mal
Arduino-Code ist kompiliert. Ein Compiler verwandelt deinen Sketch in Firmware, rohe Maschinenanweisungen für den Chip, und du flashst die ganze Firmware auf das Board. Ändere eine Zeile, und du kompilierst und flashst alles erneut.
ESPHome funktioniert genauso: Deine YAML-Datei wird zu C++, und das C++ wird zu Firmware. Eine Flipper-Zero-App ist auch kompiliert.
Interpretiert: eine Textdatei auf dem Board
MicroPython und CircuitPython drehen das um. Du flashst einmal einen Interpreter auf das Board. Danach ist dein Programm eine einfache Textdatei auf dem Board, etwa main.py, und der Interpreter liest sie Zeile für Zeile. Ändere eine Zeile, speichere die Datei, und sie läuft erneut, ganz ohne Compiler dazwischen.
Der Preis ist Geschwindigkeit und Speicher: Der Interpreter selbst läuft auf dem Chip und nimmt seinen Anteil. Halte auf kleinen Boards das Zeichnen einfach.

Schritt 1: Wähle eine Plattform
In Lopaka ist die Plattform das Erste, was du in einem neuen Projekt wählst. Klicke auf + Neues Projekt und öffne die Liste Plattform. Neben dem Namen jeder Plattform ist ein Abzeichen mit der Sprache ihres Codes: C++, YAML, Python oder C.

Beginne mit Micropython. Behalte die Screen-Größe bei 128×64, gib dem Projekt einen Titel, „Plant Monitor“, und klicke auf Speichern.
Schritt 2: Zeichne den Screen
Das Design ist ein winziger Pflanzenmonitor. Er nutzt nur Rechtecke, Kreise, Linien und Text, also kann jede Plattform in dieser Episode ihn zeichnen.
Zeichne ihn mit den Werkzeugen Rechteck, Ellipse, Linie und Text (Aa) und tippe die genauen Werte in den Inspektor:
- Der Topf: zwei gefüllte Rechtecke, der Rand bei X
8, Y42, W26, H5und der Topf darunter bei X11, Y47, W20, H15. Schalte Gefüllt für beide ein. - Der Stängel: eine Linie von
21, 24zu21, 41. - Die Blätter: drei Ellipsen mit eingeschaltetem Gefüllt und beiden Radien gleich: links bei X
9, Y26mit Radius5, rechts bei X23, Y22mit Radius5und oben bei X15, Y11mit Radius6. - Der Name: der Text
BASILbei X44, Y11und eine Linie darunter von44, 13zu123, 13. - Die Feuchtigkeitsleiste: der Text
MOISTUREbei X44, Y27, ein Rechteck bei X44, Y30, W80, H10und ein gefülltes Rechteck darin bei X46, Y32, W21, H6: der Wasserstand. - Die Erinnerung: der Text
WATER ME!bei X44, Y57.
MicroPython hat eine Schriftart, 8×8 Pixel, also haben die Text-Ebenen keine Schriftartliste.
Gib schließlich allem Namen. Doppelklicke im Ebenen-Panel auf Screen 1 und benenne ihn in Plant um, dann benenne die Ebenen rim, pot, stem, leaf_left, leaf_right, leaf_top, title, rule, caption, bar, level und hint. Der Screen-Name wird zum Namen der Funktion, und die Ebenennamen werden zu Kommentaren im Code.

Schritt 3: Dein Code
Ein Aufruf pro Ebene
Der Code ist einfaches Python. Jede Ebene wird zu einem Aufruf auf dem Display-Objekt: fill_rect, rect, line, ellipse und text. Am Ende sendet display.show() das fertige Bild an den Screen.
Der generierte Code von Lopaka sitzt zwischen den BEGIN- und END-Markierungen. Ein paar eigene Zeilen kommen darum: Vier über den Markierungen richten das Display ein, und eine darunter ruft die Funktion auf. Tippe sie direkt in das Code-Panel von Lopaka. Lopaka behält deine Zeilen außerhalb der Markierungen, und Kopieren nimmt die ganze Datei:
from machine import Pin, I2C
from ssd1306 import SSD1306_I2C
i2c = I2C(0, scl=Pin(5), sda=Pin(4))
display = SSD1306_I2C(128, 64, i2c)
# [BEGIN lopaka generated]
# SSD1306 OLED display library: https://github.com/PerfecXX/MicroPython-SSD1306/blob/main/ssd1306.py
def drawPlant(display):
# rim
display.fill_rect(8, 42, 26, 5, 1)
# pot
display.fill_rect(11, 47, 20, 15, 1)
# stem
display.line(21, 24, 21, 41, 1)
# leaf_left
display.ellipse(14, 31, 5, 5, 1, True)
# leaf_right
display.ellipse(28, 27, 5, 5, 1, True)
# leaf_top
display.ellipse(21, 17, 6, 6, 1, True)
# title
display.text("BASIL", 44, 2, 1)
# rule
display.line(44, 13, 123, 13, 1)
# caption
display.text("MOISTURE", 44, 18, 1)
# bar
display.rect(44, 30, 80, 10, 1)
# level
display.fill_rect(46, 32, 21, 6, 1)
# hint
display.text("WATER ME!", 44, 48, 1)
display.show()
# [END lopaka generated]
drawPlant(display)Deine vier Zeilen erstellen einen I2C-Bus an den Pins GP5 (SCL) und GP4 (SDA) des Pico und ein 128×64-Display-Objekt darauf. Das letzte Argument der meisten Aufrufe, 1, ist die Farbe: Pixel an.
Die Zahlen, die sich änderten
Zwei Arten von Zahlen in diesem Code unterscheiden sich vom Inspektor:
- Die Blätter. Im Inspektor hat das linke Blatt X
9, Y26und Radius5: die obere linke Ecke der Box um es.ellipse()nimmt den Mittelpunkt, also sagt der Codeellipse(14, 31, 5, 5, ...). DasTrueam Ende füllt es. - Der Text. Du platziertest
BASILbei Y11, und der Code sagttext("BASIL", 44, 2, ...). Dastext()von MicroPython nimmt die obere linke Ecke des Textes, also wandelt Lopaka die Position für dich um.
Schritt 4: Führe ihn im Simulator aus
Der Simulator führt echtes MicroPython auf einem simulierten Raspberry Pi Pico aus:
- Erstelle ein neues Raspberry-Pi-Pico-Projekt mit MicroPython.
- Füge ein SSD1306-OLED-Display hinzu und verbinde vier Drähte: GND mit GND, VCC mit 3V3, SCL mit GP5 und SDA mit GP4.
- Füge eine zweite Datei,
ssd1306.py, mit dem Display-Treiber aus dem Link im Kommentar des generierten Codes hinzu. Deine erste Zeile importiertSSD1306_I2Cdaraus. Ohne diese Datei stoppt das Programm beiimport. - Füge den kopierten Code in
main.pyein und drücke Play.
Die Pflanze erscheint auf dem Display. Es gibt keinen Kompilierschritt: Der Interpreter führt die Datei aus, wie sie ist.
Kopiere auf einem echten Pico beide Dateien, main.py und ssd1306.py, auf das Board. Das Board führt main.py jedes Mal aus, wenn es startet.
Schritt 5: Ein CircuitPython-Projekt
Jetzt derselbe Screen in CircuitPython. Ein Projekt behält die Plattform, die du gewählt hast, also braucht die neue Plattform ein neues Projekt: + Neues Projekt, Plattform CircuitPython, Größe 128×64.
Platziere dieselben Ebenen mit denselben Zahlen und gib Screen und Ebenen dieselben Namen. Zwei Dinge sehen im Inspektor anders aus:
- Ein Kreis hat einen Radius, R, statt zwei.
- Text nutzt Terminus, die in CircuitPython eingebaute Schriftart. Sein Feld Größe multipliziert die Schriftart:
1ist die normale Größe.

Schritt 6: Objekte in einer Gruppe
Der Code
Der Code sieht anders aus. Jede Form ist ein Objekt: ein Rect, ein Circle, eine Line oder ein Label für Text. Jedes Objekt wird zu einer Gruppe hinzugefügt, und das Display zeigt diese Gruppe:
import board, busio, displayio, terminalio
import adafruit_displayio_ssd1306
from adafruit_display_text import label
from adafruit_display_shapes.rect import Rect
from adafruit_display_shapes.circle import Circle
from adafruit_display_shapes.line import Line
displayio.release_displays()
i2c = busio.I2C(board.GP5, board.GP4)
bus = displayio.I2CDisplay(i2c, device_address=0x3C)
display = adafruit_displayio_ssd1306.SSD1306(bus, width=128, height=64)
# [BEGIN lopaka generated]
# Init display
Plant = displayio.Group()
display.root_group = Plant
def drawPlant():
# rim
rim = Rect(8, 42, 26, 5, fill=0xFFFFFF, outline=0xFFFFFF)
Plant.append(rim)
# pot
pot = Rect(11, 47, 20, 15, fill=0xFFFFFF, outline=0xFFFFFF)
Plant.append(pot)
# stem
stem = Line(21, 24, 21, 41, color=0xFFFFFF)
Plant.append(stem)
# leaf_left
leaf_left = Circle(14, 31, 5, fill=0xFFFFFF, outline=0xFFFFFF)
Plant.append(leaf_left)
# leaf_right
leaf_right = Circle(28, 27, 5, fill=0xFFFFFF, outline=0xFFFFFF)
Plant.append(leaf_right)
# leaf_top
leaf_top = Circle(21, 17, 6, fill=0xFFFFFF, outline=0xFFFFFF)
Plant.append(leaf_top)
# title
title = label.Label(terminalio.FONT, text="BASIL", color=0xFFFFFF)
title.x = 44
title.y = 6
Plant.append(title)
# rule
rule = Line(44, 13, 123, 13, color=0xFFFFFF)
Plant.append(rule)
# caption
caption = label.Label(terminalio.FONT, text="MOISTURE", color=0xFFFFFF)
caption.x = 44
caption.y = 22
Plant.append(caption)
# bar
bar = Rect(44, 30, 80, 10, outline=0xFFFFFF)
Plant.append(bar)
# level
level = Rect(46, 32, 21, 6, fill=0xFFFFFF, outline=0xFFFFFF)
Plant.append(level)
# hint
hint = label.Label(terminalio.FONT, text="WATER ME!", color=0xFFFFFF)
hint.x = 44
hint.y = 52
Plant.append(hint)
# [END lopaka generated]
drawPlant()
while True:
passDie elf Zeilen über den Markierungen sind deine: die Imports für Formen und Textbeschriftung und die Display-Einrichtung an denselben Pins, GP5 und GP4, bei der üblichen I2C-Adresse dieser Module, 0x3C. displayio.release_displays() gibt das Display frei, falls ein früherer Lauf es noch hält. Unter den Markierungen baut drawPlant() die Gruppe, und while True: pass hält das Programm am Laufen. Wenn ein CircuitPython-Programm endet, nimmt das Board das Display für seine eigene Textausgabe zurück, und die Pflanze würde verschwinden.
Das Display zeigt die Gruppe weiter
display.root_group = Plant sagt dem Display, welche Gruppe es zeigen soll. Von da an zeichnet das Display diese Gruppe von selbst neu. Dein Code sendet nie einen Frame: Wenn du ein Objekt bewegst oder den Text einer Beschriftung änderst, folgt der Screen.

Farben sind 24-Bit-Hex-Werte: 0xFFFFFF ist Weiß, ein leuchtendes Pixel auf diesem Display. Und title.y = 6 ist die Mitte der Textzeile: Ein Label wird an seiner linken Kante und seiner vertikalen Mitte platziert, also wandelt Lopaka das Y 11 aus dem Inspektor um.
Führe ihn im Simulator aus
Erstelle ein neues Raspberry-Pi-Pico-Projekt mit CircuitPython und verdrahte das Display wie in Schritt 4. Füge eine Datei requirements.txt mit den drei Adafruit-Display-Bibliotheken hinzu, die der Code importiert:
adafruit_displayio_ssd1306
adafruit_display_text
adafruit_display_shapesFüge den Code in code.py ein und drücke Play. Die Bibliotheken laden, wenn die Simulation startet, und dieselbe Pflanze erscheint. Kopiere auf einem echten Board diese drei Bibliotheken in den lib-Ordner des CIRCUITPY-Laufwerks.
Schritt 7: Ein ESPHome-Projekt
ESPHome ist für Smart-Home-Geräte gemacht: Du beschreibst das Gerät in einer YAML-Datei, und es erscheint in Home Assistant. Erstelle ein neues Projekt mit der Plattform ESPHome, Größe 128×64, und platziere dieselben Ebenen mit denselben Zahlen.
Der Text funktioniert hier anders. ESPHome nutzt TrueType-Schriftarten in jeder Größe, und die Liste Schriftart bietet Courier Prime, Fira Code, Merriweather, Montserrat, Roboto, Roboto Mono und Ubuntu. Wähle BASIL aus, nimm Roboto und setze Größe auf 12. Der nächste Text, den du hinzufügst, behält Roboto, startet aber bei Größe 14, also setze 12 auch bei MOISTURE und WATER ME!. Jede Schriftart und Größe, die du nutzt, wird zu einem separaten Eintrag im Code, und das Gerät speichert jede.
In ESPHome ist das Y eines Textes der untere Rand seiner Textbox, und Roboto 12 macht eine Box 14 Pixel hoch. Bei Y 11 würde der obere Rand von BASIL über den Screen stehen. Bewege die drei Texte ein wenig tiefer: Y 14, 30 und 59.

Schritt 8: Eine Schriftart und ein Lambda
Was Lopaka schreibt
In ESPHome wird das Design zu zwei Stücken YAML: einem font:-Abschnitt, der die Schriftart von Google Fonts zieht, und einem Lambda, wenigen Zeilen C++, die in der YAML-Datei leben:
# [BEGIN lopaka generated]
font:
- file: 'gfonts://Roboto'
id: Roboto
size: 12
display:
lambda: |-
// rim
it.filled_rectangle(8, 42, 26, 5);
// pot
it.filled_rectangle(11, 47, 20, 15);
// stem
it.line(21, 24, 21, 41);
// leaf_left
it.filled_circle(14, 31, 5);
// leaf_right
it.filled_circle(28, 27, 5);
// leaf_top
it.filled_circle(21, 17, 6);
// title
it.print(44, 0, id(Roboto), "BASIL");
// rule
it.line(44, 13, 123, 13);
// caption
it.print(44, 16, id(Roboto), "MOISTURE");
// bar
it.rectangle(44, 30, 80, 10);
// level
it.filled_rectangle(46, 32, 21, 6);
// hint
it.print(44, 45, id(Roboto), "WATER ME!");
# [END lopaka generated]it ist das Display. filled_circle nimmt Mittelpunkt und Radius, wie das ellipse von MicroPython. print(44, 0, ...) nimmt die obere linke Ecke der Textbox: Die Box von BASIL läuft von Zeile 0 bis Zeile 14, und der Inspektor zeigt ihren unteren Rand, 14.
Lege es in die Konfiguration deines Geräts
Der Rest der YAML-Datei beschreibt dein Gerät, und du schreibst ihn selbst: das Board, die I2C-Pins und das Display. Lege den font:-Abschnitt von Lopaka auf die oberste Ebene und sein lambda: unter dein Display, mit der Einrückung, wie sie ist:
esphome:
name: plant-monitor
esp32:
board: esp32dev
framework:
type: esp-idf
i2c:
sda: 21
scl: 22
font:
- file: 'gfonts://Roboto'
id: Roboto
size: 12
display:
- platform: ssd1306_i2c
model: 'SSD1306 128x64'
address: 0x3C
lambda: |-
// rim
it.filled_rectangle(8, 42, 26, 5);
// ...the rest of the lambda from LopakaKopiere das ganze Lambda, von // rim bis zum letzten it.print. Die einzige Zeile, die Lopaka dir unter display: nicht gibt, ist - platform: ssd1306_i2c mit seinem Modell und seiner Adresse: Dieser Teil sagt ESPHome, welches Display du hast.
Schritt 9: Baue die Firmware und führe sie aus
ESPHome ist kompiliert. Speichere die Datei als plant.yaml und führe esphome compile plant.yaml aus. ESPHome verwandelt die ganze Datei in C++, mit deinem Lambda Zeile für Zeile darin, und kompiliert Firmware für den ESP32.

Erstelle im Simulator ein ESP32-Projekt und verbinde das Display: SDA mit Pin 21, SCL mit Pin 22, GND mit GND und VCC mit 3V3. Lade die kompilierte firmware.factory.bin mit dem Befehl Firmware hochladen und Simulation starten des Simulators. Die Pflanze ist auf dem Screen.
Auf einem echten ESP32 baut esphome run plant.yaml dieselbe Firmware und flasht sie über USB. Weil das Zeichnen in einem Lambda lebt, können dieselben Zeilen später Live-Sensorwerte drucken, zum Beispiel die echte Feuchtigkeit statt einer festen Leiste.
Schritt 10: Ein Flipper-Zero-Projekt
Der Flipper Zero hat seinen eigenen Screen, auch 128×64 Pixel, schwarz auf orange. Erstelle ein neues Projekt mit der Plattform Flipper Zero, One. Diese Plattform hat keine Standardgröße: Öffne Screen-Größe und wähle Zero Horizontal (128×64).
Platziere dieselben Ebenen mit denselben Zahlen. Lopaka kennt die vier eingebauten Schriftarten des Flipper: FontPrimary, FontSecondary, FontKeyboard und FontBigNumbers. Neuer Text startet in FontPrimary, der Schriftart für alle drei Texte hier.

Schritt 11: C für die Zeichenfläche
Lopaka schreibt C für die Zeichenfläche des Flipper:
// [BEGIN lopaka generated]
void drawPlant(Canvas* canvas) {
canvas_set_bitmap_mode(canvas, true);
// rim
canvas_draw_box(canvas, 8, 42, 26, 5);
// pot
canvas_draw_box(canvas, 11, 47, 20, 15);
// stem
canvas_draw_line(canvas, 21, 24, 21, 41);
// leaf_left
canvas_draw_disc(canvas, 14, 31, 5);
// leaf_right
canvas_draw_disc(canvas, 28, 27, 5);
// leaf_top
canvas_draw_disc(canvas, 21, 17, 6);
// title
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 44, 11, "BASIL");
// rule
canvas_draw_line(canvas, 44, 13, 123, 13);
// caption
canvas_draw_str(canvas, 44, 27, "MOISTURE");
// bar
canvas_draw_frame(canvas, 44, 30, 80, 10);
// level
canvas_draw_box(canvas, 46, 32, 21, 6);
// hint
canvas_draw_str(canvas, 44, 57, "WATER ME!");
}
// [END lopaka generated]canvas_draw_frame zeichnet einen Umriss, canvas_draw_box ein gefülltes Rechteck und canvas_draw_disc einen gefüllten Kreis um seinen Mittelpunkt. Das y von canvas_draw_str ist die Grundlinie, dieselbe Zahl wie im Inspektor, also behalten die Texte 11, 27 und 57. Alle drei Texte nutzen FontPrimary, also erscheint canvas_set_font einmal.
Diese Funktion kommt in den Zeichen-Callback einer Flipper-App: die Funktion, die der Flipper aufruft, wenn er den Screen deiner App neu zeichnet. Du baust die App mit den eigenen Werkzeugen des Flipper.

Fünf Plattformen Seite an Seite
Seite an Seite: ein Arduino Uno, ein ESP32 mit ESPHome, ein Pico mit MicroPython, ein Pico mit CircuitPython und die Flipper-Zero-Version, bereit für ihre App. Der Arduino Uno führt dasselbe Design in einem U8g2-Projekt aus, eingerichtet wie in Episode 1, mit der Schriftart 6x13B.
Was sich zwischen Plattformen ändert
Gleiche Zahlen, andere Worte
Schau dir die Feuchtigkeitsleiste auf allen fünf Plattformen an. Vierundvierzig, dreißig, achtzig, zehn: Die Zahlen sind überall dieselben. Nur die Worte um sie ändern sich: der Name des Aufrufs, die 1 für die Farbe in MicroPython, das outline= in CircuitPython, das canvas vorne beim Flipper.

Jede Plattform hat ihre eigenen Schriftarten
Die Schriftarten ändern sich auch, weil jede Plattform ihren eigenen Satz hat: Petme 8×8 in MicroPython, Terminus in CircuitPython, Roboto 12 in ESPHome, FontPrimary auf dem Flipper Zero und 6x13B in U8g2. Deshalb brauchten die Texte in ESPHome ein anderes Y. Lopaka zeichnet jedes Projekt mit den eigenen Schriftarten der Plattform, also zeigt die Zeichenfläche den Text, den du auf dem Gerät bekommst.

Zusammenfassung
- Kompiliert oder interpretiert, die Pixel bleiben dieselben: Arduino-, ESPHome- und Flipper-Apps sind in Firmware kompiliert; MicroPython und CircuitPython führen eine Textdatei durch einen Interpreter aus.
- Wähle die Plattform, wenn du das Projekt erstellst: Die Plattform entscheidet die Sprache des Codes, und jede Plattform braucht ihr eigenes Projekt.
- Gleiche Ebenen, neue Sprache: Platziere dieselben Ebenen mit denselben Zahlen, und Lopaka schreibt den Code in der Sprache dieser Plattform.
Nächste Episode: E-Paper & Versand. Screens, die ihr Bild ohne Strom behalten. Lies Episode 10 oder schaue sie auf YouTube.
Bereit, es zu versuchen? Öffne Lopaka und bringe deinen Screen auf eine neue Plattform.