Domácí monitoring CO₂ s ESP32 a SCD41

Už dlouho mě zajímalo, jak jsme na tom doma s kvalitou vzduchu. Obzvlášť v zimě, kdy se člověku moc nechce větrat, ale zároveň ví, že by to nejspíš bylo potřeba.

Teploměr s vlhkoměrem už doma mám, tentokrát mě ale zajímala koncentrace CO₂, která může mít vliv na únavu a schopnost soustředit se. Hotový monitor se samozřejmě dá koupit, ale co bych to byl za bastlíře, kdybych si nepostavil vlastní.

Monitorovací stanice CO₂

Použitý hardware

Základem celého zařízení je čidlo CO₂ SCD41, kterému jsem se podrobněji věnoval v tomto článku. Jako řídicí desku jsem použil XIAO ESP32-C3. Ta pravidelně načítá koncentraci CO₂ a výsledek jednoduše signalizuje pomocí tří LED:

  • Zelená – vzduch je v pořádku
  • Žlutá – bylo by vhodné vyvětrat
  • Červená – vyvětrání je už potřeba

Zařízení napájí jeden Li-ion článek s kapacitou 3350 mAh, připojený přímo k řídicí desce. Ta zároveň zjišťuje jeho napětí a prostřednictvím USB-C se stará také o jeho nabíjení.

Měřící cyklus probíhá přibližně jednou za pět minut. Deska načte údaje ze senzoru i stav akumulátoru, rozsvítí LED odpovídající aktuální koncentraci CO₂ a následně odešle data na web. Tam se zobrazují aktuální hodnoty a průběžně vykreslují grafy.

Zapojení

Připojení k základní desce je následující:

Pin XIAO ESP32-C3Použití
D0měření napětí akumulátoru
D1zelená LED
D2žlutá LED
D3červená LED
D4SDA čidla SCD41
D5SCL čidla SCD41
3V3VCC čidla SCD41 a napájení LED
GNDGND čidla SCD41

Držák akumulátoru je připájen ke speciálním ploškám na spodní straně desky. Pro měření napětí akumulátoru jsem mezi jeho kladný pól a GND zapojil dělič složený ze dvou rezistorů 220 kΩ. Jejich společný bod je připojený k pinu D0, který je v programu použitý jako analogový vstup A0.

Dělič sníží napětí akumulátoru na polovinu. Při plně nabitém Li-ion článku s napětím 4,2 V se tak na analogový vstup dostane přibližně 2,1 V. Díky vysokým hodnotám rezistorů odebírá dělič z akumulátoru jen zanedbatelný proud.

Připojení akumulátoru a děliče napětí

Tři LEDky jsou kvůli jednoduchosti zapojené se společnou anodou a jedním společným rezistorem 10 kΩ. Jejich katody vedou přímo k jednotlivým pinům řídicí desky podle tabulky. LED se proto rozsvítí nastavením příslušného výstupu do logické nuly.

Rezistor 10 kΩ omezuje proud LED na velmi malou hodnotu. Nesvítí nijak výrazně, ale pro jednoduchou stavovou signalizaci je jejich jas dostačující. Jeden společný rezistor lze v tomto případě použít proto, že program vždy rozsvěcí pouze jednu LEDku.

Kompletní zapojení

Program pro ESP32

Program pro XIAO ESP32-C3 zajišťuje celý cyklus měření, signalizaci pomocí LED, kontrolu napětí akumulátoru i odesílání dat na web. Kvůli co nejnižší spotřebě není deska spuštěná nepřetržitě. Po většinu času se nachází v režimu Deep Sleep a probouzí se pouze jednou za pět minut.

Pro komunikaci se senzorem je potřeba nainstalovat knihovnu Sensirion I2C SCD4x. Ostatní použité knihovny jsou součástí podpory desek ESP32 v Arduino IDE.

Na začátku programu je potřeba doplnit údaje pro připojení k Wi-Fi, adresu přijímacího skriptu na serveru a vlastní API klíč:

const char WIFI_SSID[] = "xxxx";
const char WIFI_HESLO[] = "xxxx";
const char SERVER_URL[] = "https://xxxx/prijem.php";
const char API_KLIC[] = "xxxx";

Ve stejné části lze změnit také interval měření a hranice pro jednotlivé barvy LED:

