Fyzikální kabinet FyzKAB
TechHobby ESP32 + MicroPython ESP32: Čidla a senzory v MicroPythonu IR vysílač: RGB žárovka a ESP32+MicroPython

IR vysílač: RGB žárovka a ESP32+MicroPython

27. díl „volného seriálu“ článků

V předchozím článku jsme se zaměřili na detekci signálů z IR (infračerveného) dálkového ovladače, což nám umožnilo ovládat modul ESP32 pomocí tohoto ovladače. Dnes se podíváme na zcela opačný proces: jak modul ESP32 může plnit funkci dálkového ovladače a ovládat jiné zařízení prostřednictvím IR signálů.

IR dálkové ovladače dnes najdeme v mnoha domácích spotřebičích – od RGB LED pásků, televizi, Hi-Fi soupravy, rolety až po klimatizační jednotky. Když se naučíme generovat IR příkazy, otevře se nám možnost modulem ESP32 tyto přístroje ovládat. Představme si třeba, že připojíme modul ESP32 k internetu a prostřednictvím webové stránky budeme moci zadávat modulu ESP32 příkazy (To jsme již v předchozích článcích zvládli!). Tyto příkazy pak modul ESP32 přetvoří na infračervené signály, které pošle do zařízení ve svém dosahu. Tím si snadno vybudujeme základní inteligentní domácnost – a to dokonce z přístrojů, které nejsou přímo připojitelné k internetu! Navíc to vše zvládneme s minimálními náklady. Kdo by se nechtěl pustit do takového „projektu“ v rámci svého domácího bastlení? 😊

Jako ukázku vysílání IR signálů dnes postavíme projekt, ve kterém budeme ovládat dálkově řízenou RGB žárovku. RGB žárovka slouží jako modelové zařízení pro ovládání pomocí IR. Lze si však představit jakýkoli jiný přístroj, který budeme moci ovládat obdobným způsobem. Dálkový ovladač k RGB žárovce má 24 tlačítek pro nastavení různých režimů osvětlení, a naším cílem bude je všechny napodobit. Úspěch našich pokusů okamžitě ověříme reakcí žárovky, která bude měnit barvu nebo režim svého svitu.

Volba na RGB žárovku také padla proto, že se jedná o cenově dostupné zařízení, což z něj činí ideální volbu pro naše experimenty. Na obrázku č. 1 vidíte sestavu RGB žárovky a jejího ovladače.

RGB zarovka
Obrázek č. 1 – Sestava RGB žárovky a jejího IR dálkového ovladače

IR vysílač

V minulém článku jsme použili již hotový přijímací modul KY-022, ale pro vysílání IR signálů si budeme muset vlastní vysílací modul postavit! Naštěstí v setu infračerveného ovládání pro Arduino, který jsme využívali dříve, byla součástí také IR LED dioda, kterou teď použijeme.

První myšlenka, která nás asi napadne, je jednoduše připojit IR LED k některému GPIO pinu modulu ESP32 přes vhodný rezistor a začít. Koneckonců, blikání LED už jsme dělali několikrát, tak proč by nemohla blikat nyní i IR dioda? Teoreticky by to fungovalo, ale dosah takového „dálkového“ ovladače by byl velmi omezený. Jak jsme si kdysi na začátku našeho seriálu věnovanému MicroPythonu na modulu ESP32 říkali o GPIO pinech, jejich výstupní proud je spíše signálový než silový. To znamená, že GPIO piny nejsou určeny pro kdovíjaké proudy, a tak bychom je případně neměli přetěžovat.

Tím, že IR dioda pracuje v pulzním režimu, může přijímat okamžité proudy výrazně vyšší než běžná LED při trvalém svitu. Pro naše počáteční zkoušky budeme počítat s proudem kolem 50 mA, což už je pro GPIO pin modulu ESP32 proud příliš vysoký. Aby naše IR dálkové ovládání mělo rozumný dosah, a zároveň jsme neohrozili modul ESP32 přetížením GPIO pinu, vytvoříme jednoduchý obvod pro řízení IR LED diody.

Obvod pro IR LED

Abychom předešli přetěžování výstupního GPIO pinu modulu ESP32, využijeme pro napájení IR LED dostupné napětí +5V z modulu ESP32 a GPIO pin bude sloužit pouze pro spínání řídicího obvodu. Pro řízení IR LED tedy použijeme následující jednoduché zapojení.

