Řízení kotle Arduinem

31. 12. 2024 20:37 (aktualizováno) Ondřej Novák

Tento příspěvek vypráví o mé cestě k automatizaci kotelny s využitím staršího kotle a Arduina. 

TL;DR - Protože je článek dlouhý, napsal jsem na úvod shrnutí tématu pro spěchajícího čtenáře: Rozhodl jsem se prodloužit životnost poloautomatického kotle III. emisní třídy Benekov Pelling 27 tím, že jsem postavil novou řídící jednotku použitím vývojové desky Arduino UNO R4 Wifi. Práce to nebyla jednoduchá, protože jsem si musel vyvinout nejen vlastní software, ale i postavit fyzické zařízení a to včetně tzv silové části – ano, zařízení se zapojuje do zásuvky 230V a ovládá tři silová rotační zařízení. Nakonec se zadařilo, výsledkem je modernější řídící jednotka, kterou lze ovládat i pomocí WiFi a mobilní aplikace, nabízí jemnější regulaci kotle, nebo například hlídání naplněnosti zásobníku, vede různé statistiky o spotřebě, vývoj teplot a podobně. Nic z toho původní řídící jednotka nenabízela.

Představa řízení kotle arduinem :-)

Počáteční motivace

Příběh začíná v roce 2008, kdy jsem do čerstvě opatřeného RD před rekonstrukcí pořizoval vytápění. Protože v obci není zaveden plyn, byl v RD nejprve vybaven vytápěním na LTO. To zde bylo už z dob hlubokého socialismu. Bývalý majitel domu však topení před prodejem RD kompletně „vykostil“, v kotelně zůstala akorát nádrž na LTO a zrezlé trubky uřezané a trčící ze zdí. Pro obnovu vytápění se nabízelo:

  • elektrický kotel – drahá elektřina
  • zplynovací kotel na dřevo – člověk polovinu tepla vygeneruje vlastním tělem tím, že denně chodí přikládat (ačkoliv se nabízelo nahradit velkou nádrž na LTO nádrží na vodu)
  • tepelné čerpadlo – relativně drahá záležitost s nejistým výsledkem. Zde by připadalo k úvahu pouze vzduch-voda, protože shodou okolností mám na pozemku věcné břemeno znemožňující výkopové práce – zrýt záhon je ok, díru na bazén si ale nevykopu. Totéž platí o všech TČ vyžadující kopat do země
  • automatický kotel na uhlí nebo na pelety. 

A protože jsem ekologická duše, vybral jsem kotel na pelety a biomasu od firmy Benekov typ Pelling 27. (jasně, v tu dobu bylo na trhu mnoho typů kotlů, nejčastěji mi byl vnucován Viadrus ale byl asi 2× dražší a zrovna to bylo v období, kdy se dotace dávaly jen na výměnu kotlů, a já to kupoval už bez kotle). Jedná se o poloautomatický kotel III. emisní třídy, ve kterém lze spalovat dřevní pelety třídy A1 nebo A2, dále pak agropelety nebo obilí. Prakticky ovšem v kotli spaluji pouze pelety třídy A1, protože cokoliv jiného nevychází ekonomicky. Nižší třídy pelet nebo agropelety mají nižší výhřevnost, ale to neznamená že by o to byly levnější.

Skok do roku 2024, kotel, jenž měl slibovanou životnost 15 let skutečně v některých aspektech dává najevo, že je na konci životnosti. Ovšem to co odchází je řídící jednotka. Čidlo teploty driftuje do záporných hodnot, kotel náhodně hází chyby Err, a v automatickém režimu se náhodně zasekává. To se projevuje tak, že kotel ukončí automatický režim a zastaví se dokud někdo nedojde do kotelny a nezmáčkne křížek „#“, čímž se činnost obnoví. Když se to stane ve 3h ráno nebo v době, kdy jsou všichni v práci, máme pak v domě zimu a kotel se musí znovu zapálit. Nehledě, že to dále zvyšuje nároky na údržbu.

Jako dočasné řešení byly nainstalované spínací hodiny které kotel jednou za hodinu restartovaly. Dále jsem se sháněl po servisu, a asi tušíte, že po 15 letech najdete málo lidí, kteří jsou ochotni se zajímat o můj problém. Většinou vám řeknou, že se na to mám vykašlat a koupit si jejich nejnovější produkt za X stovek tisíc

Původní dodavatel si nadiktoval platbu dopředu jen za to, že se někdo z jejich servisu přijede podívat. Samotný termín mi nebyl sdělen, dokud nezaplatím. Nezaplatil jsem. Človek, který zná detaily řídící jednotky je prý v ČR jen jeden, tomu jsem se nedovolal. Samotnou řídící jednotku lze sehnat na různých e-shopech, kde se prodávají náhradní díly, pouze pokud je skladem a dotyčný e-shop si neřekne částku srovnatelnou s cenou moderního notebooku. A to za předpokladu, že chyba je skutečně v řídící jednotce a ne někde jinde, ale bez návodu a schématu těžko něco zjistím (rady v oficiálním návodu jsou samozřejmě k ničemu, schéma zapojení tam nikdo nedá)

Regulace a legislativa

Zastavme se na chvíli u regulace a legislativy. Na začátek říkám, že nejsem právník a všechny informace mám z internetu. 

V roce 2024 byl zakázán provoz kotlů na tuhá paliva bez emisní třídy a emisní třídy I a II. Zůstávají kotle emisní třídy III, které se už nesmí prodávat, nové kotle musí být emisní třídy IV a V.

Pokoušel jsem se zjistit, jak jsou jednotlivé emisní třídy definované, když už provádím úpravu řízení, abych věděl, co musí kotel splňovat. Moc jsem nepochodil. Obvyklá odpověď zněla, že emisní třídu najdu na štítku kotle. Na to mým je napsaná trojka. Později jsem objevil tabulku pro novější emisní třídy, a v té tabulce jsou uvedeny jen hodnoty pro účinnost, vypouštěné CO, prachu a dalších nečistot. Tyto hodnoty lze ovlivnit dobrým řízením, ale velkou měrou do toho zasahuje konstrukční řešení kotle. Já se budu snažit o maximální efektivitu spalování na základě informací, které lze přečíst z omezeného množství čidel.

