Fyzikální kabinet FyzKAB
Články Moduly ESP32 a ESP32-CAM ESP32 + MicroPython MicroPython: Přerušení na ESP32

MicroPython: Přerušení na ESP32

V dnešním článku si ukážeme, jak v MicroPythonu u modulu ESP32 používat přerušení. Přerušení je v programech mikrokontrolérů užitečné tím, že umožňuje automatické provádění určitých činností, tím může vyřešit různé problémy s časováním nebo reakcí na události.

Když dojde k přerušení, procesor zastaví provádění hlavního programu, aby provedl úlohu přerušení, poté se vrátí k hlavnímu programu – jak je znázorněno na obrázku níže. To je zvláště užitečné pro spuštění akce, kdykoli je detekován pohyb, stisknuto tlačítko nebo uplyne nastavený čas, aniž by byla nutná neustálá nepřetržitá kontrola aktuální hodnoty pinu, proměnné nebo průběžná kontrola času.

IRQ - princip

Funkce zpracování přerušení by měla být co nejjednodušší, aby se procesor rychle vrátil k provádění hlavního programu. Nejlepším přístupem je signalizovat hlavnímu kódu, že k přerušení došlo, například pomocí globální proměnné.

Typy přerušení

Budeme se zabývat dvěma základními typy přerušení: vnějším (externím) přerušení a přerušením časovače.

  • Přerušení časovače je iniciované na základě časových intervalů, umožňujících periodické akce nebo spuštění akce za daný časový interval. Přerušení využívá hardwarový časovač modulu ESP32.
  • Vnější (externí) přerušení je spouštěné externími signály, jako je například stisk tlačítka nebo čtení senzoru. Jedná se o hardwarové přerušení a je spojeno s konkrétním GPIO pinem. Přerušení vyvolává zadaný stav nebo změna stavu vstupního pinu.

Časovač a jeho přerušení u ESP32

Časovač a jeho přerušení umožňuje naplánovat a provádění konkrétní úkoly v pravidelných intervalech nebo po zadaném časovém zpoždění. Použití přerušení vyvolané časovačem lze tedy efektivně využívat, aniž by bylo nutné v hlavním kódu neustále kontrolovat uplynulý čas.

Třída Timer v MicroPython

Modul machine v MicroPython obsahuje třídu nazvanou Timer, která poskytuje metody pro provádění zadané funkce buď periodicky během daného období, nebo jednou po určité předem definované prodlevě. To se nám hodí pro plánování událostí nebo spouštění pravidelných úloh bez neustálé kontroly uplynulého času.

Vytvoření časovače

Chcete-li vytvořit časovač, stačí zavolat konstruktor Timer() a předat jako argument ID časovače například takto:

my_timer = Timer(1)

Poté inicializujete časovač pomocí metody init() objektu Timer() a předáme jako argument režim časovače, dobu a rutinu zpětného volání. Zde je příklad:

my_timer.init(mode=Timer.PERIODIC, period=1000, callback=timer_callback)

Tím se inicializuje periodický časovač (klíčové slovo Timer.PERIODIC), který bude spouštět každých 1000 milisekund (period=1000) funkci nazvanou timer_callback (nastaveno pomocí: callback=timer_callback). Parametr period pochopitelně můžete změnit na libovolnou požadovanou dobu, nyní ale chceme 1 vteřinu, tedy 1000 milisekund.

Místo pravidelného volání zadané funkce ji můžeme také spustit jen jednou a to po předem definované době. K tomu bychom použili nastavení parametru mode na hodnotu Timer.ONE_SHOT. Například takto:

my_timer.init(mode=Timer.ONE_SHOT, period=1000, callback=timer_callback)

Tento řádek kódu nastaví časovač (my_timer) tak, aby spustil jednorázově zadanou funkci (timer_callback) po 1000 milisekundách.

Použití si asi nejlépe ukážeme na několika příkladech.

Blikání LED s časovačem – MicroPython

V následujícím příkladu si ukážeme, jak blikat LED pomocí časovače a jeho přerušení. Mělo by nám to pomoci pochopit, jak fungují periodické časovače. Abychom nemuseli nic elektronického zapojovat. Můžeme využít zabudovanou LED na modulu ESP32, která je připojena k pinu 2.

