Programowanie w środowisku MicroPython (1). Wstęp

Programowanie w środowisku MicroPython (1). Wstęp

Uważa się, że Python jest obecnie najpopularniejszym językiem programowania. Ze względu na swoją wszechstronność znalazł zastosowanie w najróżniejszych aplikacjach, działających na wielu komputerach, serwerach, smartfonach, a nawet kalkulatorach naukowych. Od 2013 roku dostępna jest także wersja MicroPython, czyli implementacja Pythona na mikrokontrolery. Obecnie obsługiwane są prcesory z serii ESP32, STM32, SAM, Raspberry Pico, Nordic NRF i wiele innych.

Dla kogo jest MicroPython?

Kiedyś za jedyny słuszny język do programowania mikrokontrolerów był uważany asembler. Dawał pełny dostęp do każdego rejestru procesora i wprawny programista potrafił stworzyć bardzo efektywny program, ale praca w tym języku była czasochłonna i wymagała specjalistycznej wiedzy.

Problemy te w pewnym stopniu rozwiązywały języki wyższego poziomu, takie jak C i C++, które pozwalały uwolnić się od mozolnego ustawiania bitów w rejestrach. Dawniej zarzucano im, że kod generowany przez kompilatory nie jest tak efektywny, jak kod napisany w asemblerze. Jest w tym trochę prawdy, lecz słabszą optymalizację kompensował zdecydowanie krótszy czas tworzenia programu, a... czas to pieniądz. W przypadku czasu pracy programisty embedded całkiem spory pieniądz.

MicroPython jest językiem jeszcze wyższego poziomu niż C++. Kod w Pythonie nie kompiluje się do instrukcji maszynowych, wykonywanych bezpośrednio przez procesor. W zamian Python korzysta z interpretera, który analizuje kod programu bezpośrednio przed wykonaniem i przetwarza instrukcje na kod wykonywany przez maszynę wirtualną. W tym momencie warto dodać, że interpreter Pythona napisany jest w C.

Jaki to ma sens? Takie podejście pozwoliło jeszcze bardziej oddzielić kod od sprzętu, dzięki czemu ten sam kod powinien działać na ESP32, Raspberry Pico, STM32 i wielu innych mikrokontrolerach obsługujących MicroPythona. Ponadto instrukcje Pythona są bardzo przyjazne i czytelne, dzięki czemu można się go nauczyć niezwykle szybko.

Bardzo często korzystam z MicroPythona w pracy, aby szybko zrobić jakiś prototyp. Przydaje mi się także do tego, by szybko przetestować czujniki, wyświetlacze czy inne elementy. Dużo łatwiej i szybciej jest napisać kod w Pythonie niż w C lub C++.

Jednak MicroPython ma także pewne wady, które moim zdaniem wykluczają jego zastosowanie w produktach komercyjnych. Niestety nie jest on wolny od błędów i czasami zdarza się, że program zawiesi się bez żadnego widocznego powodu. Tendencja ta jest tym bardziej widoczna, im więcej mamy równolegle działających tasków. Innym problemem jest to, że nie ma sposobu, aby skutecznie zabezpieczyć kod programu przed kradzieżą. Istnieje bowiem możliwość, aby podłączyć programator i ściągnąć pliki z kodem w Pythonie. Widać także wyraźną różnicę w szybkości działania programu pomiędzy C i MicroPythonem – pod tym względem C jest wielokrotnie szybszy. Ponadto kod w C jest także lepiej zoptymalizowany pod względem zapotrzebowania na pamięć ROM.

MicroPython, podobnie jak C i C++, ma swoje wady i zalety. Mimo to uważam, że warto poznać ten język. Jest to coś, czego w świecie mikrokontrolerów jeszcze nie było.