schema-vysilace
Obrázek č. 2 – schéma řídicího obvodu IR LED

Infračervená LED se sériově připojeným rezistorem R2 je připojena mezi piny +5V a GND, přičemž obvod je spínán pomocí NPN tranzistoru. Tranzistor se sepne při přivedení proudu na svou bázi. Tento bázový proud bude dodávat řídicí GPIO pin (nastaven jako digitální výstup). Tím dosáhneme možnosti ovládat proud přes IR LED pomocí GPIO pinu.

Velikost odporu R2 určíme pomocí Ohmova zákona podle požadovaného proudu přes IR LED. Pro naše počáteční zkoušky zvolíme proud přibližně 50 mA. Napětí U2 na rezistoru R2 získáme odečtením napětí na IR LED (typicky 1,2–1,4 V) a napětí na sepnutém tranzistoru (0,7 V) od použitého napájecího napětí 5 V.

Hodnotu odporu rezistoru R2 získáme dle vzorce (1):

R 2 = U 2 I IR LED = 5  V 1,2  V 0,7  V 0,05  A = 3,1 0,05 = 62  Ω 68 R

(1)

kde jsme výsledný odpor zaokrouhlili na nejbližší hodnotu ve standardně používané řadě E12 nebo E24 hodnot elektronických součástek.

Dále musíme určit hodnotu odporu R1, který určuje proud bází tranzistoru. Zde je důležité zohlednit proudový zesilovací činitel použitého tranzistoru. V našem schématu sice používáme tranzistor BC337, ale můžete použít téměř jakýkoli jiný NPN tranzistor (například BC370, 2N2222A nebo i BC547). Hlavním parametrem je přípustný kolektorový proud tranzistorem, který by měl být alespoň dvakrát vyšší než požadovaný proud přes IR LED. Je také důležité pamatovat na to, že IR LED při odesílání modulovaného signálu „bliká“ s frekvencí 38 kHz, takže tranzistor by měl mít dostatečný frekvenční rozsah.

Aby tranzistor pracoval v saturaci a fungoval jako spínač, musíme zvolit vhodný bázový proud, který zaručí dostatečný kolektorový proud. Pokud bude zesilovací činitel tranzistoru (hFE) přibližně 200, pro bezpečnostní rezervu použijeme hodnotu hFE ≈ 20. Požadovaný bázový proud získáme dle vzorce (2):

I B = I C h FE = 50  mA 20 = 2,5  mA

(2)

Napětí na GPIO pinu modulu ESP32 je 3,3 V a napětí na PN přechodu báze-emitor tranzistoru je přibližně 0,7 V, to musíme zohlednit při výpočtu odporu R1. Velikost odporu spočítáme podle následujícího vzorce (3):

R 1 = U 1 I B = 3,3  V 0,7  V 0,0025  A = 2,6 0,0025 = 1040  Ω 1 k

(3)

kde jsme opět výsledný odpor zaokrouhlili na nejbližší hodnotu ve standardně používané řadě E12 nebo E24 hodnot elektronických součástek.

Pokud bychom použili vyšší odpor, například až 2 kΩ, bázový proud by byl o něco menší, což by stále mohlo stačit pro spolehlivé sepnutí tranzistoru. Nicméně, pokud by byl zisk tranzistoru nižší než očekávaných 200, rezerva pro kolektorový proud by byla menší.

Poznámka:
Pokud byste chtěli vytvořit korektní obvod pro napájení IR diody s konstantním proudem, doporučujeme se podívat na článek: Zdroj konstantního proudu (nejen) pro LED, kde najdete alternativní zapojení, která jsou pro napájení LED vhodnější, protože poskytují stabilní proud bez ohledu na podmínky.

Modul IR vysílače

Jednou z hlavních výhod navrhovaného zapojení pro IR LED je jeho jednoduchost a snadná realizace. Tento obvod nevyžaduje složité komponenty ani náročné pájení, to jej činí ideálním pro experimentování a prototypování. Použití dostupných a snadno sehnatelných komponent činí toto zapojení vhodné nejen pro základní pokusy, ale i pro složitější projekty s IR komunikací. I při minimálním počtu součástek nabízí dostatečnou spolehlivost a výkon. Tato jednoduchost tedy není na úkor výkonu – naopak, představuje jednu z hlavních předností tohoto návrhu. Díky své jednoduchosti navíc zapojení přispívá k nízkým nákladům, což je výhodné zejména pro „bastlířské“ projekty, kde je cena často klíčovým faktorem. Další velkou výhodou je, že zapojení lze snadno sestavit například na nepájivém kontaktním poli (breadboardu), což umožňuje rychlé testování a úpravy. Takový přístup je ideální pro ověřování funkčnosti obvodu v raných fázích vývoje.