Jak legislativa řeší zásah do kotle? Tady se dostávám na tenký led. Pryč je doba, kdy si člověk mohl kotelnu uzpůsobit k obrazu svému k dosažení nejlevnějšího provozu. Už proto, že někteří jedinci to řeší spalováním levného paliva – třeba plastových obalů. Nic z toho já provozovat nemohu ani nechci. Prvním problémem takového přístupu je násobně dražší údržba. Nasypat nepředepsané palivo do kotle znamená zanést si dopravník, spalovací prostor i kouřovody nepořádkem, které násobně prodražuje údržbu a ekonomicky to nedává smysl.

Revize kotlů

Když na štítku je uvedena trojka, tak je to trojka ne? Jak může stát vědět, jestli kotel po tak dlouhé době splňuje požadavky emisní třídy? A stát řekl: „Ale ovšem, tuhle námitku my vyřešíme, zavedeme STK kotlů“. Ale protože kotlů je hodně a nedají se přivést na jedno místo jako auta, musel by někdo oběhnout kotelny, chytře se to nechalo na servisech přidružených původním dodavatelům. Každé tři roky by měl přijet servis do kotelny a zkontrolovat technický stav a vydat o tom revizní zprávu. 

Na druhou stranu, to je super, protože konečně do mé kotelny dorazí servisák, který třeba bude vědět víc, poradí mi, nebo mi požehná (nebo i pohaní) mé řešení. Nakonec nějakou revizní zprávu dostanu, ne? Prakticky to ale funguje jinak. Každé tři roky mi přijde mail s fakturou od mého původního dodavatel Jakmile ji zaplatím, obratem mi přijde další mail s PDF souborem obsahující revizní zprávu. Šaškárna co?

Řešení na hraně

Netuším, zda zákonodárce měl představu, jak naplnění zákona bude probíhat prakticky, zda je tak naivní a nebo to nějaká konspirace, kdy zákon jde naproti k vysokým ziskům dodavatelských firem – už jen to, že se mi po třinácti letech najednou ozval prodejce, od kterého jsem kotel kupoval s tím, že musím něco zaplatit jemu a že on mi pak vydá revizní zprávu. Ten kdo je nakonec bitý je provozovatel kotle. 

Nikde není definováno oprávněnost zásahu do zařízení. Na zařízení se může leccos rozbít, motor, čerpadlo, a zavolat servis, který nemusí být oficiální, je jedno z řešení závady. Instalatéra jsem v kotelně měl celkem 3×, přičemž první dva zapojili kotel špatně. Ten první byl přímo od samotného prodejce a když jsem zapojení reklamoval, bylo mi řečeno, že se na to podívají a už se neozvali – proto mne nemile překvapilo, když se ozvali po 13 letech s fakturou.

Součástí údržby může být výměna čerpadla, podavače, čidel, konektorů a některé díly se už neprodávají a musí se nahradit neoriginálními. Spadá do toho i náhrada řídící jednotky? Tyto otázky jsem si kladl asi dva roky, než jsem se rozhoupal k řešení.

Rozhodl jsem se o neintruzovní řešení. Do tělesa samotného kotle není třeba zasahovat. Kotel má vzadu vyvedenou svorkovnici pro připojení na jednotlivé části kotle. Jediné, co není k dispozici je hodnota teploměru. Tuto obtíž jsem se rozhodl obejít zakoupení dvou nových digitálních teploměrů – proč dvou a jaký to má pozitivní efekt napíšu dále. Nicméně vysledkem mého řešení je, že kotel zůstal netknutý, pouze jaksi běží „v ručním režimu“ který ovládá má řídící jednotka postavená na Arduinu. 

Je to na hraně?  Je. Ale podle revizní zprávy je to v pořádku (revizák tu nikdy nebyl)

Vybavení které je třeba řídit

Automatický kotel na pelety má k dispozici následující zařízení 

  • Podavač - jedná se o šnekovou hřídel, která přesouvá pelety ze zásobníku do prostoru hořáku. Zde jde o retortový hořák. Nové pelety vytlačují pelety z retorty, které hoří. Řízení podavače je založeno na určení časových intervalů kdy podavač běží – přikládá, a kdy stojí – to se označuje jako doba prohořívání. Konkrétně v kotli Pelling 27 – a to je na tom vtipné – najdeme 1F motor o příkonu 120W krerý se prodává jako EPG motor na gril. Nedělám si legraci. Aspoň vím, kam se obrátit, když náhodou vyhoří.
    SSKh 71-6A2 - schválně si to naťukejte do google
  • Ventilátor – jedná se o 1F motor se startovacím kondenzátorem, dá se sehnat za 4700kč. Úkolem ventilátoru je foukat do spalovacího prostoru vzduch a je nutné řídit jeho otáčky.
  • Čerpadlo - jedná se o klasické oběhové čerpadlo, které je stejně prakticky trvale zapnuto a vypíná se pouze při zátopu.

Kromě toho mám k dispozici tyto čidla

  • Teploměr výstupní vody – tuto teplotu měřím na výstupní trubce (měděná) pomocí DS18B20
  • Teploměr vratné vody – stejným způsobem umístěný teploměr DS18B20 na vratné trubce
  • Čidlo přehřátí podavače - jedná se o bimetalové čidlo které je součástí EPG motoru a které chrání motor proti přehřátí. Nicméně ochrana je softwarová, řídící jednotka musí motor odpojit, pokud se čidlo aktivuje (výchozí je sepnuto, při přehřátí rozepnuto)
  • Čidlo otevřeného zásobníku - součástí kotle je zásobník na cca 200 – 225kg paliva. Ten má víko a pokud je víko otevřené, je aktivován koncoví spínač. Je to zdůvodu ochrany obsluhy, v případě otevřeného zásobníku může být obnažen šnek, který může být nebezpečný pro jakýkoliv nástroj, který obsluha zasune do zásobníku – například pohrabáč, nebo koště.

Výběr mikrořadiče

Jako mikrořadič jsem si vybral Arduino s celou vývojovou deskou, přesněji Arduino UNO R4 Wifi s procesorem Renesas a „koprocesorem“ ESP32MINI. Rozhodl jsem se na základě zkušeností s Arduinem ještě z dob UNO na AVR a revize R4 slibovala kompatibilitu s touto deskou. Původně jsem chtěl vybrat desku Mega, protože má víc paměti, ale R4 má ještě víc paměti.

