Lopaka Basics 09: Poza Arduino

C++. YAML. Python. Więcej Pythona. I C dla Flipper Zero. Pięć różnych programów rysuje ten sam ekran, a Lopaka napisała kod rysowania dla każdego z nich.
W tym odcinku dowiesz się, dlaczego płytki uruchamiają kod w tak różnych językach i co odróżnia kompilowany firmware od interpretera Pythona. Następnie narysujesz mały monitor roślin w Lopaka dla MicroPython, zbudujesz ten sam ekran dla CircuitPython, ESPHome i Flipper Zero oraz uruchomisz trzy z nich na symulowanych płytkach.
Co zbudujesz: ekran monitora roślin 128×64: doniczka z trzema liśćmi, nazwa rośliny, pasek wilgotności i przypomnienie o podlaniu, na czterech platformach. Czego potrzebujesz: przeglądarki i symulatora online z odcinka 1. Płytki są opcjonalne: Raspberry Pi Pico lub ESP32 z OLED SSD1306 128×64 albo Flipper Zero.
Dwa sposoby uruchamiania kodu
Kompilowany: cały firmware, za każdym razem
Kod Arduino jest kompilowany. Kompilator zamienia twój szkic w firmware, surowe instrukcje maszynowe dla chipa, a ty wgrywasz cały firmware na płytkę. Zmień jedną linię, a kompilujesz i wgrywasz wszystko od nowa.
ESPHome działa w ten sam sposób: twój plik YAML staje się C++, a C++ staje się firmware. Aplikacja Flipper Zero też jest kompilowana.
Interpretowany: plik tekstowy na płytce
MicroPython i CircuitPython odwracają to. Wgrywasz interpreter na płytkę raz. Potem twój program jest zwykłym plikiem tekstowym na płytce, takim jak main.py, a interpreter czyta go linia po linii. Zmień linię, zapisz plik, a działa od nowa, bez kompilatora pomiędzy.
Ceną jest prędkość i pamięć: sam interpreter działa na chipie i zajmuje swoją część. Na małych płytkach utrzymuj rysowanie proste.

Krok 1: wybierz platformę
W Lopaka platforma jest pierwszą rzeczą, którą wybierasz w nowym projekcie. Kliknij + Nowy projekt i otwórz listę Platforma. Obok nazwy każdej platformy znajduje się plakietka z językiem jej kodu: C++, YAML, Python lub C.

Zacznij od Micropython. Pozostaw rozmiar ekranu 128×64, nadaj projektowi tytuł „Plant Monitor” i kliknij Zapisz.
Krok 2: narysuj ekran
Projekt to malutki monitor roślin. Używa tylko prostokątów, kół, linii i tekstu, więc każda platforma w tym odcinku może go narysować.
Narysuj go narzędziami Prostokąt, elipsa, linia i tekst (Aa) oraz wpisz dokładne wartości do inspektora:
- Doniczka: dwa wypełnione prostokąty, rant na X
8, Y42, W26, H5oraz doniczka pod nim na X11, Y47, W20, H15. Włącz Wypełniony dla obu. - Łodyga: linia od
21, 24do21, 41. - Liście: trzy elipsy z włączoną opcją Wypełniony i równymi promieniami: lewy na X
9, Y26o promieniu5, prawy na X23, Y22o promieniu5oraz górny na X15, Y11o promieniu6. - Nazwa: tekst
BASILna X44, Y11oraz linia pod nim od44, 13do123, 13. - Pasek wilgotności: tekst
MOISTUREna X44, Y27, prostokąt na X44, Y30, W80, H10oraz wypełniony prostokąt wewnątrz niego na X46, Y32, W21, H6: poziom wody. - Przypomnienie: tekst
WATER ME!na X44, Y57.
MicroPython ma jedną czcionkę, 8×8 pikseli, więc warstwy tekstowe nie mają listy czcionek.
Na koniec nadaj wszystkiemu nazwy. Kliknij dwukrotnie Screen 1 w panelu warstw i zmień jej nazwę na Plant, a następnie nazwij warstwy rim, pot, stem, leaf_left, leaf_right, leaf_top, title, rule, caption, bar, level i hint. Nazwa ekranu staje się nazwą funkcji, a nazwy warstw stają się komentarzami w kodzie.

