Použití I/O na ESP32 pomocí MicroPython
Pokud máme nainstalovaný MicroPython v modulu ESP32 a zvládli jsme v prostředí Thonny IDE vytvořit první funkční skript, můžeme si začít ukazovat, jak pomocí Pythonu řešit základní situace z oblasti řízení modulu ESP32. Asi se shodneme, že základním prvkem jakéhokoliv řízení, je ovládnutí vstupně výstupních pinů daného mikrokontroléru.
Ovládání vstupně/výstupních pinů (GPIO) se v MicroPythonu provádí pomocí objektu Pin
, který je součástí modulu (knihovny) machine
. Asi jsme již v našem prvním příkladu poznali, že pro interakci s konkrétním „fyzickým“ pinem na desce musíte tomuto pinu přiřadit proměnnou typu objekt Pin
. Zkrátka, každý fyzický pin bude v programu reprezentován proměnnou (nebo objektem) typu Pin
. Bude tedy stačit využívat metody objektu Pin
na dané proměnné a tento „virtuální“ pin bude měnit stav „fyzického“ pinu.
Zní to složitě? Asi ano, tak si to raději ukážeme na příkladu.
Chceme-li ovládat GPIO kolík číslo 2
vytvoříme pro něj proměnou nazvanou např. p2
a této proměnné deklarujeme některé vlastnosti objektu Pin
:
p2 = Pin(2, Pin.OUT)
Z tohoto příkladu asi již trochu chápeme, že první argument (číslo 2) v závorce za slovem Pin odpovídá
číslu fyzického pinu na desce ESP32 a druhý argument (Pin.OUT
) určuje, že tento pin bude konfigurován jako digitální výstup. Když nyní budeme chtít jakkoliv pracovat s pinem číslo 2, například měnit jeho stav, budeme tak činit pomocí metody proměnné p2
. Tak třeba kupříkladu nastavení pinu 2 na vysokou úroveň HIGH
provedeme příkazem:
p2.on()
Snad už je to pochopitelné. Toto je základ tzv. objektového programování.
Opět je ale třeba připomenout, že v případě MicroPythonu i základní objekty musí být importovány. Proto na začátku skriptu nesmíme zapomenout na import každého dílčího modulu. V tomto případě:
from machine import Pin
Konfigurace pinu jako vstup/výstup pomocí MicroPythonu
Pokud jsme pochopili základní práci s objektem Pin, zkusíme to celé nyní shrnout do jednoho celku. Už víme, že konfigurace pinu na vstupu/výstupu se provádí během vytváření objektu Pin
. Pro konfiguraci výstupního pinu (například pro vestavěnou LED modulu ESP32) vytvoříme objekt Pin
se zadaným číslem pinu a jeho výstupním režimem (např. OUT).
Výstup
Pro nás zatím základní tvar deklarace proměnné výstupního pinu pro objekt Pin
je následující:
pin_led = Pin(25, mode=Pin.OUT, value=1)
Tento příkaz nastavuje pin 25
na digitální výstup a rovnou mu přiřazuje výstupní hodnotu HIGH
.
Pokud bychom chtěli, přímo nastavit výstupní napětí při vytváření objektu na hodnotu LOW
, vypadalo by to takto:
pin_led = Pin(25, mode=Pin.OUT, value=0)
Pozorný čtenář si jistě všiml, že zde je v případě druhého argumentu jakýsi výraz mode=
a třetí argument value=
jsme v předchozích článcích vůbec nepoužívali. Předtím jsme výstupní režim nastavovali jen výrazem Pin.OUT
, to bylo z argumentů vše.
Tak jak to tedy je?
Oficiálně by měla deklarace proměnné pro objekt Pin vypadat jako výše, ale občas je možné používat zjednodušené zápisy. Zvláště pokud zadáváme jen číslo pinu a jeho mód. Pak lze do druhého argumentu zadat jen výraz Pin.OUT
(tedy bez
mode=
). Pozor však na třetí argument, tam výraz value=
vynechat nejde! Jediné, co je možné vynechat, je celý třetí argument, protože je nepovinný. Takže je možné jej neuvádět, když jej nepotřebujeme specifikovat. Standardně je při vytváření Pin
objektu výstupní napětí 0 V, tedy value
je 0
.
- DŮLEŽITÉ:
- Abychom v tom dále nedělali zmatek, budeme nadále uvádět nastavení proměnných pro piny vždy včetně výrazů
mode=
- (i když si můžeme pamatovat, že občas to tam být nemusí. 😉)
Vstup
Deklarace digitálních vstupů bude vypadat dost podobně předchozímu. Chcete-li nakonfigurovat pin jako digitální vstup, změníme při vytváření pinu hodnotu mode
na vstup (IN).
pin_4 = Pin(4, mode=Pin.IN)
Jako třetí a volitelný argument může být argument pull
, který určuje typ vstupu. Můžeme si vybrat mezi vstupem s pull-up rezistorem, pull-down rezistorem nebo bez rezistorů. Pokud na těchto pojmech něco není jasné, je dobré se na funkci a užitečnost pull-up a pull-down rezistorů nastudovat. Trochu ji vysvětlíme v závěru v praktickém příkladu práce s vstupy a výstupy.
pin_4 = Pin(4, mode=Pin.IN, pull=Pin.PULL_DOWN) # vstup s pulldown resistorem
pin_4 = Pin(4, mode=Pin.IN, pull=Pin.PULL_UP) # vstup s pullup resistorem
pin_4 = Pin(4, mode=Pin.IN, pull=None) # vstup volny
pin_4 = Pin(4, mode=Pin.IN) # vstup volny, bez zadani parametru
Ten, kdo zná modul ESP32 jistě ví, že tento modul disponuje PWM výstupem a možností načítání analogových vstupů, stejně tak dvojicí analogových výstupů. Asi by se tedy slušelo nyní ukázat i deklaraci těchto variant vstupně/výstupních pinů. To si však ukážeme v některém z následujících článků, neboť to není jen záležitostí změny argumentu v deklaraci, jako je tomu zde při výběru vstupu/výstupu.
Raději, když jsme nyní zvládli nastavit piny modulu ESP32, se zaměříme na ukázku práce se vstupy a výstupy.
Způsoby nastavení a čtení GPIO
Nastavení výstupů
MicroPython nabízí hned několik funkcí (metod) pro nastavení výstupního stavu (napětí) pinů modulu ESP32.
Jsou to:
Pin.on()
aPin.off()
Pin.high()
aPin.low()
Pin.value(value)
Názvy metod jsou asi všeříkající. Opravdu si můžeme vybrat pár metod, které nám nejvíce vyhovují. Například metody .on()
a .off()
se dobře vyjímají v kódu pro zapnutí LED. Metody .high()
a .low()
se „tváří“ pro použití například při komunikaci s periférií. Ale jejich použití je skutečně jen věcí vkusu. Naopak metoda Pin.value()
je užitečná svým univerzálnějším použitím, protože ji můžeme použít pro natavení hodnoty pinů pomocí proměnné, např.:
pin_led = Pin(2, mode=Pin.OUT)
output_state = 1
pin_led.value(output_state) # HIGH output (3,3V)
output_state = 0
pin_led.value(output_state) # LOW output (0V)
Čtení vstupů
Ke čtení stavu pinu, tj. 3,3 V pro vysokou logickou úroveň (HIGH
) a 0 V pro nízkou logickou úroveň (LOW
), použijeme (překvapivě!) stejnou metodu Pin.value()
, ale bez zadané hodnoty. Například:
pin_4 = Pin(4, mode=Pin.IN, pull=Pin.PULL_UP)
pin_4.value() # vrátí 1 nebo 0 v závislosti na připojeném napětí
Asi to začíná být trochu moc teoretické… Pojďme si něco praktického zapojit!
Rozsvícení LED stisknutím tlačítka
Abychom uvedli ovládání pinů GPIO do praxe, zkusíme vytvořit skript, který zapne připojenou LED stisknutím tlačítka. Hned si na tom ukážeme i možnost využití rozdílné konfigurace vstupu vnitřním a vnějším s pull-down rezistorem.
Začneme standardním elektronickým zapojením. K jednomu digitálnímu pinu (v našem případě např. GPIO 5) připojíme LED. GPIO pin bude nastaven jako výstup. Protože výstupní napětí GPIO kolíku modulu ESP32 je 3,3 V, což je na červenou LED příliš, je do série s LED připojen ochranný rezistor (150–330 Ω). Pin GPIO 4 použijeme jako vstup pro načítání stavu tlačítka. Aby bylo jasné, v jakém stavu se tlačítko nachází, musíme mu přiřadit nějakou elektrickou hodnotu. Z toho důvodu je pin GPIO 4 spojen rezistorem s úrovní 0 V (GND). Protože tento rezistor vstup „přitahuje“ směrem k nízké úrovni napětí (tedy „dolů“), říká se mu pull-down rezistor. Asi je tedy jasné, že naopak pull-up rezistor bude dělat totéž, jen bude vstup „přitahovat“ k napájecímu napětí 3,3 V.
Kdybychom nyní načítali hodnotu vstupu GPIO 4 byla by načtená hodnota díky přítomnosti pull-down rezistoru vždy LOW
, což odpovídá připojenému napětí 0 V. Ve chvíli, kdy stiskneme tlačítko, připojíme na vstup GPIO 4 napájecí napětí 3,3 V. Pull-down rezistor má nyní další funkci – brání zkratu, proto je jeho hodnota zvolena poměrně velká (asi 10 k). Vstup GPIO4 bude tedy načítán jako hodnota HIGH
(logická 1). Tím poznáme, že bylo tlačítko stisknuté.
Po zapojení výše uvedeného schématu se můžeme vrhnout na vytvoření řídicího skriptu:
from machine import Pin
pin_button = Pin(4, mode=Pin.IN)
pin_led = Pin(5, mode=Pin.OUT)
while True:
if pin_button.value() == 1:
pin_led.on()
else:
pin_led.off()
To je vše!
Na začátku vidíme důležitý import objektu Pin
z modulu machine. Následuje zavedení proměnné pin_button
pro pin GPIO 4 – nastaven jako vstup, a proměnné pin_led
pro výstupní pin GPIO 5. V nekonečné smyčce je načítán stav proměnné pin_button
pomocí metody value()
. Tento stav je testován v podmínce if
na hodnotu 1 (odpovídá vysoké úrovni HIGH
). Pokud je to splněno, je nastaven výstup GPIO 5 pomocí metody .on()
proměnné pin_led
na stav HIGH
. LED se rozsvítí. Naopak pokud podmínka neplatí, nastává případ else
a LED je pomocí metody .off()
zhasnuta.
Pokud nejsme úplně obeznámeni s pravidly a příkazy jazyka Python, podívejme se na tento program i z pohledu zápisu kódu.
Zaprvé zde vidíme strukturu podmíněného příkazu. Je-li splněna nějaké podmínka v sekci if
, vykoná se blok příkazů za dvojtečkou. Pokud podmínka není splněna, nastane volitelný případ else
. Tato část být v podmínce může, ale i nemusí být. Aby bylo jasné, které příkazy patří do bloku if
, resp. do else
, není v Pythonu jako v jiných programovacích jazycích zvolena nějaká syntaxe pro uzavření kódu, ale je to zde vyznačeno pomocí typografie. Přesněji řečeno pomocí mezer před příkazy. To je ten důvod, proč všechny příkazy, které chceme mít v nekonečné smyčce, musí začínat minimálně dvěma mezerami. Stejně tak příkazy podmínky musí být „odraženy“ od příkazu if
. A stejně tak příkazy bloku else musí být také správně „zastrčeny“.
Někdo v Pythonu tento způsob zápisu blahořečí, jiný nenávidí. To je na vkusu každého. Každopádně to tak je! Pokud chceme v Pythonu programovat, musíme se s tím smířit.
Interní pull-down rezistor na vstupu
Nyní se podíváme na to, zda bychom nemohli výše uvedené zařízení zlevnit. To je mimochodem asi dnes docela běžný úkol běžné vývojářské práce. Co můžeme vynechat? Modul ESP32, LED, ochranný rezistor, tlačítko… to vše asi ne. Zkusíme tedy vynechat pull-down rezistor – viz následující schéma.
Vstupní pin GPIO 4 ale nyní při nestisknutém tlačítku není vůbec připojen k žádné napěťové úrovni. Takto „plovoucí“ pin není vůbec dobrá varianta pro čtení stavu. Jak tedy zaručíme, aby při nestisknutém tlačítku byla na pinu
GPIO 4 nastavena úroveň LOW
?
Zde právě nastupuje možnost nastavení interního pull-down rezistoru modulu ESP32. Vývojáři modulu ESP32 s podobnou situací počítali a pro tento případ jsou u pinů rezistory k dispozici přímo v samotném obvodu. Stačí je jen softwarově připojit. To učiníme při deklaraci proměnné pin_button pomocí parametru pull.
Následující kód je stejný jako předchozí, jen je přidán u proměnné pin_button
parametr pull
(zvýrazněno).
from machine import Pin
pin_button = Pin(4, mode=Pin.IN, pull=Pin.PULL_DOWN)
pin_led = Pin(5, mode=Pin.OUT)
while True:
if pin_button.value() == 1:
pin_led.on()
else:
pin_led.off()
Zapojení bude fungovat úplně stejně, protože původně externí rezistor zastoupil ten interní v modulu ESP32.
Abychom na závěr ještě trochu pozlobili čtenáře ohledně zápisu programu v Pythonu, ukážeme si upravený zápis podmínky z výše uvedeného kódu:
Původní zápis
while True:
if pin_button.value() == 1:
pin_led.on()
else:
pin_led.off()
můžeme upravit „kompaktnějším“ tvarem:
while True:
if not pin_button.value():
pin_led.on()
else:
pin_led.off()
Chceme „přitvrdit? Tak co takhle celou podmínku „zhustit“:
while True:
pin_led.on() if not pin_button.value() else pin_led.off()
Nebo dokonce celou podmínky vlastně úplně vynechat!
while True:
pin_led.value(not pin_button.value())
Všechny čtyři výše uvedené kódy jsou z funkčního hledika ekvivalentní. Přesto při reálném běhu v mikrokontroléru by se mezi nimi rozdíl našel.
Za domácí úkol zkusme tyto zápisy vyzkoušet a hlavně pochopit! 🤓
Tyto drobné optimalizace mohou být občas klíčové, pokud chcete spustit kód co nejrychleji. Nevýhodou MicroPythonu je totiž to, že se jedná o interpretr, takže se příkazy musí neustále za běhu překládat. Ve srovnání s jazykem C/C++ je prostě MicroPython velmi pomalý.
A co příště?
V příštím článku bychom se rádi věnovali PWM výstupu (výstupu s pulzní šířkovou modulací), pomocí kterého je možné nejen měnit svit připojené LED, ale později jej využijeme třeba i pro ovládání modelářského serva.