const uint32_t INTERVAL_SPANKU = 300;
const uint16_t HRANICE_ZELENA = 1000;
const uint16_t HRANICE_CERVENA = 1500;
const uint16_t HYSTEREZE = 50;

Interval 300 sekund odpovídá pěti minutám. Zelená LED svítí pod 1000 ppm, žlutá od 1000 do 1499 ppm a červená od 1500 ppm výše.

Význam jednotlivých koncentrací CO₂ jsem podrobněji popsal v článku o SCD41.

Průběh jednoho měření

Po probuzení program inicializuje sběrnici I²C, komunikaci se senzorem SCD41 a analogový vstup pro měření napětí akumulátoru. Senzor se následně probudí z úsporného režimu a provede dvě jednorázová měření. Výsledek prvního měření program nepoužije a zpracuje až druhé, ze kterého získá:

  • Koncentraci CO₂ v ppm
  • Teplotu ve °C
  • Relativní vlhkost v %
  • Napětí akumulátoru ve V

Po načtení hodnot se SCD41 znovu přepne do úsporného režimu. Napětí akumulátoru se měří šestnáctkrát a výsledky se zprůměrují, aby byl údaj méně ovlivněný šumem. Protože odporový dělič snižuje napětí na polovinu, program naměřenou hodnotu opět násobí dvěma.

Signalizace pomocí LED

Podle naměřené koncentrace CO₂ program vybere odpovídající LED. Protože mají diody společnou anodu, rozsvěcují se nastavením příslušného výstupu do logické nuly.

Přepínání barev obsahuje hysterezi 50 ppm. Ta zabraňuje tomu, aby se při hodnotě pohybující se těsně kolem nastavené hranice LED neustále přepínaly. Pokud například svítí zelená, žlutá se rozsvítí až po překročení 1050 ppm. Zpět na zelenou se přepne teprve po poklesu pod 950 ppm.

Poslední použitá barva je uložená v paměti, jejíž obsah zůstává zachovaný i během Deep Sleep. Stejně tak program uzamkne stav výstupních pinů, takže zvolená LED svítí i v době, kdy je ESP uspané.

Pokud se měření ze senzoru nepodaří, program ponechá svítit poslední platnou barvu. Chybu zároveň vypíše do sériového monitoru.

Odeslání dat na web

Po úspěšném měření se ESP připojí k nastavené Wi-Fi síti. Pokud se připojení nepodaří do deseti sekund, pokus ukončí a pokračuje uspáním, aby zbytečně nevybíjelo akumulátor.

Data se na server odesílají metodou POST v tomto tvaru:

co2=842&teplota=23.45&vlhkost=48.20&baterie=3.917

Součástí požadavku je také API klíč, podle kterého může přijímací skript ověřit, že data pocházejí z mého zařízení. Po odeslání program vypíše do sériového monitoru návratový kód a odpověď serveru, odpojí Wi-Fi a celý modul přepne na dalších pět minut do Deep Sleep.

Příklad výpisu na sériový port

V aktuální verzi používá HTTPS spojení bez ověřování certifikátu serveru pomocí setInsecure(). Přenos je sice šifrovaný, ale zařízení neověřuje totožnost serveru. Pro důkladněji zabezpečené řešení by bylo vhodné doplnit ověření jeho certifikátu.

Skripty na serveru

Pro ukládání a zobrazování naměřených hodnot jsem použil dva jednoduché PHP skripty. Není potřeba žádná databáze, všechna data se ukládají do běžných CSV souborů:

  • prijem.php přijímá a ukládá data odeslaná z ESP
  • index.php zobrazuje aktuální hodnoty a vykresluje grafy

Příjem a ukládání dat

ESP odesílá naměřené hodnoty na skript prijem.php metodou POST. Společně s daty posílá v autorizační hlavičce také API klíč. Stejný klíč musí být nastavený v programu ESP i v PHP skriptu:

const API_KLIC = 'xxxx';

Skript nejprve zkontroluje použitou metodu a API klíč. Potom ověří, jestli byly přijaty všechny čtyři očekávané hodnoty:

  • Koncentrace CO₂
  • Teplota
  • Relativní vlhkost
  • Napětí akumulátoru

Součástí kontroly jsou také povolené rozsahy. Skript například nepřijme vlhkost mimo rozsah 0 až 100 % nebo napětí akumulátoru vyšší než 6 V. Tím se do souboru nedostanou zjevně poškozená nebo nesmyslná data.