Když se ohlédnu, tak jsem mohl sáhnout i po něčem jiném. Zaujal mne například WeMos D1 R2 UNO ESP8266. Nabízí se levněji a poskytuje víceméně vše, co potřebuji, akorát 3.3V logika

Přítomnost koprocesoru, který má na starost síťové připojení slibovalo větší míru asynchronního ovládání, kdy je to právě ESP chip, který řeší síť a Renesas, který řeší řízení. Mno, byl jsem naivní, víc o tom v odstavci o problémech

Základní řídící cyklus

Kotel má v zásadě dva hlavní režimy a několik dalších vedlejších režimů. Je zde režim automatického řízení dále režim ručního ovládání. Vedlejší režimy například zátop, režim otevřeného zásobníku nebo havarijní zastavení.

ručním ovládání lze prostřednictvím webové aplikace ovládat všechny tři zařízení nezávisle na sobě. Toto je důležité v okamžiku třeba zátopu nebo odstavení kotle, kdy je třeba  natlačit (resp. vytlačit) pelety do retorty, a při zapálení zapnout ventilátor a regulovat otáčky dle aktuálního stavu plamene.

Ruční ovládání se také aktivuje při stisku tlačítka stop (momentálně v aplikaci, plánuji fyzické tlačítko)

automatickém režimu kotel sleduje výstupní a zpětnout teplotu a na základě těchto údajů provádí přikládání do retorty a nastavuje výkon ventilátoru. 

Pokud dojde k chybě, například nepodaří se přečíst teplotu teploměru, teplota vyskočí nad povolenou maximální teplotu nebo naopak klesne pod minimální provozní teplotu, ozve se čidlo přehřátí podavače, přejde kotel do havarijního zastavení a v tomto stavu setrvá, dokud chyba trvá (například se opakuje čtení teploměru a pokud se teplota přečte v pořádku, pak se kotel vrací do předchozího režimu)

Při otevření zásobníku se kotel přepne do režimu otevřeného zásobníku kdy dojde k zastavení ventilátoru a podavače. Režim se ukončí zavřením zásobníku

Režim zátopu je automatický režim s tím, že se nekontroluje minimální teplota po dobu první hodiny od zahájení, poté pokračuje automatický režim normálně. Důvod proč se kontroluje minimální teplota je čistě kvůli odhalení nějaké havárie v kotelně aby se kotel odstavil ještě dřív, než by způsobil škodu. Teploměr se totiž může utrhnout a spadnout na zem a začne měřit teplotu v kotelně. Pokud by toto řídící jednotka vyhodnotila jako nutnost pořádně zatopit, mohl by se kotel přehřát. Navíc jsou teploměry dva, také právě jako záloha pro případ, že jeden selže.

Profily automatického řízení

Původní řídící jednotka používala jednoduchý systém řízení založeném na sledování teploty a zapínání řízení jako se to dělá u dvoustavového termostatu. Z toho důvodu se na zařízení nehledí jako na automatický kotel, ale spíš na „hlouplý“ kotel s automatickým přikládáním. Sama obsluha (uživatel) musí nastavit hromadů parametrů často odhadnutých „z prstu“. Pro plně automatické řízení by kotel vyžadoval víc čidel, například detekci ohně, měření teploty spalin, měření kyslíku ve spalinách (lambda sondu). Nic z toho není k dispozici, tohle je doménou vyšších tříd (a vyšších cen).

Retortové kotle také nemívají automatické zapalování. Předpokládá se, že kotel se zapálí na začátku sezóny a pak už se oheň udržuje. Oproti tomu modernější kotle umí oheň zapálit i udusit podle potřeby (podle mne je to špatná cesta, udržování ohně plně dostačuje účelu).

Původní řídící jednotka tedy funguje tak, že kotel má dva režimy, režim topení a režim útlumu. Během topení kotel opakovaně přikládá podle nastavených intervalů a fouká do spalovacího prostoru nastaveným výkonem ventilátoru. Během útlumu se vše vypíná, oheň se dusí, zůstávají jen uhlíky a v tomto stavu kotel zůstává dokud teplota neklesne

S kotlem zápolím už přes 16 let a vím, že tento způsob řízení má své problémy. Prakticky od prvních zkušeností jsem se sám sebe ptal, proč to není řešeno lépe. Tak jasně, kotel byl představen v roce 2002, a dnes se díváme na dobu před čtvrt stoletím jako na středověk a říkáme „nonononó…“ (J. Werich).  

Jedna z prvních námitek byla, proč kotel neměří teplotu na vratce (roura, kterou se voda vrací do kotle). Přitom je to kritický údaj, na který mě upozornil už ten první instalater při zapojování a školení obsluhy (mě). Teplota na vratce by neměla klesnout pod 60 stupňů kvůli možné korozi (a zejména dehtování) při nižších teplotách. Legrace je, že tuto teplotu musí hlídat obsluha kotle sledováním mechanického teploměru (budík) na vratné trubce. V praxi to znamená, že pravidelně cca jednou za týden, nebo při výrazné změně počasí chodím do kotelny a měním nastavení aby vratka nebyla pod 60 stupňů. Ale zase by neměla být moc vysoko, protože pak topím zbytečně navíc. Já vím, mohu si pořídit chytrou ekvitermní regulaci, která tohle umí řešit pomocí nastavení otopných křivek v závislosti na teplotě uvnitř a venku, ale to je dalších X desítek tisíc navíc a není to univerzální řešení, má to své mouchy. Stačilo by měřit vratnou teplotu a problém je vyřešen. Pokud se pokojový termostat víc otevře (termostatická hlavice), pak je zaznamenána nižší teplota na vratce a to je signál pro kotel ke zvýšení výkonu.

Pokud kotel dodává víc výkonu, než je potřeba, topná voda se ohřeje na „cílovou teplotu“ a kotel se vypne na dobu, kdy je teplota zvýšená. Protože v kotli nehoří, dochází k ochlazování teploty vody. V návodě se člověk dočte, že by kotel neměl přecházet do režimu útlumu na dlouhou dobu. Pokud okolí retorty vychladne, pak se na retortě začnou vytvářet nápeky, černé kusy dřevěného uhlí, které se lepí na stěny retorty a zužují tak efektivní průměr a může to dojít až ke stržení bezpečnostní pojistky na podavači. Pro obsluhu to znamená dost nejisté nastavení parametrů, jejich častou revizi, zároveň jednou měsíčně vyčištění retorty použitím kladiva a dláta (šroubováku). 

