Luchtmeter

Het idee

Huizen worden steeds beter geïsoleerd, en alle kieren worden dichtgestopt. Goed voor het milieu en de energie-rekening, maar niet per se goed voor de luchtkwaliteit in het huis. We hebben een ouder huis, dus geen centrale ventilatie met warmtewisselaar. Te weinig ventilatie, te veel (of te weinig) vocht in de lucht, al met al hebben we geen idee hoe goed de lucht in huis is. En daar wil ik nu juist wel een idee van hebben.

Probleem is dat je het langzaam veranderen van de CO2-waarde eigenlijk als mens niet goed merkt. Wat zijn eigenlijk 'goede' CO2-waarden?

  • Echt schone lucht: 350 .. 400 PPM (Parts Per Million)
  • Schone lucht in de stad: 700 PPM
  • Stadslucht in woningen: 1400 PPM
  • Maximum waarde in schoollokalen: 4000 PPM
  • Begin problemen met gezondheid: 5000 PPM
  • Korte tijd verdraagbaar: 20.000 PPM
  • Ademhalingsproblemen: 25.000 .. 30.000 PPM
  • Uitgeademde lucht: 40.000 ... 50.000 PPM

O ja, en een goede luchtvochtigheid zit tussen de 40% en 60%.

Dus, gezocht een meter om de luchtkwaliteit in de gaten te houden... CO2-gehalte, vochtigheid, temperatuur. En misschien ook wel een mogelijkheid om de afzuiger automatisch aan te zetten als de kwaliteit te slecht wordt, én een koppeling naar mijn huis-netwerk (ook voor het bijhouden van de metingen, zoals ik ook doe voor energiegebruik). Niet standaard te krijgen, dus maar eens zelf aan de slag.

CO2-verloop over de dag
Verloop CO2-waarde over de dag, 7 dagen

< ... nu wordt er 'even' druk geknutseld ... >