V prostředí Thonny vytvoříme následující kód. Pro naši zkoušku jej ani nemusíme ukládat do modulu ESP32, stačí spustit zelenou ikonou Spustit aktuální skript (nebo klávesa F5) v prostředí Thonny (pochopitelně při připojeném modulu ESP32 s nainstalovaným MicroPythonem!)

Kód:

from machine import Pin, Timer
from time import sleep

# nastaveni LED
led_pin = 2
led = Pin(led_pin, Pin.OUT)

# funkce preruseni
def toggle_led(timer):
    led.value(not led.value()) # změna stavu LED (ON/OFF)

# Vytvoreni casovace
blink_timer = Timer(1)
blink_timer.init(mode=Timer.PERIODIC, period=500, callback=toggle_led) # Casovac - opakovani každých 1/2 s

while True:
    print('Hlavni smycka bezi...')
    sleep(5)

Princip kódu:

V tomto kódu nejdříve vytvoříme časovač nazvaný blink_timer:

blink_timer = Timer(1)

Poté inicializujeme tento časovač s následujícími parametry:

blink_timer.init(mode=Timer.PERIODIC, period=500, callback=toggle_led)

což znamená, že tento časovač každých 500 milisekund (stále nebo dokud nezastavíte program) volá funkci toggle_led.

Funkce toggle_led, jak název napovídá, přepíná stav LED (načte stav pinu led, zneguje jej a opět uloží na ten samý pin):

# Callback function for the timer
def toggle_led(timer):
    led.value(not led.value())  # Toggle the LED state (ON/OFF)

Funkce zavolaná časovačem musí mít jeden argument, kterým je automaticky předán objekt Timer při spuštění události.

Zároveň s časovačem můžeme mít v hlavní smyčce další úlohy, které běží nezávisle, aniž by se navzájem rušily s přerušením. Například v našem příkladu v hlavní smyčce neustále vypisujeme zprávu s časovým intervalem pět sekund.

while True:
    print('Hlavni smycka bezi...')
    sleep(5)

Výsledkem je tedy vypisování zprávy do shellu každých 5 sekund, zároveň zcela pravidelně („automaticky“) bliká vestavěná LED s periodou půl sekundy.

kod1 - vystup
ESP32 - blik

Ještě zmiňme, že kdybychom chtěli nějakým způsobem blikání ukončit, lze přerušení deinicializovat pomocí metody deinit(), např. takto (to v předchozím kódu není uvedeno):

blink_timer.deinit()

Blikání více LED diod při různých frekvencích

Po otestování předchozího příkladu je snadné pochopit, že pokud vytvoříte více časovačů, můžete spouštět více úloh na různých frekvencích. V následujícím příkladu budeme blikat dvěma různými LED. Jeden bude blikat každou půl sekundu a druhá každé dvě sekundy.

Zapojíme k modulu ESP32 dvojici různobarevných LED (nezapomeňte na ochranné rezistory ≈200 Ω).

  • Červenou LED připojíme k pinu 13 – bude blikat každou půl sekundu.
  • Žlutou LED připojíme k pinu 12 – tato LED bude blikat každé dvě sekundy.

Naše zapojení by mělo být podle následujícího schémata.

2 LED - schema

Následující kód používá dva časovače pro blikání dvou různých LED diod na různých frekvencích.

Kód:

from machine import Pin, Timer
from time import sleep

# LEDky
red_led_pin = 12
red_led = Pin(red_led_pin, Pin.OUT)
yellow_led_pin = 13
yellow_led = Pin(yellow_led_pin, Pin.OUT)

# funkce preruseni cervene LED
def toggle_red_led(timer):
    red_led.value(not red_led.value())  # změna stavu LED (ON/OFF)
    print('Cervena LED je: ', red_led.value())

# funkce preruseni zlute LED
def toggle_yellow_led(timer):
    yellow_led.value(not yellow_led.value())  # změna stavu LED (ON/OFF)
    print('Zluta LED je: ', yellow_led.value())

# Vytvoreni casovacu
red_timer = Timer(1)
yellow_timer = Timer(2)
red_timer.init(mode=Timer.PERIODIC, period=500, callback=toggle_red_led) # opakovani kazdych 0.5s
yellow_timer.init(mode=Timer.PERIODIC, period=2000, callback=toggle_yellow_led) # opakovani kazde 2s

