Dokončení software digitálního termostatu

24. 6. 2015
Doba čtení: 19 minut

Sdílet

Dnes si projdeme řídicí program podrobněji, ukážeme si možnosti optimalizace, boj s nedostatkem paměti, nakládání s grafickými objekty a znakovou sadou pro výstup na displej. Také vyřešíme otázku stability, souběžného zpracování více úloh, představím knihovnu Tasker a probereme, jak termostat žije v domácí LAN.

Dvousečné knihovny

minulém článku jsme si ukázali, že termostat využije celkem 11 existujících knihoven, takže se nemusíme zdržovat psaním kódu pro obsluhu hardware, ale stačí nám napsat samotnou logiku aplikace, která bude volat funkce knihoven – ať už pro přečtení teploty z čidel, zobrazení teploty na displej, odeslání teploty někam do oblak (cloudů) nebo třeba zjištění přesného času z Internetu. Kromě zřejmých pozitiv (mnohem rychlejší vývoj) to má i několik negativ. Můžeme narazit na chybu autora knihovny a pak marně tlouci hlavou do zdi, proč něco nefunguje, když to máme podle dokumentace/příkladů správně. Naštěstí jsou všechny Arduino knihovny šířené výlučně jako open source, většinou (vždy?) i jako Svobodný software, takže v nouzi (ale i v případě zájmu se naučit něco nového) můžeme do knihovny nahlédnout, prostudovat ji a případně opravit.

Aktuální podoba termostatu: informace zobrazené na displeji jsou ve vývoji, brzy budou texty s diakritikou…

Druhým problémem, který takové množství knihoven v jednom projektu přináší, je příliš mnoho obsazené flash paměti (té, kde je nahrán/„vypálen“ zkompilovaný program). Jednoduše řečeno kód knihoven zabral tolik prostoru, že nezbývá dost místa pro samotný kód implementující ten úžasný termostat, který jsme si původně plánovali. I když je GCC velmi dobrý kompiler, a přestože generuje už ve výchozím nastavení v Arduino IDE kód optimalizovaný na velikost (tedy co nejkratší), stále ty tisíce řádků C kódu zaberou dost místa i po přeložení do strojového kódu. Perfektní je, že nepoužité (nevolané) knihovní funkce nejsou do výsledného kódu vůbec zahrnuty, ale pořád to nestačí. Konkrétně můj aktuálně zhruba 800řádkový program Termostat.ino je v Arduino IDE 1.0.5 přeložen do 32058 bajtů strojového kódu. To je bohužel moc – maximální místo ve flash paměti mikrokontroléru Arduino Pro Mini s procesorem ATMEGA328p je 31 kB (poslední kilobajt je rezervovaný pro ethernetový bootloader), takže jen 31744 bajtů. Tento program se tedy do použitého Arduina nevejde (o 314 bajtů).

Jak tuto situaci vyřešit? Nabízí se několik možností:

  • hrubá síla: použít mikrokontrolér s větší flash pamětí
  • jemná síla: použít lepší kompiler, optimalizovat kód aplikace a případně i knihoven
  • rozdělit a panovat: termostat rozdělit na „backend“ a „frontend“ a pustit je na více mikrokontrolérech
  • ořezat na kost: vyhodit zbytné úlohy a vlastnosti, nechat jen to nejdůležitější

Jak jsem už v díle o HW psal, pro takto pokročilý termostat by bylo lepší použít Arduino (resp. mikrokontrolér ATMEGA) s větší flash pamětí, ale to není tak jednoduché. Procesory ATMEGA1280/2560 jsou v pouzdrech s tolika nožičkami (100), že si je člověk doma těžko sám připájí. Desky velikosti Arduino Pro Mini s procesorem ATMEGA1280 stojí 10× víc než ty s Pro Mini. Tam už zbývá jedině jít přímo do Arduino Mega a připustit si, že bude na zdi v obýváku zabírat poněkud více místa než 8×6 cm. Anebo opustit celou AVR platformu a jít do čehokoliv modernějšího – třeba klon Maple Mini s 32bit ARM mikrokontrolérem stojí do stokoruny, má 120 kB flash paměti a díky otevřenosti Arduino platformy, kterou jsem popsal minule, se dá programovat ve stejném Arduino API a překládat ve stejném Arduino IDE. Bohužel ještě nemám ověřeno, že v něm skutečně fungují všechny věci tak jak mají, takže tuto cestu nemohu s klidným svědomím doporučit.