Pro skutečnou implementaci obvodu do reálného zařízení jsme se rozhodli přejít od dočasného sestavení na nepájivém poli k trvalejší verzi na univerzální desce plošných spojů (protoboard). Na následujícím obrázku můžete vidět, jak vypadá realizace tohoto zapojení.

IR-vysilac
Obrázek č. 3 – realizace modulu IR vysílače na univerzální desce plošných spojů

Realizace na desce plošných spojů nejen usnadňuje stavbu, ale především i zajišťuje větší spolehlivost. V případě potřeby diagnostiky problémů během testování je možnost spolehnutí se na kvalitu provedení zapojení docela klíčová.

Připojení IR vysílače k modulu ESP32

Jakmile máme hotový modul IR vysílače – ať už sestavený na nepájivém poli pro rychlé testování, nebo v trvalejší podobě na plošném spoji – přichází na řadu jeho připojení k modulu ESP32. Tento krok je zásadní pro umožnění komunikace mezi ESP32 a naším IR vysílačem.

Modul IR vysílače bude napájen napětím 5 V, což je výhodné, protože na ESP32 je napětí 5 V přímo z USB, zatímco 3,3 V napětí je generováno pomocí stabilizátoru na desce. Použitím 5 V napájení pro IR vysílač se vyhneme zatěžování tohoto stabilizátoru, který je primárně určen pro napájení dalších částí systému, jako jsou GPIO piny nebo samotný procesor. I když odběr proudu pro IR LED není vysoký (cca 50 mA), není rozumné zbytečně zatěžovat stabilizátor, když máme k dispozici jinou možnost napájení.

Co se týče signálového (řídicího) pinu, můžeme pro ovládání IR vysílače využít libovolný GPIO pin, který podporuje digitální výstup. V naší ukázce jsme zvolili GPIO23, který je dostupný na ESP32 a je dostatečně vhodný pro tuto funkci. Tento pin bude sloužit k přenášení řídícího signálu pro zapnutí a vypnutí IR LED diody, tedy k modulaci signálu podle požadovaného formátu komunikace (např. 38 kHz pro standardní IR dálkové ovladače).

Pro lepší přehlednost je schéma připojení modulu IR vysílače k modulu ESP32 znázorněno na obrázku č. 4. Detaily propojení jednotlivých pinů jsou shrnuty v následující tabulce, která ukazuje, jaké konkrétní piny na ESP32 slouží k napájení a jaký GPIO pin je použit pro řízení IR vysílače.

vysilac zapojeni
Obrázek č. 4 – připojení modulu IR vysílače k modulu ESP32
ESP32 IR vysílač Pozn.
5V +5V Připojení k rezistoru 68R
GPIO23 S Připojení k rezistoru 1k
GND GND Připojení na emitor NPN tranzistoru

Program pro vysílání

Hardwarová část je hotová, takže se teď pustíme do softwaru. Naším cílem bude vytvořit jednoduchý program, který bude vysílat IR signály pro ovládání RGB žárovky. Jako základ použijeme protokol NEC, o kterém jsme se už zmiňovali v předchozím článku (článek: IR přijímač KY-022 s ESP32 a MicroPythonem).

Naším prvním krokem bude naučit se, jak takový IR signál vůbec vyslat. Krátce se podíváme na to, jak NEC protokol funguje, naznačíme si, jak by bylo možné odesílat IR signály „ručně“.

Ale jak uvidíme dále, nakonec se spíše pokusíme program vyřešit tak, abychom tyto věci vůbec řešit nemuseli. 😉

NEC protokol

Protokol NEC používá IR přenos založený na impulzním (distančním) kódování. Nosná frekvence, na které „blikají“ signály, je nastavena na 38,222 kHz, přičemž doporučený pracovní cyklus (tzv. střída) je 1/3 – viz obrázek 5.

NEC bliknuti
Obrázek č. 5 – časování „bliknutí“ IR LED (dílčí pulz dávky)