OK, gedaan... Hoe erg is het nu eigenlijk? Ik heb een winterweek uitgezet in de grafiek links (winter: ramen staan niet open, en 's avonds zijn de grote gordijnen dicht: weinig ventilatie). Dit zijn 7 lijnen, van 7 opeenvolgende dagen. De trend is goed zichtbaar, al zijn er variaties per dag, bijvoorbeeld:

  • Dag 2 (oranje lijn): ik was 's avonds weg, loopt minder op met 1 persoon thuis
  • Dag 5 en 7, piek 21 uur: was dit het opwarmen van de gluh-wein (sensor is ook gevoelig voor alkohol)?
  • Dag 7, piek rond 15 uur: 's middags in de keuken bezig (gasfornuis)

Het huis (of in ieder geval de woonkamer) is dus 'te goed' geïsoleerd. Goed is te zien dat 's nachts er niemand in de kamer is, en de CO2-waarde geleidelijk daalt tot de 'schone lucht in de stad' waarde (al is deze waarde wat kunstmatig, dit heb ik als referentie voor schone lucht gekozen; zie hoe de CCS811 werkt...)

Maar 's ochtends bij het ontbijt loopt de waarde al weer aardig op, met maar twee personen in de kamer. Daarna daalt het weer wat, om rond het einde van de dag weer op te lopen, soms tot zo'n 2500 PPM. En dat terwijl we er al ons van bewust zijn, en met koken vaak de afzuiger aanzetten.. Maar, de natuurlijke ventilatie kan 2 personen dus al niet meer aan. Ook eens met het eerstvolgende feestje loggen...

Het dalen wordt natuurlijk ook beïnvloed door bijvoorbeeld het weer (als het hard waait tocht het meer, en zal de CO2 sneller dalen). Voor mijn meter: ik houd maar even 2000 PPM aan voor een eerste waarschuwing, en 4000 PPM voor een 'alarm'.

Schema
1. Het simpele schema, met centraal de Arduino Nano

De sensoren

Wat wil ik meten: in ieder geval CO2 (koolstofdioxide), VOC (Volatile Organic Compounds, oftewel in het Nederlands vluchtige koolwaterstoffen), en de luchtvochtigheid.

Temperatuur doe ik al, maar moet er ook bij (omdat de CCS811 temperatuurgevoelig is, en er een correctie nodig is). En een lichtmeting lijkt me ook wel leuk, geeft bijvoorbeeld 's avonds inzicht of er iemand thuis is. CO2 en temperatuur geeft je gelijk de ingrediënten voor een soort brandmelder.

CCS811 CO2 sensor

Als CO2-sensor en VOC-sensor is de CCS811 van AMS een goed bekend staande sensor, al moet je wel weten hoe er mee om te gaan. Een goede beschrijving is te vinden op de github site van Maarten. Hier kan je ook de software library vinden (voor de Nano, maar ook bijvoorbeeld voor de ESP8266), en de spullen om de sensor naar firmware versie 2.0 te updaten!

CCS811 moduleHet is echter wel een sensor met gebruiksaanwijzing. De sensor meet de CO2 vrij indirect via de VOC waardes, en is dus gevoelig voor andere stoffen zoals alcohol (plezierig testen, met een glas wijn in de hand). Bovendien is elke sensor anders, en moet zichzelf een tijdje inregelen voordat de waardes betrouwbaar zijn. Hierbij moet frisse lucht als referentie gebruikt worden; sluit de CCS811 dus niet gelijk alleen maar op in 'slechte' lucht. En kijk niet vreemd op als de eerste dagen de waarden wat merkwaardig lijken. Meer info in de datasheet, zie de CCS811 technische documentatie-pagina van AMS.

CCS811

Metal oxide (MOX) digital gas sensor to detect a wide range of Volatile Organic Compounds (VOCs) for indoor air quality monitoring. Provides eCO2 level (400 ... 32768ppm) or eTVOC indication (0 ... 32768ppb). Accuracy: ???

BME280

Digitale sensor voor relatieve luchtvochtigheid (RH), temperatuur (°C) en luchtdruk (hPa).

  • Vochtigheid: 0...100 % RH, ±3% absolute accuracy
  • Temperatuur: -40...+85 °C, ±0.5 °C absolute accuracy
  • Luchtdruk: 300...1100 hPa, ±1.0 hPa absolute accuracy

De sensor is ook wat gevoelig voor temperatuur en luchtvochtigheid, maar kan hiervoor corrigeren als'ie daar de waarden van weet. En daarom ook de BME280 sensor, waar ik die waarden meet en naar de CCS811 'verscheep'.

Ik gebruik een kant-en-klare module, de losse chip is voor een leek niet te hanteren. Let op, de sensor heeft 3.3 Volt nodig, en is niet geschikt voor 5 Volt. Voor de I2C en voeding is er dus een level-converter van 5 Volt naar 3.3 Volt nodig, zie verderop bij I2C.

BME280

BME280 moduleHad in een kitje de DHT11 gekregen, maar dat is als vochtigheidssensor niet veel soeps. De DHT22 schijnt al wat beter te zijn, maar toch.

Volgens de recensies is er een betere optie: Bosch Sensortec maakt een mooie luchtvochtigheids-sensor, de (ook 3.3V) BME280. Heeft ook gelijk een temperatuursensor en luchtdrukmeter, en laag stroomgebruik.

Ook hier gebruik ik weer een kant-en-klare module. Deze module kan overigens wel 5 Volt aan (kan ook 3.3 Volt) omdat er een 5V naar 3.3V omzetter op de achterkant zit, hangt dus direct aan de Arduino Nano.

Andere sensoren

Lichtsensor: gewoon een LDR (lichtgevoelige weerstand) naar aarde, met een 6.8 kOhm weerstand naar de plus, en aangesloten op een analoge ingang van de Nano. Hoeft niet erg precies te zijn, een licht-donker indicatie is genoeg. Wel nog een 10 µF condensator over de sensor naar aarde, om 50/100 Hz van kunstlicht wat uit te filteren.

Prototype luchtmeter
2. Functioneel prototype op breadboard

De hardware

Gebaseerd op hetzelfde concept als meer kleine besturingen die ik maak: de Arduino Nano als kern.

Eerst een prototype gemaakt met losse draden op een breadboard (foto 2), en de eerste software versie gemaakt om te testen. Daarna met stukjes flatcable direct aan de Nano gesoldeerd (gaat toch niet om hoge frequenties), zodat het geheel makkelijk in een behuizing kan worden gevouwen. Dit geheel is te zien in foto 3, al mist bijvoorbeeld de piëzo-buzzer hier nog.

Display, LEDs en knoppen

Het LCD is een LCM-X01602Dxx module met 2 regels van 16 tekens, gebaseerd op de standaard Hitachi HD44780 controller. Besturing gaat met 6 lijnen (4-bit mode, plus enable en read/write), en natuurlijk voeding en aarde. Het LCD is makkelijk aan te sturen met de standaard LiquidCrystal Arduino library.

Rotary EncoderDe drie knoppen in het prototype in figuur 3 worden vervangen door een 'rotary encoder', een simpele digitale draaiknop zoals in Arduino-ontwerpen wel vaker gebruikt wordt (zie figuur 4 en 5). Drie aansluitingen: twee voor de draaipulsen en een voor het indrukken. Hiermee is een eenvoudig menu-systeem makkelijk te bedienen. De pinnen hangen direct op de Arduino inputs, waarbij ik de in de Nano ingebouwde pull-up weerstanden activeer. De 'ontdendering' gebeurt voornamelijk in software, waarbij ook verschil wordt gemaakt tussen een normale en een lange indruktijd bij het indrukken van de knop. Wel heb ik een condensatortje van 10 nF tussen de schakelaars en ground hangen, om de ergste stoorsignalen te voorkomen.

De status wordt ook weergegeven door een 2-kleuren LED (rood/groen; oranje als alletwee aan); geeft snel de luchtkwaliteit weer als goed, matig, slecht. Daarnaast heb ik ook een piëzo-keramisch schijfje als luidsprekertje gebruikt, zodat het systeem piepjes kan geven als de waarde slecht wordt. En ik heb een uitgang voor een Solid-State Relais (SSR), waarmee ik een afzuiger aan kan zetten.

I2C

Prototype
3. Gesoldeerd (90%), maar nog niet in een behuizing

De belangrijkste sensoren (de CCS811 en BME280) hangen aan de 2-draads I²C-bus (wat dus in werkelijkheid 4 draden zijn: clock, data, voeding, aarde). Ook heb ik aan deze bus optioneel een EEPROM 32Kx8 geheugenchip AT24C256 voor logging van gegevens in stand-alone mode (voor als'ie niet aan mijn huis-netwerk hangt); de Nano legt elke 5 minuten de meetwaarden vast in het bovenste chipje op foto 3. Dit is de basis voor de grafiek (figuur 6) verderop.

Voor de CCS811 is een level-converter nodig, deze werkt op 3.3 Volt. Hiervoor had ik wat printjes besteld op Banggood, maar helaas zit er een ontwerpfout in hun converter: ze hebben het ontwerp symmetrisch willen maken, maar transistoren zijn niet symmetrisch; waardoor een printspoor verkeerd zat. Nooit antwoord gehad op mijn klacht... maar was gelukkig zelf te verhelpen.

Netwerk-interface

De netwerk-interface is voor verbinding met mijn Raspberry Pi home-control system. De Raspberry kan via deze verbinding alle sensorwaardes uitlezen en opslaan, en eventueel iets mee doen. Ook kan hij bijvoorbeeld de grenzen instellen wanneer de LED naar oranje of rood gaat. Verder kan de Raspberry boodschappen van een regel op het schermpje laten verschijnen. De luchtkwaliteitsmeter wordt gelijk via deze interface gevoed, heeft dus geen aparte 5-Volt voeding nodig (zie het schema figuur 1)

Het geheel (zonder behuizing)

Alles samengevoegd en aangesloten op het netwerk ziet het er uit als in figuur 4. Dit hangt al in huis net onder de thermostaat van de CV, en heeft dezelfde maat en kleur, valt (met behuizing) dus niet op. Ik heb nog geen plaatje met de uiteindelijke behuizing, maar wel van een vergelijkbare behuizing die ik heb gemaakt voor een ander project met zelfde display en draai/drukknop, zie foto 5. Het is gemaakt met een eenvoudig gebogen plexiglas ontwerp dat van binnen uit wit geverfd is (buitenkant daardoor mooi glossy), met voor de luchtsensoren extra gaten komen voor een goede luchtstroom (op de foto zit er even een plastic beschermhoes over de sensoren, tegen stof en zo). De drukknoppen zijn hier al vervangen door een simpele 'rotary encoder' rechts naast het scherm (komt een knop op, zie foto 5): een digitale draaiknop zoals in Arduino-ontwerpen wel vaker gebruikt wordt en die een makkelijk menu-systeem mogelijk maakt.

Gemonteerd op drager
4. Gemonteerd op een drager (gaatjesprint)

Helemaal rechts op de foto is de connector en het kabeltje naar het bedrade huisnetwerk te zien. Voor het netwerk zijn er maar 3 draden nodig, de overige zijn om het solid state relais voor de afzuiger te bedienen. CO2-kwaliteit is op orde, vandaar dat de LED groen brand. Hier een beetje helder, maar dat is met het kastje er om heen beter. Bovendien wil je dit vanuit de hele kamer kunnen zien.

De bovenste regel laat de CO2-waarde (in PPM) en de relatieve luchtvochtigheid zien, voor mij de belangrijkste waarden. De onderste regel laat wisselende velden zien, op de foto de buiten-temperatuur (in februari: 0 °C klopt): bewijs dat de link met het home netwerk werkt (op dit stukje hardware zit uiteraard geen buitenthermometer, zit midden in de kamer). Maar ook de tijd, luchtdruk en zo worden weergegeven: 4 verschillende regels wisselen per 2 seconden (waarbij een regel vanaf de Raspberry kan worden gestuurd, voor korte berichten).

De electronika in de behuizing zit op een stukje gaatjesprint gemonteerd: dit is eigenlijk alleen als drager voor alle modules; de achterkant is vlak gehouden (steken geen pinnen doorheen) waardoor het geheel tegen de muur is te schroeven.

De software

Nog uit te werken: de modulaire opbouw. Netwerk low-level op basis van interrupts als in mijn ander Home-control systemen; Arduino main loop waarin alle taken gescheduled worden. Hier al vast wat details:

Human Machine Interface

De hardware voor interactie met de gebruiker is vrij simpel:

  • De uitlezing is met het LCD, met via instellingen de keuze van weergegeven waardes en het menu (niet zichtbaar op de foto 4: is 'in rust' uit beeld, maar zie foto 5),
  • Draai/druk-knop ('rotary encoder') om door een menu heen te lopen en instellingen te veranderen en op te slaan (al zijn dat er niet zo veel: met name de waardes waarbij de LED naar oranje en rood gaat),
  • LED en buzzer om snel de toestand te zien zonder kleine lettertjes op het display te hoeven lezen. De LED is groen bij schone lucht (instelbaar, standaard tot 2000 PPM CO2), oranje als het wat minder wordt (tot 4000 PPM), en rood als de lucht echt verfrist moet worden. Ook gaat'ie nu en dan piepen bij slechte lucht.
Behuizing (idee)
5. Prototype van behuizing, menu zichtbaar

Met de rotary encoder is een eenvoudig menu-systeem makkelijk te bedienen (zie de 4 meest rechtse tekens op het schermpje in figuur 5). Bovenste 4 tekens (hier 'Main') zijn de naam van het menu of de parameter, onderste is de huidige selectie/waarde: deze is met draaien aan de knop te veranderen, en door op de knop te drukken wordt de keuze bevestigd.

Zo'n menu-systeem is altijd even denken: dit moet werken terwijl de rest van het systeem ondertussen door loopt. Je wilt dus eigenlijk twee taken hebben draaien: de metingen en berekeningen, en het menu-systeem. Nu heeft de Arduino geen multi-tasking operating systeem (het is maar een simpele 8-bitter). Aan de andere kant, het 'loop()'-concept van de Arduino laat je meerdere dingen in het hoofdprogramma doen, zolang je maar steeds weer naar 'loop()' terug keert. Hiervoor moet iedere taak wel onthouden waar/hoe ver hij was.

Het menu-systeem is daarom een 'state machine' implementatie, waarbij de huidige 'state' (toestand) wordt onthouden, en bij elke actie van de gebruiker wordt bijgewerkt. Deze state onthoudt of het menu zichtbaar is, in welk menu je zit, en wat je daar aan het doen bent (menu kiezen, instellingswaarde veranderen, en zo voort).

In de main loop wordt elke 25 milliseconde dit menusysteem aangeroepen, dat bijvoorbeeld kijkt of de gebruiker aan de knop heeft gedraaid, indien nodig de state en het scherm bij werkt, en weer naar het hoofdprogramma 'loop()' terugkeert; om na 25 milliseconde weer een zetje te krijgen. Op deze manier lijkt het of het menusysteem steeds actief is, en krijg je snel reactie op je acties.

De andere delen van het programma (zoals de klok, en het meten van de CO2-waardes) zijn op een vergelijkbare manier ontworpen, en delen op deze manier de processor met elkaar. Een soort cooperative multi-tasking, wat voor een simpel systeem als de Arduino Nano prima werkt. Voor dit doel een simpele library CyclicTimer gemaakt (ook om daar eens mee te oefenen).

Gebruikte libraries

Het leuke van Arduino is dat je zelf niet alles zelf hoeft uit te vinden, maar gebruik kunt maken van door anderen geschreven libraries. Ik maak bijvoorbeeld gebruik van de volgende libraries:

  • LCD: LiquidCrystal, de aansturing van het 16x2 tekens LCD-scherm
  • CCS811 van Maarten Pennings, aansturing van de AMS CO2 sensor (via I2C)
  • BME280I2C van Tyler Glenn, aansturing van de Bosch temperatuur en luchtvochtigheidssensor (via I2C)
  • Wire voor de laag-niveau I2C besturing
  • EEPROM voor opslag van de instellingen
  • Mijn CyclicTimer om de verschillende taken te 'schedulen'

Interrupts op de Nano

De Arduino gebruikt standaard zelf de seriële poort voor debug/print mogelijkheden, en dit loopt op basis van interrupts. Ook een aantal andere functies gebruiken interrupts. Wat nu als je ze zelf wilt gebruiken? Ik wil de seriële poort gebruiken voor mijn home control netwerk; en de afhandeling in mijn eigen interrupt-routines uitvoeren. Dat botst met het gebruik van Arduino van deze poort...

Het blijkt dat als je niet de seriële module gebruikt deze ook niet meegelinkt wordt, en dus niet botst met je eigen interrupt-routines. Dus geen Serial.xxx functies gebruiken (ook niet in libraries; ik moest de css811.cpp file hierop aanpassen, al is deze er al wel op voorbereid). De interrupt-routines kan je vervolgens gewoon in C(++) schrijven, al moet je ze wel voorzien van een aanwijzing voor de compiler (het ISR keyword vervangt de normale functie-declaratie void interruptnaam(void)). Hier een eenvoudige versie van de transmit interrupt routine die zelfstandig een klaarstaand bericht verstuurd (transmitPtr en transmitCount worden in het hoofdprogramma in een start_transmit functie gezet, waarna de interrupt weer aangezet wordt):

ISR(USART_UDRE_vect) {        // Transmit interrupt
  if (transmitCount) {        // Still data in buffer?
    --transmitCount;
    UDR0 = *transmitPtr; transmitPtr++;
    if (transmitCount==0) {   // Last char: disable IRQ
      USART_TRANSMIT_DISABLE();
      transmitPtr = nullptr;  // Signal we are ready
    }
  }
}

Soortgelijk voor de binnenkomende tekens op de poort: deze worden door een interrupt opgevangen en in een buffer geplaatst. De routine is 'slim' in de zin dat deze het netwerkprotocol kent, en dus weet wanneer een bericht voor deze node is, en wanneer het volledig is ontvangen; het hoofdprogramma krijgt dan een seintje dat het bericht kan worden verwerkt.

Zijn twee type berichten:

Luchtmeting
6. Luchtmeting over 4 dagen
  • Broadcast berichten, die door alle nodes worden verwerkt (gebruikt om een keer per dag de tijd te synchroniseren). Bij verwerking hiervan wordt er geen bericht teruggestuurd.
  • Node-specifieke berichten (unicast), bijvoorbeeld om een waarde te zetten of op te vragen (b.v. het CO2 gehalte uitlezen). Dit geeft een bericht terug, bijvoorbeeld de opgevraagde waarde. De Raspberry weet dus dat het bericht correct is aangekomen.

Dit werkt al jaren, goed getest op de andere nodes als voor de CV/vloerverwarmingbesturing. Moest het alleen overzetten van de ATtiny naar de Arduino Nano.

Uitleg metingen/plaatje

Zie figuur 6 voor mijn eerste meetserie. De meting loopt over 4 dagen: de lichtmeting (onderste lijn) laat het dag-nacht ritme zien aan de hand van de lichtmeting; overdag begonnen en geëindigd. Gemeten midden februari, de dagen waren nog kort... De eerste hoge piek komt door alcohol (door de spiritus bij het streep-vrij ramen wassen), daar is deze sensor ook erg gevoelig voor. Het klippen van VOC-waardes op waarde 1024 is mijn 'fout' (sla maar 8 bits op, foutje in de schaalfactor in de software).