Pokud hardware měnit nemíníme či nemůžeme, můžeme zkusit použít lepší kompiler či optimalizovat (zjednodušit, zkrátit) kód aplikace a knihoven. Například pouhým přeskočením z Arduino IDE (se starým GCC) do novějšího GCC v Ubuntu 14.04 se díky minule popsanému Arduino-mk přeloží stejný zdrojový kód termostatu (včetně všech knihoven) do 30644 bajtů, tedy do o 4,5 % úspornějšího strojového kódu. Ušetřit necelých 5 % nezní nijak přelomově, ale na rozdíl od předchozího stavu se teď výsledek už do mikrokontroléru vejde! Navíc máme ještě 1100 bajtů volných pro vlastní logiku aplikace, a to celé jsme dosáhli pouhou změnou verze kompileru. To se vyplatí :) Povzbuzeni tímto pokrokem pokračujme v dalších optimalizacích. Můžeme si například vzít v minulém díle ukázanou funkci setup_ethernet_bootloader_address() a mírně ji přepsat (se zachováním funkčnosti a dokonce i zlepšením čitelnosti):

void setup_ethernet_bootloader_address()
{
    byte addr[] = {
        192, 168, 1, 1,                     // gateway IP
        255, 255, 255, 0,                   // netmask
        0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED, // MAC
        192, 168, 1, 100                    // Arduino IP
    };
    for(byte i = 0; i < sizeof(addr); i++)
        Ewrite(i + 0x10, addr[i]);
}

Po překladu vidím, že jsem ušetřil dalších 32 bajtů. Některým čtenářům/programátorům může takový postup připomínat čirý masochismus, ale dá se to brát i jako sport či zábava. Můj první počítač Atari 800 XL měl méně než 64 kB programové paměti, takže jsem se rovnou učil vyjít s omezenými programovými prostředky. Dnešní programování na AVR mi tehdejší dobu velmi připomíná. Po každodenní směně strávené řešením problémů masivních mnohavrstvých webových aplikací na Xjádrových strojích s gigabajty RAM pro mě představuje optimalizace kódu na této úrovni příjemnou změnu až osvěžení.

Rychlejšího pokroku ve zmenšování kódu bychom dosáhli hrábnutím do knihoven či jejich postupným vyházením – například knihovna DallasTemperature je napsána poměrně rozšafně. Samotné přečtení teploty z DS18B20 jde zařídit na pár řádků přímým voláním funkcí knihovny OneWire. Další potenciální úsporou by bylo úplné opuštění proměnných typu ‚float‘ udržováním teplot v celočíselných proměnných (ne nezbytně zaokrouhlených na celé stupně – hodnoty můžeme bitově posunout či vynásobit např. 10). Z paměti by díky tomu mohly zmizet knihovny pro aritmetiku v plovoucí řádové čárce, tedy pokud bychom byli v úklidu knihoven opravdu důkladní (typ proměnné ‚float‘ používá i knihovna TouchScreen).

Přestože předchozí řádky nejspíš vzbudily u většiny čtenářů neodbytný pocit, že zvolený mikrokontrolér ATMEGA328 na tento úkol nestačí, rád bych se ho ještě jednou zastal. Výkonově má nesmírné rezervy – vždyť má 16 MIPS, tedy více než třeba Intel 80386. I RAM má relativně dostatek – z 2048 bajtů mi dnes termostat využívá 826, takže tu ještě jakási rezerva je. Jen ta flash paměť 32 kB není zcela dostačující (samozřejmě pouze při výše popsaném programování stylem naházení knihoven v C/C++ do projektu – assembleristi mají flash paměti přebytek).