Krok 3: twój kod
Jedno wywołanie na warstwę
Kod to zwykły Python. Każda warstwa staje się jednym wywołaniem na obiekcie wyświetlacza: fill_rect, rect, line, ellipse i text. Na końcu display.show() wysyła gotowy obraz na ekran.
Wygenerowany kod Lopaka znajduje się między znacznikami BEGIN i END. Kilka linii twojego kodu znajduje się wokół niego: cztery nad znacznikami konfigurują wyświetlacz, a jedna pod nimi wywołuje funkcję. Wpisz je wprost do panelu kodu Lopaka. Lopaka przechowuje twoje linie poza znacznikami, a opcja Kopiuj pobiera cały plik:
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)Twoje cztery linie tworzą magistralę I2C na pinach Pico GP5 (SCL) i GP4 (SDA) oraz obiekt wyświetlacza 128×64 na niej. Ostatni argument większości wywołań, 1, to kolor: piksel włączony.
Liczby, które się zmieniły
Dwa rodzaje liczb w tym kodzie różnią się od inspektora:
- Liście. W inspektorze lewy liść ma X
9, Y26i promień5: lewy górny róg ramki wokół niego. Funkcjaellipse()przyjmuje środek, więc kod mówiellipse(14, 31, 5, 5, ...).Truena końcu go wypełnia. - Tekst. Umieściłeś
BASILna Y11, a kod mówitext("BASIL", 44, 2, ...). Funkcjatext()w MicroPython przyjmuje lewy górny róg tekstu, więc Lopaka konwertuje pozycję za ciebie.
Krok 4: uruchom to w symulatorze
Symulator uruchamia prawdziwy MicroPython na symulowanym Raspberry Pi Pico:
- Utwórz nowy projekt Raspberry Pi Pico z MicroPython.
- Dodaj wyświetlacz SSD1306 OLED i podłącz cztery przewody: GND do GND, VCC do 3V3, SCL do GP5 i SDA do GP4.
- Dodaj drugi plik,
ssd1306.py, ze sterownikiem wyświetlacza z linku w komentarzu wygenerowanego kodu. Twoja pierwsza linia importuje z niegoSSD1306_I2C. Bez tego pliku program zatrzymuje się naimport. - Wklej skopiowany kod do
main.pyi naciśnij Play.
Roślina pojawia się na wyświetlaczu. Nie ma kroku kompilacji: interpreter uruchamia plik takim, jaki jest.
Na prawdziwym Pico skopiuj oba pliki, main.py i ssd1306.py, na płytkę. Płytka uruchamia main.py przy każdym starcie.
Krok 5: projekt CircuitPython
Teraz ten sam ekran w CircuitPython. Projekt zachowuje wybraną platformę, więc nowa platforma potrzebuje nowego projektu: + Nowy projekt, platforma CircuitPython, rozmiar 128×64.
Umieść te same warstwy z tymi samymi liczbami i nadaj ekranowi oraz warstwom te same nazwy. Dwie rzeczy wyglądają inaczej w inspektorze:
- Koło ma jeden promień, R, zamiast dwóch.
- Tekst używa Terminus, czcionki wbudowanej w CircuitPython. Jego pole Rozmiar mnoży czcionkę:
1to normalny rozmiar.

Krok 6: obiekty w grupie
Kod
Kod wygląda inaczej. Każdy kształt jest obiektem: Rect, Circle, Line lub Label dla tekstu. Każdy obiekt jest dodawany do grupy, a wyświetlacz pokazuje tę grupę:
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:
passJedenaście linii nad znacznikami jest twoich: importy kształtów i etykiety tekstowej oraz konfiguracja wyświetlacza na tych samych pinach, GP5 i GP4, pod zwykłym adresem I2C tych modułów, 0x3C. displayio.release_displays() zwalnia wyświetlacz na wypadek, gdyby wcześniejsze uruchomienie nadal go trzymało. Pod znacznikami drawPlant() buduje grupę, a while True: pass utrzymuje program w działaniu. Gdy program CircuitPython się zakończy, płytka przejmuje wyświetlacz z powrotem na własny tekst wyjściowy, a roślina zniknęłaby.
Wyświetlacz nadal pokazuje grupę
display.root_group = Plant mówi wyświetlaczowi, którą grupę pokazać. Od tego momentu wyświetlacz przerysowuje tę grupę samodzielnie. Twój kod nigdy nie wysyła ramki: jeśli przesuniesz obiekt lub zmienisz tekst etykiety, ekran podąży za tym.