Při návrhu vylepšení řízení jsem právě bral v úvahu výše zmíněné problémy a proto můj program není čistou kopii původního řízení. Nová řídící jednotka má 3 režimy z toho 2 jsou profily nastavení a třetí je režim útlumu.

  • Plný výkon - profil nastavení 1
  • Snížený výkon – profil nastavení 2
  • Útlum

U obou profilů se nastavují tři parametry: doba přikládání, doba prohořívání (podavač stojí),rychlost ventilátoru. Uživatel v aplikaci nastavuje požadovaný výkon v kW a rychlost ventilátoru v procentech. Přepočet výkonu na parametry provádí sama aplikace na základě nastavené výhřevnosti a údaji o váze paliva přeneseného za daný čas. Výhřevnost u pelet je 17MJ/kg, a přenesená hmotnost se dá změřit třeba tak, že se změří čistý čas podavače, za jaký se přesune jeden 15kg pytel. Aplikace je schopna provádět toto měření za běhu.

Jednotlivé profily se přepínají podle teploty na vratce. Pokud teplota klesne pod nastavenou hodnotu (nastaveno mám na 63 stupňů), přejde řízení do plného výkonu. Pokud teplota vystoupá nad 63 stupnů, přejde řízení do sníženého výkonu. Pokud by výstupní teplota přesáhla maximální teplotu (typicky 80 stupňů), přejde řízení do režimu útlumu.

Po třech týdnech beta provozu jsem nezaznamenal přepnutí do útlumu, kotel střídavě přepíná mezi plným a sníženým výkonem. Plný výkon je nastaven na 15kW (8/33/50%), a vždy vede ke zvýšení teploty kotlové vody. Snížený výkon je nastaven na 4kW (5/100/30%) a způsobuje pomalé snížení teploty kotlové vody. Důležité je, že v žádném případě retorta nevyhasne a zaznamenal jsem nižší výskyt nápeků. Výše uvedené řízení nezpůsobilo vyšší spotřebu pelet, jak jsem se obával

Řízení kotle podle vratné teploty se mi osvědčilo.

Extrapolace teploty

Protože celý systém má velkou setrvačnost a nějakou dobu trvá, než se ohřátá voda vrátí zpět do kotle, zavedl jsem řízení podle extrapolace – odhad budoucího vývoje teplot. Provádí se to jednoduše pomocí lineární regrese krátké historie (asi 10 minut) vývoje teplot. Délku extrapolace lze nastavit pro oba teploměry zvlášť. Tento systém se mi osvědčil, zejména je to znát při používání TUV (rodina se koupe), kdy kotel je schopen zvýšit výkon dřív, než vratka začne klesat nebezpečně pod 60 stupňů

Ovládání motorů

Z vývojové desky ovládám tři točivá zařízení (motory). Zapínání těchto zařízení mám řešeno pomocí SSR relé. Modul s dvěma rele pro menší motory (čerpadlo, ventilátor), dále samostatné relé SSR-10DA pro podavač. V tomto případě jsem si vědom, že je to trochu předimenzované, ale v původním zapojení se používá tranzistor, který spínal velké elektromagnetické relé se zhášecím obvodem. Takže proto.

Oříškem bylo řízení ventilátoru. Tam je potřeba řídit otáčky. To nejprve ukazovalo na klasické triakové řízení pomocí pulsní modulace, ale jednoduché řešení pro Arduino jsem nesehnal. Všechna řešení vyžadovaly aktivní hlídání průchodu střídavého proudu nulou (tím se aktivuje přesné časování sepnutí triaku).  Problém je, že ventilátor má rozběhový/běhový kondenzátor a tam se tento způsob řízení nehodí. Jiní mi radili koupit frekvenční měnič. To je drahé řešení. Navíc se původní řízení takto neprovádělo. Všiml jsem si, že stará řídící jednotka posílá do ventilátoru delší pulsy s malou frekvencí. Nakonec se to tedy vyřešilo následovně: Protože ventilátor má obrovskou setrvačnost (je to takový setrvačník), postačí SSR relé spínaný pomocí PWM s malou frekvenci, aktuálně mám něco kolem 6.8Hz. Střída pak určuje rychlost otáček. Impuls by ale měl být aspoň tak dlouhý jako je jedna vlna 50Hz. Použil jsem SSR, která spínají při průchodu nulou a to by mělo pomoci běhovému kondenzátoru získat dostatečnou energii pro řízení fázově posunutého druhého vinutí motoru.

Plánovač

Cesta k tomu, jak celý cirkus správně řídit, vede přes plánovač. Procesor Renesas má, pokud vím, pouze jedno jádro, takže jakékoliv paralelní řízení musí být řešeno pomocí kooperativního multitaskingu. To je ideální prostředí pro použití C++ korutin, kdy přes co_await může docházet k přepínání úloh. Problém je, že Arduino je dodáváno s GCC-7, které ještě korutiny neumí. Velká škoda, moc jsem se těšil. Namísto korutin jsem tedy musel sáhnout po klasické třídě Task (AbstractTask), která implementuje metodu void run()

// kód je určen pro jednojádrové CPU

class AbstractTask {
public:
    static constexpr TimeStampMs disabled_task = static_cast<TimeStampMs>(-1);
    static uint8_t _reschedule_flag ;
    static AbstractTask *_cur_task;

    virtual void run(TimeStampMs cur_time) = 0;
    virtual ~AbstractTask() = default;

    void resume_at(TimeStampMs at) {
        if (at != _scheduled_time) {
           _scheduled_time = at;
           if (_cur_task != this)
               _reschedule_flag++;
        }
    }
    void stop() {
        resume_at(disabled_task);
    }

    TimeStampMs get_scheduled_time() const {
        return _scheduled_time;
    }

    void resume(TimeStampMs cur_time) {
        _scheduled_time = disabled_task;
        _cur_task = this;
        run(cur_time);
        _cur_task = nullptr;
    }


protected:
    TimeStampMs _scheduled_time = 0;

};