Další možností, jak ušetřit paměť, je rozdělit úlohy, které jsem do termostatu na začátku vměstnal, do několika skupin a ty implementovat na samostatných mikrokontrolérech. Jakkoliv to zní jako další šílenost, dává to určitý smysl. Například já teď tahám sítě termočidel až do obýváku na zdi – přitom jsem mohl mít „backend“ obsluhující sběrnice čidel umístěný někde blíž k místu, kde se mi setkává vnější a vnitřní kabeláž („rack v serverovně“). Samotné GUI na dotykové obrazovce v obýváku pak mohl obsluhovat jiný mikrokontrolér, který by navíc nemusel řešit webový server. Anebo jsem mohl v obýváku na zeď přitlouci ojetý Android/iOS/N810 tablet, kterému už dávno došel dech na nejmodernější hry, a Arduino vůbec GUI, obrazovkou a dotyky netrápit – termostat mohl mít rovnou jen webové rozhraní. Těch možností je víc, a vlastně záleží především na tom, chcete-li se při stavbě termostatu něčemu přiučit, nebo chcete-li co nejpřímější cestu k výsledku.

Mimochodem, vydělení úlohy čtení termočidel na samostatné Arduino by elegantně řešilo i v diskusi tolik propíranou odolnost proti naindukovanému přepětí (nejen po zásahu bleskem). Prostě by přinejhůř odešlo to Arduino přímo spojené s čidly (škoda 50 Kč, výměna v objímce za 10 sekund), ale zbytek řešení s displejem, ethernetem atd. by chráněn optickými oddělovači přežil bez ztráty kytičky.

Poslední možností, jak se vyvléct z nedostatku paměti, je ubrat z původně plánovaných vlastností digitálního inteligentního termostatu. Celkem brzy jsem proto opustil myšlenku zobrazování grafů teplot na displeji v obýváku. Nahrazuji to zobrazením maximálních a minimálních teplot za uplynulých 24 hodin, což se ukázalo zcela dostačující. Podrobné grafy jsou stejně lépe čitelné na větším displeji PC než na tom termostatickém s 6cm úhlopříčkou. Dalším na řadě k odstřelení byl plán tahat předpověď počasí z Internetu: tento nápad vlastně není ještě zcela zatracen, ale je jisté, že není rozumné pokoušet se v limitované paměti Arduina o nějaké větší parsování dat stažených někde z webu. Co je pořád možno udělat, je předžvýkat staženou předpověď na jiném stroji v síti a teprve konkrétní hodnoty teplot, oblačnosti atd. stáhnout do termostatu a tam zobrazit.

Graf teplot je ve velkém rozlišení na PC lépe čitelný.

Se zobrazováním předpovědi počasí přímo souvisí grafika – displej oplývá 18bitovou barevnou hloubkou a jemným rastrem 220 × 176 bodů, proto by každého lákalo zobrazit barevné ikony sluníček, mráčků, vodních kapek atd. Bohužel, obrázky/ikony jsou obzvlášť velkým žroutem flash paměti, pokud je natvrdo zakompilujeme do kódu programu. Knihovna Adafruit_GFX ale nabízí i možnost objekty nakreslit z grafických primitiv (čára, kruh, trojúhelník, obdélník – i vyplněný, a dokonce se zakulacenými rohy). Ovšem než z vektorů nakreslíte mráček, vyplýtváte víc paměti, než by ten samý mráček zabral v paměti uložený jako černobílá bitmapa…

Neházejme ale flintu do žita – ještě by šlo předem připravené grafické objekty umístit do externí paměti, anebo je dokonce online stahovat odněkud z domácí sítě. Externí pamětí mám na mysli třeba SD kartu. Slot pro ni bývá zadarmo přilepen jak k displejům, tak i k ethernet shieldům (anebo se dá přikoupit zvlášť za dvacku a připojit na SPI sběrnici). Jen se musíme už předem vzdát pohodlného života s filesystémem na kartě – ten by totiž zase vyžadoval spoustu paměti (jak flash tak i RAM). Externí paměť by musela mít předem známou jednoduchou strukturu a obrázky na ní by byly přímo ve formátu displeje.

Online stahování grafických elementů bych si představoval tak, že někde na serveru v domácí síti nám běží velmi primitivní server typu TFTP, a termostat si odtamtud při vykreslování na obrazovku stahuje grafiku, kterou by bez ukládání do mezipaměti (již nemáme) házel přímo z ethernetu na displej. Tuto možnost jsem ještě nezkoušel – očekávám, že by to bylo docela pomalé a mohlo by to vypadat asi jako když 8bitové počítače nahrávaly splash screen hry z kazety – takže vlastně velmi retro cool.

Displej