Každý bit je reprezentován dávkou pulzů trvající 562,5 μs, po které následuje pauza. Délka této pauzy pak určuje, jestli jde o logickou nulu nebo jedničku (viz obrázek č. 6):

  • Logická 0: 562,5 μs pulz + 562,5 μs pauza
  • Logická 1: 562,5 μs pulz + 1675 μs pauza
protokol NEC 0-1
Obrázek č. 6 – ukázka kódování logické „0“ a logické „1“ v protokolu NEC

Když stiskneme tlačítko na IR ovladači, vyšle se konkrétní sekvence:

  • 9 ms dlouhý úvodní signál (což odpovídá 16 „běžným“ pulzům)
  • 4,5 ms pauza
  • 8bitová adresa zařízení
  • 8bitová inverze adresy
  • 8bitový příkaz
  • 8bitová inverze příkazu
  • a nakonec krátký 562,5 μs pulz jako zakončení přenosu.

Adresu i příkaz zařízení posílá dvakrát – jednou normálně, podruhé inverzně. Díky tomu si přijímač může ověřit, že data přišla správně – viz obrázek 6. Bity se posílají od nejméně významného (LSB).

NEC protocol
Obrázek č. 6 – odeslání celé sekvence NEC protokolu po stisknutí tlačítka

Odeslání příkazu pomocí IR

A teď to nejzajímavější – jak takový signál vytvořit na modulu ESP32? Pokud se podíváme na přesné časování, zjistíme, že budeme potřebovat ovládat výstupní úrovně s přesností kolem 8,5 μs. To už je poměrně náročné, zejména při použití MicroPythonu.

ESP32 sice zvládne časování v mikrosekundách, ale není to úplně real-time zařízení. Funkce utime.sleep_us() sice umí čekat v mikrosekundách, ale s přesností ±1–2 μs. Pro potřeby signálu, který má např. 8,5 μs HIGH a 18 μs LOW, to může (ale nemusí) být dostačující – záleží na konkrétním případu a přijímači.

Modul RMT

Pro generování přesných mikrosekundových pulzů si nevystačíme s běžným programovým zpožděním. Naštěstí ESP32 disponuje specializovanou hardwarovou periferií, která je pro takové účely jako stvořená – modul RMT (Remote Control Module). Původně byl navržen pro práci s IR dálkovými ovladači, ale díky své přesnosti nachází uplatnění i v celé řadě dalších aplikací.

Apropos, co to teď zrovna potřebujeme? Přesně tak – vysílat IR signály! 😊

Modul RMT se velmi dobře hodí například pro:

  • Mikrosekundové pulzy (jako třeba pro naše vysílání: 8,5 μs HIGH + 18 μs LOW),
  • řízení RGB LED diod typu NeoPixel,
  • nebo vytváření přesných signálů podobných PWM, které přitom nezatěžují procesor.

Použití RMT v MicroPythonu je poměrně přímočaré, ale nelze říct, že by bylo zcela bez komplikací. Ve skutečnosti se jedná o téma natolik obsáhlé, že by si zasloužilo samostatný článek (který třeba jednou vznikne).

Je také třeba přiznat, že dosud se nám pomocí přímého přístupu (byť s využitím výkonného RMT modulu) nepodařilo IR signál odeslat tak, aby byl komerčním zařízením úspěšně detekován. 😒

Proto se nabízí otázka: Proč složitě vymýšlet vlastní řešení, když existuje hotové a funkční? Navíc v referenční příručce MicroPythonu pro modul ESP32 je stále kategorie metod a funkcí týkající se modulu RMT stále vedena jako experimentální – může se tedy stát, že v dalším buildu se něco může (nebo nemusí) změnit.

Nastal tedy vhodný okamžik opustit nízkoúrovňové experimentování a sáhnout po knihovně ir_tx.py, která celou situaci výrazně zjednodušuje!

Knihovna ir_tx.py

Knihovnu pro příjem IR signálů (ir_rx.py) jsme si představili v minulém článku, nyní se tedy budeme věnovat knihovně ir_tx.py, která za nás vyřeší všechny výše uvedené patálie s vysláním IR signálu. Knihovna se bohužel nedá přímo nainstalovat ve správci „balíků“ v prostředí Thonny IDE, takže pro ni musíme vyrazit na GitHub autora Petra Hinche.