Celem kursu jest zaznajomienie czytelnika z możliwościami środowiska MicroPython na przykładzie mikrokontrolerów ESP32, poprzez teoretyczny opis różnych funkcji i klas oraz ich zastosowanie w praktycznych przykładach. Główny nacisk będzie położony na użycie peryferiów sprzętowych, takich jak piny, timery, przerwania, a w szczególności kurs będzie omawiał peryferia typowe dla ESP32, czyli Wi-Fi, ESP-NOW i Bluetooth.

Zakładam, że Czytelnik zna język Python w zakresie podstawowym. Z tego powodu kody używane w kursie mają być proste do zrozumienia i nie będą wymagać znajomości zaawansowanych możliwości tego języka. Jeżeli wiesz, jak działa instrukcja if, for oraz jak napisać funkcję czy klasę, to ta wiedza wystarczy do zrozumienia wszystkich kodów z niniejszego kursu. Jeżeli tak nie jest, to polecam zapoznanie się z jakimś kursem „normalnego” Pythona na zwykły komputer – takich tutoriali jest w Internecie bardzo dużo.

Lista zakupów

Podczas kursu nie będziemy używać żadnej rozbudowanej płytki testowej. Zamiast niej skorzystamy z niedrogiego i popularnego devkitu z modułem ESP32-S3 firmy Espressif. Za pomocą płytki stykowej i kabelków będziemy podłączać różne peryferia omawiane w kursie, takie jak diody LED, zegar RTC, pamięć EEPROM oraz wyświetlacze OLED i TFT. Jestem przekonany, że większość Czytelników już ma pod ręką płytki stykowe i drobne elementy. Jeżeli nie – to jest to odpowiedni moment, aby zamówić te elementy, by zdążyły przyjechać przed publikacją kolejnych odcinków. Poniżej znajduje się lista wyposażenia, z którego będziemy korzystać w 12 odcinkach niniejszego kursu.

  1. Płytka ESP32-S3-DevKitC-1-N8R8
  2. Płytka z zegarem RTC DS1307 i pamięcią EEPROM 24C32
  3. Wyświetlacz TFT 480×320 z kontrolerem ST7796 (SPI) i pojemnościowym panelem dotykowym z kontrolerem FT6336 (I²C)
  4. Wyświetlacz OLED 128×64 z kontrolerem SSD1309 (I²C) – w sprzedaży dostępne są wyświetlacze w kolorach: białym, żółtym, zielonym i niebieskim, a każdy może mieć interfejs I²C lub SPI. Płytki z I²C mają pewną wadę objawiającą się piszczeniem przetwornicy. Lepiej kupić wyświetlacz z SPI (bo nie piszczy), a następnie przerobić go na I²C, lutując dwie zworki. W razie potrzeby można przywrócić interfejs SPI.
  5. Karta MicroSD i przejściówka SD, do której można przylutować goldpiny lub dowolna inna płytka umożliwiająca podłączenie karty MicroSD
  6. Płytka stykowa 830 pól
  7. Kabelki do płytki stykowej
  8. Kilka diod LED przewlekanych: czerwona, żółta, zielona, niebieska
  9. Rezystory przewlekane do diod LED (np. 100 Ω, 220 Ω, 470 Ω)

Dlaczego ESP32-S3?

ESP32 to popularna rodzina układów SoC, która zdobyła uznanie dzięki niskiej cenie, wysokiej mocy obliczeniowej, a przede wszystkim dzięki bajecznie prostej komunikacji przez Wi-Fi i Bluetooth. ESP32-S3 to kolejna generacja tych mikrokontrolerów. Wyposażone są w dwa rdzenie taktowane zegarem o częstotliwości 240 MHz. Moduły z tymi układami dostępne są w wersjach z pamięcią Flash o rozmiarze 4, 8, 16 i 32 MB. Każdy ma 512 kB wbudowanej pamięci RAM, a – w zależności od wersji – może mieć zamontowane dodatkowe 2 lub 8 MB pamięci PSRAM (warto wiedzieć, że wersja z 8 MB PSRAM jest szybsza niż model wyposażony w 2 MB tej pamięci). W jednym z odcinków będziemy korzystać z wyświetlacza TFT, który potrzebuje całkiem sporo pamięci RAM do obsługi bufora obrazu. Z tego powodu zalecam użycie ESP32-S3 z wbudowaną pamięcią PSRAM o rozmiarze 8 MB.