U displeje se ještě na chvíli zastavme. Pokud ho k Arduinu připojíme, láká nás to velké rozlišení pod rukama k zobrazení mnoha různých údajů. Po pověšení na zeď se však ukáže, že hlavním požadavkem uživatelů není co největší množství informací zobrazených naráz, ale jejich dobrá čitelnost. Nikomu se nechce chodit tak blízko, až se nosem skoro dotýká obrazovky – ideální je, když se z prostoru celého obýváku dovíme základní údaje pouhým mrknutím oka. Proto i když mám na displeji 176 grafických řádků, tak se mi na ně nakonec vešly jen čtyři řádky textu – to abych je mohl mít co největší. Knihovna Adafruit_GFX nabízí výpis textu různou velikostí písmen, takže napsat opravdu velké písmena není žádný problém. Stačí už jen vymyslet, jaké informace a jak na displej rozmístit. U mě vévodí displeji přesný čas, protože v obýváku nikde jiné hodiny nemám. Komu doma už svítí hodiny na jiném přístroji, může horní třetinu displeje termostatu využít lépe.

Displej ukazuje čas, postup větrání (ta vodorovná čárka roste doprava), teplotu v horním a dolním patře, venku ve stínu a na slunci (plus denní maxima a minima). A dvě tlačítka…

Apropo text – většina grafických knihoven pro Arduino nabízí font v rastru 5×7 bodů, který obsahuje jen písmena anglické abecedy plus pár neužitečných symbolů. Chtěl jsem situaci napravit přidáním 30 českých znaků a užitečných symbolů typu stupně (°) apod. Proto jsem začal pátrat po vhodném editoru fontů. Po vyzkoušení asi pěti různých jak na Atari ST, tak pro Windows padlo rozhodnutí napsat vlastní editor, který dělám rovnou v HTML, aby byl zcela platformně nezávislý. Bude umět číst jak zdroják v C, tak i obrázky fontů, a výstupem bude přímo zdroják v C. Je hotov tak ze tří čtvrtin a snad ho brzy hodím na svůj GitHub. V tomto editoru také doplním font kromě českých znaků i o klasické semigrafické symboly, neboť jsem si spočítal, že například vykreslení obdélníku (obrysu tlačítka) na displeji ze znaků bude paměťově úspornější než malování téhož z vektorů. Tím se dostanu až někam ke schopnostem textových uživatelských rozhraní Turbo Pascalu či knihovny curses z minulého století.

Rozpracovaný editor znakové sady v HTML a javascriptu

Perlička: písmo v rastru 5×7 po čtyřnásobném zvětšení bez antialiasingu působí samozřejmě docela kostrbatě. Že tomu tak být nemusí ukazuje následující projekt „Arduino hodinek“. Stačí mít dost volné flash paměti a použít font s větším rastrem a hned to vypadá krásně.

Stabilita termostatu

Když jsem termostat na podzim 2013 spouštěl, byl jsem zvědavý na jeho stabilitu – jestli zkrátka nevytuhne uprostřed topení či netopení (oboje by mělo zlé následky). Proto jsem záhy přidal do programu podporu watchdogu, což je na procesoru nezávislý obvod (ale je uvnitř mikrokontroléru), který hlídá hlavní procesor, jestli se někde nezasekl. Používá se jednoduše tak, že se mu nejdřív nastaví, do kolika (mili)sekund má napočítat, a pak se ho snažíme periodicky resetovat dřív, než k té hodnotě napočítá, protože pak by vyvolal externí reset celého procesoru (v reálu možná počítá směrem dolů k nule, ale na principu to nic nemění). No a pokud bychom se někde zasekli (třeba kvůli chybě v knihovně), tak nestihneme resetnout watchdog do nastaveného času a ten resetuje celý procesor, čímž nás osvobodí ze zaseknuté knihovny a kotel začneme znovu řídit.

void setup()
{
    ... // náš kód pro inicializaci termostatu
    wdt_enable(WDTO_4S);      // 4s watchdog timer
}

void loop()
{
    wdt_reset();    // nutno volat nejméně jedenkrát za 4 sekundy!
    ... // náš periodicky volaný kód pro obsluhu termostatu
}