while True:
    print('Hlavni smycka bezi...')
    sleep(5)

Princip kódu:

V tomto kódu vytvoříme dva různé časovače, jeden pro každou LED:

# Vytvoreni casovacu
red_timer = Timer(1)
yellow_timer = Timer(2)

Poté zavoláme odpovídající funkce přerušení (tzv. funkce zpětného volání) v různých intervalech:

red_timer.init(mode=Timer.PERIODIC, period=500, callback=toggle_red_led)  # opakovani kazdych 0.5 s
yellow_timer.init(mode=Timer.PERIODIC, period=2000, callback=toggle_yellow_led)  # opakovani kazde 2 s

Funkce voláné přerušeními časovače stejně jako v předešlém příkladu přepínají aktuální hodnotu dané LED. Zároveň do shellu vypisují aktuální stav dané LED:

# funkce preruseni cervene LED
def toggle_red_led(timer):
    red_led.value(not red_led.value()) # změna stavu LED (ON/OFF)
    print('Cervena LED je: ', red_led.value())

# funkce preruseni zlute LED
def toggle_yellow_led(timer):
    yellow_led.value(not yellow_led.value())  # změna stavu LED (ON/OFF)
    print('Zluta LED je: ', yellow_led.value())

Testování kódu

Po spuštění kódu na ESP32 si vidíme, že dvojice LED bliká s různými frekvencemi.

2 LED - foto
zdroj obrázku: https://randomnerdtutorials.com/micropython-timer-interrupts-ep32-esp8266/

Zároveň každých pět sekundy dostanete zprávu z hlavní smyčky while. Nyní tedy opět vidíme, že ani jedna z daných úloh přerušení viditelně nezasahuje do běhu hlavní smyčky.

kod 2 - vystup

Nastavení vnějšího přerušení

Kromě vyvolávání funkce přerušení pomocí časovače je možné, jak již bylo naznačeno, i pomocí vnějších (externích) podnětů. V případě modulu ESP32 to bude pomocí logické úrovně nebo její změny na některém z GPIO pinů. Typicky tak budeme schopni nejen zareagovat na klasický stisk tlačítka, ale třeba i na výstup nějakého čidla (viz příklad dále).

Chcete-li nastavit externí přerušení v MicroPythonu, musíte provést následující kroky:

  1. Pochopitelně je nejdříve třeba nastavit zvolený pin jako digitální vstup, aby mohl fungovat jako indikátor přerušení.
    Například:
    pir = Pin(14, Pin.IN)
  2. Důležitým nastavením je připojení přerušení k tomuto pinu, a to pomocí metody irq():
    pir.irq(trigger=Pin.IRQ_RISING, handler=handle_interrupt)

    Metoda irq() přijímá několik parametrů, pro nás jsou důležité jen následující dva:
    1. trigger: definuje režim spouštění. Lze zadat různé honodty:
      • Pin.IRQ_FALLING: ke spuštění přerušení dojde vždy, když pin přejde z úrovně HIGH na LOW;
      • Pin.IRQ_RISING: ke spuštění přerušení dojde vždy, když pin přejde z úrovně LOW do HIGH.
      • Pin.IRQ_HIGH_LEVEL: ke spuštění přerušení dojde vždy, když je pin na úrovni HIGH.
      • Pin.IRQ_LOW_LEVEL: ke spuštění přerušení dojde vždy, když je pin na úrovni LOW.
      • Logické sloučení (disjunkce) předešlých možností: přerušení se spustí při více událostech.
        Např. pro trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING se přerušení spouští při obou změnách úrovně.
    2. handler: toto je název funkce, která bude volána, když je detekováno přerušení.
  3. Z předchozího bodu vidíme, že je tedy třeba definovat funkci pro zpracování přerušení. To jsme již viděli i v případě časovače. Funkce zpracování vnějšího (externího) přerušení zde ale přijímá parametr typu Pin. Tento parametr je předán do funkce zpětného volání a odkazuje na GPIO pin, který způsobil přerušení.
    def handle_interrupt(pin):

Zní to složitě? Podíváme se tedy na praktický příklad, který nám snad vše objasní.

Příklad s PIR pohybovým čidlem

Abychom demonstrovali, jak zacházet s vnějším přerušeními, vytvoříme jednoduchý projekt s pohybovým PIR senzorem. Kdykoli bude detekován pohyb, rozsvítíme LED na 20 sekund.