Určitý problém knihovny ir_tx.py je v její členitosti a množství souborů, ze kterých se skládá. Dovoli jsme si pro tento článek (podobně jako u knihovny ir_rx.py) vytvořit jednosouborovou verzi. Koneckonců k tomuto kroku nás inspiroval George Bantique v článku MicroPython TechNotes: Infrared Transmitter, který to vyřešil stejně ve svém článku.

Poznámka:
V případě knihovny George Bantiqua si ale dejte pozor, neboť v MicroPythonu došlo ke změně příkazů kolem modulu RMT, takže knihovna (©2020 Peter Hinch) uvedená na stránkách techtotinker.com hlásí chybu.

Následující kód je tedy odlehčená knihovna ir_tx.py. Pochopitelně, kdo chce do svého projektu nainstalovat celou strukturu knihovny ir_tx.py Petra Hinche, jistě neudělá chybu a nechť využije výše uvedený odkaz na jeho GitHub. V případě naší jednosouborové knihovny stačí níže uvedený kód nakopírovat do prostředí Thonny IDE a uložit do složky lib na modulu ESP32 s MicroPythonem. (Dále uvedený kód programu MAGLAJS by měl fungovat s oběma knihovnami.)

Kód knihovny ir_tx.py:

# Author: Peter Hinch
# modified by FyzKAB (2025)
# Copyright Peter Hinch 2020-2021 Released under the MIT license
# http://github.com/peterhinch/micropython_ir

# __init__.py Nonblocking IR blaster
# Runs on Pyboard D or Pyboard 1.x (not Pyboard Lite), ESP32 and RP2

# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2020-2021 Peter Hinch

from sys import platform
ESP32 = platform == 'esp32'   # Loboris not supported owing to RMT
RP2 = platform == 'rp2'
if ESP32:
    from machine import Pin, PWM
    from esp32 import RMT
elif RP2:
    from .rp2_rmt import RP2_RMT
else:
    from pyb import Pin, Timer   # Pyboard does not support machine.PWM

from micropython import const
from array import array
from time import ticks_us, ticks_diff, sleep_ms
# import micropython
# micropython.alloc_emergency_exception_buf(100)

# Shared by NEC
STOP = const(0) # End of data

# IR abstract base class. Array holds periods in μs between toggling 36/38KHz
# carrier on or off. Physical transmission occurs in an ISR context controlled
# by timer 2 and timer 5. See TRANSMITTER.md for details of operation.
class IR:
    _active_high = True   # Hardware turns IRLED on if pin goes high.
    _space = 0   # Duty ratio that causes IRLED to be off
    timeit = False   # Print timing info

    @classmethod
    def active_low(cls):
        if ESP32:
            raise ValueError('Cannot set active low on ESP32')
        cls._active_high = False
        cls._space = 100

    def __init__(self, pin, cfreq, asize, duty, verbose):
        if ESP32:
            self._rmt = RMT(0, pin=pin, clock_div=80, tx_carrier = (cfreq, duty, 1))
            # 1μs resolution
        elif RP2: # PIO-based RMT-like device
            self._rmt = RP2_RMT(pin_pulse=None, carrier=(pin, cfreq, duty))   # 1μs resolution
            asize += 1   # Allow for possible extra space pulse
        else:   # Pyboard
            if not IR._active_high:
                duty = 100 - duty
            tim = Timer(2, freq=cfreq)   # Timer 2/pin produces 36/38/40KHz carrier
            self._ch = tim.channel(1, Timer.PWM, pin=pin)
            self._ch.pulse_width_percent(self._space)   # Turn off IR LED
            # Pyboard: 0 <= pulse_width_percent <= 100
            self._duty = duty
            self._tim = Timer(5)   # Timer 5 controls carrier on/off times
        self._tcb = self._cb   # Pre-allocate
        self._arr = array('H', 0 for _ in range(asize))   # on/off times (μs)
        self._mva = memoryview(self._arr)
        # Subclass interface
        self.verbose = verbose
        self.carrier = False   # Notional carrier state while encoding biphase
        self.aptr = 0   # Index into array
        self._busy = False

    def _cb(self, t): # T5 callback, generate a carrier mark or space
        self._busy = True
        t.deinit()
        p = self.aptr
        v = self._arr[p]
        if v == STOP:
            self._ch.pulse_width_percent(self._space)   # Turn off IR LED.
            self._busy = False
            return
        self._ch.pulse_width_percent(self._space if p & 1 else self._duty)
        self._tim.init(prescaler=84, period=v, callback=self._tcb)
        self.aptr += 1

    def busy(self):
        if ESP32:
            return not self._rmt.wait_done()
        if RP2:
            return self._rmt.busy()
        return self._busy

    # Public interface
    # Before populating array, zero pointer, set notional carrier state (off).
    def transmit(self, addr, data, toggle=0, validate=False):   # NEC: toggle is unused
        while self.busy():
            pass
        t = ticks_us()
        if validate:
            if addr > self.valid[0] or addr < 0:
                raise ValueError('Address out of range', addr)
            if data > self.valid[1] or data < 0:
                raise ValueError('Data out of range', data)
            if toggle > self.valid[2] or toggle < 0:
                raise ValueError('Toggle out of range', toggle)
        self.aptr = 0   # Inital conditions for tx: index into array
        self.carrier = False
        self.tx(addr, data, toggle)   # Subclass populates ._arr
        self.trigger() # Initiate transmission
        if self.timeit:
            dt = ticks_diff(ticks_us(), t)
            print('Time = {}μs'.format(dt))
        sleep_ms(1)   # Ensure ._busy is set prior to return

    # Subclass interface
    def trigger(self):   # Used by NEC to initiate a repeat frame
        if ESP32:
            self._rmt.write_pulses(tuple(self._mva[0 : self.aptr]))
        elif RP2:
            self.append(STOP)
            self._rmt.send(self._arr)
        else:
            self.append(STOP)
            self.aptr = 0 # Reset pointer
            self._cb(self._tim)   # Initiate physical transmission.

    def append(self, *times):   # Append one or more time peiods to ._arr
        for t in times:
            self._arr[self.aptr] = t
            self.aptr += 1
            self.carrier = not self.carrier   # Keep track of carrier state
            self.verbose and print('append', t, 'carrier', self.carrier)

    def add(self, t):   # Increase last time value (for biphase)
        assert t > 0
        self.verbose and print('add', t)
        # .carrier unaffected
        self._arr[self.aptr - 1] += t