Instalujemy

Podczas naszego kursu będziemy pracować w środowisku Thonny. W chwili pisania tego odcinka najnowsza wersja programu to 4.1.7. Najpierw wchodzimy na stronę www.thonny.org i pobieramy instalator właściwy dla systemu na naszym komputerze. Program dostępny jest na systemy Windows, Linux i MacOS w wielu różnych wersjach, w tym również jako portable i kod źródłowy [3]. Ja, pisząc ten kurs, będę używał wersji na system Windows, zainstalowanej przez instalator Installer with 64-bit Python 3.10 (thonny-4.1.7.exe). Proces wgrywania programu jest banalnie prosty i nie wymaga komentarza.

Podłączamy płytkę z ESP32-S3-DevKitC do komputera. Na module znajdują się dwa porty microUSB. Port oznaczony etykietą „USB” prowadzi prosto do układu ESP32-S3, który ma wbudowany interfejs USB Device/Host. Może zostać użyty w dowolnym celu, także do wgrywania i debugowania programu.

Jednak jest to trochę niewygodne, ponieważ każdy reset mikrokontrolera resetuje również USB, a to powoduje, że urządzenie na chwilę przestaje być widziane przez komputer, po czym na nowo się pojawia i za każdym razem musi ponownie łączyć się z komputerem.

Port oznaczony etykietą „UART” służy tylko do wgrywania programu i diagnostyki poprzez konsolę. Pomiędzy tym gniazdem a ESP32-S3 znajduje się przejściówka USB/UART typu CP2102N firmy Silicon Labs. Użycie tej przejściówki jest wygodne, ponieważ ESP możemy wtedy dowolnie resetować, a płytka cały czas jest widziana przez system, nawet podczas rozruchu mikrokontrolera.

Może się zdarzyć, że Windows nie rozpozna układu CP2102N. Wtedy trzeba pobrać sterownik ze strony [7]. Przyda nam się także „zwykły Python” na normalny komputer, który można pobrać spod adresu [8]. Użyjemy go do konwertowania grafik w celu pokazania ich na wyświetlaczu OLED i TFT. Instalując Pythona, należy pamiętać o tym, aby podczas instalacji zaznaczyć opcję Add python.exe to PATH.

Uruchom program Thonny. Przy pierwszym uruchomieniu program zapyta nas o wybór języka i domyślny zestaw ustawień. Wybieramy język polski i ustawienia standardowe, po czym klikamy Let’s go.

Domyślnie w Thonny wyłączone są wszystkie przydatne opcje. W górnej części ekranu widać tylko edytor kodu, a na dole jest umieszczona konsola Pythona. Otwórz menu Podgląd, a następnie włącz opcje takie jak: Pliki, Zarys, Zmienne i Inspektor Obiektów. Pojawią się dodatkowe okienka po prawej i lewej stronie edytora tekstu.

Okno Pliki pozwala wyświetlić listę plików i katalogów naszego projektu na komputerze oraz w pamięci ESP32. Dzięki temu łatwiej można kopiować pliki pomiędzy komputerem i układem SoC. W oknie Zarys można zobaczyć „spis treści” aktualnie otwartego pliku, tzn. nazwy wszystkich funkcji i klas. Dzięki temu można szybko odnaleźć potrzebne rzeczy w długich plikach. Okno Zmienne pokazuje, jakie zmienne znajdują się w pamięci i jaki mają typ oraz zawartość. Inspektor obiektów pozwala dokładniej zbadać zmienne. Za pomocą tej funkcjonalności możemy zaglądać także do środka modułów i klas, aby zobaczyć, jakie w nich są dostępne funkcje i zmienne.