PIR - reakce
Poznámka:
V našem příkladu jsme sice pro demonstraci přerušení zvolili pohybový PIR senzor, ale pokud čidlo zrovna nemáte, můžete k simulaci změny vstupu použít například tlačítko.

Schéma zapojení PIR čidla a modulu ESP32:

PIR - schema
zdroj obrázku: https://randomnerdtutorials.com/micropython-interrupts-esp32-esp8266/

Na následujícím obrázku jsou označeny vývody pohybového senzoru Mini AM312 PIR. Pochopitelně, pokud používáte jiný pohybový senzor, musíte dodržet osazení jeho pinů dle datového listu výrobce.

mini pir am312 - pinout
zdroj obrázku: https://randomnerdtutorials.com/micropython-interrupts-esp32-esp8266/
DŮLEŽITÁ POZNÁMKA:
PIR pohybový senzor Mini AM312, který je použit v tomto projektu, pracuje při napětí 3,3 V. Pokud chceme použít jiný pohybový PIR senzor, kupříkladu obvyklejší HC-SR501, je třeba si uvědomit, že může pracuje na 5 V! Modul ESP32 však pracuje na 3,3 V logice, musíme tedy daný PIR senzor upravit tak, aby též fungoval při 3,3 V.
Úprava HC-SR501 na 3,3 V
Ve výchozím nastavení modul HC-SR501 je sice napájen 5 V, ale ve skutečnosti funguje na logice 3,3 V. Potřebné pracovní napětí si vytváří zabudovaným regulátor napětí, který napětí snižuje na potřebných 3,3 V. Následující obrázek ukazuje schéma celého čidla, zmíněný regulátor je pravém horním rohu.
PIR - HC-SR501 - diagram
Chceme-li senzor provozovat na napětí 3,3 V, stačí se s 3,3 V napájením připojit až na výstup napěťového regulátoru. Některé moduly jsou dodávány s napájecími kolíky, takže ani nemusíme nic upravovat na desce DPS. Jednoduše se stačí připojit na potřebném pájecím bodě (ukázáno červeně na následujícím obrázku).
PIR - napajeni 3,3 V
zdroj obrázku: https://randomnerdtutorials.com/modifying-cheap-pir-motion-sensor-to-work-at-3-3v/

Následující program detekuje pohyb pomocí PIR čidla a rozsvítí LED na 20 s, jak jsme ve svém zadání požadovali.

Kód:

from machine import Pin
from time import sleep

motion = False

def handle_interrupt(pin):
    global motion
    motion = True
    global interrupt_pin
    interrupt_pin = pin

led = Pin(12, Pin.OUT)
pir = Pin(14, Pin.IN)

pir.irq(trigger=Pin.IRQ_RISING, handler=handle_interrupt)

while True:
    if motion:
        print('Pohyb detekovan! Preruseni vyvolal pin:', interrupt_pin)
        led.value(1)
        sleep(20)
        led.value(0)
        print('Konec pohybu. LED zhasnuto!')
        motion = False

Jak kód funguje

Nejdříve importujte třídu Pin z modulu machine a metodu sleep z modulu Time.

from machine import Pin
from time import sleep

Následně vytvoříme proměnnou s názvem motion, která může nabývat logických hodnot True, nebo False podle toho, zda bude/nebude detekován pohyb. Tato proměnná bude globální proměnná pro celý program, měnit se bude ve funkci reakce na přerušení s názvem handle_interrupt.

def handle_interrupt(pin):
    global motion
    motion = True
    global interrupt_pin
    interrupt_pin = pin

Tato funkce bude volána pokaždé, když je detekován pohyb. Funkce handle_interrupt má vstupní parametr (pin), ve kterém bude předán objekt třídy Pin, když dojde k přerušení (označuje, který pin přerušení způsobil). Předaný objekt pinu, který způsobil přerušení, uložíme do globální proměnné interrupt_pin. V tomto případě to není úplně užitečné, protože máme pouze jeden pin přerušení. Může to ale být užitečné, pokud máme několik přerušení, která spouštějí stejnou funkci přerušení, a chceme vědět, který GPIO pin přerušení způsobil.