Abych měl přehled, jak často se termostat resetuje, přidal jsem tam jednoduchý výpis času (uptime). Díky použité knihovně Time je převod sekund na minuty, hodiny a dny jednoduchý (objekt tft reprezentuje TFT LCD, tedy displej):

    time_t uptime = millis() / 1000;
    tft.print(uptime / 86400UL);
    tft.print(F("days "));
    print2Digits(hour(uptime),':');
    print2Digits(minute(uptime), ':');
    print2Digits(second(uptime));

Funkci print2Digits jsem si napsal, abych udržel informace na displeji zprava zarovnané na daný počet znaků:

void printXdigits(int number, byte digits, bool leadingZero)
{
    char lead = leadingZero ? '0' : ' ';
    if (digits >= 4 && number < 1000)
        tft.print(lead);
    if (digits >= 3 && number < 100)
        tft.print(lead);
    if (digits >= 2 && number < 10)
        tft.print(lead);
    tft.print(number);
}

void print2Digits(byte number, char separator)
{
    printXdigits(number, 2, true);
    if (separator)
        tft.print(separator);
}

Termostat vesele fungoval, akorát mě trochu trápilo, že málokdy vydrží běžet aspoň dva měsíce v kuse. Většinu dosáhl uptime něco přes čtyřicet dní a pak se resetl – nebo to tak aspoň vypadalo, neboť při mé příští náhodné kontrole ukazoval méně dnů online, než při kontrole předchozí. Trvalo mi velmi velmi dlouho, než mi došlo, že se termostat neresetuje – že mi jen přetéká proměnná! millis() vrací počet milisekund od startu Arduina, ale tato hodnota je pouze čtyřbajtová ( unsigned long), takže maximum má něco málo přes 4 miliardy milisekund, a pak začíná znovu od nuly. To znamená, že v mém výpise výše po 49. dni následuje den nultý…

Řešení je samozřejmě jednoduché: každou sekundu si zvýšit vlastní unsigned long uptime čítač o jedničku – to pak vydrží 49710 dní (tj. 136 let), a poté už bude termostat morálně tak zastaralý, že nás přetečení jeho uptime trápit nebude.

Programování souběžných úloh

Ještě mi dovolte pár slov k naprogramování všech těch činností, které má termostat provádět „zároveň“. Původní účel Arduina byl přilákat k programování i ty italské středoškoláky, kteří by se programování mohli bát. Proto je C++ maskováno za „Wiring“, int main(int argc, char *argv[]) není vidět vůbec a místo něj se nováčkům vnucuje dvojice jednoduchých funkcí void setup() a void loop(). Zatímco ve funkci setup(), kam naházíme počáteční inicializaci celého termostatu, nemáme moc co zkazit, tak funkce loop(), kterou za nás magicky Arduino volá neustále dokola, svádí k opravdu nepěkným věcem. To nejméně hrozné, k čemu nás to může dovést, se nazývá špagetovým kódem.

Situace se ještě zkomplikuje, když si uvědomíme, že přestože všechny činnosti (čtení teplot, čtení vlhkosti, výpis na displej, obsluha web serveru, reakce na dotyky na displeji, …) potřebujeme provádět opakovaně, tak každá z nich má jinou periodu. Například teploty ze 1-Wire sběrnic chceme číst jednou za 4 sekundy, aby se čidla moc neohřívala častým čtením. Výpis na displej musí být nejméně 1× za sekundu, aby se správně překresloval čas. Web server by měl být obsluhován ještě častěji, ať prohlížeč nečeká. No a dotyky na displeji by měly být registrovány úplně nejčastěji, jinak ho netrpělivá obsluha nehtem rozryje (dotyková vrstva je rezistivní, nikoliv kapacitní, proto dojde k nervóznímu rozrytí nehtem, nikoliv k vytlačení důlku bříškem prstu).

Třešničkou na špagetovém dortu s konfliktními periodami je pak samotná implementace většiny knihoven dostupných pro Arduino platformu, kdy si jejich autor myslí, že jeho knihovna je v dané chvíli ta jediná na Arduinu běžící a proto se neostýchá využívat prostředky jen pro sebe. Pokud tedy například potřebuje během čtení dat z periférie chvíli počkat, tak si počká – ale v tu chvíli stojí i všechny ostatní výše zmíněné úlohy, které by měl termostat provádět zároveň. Nejkritičtější situace by mohla být u čtení teplot z čidel DS18B20, kdy je nutné mezi odesláním příkazu ke konverzi teplot a příkazu k vyčtení teplot počkat až 750 milisekund. Pokud bychom použili běžné příklady obsluhy DS18B20, tak bychom na 800 milisekund zastavili celý termostat – a to si nemůžeme dovolit.