Pokud je všechno v pořádku, naměřené hodnoty se uloží do adresáře data. Ten si skript při prvním spuštění automaticky vytvoří. Webový server ale musí mít povolený zápis do adresáře, ve kterém je prijem.php uložený.

Pro každý měsíc vznikne samostatný soubor, například:

data_2026-08.csv

Jednotlivé řádky mají tento formát:

datum;cas;co2;teplota;vlhkost;baterie
2026-08-09;03:45:12;842;23.45;48.20;3.917

Datum a čas nepřicházejí z ESP, ale doplní je server v okamžiku přijetí dat. Ve skriptu je proto nastavené české časové pásmo:

date_default_timezone_set('Europe/Prague');

Během zápisu se CSV soubor uzamkne pomocí flock(). Tím je chráněný proti situaci, kdy by se do něj pokoušelo zapisovat více požadavků současně. Po úspěšném uložení vrátí server odpověď:

OK

Při chybě vrátí odpovídající HTTP kód a krátký popis, například ERR auth, ERR missing nebo ERR range. Tyto odpovědi se zobrazují také v sériovém monitoru ESP, takže se podle nich dá problém snadněji hledat.

Zobrazení hodnot a grafů

Druhý skript index.php prochází uložené CSV soubory a načítá z nich data za zvolené období. Na webové stránce lze přepínat mezi třemi rozsahy:

  • Posledních 24 hodin
  • Posledních 7 dní
  • Posledních 30 dní

V horní části stránky se zobrazí čas posledního přijatého měření a aktuální hodnoty CO₂, teploty, vlhkosti a napětí akumulátoru.

Pod nimi jsou samostatné grafy pro všechny čtyři veličiny. K jejich vykreslení je použitá javascriptová knihovna Chart.js, která se načítá z veřejného CDN serveru. Webový prohlížeč proto při otevření stránky potřebuje přístup k internetu.

Graf koncentrace CO₂ má navíc barevně podbarvená pásma odpovídající signalizaci na zařízení:

  • Zelené pásmo do 1000 ppm
  • Oranžové pásmo od 1000 do 1500 ppm
  • Červené pásmo nad 1500 ppm

Díky tomu je na první pohled vidět nejen aktuální stav, ale také například průběh koncentrace CO₂ během noci nebo její pokles po otevření okna.

Náhled webové stránky

Nahrání na server

Oba PHP soubory stačí nahrát do stejného adresáře na serveru s podporou PHP. V souboru prijem.php je potřeba nastavit vlastní dostatečně dlouhý a náhodný API klíč a stejnou hodnotu zadat také do programu ESP.

API klíč omezuje příjem dat na požadavky, které tento klíč znají. Samotná stránka s grafy v této verzi není chráněná heslem a po zadání její adresy je veřejně přístupná.

Pokud server vrací chybu ERR auth, přestože jsou oba klíče stejné, může být problém v tom, že hosting nepředává autorizační hlavičku Authorization PHP skriptu.

Lokální verze programu

Pro použití bez webového serveru jsem vytvořil také samostatnou verzi programu. ESP32 v tomto případě neodesílá data na internet, ale vytvoří vlastní Wi-Fi síť s názvem CO2-monitor. Po připojení telefonu nebo počítače stačí v prohlížeči otevřít adresu http://192.168.4.1 .

Výchozí název sítě a heslo lze změnit na začátku programu:

const char WIFI_NAZEV[] = "CO2-monitor";
const char WIFI_HESLO[] = "co2monitor";

Celá webová stránka je uložená přímo v paměti ESP a nepoužívá žádné externí knihovny ani internetové služby. Lokální monitor proto funguje i bez domácí Wi-Fi a bez připojení k internetu.

Měření a zobrazení hodnot

Senzor SCD41 pracuje v režimu periodického měření a nové údaje poskytuje přibližně jednou za pět sekund. Program načítá koncentraci CO₂, teplotu a relativní vlhkost. Podle koncentrace CO₂ zároveň ovládá trojici LED stejným způsobem jako předchozí verze programu.

Na webové stránce se zobrazují:

  • Aktuální koncentrace CO₂
  • Teplota
  • Relativní vlhkost
  • Barevné hodnocení kvality vzduchu
  • Doba běhu zařízení
  • Počet připojených zařízení
  • Počet bodů uložených v historii