Kolory to 24-bitowe wartości szesnastkowe: 0xFFFFFF to biały, zapalony piksel na tym wyświetlaczu. A title.y = 6 to środek linii tekstu: Label jest umieszczany według lewej krawędzi i pionowego środka, więc Lopaka konwertuje Y 11 z inspektora.
Uruchom to w symulatorze
Utwórz nowy projekt Raspberry Pi Pico z CircuitPython i podłącz wyświetlacz jak w kroku 4. Dodaj plik requirements.txt z trzema bibliotekami wyświetlaczy Adafruit, które importuje kod:
adafruit_displayio_ssd1306
adafruit_display_text
adafruit_display_shapesWklej kod do code.py i naciśnij Play. Biblioteki ładują się przy starcie symulacji i pojawia się ta sama roślina. Na prawdziwej płytce skopiuj te trzy biblioteki do folderu lib dysku CIRCUITPY.
Krok 7: projekt ESPHome
ESPHome jest stworzony dla urządzeń inteligentnego domu: opisujesz urządzenie w pliku YAML, a pojawia się ono w Home Assistant. Utwórz nowy projekt na platformie ESPHome, o rozmiarze 128×64, i umieść te same warstwy z tymi samymi liczbami.
Tekst działa tutaj inaczej. ESPHome używa czcionek TrueType w dowolnym rozmiarze, a lista Czcionka oferuje Courier Prime, Fira Code, Merriweather, Montserrat, Roboto, Roboto Mono i Ubuntu. Zaznacz BASIL, wybierz Roboto i ustaw Rozmiar na 12. Następny dodany tekst zachowuje Roboto, ale zaczyna się od rozmiaru 14, więc ustaw 12 także na MOISTURE i WATER ME!. Każda użyta czcionka i rozmiar staje się osobnym wpisem w kodzie, a urządzenie przechowuje każdy z nich.
W ESPHome Y tekstu to dół jego ramki tekstowej, a Roboto 12 tworzy ramkę o wysokości 14 pikseli. Przy Y 11 góra BASIL wystawałaby nad ekran. Przesuń trzy teksty nieco niżej: Y 14, 30 i 59.

Krok 8: czcionka i lambda
Co pisze Lopaka
W ESPHome projekt staje się dwoma fragmentami YAML: sekcją font:, która pobiera czcionkę z Google Fonts, oraz lambdą, kilkoma liniami C++, które żyją wewnątrz pliku YAML:
# [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 to wyświetlacz. filled_circle przyjmuje środek i promień, jak ellipse w MicroPython. print(44, 0, ...) przyjmuje lewy górny róg ramki tekstowej: ramka BASIL biegnie od wiersza 0 do wiersza 14, a inspektor pokazuje jej dół, 14.
Umieść to w konfiguracji urządzenia
Reszta pliku YAML opisuje twoje urządzenie i piszesz ją samodzielnie: płytkę, piny I2C i wyświetlacz. Umieść sekcję font: Lopaka na najwyższym poziomie, a jej lambda: pod swoim wyświetlaczem, z wcięciem takim, jakie jest:
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 LopakaSkopiuj całą lambdę, od // rim do ostatniego it.print. Jedyna linia, której Lopaka nie daje ci pod display:, to - platform: ssd1306_i2c z jej modelem i adresem: ta część mówi ESPHome, jaki masz wyświetlacz.
Krok 9: zbuduj firmware i uruchom go
ESPHome jest kompilowane. Zapisz plik jako plant.yaml i uruchom esphome compile plant.yaml. ESPHome zamienia cały plik w C++, z twoją lambdą w środku linia w linię, i kompiluje firmware dla ESP32.