Jak z toho ven? Extrémem je nainstalovat si na 8bitové Arduino multitáskový operační systém. Skutečně takové existují, například FreeRTOS, ChibiOS, NuttX a zřejmě i další. Nemám s nimi pražádné zkušenosti a nemám na ně místo ve flash paměti, takže tudy ne. Opačným extrémem je pokoušet se situaci nějak ukočírovat v loop()  smyčce pomocí různých proměnných, které by si pamatovaly, kdy naposledy jsem volal tu kterou funkci termostatu (čtení, výpis, dotyky, web, …) a pak si počítaly, kolik milisekund od té doby uběhlo, abychom tu funkci nezavolali podruhé příliš brzy. Zkoušel jsem to a byla z toho špageta jak vyšitá.

Čtenářům bych u podobně složitého projektu doporučil vydat se rovnou zlatou střední cestou a svěřit časování volání funkcí nějakému „frameworku“. Když jsem k této myšlence dozrál, udělal jsem si rešerši dostupných řešení. Stačilo hledat frázi „Arduino task“, případně „Arduino scheduler“ a hned bylo z čeho vybírat. Nakonec jsem pečlivě prošel dvanáct různých řešení a můj závěr z toho byl, že si to budu muset udělat po svém. Tak vznikla knihovna Tasker, která je dostupná na GitHubu. Česky jsem se pokusil popsat její použití na mém blogu.

My si teď ukážeme, jak se termostat s Taskerem zpřehlední (přestože jsem raději neukázal tu předchozí hrůzu):

void setup()
{
    tasker.setInterval(vypis_na_displej, 1000);
    tasker.setInterval(cti_teploty, 4000);
    tasker.setInterval(zatop, 6000);
    tasker.setInterval(listenForWebClients, 500);
    tasker.setInterval(listenForClients, 500);
    tasker.setInterval(set_backlight, 500);
    tasker.setInterval(watchdog_reset, 3000);
    tasker.setTimeout(check_touch, 100);
    tasker.run();
}

void loop()
{
}

Většina Taskerem volaných funkcí je pak velice jednoduchá. Pozornost si zaslouží funkce cti_teploty(), která mazaně využívá Tasker k tomu, aby nečekala v delay() 750 milisekund na dokončení konverze teplot, ale místo toho dala procesorový čas k dispozici a přečetla teploty, až budou připravené:

void watchdog_reset(int) { wdt_reset(); }

void set_backlight(int)
{
    // nastav podsviceni dle intenzity okolniho svetla
    analogWrite(lpwm_pin, map(analogRead(photo_pin), 0, 1024, 240, 0));
}

void cti_teploty(int)
{
    sensors_interni.requestTemperatures();
    sensors_externi.requestTemperatures();
    tasker.setTimeout(precti_teploty, DallasTemperature::millisToWaitForConversion(12));
}

void precti_teploty(int)
{
    for(byte i = 0; i < TEPLOT; i++) {
        float t = (i < INTERNICH) ? sensors_interni.getTempC(cidla_interni[i]) : sensors_externi.getTempC(cidla_externi[i - INTERNICH]);
        if (t != -127.0f && t != 85.0f) // -127.0f = connection error, +85.0 = conversion error (or something like that)
            teploty[i] = t;
        else
            errory[i]++;
    }
}

Jak je vidět, cti_teploty() si na obou 1-Wire sběrnicích vyžádá začátek konverze teplot a pak přidá do seznamu úloh funkci precti_teploty(), která bude volána za určitý počet milisekund od teďka (ta statická funkce třídy DallasTemperature vrací 750). Aby se funkce requestTemperatures() po zavolání nezastavila sama na těch 750 milisekund, je potřeba o tom danou instanci třídy DallasTemperatures nejprve požádat, takže ve funkci setup() mám ještě následující kód:

    sensors_interni.setWaitForConversion(false);
    sensors_externi.setWaitForConversion(false);

