Edycja 2026. Kompleksowy przewodnik po MicroPythonie dla mikrokontrolerów. Dowiedz się, jak uruchomić pełny interpreter Python 3 na sprzęcie typu bare-metal.
Odkryj, jak MicroPython wciska pełny interpreter Python 3 do mikrokontrolerów typu bare-metal. Badamy jego podstawowe założenia, różnice w stosunku do CPython oraz to, jak radzi sobie z działaniem w wysoce ograniczonych środowiskach.
3m 49s
2
Sprzętowy most: Moduł machine
Dowiedz się, jak sterować urządzeniami peryferyjnymi mikrokontrolera bezpośrednio z Pythona. Zagłębiamy się w moduł machine, badając, jak wchodzić w interakcje z obiektami Pin, PWM i surową pamięcią.
4m 03s
3
Programowanie na żywo MCU: REPL i mpremote
Zrewolucjonizuj swój przepływ pracy w programowaniu systemów wbudowanych. Omawiamy REPL w MicroPythonie oraz narzędzie wiersza poleceń mpremote do automatyzacji połączeń szeregowych i wykonywania kodu na żywo.
4m 06s
4
Trzy linijki do WiFi: Moduł network
Przekształć mikrokontroler w połączony węzeł IoT. Odkrywamy moduł network, szczegółowo opisując, jak połączyć się z WiFi jako stacja lub hostować własny Access Point.
3m 38s
5
Przetrwać ograniczenia: RAM i GC
Opanuj sztukę pisania wydajnego pamięciowo kodu w Pythonie. Omawiamy fragmentację sterty, wstępną alokację buforów i ręczne garbage collection, aby twój mikrokontroler działał płynnie.
3m 59s
6
Skompilowane czy zamrożone: Wdrażanie na produkcję
Dowiedz się, jak wdrażać ogromne aplikacje bez wyczerpywania pamięci RAM. Badamy wstępnie skompilowane pliki .mpy i zamrażanie kodu bajtowego bezpośrednio w pamięci flash mikrokontrolera.
4m 06s
7
Determinizm w Pythonie: Timery i przerwania
Osiągnij zachowanie w czasie rzeczywistym w MicroPythonie, używając sprzętowych timerów i procedur obsługi przerwań. Omawiamy surowe zasady pisania ISR i unikania alokacji pamięci.
4m 06s
Odcinki
1
Python, który mieści się w 256K
3m 49s
Odkryj, jak MicroPython wciska pełny interpreter Python 3 do mikrokontrolerów typu bare-metal. Badamy jego podstawowe założenia, różnice w stosunku do CPython oraz to, jak radzi sobie z działaniem w wysoce ograniczonych środowiskach.
Cześć, tu Alex z DEV STORIES DOT EU. MicroPython, odcinek 1 z 7. Desktopowy Python zazwyczaj zużywa megabajty pamięci na samo uruchomienie. Spróbuj wrzucić to na mały, dwudolarowy chip, a systemowi natychmiast zabraknie zasobów. Żeby pisać w Pythonie na mikrokontrolery, potrzebujesz interpretera, który zmieści się w ułamku tej przestrzeni bez utraty kluczowych funkcji języka, na których polegasz. Właśnie to oferuje MicroPython.
MicroPython to odchudzona, wysoce wydajna implementacja języka Python 3. Został zaprojektowany specjalnie do działania w środowiskach o ograniczonych zasobach. Standardowy Python, często nazywany CPythonem, zakłada, że działa na pełnoprawnym systemie operacyjnym, takim jak Linux czy Windows, z gigabajtami miejsca na dysku i dużą ilością RAM-u. MicroPython odwraca to założenie. Został stworzony, by działać na bare metal. Nie ma pod spodem żadnego systemu operacyjnego zarządzającego sprzętem. Zamiast tego, to MicroPython pełni rolę systemu operacyjnego. Komunikuje się bezpośrednio ze sprzętem, dzięki czemu możesz pisać wysokopoziomowe skrypty do sterowania fizycznymi urządzeniami.
Inżynieria stojąca za tym projektem skupia się całkowicie na drastycznych ograniczeniach zasobów. MicroPython potrafi działać w całości w zaledwie 256 kilobajtach przestrzeni na skompilowany kod. Co jeszcze bardziej imponujące, do działania wymaga zaledwie 16 kilobajtów RAM-u. W tych ciasnych ramach dostarcza w pełni funkcjonalne środowisko Pythona. Standardowy Python zawiera potężną bibliotekę standardową, co jest znane jako podejście batteries included. MicroPython okrawa to do starannie dobranego podzbioru modułów, zostawiając tylko to, co ma sens w systemach embedded.
Mimo tych mocnych optymalizacji, sam język pozostaje autentycznym Pythonem 3. Oznacza to, że kod, który piszesz na swoim desktopie, przenosi się bezpośrednio na urządzenie embedded. MicroPython wspiera zaawansowane funkcje języka, takie jak closures, list comprehensions, generatory i standardową obsługę wyjątków. Radzi sobie nawet z liczbami całkowitymi o dowolnej precyzji. Jeśli twoja logika wymaga mnożenia ogromnych liczb, MicroPython dynamicznie skaluje pamięć, żeby dać ci poprawny wynik bez uderzania w sztywny limit architektury, dokładnie tak jak desktopowy Python.
A oto kluczowa sprawa. Ponieważ cały silnik Pythona mieści się na chipie, dostajesz interaktywny prompt na żywo bezpośrednio na sprzęcie. Ten read-eval-print loop zmienia sposób, w jaki piszesz kod embedded. Zazwyczaj programowanie mikrokontrolera polega na napisaniu kodu w C, skompilowaniu go na desktopie, flashowaniu go na płytkę i czekaniu, aż się uruchomi. Z MicroPythonem po prostu podłączasz płytkę do komputera, otwierasz terminal szeregowy i wpisujesz komendy Pythona bezpośrednio do chipa. Możesz testować logikę, odczytywać wartości z sensorów lub natychmiast wyzwalać akcje.
Żeby to było możliwe, MicroPython zawiera specyficzne wbudowane moduły, które wykraczają poza standardowego Pythona. Ponieważ standardowy Python nie wie nic o odczytywaniu fizycznych pinów czy konfigurowaniu sprzętowych timerów, MicroPython dostarcza wyspecjalizowane, mocno zoptymalizowane interfejsy do tych zadań. Są one wbudowane bezpośrednio w firmware twojego konkretnego chipa, dając ci bezpośrednią kontrolę nad fizycznym światem bez niepotrzebnego zwiększania memory footprintu.
Pozbywając się systemu operacyjnego i kurcząc interpreter tak, by zmieścił się w 256 kilobajtach, MicroPython zamienia tani, ubogi w zasoby sprzęt w dynamiczne, interaktywne środowisko programistyczne. Jeśli chcesz wesprzeć nasz podcast, znajdziesz nas, wyszukując DevStoriesEU na Patreon. Dzięki za odsłuch. Do usłyszenia następnym razem!
2
Sprzętowy most: Moduł machine
4m 03s
Dowiedz się, jak sterować urządzeniami peryferyjnymi mikrokontrolera bezpośrednio z Pythona. Zagłębiamy się w moduł machine, badając, jak wchodzić w interakcje z obiektami Pin, PWM i surową pamięcią.
Cześć, tu Alex z DEV STORIES DOT EU. MicroPython, odcinek 2 z 7. Przestań rekompilować kod w C i flashować firmware tylko po to, żeby przetestować czujnik albo zmienić stan przełącznika. Chcesz mieć bezpośrednią kontrolę nad mikrokontrolerem bez zbędnego boilerplate'u, a moduł machine zapewnia dokładnie to.
Moduł machine to twój sprzętowy most. Tłumaczy kod w Pythonie na sygnały elektryczne, które sterują fizycznymi pinami na twojej płytce. Zapewnia dostęp do kluczowych funkcji sprzętowych, takich jak częstotliwość procesora, tryby uśpienia oraz cyfrowe wejścia i wyjścia. Ale ponieważ działa tak blisko sprzętu, wiąże się z nim pewne ostrzeżenie. Ten moduł nie prowadzi cię za rękę. Jeśli skonfigurujesz zły rejestr sprzętowy albo zapiszesz coś pod chroniony adres pamięci, twoja płytka zaliczy crasha i zrebootuje się.
Fundamentem tego modułu jest klasa Pin. Pin to fizyczne wyprowadzenie na mikrokontrolerze, które może wysyłać lub odbierać napięcie elektryczne. Żeby sterować podstawowym komponentem, takim jak dioda LED, musisz podać napięcie na konkretny pin. Robisz to, importując moduł machine i tworząc obiekt Pin. Podajesz fizyczny numer pinu i definiujesz, czy ma on działać jako input, czy output. W przypadku diody LED konfigurujesz go jako output, przekazując odpowiednią stałą output. Kiedy obiekt jest już utworzony, używasz go do ustawienia stanu. Przekazanie wartości jeden ustawia linię sprzętową w stan wysoki i włącza diodę LED. Przekazanie wartości zero ustawia ją w stan niski i wyłącza diodę. Manipulujesz cyfrowym stanem sprzętu w czasie rzeczywistym prosto z prompta w Pythonie.
Czasami podstawowe stany on i off to za mało. Jeśli chcesz przyciemnić tę diodę albo sterować prędkością silnika, potrzebujesz Pulse Width Modulation, czyli PWM. Moduł machine ma do tego dedykowaną klasę PWM. Zamiast stałego napięcia, PWM włącza i wyłącza pin niezwykle szybko. Tworzysz obiekt PWM, przekazując mu obiekt Pin, który już skonfigurowałeś. Następnie ustawiasz dwa parametry sprzętowe: frequency i duty cycle.
Oto kluczowa sprawa. Frequency określa, ile razy na sekundę sygnał powtarza swój cykl, podczas gdy duty cycle określa, przez jaki procent tego czasu sygnał jest faktycznie włączony. Jeśli ustawisz duty cycle na pięćdziesiąt procent, dioda LED otrzymuje zasilanie tylko przez połowę czasu, przez co dla ludzkiego oka wydaje się o połowę słabsza. Stopniowo zmieniając wartość duty cycle wewnątrz pętli, instruujesz sprzęt, żeby stworzył płynny efekt przygasania.
W zaawansowanych przypadkach użycia, moduł machine oferuje bezpośredni dostęp do niskopoziomowych rejestrów sprzętowych poprzez obiekty o nazwach mem8, mem16 i mem32. Pozwalają one na odczyt i zapis surowych bajtów, half-wordów lub 32-bitowych wordów bezpośrednio pod konkretne adresy pamięci. Nie używasz do tego obiektu Pin. Używasz standardowej notacji nawiasowej, przekazując fizyczny adres pamięci, do którego chcesz uzyskać dostęp, dokładnie tak samo, jak przy szukaniu klucza w słowniku. Przypisanie wartości do adresu mem32 natychmiast nadpisuje ten rejestr sprzętowy. W ten sposób wchodzisz w interakcję z mało znanymi funkcjami mikrokontrolera, których MicroPython nie opakował jeszcze jawnie w żadną klasę. Daje ci to absolutną władzę nad krzemem.
Prawdziwa moc modułu machine polega nie tylko na tym, że zapewnia dostęp do sprzętu, ale też na tym, że całkowicie usuwa barierę między pisaniem wysokopoziomowej logiki w kodzie a manipulowaniem fizycznym napięciem.
Dzięki za wysłuchanie. Trzymajcie się wszyscy.
3
Programowanie na żywo MCU: REPL i mpremote
4m 06s
Zrewolucjonizuj swój przepływ pracy w programowaniu systemów wbudowanych. Omawiamy REPL w MicroPythonie oraz narzędzie wiersza poleceń mpremote do automatyzacji połączeń szeregowych i wykonywania kodu na żywo.
Cześć, tu Alex z DEV STORIES DOT EU. MicroPython, odcinek 3 z 7. Zapomnij o żmudnym cyklu kompilacji, flashowania i rebootu. W typowym projekcie embedded zmieniasz jedną zmienną, czekasz minuty na build, zapisujesz do pamięci flash i masz nadzieję, że zadziała. MicroPython całkowicie to omija, pozwalając ci rozmawiać bezpośrednio z chipem w czasie rzeczywistym. W tym odcinku omówimy Live Coding na MCU przy użyciu REPL i mpremote.
REPL to skrót od Read-Eval-Print Loop. To interaktywny prompt działający bezpośrednio na twoim mikrokontrolerze. Podłączasz płytkę po USB, otwierasz terminal szeregowy, wpisujesz kod w Pythonie, a chip wykonuje go natychmiast. Standardowy REPL jest zaprojektowany dla ludzi. Zapewnia auto-indentację, historię komend i podstawowe auto-uzupełnianie. Idealnie nadaje się do szybkiego przetestowania przełączania pinu sprzętowego.
Istnieje jednak drugi tryb, tak zwany raw REPL. Ludzie często mylą go z ograniczoną wersją MicroPythona, ale w rzeczywistości to po prostu inny tryb wejścia, wyzwalany przez wysłanie konkretnego znaku sterującego po serialu. Raw REPL wyłącza echo znaków i auto-indentację. Narzędzia używają raw REPL do programowego wstrzykiwania dużych bloków kodu do mikrokontrolera. Gdyby skrypt spróbował wkleić tekst do standardowego REPL, przyjazna dla człowieka auto-indentacja wywołałaby efekt kaskady i całkowicie zepsuła formatowanie Pythona. Raw REPL gwarantuje, że kod wejdzie dokładnie tak, jak został napisany.
I tu dochodzimy do mpremote. To oficjalne narzędzie command-line do interakcji z urządzeniami z MicroPythonem, które pod spodem steruje raw REPL, odwalając całą czarną robotę. Do Live Codingu mpremote oferuje dwie potężne komendy: run i mount.
Komendy run używasz, żeby odpalić lokalny skrypt z komputera bezpośrednio na płytce. Piszesz kod w swoim ulubionym edytorze tekstu, otwierasz terminal i wywołujesz mpremote run, a po nim nazwę pliku. I tu jest kluczowa sprawa. Kod jest wysyłany po serialu i wykonywany w całości w pamięci RAM mikrokontrolera. Nigdy nie jest zapisywany do pamięci flash urządzenia. Jeśli napiszesz złą pętlę i zcrashujesz płytkę, po prostu robisz hard reset chipa. Pamięć flash pozostaje nietknięta, co oszczędza cykle zapisu, a twój development loop zajmuje dosłownie sekundy.
Kiedy twój projekt rozrasta się poza pojedynczy skrypt, używasz komendy mount. Ta komenda mówi mpremote, żeby wziął lokalny katalog na twoim PC i zmapował go po połączeniu serialowym. Mikrokontroler tymczasowo nadpisuje swój własny storage i traktuje ten folder na twoim komputerze tak, jakby to był jego wewnętrzny system plików.
Wyobraź sobie scenariusz, w którym piszesz skomplikowany sterownik wyświetlacza. Zazwyczaj musiałbyś skopiować plik sterownika na płytkę, odpalić swój główny skrypt, zauważyć glitch na ekranie, zedytować plik na PC i skopiować go ponownie. Dzięki komendzie mount, mikrokontroler wykonuje sterownik bezpośrednio ze storage'u twojego PC. Wciskasz save w edytorze tekstu na laptopie, restartujesz skrypt na płytce, a zmiany aplikują się natychmiast. Iterujesz tak szybko, jak potrafisz pisać, całkowicie eliminując krok flashowania ze swojego workflow.
Prawdziwa moc MicroPythona to nie tylko sama składnia Pythona, ale to, jak narzędzia takie jak mpremote zacierają granicę między twoim lokalnym środowiskiem deweloperskim a fizycznym sprzętem embedded. To wszystko w tym odcinku. Dzięki za wysłuchanie i koduj dalej!
4
Trzy linijki do WiFi: Moduł network
3m 38s
Przekształć mikrokontroler w połączony węzeł IoT. Odkrywamy moduł network, szczegółowo opisując, jak połączyć się z WiFi jako stacja lub hostować własny Access Point.
Cześć, tu Alex z DEV STORIES DOT EU. MicroPython, odcinek 4 z 7. Podłączenie gołego mikrokontrolera do sieci Wi-Fi za pomocą tradycyjnego kodu w C zazwyczaj wymaga setek linijek boilerplate'u, tylko po to, żeby zainicjować radio i obsłużyć handshake. To żmudne i podatne na błędy. W MicroPythonie możesz zestawić połączenie bezprzewodowe dosłownie w trzech linijkach. To zadanie modułu network, a konkretnie klasy WLAN.
Moduł network abstrahuje niskopoziomowe sprzętowe interfejsy sieciowe. Do obsługi Wi-Fi używasz klasy network kropka WLAN. Standardowy chip Wi-Fi może działać w dwóch różnych rolach. Musisz jawnie zdefiniować, której roli potrzebujesz, podczas tworzenia obiektu WLAN.
Pierwsza rola to tryb Station, określany za pomocą stałej STA podkreślnik IF. Trybu Station używasz, gdy twój sprzęt musi połączyć się jako klient z nadrzędnym routerem, tak jak smartfon dołączający do domowej sieci. Druga rola to tryb Access Point, określany za pomocą stałej AP podkreślnik IF. W tym trybie mikrokontroler sam działa jak router, rozgłaszając własną sieć bezprzewodową, dzięki czemu zewnętrzne urządzenia mogą łączyć się z nim bezpośrednio. Tryb Access Point jest niezwykle przydatny podczas deployowania sprzętu w odległych lokalizacjach, gdzie nie ma zewnętrznej sieci, pozwalając ci na lokalne połączenie w celu konfiguracji.
Podłączenie twojej płytki do sieci wymaga określonej sekwencji. Najpierw tworzysz obiekt WLAN, używając stałej trybu Station. Domyślnie fizyczne radio Wi-Fi jest wyłączone, żeby oszczędzać energię. Musisz je włączyć, przekazując True do metody active na twoim obiekcie WLAN. Kiedy interfejs jest aktywny, wywołujesz metodę connect z nazwą i hasłem twojej sieci bezprzewodowej.
I tu jest kluczowa sprawa. Metoda connect jest całkowicie nieblokująca. Kiedy ją wywołujesz, MicroPython przekazuje credentials do procesora sieciowego i natychmiast kontynuuje wykonywanie twojego skryptu. Celowo nie czeka na zakończenie autentykacji sieciowej. Taki design pozwala twojej głównej aplikacji na aktualizację wyświetlacza lub odczyt z sensora, podczas gdy radio negocjuje połączenie z routerem w tle. Jeśli jednak twój kod spróbuje wysłać dane natychmiast po wywołaniu connect, operacja zcrashuje, ponieważ płytka nie ma jeszcze tak naprawdę połączenia.
Zarządzasz tym poprzez polling interfejsu. Obiekt WLAN dostarcza metodę o nazwie isconnected, która zwraca prostego booleana. Standardowy pattern to napisanie pętli while, która sprawdza, czy isconnected to false. Wewnątrz tej pętli wprowadzasz krótkie opóźnienie, na przykład sleep na pół sekundy. Wykonywanie kodu pauzuje w tym miejscu, wielokrotnie sprawdzając stan sprzętu, aż router w końcu zaakceptuje połączenie, a isconnected zwróci true.
Kiedy pętla się zakończy, sprzęt jest oficjalnie online. Aby zweryfikować szczegóły sieci, wywołujesz metodę ifconfig na twoim obiekcie WLAN. Ta metoda zwraca kolekcję czterech konkretnych elementów: przypisany adres IP, maskę podsieci, bramę i główny serwer DNS. Możesz wyciągnąć pierwszy element z tej kolekcji i wypisać go w konsoli, żeby dokładnie zobaczyć, jaki adres IP router przyznał twojej płytce.
Nieblokująca natura metody connect oznacza, że zawsze jesteś odpowiedzialny za weryfikację stanu sprzętu przed próbą routowania danych przez sieć.
To wszystko na dziś. Dzięki za wysłuchanie — idź zbudować coś fajnego.
5
Przetrwać ograniczenia: RAM i GC
3m 59s
Opanuj sztukę pisania wydajnego pamięciowo kodu w Pythonie. Omawiamy fragmentację sterty, wstępną alokację buforów i ręczne garbage collection, aby twój mikrokontroler działał płynnie.
Cześć, tu Alex z DEV STORIES DOT EU. MicroPython, odcinek 5 z 7. Python uwielbia dynamicznie alokować pamięć, tworząc nowe obiekty dla każdego obliczenia. Działa to idealnie na serwerze, ale na mikrokontrolerze ten nawyk w końcu zawiesi twoją płytkę, zostawiając cię z błędem pamięci, nawet jeśli pozornie masz jeszcze wolny RAM. Dzisiaj skupimy się na przetrwaniu w warunkach ograniczeń: RAM i GC.
Pamięć, w której żyją twoje obiekty w Pythonie, to tak zwany heap. W MicroPythonie heap jest niezwykle mały. Kiedy tworzysz obiekty, zajmują one bloki pamięci. Gdy te obiekty nie są już potrzebne, są usuwane. To zostawia dziury w twojej pamięci. Z czasem, gdy obiekty o różnych rozmiarach są tworzone i niszczone, twój heap zaczyna przypominać szwajcarski ser. To jest fragmentacja heapa. I tu pojawia się kluczowa sprawa. Kiedy musisz zaalokować nowy obiekt, MicroPython potrzebuje pojedynczego, ciągłego bloku pamięci, żeby go pomieścić. Jeśli twój heap jest mocno pofragmentowany, możesz mieć łącznie dwadzieścia kilobajtów wolnego RAM-u, ale żadna pojedyncza dziura nie będzie większa niż jeden kilobajt. Jeśli poprosisz o dwa kilobajty, twój program się zawiesi.
Musisz przestać marnować RAM, zaczynając od swoich zmiennych globalnych. Jeśli masz wartości typu integer, które nigdy się nie zmieniają, takie jak numery pinów czy adresy sprzętowe, nie przypisuj ich w zwykły sposób. MicroPython udostępnia specjalną deklarację o nazwie const. Jeśli owiniesz swojego integera w tę deklarację const, kompilator podczas kompilacji zastąpi nazwę zmiennej konkretną liczbą w całym twoim kodzie. Dzięki temu zmienna w ogóle nie jest tworzona w RAM-ie.
Następnie przyjrzyj się swoim pętlom. Dynamiczne tworzenie obiektów wewnątrz szybkiej pętli to główna przyczyna fragmentacji. Odczytujesz dane z czujnika po magistrali SPI sto razy na sekundę. Jeśli użyjesz standardowego polecenia read, MicroPython utworzy w pamięci zupełnie nową sekwencję bajtów dla każdego pojedynczego odczytu. Te obiekty szybko się piętrzą, wypełniając luki i niszcząc twoją ciągłą przestrzeń pamięci.
Zamiast tworzyć nowe obiekty, używaj ponownie tych, które już masz. Zanim zacznie się twoja pętla, utwórz bytearray o odpowiednim rozmiarze. Bytearray to mutowalny blok pamięci. Następnie, wewnątrz pętli, użyj metody sprzętowej o nazwie readinto. Przekazujesz swój wcześniej utworzony bytearray do readinto. Sprzęt wrzuca dane z czujnika bezpośrednio do tego samego bloku pamięci, nadpisując stare dane. Przetwarzasz dane, pętla się powtarza i nie jest alokowana żadna nowa pamięć.
Nawet przy ostrożnym kodowaniu, tworzenie niektórych obiektów jest nieuniknione, co prowadzi nas do Garbage Collectora. Domyślnie Garbage Collector uruchamia się automatycznie, gdy alokacja pamięci się nie powiedzie. Skanuje pamięć w poszukiwaniu nieużywanych obiektów i zwalnia ich miejsce. Jednak ten proces zajmuje trochę czasu. Jeśli uruchomi się automatycznie podczas operacji wrażliwej na czas, twój kod nieprzewidywalnie się zatrzyma. Aby temu zaradzić, musisz przejąć kontrolę. Wywołaj jawnie funkcję collect z modułu gc. Znajdź bezpieczne miejsce w swoim kodzie, na przykład koniec głównej pętli, i tam uruchom Garbage Collectora. Dzięki temu utrzymasz heapa w czystości zgodnie z własnym harmonogramem, zapobiegając nieoczekiwanym opóźnieniom.
W desktopowym Pythonie pamięć to coś, co język obsługuje za ciebie, ale w MicroPythonie pamięć to fizyczny pojemnik, który musisz pakować ręcznie.
To wszystko w tym odcinku. Dzięki za wysłuchanie i twórz dalej!
6
Skompilowane czy zamrożone: Wdrażanie na produkcję
4m 06s
Dowiedz się, jak wdrażać ogromne aplikacje bez wyczerpywania pamięci RAM. Badamy wstępnie skompilowane pliki .mpy i zamrażanie kodu bajtowego bezpośrednio w pamięci flash mikrokontrolera.
Cześć, tu Alex z DEV STORIES DOT EU. MicroPython, odcinek 6 z 7. Wgrywasz całkowicie poprawny plik Pythona na swój mikrokontroler, wpisujesz import, a płytka natychmiast crashuje z błędem out-of-memory. Sam kod jest w porządku, ale proces jego ładowania po prostu zużył całą dostępną pamięć. Żeby to naprawić, musisz pominąć kompilator na urządzeniu, co prowadzi nas do tematu skompilowanego i zamrożonego kodu przy produkcyjnych deployach.
Kiedy odpalasz standardowy skrypt Pythona na desktopie, pamięć jest praktycznie nieskończona. Na mikrokontrolerze pamięć jest mocno ograniczona. Kiedy wrzucasz standardowy plik źródłowy Pythona na urządzenie i odpalasz komendę import, MicroPython nie tylko odczytuje plik. Kompilator odpala się bezpośrednio na mikrokontrolerze. Parsuje tekst, buduje abstract syntax tree i generuje bytecode. Każdy z tych kroków alokuje pamięć w RAM-ie.
Weźmy na przykład duży framework IoT, który ogarnia networking, bezpieczeństwo i dane z sensorów. Jeśli spróbujesz zaimportować ten ogromny moduł ze standardowego pliku źródłowego, kompilator prawdopodobnie wyczerpie dostępny heap, zanim wykona się pierwsza linijka twojej właściwej aplikacji. Skok zużycia RAM-u podczas fazy kompilacji zabija ten proces.
Żeby to ominąć, musisz usunąć etap kompilacji z mikrokontrolera. Robisz to, prekompilując swój kod za pomocą desktopowego narzędzia zwanego cross-compilerem, czyli mpy-cross. Odpalasz to narzędzie na swoim głównym komputerze. Przetwarza ono twój plik źródłowy Pythona i na wyjściu generuje skompilowany plik z rozszerzeniem M-P-Y. Ten plik zawiera wygenerowany wcześniej bytecode.
Kiedy prześlesz ten skompilowany plik na mikrokontroler i zrobisz import, MicroPython rozpoznaje format i całkowicie pomija fazę kompilacji. Całkowicie unikasz skoku zużycia pamięci spowodowanego przez kompilator. I tu robi się ciekawie. Chociaż prekompilacja ratuje cię przed skokiem pamięci przy kompilacji, mikrokontroler nadal ładuje właściwy bytecode z systemu plików do RAM-u, żeby go wykonać. Jeśli twój framework IoT jest wystarczająco duży, sam bytecode i tak zużyje zbyt dużo RAM-u, zostawiając ci bardzo mało miejsca na zmienne i dane twojej aplikacji.
Kiedy prekompilowane pliki to za mało, ostatnim krokiem jest zamrożenie bytecode'u. Zamrożenie oznacza wpieczenie skompilowanego kodu bezpośrednio w firmware MicroPythona. Zamiast kopiować skompilowany plik do systemu plików płytki, wrzucasz go do konkretnego katalogu z modułami w kodzie źródłowym MicroPythona na swoim komputerze. Następnie przebudowujesz cały obraz firmware'u MicroPythona od zera i flashujesz ten customowy firmware na swoją płytkę.
To całkowicie zmienia sposób, w jaki wykonywany jest kod. Kiedy importujesz zamrożony moduł, MicroPython w ogóle nie kopiuje bytecode'u do RAM-u. Ponieważ kod jest wpieczony w firmware, na stałe rezyduje w pamięci flash mikrokontrolera. MicroPython wykonuje bytecode bezpośrednio z flasha. Dzięki wykonywaniu z flasha, zużycie RAM-u przy imporcie tego potężnego frameworka IoT spada prawie do zera. Cały twój RAM pozostaje całkowicie wolny do przetwarzania danych w czasie rzeczywistym.
Podstawowa różnica sprowadza się do tego, gdzie żyje twój kod podczas działania. Prekompilacja oszczędza pamięć podczas kroku import, ale zamrożenie oszczędza pamięć przez cały czas życia programu, wymuszając wykonywanie w całości w pamięci flash. Jeśli chcesz wesprzeć nasz podcast, możesz wyszukać DevStoriesEU na Patreonie. To wszystko w tym odcinku. Dzięki za słuchanie i budujcie dalej!
7
Determinizm w Pythonie: Timery i przerwania
4m 06s
Osiągnij zachowanie w czasie rzeczywistym w MicroPythonie, używając sprzętowych timerów i procedur obsługi przerwań. Omawiamy surowe zasady pisania ISR i unikania alokacji pamięci.
Cześć, tu Alex z DEV STORIES DOT EU. MicroPython, odcinek 7 z 7. Napisanie procedury obsługi przerwań sprzętowych w języku wysokiego poziomu z garbage collectorem brzmi jak proszenie się o natychmiastowy crash systemu. Ale MicroPython tak naprawdę na to pozwala, o ile przestrzegasz jednej, żelaznej zasady: zero alokacji pamięci. Ten odcinek omawia determinizm w Pythonie: timery i przerwania.
Kiedy musisz odczytać dane z czujnika dokładnie tysiąc razy na sekundę, standardowa pętla z funkcją sleep po prostu nie zadziała. Garbage collection i zadania w tle wprowadzają losowe opóźnienia, niszcząc twój timing. Aby uzyskać deterministyczne działanie, potrzebujesz sprzętowego timera. Używając modułu machine, konfigurujesz obiekt Timer, aby odpalał się okresowo i wykonywał określony callback. Ten callback to twoja procedura obsługi przerwań, czyli ISR.
Timery wyzwalają miękkie lub twarde przerwania. Miękkie przerwanie jest kolejkowane przez maszynę wirtualną MicroPythona, kiedy jest to bezpieczne. Piszesz miękki ISR tak jak normalny kod w Pythonie. Może tworzyć obiekty, robić append do list i obsługiwać floaty. Minusem jest latencja. Jeśli garbage collector akurat działa, kiedy odpala się timer, twoje miękkie przerwanie po prostu czeka w kolejce.
Jeśli potrzebujesz ścisłej dokładności timingu, konfigurujesz timer tak, aby wyzwalał twarde przerwanie. Twarde przerwanie natychmiast zatrzymuje procesor. Omija maszynę wirtualną i natychmiast uruchamia twój callback. I tu jest kluczowa sprawa. Ponieważ twarde przerwanie wywłaszcza bieżący stan systemu, może przerwać działanie samego menedżera pamięci. Jeśli twój ISR spróbuje zaalokować pamięć na stercie, gdy sterta jest w trakcie modyfikacji, mikrokontroler zaliczy crasha.
To ograniczenie dokładnie dyktuje, jak piszesz twarde ISR-y. Nie możesz alokować żadnej pamięci. Żadnego tworzenia list. Żadnego budowania słowników. Żadnej manipulacji stringami. Nawet operacje na floatach są surowo zabronione, ponieważ w Pythonie każdy float to nowo zaalokowany obiekt na stercie. Twój kod musi wykonywać się szybko, opierać się na matematyce na integerach i wykorzystywać już istniejące struktury pamięci.
Wyobraź sobie scenariusz, w którym sprzętowy timer odpala się z częstotliwością 1000 Hz. Co milisekundę twardy ISR odczytuje wartość z czujnika. Ponieważ nie możesz zrobić append odczytu do listy, musisz prealokować pamięć przed wystartowaniem timera. W swoim głównym programie tworzysz bytearray o rozmiarze tysiąca bajtów. Definiujesz również globalną zmienną indeksową.
Kiedy timer odpala, ISR odczytuje czujnik. Aktualizuje globalny indeks, zapisuje surowego integera w bytearray na tej pozycji i kończy działanie. Przestrzeń pamięci została w całości zarezerwowana z góry. Przerwanie po prostu aktualizuje ją in-place, gwarantując wykonanie bez jittera. Po zapełnieniu bufora, ISR musi przekazać dane do programu głównego. Nie możesz odpalić złożonej funkcji przetwarzania danych bezpośrednio z twardego ISR. Zamiast tego używasz micropython dot schedule. Ta funkcja przyjmuje referencję do callbacku i kolejkuje go do uruchomienia, gdy tylko system powróci do bezpiecznego, miękkiego stanu wykonania.
Obsługa przerwań sprzętowych zmusza cię do porzucenia myślenia o Pythonie jako o nieskończonej puli zasobów i rozpoczęcia śledzenia, kiedy i gdzie dokładnie alokowana jest twoja pamięć. Sprawdź oficjalną dokumentację MicroPythona, aby poznać specyficzne zachowania przerwań dla twojego portu sprzętowego, i spróbuj celowo złamać zasady dotyczące pamięci na własnej płytce, aby zobaczyć, jak zareaguje system. Jeśli chcesz zaproponować tematy do naszej kolejnej serii, wpadnij na devstories dot eu. Chciałbym poświęcić chwilę, aby podziękować ci za wysłuchanie – to bardzo nam pomaga. Miłego dnia!
Tap to start playing
Browsers block autoplay
Share this episode
Episode
—
Copy this episode in another language:
Ta strona nie używa plików cookie. Nasz dostawca hostingu może rejestrować Twój adres IP do celów analitycznych. Dowiedz się więcej.