Plánovač si jednotlivé úlohy řadí podle get_scheduled_time() do prioritní fronty. Pro úlohu na vrchu zavolá resume() a po tom, co úloha skončí, tak se zařadí do prioritní fronty. Předpokládá se, že úloha ve své obsluze zavolá resume_at, pokud to neudělá, pak se považuje za zastavenou.

Pokud se resume_at volá mimo aktuální úlohu, pak se aktivuje _reschedule_flag. Tento flag donutí plánovač k přegenerování prioritní fronty – zpravidla proto, že některá úloha změnila své plánování, nebo byla spuštěna.

Aktuální stav každé úlohy tak eviduji pomocí enum proměnné a zpravidla implementace run() obsahuje velký switch/case, jako třeba zde (asynchronní čtení teplot)

Vývojové prostředí 

Celý program má cca 128kB kódu (z 256kB) pro procesor ARM Renesas. Celkem 16kB je alokováno pro statické proměnné (z 32kB). Kód samotné řídící jednotky má 5949 řádků, knihovny dalších 8220. To je relativně velký projekt, který se nevejde do jednoho sketch.ino souborů. Přesto abych si co nejvíc ušetřil trápení s toolchainem, napsal celou řídící jednotku jako knihovnu pro Arduino-IDE a samotný sketch kotel.ino po startu pouze zavolá vstupní bod knihovny. 

//kotel.ino

#include "kotel.h"

void setup() {
  kotel::setup();
}

void loop() {
  kotel::loop();
}

To mi umožnilo překládat program v Arduino-IDE včetně funkce uploadu na board a dokonce jsem mohl využívat i vestavěný debugger. Nicméně velká část kód se vyvíjela a ladila v simulátoru

Simulátor? Jaký? V zásadě ad-hoc. Projekt mám zároveň napsaný jako CMake projekt pro linux. Ke knihovně kotel je připojen zavaděč (obsahuje main() ) a jednoduchý simulátor, který implementuje všechny arduino specifické operace, jako digitalWrite, digitalRead, objekt Serial atd. Musel jsem implementovat simulace rozhrani WiFi a samozřejmě rozhraní OneWire, na které jsou napojené teploměry DS1820.

Pokud se projekt tedy zkompiluje pomoci CMake, pak je výsledkem program bin/emul, který lze spustit v linuxu, lze mu předložit krátký script jako scénář vývoje vnějších dat (teplot, čidel) a lze nastavit i rychlost simulace (protože kdo by chtěl čekat hodinu na zátop, že?)

Prezentace běhu

Display

Ač jsem původně plánoval, že použiji vestavěnou DotMatrix přímo na Arduino UNO R4, nakonec se to ukázalo jako nepraktické. Display je malý, má malé rozlišení a moc údajů se tam nevejde. Pořídil jsem tedy 32×8 LED matici. Na 4 moduly se vejdou dvě teploty, symboly představující aktuální stav řízení a ukazatel naplnění zásobníku

Výpočet naplnění zásobníku se provádí z informace o počátečním naplnění a doby běhu podavače, přičemž je známa průměrná hodnota množství dodaného paliva za časový interval. Pak lze jednoduše pouze pohledem na display zjistit jestli není čas zásobník opět naplnit (bez nutnosti otevírat zásobník)

Webová aplikace

Aby bylo možné jednotku řídit z webového prohlížeče, je součástí webová aplikace. Tato aplikace je napsána v čistém vanilla JS + HTML + CSS + nějaká vektorová grafika. Všechny části jsou pak slepeny do jednoho velkého souboru, komprimován pomocí gzip a tento soubor uložen jako statická data ( constexpr) přímo do binárního image. Pro ovládání tedy vyžaduje prohlížeč, který umí gzip jako content-encoding. Díky tomu je výsledný blob kratší téměř 4×. A že přenést pár kilobajtů po WiFi z ESP32 je problém si povíme dále

Pro zbývající komunikaci mezi webovou aplikací a řídící jednotkou jsem zvolil protokol WebSockets s binárními rámci. Důvodem je opět problémy s přenosem velkých dat. Aby webová aplikace byla „připojená“ k řídící jednotce, posílá pravidelně 1× za sekundu request na stav a řídící jednotka ji odpovídá svým stavem. Je to komunikace čistě request/response. Výhodou použití WebSockets je malá velikost rámců. Oproti klasickým http requestům, do kterých například chrome byl schopen nacpat 1kB hlaviček

Webová aplikace a testovací režim (ještě bez krabice)

Webová aplikace a testovací režim (ještě bez krabice)

Zabezpečení

Žádné velké zabezpečení jsem nedělal. Celá komunikace probíhá po HTTP (nezabezpečený protokol) v rámci vnitřní sítě. I tak mne trochu strašila myšlenka zejména škodolibé návštěvy, která dostane přístup do naší WiFi (na internet) a objeví adresu webové aplikace. Mohla by mít škodolibý nápad mi rozhasit nastavení kotle a třeba ho nastavit do nebezpečného stavu. 

První level zabezpečení je neumožnit návštěvám přístup do intranetu. K tomu máme návštěvnickou síť. (guest network)

Další úroveň je přímo ve webové aplikaci. Jakýkoliv prohlížeč může být nebezpečný a proto přístup k ovládání je omezeno jen určitým uživatelům. A abych si nemusel vytvářet nějaké databáze hesel, zvolil jsem si jako oprávněného uživatele každého, kdo má přístup do kotelny. Ověření probíhá přes display zařízení ze kterého se musí opsat kód a tím se odemkne řízení z prohlížeče. Samotná session je pak zabezpečena HMAC hashem s tajným heslem uloženým v EEPROM řídící jednotky. Kdykoliv lze pak heslo změnit a tím všechna připojená zařízení odpojit.

 Náhodná návštěva zpravidla nechodí do kotelny opisovat si kód, minimálně jí tam nepustím a nebezpečný vtípek je tak obtížnější realizovat.

Samozřejmě návštěva s notebookem a skenovacími nástroji by mohla nabourat nezabezpečenou komunikaci, ale to už se dostáváme na jinou úroveň.

Fyzické provedení

Celá řídící jednotka je umístěna do rozvaděčový box s průhledným víkem a s DIN lištou. V boxu je jistič 6A, spínaný zdroj 12V / 15W, Arduino v plastovém obalu s držákem na DIN lištu, krabice pro SSR modul na DIN lištu a SSR relé s držákem na DIN lištu, řešení celkem zabírá 12 modulů. Pracovní a ochranné vodiče jsou svedeny na můstky. Z krabice jsou vyvedeny kabely ke konektorům, které se připojují na různá zařízení nejen na kotli, ale i například na teploměry, nebo na čidlo zásobníku, tak aby se jednotka dala kdykoliv odpojit.  Každá dvojice  konektorů je jiná, tak aby se při připojování nemohli poplést.

Osazení

Překážky

A teď si povíme něco o překonávání překážek během vývoje. Na začátku bývá člověk optimistický, má skvělý plán a všechno bude fajn. Na konci za sebou vidí překážkovou dráhu, kterou nakonec zdolal, i když to často znamenalo významný zásah do plánu vývoje

Tragická práce „open source“ komunity

První a asi nejhorším problémem byl náraz na tvrdou realitu schopností open source komunity. Na jednu stranu bych asi měl být vděčný, že udržují kód zadarmo, na druhou stranu… né tak zadarmo, protože je vlastně vydavatel modulů Arduino zneužívá. Já za board zaplatil 2× 30 EUR (viz dále proč 2×) , to není málo peněz. Za to mám nějaké minimální požadavky na kvalitu kódu

A obdržel jsem mizerný kód

I takové věci najdete v produkčním kódu

  • Programátoři neumí C++. Ale vůbec. Tady se ve velkém nadává na ten jazyk, ale nikdo se ho nehodlá naučit. Nikdo nesleduje, že ten jazyk se dávno posunul. Je to jako by se dneska programovalo v Javě 1. Ale ti lidi se ani neseznámili s tím,jak fungují věci v STL. Nic, jim je to jedno, oni to tam naprasí a nějak to funguje. Co řádek to adept na přepsání. Drtivá většina má silný vibe z C, jako by si právě někdo prošel práci s třídami a rychlokurz v STL. Přitom Arduino-IDE se dodává s GCC-7 který umí C++17. Víte třeba, že existuje string_view?
  • Ignorování pravidel embed programování. Tak sakra, když už rezignuju na C++, proč aspoň nedodržuju pravidla embed programování? Třeba eliminace alokací? Ale proč se snažit, uvnitř použiji std::string a std::vector. 
  • RVO nikdo neuznává, nepoužívá, fůj, dokonce mi bylo vyčteno na githubu, že takhle se to nedělá. Funkce vytvoří std::vector s daty, které potřebuji a implementuje komplikované rozhraní pro extrakci těchto dat, namísto toho, aby ten vektor vrátila jako výsledek. Prostě Céčkové rozhraní.
  • Pokud jsou mikrořadiče používány pro řízení v reálném čase, tak proč jsou všechny operace blokující? Chceš přečíst teplotu… ok, počkej si 200ms na výsledek. Jo ty bys chtěl během té doby dělat něco jiného? Smůla, napiš si to sám. Nejhorší je snad třída WiFi. Celá komunikace s ESP je blokující. Zavolám scanNetwork a program se mi zablokuje na 10 sekund. Jo ty máš WDT na 5 sekundu? Že je to max? Tvůj problém. Stejně tak WiFi.begin() opět se čeká 10 sekund. Odeslání dat do socketu je také blokující s timeoutem 1 sekundu, nedá se změnit. Nedá se zapnout NONBLOCKING, nedá se poznat, že volání bude blokující. Jeden z důvodů, proč komunikace probíhá po websoketech jako ping-pong je přesně tento důvod. Program má jistotu, že webová aplikace už zpracovala předchozí rámce a výstupní buffer je tedy prázdný a další komunikace proběhne zase až s dalším requestem.
  • Softwarové vybavení celého ekosystému kolem Arduina jako by vypadl z pera jistého Potěmkina. Na venek to vypadá jednoduše, uvnitř najdete zmatek a chaos.

Jak si (ne)odpálit board

Důvod, proč jsem si Arduino pořizoval 2× byl ten, že první kus odešel do křemíkového nebe po mé chybě připojení pinů v kombinaci špatné implementace oficiální OneWire knihovny (která mimochodem, není oficiálně podporována na Renesas, ale to se dozvíte až ve zdrojácích)

Napřed ale malé opáčko kolem stavové logiky V/V pinů. Piny mohou být v následujících stavech

  • LOW – pin je spojen s 0V vodičem (bez odporu)
  • HIGH - pin je spojen s 5Vvodičem (bez odporu)
  • high impedance (Z) – pin se tváří  odpojen, k oboum úrovním má odpor v řádu megaohmů a lze číst úroveň
  • pull up (PU)- pin je spojen s 5V přes odpor řádově 10k, lze číst úroveň
  • pull down (PD) – pin je spojen s 0V přes odpor řádově 10k, lze číst úroveň

Aktivní HIGH a LOW jsou tedy považovány za výstupní režim, ostatní za vstupní. Není tedy dobrý nápad pokud člověk uzemní pin, který je nastaven na HIGH, nebo naopak připojí pin na 5V když má úroveň LOW. Častěji se tedy spíš uzemňuje, typicky čidlo při aktivaci uzemňuje na nulu, přičemž mezi pinem a napájením je velký odpor (PULL-UP). Oba problémy, tedy uzemnění HIGH nebo napojení LOW na 5V mohou poslat board do křemíkového nebe, protože je to zkrat přes aktivní součástky v mikrořadiči – a to se mi také stalo. Jenže jak to? Jak se objevil HIGH na pinu, který byl určen pro OneWire sběrnici?

Je třeba říct, že OneWire sběrnice je navržená pro řízení s otevřeným kolektorem. To znamená, že sběrnice je buď řízena LOW, nebo ve stavu Z. Na sběrnici musí být instalovan odpor cca 4k7, který v okamžiku, kdy žádné zařízení na sběrnici není připojeno, nastavuje sběrnici do slabého HIGH. To umožňuje aby víc zařízení řídílo sběrnici do LOW a výsledkem je logická operace AND všech zařízení. Takže pokud omylem územním pin s OneWire sběrnicí, tak by se nemělo nic stát, že? Že?

