UPDATE: Jak (ne)měřit sníh strojově bez nutné korekce člověkem?

Měřit sníh je přece jednoduché, třeba jednou řeknete si. Vždyť stačí zapíchnout do sněhu pravítko na 3 místech a podívat se z boku, kolik to tam ukazuje. 3 hodnoty zprůměrujeme a máme hotovo. Nebo tyčku s čísly zapíchneme rovnou do země a můžeme odečítat dálkově třeba dalekohledem. Nojo, ale jak to udělat tak, aby to za nás dělal sám počítač? Nebo nějaká automatická meteostanice?

Poslední aktualizace: 24 listopadu

Jak to dělají profesionálové?

Český hydrometeorologický ústav to dělá poněkud jinak. Na rámu asi 2 metry vysokém má umístěné poměrně dost drahé ultrazvukové, řekneme dělo. Je to technicky vzato membrána produkující ultrazvuk, která ho zároveň přijímá. Ze specifického útlumu a s pomocí korekcí dle teploty a vlhkosti dokáže poměrně přesně vypočítat výšku sněhu. Je to ale strašně komplexní, protože sníh sám o sobě v závislosti (nejen) na teplotě, při které padá a dále při které leží na zemi, mění svoje vlastnosti. Například ledová krusta na sněhu při ledovce odráží větší množství zvuku než jemný prašan, či velké vlhké vločky s výraznou porézností. Je to prostě věda, kterou lze bez výzkumu a velkého množství financí jen těžko replikovat.

Ultrasonic Snow Depth Sensor (USH-9) provozovaný ČHMÚ; Zdroj: CHMI.CZ

Jak jsem postupoval já?

První pokus byl samozřejmě ultrazvuk. Nejběžnější ultrazvukový senzor na trhu je HC-SR04 se schopností detekce vzdálenosti mezi 2 cm až 4 metry. Je parádní, až na to, že jeho ultrazvuková vlna je natolik slabá, že ji sníh jednoduše pohltí nebo rozptýlí. Nelze s tím detekovat nic.

HC-SR04

Druhý pokus byl laser. Vl53L0X je perfektní čidlo pro obdobnou aplikaci, jako to ultrazvukové. S jediným rozdílem. Vzdálenost měří pomocí zpoždění odrazu světla. Světlo se stejně jako zvuk šíří vzduchem specifickou rychlostí, dá se proto spočítat jednoduše vzdálenost. Sníh je přece bílý, bude to fungovat…
Jediným nepřítelem venku je vám vzdušná vlhkost a nedejbože mlha. Paprsek je tak drobný, že lze jednoduše roztříštit nebo se umí lehce vrátit zpět klidně 2 cm od čidla, protože aerosol. Tento pokus byl tedy také fiasko.

Třetí pokus byly kapacitní senzory. Ani já jsem tomu však nevěřil právě kvůli již dříve zmíněným různorodým vlastnostem sněhu při různých atmosférických a tepelných podmínkách. Měření proto nemohlo být konzistentní. Existuje samozřejmě *jedna školní zahraniční práce*, která pojednává o měření s větším množstvím kapacitních čidel. I když to třeba funguje, rozlišení nikdy nebude uspokojivé.

Sníh zároveň nelze měřit stejně jako hladinu vody právě kvůli poréznosti a různorodé vodivosti.

Tak jak to tedy udělat?

Pokud neřešíme typ sněhu, stačí nám především výška sněhu. Množství srážek navíc měřím vyhřívaným srážkoměrem, dá se tedy spočítat spousta dalších zajímavých údajů.

Zbývá nám tedy vizuální pozorování. V dnešní době je naprosto běžné a velmi populární využití automatické detekce různými nástroji pomocí kamer. Rozhodl jsem se proto pro jednoduchou strojovou detekci bílé vrstvy sněhu přes černé sněhoměrné tyče. Má to ale několik specifických problémů…

První koncept: vlevo nahoře IP kamera s přísvitem a 4 černé tyče

1. problém: detekce v noci

Poměrně jednoduché řešení je využití kamery s infračerveným přísvitem. Sníh je v noci tak proti černým tyčím velmi dobře kontrastní a přes den by neměl být problém.

2. problém: možný pohyb kamery a deformace obrazu

Tento problém je poměrně specifický. Konkrétní kamera je poměrně širokoúhlá, má pouze HD rozlišení a sem tam se může stát, že se trošku pohne vůči tyčím, nebo se hnou časem tyče samotné a může dojít chybné detekci. Určité zkreslení horní části obrazu navíc vzniká při přepínání IR filtru mezi dnem a nocí. Je to jen o kousek, ale úplně to stačí pro možnou chybu měření.
Jednoduchou detekcí specifických aruco* značek v obraze a jejich pozic, lze vypočítat tuto odchylku a vždy se trefit na správné měřené místo tak, aby byla přesnost pokud možno co nejvyšší. Značky ale nelze umístit z důvodu zasněžení na spodek tyčí. Budou tedy pouze na vršku každé z tyčí. Sněhoměrné tyče budou muset být proto k zemi připevněny nehybně a pevně, aby nedošlo k nechtěnému pohybu a dolní bod bude muset být také softwarově fixní.

3. problém: výpočetní výkon

Protože vše poběží na starém Raspberry Pi 2, na kterém běží navíc všechno ostatní, nechceme zbytečně bez sněhové nadílky zatěžovat počítač. Mohla by tomu pomoci detekce 3 černých ploch umístěných na zemi mezi tyčemi, které dokud se nezasněží (tedy nebudou všechna 3 místa bílá), celý proces se jednoduše přeskočí a změřeno bude 0 cm.

Další možností může být strojové učení, které sice nemusí nutně zefektivnit celý proces detekce, ale může zvýšit přesnost a náchylnost systému v případě vyššího šumu a různých jevů jako je ledovka, silný déšť, sněžení a nebo třeba v protisvětle. To momentálně neřeším a budu řešit třeba později.

Další jednoduchou kontrolou velikosti měřeného obrázku můžeme eliminovat problematický snímek (nekompletní nebo příliš tmavý) a při teplotách nad 5 stupňů jednoduše nemá smysl měření provádět. Tím se omezí zbytečná analýza.

4. problém: rozlišení detekce

Kamera má, jak jsem již výše zmínil, pouze HD rozlišení. Na tyče pohlíží přibližně z metrové vzdálenosti a jejich výška je 50 cm. Nepředpokládám, že by v nejbližší době v měřené lokalitě mohlo nasněžit více sněhu.
Podle posledních měření je rozlišení tedy něco přes 13 pixelů na 1 cm. Kvůli přeexponovaným hranám lze tedy bezpečně počítat s rozlišením 1 cm. Uvidíme časem, zda to bude udržitelné třeba po 0,5 cm.

5. problém: neočekávané chyby měření

Představte si, že měření probíhá během sněžení. V infračerveném přísvitu je vysoce pravděpodobné, že bude docházet k odchytu vloček před sněhoměrnými tyčemi. Algoritmus je může detekovat jako ležící sníh poměrně vysoko nad reálnou „hladinou“. Pomocí nastavení prahu se tak dá problém jednoduše vyřešit. Stačí nastavit správnou hodnotu a vločky výše již nebudou započítávány.

6. problém: validace

Protože se může stát, že před jednu z tyčí spadne list, který v infračerveném světle bude bílý, bude se měřit na 4 tyčích nezávisle. S pomocí normalizace lze podobné výkyvy oproti normálu ignorovat.

Prahování váhy hodnot na tyči a normalizace s validací hodnot vizualizovaná v excelovské tabulce

Praxe

Pro správné měření bylo nutné zafixovat spodní desku dostatečně tak, aby nedocházelo k pohybu dolních pozic tyčí. Bylo nutné zarazit do země 4 kolíky, na které je našroubována měřící deska. Dále je nutné zajistit, aby nedocházelo k nechtěnému pohybu kamery. Je nyní lépe krytá také proti sněhu, který kdyby se pod kamerou nahromadil, může nepříjemně odrážet IR světlo a tvořit tak světelný smog před optikou kamery.

Původní myšlenku, mít jeden aruco kód pro celé měření, jsem rychle opustil kvůli různým deformacím obrazu a možnému pohybu tyčí. Z každé dolní hrany kódu se spočítá délka tyče v pixelech na centimetr (cca 11-12 px/cm), z něhož se pak tvoří … žebřík 50 políček podél tyče samotné.

Nojo, ale aby to nebylo tak jednoduché, musel jsem na 1. levou a úplně pravou tyč aplikovat kvadratickou funkci v ose x (tedy doprava-doleva) kvůli částečnému zkreslení širokoúhlým objektivem (rybí oko).  Vizualizovaná detekce proto nyní vypadá přibližně takhle:

Denní detekce

 

Noční detekce

Na obrázcích jsou dole vidět 3 tyrkysové čtverečky reagující na vrstvu sněhu. Dále růžové tečky na dolních a spodních hranách tyčí, samozřejmě aruco značky rámované zeleně a žluté obdélníky 5×4 px odpovídající horní polovině každého centimetru na tyči. Zaokrouhluje se tedy každý centimetr cca od 0,6 mm teprve nahoru. Vzhledem k možnému přeexponování hrany sněhu nebo možnému nafoukávání na tyč je to spíše žádoucí.

Zpětná validace měření bude pochopitelně časem důležitá pro přesnost výsledků.

A k čemu to všechno?

Projekt je součástí časosběrné kamery a meteostanice na mém webu. V současné chvíli měření již probíhá v experimentálním režimu. Protože bohužel nejvíce za listopad sněžilo asi v pátek a sobotu 22. a 23., opravdových výsledků se dočkám až později. Softwarově ale vše funguje jak má.

Jak jsem si postavil ženu na zalévání květin

Znáte to, sem tam zapomenete zalít kytky a ony povadnou. Buď to stihnete ještě přelít, nebo vám odejdou do zeleného nebe (rozuměj do kompostu). Já mám balkon, respektive lodžii orientovanou na jihozápad. Je to sice skvělé na jaře a na podzim, kdy vyvalení s kávičkou koukáte na krásný západ slunce, v letních měsících je to ale problém. Teplota během dne i s úplně otevřenými skly lodžie dosahuje mnohdy až 38 °C. V těchto podmínkách je potřeba řešit téměř trvalou závlahu, což pro nás, tvrdě pracující lid, bývá dost problematické.

Kapková závlaha (2021)

Vloni jsem zkusil kapkovou závlahu. Konceptuálně parádní low-cost řešení skončilo fiaskem. Kapková závlaha se hodí pro rostliny zvyklé na trvalou závlahu a hlavně pro kontejnery s otvory nebo úplně bez dna, což v mém případě uzavřené lodžie s koberečkem není možné. Celý pokus skončil několika potopami nebo vyprahnutím květináčů. :)

Kanystr s řešením samospádem

Výhody kapkové závlahy

  • do jisté míry regulovatelná rovnoměrná závlaha
  • neřeším tolik změny tlaku v hadici způsobené převýšením (police v regálu)

Nevýhody kapkové závlahy

  • nezohledňuje změny počasí, zejména teploty a umístění jednotlivých květináčů (slunce/stín),
  • zanáší se řasami kvůli mírnému proudění vody a působení slunce,
  • hadice jsou trvale pod tlakem a při vyšších teplotách povolily některé spoje měkkých hadic,
  • ve vysokých teplotách se voda ani nestihla vsakovat a odpařovala se na povrchu hlíny

V případě jahod je kapková závlaha velmi nevhodná. Při trvalé vláze vám zahnijí kořínky.

Všechny výhody a nevýhody řešení jsem zohlednil a přišel na možné řešení. Co bylo největší nevýhodou kapkové závlahy? Nádrž s vodou, v mém případě kanystr, působil vlivem převýšení neustálým tlakem a v hadicích v podstatě stála voda. Při zanesení zalévacích otvorů nebo povolení spoje vždycky vznikl velký problém vyžadující opravu (nebo sušení koberce).
Změna vyžaduje umístění nádrže s vodou pod úroveň květináčů tak, aby v hadicích nezůstávala zbytečně voda. A tak vznikl nápad, že si koupím další Raspberry Pi.

Raspberry Pi Pico – zdroj farnell.com

Automatický zavlažovací systém alias žena, která se postará, i když nejste doma

Když jsem si dohledal, že teď frčí mikropočítač RP2040 od Raspberry Pi, začal jsem si zjišťovat, jestli je možné ho využít pro tuto aplikaci. Ideální v jeho případě je to, že má minimální spotřebu, má GPIO a je možné ho velice jednoduše programovat přes MicroPython. Bylo rozhodnuto, teď jen vymyslet funkční řešení.

Každé video nebo návod na automatické zavlažování buď pomocí Arduina, klasického Raspberry Pi, Zero nebo už Pica obsahovalo velmi strohý koncept, většinou dost nedotažený do konce. Já jsem si řekl, že musí být systém naprosto bezúdržbový. Něco jako instantní polévka ze sáčku. Stačí přidat vodu. Rázem jsem narazil na několik komplikací.

Energie

První, co mě napadlo, byly baterie 18650, které jsem již dlouhou dobu měl. Mám ty s čudlíkem. Mají ochranu proti přebití, podbití i zkratu. To je pro mě ideální, protože je budu mít paralelně a baterie si tak budou napětí mezi sebou vyrovnávat nezávisle. Sehnal jsem k nim bez rozmyslu dva malé 200 mA solární panely. Při zapojení mi došlo, že napájecí napětí Pico se pohybuje mezi 3,3 V – 5,5 V. Solární panel dosahuje ve špičce bez zátěže klidně 6,5 V. Navíc jsou dva paralelně zapojené, a tak se jim zátěž dělí na polovic. Jakmile dojde při slunném dni k nabití obou článků, odpojí je jejich vlastní elektronika. Zátěží tak bude jen Pico a trocha elektroniky kolem. To může být problém. Kdyby solární panely dokázaly dodat dostatek, napětí by se mohlo nekontrolovatelně dostat nad 5,5 V a Pico se začne pomaličku smažit. Pořídil jsem mezi solární panely a baterie malý MPPT regulátor. Ten si vše hlídá sám a nepustí nic nad 4,2 V. Po nabití spadne na 3,7 V. Pico se proto celou dobu bude pohybovat v přijatelných hodnotách.

MPPT regulátor na 6V do 2A – zdroj hadex.cz

Baterie 18650 – zdroj mall.cz

6V – max 200 mA – zdroj dratek.cz

Skoro to vypadalo, že mám vše vyřešené a pak jsem začal počítat. Spotřeba se teoreticky pohybuje kolem 23 mA plus opakované proudové špičky od čerpadel v řádu několika stovek mA. Solární panely ale reálně při zataženém dni nedodají víc než 10% výkonu. Je tedy jasné, že většinu dne pojede počítač na baterie a panely nebudou skoro stíhat ani dobíjet. Proto jsem zvolil vyšší kalibr a současným dvěma malým panelům dokoupím ještě dva 4,5W.

520 mA, 4,5W, 6V – zdroj hadex.cz

Panely tak při správném umístění dodají vždy o něco navíc a baterie jsou proto reálně během dne jen jako vyrovnávací záloha při vyšším odběru a během noci udržují systém v chodu.

Květináč na slunci a ve stínu

U kapkové závlahy byl další problém nevyváženost zastíněných květináčů a těch vystavených slunci. Na balkoně mám po stranách 2 regály, zastíněnou poličku na vnější stěně, parapet u okna na přímém slunci a truhlíky u země pod oknem. S ohledem na převýšení a působení slunce jsem vše rozdělil na 4 zóny. Každá zóna bude mít jedno čerpadlo s vlastním zásobníkem vody a jedno čidlo půdní vlhkosti v referenčním květináči. Tedy ani ne moc velkém nebo malém. Na základě jeho dat bude systém nezávisle vyhodnocovat, kdy kterou zónu zalít. Nebude proto docházet k přemokření květináčů ve stínu ani k vysoušení těch na slunci.

Zásobník o objemu 10 litrů s čerpadlem pod hladinou

Koroze čidla

Čidlo vlhkosti půdy je za pár korun. Jeho špatná pověst jej předchází. Rádo totiž koroduje, protože je vystaveno neustále vlhké půdě a elektrickému proudu. Anoda logicky degraduje. Předběžně se problému snažím čelit přímým ovládáním napájení každého čidla. GPIO oficiálně pustí na pinu maximálně 19 mA. Čidlo má spotřebu do 10 mA a spouští se na střídačku. Proto můžu čidla napájet přímým výstupem signálu z GPIO. Je pod napětím na krátkou chvilku v době měření (cca 500 ms), zbytek času je jen uzemněné, tedy žádný proud jím neprochází.

Půdní vlhkoměr za pár korun – zdroj dratek.cz

Na trhu je pro podobné aplikace oblíbené kapacitní čidlo, které mi ale zatím přijde zbytečné. Systém již měsíc běží a viditelná koroze na čidlech zatím stále není.

Nedostatečné napětí pro čerpadla

Protože vše běží v rozmezí 3,7 – 4,2 V, na nějaké silnější motory můžeme předem zapomenout. Pokud tedy nepoužijeme step-up měnič a ovládání relé deskou s optočleny na vstupu. Já si zvolil čerpadla na 6 V. Vytlačí cca 100 cm, krátkodobě zvládnou i 6,5 V. Při 6,35 V, které mám momentálně nastavené, vytlačí vodu do 130 cm, což je dostačující. Protože budu zalévat v kratších intervalech (cca 9 – 17 sekund) s přestávkami, nemělo by vyšší napětí na čerpadlech dělat neplechu. Díky optočlenům se nemusím bát připojit relé desku přímo na GPIO. Li-Ion baterky mírnou proudovou zátěž zvládnou hravě.

Čerpadlo na 6V, výtlak cca 100 cm – zdroj dratek.cz

Relé deska se 4 výstupy 1NO/1NC – zdroj dratek.cz

Step-up měnič SX1308 – zdroj laskakit.cz

Step-up měnič je v tomto případě skvělý v tom, že díky němu dokážu kompenzovat výchylky napětí na bateriích a útlum způsobený ztrátami na dlouhých vodičích. Pico bude na jedné straně balkonu, čerpadlo bude na téměř 8m vodiči. V zalévání se budou střídat, proto nebude docházet k příliš velkému zatížení. Při častějším zalévání kvůli teplejšímu počasí lze logicky předpokládat vyšší sluneční aktivitu. Panely jsou schopny odběr čerpadel s klidem kompenzovat.

Řasy v hadicích

Protože u kapkové závlahy vlivem tepla, slunce a pomalu proudící vody zarostly hadice a otvory se značně ucpávaly, musel jsem přehodnotit původní plán. Zpočátku jsem počítal s tím, že za každé čerpadlo umístím zpětný ventil tak, abych šetřil energii a pro zalévání stačil jen krátký impulz. K ničemu by to ale nebylo. Voda v prázdné hadici proudí vysokou rychlostí a hadice by s ventilem plné vody zase zarůstaly dál. Po vypnutí čerpadla dojde vlivem přitažlivosti zemské k okamžitému stažení vody zpět. Možné nečistoty se proudem spláchnou přes čerpadlo do nádrže a mezi cykly budou mít čas pomalu klesnout zpět ke dnu. Nemělo by proto docházet k zanášení a ucpávání.

Zarostlá hadice vlivem působení slunce na zbytkovou vodu uvnitř

Zavzdušňování čerpadel

Během ostrého provozu se rychle projevil problém se zavzdušňováním. Vzhledem k množství otvorů v celé délce hadice vznikla ve vracející se vodě spousta bublin, které zůstávaly v prostoru čerpadla klidně celé hodiny. Řešení je jednodušší, než si myslíte. Jednoduše čerpadlo před samotným zaléváním dvakrát na pár set milisekund spustím, vyženou se bubliny a hned poté se začne zalévat. Funguje to dokonale.

Výrazná změna tlaku v hadicích

Protože jedním čerpadlem zalévám ve 3 ze 4 případů více pater regálů, případně poličku nebo parapet, musím počítat s téměř exponenciálním úbytkem tlaku. V nižších patrech kompenzuji tlak kompenzátorem – proloženým kouskem kuchyňské houby uvnitř hadice, která je propustná, ale zároveň výrazně omezuje tlak. Zalévací otvory jsou co nejmenší tak, aby byly rozdíly pokud možno co nejmenší. Pro větší květináč, vzhledem k jeho objemu, musí být otvorů více. Na konci hadice jsou otvory větší nebo jich je víc. Voda tam sice už jen kape, ale poměrně rychle a z více dírek. Tato optimalizace je nejsložitější, proto vyžaduje ještě nějaký čas. Důležité zjištění již u kapkové závlahy bylo to, že otvory v hadicích musí být protaveny tenkým horkým drátkem. V případě provrtání se u takto měkkých hadic otvor smršťuje a roztahuje v závislosti na teplotě hadice a času, což je nežádoucí.

Protavená dírka velmi malého průměru

 

Kompenzátor tlaku pro květináče v nižší výšce

Rychlost vysoušení půdy náhlou změnou tepla

Teplo a slunce, to je jednoduše měřitelná veličina. Jak ji ale zohledňovat, to je věc druhá. Pico má v hlavním čipu integrované teplotní čidlo, k jehož datům se dá v pythonu jednoduše dostat. Já jsem prvně určil rozsah dosažitelných teplot, což je při provozu od 10 do 40 °C (zejména kvůli bateriím). Nejprve jsem v excelu vytvořil lineární oranžovou funkci, která měla při 10 °C výstup přibližně 24 hodin. Při 40 °C byl výstup 0 sekund. To znamená, že se po zalití spočítá z teploty doba spánku mezi cykly. Při bližším zamyšlení bylo jasné, že tato funkce je příliš „na pohodu“. V běžných teplotách kolem 20 ˚C by byla délka spánku třeba 10 hodin. Při náhlém prudkém slunci by doslova Pico vše zaspalo v řádu hodin.

Bylo nezbytné vytvořit funkci novou. Blížila se logaritmické funkci. Při nízké teplotě má výstup poměrně dlouhý, rychle ale klesá, zpomaluje a blíží se nule. Vždy bude jeho výstupem ale minimálně 10 sekund, aby při teplotách kolem 40 ˚C nešla výstupní hodnota do mínusu. Díky tomu reaguje v kratších intervalech během teplot nad 20 ˚C. Na obrázku se jedná o tmavě červenohnědou křivku. Pokud je však v zóně závlaha dostatečná, kontrola vlhkosti zónu přeskočí.

Během spánku ještě počítač průběžně vyhodnocuje aktuální teplotu a porovnává ji s počáteční teplotou, kdy se uchyloval ke spánku. Pokud je aktuální teplota o 7 °C vyšší, spánek se přeruší a provede se standatní kontrola vlhkosti. Jedná se zejména o prudké oteplování po ránu s přímým sluníčkem, které je stále nízko a stihne vysušit většinu květináčů během krátké doby.

Kontrola stavu

Protože Pico je mikropočítač, nemá žádné periferie jako je bluetooth nebo wifi. Jak tedy při dlouhodobém provozu kontrolovat jeho stav? V jednoduchosti je síla a nejjednodušší výstupní perieferie je led dioda. Protože má Pico nespočet pinů, použil jsem RGB diodu z ledpásku. Díky PWM, které je, jako všechno, v MicroPythonu velice jednoduché můžu jednotlivé akce oznamovat probliknutím specifické barvy. Každý důležitý krok má proto svou stavovou barvu. Barvičky má každý rád!

V současnosti program eviduje start globálního cyklu (jak je zalito, jde se spát dle teploty okolí a poté začínáme znovu), lokálního cyklu (zalévá se, dokud není zalito), zaléváme, nezaléváme, vyčkáváme po zalití 5 s na další kontrolu vlhkosti (PWM RGB animace), hotovo, chyba a časová indikace spánku podobně jako bijící zvon u kostela.

Chybový status zabliká po dokončení zalévacího cyklu, pokud dlouhodobě není dosaženo skutečně zalitého stavu v jedné nebo více zón. Znamená to, že někde došla voda, nebo v horším případě upadla hadice a voda může odtékat někam na zem. Počítač proto po zahlášení chyby vyčkává tolik minut, kolikrát se pokusil zónu neúspěšně zalít, aby nedošlo k nějakému většímu problému. Upozorní výrazným rudým zablikáním po 12 a více nezdařených pokusech.

Během spánku jednou za minutu počítač po kontrole teploty oznámí zbývající čas krátkým probliknutím. Delší žlutá barva znamená 10 hodin a krátká oranžová jsou hodiny navíc. 1 žlutá a 2 oranžové tak znamená zbývajících 12 hodin spánku.

Manuální ovládání

K Picu mám přímo připojené tlačítko, kterým v přerušení můžu úplně přetnout spánkový cyklus. Přerušení změní pouze globální proměnnou, které se však periodicky testuje hodnota právě při spánkových cyklech. Konkrétně tedy v hlavním spánkovém cyklu a v cyklu odkladu po zahlášení chyby při nedostatku vody. Toto řešení přerušení se mi nakonec projevilo jako problematické, protože vlivem nějakého rušení se funkce spouštěla spontánně. Přerušení řeším proto běžným přepínačem, kterým přímo odpojuji napájení počítače. Reset však při současném fungování téměř není nutný.

Konečné řešení

Jak si tedy celý systém představit? Je to změť drátů a DPS na kousku dřevěné desky, ze které ústí několik svorkovnic. Z nich pokračuje pavouk vodičů sdružených i nesdružených nenápadně do všech koutů lodžie. Uzemňovací vodič je veden zvlášť pro čerpadla a čidla. V květináčích jsou vnořená čidla do hlíny a nad úplně všemi květináči vedou hadičky se zalévacími otvory.

Nezbývá, než doufat, že bude toto řešení dostatečné. Je však navržené adaptovatelně a já jsem otevřen dalším vylepšením. Těším se na léto, kdy budou teploty ještě vyšší a skutečně se funčknost projeví v extrémních podmínkách.

Tentokrát si kávu vychutnám při střídavém cvrkotu vody a blikajících čidlech v okolních květináčích. :)

Samotný kód

Pro zajímavost přikládám rozpracovaný a aktuálně běžící zdroják.

from machine import Pin, PWM, ADC
import utime
zelena = Pin(25,Pin.OUT)
zelena.high()
utime.sleep(0.5)
zelena.low()

cykly = 0
cyklylok = 0
chybazona1 = 0
chybazona2 = 0
chybazona3 = 0
chybazona4 = 0
chybaprum = 0
posledniteplota = 0

superpreruseni = False

sensor_temp = ADC(4)
conversion_factor = 3.3 / (65535)
#GPIO SENZORY
senz1 = Pin(21,Pin.IN)
senz2 = Pin(20,Pin.IN)
senz3 = Pin(19,Pin.IN)
senz4 = Pin(18,Pin.IN)
#NAPÁJENÍ SENZORŮ
senznap1 = Pin(2,Pin.OUT)
senznap2 = Pin(3,Pin.OUT)
senznap3 = Pin(4,Pin.OUT)
senznap4 = Pin(5,Pin.OUT)
#SIGNÁL PRO ČERPADLA
cerpsig1 = Pin(9,Pin.OUT)
cerpsig2 = Pin(6,Pin.OUT)
cerpsig3 = Pin(7,Pin.OUT)
cerpsig4 = Pin(8,Pin.OUT)
#VYPNOUT VŠECHNY RELÉ
cerpsig1.high()
cerpsig2.high()
cerpsig3.high()
cerpsig4.high()

#odpojenetl = Pin(0, Pin.IN, Pin.PULL_UP)
resettl = Pin(1, Pin.IN, Pin.PULL_DOWN)

pocetblik = 0
reverz = 1 # obrácení naměřené hodnoty násobkem nuly (neponořená čidla)

def status(id):
    global chybaprum
    global superpreruseni
    jas = 10
    delka = 0.5
    blue = PWM(Pin(15))
    red = PWM(Pin(14))
    green = PWM(Pin(13))
    blue.freq(1000)
    green.freq(1000)
    red.freq(1000)
    
    if id == "hotovo":
        blue.duty_u16(0)
        green.duty_u16(int(30000/jas))
        red.duty_u16(0)
        utime.sleep(delka)
        blue.duty_u16(0)
        green.duty_u16(0)
        red.duty_u16(0)
        print(id)
        
    if id == "otepleni":
        blue.duty_u16(int(10000/jas))
        green.duty_u16(int(65000/jas))
        red.duty_u16(int(10000/jas))
        utime.sleep(0.5)
        blue.duty_u16(0)
        green.duty_u16(int(50000/jas))
        red.duty_u16(int(50000/jas))
        utime.sleep(0.5)
        blue.duty_u16(int(10000/jas))
        green.duty_u16(int(10000/jas))
        red.duty_u16(int(65000/jas))
        utime.sleep(0.5)
        blue.duty_u16(0)
        green.duty_u16(0)
        red.duty_u16(0)
        print(id) 

    if id == "superpr":
        sp = 0
        while sp < 4:
            zelena.high()
            utime.sleep(0.05)
            zelena.low()
            utime.sleep(0.05)
            sp += 1
        print(id) 

    if id == "chyba":
        if chybaprum != 0:
            chp = chybaprum*2
        else:
            chp = 4
        ch = 0
        while ch<chp:
            blue.duty_u16(0)
            green.duty_u16(0)
            red.duty_u16(int(65536))
            utime.sleep(delka/5)
            blue.duty_u16(0)
            green.duty_u16(0)
            red.duty_u16(0)
            ch = ch+1
            utime.sleep(delka/chp)
        print(id)
        print("čekám",chp,"minut, aby se zamezilo problémům")
        davky = 30 #sekundové
        index = 0
        while index < chp*60 and superpreruseni != True:
            index += davky
            utime.sleep(davky)
        if superpreruseni == True:
            superpreruseni = False

    if id == "alive":
        global indexcekani
        global cekam
        zbyva = (cekam-indexcekani)/3600
        zbv = 0        
        zbyvaplus = 0
        if zbyva >= 10:
            blue.duty_u16(0)
            green.duty_u16(int(30000/jas))
            red.duty_u16(int(35535/jas))
            utime.sleep(0.2)
            blue.duty_u16(0)
            green.duty_u16(0)
            red.duty_u16(0)
            utime.sleep(0.2)
            zbyva-=10
            zbyvaplus = 10
        while zbv<zbyva:            
            blue.duty_u16(0)
            green.duty_u16(int(10000/jas))
            red.duty_u16(int(25535/jas))
            utime.sleep(0.1)
            blue.duty_u16(0)
            green.duty_u16(0)
            red.duty_u16(0)
            utime.sleep(0.1)
            zbv = zbv+1
        print(id) 

    if id == "zalevani":
        blue.duty_u16(int(65535/jas))
        green.duty_u16(int(10000/jas))
        red.duty_u16(0)
        utime.sleep(delka)
        blue.duty_u16(0)
        green.duty_u16(0)
        red.duty_u16(0)
        print(id) 
        
    if id == "nezalevani":
        blue.duty_u16(int(20000/jas))
        green.duty_u16(int(50000/jas))
        red.duty_u16(int(20000/jas))
        utime.sleep(delka)
        blue.duty_u16(0)
        green.duty_u16(0)
        red.duty_u16(0)
        print(id)
        
    if id == "ztlumit":
        blue.duty_u16(0)
        green.duty_u16(0)
        red.duty_u16(0)
        print(id)

    if id == "cyklus_start":
        cs = 0
        while cs<2:            
            blue.duty_u16(int(30000/jas))
            green.duty_u16(0)
            red.duty_u16(int(65535/jas))
            utime.sleep(delka/2)
            blue.duty_u16(0)
            green.duty_u16(0)
            red.duty_u16(0)
            cs = cs+1
            utime.sleep(delka/2)
        print(id)

    if id == "cyklus_loop":
        css = 0
        while css<3:            
            blue.duty_u16(int(65535/jas))
            green.duty_u16(0)
            red.duty_u16(int(25000/jas))
            utime.sleep(delka/3)
            blue.duty_u16(0)
            green.duty_u16(0)
            red.duty_u16(0)
            utime.sleep(delka/2)
            css = css+1
        print(id)

    if id == "spanek":
        global pocetblik
        pcb = 0
        pocetplus = 0
        if pocetblik >= 10:
            blue.duty_u16(0)
            green.duty_u16(int(60000/jas))
            red.duty_u16(int(65535/jas))
            utime.sleep(2)
            blue.duty_u16(0)
            green.duty_u16(0)
            red.duty_u16(0)
            utime.sleep(delka)
            pocetblik-=10
            pocetplus = 10
        while pcb<pocetblik:            
            blue.duty_u16(0)
            green.duty_u16(int(30000/jas))
            red.duty_u16(int(65535/jas))
            utime.sleep(delka/(pocetblik*2))
            blue.duty_u16(0)
            green.duty_u16(0)
            red.duty_u16(0)
            utime.sleep(delka/(pocetblik/2))
            pcb = pcb+1
        print(id,pocetblik+pocetplus,"hodin")
        
    if id == "mokreni":
        mok = 0
        while mok<65535:            
            blue.duty_u16(int(mok/jas))
            green.duty_u16(int(25000/jas))
            red.duty_u16(int((40535-round(0.618524453*mok))/jas))
            mok = mok+2
            utime.sleep(0.00001)
        blue.duty_u16(0)
        green.duty_u16(0)
        red.duty_u16(0)
        print(id)
# Konec funkce status

def zalij(zona):
    global chybazona1
    global chybazona2
    global chybazona3
    global chybazona4
    global chybaprum
    status("zalevani")
    #INDIVIDUÁLNÍ DÉLKY ZALÉVÁNÍ
    cas1 = 13                              #meloun policka + regal nahore
    cas2 = 9                              # regal dole vlhke
    cas3 = 17                              # regal naproti nahore
    cas4 = 17                             # truhliky a okno
    if zona == 1:
        cerpsig1.low()
        utime.sleep(1)
        cerpsig1.high()
        utime.sleep(1)
        cerpsig1.low()
        utime.sleep(1)
        cerpsig1.high()
        utime.sleep(0.1)
        cerpsig1.low()
        utime.sleep(cas1)
        cerpsig1.high()
        chybazona1 += 1
        print("zóna", zona, "| opakováno", chybazona1,"x")

    if zona == 2:
        cerpsig2.low()
        utime.sleep(1)
        cerpsig2.high()
        utime.sleep(1)
        cerpsig2.low()
        utime.sleep(1)
        cerpsig2.high()
        utime.sleep(0.1)
        cerpsig2.low()
        utime.sleep(cas2)
        cerpsig2.high()
        chybazona2 += 1
        print("zóna", zona, "| opakováno", chybazona2,"x")

    if zona == 3:
        cerpsig3.low()
        utime.sleep(1)
        cerpsig3.high()
        utime.sleep(1)
        cerpsig3.low()
        utime.sleep(1)
        cerpsig3.high()
        utime.sleep(0.1)
        cerpsig3.low()
        utime.sleep(cas3)
        cerpsig3.high()
        chybazona3 += 1
        print("zóna", zona, "| opakováno", chybazona3,"x")

    if zona == 4:
        cerpsig4.low()
        utime.sleep(1)
        cerpsig4.high()
        utime.sleep(1)
        cerpsig4.low()
        utime.sleep(1)
        cerpsig4.high()
        utime.sleep(0.1)
        cerpsig4.low()
        utime.sleep(cas4)
        cerpsig4.high()
        chybazona4 += 1
        print("zóna", zona, "| opakováno", chybazona4,"x")
    
def restart(pin):
#    global superpreruseni
    resettl.irq(handler=None)
#    status("superpr")
#    superpreruseni = True
#    utime.sleep(0.5)
    resettl.irq(handler=restart)

def kontrola():
    global chybazona1
    global chybazona2
    global chybazona3
    global chybazona4
    global chybaprum
    global hodnota
    hodnota = 0
    
    status("kontrola")
    senznap1.high()
    utime.sleep(0.5)
    if senz1.value() == 1*reverz:
        senznap1.low()
        zalij(1)
    else:
        senznap1.low()
        status("nezalevani")
        hodnota += 1
        chybazona1 = 0
        utime.sleep(0.3)

    senznap2.high()
    utime.sleep(0.8)
    if senz2.value() == 1*reverz:
        senznap2.low()
        zalij(2)
    else:
        senznap2.low()
        status("nezalevani")
        hodnota += 1
        chybazona2 = 0
        utime.sleep(0.3)

    senznap3.high()
    utime.sleep(0.5)
    if senz3.value() == 1*reverz:
        senznap3.low()
        zalij(3)
    else:
        senznap3.low()
        status("nezalevani")
        hodnota += 1
        chybazona3 = 0
        utime.sleep(0.3)

    senznap4.high()
    utime.sleep(0.5)
    if senz4.value() == 1*reverz:
        senznap4.low()
        zalij(4)
    else:
        senznap4.low()
        status("nezalevani")
        hodnota += 1
        chybazona4 = 0
        utime.sleep(0.3)
    
    if chybazona1 > 12 or chybazona2 > 12 or chybazona3 > 12 or chybazona4 > 12:
        chybaprum = round((chybazona1+chybazona2+chybazona3+chybazona4)/4);
        status("chyba")
    else:
        status("hotovo")
#KONEC FUNKCE KONTROLA/ZALÉVÁNÍ

resettl.irq(trigger=Pin.IRQ_RISING, handler=restart) # při stisku tlačítka funkce restart

status("cyklus_start")
while True: # HLAVNÍ SMYČKA NEKONEČNÁ
    cykly += 1
    utime.sleep(0.5)
    hodnota = ""
    status("cyklus_start")
    print(cykly,"hlavní cyklus")
    while hodnota != 4: #LOKÁLNÍ SMYČKA BĚŽÍ DOKUD VŠECHNY ZÓNY NEHLÁSÍ ZALITO (1+1+1+1)
        cyklylok += 1
        print(cyklylok,"zalévací cyklus")
        kontrola()
        utime.sleep(0.5)
        if hodnota != 4:
            print("zkusíme to znovu")
            print("vyčkáme 5s na promokření hlíny")
            status("mokreni")
        if hodnota == 4:
            cyklylok = 0
        status("cyklus_loop")
    #KONEC LOKÁLNÍ SMYČKY
    print(hodnota)
    #VÝPOČET DLE TEPLOTY
    reading = sensor_temp.read_u16() * conversion_factor 
    temperature = 29 - (reading - 0.706)/0.001721
    posledniteplota = temperature
    #LINEÁRNÍ FUNKCE 24h-0
    #cekam = (115200-temperature*2880)/2
    #LOGARITMICKÁ FUNKCE 13h-0
    #č 1 cekam = 1000*5000*10/(temperature*temperature*temperature)-781
    #LOGARITMITCKÁ FUNKCE KLIDNĚJŠÍ 26h-250s
    cekam = 2000*5000/(temperature*temperature)-6000 # 10C=26H, 20C=5,3H, 30C=1,4H, 40C=4M
    if cekam < 10: #Minimálně 10s
        cekam = 10
    print("Teplota nyní: ",temperature,"°C a čekám tím pádem ",cekam,"s = ",cekam/60,"min = ",cekam/60/60," hodin")
    pocetblik = round(cekam/3600)
    status("spanek")
    #VYČKÁVACÍ CYKLUS MOŽNÝ PŘERUŠIT
    indexcekani = 0
    davkovani = 60 #sekundové
    while indexcekani < cekam and superpreruseni != True:
        indexcekani += davkovani
        utime.sleep(davkovani)
        status("alive")
        #HLÍDÁNÍ TEPLOTY
        reading = sensor_temp.read_u16() * conversion_factor 
        prubeznateplota = 29 - (reading - 0.706)/0.001721
        if prubeznateplota > (posledniteplota + 7): #je-li o 7°C vyšší teplota než minule
            status("otepleni")
            print("Aktuální teplota - ",prubeznateplota,"°C - je vyšší o 7°C! Teplota z minulého cyklu byla ",posledniteplota,"°C")
            superpreruseni = True
    if superpreruseni == True:
        superpreruseni = False
    status("cyklus_loop")