IR vysílač: RGB žárovka a ESP32+MicroPython
Aktualizováno: 10. 6. 2025
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 kupříkladu, ž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.

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í.

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):
(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):
(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):
(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í.

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.

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.

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

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).

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
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 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ý možná jednou vznikne).
Je také dobré 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_
) jsme si představili v minulém článku, nyní se tedy budeme věnovat knihovně ir_
, 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_
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_
) 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_
. Pochopitelně, kdo chce do svého projektu nainstalovat celou strukturu knihovny ir_
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.)
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_
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(
.
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!

Ale pojďme na to postupně.
Importem třídy NEC z knihovny ir_
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:

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í.
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.

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í.
Program „Wi-Fi žárovka“
V úvodu článku jsme si nastínili možnost internetového řízení pomocí modulu ESP32 a jeho rozesílání IR povelů do svého okolí. Zkusíme si tedy ukázat, jak touto metodou ovládat naši RGB žárovku přes Wi-Fi – a to přímo z mobilu. Jelikož jsme s Wi-Fi na modulu ESP32 již pracovali (viz články: ESP32: Wi-Fi v MicroPythonu), můžeme se do toho hned pustit!
Tentokrát však nebudeme modul připojovat k internetu. Místo toho využijeme schopnost ESP32 vytvořit vlastní Wi-Fi síť, ke které se připojíme telefonem nebo tabletem. Tato lokální síť zpravidla pohodlně pokryje celou domácnost (pokud tedy nebydlíte v rozsáhlem sídle typu na zámek Hluboká 😉). Díky tomu budeme moct ovládat RGB žárovku odkudkoli z domova.
- Poznámka:
- Pokud byste chtěli žárovku ovládat i mimo domov – z práce, z dovolené… – museli byste ESP32 připojit k internetu v režimu stanice (STA) a nastavit přístup „zvenčí“ přes váš router. To ale může být technicky náročnější a ne každý domácí router to podporuje.
Než se vrhneme na samotný kód, pojďme si stručně říct, co přesně bude náš projekt dělat:
- Vytvoří vlastní Wi-Fi síť (Access Point) – nazveme ji „Wi-Fi žárovka“ a nastavíme heslo „blik-blik“.
- Spustí jednoduchý webový server, který bude nabízet ovládací stránku s tlačítky.
- Zpracuje kliknutí na tlačítka na webové stránce – každé tlačítko bude představovat jiný příkaz pro žárovku.
- Pošle odpovídající IR signál do žárovky podle vybraného tlačítka.
To je docela dost úkolů, ale nebojte – nebudeme vynalézat kolo. Využijeme existující MicroPython moduly, které nám práci výrazně usnadní. Například:
Microdot
pro vytvoření jednoduchého webového serveru, (nainstalujeme pomocí správce „balíků“ přímo v prostředí Thonny IDE – viz obr. 10)ir_tx
pro odesílání IR příkazů.

microdot
pomocí správce „balíků“ prostředí Thonny IDENyní se tedy podíváme na celý program, který propojuje všechny popsané funkce – od vytvoření vlastní Wi-Fi sítě až po odesílání IR signálů podle kliknutí na webové stránce.
Kód si nejprve představíme vcelku, abychom viděli, jak do sebe jednotlivé části zapadají. Poté si ho postupně rozebereme a vysvětlíme si, co přesně jednotlivé části dělají a proč tam jsou.
import network
from microdot import Microdot, Response
from ir_tx import NEC
from machine import Pin
import time
# Nastavení IR vysílače na pin 23
ir_led = Pin(23, Pin.OUT, value=0)
nec = NEC(ir_led)
# Vytvoření Access Pointu
ap = network.WLAN(network.AP_IF)
ap.active(True)
ap.config(essid="Wi-Fi-zarovka", password="blik-blik", authmode=3)
# Čekání na aktivaci
while not ap.active():
time.sleep(1)
print('AP vytvořen na IP:', ap.ifconfig()[0])
# Nastavení Microdot serveru
app = Microdot()
Response.default_content_type = 'text/html'
# Definice kódů příkazů jako seznam tuple
IR_CODES = [
(0x0d, 'on'),
(0x1f, 'off'),
(0x15, 'white'),
(0x19, 'red'),
(0x1b, 'green'),
(0x11, 'blue'),
(0x1d, 'sniz_int'),
(0x09, 'zvys_int'),
(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')
]
# Generování HTML tlačítek pro každý příkaz a vložení do webové stránky
buttons_html = ""
for code, name in IR_CODES:
formatted_name = name.replace("_", " ") # Nahradí podtržítka mezerami
buttons_html += f'<button onclick="sendCommand(\'{name}\')">{formatted_name}</button>\n'
# Webová stránka
HTML_PAGE = f"""
<!DOCTYPE html>
<html lang="cs">
<head>
<title>Ovládání IR žárovky</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {{
font-family: Arial, sans-serif;
background-color: #f2f2f2;
text-align: center;
margin: 0;
padding-top: 50px;
}}
h1 {{
color: #333;
font-size: 24px;
margin-bottom: 30px;
}}
.button-container {{
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
justify-items: center;
padding: 0 10px;
}}
button {{
padding: 15px 30px;
margin: 10px;
font-size: 18px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.3s;
max-width: 300px; /* Max. šířka tlačítka */
width: 90%;
}}
button:hover {{
background-color: #45a049;
}}
#status {{
margin-top: 20px;
font-weight: bold;
color: #333;
font-size: 16px;
}}
</style>
<script>
function sendCommand(cmd) {{
fetch('/send/' + cmd, {{
method: 'POST'
}})
.then(response => response.text())
.then(data => {{
document.getElementById('status').innerText = data;
}})
.catch(error => {{
document.getElementById('status').innerText = 'Chyba!';
}});
}}
</script>
</head>
<body>
<h1>Ovládání IR žárovky</h1>
<div class="button-container">
{buttons_html}
</div>
<p id="status"></p>
</body>
</html>
"""
@app.route('/')
def index(request):
return HTML_PAGE
@app.route('/send/<command>', methods=['POST'])
def send_command(request, command):
print(f'Přijatý příkaz: {command}')
for code, name in IR_CODES:
if name == command:
nec.transmit(0x0000, code)
return f'Odesláno: {command}'
return Response('Neznámý příkaz!', status_code=404)
app.run(host='0.0.0.0', port=80)
Popis kódu
- Na začátku se načítají knihovny:
network
pro práci s Wi-Fi,microdot
pro spuštění jednoduchého webového serveru,ir_tx
pro vysílání IR signálů v NEC formátu,Pin
pro ovládání výstupu na GPIO- a
time
pro nastavení zpoždění.
import network
from microdot import Microdot, Response
from ir_tx import NEC
from machine import Pin
import time
Nastavení GPIO a IR
Jako první nastavíme pin 23
jako výstup pro IR LED a připravíme si instanci třídy NEC
, která se postará o odesílání IR signálů.
ir_led = Pin(23, Pin.OUT, value=0)
nec = NEC(ir_led)
Součástí IR komunikace je i vytvoření určité „banky“ IR příkazů, které odpovídají různým funkcím žárovky – zapnutí, barvy, efekty, intenzita atd. Každému příkazu je přiřazen unikátní IR kód.
IR_CODES = [
(0x0d, 'on'),
(0x1f, 'off'),
...
]
Wi-Fi a server
Následuje vytvoření Wi-Fi sítě (Access Point). ESP32 vytvoří vlastní Wi-Fi síť s názvem „Wi-Fi-zarovka“ a heslem „blik-blik“. Do této sítě se pak připojíme z mobilu nebo počítače.
ap = network.WLAN(network.AP_IF)
ap.active(True)
ap.config(essid="Wi-Fi-zarovka", password="blik-blik", authmode=3)
Vytvoření Wi-Fi sítě chvíli trvá, proto cyklus počká, než je síť aktivní, a potom vypíše její IP adresu. Tu budeme potřebovat pro připojení k ovládací stránce.
while not ap.active():
time.sleep(1)
print('AP vytvořen na IP:', ap.ifconfig()[0])
Úvodní část věnovaná Wi-Fi končí spustěním webové serveru pomocí knihovny Microdot
. (Ten bude zobrazovat stránku s tlačítky a přijímat požadavky.)
app = Microdot()
Response.default_content_type = 'text/html'
HTML stránka
Dále potřebujeme vytvořit HTML kód stránky, která se zobrazí uživateli. HTML kód musí obsahovat tlačítka, stylování a hlavně JavaScript, který odešle příkaz na server po kliknutí na vybrané tlačítko. HTML kód je vložen do programu jako několikřádkový string (uzavřen v trojitých uvozovkách; všimněme si zápisu některých speciálních znaků, např. složených závorek!), sekce ovládacích tlačítek je zde vygenerována do proměnné buttons_html
podle banky příkazů a následně vložena metodou f
:
buttons_html = ""
for code, name in IR_CODES:
formatted_name = name.replace("_", " ") # Nahradí podtržítka mezerami
buttons_html += f'<button onclick="sendCommand(\'{name}\')">{formatted_name}</button>\n'
HTML_PAGE = f"""
<!DOCTYPE html>
<html>
...
{buttons_html}
...
"""
Obsluha požadavků
ESP32 se po spuštění programu chová jako jednoduchý webový server. Knihovna Microdot
umožňuje definovat, co se má stát při návštěvě určité „adresy“ (tzv. route). Tyto adresy obsluhujeme pomocí funkcí, které zavolá, když někdo požadavek odešle.
/
– zobrazí webovou stránku s tlačítky,/send/
– přijme požadavek z JavaScriptu a podle příkazu odešle IR signál pro žárovku.
Hlavní stránka
@app.route('/')
def index(request):
return HTML_PAGE
Toto je hlavní (a jediná) stránka, která se zobrazí v prohlížeči poté, co do webového prohlížeče zadáme IP adresu modulu ESP32. Funkce index()
vrací obsah proměnné HTML_
, což je celý HTML kód naší ovládací stránky s tlačítky (viz obr. 11).

Odeslání IR příkazu
@app.route('/send/', methods=['POST'])
def send_command(request, command):
print(f'Přijatý příkaz: {command}')
for code, name in IR_CODES:
if name == command:
nec.transmit(0x0000, code)
return f'Odesláno: {command}'
return Response('Neznámý příkaz!', status_code=404)
Tato „routa“ obsluhuje požadavky odeslané JavaScriptem ze stránky poté, co uživatel klikne na nějaké tlačítko. Kliknutí na tlačítko ve webovém rozhraní spustí funkci send
v JavaScriptu, která pošle požadavek typu POST na /send/
(např. /send/
). Server tento požadavek zachytí a spustí funkci send_
.
V této funkci:
- vytiskne se do výstupu (konzole), jaký příkaz byl přijat.
- prohledá se seznam
IR_
, zda daný příkaz odpovídá nějakému kódu.CODES - odešle se pomocí
nec.
odpovídající IR signál žárovce (pokud přijatý příkaz existuje).transmit() - vrátí se jednoduchý textový výstup
Odesláno: „příkaz“
, který se pak zobrazí na webové stránce v části „status“. - Pokud se žádný příkaz neshoduje, vrátí server odpověď
404 – neznámý příkaz
.
Poslední řádkou kódu programu je spuštění serveru, který pak čeká na příkazy na portu 80.
app.run(host='0.0.0.0', port=80)
Nyní stačí se připojit k vytvořené Wi-Fi a do prohlížeče zadat IP adresu zařízení.

- Poznámka:
- V našem řešení jsme zvolili jednu společnou routu
/send/<command>
, která dokáže zpracovat všechny příkazy – podle toho, jaký název příkazu jí předáme. To je výrazně efektivnější a přehlednější, než kdybychom pro každý příkaz (např./send/
,red /send/
,green /send/
, atd.) museli vytvářet samostatnou funkci nebo obslužnou rutinu.blue - Výhoda tohoto přístupu spočívá hlavně v tom, že kód je kratší a snadněji se udržuje – přidání nového příkazu znamená jen přidat položku do seznamu
IR_
. Není nutné upravovat samotný server, všechna logika je centralizovaná – víme přesně, kde se zpracovávají příkazy a není potřeba udržovat desítky drobných funkcí, je to flexibilnější. Stručně řečeno – díky jediné „chytré“ routě je náš kód přehledný, úsporný a připravený na další rozšiřování bez zbytečného opakování velmi podobného kódu.CODES
Projekt „Wi-Fi žárovka“ krásně ukazuje, jak lze i s relativně jednoduchým hardwarem a několika desítkami řádků kódu vytvořit plně funkční dálkové ovládání – a to bez potřeby jakéhokoliv cloudu, bez aplikací třetích stran a bez složité konfigurace. Stačí ESP32, IR LED, pár knihoven v MicroPythonu a trochu zvědavosti. Celé řešení běží čistě lokálně, což má výhodu v jednoduchosti i soukromí – žádná data neopouští vaši domácnost a vše máte pod plnou kontrolou.
Chcete přidat další funkce? Není problém! Stačí rozšířit seznam příkazů nebo webové rozhraní.
Chcete ovládat i jiná zařízení s IR přijímačem? Stačí program „naučit“ nové kódy!
Ať už jste bastlíř, učitel, nebo jen nadšený domácí „automatizátor“, tento projekt může být slušným výchozím bodem. Není to jen o blikání světel, když trochu pochopíme, jak věci fungují, rázem zjistíme, že možnosti jsou téměř neomezené!
Závěr
Možná jste zatím jen rozblikali žárovku, ale stejně snadno můžete zkusit ovládat 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 – možná 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.