# Given an iterable (e.g. list or tuple) of times, emit it as an IR stream.
class Player(IR):

    def __init__(self, pin, freq=38000, verbose=False, asize=68):   # NEC specifies 38KHz
        super().__init__(pin, freq, asize, 33, verbose)   # Measured duty ratio 33%

    def play(self, lst):
        for x, t in enumerate(lst):
            self._arr[x] = t
        self.aptr = x + 1
        self.trigger()

_TBURST = const(563)
_T_ONE = const(1687)

class NEC(IR):
    valid = (0xffff, 0xff, 0)   # Max addr, data, toggle
    samsung = False

    def __init__(self, pin, freq=38000, verbose=False):   # NEC specifies 38KHz also Samsung
        super().__init__(pin, freq, 68, 33, verbose)   # Measured duty ratio 33%

    def _bit(self, b):
        self.append(_TBURST, _T_ONE if b else _TBURST)

    def tx(self, addr, data, _): # Ignore toggle
        if self.samsung:
            self.append(4500, 4500)
        else:
            self.append(9000, 4500)
        if addr < 256:   # Short address: append complement
            if self.samsung:
            addr |= addr << 8
            else:
            addr |= ((addr ^ 0xff) << 8)
        for _ in range(16):
            self._bit(addr & 1)
            addr >>= 1
        data |= ((data ^ 0xff) << 8)
        for _ in range(16):
            self._bit(data & 1)
            data >>= 1
        self.append(_TBURST)

    def repeat(self):
        self.aptr = 0
        self.append(9000, 2250, _TBURST)
        self.trigger()   # Initiate physical transmission.

Program „MAGLAJS“

Pokud nás předešlé povídání o „vyblikávání“ modulovaného signálu a celém tom protokolu NEC vyděsilo, tak použití knihovny ir_tx.py nás zcela vrátí do „těžké pohody“. V tuto chvíli je pro nás důležitá třída NEC a pak metoda transmit(addr, code). A to je opravdu vše!

Stačí tedy vzít naši dálkově ovládatelnou RGB žárovku, modul ESP32, výše popsaný vysílací modul s IR LED a můžeme začít experimentovat!

experimentalni sestava
Obrázek č. 7 – „Experimentální sestava“ našich IR dálkových RGB hrátek

Ale pojďme na to postupně.

Importem třídy NEC z knihovny ir_tx.py získáme možnost vytvoření objektu pro odesílání příkazů přes zadaný pin:

from ir_tx import NEC

nec = NEC(Pin(23, Pin.OUT, value=0))

Vytvořený objekt nec nám nyní umožňuje na pinu GPIO23 (nastaveném na výstup a výchozí výstupní hodnotu LOW) odesílat kompletní zprávy v protokolu NEC. Odeslaná data se budou skládat z adresy a příkazu (data) – například takto:

nec.transmit(0x0000, 0x0d)

Knihovna ir_tx.py pracuje s protokolem NEC16, takže adresa je odesílána v 16-bitové podobě. Ale jelikož stejně budeme odesílat adresu 0, tak je to vlastně jedno. To jen, kdyby někdo analyzoval odeslaná data a v druhém byte neviděl avízovanou kontrolní inverzi adresy z prvního byte.

Pro ovládání naší RGB žárovky (ale i jakéhokoliv jiného IR řiditelného zařízení) potřebujeme znát kódy jednotlivých tlačítek, které pak budeme odesílat. Nejlepší metoda je, nejdříve použít IR přijímač, načíst odeslaná data z původního IR ovladače a uložit si je do nějakého seznamu (slovníku, pole).

V našem případě jsme načetli kódy následujícího dálkového ovladače RGB žárovky:

IR ovladac
Obrázek č. 8 – dálkový ovladač RGB žárovky a funkce jednotlivých tlačítek

Získané kódy (příkazy) IR ovladače si uložíme do slovníku nazvaného ir_key. Klíčem pole je kód příkazu, hodnotou pak textová podoba názvu tlačítka.

ir_key = {
    0x0f: 'RUZOVA',
    0x0e: 'STARORUZ.',
    0x04: 'FIALOVA',
    0x16: 'MODROF.',
    0x14: 'BLANKYT',
    0x1e: 'TYRKYS',
    0x4c: 'MODRA SV.',
    0x12: 'ZELENA SV.',
    0x1c: 'ZLUTA',
    0x0a: 'OKROVA',
    0x40: 'ORANZ.',
    0x17: 'CERVENA SV.',
    0x0c: 'SMOOTH',
    0x1a: 'FADE',
    0x4d: 'FLASH',
    0x15: 'WHITE',
    0x11: 'BLUE',
    0x1b: 'GREEN',
    0x19: 'RED',
    0x0d: 'ON',
    0x1f: 'OFF',
    0x1d: 'sniz int.',
    0x09: 'zvys int.'
}

Máme-li získané příkazy odpovídající ovládacím tlačítkům, můžeme IR přijímač od modulu ESP32 odpojit a vrátit se zpět k vysílání.

V následujícím programu nazvaném MAGLAJS budeme ze slovníku příkazů postupně náhodně načítat položky a v pravidelných časových intervalech (5 s) odesílat. RGB řárovka, která bude v dosahu IR signálu, pak bude náhodně měnit barvu nebo spouštět různé režimy svého blikání. Zkrátka to bude takový „MAGLAJS“! 😊

Na výstup se bude vypisovat kód odeslaného příkazu a jeho textová podoba. Ve chvíli, kdy odešleme příkaz odpovídající zhasnutí žárovky, vypíšeme hlásku: „A uz toho bylo dost!“ a program se ukončí.

Program je ve své podstatě jednoduchý, takže si jej zde uvedeme rovnou celý a pod jeho kódem si spíše jen ukážeme část, která je z hlediska našeho seznámení s Pythonem aspoň trochu inovativní.

Kód programu „IR-odesílaní-MAGLAJS“

from machine import Pin
from ir_tx import NEC
import time
import random

nec = NEC(Pin(23, Pin.OUT, value=0))

ir_key = {
    0x0f: 'RUZOVA',
    0x0e: 'STARORUZ.',
    0x04: 'FIALOVA',
    0x16: 'MODROF.',
    0x14: 'BLANKYT',
    0x1e: 'TYRKYS',
    0x4c: 'MODRA SV.',
    0x12: 'ZELENA SV.',
    0x1c: 'ZLUTA',
    0x0a: 'OKROVA',
    0x40: 'ORANZ.',
    0x17: 'CERVENA SV.',
    0x0c: 'SMOOTH',
    0x1a: 'FADE',
    0x4d: 'FLASH',
    0x15: 'WHITE',
    0x11: 'BLUE',
    0x1b: 'GREEN',
    0x19: 'RED',
    0x0d: 'ON',
    0x1f: 'OFF',
    0x1d: 'sniz int.',
    0x09: 'zvys int.'
}