V našem příkladu funkce handle_interrupt jednoduše změní proměnnou motion na hodnotu True a uloží pin přerušení. Více ani nechceme. Měli bychom udržovat funkci přerušení co nejkratší a také se v ní vyvarovat používání funkce print(). Hlavní kód pak musí obsahovat všechny věci, které chceme, aby se odehrály, když dojde k přerušení.

Nyní již stačí jen doplnit, že v kódu ještě musíme vytvořit dva objekty Pin. Jeden pro rozsvícenou LED na pinu 12 a další pro detekci výstupu pohybového PIR senzoru na pinu 14.

led = Pin(12, Pin.OUT)
pir = Pin(14, Pin.IN)

Nyní je třeba propojit pin výstupu PIR s obslužnou procedurou přerušení. Nastavíme u pinu pir přerušení zavoláním metody irq().

pir.irq(trigger=Pin.IRQ_RISING, handler=handle_interrupt)

V hlavní smyčce testujeme proměnnou motion, když je True, rozsvítíme LED na 20 sekund. Také vytiskneme zprávu, která indikuje, že byl detekován pohyb a vypíšeme pin, který způsobil přerušení.

if motion:
    print('Pohyb detekovan! Preruseni vyvolal pin:', interrupt_pin)
    led.value(1)
    sleep(20)

Po 20 sekundách vypněte LED a vytiskněte zprávu, která oznámí konec pohybu.

led.value(0)
print('Konec pohybu. LED zhasnuto!')

Nakonec nastavíme proměnnou motion na výchozí hodnotu False:

motion = False

Proměnná motion se opět změní na True pouze tehdy, je-li detekován pohyb a funkce handle_interrupt je zavolána přerušením.

Pro jednoduchost jsme v tomto příkladu použili nastavení doby rozsvícení LED (na dobu 20 s) jen pomocí příkazu sleep(20). Pokud jsme četli předešlý článek pozorně, jistě bychom zvládli zhasnout LED pomocí přerušení vyvolaného časovačem, nebo ne? 😊

PIR - test
zdroj obrázku: https://randomnerdtutorials.com/micropython-interrupts-esp32-esp8266/

Po nahrátí kódu do desky ESP32 by mělo vše fungovat tak, jak bylo popsáno. LED by se měla rozsvítit, jakmile je detekován pohyb. V shellu by se měly vypisovat odpovídající zprávy. Po 20 sekundách LED zhasne.

Poznámka:
Pohybový PIR senzor AM312 má výchozí hardwarovou dobu zpoždění 8 sekund. To znamená, že se od posledního spuštění nespustí dříve, než uplyne doba 8 sekund. To je třeba mít na mysli, abychom nebyli překvapeni, pokud se rozhodneme s PIR čidlem různě experimentovat.

Použítí obou přerušení: Ošetření tlačítka proti zákmitu kontaktů

Když jsme se zmínili o tom, že by bylo zajímavé v jednom kódu zkombinovat použití obou typů přerušení, není nic jednoduššího, než si to na nějakém praktickém příkladu hned vyzkoušet. Jako takový příklad si můžeme ukázat jednu z mnoha možností ošetření zakmitání mechanického spínače (tzv. „switch bouncing“).

K mechanickému zakmitání tlačítka dochází, když kontakt tlačítka nesepne jednorázově, ale dojde na něm k mechanickému (tedy i signálovému) zakmitání. Výsledkem je pak odezva, jako kdybychom stiskli několikrát (I když ve skutečnosti jsme je stiskli pouze jednou!). Tento jev je velmi běžný zejména u mechanických spínačů a tlačítek. Následující obrázek ukazuje časový diagram typické signálové odezvy při stisknutí mechanického tlačítka.

zakmitani tlacitka - casovy prubeh

Jak vidíme na časovém diagramu, při reálném stisknutí tlačítka dojde k tomu, že elektrické kontakty tlačítka se po určitou dobu několikrát spojí a odpojí. Je to velmi rychlé a trvá to jen krátkou chvíli, pak již dojde k dosažení ustáleného stavu. Jenže i takto krátký čas je oproti rychlosti kódu běžícímu v modulu ESP32 dostatečně dlouhý, aby jej mohl modul zaznamenat. Tím pádem dojde k tomu, že systém zaregistruje vícenásobné stisknutí. Pokud je na změnu stavu tlačítka navázána nějaké funkce, dojde k jejímu násobnému vykonání. Například to způsobí nepřesný počet stisknutí.