Prohlížeč si aktuální hodnoty načítá každé dvě sekundy. Protože samotný SCD41 měří přibližně jednou za pět sekund, hodnoty se mohou mezi několika načteními stránky opakovat.

Lokální grafy

Součástí stránky jsou také samostatné grafy koncentrace CO₂, teploty a vlhkosti. Graf CO₂ má barevně podbarvená pásma odpovídající zelenému, oranžovému a červenému stavu.

Do historie se ukládá jeden bod každých pět minut. Paměť pojme maximálně 288 bodů, což odpovídá posledním 24 hodinám měření. Po zaplnění se nejstarší hodnoty postupně přepisují novými. Webová stránka načítá aktualizovanou historii každých 30 sekund.

Historie je uložená pouze v operační paměti ESP. Po restartu nebo odpojení napájení se proto smaže a začne se vytvářet znovu. Tato verze navíc nezná skutečné datum a čas, takže graf zobrazuje stáří jednotlivých záznamů vzhledem k době běhu zařízení.

Zobrazení lokální verze

Napájení

U lokální verze se počítá s trvalým napájením prostřednictvím USB. ESP se neuspává, nepřetržitě provozuje vlastní Wi-Fi síť a obsluhuje webový server, takže má výrazně vyšší spotřebu než varianta odesílající data jednou za pět minut a následně přecházející do režimu Deep Sleep.

Lokální verze je proto vhodná hlavně tam, kde není k dispozici server nebo připojení k internetu, ale zařízení může být trvale napájené. Její výhodou je úplná nezávislost na další síťové infrastruktuře, nevýhodou je vyšší spotřeba a ztráta historie po restartu.

Krabička

Hardware i software fungovaly tak, jak měly, ale z mnoha důvodů není vhodné nechávat zařízení trvale ve stavu vrabčího hnízda. Po důkladném otestování proto přišel čas na výrobu krabičky, která elektroniku ochrání a zároveň bude alespoň trochu dobře vypadat.

Krabičku jsem slepil z několika překližkových desek. V horní části je umístěný senzor SCD41, kolem kterého jsem vyvrtal několik otvorů umožňujících proudění vzduchu. Bez nich by se vzduch uvnitř krabičky vyměňoval příliš pomalu a měření by reagovalo na změny koncentrace CO₂ se zpožděním.

Ve spodní části jsou tři otvory pro zelenou, žlutou a červenou LED. Aktuální stav vzduchu je tak vidět přímo na přední straně zařízení.

Začátek výroby

Slepenou krabičku jsem zabrousil, zaoblil její hrany a poté ji natřel svou oblíbenou mahagonovou lazurou. Elektroniku jsem přilepil do spodní části krabičky, do které jsem předtím vyvrtal a vybrousil otvor pro USB konektor. Ten slouží k nabíjení a případnému přeprogramování zařízení.

Nakonec jsem elektroniku zasunul dovnitř, LED diody usadil do připravených otvorů a spodní část přilepil k horní. Spoj jsem následně zabrousil do roviny a přetřel barvou.

Pohled zepředu
USB-C konektor ve dně

Závěr

Výsledkem je jednoduchý domácí monitor CO₂, u kterého pro základní orientaci nemusím pokaždé otevírat web – aktuální stav je hned vidět podle jedné ze tří LED. Když mě zajímá podrobnější průběh, serverová verze ukládá naměřené hodnoty do historie a vykresluje je do grafů. Pokud nechci používat žádný server, můžu nahrát lokální verzi, která si vytvoří vlastní Wi-Fi síť a webovou stránku.

U samotného měření je důležité také umístění monitoru. Aby byla naměřená koncentrace CO₂ vypovídající, je vhodné zařízení umístit přibližně do výšky hlavy. Teplotu a relativní vlhkost ze SCD41 v tomto projektu beru pouze jako orientační hodnoty – hlavní sledovanou veličinou je koncentrace CO₂.

Celkově se mi kombinace SCD41 a XIAO ESP32-C3 osvědčila. Pro bateriový provoz můžu většinu času využít Deep Sleep, přitom mi zůstává jednoduchá signalizace přímo na zařízení a podle zvolené verze programu i možnost dlouhodobého sledování naměřených dat.

Související články

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *