Programování | Pojďme programovat elektroniku

Rozluštili jsme infračervený signál a připojili starou stropní klimatizaci k internetu

  • Nedávno jsme si pohráli s mobilní klimatizací a Wi-Fi
  • Ale co se starými kancelářskými kusy?
  • Dekódujeme jejich IR signál a vyrobíme si Wi-Fi ovladač

Před pár dny jsem na Twitteru narazil na příspěvek Martina Nerudy, který vyzbrojil svoji nástěnnou klimatizaci prototypovací destičkou s Wi-Fi čipem ESP8266, a mohl tak vzduchotechniku snadno napojit do systému chytré domácnosti Home Assistant.

Dálkové ovládání přes LAN mě samozřejmě okamžitě zaujalo, a tak si dnes zbastlíme něco podobného. Nástěnné a doposud hloupé klimatizační jednotky totiž máme i v našich kancelářích, jsou to ale prehistorické modely od Carrieru, které už mají hodně za sebou.

Melou z posledního, nedá se k nim dohledat žádná kloudná dokumentace, a jelikož tu jsme pouze v nájmu, představa, že se budu rýpat v jejich nitru, nepřichází v úvahu. Musí to být něco bezzásahového.

Jak jsem dekódoval infračervený signál staré kancelářské klimatizace a vyrobil dálkový ovladač s Wi-Fi a ovládáním z webového prohlížeče:

Nedá se nic dělat, dnes na to budu muset jít postaru – přes infračervený dálkový ovladač, který byl kdysi možná sněhově bílý, Slunce jej ale za tu dobu obalilo patinou zažloutlého plastu.

Budu potřebovat infračervený vysílač a přijímač

Jelikož o klimatizaci a jejím IR protokolu nevím naprosto nic, jednoduše na začátku zachytím optický signál s instrukcí pro zapnutí. Pokud se mi to podaří, mohu později stejný sled pulzů vyslat z vlastního mikropočítače připojeného k internetu.

c7f82bee-a86c-433f-b764-6e66eb9a3f19
Modul infračervené LED – primitivního IR vysílače bez modulace

K vysílání budu potřebovat LED, která září v oboru infračervených vln. Nabízí je každý krámek pro kutily zhruba za cenu jednoho rohlíku. V začátcích ještě lépe poslouží jen o pár kaček dražší prototypovací modul, který je vyzbrojený také ochrannými rezistory a paralelně připojenou LED ve viditelném spektru, která napoví, že IR světýlko právě vysílá.

d867b452-9e54-458f-aded-7175e577968f
Infračervený přijímač 38kHz modulovaného signálu VS1838B s tmavým filtrem pro odstranění nevhodných vlnových délek

Se zachycením infračervených pulzů pomůže naopak IR přijímač. Třeba VS1838B (datasheet), který seženete opět jako diskrétní součástku za cenu okolo pěti korun (1, 2), nebo jako prototypovací modul se signalizační LED a ochranným rezistorem. V Česku zaplatíte 30 Kč, takže nemá smysl čekat týdny na Aliexpress.

IR ovladače používají 38kHz modulovaný signál

Přijímač VS1838B je o něco sofistikovanější, obsahuje totiž 38kHz demodulátor. To znamená, že když na něj budu jen tak ledabyle blikat primitivní IR LED, nejspíše se vůbec nic nestane.

da9de4b6-714b-4c53-b29a-334cce03a68c
Vytvořil jsem na IR LED čtyři pulzy, ale VS1838B je vůbec nezachytil

Když se ale bude každé takové bliknutí skládat z dalších kratičkých pulzů s frekvencí okolo 38 kHz, přijímač díky demodulátoru vše korektně rozluští a základní signál vyjádří měnícím se stavem na výstupním logickém vodiči.

eef1ca34-9408-49dd-afd9-248f3d5b1df9
Modulovaným pulzům na frekvenci 38 kHz už přijímač porozumí

Signál zachytím pomocí logického analyzátoru

VS1838B pracuje v obráceném režimu pull-up, takže v klidovém stavu bude na jeho výstupním vodiči vysoký stav – digitální 1 –, no a když dorazí modulovaný pulz třeba o délce 1 ms, přijímač na stejnou dobu nastaví nízký stav – logickou 0.

492697e1-7253-4ab2-9e96-4ffc681ca263
Připojil jsem IR přijímač k logickému analyzátoru a na dálkovém ovládání stiskl tlačítko

Pro nás je podstatné to, že modulační frekvenci 38 kHz používá většina běžných komerčních dálkových ovladačů. Je to v podstatě standard. Takže ať už na VS1838B namířím dálkové ovládání televizoru, nebo naopak zažloutlý ovladač naší prehistorické redakční klimatizace, a stisknu nějaké tlačítko, přijímač vše rozluští a na jeho výstupním pinu se objeví sled pulzů, které zachytím v logickém analyzátoru a aplikaci Logic od Saleae.

60d515cf-44dd-4e8d-a9e1-dd6d1a653fe4
Čip VS1838B demoduloval 38kHz signál a vyjádřil jej sledem digitálních stavů na výstupním vodiči, který jsem připojil k logickému analyzátoru

Proč se používá modulace?

Proč se vůbec samotné infračervené pulzy, které nesou nějakou informaci, skládají ještě z oněch mikropulzů opakujících se rychlostí 38 kHz? Opověď je prostá, běžný prostor totiž vyplňuje hromada dalších zdrojů IR záření a docházelo by k rušení.

Představte si například, že by měl přijímač televizoru čekat na infračervený signál, který se skládá třeba ze čtyř po sobě jdoucích a různě dlouhých pulzů:

9ce15491-ca60-4eb6-bea2-724b3023ea51
Na televizor nesvítí jen váš dálkový ovladač, ale i hromada dalších zdrojů IR

Jelikož budeme mít v dosahu nejspíše hromadu dalších rušivých zdrojů IR záření počínaje slunečním svitem a konče emisí kdejaké žárovky, všechna dílčí záření se pomíchají dohromady, sečtou a odečtou a IR přijímač vůbec nic nerozluští:

9815c280-929b-4dd2-9c5d-dd82e7a6fc42
Bez modulace jen těžko odlišíme signál od šumu

Když ale původní čtyři pulzy rozdělíme na hromadu droboučkých kmitů s dostatečně unikátní frekvencí, získáme strojově čitelný periodický vzor, který se propíše do součtu všech ostatních infračervených signálů – šumu:

3e55961b-9f6e-4a9f-b453-84afec9d7b6a
38kHz blikáním oddělíme signál od šumu stejným způsobem, jako si řidič večer a v šumu ostatního dopravního světla všimne spíše cyklisty s blikačkou než se spojitě zářící lampou

Pak už jen stačí vyrobit přijímač, který bude tyto drobné záblesky šumu na zvolené frekvenci detekovat, takže pozná začátek a konec jejich souvislého proudu – tzv. obálky pulzu – a vyjádří ji změnou logického stavu na výstupu. Přesně tak funguje i přijímač VS1838B.

Desítky a stovky protokolů

Tak, už víme, jak funguje infračervený signál na té nejnižší úrovni, takže se pojďme posunout o patro výše. Ani v roce 2022 bohužel neexistuje žádný plošný a široce používaný protokol, jak skrze IR přenášet samotnou informaci a jak kódovat bity. Nemáme žádné USB pro IR.

Kódování podle vzdálenosti pulzů

Bity 0 a 1 bychom mohli rozlišovat třeba podle prodlevy mezi konstantními pulzy. Bit 0 oddělíme třeba prodlevou 500 μs a bit 1 dvojnásobkem 1 000 μs. Pro přenos čísla 3, které má ve dvojkové soustavě hodnotu 0011, v takovém případě zleva doprava (MSB pořadí) postupně vyblikáme tuto sérii pulzů:

7308da34-8c57-4d70-8bf9-1374dd656747
Hodnotu bitu může určovat vzdálenost mezi konstantními pulzy

Tento přístup má jeden vedlejší efekt. Přenos nebude mít stabilní rychlost, bity 0 totiž přenášíme dvakrát rychleji než bity 1. Stejnou techniku používá třeba relativně rozšířený infračervený protokol NEC a jeho odnože.

Kódování podle šířky pulzu

Dalším používaným typem kódování je pulzně-šířkové s konstantní mezerou mezi pulzy. Hodnotu bitu tentokrát stanoví šířka pulzu:

519079ec-b851-4201-9167-2e5d2950bc7f
Hodnotu bitu může určovat šířka pulzu oddělená konstantní mezerou

I v tomto případě se tedy jedná o přenos s proměnlivou rychlostí b/s, dávka s větším počtem 0 totiž bude o něco rychlejší než ta, která obsahuje spíše logické 1.

Kódování redakční klimatizace

Když jsem pomocí dálkového ovladače, IR přijímače VS1838B a logického analyzátoru nahrál signál odpovídající zapnutí naši redakční klimatizace, na monitoru počítače se objevil sled tři shodných zpráv.

Ovladač ji vyšle 3× nejspíše proto, abychom měli jistotu, že ji stropní klimatizace zaregistruje i z větší vzdálenosti. Při pozdějším testování jsem ale zjistit, že klimatizace zareaguje i na jednu jedinou dávku.

3dea05e9-3627-41df-93d0-28b6b458c645
Infračervená zpráva pro zapnutí klimatizace, jak ji zachytil, demoduloval a invertoval přijímač. Celá zpráva zabere zhruba 170 milisekund

Kódování zprávy bylo v tomto případě pulzně-šířkové, ale se stejně dlouhými periodami, jak to známe z klasické pulzně-šířkové modulace PWM.

Na začátku zprávy se tedy pokaždé nacházela dlouhá hlavička sestávající z pulzu o délce zhruba 8 ms, za kterým následovala prodleva o délce 4 ms. Hlavička dává klimatizaci najevo, že po ní už bude následovat sled periodických bitů.

380bd3f6-42ab-4e8c-acc1-ad6f942d0486
Formát periodické části zprávy za hlavičkou

Každý bit v našem případě tvoří perioda s délkou okolo 2 450 μs. O tom, jestli bude mít bit hodnotu 0, nebo naopak 1, rozhoduje její střída/duty cycle, tedy poměr mezi pulzem a prodlevou.

  • Bit 0 má pulz o délce zhruba 530 μs, pak následuje 1 920μs mezera
  • Bit 1 má pulz o délce 980 μs, pak následuje 1 470μs mezera

Tyto časové poměry jsou hrubé, protože přesnost přijímacího čipu VS1838B má své technické limity a jsou spíše průměrem, který jsem stanovil po několika opakováních.

Podstatné je to, že budou fungovat i později, až budu jednotlivé bity vytvářet sám pomocí IR LED a softwarové modulace. Nástěnná klimatizace na ně bez zaváhání zareaguje!

b95d214c-6f26-44ec-ab15-9506daba4f29606e7768-e08f-4f6d-8c99-8cfa7f48c4c5
Úvodní hlavička
7fb3c4ae-741e-41f4-a987-7bec588df58e7eea3b63-0c9a-4572-9fa8-fc538338ffed
Bit 0
9ef16093-9667-448d-8f0c-a1df9a27f599f6c50fbc-254b-4f56-940b-69574cfd37ce
Bit 1

Ke snímkům výše opět připomenu, že přijímač VS1838B pracuje v režimu pull-up (vysoký klidový stav), příjem IR pulzu tedy interpretuje nízkým logickým stavem.

Co znamená který bit?

Díky tomu, že jsem se nesnažil pouze zachytit jakýsi abstraktní signál, který pak doslova jen přehraji, ale pokouším se rozluštit podstatu jednotlivých bitů, mohu posléze lépe rozlišit, jak se mění, pokud budu na dálkovém ovladači mačkat na různá tlačítka.

Takže zatímco bitový přepis pro instrukci k zapnutí vypadá takto:

HLAVIČKA, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, PATIČKA

Bitový přepis pro vypnutí vypadá takto:

HLAVIČKA, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, PATIČKA

Obě zprávy jsou téměř shodné až na tři opačné bity, které jsem ztučnil. Zatímco v instrukci pro vypnutí mají hodnotu 1, v instrukci pro zapnutí 0. Zpráva má zároveň na první pohled identický formát i pro ostatní příkazy (zvyšování/snižování teploty apod.), ostatní bity tedy budou nejspíše patřit dalším parametrům klimatizace.

Pro jistotu ale opět zopakuji, že celá bitová interpretace je pouze moje vlastní a reverzní abstrakce originálního signálu. Nemám k němu žádnou dokumentaci a jen jsem si všiml opakujícího se vzoru a usoudil, že by to mohla být právě dvojková čísla 0 a 1.

Jak IR zprávu modulovat a odeslat zpět?

Fajn, takže jsme si ukázali základy jednoduché reverzní analýzy digitálního infračerveného signálu, ale jak tu naši uměle zkonstruovanou zprávu odeslat zpět do klimatizace a dát ji povel k zapnutí? Jak už jsme si řekli výše, drtivá většina obvyklých IR přijímačů pracuje výhradně s modulovaným signálem a zpravidla na frekvenci 38 kHz.

8515d91d-5d32-4240-aaf1-877848e659c6
Modulovaná zpráva vyblikaná na IR LED

Nestačí tedy moje stavy 0 a 1 jen vyblikat na infračervené diodě. Musím jednotlivé pulzy zprávy ještě rozřezat na nosné 38kHz subpulzy. Jak na to? Každý čip má k dispozici relativně přesné časovače pro generování spojitého obdélníkového PWM signálu, takže bych jej mohl nastavit na frekvenci 38 kHz a pak jen vždy zapínat a vypínat podle toho, jestli chci zrovna vyrobit modulovanou obálku logické 1, nebo naopak 0.

baffbc31-d482-41e0-a11e-ca0f9e1ef6f9
Detailní záběr na obálky bitů a jejich 38kHz modulaci, jak vše zachytil logický analyzátor

Na podobné hrátky už ale nemáme čas, a tak namísto toho sáhnu pro hotovém řešení a knihovně IRremote pro Arduino.

Kompletní knihovna pro IR

Knihovnu lze použít pro práci s různými spotřebiči, které pracují s některým z mnoha podporovaných protokolů (naše stará klimatizace na seznamu samozřejmě nebyla), stejně tak ale podporuje tvorbu modulovaného signálu, jehož vstupem budou surová časová data – po sobě jdoucí doby v mikrosekundách, které odpovídají pulzu a prodlevě.

Knihovna pak toto pole s délkami vysokých a nízkých stavů moduluje stanovenou frekvencí a vybliká na vybraném digitálním pinu, ke kterému stačí připojit vysílací IR LED:

IrSender.sendRaw(pole, 129, 38);

Příklad výše tedy moduloval frekvencí 38 kHz hodnoty v proměnné pole, které se skládá ze 129 položek

Knihovna IRremote samozřejmě umí i nahrávat surový signál, takže pokud se vám nebude chtít pracovat s logickým analyzátorem, můžete k některé z destiček Arduino připojit přijímač a použít hotový příklad z GitHubu jménem ReceiveDump.

Program pak do sériové linky vypíše délky po sobě jdoucích pulzů (kladné číslo) a mezer (záporné číslo), které můžete opět interpretovat.

A teď už jen ten internet

Modul s vysílací IR LED jsem nakonec připojil k prototypovací destičce s oblíbeným čipem ESP32, který lze po doinstalování podpory programovat i v prostředí Arduino. K čipu jsem na jeho digitální pin 4 připojil logický signál modulu s infračervenou LED. Mikrokontroler se po startu automaticky připojí k redakční Wi-Fi a spustí primitivní HTTP server.

58ab66fd-b8d4-462c-b016-707a1651c459
Čip ESP32 se připojil k Wi-Fi a do sériového terminálu vypsal svoji LAN IP adresu 

Když načtu IP adresu čipu ve webovém prohlížeči, budu moci klepnutím na jeden ze dvou odkazů klimatizaci buď zapnout, nebo vypnout. Použitá IR LED má nicméně velmi slabý výkon, a tak náš prototyp funguje jen na vzdálenost několika desítek centimetrů od čelní stěny klimatizace.

2055756a-fa61-4499-82ff-851a94af95c0
Když načtu IP adresu čipu v prohlížeči, zobrazí se primitivní ovládací pultík 

Kdybyste tedy sami experimentovali s infračerveným světlem, mějte na paměti, že problém může být často právě v této drobnosti. Pro začátek to ale stačí a výkon zvýšíte třeba použitím vícero paralelně zapojených IR LED, anebo silnějším napájením.

Zdrojový kód

Na závěr se podívejte na zdrojový kód dnešního experimentu, jehož základy jsou sice univerzální, samotná konfigurace IR signálu už ale odpovídá pochopitelně výhradně našemu redakčnímu scénáři.

// Wi-Fi ovladani pro klimatizaci pomoci cipu ESP32, vysilaci IR LED a kod pro Arduino

// Hlavickove soubory pro praci s Wi-Fi na cipu ESP32 v Arduinu
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <uri/UriBraces.h>

// Pin, ke kteremu je pripojena vysilaci IR LED
#define IR_SEND_PIN 4
// Knihovna Arduino IRremote pro praci s IR signalem
// Pouzijeme ji pro modulaci naseho signalu
// https://github.com/Arduino-IRremote/Arduino-IRremote
#include <IRremote.hpp>

// Prumerne doby v mikrosekundach,
// jak jsem je opakovane zachytil logickym analyzatorem
#define HLAVICKA_PULZ 7930
#define HLAVICKA_MEZERA 4070
#define PATICKA_PULZ 980
#define BIT0_PULZ 530
#define BIT0_MEZERA 1920
#define BIT1_PULZ 980
#define BIT1_MEZERA 1470