Nyní už vše funguje jak má. Ještě letem-světem prosvištíme ostatní funkce: vypis_na_displej() samozřejmě vypisuje čas, teploty, stav kotle a vykresluje tlačítka uživatelského rozhraní na displej. Funkce zatop() pak implementuje celé to inteligentní řízení kotle podle všech teplot a jejich historie, které mám v termostatu k dispozici. listenForWebClients() implementuje webové rozhraní na standardním portu 80, kde klientům posílá jednoduchou HTML stránku s aktuálním stavem termostatu (teploty, stav kotle, doba uptime atd.) a možností změnit některé parametry. listenForClients() implementuje servisní rozhraní, přes které si můžu jednoduše vyčíst teploty čidel a také požádat o restart termostatu do bootloaderu, když mu chci poslat novou verzi software (podrobněji popsáno v minulém díle). A funkce check_touch() registruje doteky na obrazovce a podle jejich souřadnic pak vyhodnotí, které „tlačítko“ bylo stisknuto a příslušně na něj zareaguje.

Termostat v LAN

Na závěr už nejstručněji k uspořádání mé domácí LAN, které může inspirovat: v síti mám krom jiného i malinký Atomový servřík s Xubuntu, kde běží web server přístupný z celého Internetu. Zde je v cronu co minutu spouštěn prajednoduchý skript, který vyčte teploty z termostatu a nakrmí jimi RRDTool. Ten mi potom vykreslí grafy s různou délkou periody (poslední 3 hodiny, 12 hodin, 2 dny, týden, měsíc), na které se můžu podívat pohodlně na 23" monitoru místo na 2" displeji termostatu. Také tu běží lokální NTP server, který je používán termostatem (to aby termostat pro čas nemusel chodit až někam na Internet). No a zřejmě bych si tu mohl spustit i nějakou IPv6 to IPv4 web proxy, abych si zpřístupnil webové rozhraní termostatu i po IPv6, kdybych to potřeboval (toto byla speciální poznámka určená fandům IPv6, kteří se chystají poukázat na to, že zvolený ethernet modul s čipem Wiznet W5100 IPv6 neumí a proto je celý termostat k ničemu).

Další ukázka grafu teplot: je zde vidět i jak plynový bojler nepřímo ohřívá čidla na vratce a vodě z kotle (ty zoubky na červené a modré čáře), takže vlastně „monitoruji“ i bojler…

Na tomto serveru bych mohl také předžvýkat předpověď počasí staženou nevím odkud (jsou někde přístupná data, která používá aplikace Aladin na Androidu?), aby termostat dostal už jen konkrétní data teplot a srážek na nejbližší období a rovnou je mohl bez dalšího náročného parsování použít/zobrazit. Též by tu mohl běžet TFTP server s grafickými prvky (sluníčko, mráček, ..) pro displej, jak jsem teoretizoval výše. A dokonce by tu mohly sídlit CSS a grafické prvky pro webové rozhraní termostatu, které by díky tomu mohlo být moderní (tj. plné jQuery, Bootstrapů a podobných vymožeností), přestože by základní HTML dál generovalo maličké Arduino.

bitcoin_skoleni

Závěr

Toť vše k mému termostatu. Snažil jsem se v celém seriálu zmínit co nejvíc informací, úskalí a „špeků“, na které jsem po cestě narazil. Rozumím, že by někteří přivítali schéma, plošný spoj a kompletní zdrojový kód, ale dal jsem přednost obecnějšímu popisu, který by měl v duchu „nakrmit rybou versus naučit rybařit“ být pro čtenáře nakonec snad přínosnější. Bude-li mít redakce a čtenáři zájem, mám postavených pár dalších „udělátek“ se srdcem z Arduina, které bych mohl popsat – tentokrát bez omáčky obecných názorů na HW a SW Arduino platformy, které už zazněly v tomto seriálu.

P.S. Dva roky po rozjetí termostatu a dva dny po dopsání tohoto článku jsem znovunašel knihovnu u8glib. Zjevně řeší některé mé stesky (např. pěkné fonty v mnoha velikostech a dokonce má bohaté GUI), ale asi nemá přímou podporu pro můj displej. Nicméně ji budu stejně chtít omrknout a případně z ní to dobré v termostatu do budoucna použít.

Autor článku

Petr Stehlík vystudoval aplikovanou informatiku a pracuje jako vývojář webových aplikací a administrátor linuxových serverů. Provozuje vlastní server tvpc.cz.