Abychom tomuto problému předešli, musíme tento efekt ošetřit některou z technik ošetření zákmitu kontaktů – například pomocí zpoždění reakce na změnu stavu spínače (pochopitelně pomocí časovače a jeho přerušení 👍).

V následujícím příkladu se tedy podíváme na to, jak můžeme použít časovač a přerušení události pro ošetření zakmitání tlačítka. Princip našeho ošetření bude v podstatě jednoduchý. Zareagujeme na první zadanou změnu vstupního pinu, poté po určený čas (asi 200 ms) budeme jakékoliv další změny ignorovat. Bude-li tento čas dostatečně krátký, aby nezamezil detekci klasického zmáčknutí, ale přesto natolik dlouhý, aby překryl dobu kmitání kontaktů, máme problém vyřešený. K tomu nám právě výborně poslouží časovat, který nastaví opětovnou reakci na změnu stavu pinu až po zadaném čase.

Nejdříve sestavíme klasické zapojení, tedy připojíme k modulu ESP32 LED k pinu 13 a tlačítko k pinu 12 (viz následující schéma).

zakmitani tlacitka - schema

Budeme chtít nejen rozsvěcet a zhasínat LED pomocí tlačítka, ale zároveň i počítat počet stisknutí. A to skutečný počet stisknutí, nikoliv záchvěvů kontaktu spínače!

Do prostředí Thonny nakopírujeme následující kód:

Kód:

from machine import Pin, Timer
from time import sleep

led = Pin(13, Pin.OUT)
button = Pin(12, Pin.IN, Pin.PULL_UP)
counter = 0  # nastaveni pocitadla stisknuti
debounce_timer = None

def button_pressed(pin):
    global counter, debounce_timer # prevzeti globalni promenne

    if debounce_timer is None:
        counter += 1
        print('Tlacitko stisknuto! Pocet: ', counter)

        # zmena stavu LED
        led.value(not led.value())

        # Start casovace - preklenuti zakmitu kontaktu spinace (napr. 200 ms)
        debounce_timer = Timer(1)
        debounce_timer.init(mode=Timer.ONE_SHOT, period=200, callback=debounce_callback)

def debounce_callback(timer):
    global debounce_timer
    debounce_timer = None

# Pripojeni preruseni od tlacitka (nabezna hrana)
button.irq(trigger=Pin.IRQ_RISING, handler=button_pressed)

while True:
    print('Hlavni smycka bezi...')
    sleep(5)

POZN:
Pokud nechceme nic zapojovat, můžeme využít vestavěné LED a tlačítka modulu ESP32. Stačí si jen uvědomit, že vestavěná LED je připojena na pin 2 a vestavené tlačítko BOOT je na pinu 0 (druhé tlačítko je reset!). Stačí tedy v úvodu kódu jen změnit nastavení pinů pro LED a pro tlačítko, pak lze kód vyzkoušet jen s „holýmu“ modulem ESP32.
led = Pin(2, Pin.OUT)
button = Pin(0, Pin.IN, Pin.PULL_UP)

Jak kód funguje?

Tento příklad používá jednorázový časovač (debounce_timer), který se spouští při každém stisknutí tlačítka se zadanou periodou, v tomto případě 200 milisekund. Pokud stále získáváte falešné odezvy, můžete prodloužit nastavenou dobu zákmitů.

debounce_timer.init(mode=Timer.ONE_SHOT, period=200, callback=debounce_callback)

K detekci stisknutí tlačítka využijeme externí přerušení. Pokud dojde k uvolnění tlačítka po předchozím stisknutí (stav pinu se mění z LOW na HIGH), spustí se funkce button_pressed. Ke spuštění této funkce dochází při náběžné hraně, protože pin je ve výchozím nastavení nastaven jako Pin.PULL_UP, je tedy vnitřním pull-up rezistorem modulu ESP32 držen na výchozí hodnotě HIGH a stiknutím tlačítka je pin připojen k zemnění (tj. úroveň LOW).

button.irq(trigger=Pin.IRQ_RISING, handler=button_pressed)

Zavolaná funkce button_pressed zvýší čítací proměnnou counter, přepne stav LED a spustí jednorázový časovač pro zakmitání kontaktů.