W symulatorze utwórz projekt ESP32 i podłącz wyświetlacz: SDA do pinu 21, SCL do pinu 22, GND do GND i VCC do 3V3. Załaduj skompilowany firmware.factory.bin poleceniem symulatora Upload Firmware and Start Simulation. Roślina jest na ekranie.
Na prawdziwym ESP32 esphome run plant.yaml buduje ten sam firmware i wgrywa go przez USB. Ponieważ rysowanie żyje w lambdzie, te same linie mogą później wypisywać wartości czujników na żywo, na przykład prawdziwą wilgotność zamiast stałego paska.
Krok 10: projekt Flipper Zero
Flipper Zero ma własny ekran, również 128×64 piksele, czarny na pomarańczowym. Utwórz nowy projekt na platformie Flipper Zero, One. Ta platforma nie ma domyślnego rozmiaru: otwórz Rozmiar ekranu i wybierz Zero Horizontal (128×64).
Umieść te same warstwy z tymi samymi liczbami. Lopaka zna cztery wbudowane czcionki Flippera: FontPrimary, FontSecondary, FontKeyboard i FontBigNumbers. Nowy tekst zaczyna się od FontPrimary, która jest czcionką dla wszystkich trzech tekstów tutaj.

Krok 11: C dla obszaru rysowania
Lopaka pisze C dla obszaru rysowania Flippera:
// [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 rysuje kontur, canvas_draw_box wypełniony prostokąt, a canvas_draw_disc wypełnione koło wokół jego środka. Współrzędna y w canvas_draw_str to linia bazowa, ta sama liczba co w inspektorze, więc teksty zachowują 11, 27 i 57. Wszystkie trzy teksty używają FontPrimary, więc canvas_set_font występuje raz.
Ta funkcja trafia do wywołania zwrotnego rysowania aplikacji Flipper: funkcji, którą Flipper wywołuje za każdym razem, gdy przerysowuje ekran twojej aplikacji. Budujesz aplikację własnymi narzędziami Flippera.

Pięć platform obok siebie
Obok siebie: Arduino Uno, ESP32 z ESPHome, Pico z MicroPython, Pico z CircuitPython oraz wersja Flipper Zero, gotowa dla swojej aplikacji. Arduino Uno uruchamia ten sam projekt w projekcie U8g2, skonfigurowanym jak w odcinku 1, czcionką 6x13B.
Co zmienia się między platformami
Te same liczby, inne słowa
Spójrz na pasek wilgotności na wszystkich pięciu platformach. Czterdzieści cztery, trzydzieści, osiemdziesiąt, dziesięć: liczby są wszędzie takie same. Zmieniają się tylko słowa wokół nich: nazwa wywołania, 1 dla koloru w MicroPython, outline= w CircuitPython, canvas z przodu na Flipperze.

Każda platforma ma własne czcionki
Czcionki też się zmieniają, ponieważ każda platforma ma własny zestaw: Petme 8×8 w MicroPython, Terminus w CircuitPython, Roboto 12 w ESPHome, FontPrimary na Flipper Zero i 6x13B w U8g2. Dlatego teksty potrzebowały innego Y w ESPHome. Lopaka rysuje każdy projekt czcionkami własnej platformy, więc obszar rysowania pokazuje tekst, który otrzymasz na urządzeniu.

Podsumowanie
- Kompilowane czy interpretowane, piksele pozostają te same: Arduino, ESPHome i aplikacje Flipper są kompilowane do firmware; MicroPython i CircuitPython uruchamiają plik tekstowy przez interpreter.
- Wybierz platformę przy tworzeniu projektu: platforma decyduje o języku kodu, a każda platforma potrzebuje własnego projektu.
- Te same warstwy, nowy język: umieść te same warstwy z tymi samymi liczbami, a Lopaka napisze kod w języku tej platformy.
Następny odcinek: Papier elektroniczny i wysyłka. Ekrany, które zachowują obraz bez zasilania. Przeczytaj odcinek 10 lub obejrzyj go na YouTube.
Gotowy, aby spróbować? Otwórz Lopaka i przenieś swój ekran na nową platformę.