nec.transmit(0x0000, 0x0d)   # nejdriv to rozsvitime

konec = False

while not konec:
    code, desc = random.choice(list(ir_key.items()))
    print(f"Odesílám IR kód: 0x{code:02x} – {desc}")
    nec.transmit(0x0000, code)
    if desc == "OFF":
        konec = True

    time.sleep(5)

print("A uz toho bylo dost!")

Vysvětlení výrazu: code, desc = random.choice(list(ir_key.items()))

Přestože je kód programu poměrně jednoduchý, může být překvapivou strukturou ve výše uvedeném programu následující řádek kódu:

code, desc = random.choice(list(ir_key.items()))

kde proměnná ir_key má tuto strukturu:

ir_key = {
    0x0f: 'RUZOVA',
    0x0e: 'STARORUZ.',
    # ...
    0x09: 'zvys int.'
}

Běžně bychom mohli generovat náhodné číslo jako index pole a podle něj vybírat položku. V tomto případě to ale není možné kvůli specifické struktuře slovníku (ir_key). Každý záznam obsahuje klíč (např. 0x0f) a k němu přiřazenou hodnotu (např. 'RUZOVA').

Funkce .items() vrací tzv. view objekt, který obsahuje dvojice (klíč, hodnota). Například pokud bychom tento výsledek uložili do proměnné dict_items, vypadala by takto:

dict_items([
    (0x0f, 'RUZOVA'),
    (0x0e, 'STARORUZ.'),
    ...
    (0x09, 'zvys int.')
])

Tento objekt ale není seznam, a proto ho nelze přímo použít s funkcí random.choice(), která vyžaduje sekvenci (např. seznam nebo řetězec). Proto je nutné tento objekt převést na seznam:

seznam_paru = list(ir_key.items())

Tím získáme klasický seznam dvojic:

[
    (0x0f, 'RUZOVA'),
    (0x0e, 'STARORUZ.'),
    ...
    (0x09, 'zvys int.')
]

Nyní již lze pro výběr náhodné položky bezpečně použít:

random.choice(seznam_paru)

Tento výraz náhodně vybere jednu dvojici, například (0x1b, 'GREEN'), tedy náhodně zvolený kód a jeho popis.

Poznámka:
Alternativou předchozí konstrukce by mohlo být:
import random

items = list(ir_key.items())                 # převedeme na seznam dvojic
index = random.randint(0, len(items) - 1)    # náhodný index
code, desc = items[index]                    # rozbalíme dvojici

Po připojení IR vysílače k modulu ESP32 můžeme program MAGLAJS buď nahrát přímo do modulu ESP32, nebo jej spustit v REPLu prostředí Thonny IDE. Pro první otestování je obvykle jednodušší ta druhá možnost.

Pokud je modul ESP32 připojen k počítači s otevřeným prostředím Thonny IDE, můžeme kromě samotného blikání žárovky sledovat i výstupní hlášky v jeho výstupním okně. Po spuštění programu by měla RGB žárovka začít blikat v různých barevných kombinacích a ve výstupním okně Thonny IDE bychom měli vidět podobný výstup, jako je uvedený na obrázku č. 9.

vystup Thonny-IDE
Obrázek č. 9 – dálkový ovladač RGB žárovky a funkce jednotlivých tlačítek

Pokud vše funguje, jak jsme zamýšleli, podařilo se nám vyslat IR signály a ovládat jimi reálná zařízení – to už je krok od testování k reálnému použití.

Závěr

Možná jste zatím jen rozblikali žárovku, ale stejně snadno můžete zkusit ovládat třeba větrák, světla, nebo jiné spotřebiče s IR přijímačem. Kombinace ESP32 a IR vysílače se tak může stát, jak už jsme asi několikrát zmínili, základem pro vlastní domácí automatizaci. V předchozím článku jsme se věnovali IR příjmu – nyní máte v rukou i schopnost vysílat.

A právě v tom spočívá kouzlo: můžete tvořit vlastní „dálkové ovladače“, přizpůsobené přesně tomu, co vás napadne. Zkuste si pohrát, objevovat a stavět – třeba tím položíte základy vlastního chytrého systému, ušitého na míru vašim nápadům a potřebám.

Pokračování: Připravujeme…
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!