counter += 1
print("Tlacitko stisknuto! Count: ", counter)

# zmena stavu LED
led.value(not led.value())

# Start casovace - preklenuti zakmitu kontaktu spinace (napr. 200 ms)
debounce_timer = Timer(1)
debounce_timer.init(mode=Timer.ONE_SHOT, period=200, callback=debounce_callback)

Funkce časovače debounce_callback se díky tomu zavolá, až po vypršení zadaného času (jednorázový časovač). Ve funkci debounce_callback se nastaví proměnná debounce_timer na hodnotu None.

def debounce_callback(timer):
global debounce_timer
debounce_timer = None

Pokud dojde k další změně stavu tlačítka a časovač ještě nevypršel, jde o zakmitání kontaktů – neměli bychom na to reagovat. To je zařízeno tím, že pokud je detekováno další stisknutí tlačítka, ale nebyla proměnná debounce_timer resetována na hodnotu None, nebude nic započítáno. To zaručuje podmínka, do které je celý počítací kód vnořen:

if debounce_timer is None:

Všimněme si, jak se ve funkcích deklaruje, že daná proměnná není lokální, ale jde o přístup ke globální proměnné. Kdybychom ve funkci volali proměnné (byť stejného jména jako je ta globální) a neřekli, že jde globální proměnnou, pracovali bychom s proměnnou, která by existovala jen v rámci dané funkce:

global counter, debounce_timer  # prevzeti globalni promenne

Použití None:

V Pythonu/MicroPythonu se None často používá jako zástupný symbol nebo výchozí hodnota k označení absence smysluplné hodnoty. V tomto příkladu nám None pomáhá řídit stav proměnné debounce_timer. Pokud je obsah None, není nastaven časovač – nedošlo ke stisku, nebo již proběhl potřebný čas ošetřující zákmity.

Jakmile ale běží časování, je obsahem proměnné debounce_timer objekt právě běžícího časovače – tedy není obsah None a nesmí se na případné změny stavu tlačítka ještě reagovat.

debounce_timer = Timer(1)
debounce_timer.init(mode=Timer.ONE_SHOT, period=200, callback=debounce_callback)

Když jednorázový časovač vyprší, funkce debounce_callback se zavolá a nastaví debounce_timer zpět na None, což znamená, že období debounce skončilo.

def debounce_callback(timer):
    global debounce_timer
    debounce_timer = None

Testování kódu

Spustíme kód na modulu ESP32. Stiskneme několikrát tlačítko. Pozorujeme, že nemáme žádné falešné stisky. Počet stisků odpovídá skutečné hodnotě, kolikrát bylo tlačítko stisknuto. Zároveň vidíme pravidelný „ohlas“ hlavní smyčky, která si běží nezávisle.

kod 3 - vystup
ESP32 - osetreni tlacitka
zdroj obrázku: https://randomnerdtutorials.com/micropython-timer-interrupts-ep32-esp8266/

Jak bylo již zmíněno, pokud i při našem ošetření tlačítka stále získáváme falešná stisknutí, je třeba prodloužit dobu ošetření zákmitů, například na dobu 300 ms.

debounce_timer.init(mode=Timer.ONE_SHOT, period=300, callback=debounce_callback)

Závěr

Doufáme, že Vás dnešní článek zaujal a přinesl pár užitečných informací. Naučili jsme se:

  • Co to je přerušení a jak funguje pod MicroPythonem.
  • Jak nastavit a pracovat s přerušením vyvolaným časovačem.
  • Jak nastavit pin jako zdroj externího přerušení a jak ošetřit toto přerušení ve svém kódu.
  • Jak lze využít oba způsoby přerušení v jednom projektu.

Text je kompilací volných překladů následujících článků:

Pozn.
Omlouvám se autorovi předešlých článlů, že při deklaraci pinů nepíši klíčové slovo mode= (např. led = Pin(2, mode=Pin.OUT)), ale používám zkrácenou formu (např. led = Pin(2, Pin.OUT)).
Ale s tím mode= to fakt nikdo „rozumný“ nepíše! 😉
Autor & -ka článku: M.P. & Maruš_Ka
UPOZORNĚNÍ:
Nesouhlasíme s vyřazením Newtonových zákonů, Ohmova zákona a zákona zachování energie z učiva fyziky základních škol v České republice!