Z menu Narzędzia wybierz Opcje. Przejdź do zakładki Edytor i zaznacz następujące pola:

  • Podświetl zmienne lokalne,
  • Podświetl bieżący wiersz,
  • Automatycznie pokazać informacje o parametrze po wpisaniu ‘(‘.

Wybierz zakładkę Interpreter. Domyślnie Thonny uruchamia interpreter Pythona na komputerze, przez co aktywna jest opcja Lokalny Python 3. Z listy rozwijanej wybierz MicroPython (ESP32). Zwróć uwagę, że w tym miejscu dostępna jest też cała gama innych mikrokontrolerów.

Musimy teraz wgrać MicroPythona do ESP32. W tym celu klikamy Zainstaluj lub zaktualizuj MicroPythona w prawym dolnym rogu okna. Pojawi się okienko, w którym podajemy parametry potrzebne do wgrywania programu. Pierwszym z nich jest wybór portu COM. Jeżeli w Twoim komputerze jest więcej niż jeden port szeregowy, upewnij się, który jest odpowiedzialny za komunikację z ESP32. W dalszej części można wybrać typ mikrokontrolera, wariant i żądaną wersję MicroPythona.

Autorzy Thonny przeoczyli jednak układ ESP32-S3 z dodatkową pamięcią RAM, zatem potrzebną wersję musimy pobrać ręcznie spod adresu [6]. Na stronie znajdujemy Firmware (Support for Octal-SPIRAM) i pobieramy plik .bin z najnowszą wersją firmware (w chwili pisania tego kursu jest to 1.24.1).

Zapisujemy plik na komputerze i wracamy do Thonny. Klikamy przycisk z trzema kreskami (rysunek 1) i wskazujemy pobrany plik, po czym klikamy Zainstaluj. Program wgra firmware do ESP32, co zajmie trochę czasu. Kiedy pojawi się komunikat Done! możemy kliknąć Zamknij i wyłączyć okno ustawień.

Rysunek 1. Instalacja firmware z MicroPythonem w ESP32

Program powinien otworzyć port COM i nawiązać połączenie z mikrokontrolerem. W konsoli na dole okna programu powinniśmy zobaczyć kilka komunikatów, a w nich MicroPython v1.24.1 on 2024-11-29; Generic ESP32S3 module with Octal-SPIRAM with ESP32S3. Jeżeli program nie połączył się automatycznie, kliknij przycisk STOP na górnym pasku narzędzi.

Wszystkie kody programów omawianych w kursie publikowane będą na GitHubie pod adresem [1]. Warto sklonować to repozytorium i przed każdym odcinkiem wykonać polecenie git pull. W okienku Pliki po lewej stronie otwórz katalog kursu, aby mieć łatwy dostęp do wszystkich plików omawianych w tym i w kolejnych odcinkach. Otwórz plik hello_world.py z katalogu 01_Wstep.

Program Thonny powinien wyglądać tak, jak to pokazano na rysunku 2.

Rysunek 2. Program Thonny z włączonymi dodatkowymi funkcjonalnościami

Pierwszy program

Czas napisać pierwszy program! Jego celem będzie zademonstrowanie podstawowych funkcjonalności, takich jak wyświetlanie komunikatów na konsoli systemowej, pobieranie danych z konsoli, odczytywanie przycisku i miganie diodą LED co określony czas. Zobacz listing 1.

# Plik hello_world.py
import time # 1
from machine import Pin # 2

print("Hello world!") # 3

button = Pin(0, Pin.IN, Pin.PULL_UP) # 4
led = Pin(21, Pin.OUT) # 5
name = input("Jak masz na imię? ") # 6
print(f"Cześć {name}!!!”) # 7

print("Naciśnij przycisk")
while button(): # 8
pass # 9

for i in range(5): # 10
print("Dioda świeci")
led(1) # 11
time.sleep_ms(250) # 12
print("Dioda nie świeci")
led(0) # 13
time.sleep_ms(250) # 14

print("Koniec programu!")

Listing 1. Kod pliku hello_world.py

Programy w Pythonie rozpoczynamy najczęściej od zaimportowania bibliotek, które są używane naszym kodzie. Pierwsze dwie linie demonstrują dwa sposoby umożliwiające wykonanie tej operacji. W linii 1 pobieramy całą bibliotekę time, tzn. wszystkie klasy, funkcje, zmienne czy inne elementy, jakie w tej bibliotece są zawarte. W linii 2 natomiast pokazano przykład, jak z biblioteki można zaimportować tylko interesujące nas elementy. W tym przypadku z biblioteki machine, która zawiera kod obsługujący wszystkie peryferia sprzętowe, importujemy tylko klasę Pin do sterowania liniami GPIO.

W linii 3 korzystamy z funkcji print() do wyświetlenia komunikatu na konsoli. Napisy w Pythonie możemy umieszczać wewnątrz cudzysłowów „podwójnych” i ‘pojedynczych’. Ponieważ jestem przyzwyczajony do składni C++, będę używał cudzysłowów podwójnych.

Przechodzimy do inicjalizacji peryferiów. Wszystkie piny wejścia i wyjścia zrealizowane są obiektowo (ale nie jest to reguła dla wszystkich peryferiów). W linii 4 tworzymy zmienną button i zapisujemy do niej obiekt obiekt klasy Pin. Ta klasa ma konstruktor, który wywołuje się, wpisując nazwę klasy z nawiasami okrągłymi. Wewnątrz nawiasów konstruktor przyjmuje argumenty służące do zainicjalizowania nowo tworzonego obiektu. W tym przypadku są to:

  1. Pierwszy argument 0 to numer pinu, którym ma sterować tworzony obiekt. Pin GPIO 0 podłączony jest do przycisku BOOT.
  2. Drugi argument Pin.IN oznacza, że pin ma być wejściem. Inne opcje możliwe do wyboru to Pin.OUT (czyli pin wyjściowy typu push-pull) lub Pin.OPEN_DRAIN (czyli wyjście z otwartym kolektorem).
  3. Trzeci, opcjonalny argument Pin.PULL_UP włącza wewnętrzny rezystor podciągający do linii zasilania. Dostępny jest także rezystor ściągający wybraną linię do masy (Pin.PULL_DOWN).

W podobny sposób tworzymy obiekt do sterowania pinem podłączonym do diody LED (linia 5) – będzie to pin GPIO 26, skonfigurowany jako wyjście bez żadnych rezystorów pull-up ani pull-down.

W MicroPythonie możemy pobierać dane także z konsoli systemowej, podobnie jak w Pythonie na zwykłych komputerach. Przykład takiej operacji znajduje się w linii 6. Program wyświetli komunikat „Jak masz na imię?” i będzie czekał, aż użytkownik wpisze jakiś ciąg znaków i potwierdzi go klawiszem ENTER.

Tak pobrany string zostanie zapisany do zmiennej name.

W linii 8 wyświetlimy zmienną name za pomocą funkcji print(). Zwróć uwagę, że jest pewna istotna różnica między liniami 8 i 3. W tym przypadku zastosujemy tzw. f-string, czyli napis objęty w cudzysłów poprzedzony literą f. Powoduje to, że Python wyszuka w stringu nazwy zmiennych, objętych w nawiasy klamrowe, a następnie wstawi wartości tych zmiennych (można je dodatkowo sformatować na różne sposoby).

Zobaczmy, jak odczytuje się stan przycisku na przykładzie z linii 8. Zadaniem programu jest kręcić się w pustej pętli tak długo, aż użytkownik naciśnie przycisk BOOT. Pętla while sprawdza, czy warunek jest spełniony i następnie wykonuje instrukcję pass (linia 9), która oznacza „nic nie rób”. Warunkiem pętli jest to, by stan przycisku button był inny niż zero. Zwróć uwagę na nawiasy okrągłe za button(). Jest to skrócony zapis button.value(). Jeżeli przycisk jest wciśnięty, to funkcja zwróci 0, a w przeciwnym razie – wartość 1. Zatem wciśnięcie przycisku sprawia, że warunek pętli przestaje być prawdziwy, więc interpreter przechodzi do kolejnych linii kodu.

W linii 10 rozpoczynamy prostą pętlę, która wykona się 5 razy. Iteratorem pętli jest zmienna i, ale nie będziemy jej nigdzie używać. Zadaniem pętli jest pięciokrotne mrugnięcie diodą, którą steruje obiektem led utworzonym w linii 5.

Aby zmienić stan pinu, posługujemy się zapisem pokazanym w liniach 11 i 13. Alternatywnie możemy napisać także led.value(1), ale zapis led(1) jest lepszy, bo wykona się trochę szybciej. Można także użyć led.on() lub led.off().

Ostatnie do omówienia pozostały linie 12 i 14. W tych miejscach wywołujemy funkcję sleep_ms() z biblioteki time. Funkcja ta powoduje zatrzymanie programu na czas podany w nawiasach.

Testujemy na żywo!

Włóż devboard z ESP32-S3 do płytki stykowej i podłącz diodę LED pomiędzy pin 26 i masę. Teoretycznie szeregowo z diodą powinieneś wstawić rezystor, ale piny w ESP32 mają wbudowany ogranicznik prądowy domyślnie ustawiony na 20 mA i większość diod LED powinna bez problemu wytrzymać takie obciążenie.

Aby uruchomić program, trzeba kliknąć przycisk z zielonym trójkątem na górnym pasku narzędzi lub nacisnąć klawisz F5. Zawartość aktualnie otwartego pliku zostanie przesłana do mikrokontrolera, po czym zostanie on uruchomiony przez MicroPythona w ESP32.

Efekt działania programu pokazano na rysunku 3. Najpierw program zapyta Cię o imię, a po otrzymaniu odpowiedzi wyświetli powitanie i kilka razy zaświeci diodę LED.

Rysunek 3. Efekt działania testowego programu

Spójrz teraz na okienko Zmienne. Są tam widoczne wszystkie zmienne użyte w programie oraz kilka obiektów systemowych. Możesz sprawdzić zawartość tych zmiennych.

To jeszcze nie koniec. Na samym dole konsoli systemowej znajduje się ciąg >>>, czyli tzw. znak zachęty informujący o tym, że w tym miejscu możemy wpisać jakieś polecenia. Spróbuj wpisać import sys, a następnie sys.implementation. Po każdym poleceniu oczywiście naciśnij przycisk Enter. W konsoli wyświetli się krótka notka zawierająca informację o wersji MicroPythona.

>>> import sys
>>> sys.implementation
(name=’micropython’, version=(1, 24, 1, ‘’), _machine=’Generic ESP32S3 module with Octal-SPIRAM with ESP32S3’, _mpy=11014)

Tym sposobem możemy tworzyć nowe obiekty, modyfikować istniejące, a także wywoływać różne funkcje.

W następnym odcinku przyjrzymy się dokładniej klasie Pin, a także zobaczymy, w jaki sposób zastosować przerwania od pinów i timerów oraz jak sterować popularnymi diodami LED ze sterownikiem WS2812.

Zobacz więcej

Dominik Bieczyński
leonow32@gmail.com

Artykuł ukazał się w
Elektronika Praktyczna
maj 2025
DO POBRANIA
Materiały dodatkowe
Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik listopad 2025

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio listopad - grudzień 2025

Świat Radio

Magazyn krótkofalowców i amatorów CB

Automatyka, Podzespoły, Aplikacje listopad - grudzień 2025

Automatyka, Podzespoły, Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna listopad 2025

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Elektronika dla Wszystkich grudzień 2025

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów