Čidlo MH-Z19B změří koncentraci CO2 v rozsahu 0 až 5 000 ppm

Čidlo MH-Z19B změří koncentraci CO2 v rozsahu 0 až 5 000 ppm

Jeho cena se na čínských e-shopech pohybuje okolo 600 korun

Jeho cena se na čínských e-shopech pohybuje okolo 600 korun

Uvnitř plastové schránky je komora detektoru NDIR

Uvnitř plastové schránky je komora detektoru NDIR

Čidlo komunikuje skrze sériovou linku, PWM a analogový výstup

Čidlo komunikuje skrze sériovou linku, PWM a analogový výstup

Na asijských e-shopech narazíte zejména na toto provedení, které má i konektor JST

Na asijských e-shopech narazíte zejména na toto provedení, které má i konektor JST

Na asijských e-shopech narazíte zejména na toto provedení, které má i konektor JST

Na asijských e-shopech narazíte zejména na toto provedení, které má i konektor JST

Čidlo k detekci CO2 používá technologii NDIR. Uvnitř komory je IR dioda, která prozáří plyn a detektor na základně proměny světla odhadne koncentraci.

Čidlo k detekci CO2 používá technologii NDIR. Uvnitř komory je IR dioda, která prozáří plyn a detektor na základně proměny světla odhadne koncentraci.

Průběh růstu koncentrace CO2 v ložnici bez/s větráním

Průběh růstu koncentrace CO2 v ložnici bez/s větráním

Stačí mít okno od jara stále pootevřené a koncentrace bude nízká. Když jej zavřete do rána všechen čerstvý vzduch spolehlivě vydýcháte.

Stačí mít okno od jara stále pootevřené a koncentrace bude nízká. Když jej zavřete do rána všechen čerstvý vzduch spolehlivě vydýcháte.

Čidlo MH-Z19B připojené skrze sériovou linku a knihovnu SoftwareSerial na desku NodeMCU v3 s Wi-Fi čipem ESP8266

Čidlo MH-Z19B připojené skrze sériovou linku a knihovnu SoftwareSerial na desku NodeMCU v3 s Wi-Fi čipem ESP8266

Schéma zapojení čidla k prototypovací desce

Schéma zapojení čidla k prototypovací desce

Propojení skrze nepájivé pole

Propojení skrze nepájivé pole

Propojení skrze nepájivé pole

Propojení skrze nepájivé pole

Obecná instrukce pro zaslání povelu do čidla skrze sériovou linku

Obecná instrukce pro zaslání povelu do čidla skrze sériovou linku

Instrukce k přečtení stavu CO2

Instrukce k přečtení stavu CO2

Odpověď se stavem CO2. Koncentrace je rozložená ve 2. a 3. bajtu.

Odpověď se stavem CO2. Koncentrace je rozložená ve 2. a 3. bajtu.

Instrukce pro aktivaci a deaktivaci automatické kalibrace čidla na 400 ppm

Instrukce pro aktivaci a deaktivaci automatické kalibrace čidla na 400 ppm

Instrukce k ruční kalibraci na 400 ppm

Instrukce k ruční kalibraci na 400 ppm

Instrukce ke změně rozsahu měření. Ve výchozím stavu je zpravidla 9-5 000 ppm

Instrukce ke změně rozsahu měření. Ve výchozím stavu je zpravidla 9-5 000 ppm

Automatické ukládání změřených hodnot do tabulkového dokumentu na Google Drive

Automatické ukládání změřených hodnot do tabulkového dokumentu na Google Drive

Zprovoznění Apps Scriptu jako webové aplikace s veřejnou URL

Zprovoznění Apps Scriptu jako webové aplikace s veřejnou URL

Zprovoznění Apps Scriptu jako webové aplikace s veřejnou URL

Zprovoznění Apps Scriptu jako webové aplikace s veřejnou URL

Zjištění fingerprintu certifikátu

Zjištění fingerprintu certifikátu

Zjištění fingerprintu certifikátu

Zjištění fingerprintu certifikátu

Výpis běhu programu do do sériového terminálu na PC. Po každém cyklu čip na pět minut usne. Poté se probudí a vše provede znovu.

Výpis běhu programu do do sériového terminálu na PC. Po každém cyklu čip na pět minut usne. Poté se probudí a vše provede znovu.

Jeho cena se na čínských e-shopech pohybuje okolo 600 korun
Uvnitř plastové schránky je komora detektoru NDIR
Čidlo komunikuje skrze sériovou linku, PWM a analogový výstup
Na asijských e-shopech narazíte zejména na toto provedení, které má i konektor JST
26
Fotogalerie

Pojďme programovat elektroniku: Víme, proč se vám ráno nechce z postele

  • Nechce se vám ráno vstávat?
  • Jste rozlámaní a unavení?
  • Možná za to může CO2. Dnes ho budeme měřit

Pokud se vám nechce ráno vylézat z postele, příčin by mohlo být hned několik. O jedné z nich se v širší veřejnosti začíná mluvit až v posledních letech, ačkoliv se nejedná o nic nového. Možná jste si prostě jen pořádně nevyvětrali.

Nebohá lidská schránka vypouští během noci do svého okolí všemožné chemické substance a jednou z nich je i oxid uhličitý – CO2. Zatímco v přírodě tvoří jeho koncentrace ve vzduchu asi 0,04 %, plyn, který vydechujete, je tímto oxidem nasycený až z několika procent!

Podívejte se na animaci přírodní koncentrace CO2:

Je to problém? Jak se to vezme. Stačí si vzpomenout na snímek Apollo 13 s Tomem Hanksem v hlavní roli a snahu posádky z všemožných cetek na palubě kosmické lodi opravit filtr CO2. Pokud by se jim to tehdy nepodařilo, NASA se reálně obávala, že při koncentraci nad 6 % by posádka mohla postupně začít blouznit a ztrácet vědomí. Ale nebojte, to se vám v ložnici nestane, i kdybyste vydechovali celou noc o sto šest.

Přesto se však doporučuje, aby koncentrace CO2 třeba v kancelářích, ve kterých sedíme celý den, nepřekračovala hladinu zhruba 0,1-0,2 % CO2. Ne že by vás snad vyšší podíl oxidu uhličitého přizabil, ale u citlivějších osob se postupně může objevovat pocit únavy, horší soustředění, nevolnost a tak dále.

Levný senzor oxidu uhličitého MH-Z19B

I proto se na některých pracovištích a v progresivních školách objevují stále častěji senzory CO2. Jeden takový si můžete za relativně malý peníz pořídit i do své ložnice. Jmenuje se MH-Z19B (PDF dokumentace), vyrábí jej čínská fabrika Zhengzhou Winsen Electronics Technology a jedná se do jisté míry o kopii švédských čidel SenseAir.

b5d0beab-74a3-461d-905c-2cfc50bd22d8c0b43206-cb80-4cb3-b57e-fc08774419124a7249dc-49e7-4a4b-82f3-9c8f632cbc72
3014ff56-1196-4e92-83b6-08d70da724e6b07b9250-6894-4a83-91da-25f85bdd12306d011fcd-712e-40f9-8e8d-b13d9d3949b5
Senzor CO2 Tondy Trčálka s otvory pro piny a zároveň jednotným konektorem JST. Bílými plochami filtrů na horní a boční straně proniká do měřící komory v nitru okolní vzduch.

MH-Z19B v kostce:

  • Princip měření: NDIR
  • Rozsah: 0-5 000 ppm (přesnost 50 ppm)
  • Napájení: 4,5-5,5 V
  • Odběr proudu: 60 mA (průměr), ve špičce až 100+ mA
  • I/O logika: 3,3 V (tolerantní s 5 V)
  • Komunikace: UART, PWM, analog (0-2V)
  • Cena: okolo 600 Kč (eBay aspol.)

Oproti mnoha ostatním čidlům – třeba teploměrům – začátečníka nejprve překvapí cena. Změřit koncentraci CO2 je totiž hotová věda a z mnoha důvodů jsou tato čidla poměrně drahá. Cena těch značkových se bez problému vyšplhá až na několik tisíc korun!

Technologie NDIR

MH-Z19B k měření úrovně CO2 používá technologii NDIR (Nondispersive infrared). Senzor se skládá z komory, do které neustále proudí okolní vzduch. Komoru poté každých několik sekund prozáří červená IR dioda a detektor na opačné straně pracuje jako jednoduchý spektrální analyzátor. Jednoduše řečeno, koncentrace CO2 v komoře pozmění vlastnosti infračerveného světla a detektor tuto změnu zaznamená.

a1992f08-931d-4722-b598-e2350160bac3
Základní schéma detektoru plynů, který používá technologii NDIR

Senzory NDIR jsou zpravidla energeticky mnohem úspornější než chemické detektory, které měří koncentraci chemickou cestou. Díky NDIR potřebuje MH-Z19 k chodu jen zhruba 4-5 V a v průměru spaluje desítky mA elektrického proudu.

MH-Z19B je v tomto směru opravdu superlevný model, na eBayi a mnoha čínských tržištích jej totiž najdete za částku zhruba okolo 600 korun. Při nákupu si ale dávejte dobrý pozor na to, jestli se nejedná o konstrukčně prakticky identický senzor MH-Z19 (bez písmene B na konci), který je o něco hloupější, chybí mu totiž vlastní automatická kalibrace nebo třeba analogový výstup.

MH-Z19B pod napětím a patrné blikání detektoru NDIR:

Co je to PPM

Senzory plynů a prachových částic zpravidla neměří koncentraci v okolním vzduchu v procentech, ale v jednotce ppm (parts per million – dílů/částic na jeden milion). Takže platí:

  • 100 % = 1 000 000 ppm
  • 1 % = 10 000 ppm
  • 0,04 % = 400 ppm

Přírodní koncentrace CO2 ve vzduchu je proto zhruba 360-400 ppm a neustále roste. Ještě na počátku 20. století se pohybovala pod 300 ppm.

Pro přehlednost si přepočtěme na ppm všechny limitní hodnoty, o kterých tu byla zatím řeč. V kanceláři nebo u vás v obývacím pokoji – prostě v uzavřených prostorách – by měla být v ideálním případě do 1 000 až 2 000 ppm, vydechovaný vzduch má koncentraci až několika desítek tisíc ppm a během havárie Apolla 13 se NASA obávala, aby úroveň v kosmické lodi nepřekročila okolo 60 000 ppm, která už je životu nebezpečná.

Senzor může spustit třeba ventilaci

Podobný senzor tedy může typicky v ložnici měřit periodicky hodnotu ppm, a pokud překročí kritickou mez, upozorní vás, že je vhodné vyvětrat. Ještě sofistikovanější a chytřejší systémy to pak učiní za vás, a když v ložnici v noci vzroste koncentrace třeba nad 2 000 ppm, spustí ventilaci, anebo pomocí nějakého aktuátoru pootevřou okno.

Nutno podotknout, že CO2 je těžší než vzduch jako takový, čili se bude během spánku (nikdo nevíří vzduch) držet spíše při zemi. Čidlo umístěné kdesi na skříni výše než je vaše postel proto může zaznamenávat o něco nižší hodnoty.

5 000+ ppm po bujaré noci? Žádný problém!

MH-Z19B umí měřit CO2 až do koncentrace 5 000 ppm (0,5 %) a mohu vám zaručit, že když se v zimě večer hermeticky uzavřete v ložnici a usnete, i v jedné osobě této hladiny nad ránem bez problému dosáhnete. Bohatě stačí, když si před tím otevřete láhev dobrého vína, anebo zajdete s přáteli na jedno. Odbourávání alkoholu z krve totiž nebude zadarmo, ale projeví se vyšší aktivitou metabolismu a tedy i zrychleným dechem. Za jednotku času tedy do okolí vypustíte mnohem více CO2, než kdybyste si namísto toho dali večer mátový čaj a přečetli sbírku poezie.

5977512e-29f9-4652-a98d-2152d1c3e578
Jarní noci okolo Velikonoc. Během večera jsem měl zavřené okno a postupně vydýchal čerstvý vzduch. V noci jsem však vždy pootevřel okno a hodnoty se srovnaly. Na velikonoční pondělí jsem to však pod tíhou předchozích oslav neudělal a i díky zrychlenému metabolismu nad ránem koncentrace dosáhla maximální změřitelné hladiny 5 000 ppm.

Ve dvou osobách pak ložnici obvyklých rozměrů (panelák, bytový dům) vydýcháte za polovinu času a právě v zimě, kdy asi nebudete mít pootevřené okno celou noc, bude nad ránem čidlo MH-Z19B zaznamenávat plných 5 000 ppm. Více už nezměří, čili skutečná koncentrace může být ještě mnohem vyšší.

No, a v těchto chvílích už skutečně může nastupovat zvýšená únava, nechuť radostně vyskočit z postele a utíkat v pondělí ráno nadšeně za dalšími pracovními povinnostmi. Nutno však podotknout, že reakce organizmu je při těchto koncentracích stále individuální, protože jsme dalece od skutečně nebezpečných hodnot. Například autor článku v pondělí ráno nerad vstává při jakékoliv koncentraci CO2.

ff27ced3-4bf2-4496-834d-c2870bd1b8ec
S příchodem teplých jarních dnů a okna otevřeného permanentně na ventilaci se vnitřní koncentrace srovnává s tou venkovní prakticky neustále. Jen jedna noc za poslední dny byla opravdu chladná a koncentrace CO2 kvůli zavřenému oknu v noční ložnici vystřelila opět vzhůru.

Senzor bude ukládat hodnoty do tabulky na Google Drive

Tak, po malém medicínském okénku se konečně pusťme do bastlení, teď si totiž naprogramujeme primitivní obvod s jedním čidlem MH-Z9B a Wi-Fi destičkou NodeMCU „v3“ s čipem ESP8266.

Řídící čip se každých pět minut probudí (po nahrání programu je třeba propojit vodičem jeho piny RST a D0/WAKE), vyžádá si od čidla aktuální koncentraci CO2, údaj skrze Google Apps Script uloží do tabulkového dokumentu na úložišti Google Drive a zase usne.

MH-Z19B má k dispozici několik komunikačních rozhraní. Analogovou hodnotu můžeme přečíst na pinu Vo jako napětí (0 až 2 V). Dále je tu pin PWM pro dekódování koncentrace pomocí pulzně-šířkové modulace. Čidlo tedy na tomto pinu neustále pulzuje, přičemž z délky pulzu lze spočítat koncentraci. A nakonec tu jsou piny RX a TX pro oboustrannou komunikaci skrze sériovou linku a přesně tu použijeme i my.

Sériová linka

MH-Z19B používá 3,3V komunikační logiku (tolerantní na 5V), nicméně jej musíte napájet o něco vyšším napětím – ideálně 5V. Modifikovaná deska NodeMCU „v3“ má na desce vyvedený pin označený jako VU (VUSB), který je připojený k USB konektoru desky, takže nabídne 5V. Případně bychom mohli použít třeba maličkou destičku Wemos D1 Mini, která tento pin má také a rovnou označený jako 5V. S oběma už jsme si v našem seriálu pohráli hned několikrát.

Připojíme čidlo pomocí SoftwareSerial

Piny na senzoru RX a TX nepřipojíme křížově na totožné piny na naší řídící desce, čip ESP8266 totiž při startu vypisuje do sériové linky provozní údaje a tato aktivita by mohla čidlo oxidu uhličitého zmást, co po něm vlastně chceme. Zároveň během vývoje nejspíše budeme chtít skrze základní sériovou linku napojenou na USB vypisovat data do počítače, takže ji potřebujeme ponechat volnou.

V Arduinu můžeme u těchto desek využít ještě přemapování sériové linky na piny D7 a D8 a mezi tímto nastavením pak přepínat příkazem Serial.swap(). Čídlo tedy připojíme na tyto alternativní piny a v úvodu programu zavoláme funkci swap. Díky tomu by čip při startu do senzoru neposílal žádná provozní data.

7057c4c0-04ea-4854-9497-8c29f04060d8c566bc6b-e5f4-48d9-bc02-847bd877d7fc444d6a68-fa5e-427c-a95b-fd2067200e00075b10b3-baf3-46e8-942b-c570ea26315f
Připojení MH-Z19B k destičce NodeMCU na piny D5 a D6 pomocí knihovny SoftwareSerial

My ale uděláme ještě něco jiného. Použijeme oblíbenou knihovnu SoftwareSerial, která vytvoří virtuální RX a TX na libovolných dvou pinech GPIO, čili základní sériovou linku můžeme použít k vypisování dat do počítače. Ale pozor, pro čip ESP8266 nemůžete použít vestavěnou verzi knihovny v Arduinu, ale musíte si stáhnout a nainstalovat tuto knihovnu z GitHubu.

Kód pro inicializaci čidla připojeného na piny D5 a D6 pomocí knihovny SoftwareSerial může vypadat třeba takto:

// https://github.com/plerup/espsoftwareserial
#include <SoftwareSerial.h>

// D5 -> TX na MH-Z19B
// D6 -> RX na MH-Z19B
SoftwareSerial co2_senzor(D5, D6);

void setup() {
  co2_senzor.begin(9600);
}

Komunikujeme s čidlem MH-Z19B

Doposud jsme pro čtení dat ze senzorů používali nejrůznější knihovny třetích stran. Aplikační rozhraní měřáku CO2 MH-Z19B je ale docela jednoduché, a tak si vše na té nejnižší úrovni pošéfujeme sami.

Komunikace s čidlem je opravdu primitivní – skládá se vždy z devět bajtů dlouhého příkazu a případně stejně dlouhé odpovědi. Příkaz je tedy pole devíti bajtů – osmibitových čísel 0-255. Každý bajt má nějakou funkci, takže podle našich potřeb změníme jeho hodnotu, no a nakonec celé pole pošleme jedním jediným příkazem do čidla, které poté provede, co jsme mu přikázali.

Podívejte se, jak vypadá obecný blok devíti bajtů pro příkaz:

c9521c16-9400-4a78-893a-b0f9b0c329be

První dva bajty jsou vždy stejné (červeně), ve třetím je pak číslo povelu, který má čidlo vykonat (zeleně), následuje pět bajtů dat, ve kterých mohou být pomocné parametry (modře), a vše uzavírá bajt s kontrolním součtem (žlutě).

Díky kontrolnímu součtu máme jistotu, že celá řada bajtů došla v pořádku a žádný není poškozený, protože by jinak jeho výpočet nedopovídal.

Tak a teď prakticky. Takhle vypadá instrukce, která dává čidlu povel, aby nám odpovědělo, jaká je koncentrace CO2 v okolním vzduchu:

a22f6285-d176-4451-bbf0-201174c118fe

Všimněte si, že povel k přečtení koncentrace má v šestnáctkové soustavě číslo 0x86 (v desítkové 134). V datové části žádné další parametry nejsou a poslední bajt s kontrolním součtem má šestnáctkovou hodnotu 0x79, protože platí:

(0xFF – (0x01 + 0x86)) + 0x01 = 0x79

V Arduinu by kód k odeslání této instrukce vypadal následovně:

// Odesli 9B instrukci s zadosti o koncentraci CO2 
uint8_t prikaz[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
co2_senzor.write(prikaz, sizeof(prikaz));

Krátce na to čidlo odešle devět bajtů dlouhou odpověď, která by mohla vypadat v noční ložnici třeba takto:

4c5580cf-0053-495a-b5d4-03b51c6abbc8

První bajt 0xFF je opět totožný, na druhé pozici je ale potvrzení povelu 0x86 (přečtení koncentrace). Když tedy bude mít odpověď v úvodu právě tyto dva bajty, víme, že se jedná o zprávu s koncentrací. Ta je rozložená do dalších dvou bajtů a za nimi jsou opět samé nulové bajty a jako poslední bajt kontrolní součet, jehož opětovným výpočtem si můžeme (ale nemusíme) ověřit, jestli se data po cestě třeba nepoškodila.

Koncentrace je v tomto případě rozložená do dvou bajtů, tedy samostatných osmibitových čísel, s hodnotami 0x05 a 0x1A, které potřebujeme složit do jednoho 16bitového čísla 0x051A (1 306 ppm). Můžeme k tomu použít operátory pro práci s jednotlivými bity, kterým jsme se věnovali už v jednom z předchozích dílů našeho seriálu o programování:

uint8_t bajt2 = 0x05;
uint8_t bajt3 = 0x1A;

// Promenna co2 bude mit po tomto vypoctu hodnotu 1306
uint16_t co2 = (bajt2 << 8) ^ bajt3;

Do šestnáctibitové proměnné co2 nejprve zkopírujeme první osmibitové číslo a operátorem << jej posuneme o osm pozic doleva. Na uvolněné místo pak operátorem ^ vložíme druhé osmibitové číslo.

Pokud jsou však pro vás tyto operátory španělskou vesnicí, můžete použít i normální matematiku a výpočet zmíněný i v oficiální dokumentaci:

uint16_t co2 = (bajt2 * 256) + bajt3;

Výsledek bude naprosto stejný, namísto pohybů jednotlivých bitů totiž s bajty pracujeme jako s čísly:

uint16_t co2 = (5 * 256) + 26;

Celý kód s příkazem k odeslání aktuální koncentrace a jejím přečtením bude vypadat následovně:

// Odesli 9B instrukci s zadosti o koncentraci CO2 
uint8_t prikaz[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
co2_senzor.write(prikaz, sizeof(prikaz));

// Precti 9B odpoved ze seriove linky
uint8_t odpoved[9];
co2_senzor.readBytes(odpoved, sizeof(odpoved));

// Pokud je 0. bajt roven 0xFF a 1. bajt roven 0x86, pokracuj
if ((odpoved[0] == 0xFF) && (odpoved[1] == 0x86)) {

  // Spocitej kontrolni soucet
  uint8_t ks = 0;
  for (uint8_t i = 1; i < 8; i++) {
    ks += odpoved[i];
  }
  ks = (255 - ks) + 1;

  // Pokud kontrolni soucet odpovida 8. bajtu,
  // zprava je v poradku
  if (odpoved[8] == ks) {
    // Spoj 2. a 3. bajt dohromady a ziskej koncentraci
    uint16_t co2 = (odpoved[2] << 8) ^ odpoved[3];
  }
}

Automatická kalibrace

Jak už jsem napsal výše, MH-Z19B je oproti svému předchůdci vybavený systémem automatické kalibrace ABC. Kalibrace? Ano, zatímco čidla teploty, vlhkosti nebo luxmetry, se kterými jsme si v našem seriálu doposud hráli, jsou kalibrované od výroby a tato kalibrace je v čase stálá, u měření CO2 pomocí technologie NDIR je to mnohem složitější.

Stručně řečeno, čidlo se čas od času potřebuje dozvědět, jak vypadá bazální hodnota – tedy přírodní koncentrace 400 ppm. U starého modelu MH-Z19 jste tedy museli občas (no vlastně poměrně často) otevřít okno, pořádně vyvětrat, nebo umístit čidlo na půl hodiny na balkon, a odeslat příkaz ke kalibraci. MH-Z19 si pak řekl: „Aha, to, co jsem právě změřil, je 400 ppm“ a od této hodnoty mohl odvíjet další výpočty.

Automatická kalibrace by měla být od výroby zapnutá. Ručně ji můžete aktivovat/deaktivovat těmito instrukcemi:

cda5b3e0-ebe4-4ea2-b95c-e0368cb6a8f6

Automatická kalibrace funguje tak, že si čidlo tuto bazální hladinu 400 ppm nastavuje samo z naměřených dat v posledních několika desítkách hodin.

Je to logické, výrobce totiž předpokládá, že si čas od času pořádně vyvětráte, čidlo tento pokles svým průběžným měřením zaznamená a nastaví jako bazální hladinu. Po prvním připojení čidla je tedy dobré vyvětrat, aby si mohlo vytvořit novou kalibraci. V opačném případě bude používat tu z výroby, která nemusí odpovídat místním podmínkám.

Přesnost čidla se zároveň díky této průběžné kalibraci může v čase měnit a zlepšovat, takže pokud se zpočátku leknete podivných hodnot, nemusí to být hned chyba. Jen dejte čidlu pár dnů čas.

Na stranu druhou, ze stejného důvodu je automatická kalibrace naprosto nevhodná pro použití v dlouhodobě nevětraných/nevětratelných prostorách. Jednoduše proto, že nejnižší změřená hodnota možná nikdy nedosáhne přírodních 400 ppm, a tak se na ni čip nedokáže ani zkalibrovat.

Ruční kalibrace

Pokud automatika selže, můžete čidlo ke kalibraci přinutit. Opět je tu primitivní cesta, kdy stačí na pinu HD nejméně na 7 sekund nastavit logickou nulu, ale zároveň ta chytřejší skrze instrukci odeslanou přes sériovou linku. Vypadá takto:

63bbbd85-bcd1-4f72-b76d-8aec32a72b65

Výrobce udává, že před ručně vyvolanou kalibrací by měl senzor pracovat alespoň dvacet minut v podmínkách blízkých 400 ppm, protože okolní vzduch musí proniknout skrze filtry do komory čidla, což může pár minut trvat.

Další praktické instrukce

Ačkoliv oficiální i neoficiální dokumentace skýtá hromadu dalších možných instrukcí, zmíním z těch (možná) praktických ještě změnu měřícího rozsahu. Čidlo totiž nemusí měřit jen v rozsahu 0-5000 ppm, ale třeba jen do 2 000 ppm.

2d893181-dc8d-49ea-833b-73a26ce89a58

Jak odeslat změřená data na Google Drive

Tak, čidlo už ovládáme, čili zbývá jediná věc. Změřenou hodnotu musíme odeslat na web, přičemž já si oblíbil Google Drive a jeho tabulkový dokument. Jednoduše řečeno, každých pět minut se v tabulkovém dokumentu automaticky přidá nový řádek s časem a hodnotou CO2 v ppm. Jelikož webový tabulkový procesor funguje podobně jako Excel, můžete si nad změřenými hodnotami kreslit grafy, zpracovávat statistiky atp.

9b66e03f-3092-445e-aa9c-7aab8f01a78f
Mějme jednoho Čížka, jednu redakci a jednu sobotu, během které se snaží vydýchat všechen čerstvý vzduch. Je to poznat, po několika hodinách bez větrání totiž koncentrace CO2 dosáhla bezmála 1 000 ppm

S tabulkovým dokumentem se spojíme skrze Google Apps Script dostupný ne veřejné adrese (kterou však budeme znát jen my). K této adrese přidáme běžný URL parametr, třeba ?co2=XXX, a když pak čip ESP8266 celou tuto adresu i s parametrem, do kterého vložíme aktuální hodnotu koncentrace, zavolá, skript kdesi v útrobách datového centra Googlu hodnotu konečně uloží do tabulkového dokumentu.

c6ff3b8a-7cef-4091-8960-cfde1d89a628 65a514b7-3eca-4f9d-b995-e1d4edd072e1100ac306-eba6-433a-a444-cc542d86161a
Příprava Apps Scriptu (vytvoříte jej ve svém Google Drive v nabídce Přidat) a jeho zprovoznění jako webové aplikace dostupné na veřejné WWW adrese, se kterou se spojí náš čip ESP8266. Apps Script bude ukládat hodnoty do tabulkového dokumentu, který určíme podle jeho identifikátoru (část WWW adresy). Skript musíme ještě autorizovat (třeba klepnutím na šipku Spustit a potvrdit jeho práva).

ESP8266 a HTTP klient

Jednoduché jako facka. Tedy až na jednu drobnost. Váš veřejný Apps Script je z bezpečnostních důvodů dostupný výhradně na šifrovaném protokolu HTTPS. To je dnes už pochopitelně standard na většině webových serverů, ale drobný čip ESP8266 s maličkou pamětí nemá prostředky k tomu, aby si držel jako velký operační systém hromadu kořenových šifrovacích certifikátů. Namísto toho se tedy používá tzv. fingerprint.

Ale pěkně popořadě. Když budete chtít zavolat nějakou adresu a získat její odpověď (HTML kód, JSON, cokoliv), stačí v případě čipu ESP8266 použít vestavěnou knihovnu ESP8266HttpClient (pod odkazem najdete GitHub s příklady).

Základní kód pro přečtení webové adresy a stažení obsahu do objektu Arduino String by mohl vypadat takto:

HTTPClient http;
http.begin("http://www.nejakyweb.cz");
int http_kod = http.GET();
if(http_kod == HTTP_CODE_OK){
  // V data je nyni obsah, ktery vraci adresa www.nejakyweb.cz
  // Treba HTML kod stranky.
  String data = http.getString();
}

Podstatné je nicméně to, že stahujeme (třeba) HTML kód stránky, která běží na protokolu HTTP a nikoliv šifrovaném HTTPS. S ním se takto jednoduše spojit nemůžeme.

Jak se spojit s HTTP serverem šifrovaně

Kdybychom komunikovali s webem na HTTPS, musíme použít na začátku příkaz:

http.begin("https://nejakyweb.cz", "7a9cf4db40d3625a6e21bc5ccc66c83ea1455938");

Druhý parametr je v tomto případě onen fingerprint – jedinečný kryptografický otisk – konkrétního bezpečnostního certifikátu, který zjistíte po jeho otevření na kartě Podrobnosti, anebo třeba pomocí webového vyhledávače na webu grc.com/fingerprints.htm (fingerprint můžete napsat i ve formátu XX XX, nebo XX:XX apod.). Zbytek kódu pak bude totožný.

obrázek 033.pngobrázek 034.png
Získání fingerprintu certifikátu z prohlížeče

Bohužel, ani toto není úplně trvalé řešení. Stačí, aby se na cílovém webovém serveru změnil certifikát a změní se i jeho fingerprint a váš kód přestane fungovat. Budete jej tedy muset průběžně měnit. Nicméně ani to nemusí stačit.

Jak se spojit s naším Apps Scriptem

Některé servery mohou používat více certifikátů, případně některé pokročilejší, které nemusí knihovny Arduina pro ESP8266 vůbec podporovat, takže i když fingerprint dohledáte, volání http.begin selže s tím, že se nepodařilo otevřít šifrované spojení. Stejně dopadnete i v případě pokusu komunikace se servery Googlu.

Namísto funkce Esp8266HttpClient si tedy napíšeme vlastní rutinu založenou na další vestavěné a nižší knihovně WiFiClientSecure. V podstatě uděláme to samé, co knihovna ESP8266HttpClient, ovšem v našem případě odstraníme kontrolu platnosti certifikátu pomocí fingerprintu. Náš kód vychází z příkladu pro knihovnu WiFiClientSecure, který najdete na oficiálním GitHubu.

bool odesliKoncentraci(uint16_t co2){

  const char* https_domena = "script.google.com";
  String https_url = "/macros/s/xxxxxxxxxxxxxxxx/exec?co2=" + String(co2);
  const int https_port = 443;
  WiFiClientSecure klient;

  if (!klient.connect(https_domena, https_port)) {
    Serial.println("Nelze se spojit");
    // Pokud nelze navazat spojeni, vrat false
    return false;
  }

  /* Ted by prisla rada na kontrolu duveryhodnosti certifikatu
     pomoci fingerprintu. Tuto cast komunikace ale ignorujeme.
     Snizime zabezpeceni, ale bude to fungovat.
  */
  
  // HTTP GET prikaz s adresou skriptu a hodnotou CO2
  klient.print(String("GET ") + https_url + " HTTP/1.1\r\n" +
               "Host: " + String(https_domena) + "\r\n" +
               "User-Agent: CO2SenzorZBrna\r\n" +
               "Connection: close\r\n\r\n");

  // Pokud server odpovi 200 OK, vse probehlo v poradku
  // Zahod zbytek a vrat true
  while (klient.connected()) {
    String line = klient.readStringUntil('\n');
    if (line.indexOf("200 OK")) {
      klient.flush();
      return true;
    }
  }
  // V opacnem pripade vrat false
  return false;
}

V praxi tedy dojde k tomu, že se naše destička skrze Wi-Fi spojí se servery Googlu, otevře se šifrované spojení, ale neprovede se kontrola důvěryhodnosti certifikátu. Tímto zákrokem tedy snížíme zabezpečení a principiálně se jedná o designovou chybu, ale je to hack, jak docílit toho, abychom se dokázali napřímo spojit se serverem Google Apps Script.

(Pokud zkušení čtenáři najdou lepší techniku, rád si ji přečtu v komentářích, anebo mně napište e-mail.)

A to je všechno! Teď už jen celý kód

A jsme u konce! Pokud vše proběhne, jak má, každých pět minut se v tabulce na Google Drive objeví nová hodnota koncentrace včetně času měření. Dalšími úpravami bychom mohli docílit třeba toho, že se budou v tabulce automaticky mazat údaje starší několik dnů, protože webový tabulkový procesor Googlu pochopitelně není ideální řešení pro ukládání většího množství dat. Není to klasická a k tomu určená databáze, která pojme desítky tisíc měření třeba po ročním provozu.

cf378684-bd6e-40d4-9318-56c04ea41ee0
Výpis informací do sériové linky PC během chodu programu

Na závěr nesmí chybět kompletní kód. Nejprve kód firmwaru pro desku NodeMCU s čipem ESp8266, poté kód pro Google Apps, který budete muset ještě autorizovat a nastavit, aby byl veřejně dostupný z internetu.

Kód hlavního programu:

// https://github.com/plerup/espsoftwareserial
#include <SoftwareSerial.h>
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>

const char* wifi_ap = "MalyKloboucek";
const char* wifi_heslo = "mameradikubucizka";

// Priprava softwarové seriove linky
// D5 -> TX na MH-Z19B
// D6 -> RX na MH-Z19B
SoftwareSerial co2_senzor(D5, D6);

// Jen ukazka, v praxi rucni kalibraci dnes volat nebudu
void kalibrace() {
  uint8_t prikaz[9] = {0xFF, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78};
  co2_senzor.write(prikaz, sizeof(prikaz));
}

// Funkce pro zmereni koncentrace CO2
int16_t zmeritKoncentraci() {
  // Odesli 9B instrukci s zadosti o koncentraci CO2
  uint8_t prikaz[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
  co2_senzor.write(prikaz, sizeof(prikaz));

  // Precti 9B odpoved ze seriove linky
  uint8_t odpoved[9];
  co2_senzor.readBytes(odpoved, sizeof(odpoved));

  // Pokud je 0. bajt roven 0xFF a 1. bajt roven 0x86, pokracuj
  if ((odpoved[0] == 0xFF) && (odpoved[1] == 0x86)) {

    // Spocitej kontrolni soucet
    uint8_t ks = 0;
    for (uint8_t i = 1; i < 8; i++) {
      ks += odpoved[i];
    }
    ks = (255 - ks) + 1;

    // Pokud kontrolni soucet odpovida 8. bajtu,
    // zprava je v poradku, takze spoj dva bajty
    // a konecne ziskej koncentraci
    if (odpoved[8] == ks) {
      uint16_t ppm = (odpoved[2] << 8) ^ odpoved[3];
      return ppm;
    }
    else {
      return -1;
    }
  }
  else {
    return -1;
  }
}

// Funkce pro odeslani koncentrace do tabulky na Google Drive
bool odeslatNaGoogleDrive(uint16_t co2) {
  const char* https_domena = "script.google.com";
  String https_url = "/macros/s/AKfycbxqdsdfdfsfgedgrgergerhehrhhp4xHZmnudtBzg/exec?co2=" + String(co2);
  const int https_port = 443;
  WiFiClientSecure klient;

  if (!klient.connect(https_domena, https_port)) {
    Serial.println("Nelze se spojit");
    // Pokud nelze navazat spojeni, vrat false
    return false;
  }

  /* Ted by prisla rada na kontrolu duveryhodnosti certifikatu
     pomoci fingerprintu. Tuto cast komunikace ale ignorujeme.
     Snizime zabezpeceni, ale bude to fungovat.
  */

  // HTTP GET prikaz s adresou skriptu a hodnotou CO2
  klient.print(String("GET ") + https_url + " HTTP/1.1\r\n" +
               "Host: " + String(https_domena) + "\r\n" +
               "User-Agent: CO2SenzorZBrna\r\n" +
               "Connection: close\r\n\r\n");

  // Pokud server odpovi 200 OK, vse probehlo v poradku
  // Zahod zbytek a vrat true
  while (klient.connected()) {
    String line = klient.readStringUntil('\n');
    if (line.indexOf("200 OK")) {
      klient.flush();
      return true;
    }
  }
  // V opacnem pripade vrat false
  return false;
}

// Hlavni funkce, ktera se spusti po pripojeni desky k napajeni
void setup() {
  // Nastartuj seriovou linku do PC
  Serial.begin(115200);
  // Nastartuj seriovou linku do MH-Z19B
  co2_senzor.begin(9600);

  // Pripoj se k Wi-Fi
  WiFi.mode(WIFI_OFF);
  WiFi.mode(WIFI_STA);
  WiFi.begin(wifi_ap, wifi_heslo);
  Serial.print("\r\nPripojuji se k Wi-Fi ");
  Serial.print(wifi_ap);
  uint32_t start = millis();
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    // Pokud se ani po 30 sekundach nemuzes pripojit,
    // restartuj cip a zacni znovu
    if ((millis() - start) > 30e3) {
      // Pri prvnim spusteni po flashnuti noveho firmwaru nebude restart fungovat
      // Dale uz ano
      ESP.restart();
    }
    delay(500);
  }

  // Pro kontrolu napis IP adresu, kterou jsi dostal od DHCP
  Serial.println(" OK");
  Serial.println(WiFi.localIP());

  // Zmer koncentraci CO2
  int16_t co2 = zmeritKoncentraci();
  if (co2 == -1) {
    Serial.println("Ty tragikomiku, mas to spravne zapojeny? Nelze precist koncentraci :-(");
  }
  else {
    Serial.printf("Koncentrace CO2: %d ppm\r\n", co2);
    // Uloz udaj skrze Apps Script do tabulky na Google Drive
    if (odeslatNaGoogleDrive(co2)) {
      Serial.println("Uspesne odeslano na Google Drive!");
    }
    else {
      Serial.println("Jsi disfunkcni jelito a mas nekde chybu, protoze to nejde odeslat na Google Drive :-(");
    }
  }

  // Uspani cipu na cca pet minut (300 000 000 us)
  // Je treba propojit piny RST a WAKE/D0,
  // jinak se cip neprobudi. Piny kabelem propojte
  // az po nahrani programu do cipu!

  ESP.deepSleep(300e6);
}

// Funkce loop je prazdna, cip totiz usina
void loop() {
  ;
}

Kód Apps Scriptu:

/* SKRIPT PŘISTUPUJE K TABULKOVÉMU DOKUMENTU
   A POTŘEBUJE K TOMU PRÁVA. TA MU UDĚLÍTE TŘEBA TAK,
   ŽE NEJPRVE SKRIPT SPUSTITE PŘIMO Z EDITORU „NA PRÁZDNO“
   GOOGLE VÁS POTÉ VYZVE, ABYSTE MU UDĚLILI PRÁVA
*/


// Vestavěná funkce doGet
// Zpracuje se v případě HTTP GET požadavku, 
// pokud je skript publikovaný jako webová aplikace
function doGet(e){
  // Převeď URL parametr co2 na číslo (www-adresa-skriptu?co2=XXX)
  var co2 = parseInt(e.parameter.co2);
  // Pokud se podařil převod na číslo, pokračuj
  if(!isNaN(co2)){
    // Zjisti aktuální středoevropský čas/datum ve formátu HH:mm:ss dd.MM. yyyy
    var cas = Utilities.formatDate(new Date(), "Europe/Prague", "HH:mm:ss dd.MM. yyyy");
    // Otevři tabulkový dokument podle jeho ID
    // ID získáte třeba tak, že otevřete dokument v prohlížeči – je to část jeho URL
    var doc = SpreadsheetApp.openById("XXXXXXXXXXX");
    // Získej první list tabulkového dokumnetu
    var list = doc.getSheets()[0];
    // Přidej nový řádek
    // V první buňce bude čas, ve druhé hodnota CO2
    list.appendRow([cas, co2]);
  }
}

Určitě si přečtěte

Články odjinud