Jak se to vezme, originální knihovna OneWire totiž takto sběrnici neřídí. V okamžiku kdy vysílá data na sběrnici, tak ji řídí jako HIGH-LOW nikoliv jako by člověk čekal Z-LOW. Tady máte důkaz. Chyba lávky je samozřejmě v tom makru, které nikdo pro Renesas netestoval a které se přeloží takto ( digitalWrite(...,HIGH))

Mno, a protože se nerad rozčiluju a nerad nadávám v angličtině, stejně by to bylo jako házet perly prasatům, tak jsem krátce přispěl do místního issue trackeru (bez odezvy) a celou OneWire knihovnu přepsal k obrazu svému.

Ale chápu ty těžkosti. On je třeba problém už v rozhraní Arduina v příkazech digitalWrite a pinMode. Už jen to, že je to rozděleno na dvě operace, už jen to, že pinMode(pin,OUTPUT) způsobí, že pin se přepne do output a automaticky přejde do LOW. I když potom udělám digitalWrite(pin,HIGH), tak už můžu mít board po smrti, na ten krátký okamžik, co jsem mohl tímto způsobem vyrobit zkrat. A jak se mohu spolehnout na to, že pinMode vždycky přepne na LOW? Co když náhodou vyjde nová verze a ta bude přepínat na HIGH?

A tím se vracíme k předchozímu odstavci „mizerná práce“ open source komunity. 

Pro moje maximální bezpečí jsem všechny výstupní signály přepsal do režimu řízení PU-LOW, tedy aktivní jsou vždy v LOW a v klidovém stavu je tam pull-up rezistor. Pokud cokoliv náhodou vyzkratuju (otočím omylem konektor), tak je výsledkem akorát nefungování a chyba, ne zničený board

Driftující teploměry

Měl jsem za to, že teploměry společnosti Dallas DS1820 mají nějakou rozumnou přesnost a stabilitu. V originální řídící jednotce se používá PT1000 a buď ten, nebo následně obvod na zpracování signálu, nějak driftuje, protože pokud je v kotelně 15 stupňů a kotel je studený, ukazuje kotel chybu E2, což značí chybu teploměru a já vím, že je to tím, že ukazuje záporné číslo. Stačí v kotli zatopit, chyba E2 zmizí a teploměr začne ukazovat 0, pak 1, pak 2, atd…

Jaké překvapení bylo, když v rámci vývoje a testů v obýváku u počítače v přítomnosti zimou se třesoucí manželky válíc se na gauči jsem naměřil teplotu 27 stupňů. Na pokojovém teploměru svítilo 24 stupňů. Má důvěra v DS1820 významně klesla.

Následně jsem řešil, že bych drift nějak kalibroval, ale předně bylo potřeba zjistit, jaká je skutečná teplota. Jenže co použít jako etalon (promrzlá manželka se jako etalon použít nedá). Na kotlovém tělese jsou dva teploměry, jeden je pevný a mechanický s tlakoměrem, nezasáhlo se do něj od instalace. Tam se teplota od DS1820 liší asi o stupeň dolu, ale člověk si na to musí vzít lupu, protože stupnice je malá a nahuštěna. Druhý je mechanický na vratce a dá se vyjmout a přeštelovat. Tento teploměr ukazoval o 3 stupně víc než DS1820 (měl by ukazovat méně?). K přeměření jsem použil i teploměr od syna, který si ve sklepě vaří pivo a má na to přesné teploměry. Ten ukazoval o 6 stupňů více než než DS1820 a tedy o 3 stupně víc než teploměr na vratce. Zmatek?

Nakonec jsem sáhl ještě po vaření teploměrů. Všechny jsem postupně ponořil do vroucí vody. Oba DS1820 ukázaly 99.5. Mechanický teploměr ukázal 106, ten jsem tedy přešteloval. Výsledkem je, že při teplotě 63 podle DS1820 mi mechanický teploměr ukazuje 60 stupňů. Mimochodem to je ten teploměr, na který mě tehdy instalatér při instalaci kotle upozorňoval, že nesmí ukazovat pod 60. Tak která teplota je správně?

Zatím jsem se rozhodl, že teplotní čidla nebudu softwarově kalibrovat. Ale uvidíme

Problémy se sítí

Na začátek je třeba říct, že v kotelně je mizerný signál Wifi a zařízení má celkem vysoký packet loss. To by ale nebyl problém, pokud webová aplikace je k tomu přizpůsobena. Například neposílá paralelní requesty, všechny requesty jedou seriově za sebou tedy je to ve stylu ping-pong. Pokud se sejdou dva requesty, řadí se do fronty. Pokud dojde k výpadku packetu, tak se response opozdí a tím celá fronta. (ano, veškerá komunikace je async-await). Websocketové spojení má timeout 10 sekund, pak se zavírá a znovu navazuje. Mám zkušenost, že chrome je schopen držet mrtvé spojení i několik minut, než mu dojde, že je mrtvé.

Záznam websocket komunikace

Záznam komunikace wireshark

Mé budoucí plány s vývojem zahrnují i pravidelné ukládání statistik a k tomu potřebuji hodiny reálného času. Po několika neúspěšných pokusech rozběhat RTC - mimochodem totálně zprasená třída, kde se autor nebyl schopen rozhodnout, zda použije linux timestamp nebo tm strukturu nebo něco třetího vlastního a tak to všechno zkombinoval dohromady, a tím způsobil, že kód narostl o 2% a stejně to nefungovalo – jsem se rozhodl, že si naprogramuju vlastního NTP klienta a čas budu odvozovat od interního timeru ( millis()). NTP klient bude čas aktualizovat jednou za 24 hodin. 

NTP klient používá UDP protokol, který implementuje WiFiUDP. I tuhle třídu jsem si nakonec přepsal, ale objevil se problém. Po navázání spojení s wifi se celý board zablokoval a po pěti sekundách ho sestřelil WDT. Pátral jsem co se děje a zjistil jsem, že to celé padá na beginPacket() pokud cílová destinace je DNS záznam (pool.ntp.org). Co by to asi mohlo být?

Je to jednoduchá hádanka. Samozřejmě, byl to gethostbyname který se volá v samotném firmware ESP32, tedy ještě mnohem hloubš, než bych mohl zasáhnout bez toho abych flashoval firmware. Existovala jediná cesta jak to vyřešit. Správně, napsat si ještě vlastního DNS klienta, který by to vyřídil asynchronně.