// SSID a heslo 2,4GHz Wi-Fi site
const char *ssid = "MojeKrasnaWifinka";
const char *password = "ctuziveczodranadovecera";

// Protokol klimatizace pouziva zpravu o delce 63 bitu,
// ktera je uvozena hlavickou a ukoncujicim bitem
uint8_t zapnout[63] = {0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0};
uint8_t vypnout[63] = {0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0};

// Z poli bitu vyse vytvorime finalni pole casu v mikrosekundach po sobe jdoucich pulzu a mezer
// Tyto surove casy zpracuje knihovna IRremote a vytvori z nich modulovany signal, ktery posle na pin vysilaci IR LED
uint16_t zprava[129] = {0};

// Funkce pro prepis bitovych sablon vyse na finalni pole s casy pulzu a mezer
void vytvorZpravu(uint8_t *sablona, uint8_t delka) {
  resetZpravy();
  uint16_t pozice = 2;
  for (uint8_t i = 0; i < delka; i++) {
    if (sablona[i] == 0) {
      zprava[pozice++] = BIT0_PULZ;
      zprava[pozice++] = BIT0_MEZERA;
    }
    else if (sablona[i] == 1) {
      zprava[pozice++] = BIT1_PULZ;
      zprava[pozice++] = BIT1_MEZERA;
    }
  }
}

// Funkce pro pripravu/reset zpravy s hlavickou, patickou
// a zatim prazdnou datovou casti
void resetZpravy() {
  memset(&zprava, 0, (129 * 2));
  zprava[0] = HLAVICKA_PULZ;
  zprava[1] = HLAVICKA_MEZERA;
  zprava[128] = PATICKA_PULZ;
}

// Trida HTTP serveru, ktery pobezi na standardnim TCP portu 80
WebServer server(80);

// Hlavni funkce setup, ktera se zpracuje po startu
void setup() {
  // Nastartuj seriovou linku rychlosti 115 200 b/s
  Serial.begin(115200);

  // Prepni Wi-Fi do rezimu klienta, ktery se bude pripojovat k AP
  WiFi.mode(WIFI_STA);

  // Zacni se pripojovat
  WiFi.begin(ssid, password);

  // Dokud nejsi pripojeny, vypisuj do seriove linky tecky
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  // Vypis do seriove linky LAN IP adresu cipu
  Serial.printf("\r\nIP: %s\r\n", WiFi.localIP().toString().c_str());

  // Cip se bude v lokalni siti hlasit jako zarizeni klima
  MDNS.begin("klima");

  // Pokud zadame IP adresu cipu do WWW prohlizece, zobrazi se jedndoucha stranka s odkazy pro zapnuti a vypnuti
  server.on(F("/"), []() {
    server.send(200, "text/html", "<a href='/prikaz/zapni'>Zapni klimatizaci</a><br><a href='/prikaz/vypni'>Vypni klimatizaci</a>");
  });

  // Pokud zadame URL /prikaz/...
  server.on(UriBraces("/prikaz/{}"), []() {
    String prikaz = server.pathArg(0);

    // Pokud to bude /prikaz/zapni
    if (prikaz == "zapni") {
      // Vytvorime surovou zpravu pro zapnuti klimatizace
      vytvorZpravu(zapnout, sizeof(zapnout));
      // Modulujeme ji frekvenci 38 kHz a vyblikame do eteru
      IrSender.sendRaw(zprava, 129, 38);
      server.send(200, "text/html", "Zapnuto!<br><a href='/'>Zpet</a>");
    }

    // Pokud to bude /prikaz/vypni,
    // analogicky provedeme operaci pro vypnuti klimatizace
    else if (prikaz == "vypni") {
      vytvorZpravu(vypnout, sizeof(vypnout));
      IrSender.sendRaw(zprava, 129, 38);
      server.send(200, "text/html", "Vypnuto!<br><a href='/'>Zpet</a>");;
    }
  });

  // Nastartujeme HTTP server
  server.begin();
  // Nastartujeme objekt knihovny IRremote pro vysilani
  IrSender.begin();

}

// Funkce loop se spousti po zpracovani funkce setup
// Jeji obsah se opakuje stale dokola
void loop() {
  // Vyridime pripadne klientske pozadavky serveru
  server.handleClient();
  // Pockame 2 ms, at ma CPU jadro cas na ostatni praci, a vse zopakujeme
  delay(2);
}
Diskuze (31) Další článek: Microsoft to opět zkouší se sociálními sítěmi. Viva Engage vypadá jako Facebook, ale cílí na firmy

Témata článku: , , , , , , , , , , , , , , , , , , , , , , , , ,