NTP klient

Simple DNS klient

v obou případech radil ChatGPT

EEPROM

Podle datasheetu má Renesas k dispozici EEPROM o velikost 8KB. To je dost na uložení konfigurace a nějakých statistik. 

Ve skutečnosti to je jinak. Renesas nemá žádnou „EEPROM“ (ve smyslu jak funguje na AVR), jde o část flash vyhrazenou pro data, kterou jde flashovat za běhu programu. Přístup k tomuto „zařízení“ je jiný, než u AVR. Už proto, že mazatelná stránka má velikost 1KB, tedy nedají se mazat jednotlivé bajty jako u AVR. To mimojiné znamená, že EEPROM má 8 nezávisle mazatelných stránek.

Pokud se podíváte do zdrojáku k EEPROM pro Renesas, zjistíte, že se jedná o emulaci EEPROM na AVR, ale celá emulace naprosto mizerně naprogramovaná. Změna jakéhokoliv bajtů v EEPROM znamená smazání a naprogramování celé stránky. Tím se drasticky snižuje životnost flash nehledě na to, že v jeden okamžik může při zápisu dojít ke ztrátě celé stránky. A to v situaci, když dojde k výpadku napájení nebo resetu zařízení v okamžiku, kdy se dokončilo mazání stránky a ještě nezačal nový zápis. Během té doby je stránka uložená v RAM. O nějakém wearlevelingu si můžeme nechat jen zdát.

Jo a tenhle oficiální tutoriál je výživný. Zvlášť když člověk ví, jak je to implementované. Proboha, hlavně podle toho neprogramujte!

A ještě jeden odkaz na blog, kde se odhaluje celý podvod

Asi nemusím říkat, že se mi tohle řešení ani náhodou nelíbilo, takže vznikla alternativní implementace, která se na EEPROM dívá jako na kruhový buffer sektorů o velikost cca 20 bajtů (22 raw). Vše se zapisuje na top bufferu a spodek bufferu se maže. Pokud ve spodku bufferu jsou důležitá data, překopírují se před tím na top. Tak by nemělo dojít ke ztrátě při zápisu pokud dojde k výpadku proudu, protože ta informace vždycky někde bude zapsaná. Bohužel to také znamená, že efektivní velikost flash je o 1 stránku menší, protože jedna stránka vždy musí zůstat prázdná, aby byla připravena přijmout sektory, které se nesmí smazat. Naštěstí tolik informací v konfiguraci nemám a tak využívám celých 8KB pro wearleveling.

Vývoj nekončí

Ač už mám řídící jednotku „nasazenou“ v provozu a zřejmě bude po zbytek zimní sezóny řídit kotel, tak to není ještě konec. Co tedy mám v plánu

Ukládání statistik.

Aktuálně se evidují jen celkové a průměrné statiky od počátku věků (od vynulování). Bylo by dobré pravidelně statistiku ukládat aby je bylo možné stáhnout do počítače k další analýze (třeba do excelu). Otázkou je samozřejmě, kam je ukládat? Nabízí se možnost ukládat je na internet, k tomu si musím zřídit nějaké úložiště (webhosting zdarma? většina cloudových úložišť si to nechají zaplatit, a nemám prostor v mikrořadiči implementovat třeba google drive). 

Další možnost je teoretická, totiž firmware ESP32 obsahuje úložiště o celkové kapacitě 512kB prezentovaný jako WiFiFilesystem. Bohužel implementace na straně Renesas není dodělaná a je otázkou, jak moc dobře odladěná implementace na straně ESP32 bridge.

Opravdu vtipná je poznámka Opravím to v příštím commitu, zatím stačí vytisknout obsah souboru na řádce 35.

Další možností je připojit k Arduino adaptér na SD karty. To ovšem znamená další knihovnu a další dráty do už tak nepřehledného vrabčího hnízda drátů v krabici.

Měření emisí

Určitě bych chtěl nějak měřit emise a podle toho upravovat řízení. Jednou cestou je měření O2, ale zapojení lambda sondy není triviální, navíc lambda sonda vyžaduje extra zdroj na vyhřívání (je instalovaný 15W spínaný zdroj na 12V, ten by nestačil)

Další možnost, kterou chci vyzkoušet je senzor MQ-9 na měření koncentrace CO. Tento senzor však nemá provozní teploty pro umístění v kouřovodu, rozhodně by nemohl být přímo u kotle, možná někde dál. Nebo vyrobit nějakou jímku mimo kouřovod, kde by se to měřilo?

Pokud by měl někdo nápad, jak to udělat jednoduše, tak jsem jedno ucho.

Klávesnice

Ač se celá jednotka dá ovládat z mobilní aplikace, přesto mi chybí fyzická klávesnice. Jo, jsem trochu konzervativní člověk. Stačit budou tři klávesy, které budou mít několik významů podle aktuálního režimu. V plánu mám

  • Přepínání mezi ručním a automatickým režimem
  • V ručním režimu dvě tlačítka na ovládání podavače a ventilátoru
  • V režimu kdy je otevřený zásobník by mohla obsluha naťukat do zařízení počet nasypaných pytlů. Stisknu tlačítko s každým nasypaným pytlem, je to pohodlnější, než si pamatovat během nošení pytlů poslední číslo.
  • V ostatních režimech například přepínání zobrazených informací.
  • Možnost provádět nastavení pomocí tlačítek by byl oříšek, ale také by to šlo. K nastavení času na hodinkách mi taky stačí tři tlačítka 

Klávesnici chci realizovat pomocí tří drátů, dva napájení a jeden signální. Je to řešení „one-wire“ pomocí odporového děliče. Stisknuté tlačítko se pak identifikuje změřením úrovně na analogovém vstupu A0.  A je snadné sehnat trojžilový kabel na míru (v každém hobbyshopu)

Závěr

Už bude teplo

Jsem na konci, děkuji za pozornost. Projekt se mi zdařilo dokončit do konce roku a to během zimní sezóny, kdy se právě topí. Celý příspěvek jsem chtěl pojmout jako vtipné sdílení kladných i negativních zkušeností. Prostě jsem si chtěl trochu postěžovat a ukojit své grafomanské potřeby. Tak snad se mi to podařilo

Tady ještě krátké video (facebook)

Kód je na githubu

Sdílet