KKM: SGP30-2.5K SENZOR KVALITETE ZRAKA
Početnik si s Dasduinom. Ili s elektronikom? Za oko ti je zapeo određeni modul, ali ne znaš kako ga koristiti? Bez brige, tu je KKM! Kako Koristiti Module (KKM) je serija blog tutorijala tvrtke soldered na kojoj ćeš pronaći sve što ti treba kako bi započeo rad sa svojim omiljenim modulom. Tutorijali obuhvaćaju: tehničke karakteristike, princip rada, upute kako povezati modul s Dasduinom te osnovni kod. Sve ostalo prepuštamo tebi na maštu.
UVOD
Ovoga puta u tutorijalu, upoznat’ ćemo se s SGP30-2.5K senzorom kvalitete zraka. Ovaj senzor može očitavati količinu CO2 čestica u zraku, no osim toga i samo zagađenje zraka, a mjerna jedinica koja se koristi jest PPM (Parts Per Million), odnosno PPB (Parts Per Billion) za kvalitetu zraka. Ovakav senzor je vrlo bitna stvar ukoliko želimo napraviti neki automatizirani sustav koji će aktivirati ventilacijski sustav ukoliko je zrak u kojem se nalazimo vrlo kontaminiran, te koji će pročistiti zrak ili pak samo se želimo baviti prikupljanjem podataka o prostoru oko nas (uz klasična mjerenja temperature, vlage i tlaka zraka). Ovakav senzor se često može naći u pročišćivačima i ovlaživačima zraka.
Karakteristike modula:
Napon napajanja: 2.2 VDC – 6VDC
Komunikacija: I2C
Struja: 48.2 mA za normalan način rada (očitavanje vrijednosti), 2-10 uA za sleep način rada
Frekvencija očitavanja vrijednosti: 1 Hz
Opseg mjerenja vijednosti: 400 – 60000 ppm (Parts Per Million) za eCO2, 0 – 60000 ppb (Parts Per Billion) za TVOC
KAKO MODUL RADI
Modul na sebi ima 4 pina: GND, VCC, SDA i SCL, što bi značilo da sa našim Dasduinom komunicira preko I2C komunikacije. Sam senzor radi na 1.8 VDC, no radi jednostavnosti, na samome modulu već postoji stabilizator napona koji će “spustiti” 5V s Dasduina (ili 3.3V) na 1.8V, te također na modulu već postoji i Level Konverter za spuštanje napona komunikacije s 5V (ili 3.3V) na 1.8V. Sam modul ne detektira CO2 direktno kao neki industrijski ili laboratorijski CO2 senzor, već detektira ekvivalentnu razinu CO2 u zraku (to je ujedno i razlog zašto piše eCO2, a ne CO2). Sama detekcija količine CO2 čestica u zraku jest bazirana na detekciji količine molekula vodika u zraku. Dakle, kada CO2 uđe u senzor, nizom kemijskih procesa se taj CO2 “pretvori” u vodik što se zatim detektira na senzoru. Ono što na prvu može bit problem jest to da će takav senzor detektirati i sam vodik kao CO2, što će poremetiti mjerenja. No, većina takvih senzora ima niz filtra koji onemogućavaju prolaz čestica vodika, već samo CO2 do mjesta gdje se vrši pretvorba CO2 u vodik. Ali isto tako treba imati na umu da niti jedan takav filtar nije savršen, te da će pri većoj količini vodika u zraku, propustiti dio vodika u sami senzor za detekciju, stoga nećemo imati točna mjerenja, a to je ujedno i razlog zašto taj senzor ne može zamijeniti neke laboratorijske i industrijske CO2 senzore. Nešto više o samom načinu rada CO2 i CO senzora pročitajte ovdje. Također, ovakav senzor nije namijenjen da bi se koristio u vanjskim uvjetima, već je namijenjen da se koristi unutar nekih prostorija. Razlog zato nije namijenjen za vanjsku uporabu jesu uvjeti u kojima bi radio, a to su velike razlike u temperaturi, velika razine kontaminacije zraka kojeg mjeri, velike i nagle promjene u vlazi zraka (koja jako utječe na točnost mjerenja), te same vrlo nagle promjene kontaminacije zraka s raznim česticama. Sve prethodno navedeno (osim zadnjeg) će drastično skratiti životni vijek senzora, no korisniku će dati krive podatke, pogotovo zadnje navedeno (vrlo nagle promjene u kontaminaciji zraka).
Senzor radi na način da on promatra količinu CO2 i kvalitetu zraka, te prati kako se mijenja, koja je “pozadinska” razina tih vrijednosti. Na temelju toga, on proračunava baseline koja mu daje neku referencu s kojom on može uspoređivati količinu CO2 u zraku, kao i kvalitetu zraka. Stoga, ukoliko želimo da nam taj senzor daje što je moguće točnije rezultate, potrebno je izvršiti proces kalibracije. Proces kalibracije je ovakav:
Senzor se mora postaviti u prostor u kojem je količina CO2 vrlo malena i kvaliteta zraka je vrlo dobra (zrak je vrlo malo kontaminiran s raznim česticama i molekulama). Količina CO2 i kvaliteta zraka za vrijeme kalibracije senzora ne smije varirati, odnosno, treba biti minimalna. Senzor mora raditi najmanje 12h. Nakon toga, senzor će imati izračunatu baseline vrijednost koju trebamo očitati i spremiti. S ime je proces kalibracije gotov, no za daljnji rad senzora, potrebno je barem svakih sat vremena očitavati novu vrijednost, da bi u slučaju deaktivacije senzora imali zapamćenu vrijednost koju ćemo odmah pri pokretanju senzora unijeti u njega, da senzor ne bi trebao ponovno raditi proces kalibracije. Valja imati na umu da te vrijednosti vrijede 7 dana ukoliko se senzor ne koristi, te da je nakon toga, ponovno potreban proces kalibracije da bi se saznala nova baseline vrijednost. Moguće je koristiti staru baseline vrijednost, no tada mjerenja senzora neće biti vrlo točna.
KAKO POVEZATI
Spajanje modula je vrlo jednostavno, pošto koristi I2C komunikaciju, te sam modul ima na sebi sve što je potrebno za ispravan rad senzora. Na slici dolje je moguće vidjeti shemu spajanja senzora. Ukoliko se podaci žele ispisivati na LCDu, potrebno je spojiti 16×2 LCD, no ukoliko se podaci žele ispisivati na Serial Monitoru, spajanje LCDa i trimera za podešavanje kontrasta na LCDu nije potrebno. Također, ukoliko se ne želi izvršiti korekcija mjerenja, tako da se mjeri vlaga i temperatura (u ovome primjeru preko DHT22 senzora), nije ga potrebno spajati. Trimer koji se koristi je 10kOhma, kao i otpornik kod DHT22 senzora.
ARDUINO KOD
Postoje tri koda; jedan koji izmjerene vrijednosti ispisuje na 16×2 LCD displayu, drugi ispis podataka ispisuje na Serial Monitoru Arduino IDEa, dok treći podatke ispisuje na LCDu s tim da osim podataka s SPG30 senzora, ispisuje podatke s DHT22 senzora u svrhu korekcije parametara koje mjeri SGP30 senzor. Da bi nam bilo lakše komunicirati s senzorom, čitati vrijednosti iz njega, koristit’ ćemo već gotovu biblioteku. Ukoliko ne znate kako instalirati biblioteku, svakako pogledajte naš tutorijal o tome.
Prvo kod koji podatke ispisuje na Serial Monitor, a pritiskom na gumb ispisuje baseline vrijednosti senzora.
#include <Wire.h> //Dodavanje biblioteke koja omogucava komunikaciju s samim senzorom
#include "Adafruit_SGP30.h" //Dodavanje biblioteke koja nam omogucava citanje vrijednosti sa samog senzora
Adafruit_SGP30 sgp;
//Konstruktor za biblioteku SGP30 senzora
#define TIPKA 12 //Na koji Croduino pin je spojeno tipkalo
void
setup
() {
Serial.begin(9600);
//Zapocni serijsku komunikaciju s brzinom od 9600 bauda
Serial.println(F(
"Test SGP30-2.5k senzora"
));
//Ispisi poruku da vidimo da li imamo uopće vezu s Croduinom
pinMode
(TIPKA,
INPUT_PULLUP
);
//Pin na koji je spojen gumb, definiramo kao ulaz s uključenim pull up otpornicima.
if
(! sgp.begin()) {
//Provjeri da li je uopce moguce pronaci senzor na I2C vezi
Serial.println(F(
"Ne mogu pronaci senzor :(\nProvjeri veze!"
));
while
(1);
//Ako nije moguce pronaci senzor, zaustavi daljnje izvodjenje programa
}
Serial.println(F(
"Senzor pronadjen! :)"
));
//Ukoliko imamo baseline vrijednosti od prije, mozemo ih odmah unijeti u senzor.
//Ukoliko ih nemamo, ovo ostaviti iskomentirano.
//sgp.setIAQBaseline(0x8E68, 0x8F41);
//NAPOMENA: Vrijednosti navedene ovdje su samo primjer i nije ih pozeljno unositi, jer ce u protivnom doci do krivog ocitanja.
}
uint8_t tipkaStaro = 1, tipkaTrenutno = 1;
//Varijabla koja pamti proslo stanje na tipkalu. To nam je potrebno da bi znali kada je tipka pritisnuta.
void
loop
() {
if
(! sgp.IAQmeasure()) {
//Pokusaj ocitati eCO2 i TVOC vrijednosti sa senzora i ispisi ih na Serial Monitoru
Serial.println(F(
"Neuspjesno ocitavanje vrijednosti sa senzora! :("
));
return
;
}
else
{
Serial.print(F(
"TVOC: "
)); Serial.print(sgp.TVOC); Serial.print(F(
" ppb\t"
));
Serial.print(F(
"eCO2: "
)); Serial.print(sgp.eCO2); Serial.println(F(
" ppm"
));
}
tipkaTrenutno =
digitalRead
(TIPKA);
if
(!tipkaTrenutno && tipkaStaro) {
//Ako je tipka pritisnuta, ocitaj i ispisi baseline vrijednosti na Serial Monitoru
uint16_t TVOC_base, eCO2_base;
//Varijable u koje spremamo baseline vrijednosti.
if
(! sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) {
Serial.println(F(
"Neuspjesno ocitavanje baseline vrijednosti sa senzora"
));
return
;
}
else
{
Serial.print(F(
">>>>Baseline vrijednosti [u HEX formatu]: eCO2: 0x"
)); Serial.print(eCO2_base, HEX);
Serial.print(F(
" & TVOC: 0x"
)); Serial.println(TVOC_base, HEX);
}
}
tipkaStaro = tipkaTrenutno;
//Zapamti prošlo stanje tipke, da bi znali kada je tipka pritisnuta (tipka je pritisnuta ako je staro stanje 1, a novo 0)
delay
(1000);
}
Drugi kod radi skoro isto kao i prvi, osim što, umjesto da izmjerene podatke ispiše na Serial Monitoru Arduino IDEa, on ih ispisuje na 16×2 LCDu.
#include <Wire.h> //Dodavanje biblioteke koja omogucava komunikaciju sa samim senzorom
#include "Adafruit_SGP30.h" //Dodavanje biblioteke koja nam omogucava citanje vrijednosti sa samog senzora
#include <LiquidCrystal.h> //Dodavanje biblioteke koja nam omogucava komunikaciju i upravljanje s 16x2 LCDom
Adafruit_SGP30 sgp;
//Konstruktor za biblioteku SGP30 senzora
//Odredjivanje koji pin LCDa će biti poveznan s Croduino pinom, npr. RS pin na LCDu će biti spojen na pin D2 Croduina.
const
int
LCD_RS = 2, LCD_EN = 3, LCD_D4 = 4, LCD_D5 = 5, LCD_D6 = 6, LCD_D7 = 7;
LiquidCrystal lcd(LCD_RS, LCD_EN, LCD_D4, LCD_D5, LCD_D6, LCD_D7);
#define TIPKA 12 //Na koji Croduino pin je spojeno tipkalo
void
setup
() {
lcd.begin(16, 2);
//Inicijalizacija biblioteke za LCD, te ju odmah inicijaliziramo na LCD dimenzija 16x2
pinMode
(TIPKA,
INPUT_PULLUP
);
//Pin na koji je spojen gumb, definiramo kao ulaz s ukljucenim pull up otpornicima.
if
(! sgp.begin()) {
//Provjeri da li je uopce moguce pronaci senzor na I2C vezi
lcd.print(F(
"Nema senzora :(\nProvjeri veze!"
));
while
(1);
//Ako nije moguce pronaci senzor, zaustavi daljnje izvodjenje programa
}
//Ukoliko imamo baseline vrijednosti od prije, mozemo ih odmah unijeti u senzor.
//Ukoliko ih nemamo, ovo ostaviti iskomentirano.
//sgp.setIAQBaseline(0x8E68, 0x8F41);
//NAPOMENA: Vrijednosti navedene ovdje su samo primjer i nije ih pozeljno unositi, jer ce u protivnom doci do krivog ocitanja.
}
uint8_t tipkaStaro = 1, tipkaTrenutno = 1;
//Varijabla koja pamti proslo stanje na tipkalu. To nam je potrebno da bi znali kada je tipka pritisnuta.
void
loop
() {
if
(! sgp.IAQmeasure()) {
//Pokusaj ocitati eCO2 i TVOC vrijednosti sa senzora i ispisi ih na LCDu
lcd.clear();
//Ocisti LCD prije svakog novog ispisa
lcd.print(F(
"Neuspjesno"
));
lcd.setCursor(0, 1);
lcd.print(F(
"ocitavanje :("
));
return
;
}
else
{
lcd.clear();
//Ocisti LCD prije svakog novog ispisa
lcd.print(F(
"TVOC:"
)); lcd.print(sgp.TVOC); lcd.print(F(
"ppb"
));
lcd.setCursor(0,1);
lcd.print(F(
"eCO2:"
)); lcd.print(sgp.eCO2); lcd.print(F(
"ppm"
));
}
tipkaTrenutno =
digitalRead
(TIPKA);
if
(!tipkaTrenutno && tipkaStaro) {
//Ako je tipka pritisnuta, ocitaj i ispisi baseline vrijednosti na LCDu
uint16_t TVOC_base, eCO2_base;
//Varijable u koje spremamo baseline vrijednosti.
if
(! sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) {
lcd.clear();
//Ocisti LCD prije svakog novog ispisa
lcd.println(F(
"Ne mogu ocitati"
));
lcd.setCursor(0, 1);
lcd.print(F(
"baseline"
));
return
;
}
else
{
lcd.clear();
//Ocisti LCD prije svakog novog ispisa
lcd.print(F(
"Base CO2:0x"
)); lcd.print(eCO2_base, HEX);
lcd.setCursor(0,1);
lcd.print(F(
"Base TVOC:0x"
)); lcd.print(TVOC_base, HEX);
delay
(1000);
}
}
tipkaStaro = tipkaTrenutno;
//Zapamti prošlo stanje tipke, da bi znali kada je tipka pritisnuta (tipka je pritisnuta ako je staro stanje 1, a novo 0)
delay
(1000);
}
I na kraju, kod koji osim što čita izmjerene podatke s SGP30 senzora, on mjeri i temperaturu i vlagu zraka s DHT22 senzora, pri tome korektirajući izmjerene podatke s SGP30 senzora u svrhu dobivanja točnijih izmjerenih podatka s SGP30 senzora (jer vlaga i temperatura najviše utječu na točnost izmjerenih podataka CO2 i TVOC).
#include <Wire.h> //Dodavanje biblioteke koja omogucava komunikaciju sa samim senzorom
#include "Adafruit_SGP30.h" //Dodavanje biblioteke koja nam omogucava citanje vrijednosti sa samog senzora
#include <LiquidCrystal.h> //Dodavanje biblioteke koja nam omogucava komunikaciju i upravljanje s 16x2 LCDom
#include "DHT.h" //Dodavanje biblioteke za citanje vrijednosti vlage i temperature zraka s DHT22 senzora
#define TIPKA 12 //Na koji Croduino pin je spojeno tipkalo
#define DHTPIN 8 //Na koji pin Croduina je spojen DHT22 senzor (u ovom slucaju na Croduinov pin D8)
#define DHTTYPE DHT22 //Vrsta DHT senzora koju koristimo - u ovom slucaju DHT22
Adafruit_SGP30 sgp;
//Konstruktor za biblioteku SGP30 senzora
//Odredjivanje koji pin LCDa ce biti poveznan s Croduino pinom, npr. RS pin na LCDu ce biti spojen na pin D2 Croduina.
const
int
LCD_RS = 2, LCD_EN = 3, LCD_D4 = 4, LCD_D5 = 5, LCD_D6 = 6, LCD_D7 = 7;
LiquidCrystal lcd(LCD_RS, LCD_EN, LCD_D4, LCD_D5, LCD_D6, LCD_D7);
DHT dht(DHTPIN, DHTTYPE);
//Konstruktor za DHT biblioteku
//Funkcija koja vraca apsolutnu vrijednost vlage u zraku
uint32_t getAbsoluteHumidity(
float
temperature,
float
humidity) {
//Aproksimacijska formula koja se moze naci u datasheetu SGP30 senzora
const
float
absoluteHumidity = 216.7f * ((humidity / 100.0f) * 6.112f *
exp
((17.62f * temperature) / (243.12f + temperature)) / (273.15f + temperature));
// [g/m^3]
const
uint32_t absoluteHumidityScaled =
static_cast
<uint32_t>(1000.0f * absoluteHumidity);
// [mg/m^3]
return
absoluteHumidityScaled;
}
void
setup
() {
lcd.begin(16, 2);
//Inicijalizacija biblioteke za LCD, te ju odmah inicijaliziramo na LCD dimenzija 16x2
dht.begin();
//Inicijalizacija bibliteke DHT senzora temperature i vlage zraka
pinMode
(TIPKA,
INPUT_PULLUP
);
//Pin na koji je spojen gumb, definiramo kao ulaz s ukljucenim pull up otpornicima.
if
(! sgp.begin()) {
//Provjeri da li je uopce moguce pronaci senzor na I2C vezi
lcd.print(F(
"Nema senzora :(\nProvjeri veze!"
));
while
(1);
//Ako nije moguce pronaci senzor, zaustavi daljnje izvodjenje programa
}
//Ukoliko imamo baseline vrijednosti od prije, mozemo ih odmah unijeti u senzor.
//Ukoliko ih nemamo, ovo ostaviti iskomentirano.
//sgp.setIAQBaseline(0x8E68, 0x8F41);
//NAPOMENA: Vrijednosti navedene ovdje su samo primjer i nije ih pozeljno unositi, jer ce u protivnom doci do krivog ocitanja.
}
uint8_t tipkaStaro = 1, tipkaTrenutno = 1;
//Varijabla koja pamti proslo stanje na tipkalu. To nam je potrebno da bi znali kada je tipka pritisuta.
void
loop
() {
float
temperature = dht.readTemperature();
//Ocitaj vrijednost temperature zraka s DHT22 senzora
float
humidity = dht.readHumidity();
//Ocitaj vlaznost zraka s DHT 22 senzora
if
(!(isnan(temperature) || isnan(humidity))) sgp.setHumidity(getAbsoluteHumidity(temperature, humidity));
//Korektiraj mjerenja s SGP30 senzora tako sto cemo uracunati temperatutu i vlaznost zraka koja najvise utjece na tocnost mjerenja SGP30 senzora
if
(! sgp.IAQmeasure()) {
//Pokusaj ocitati eCO2 i TVOC vrijednosti sa senzora i ispisi ih na LCDu
lcd.clear();
//Ocisti LCD prije svakog novog ispisa
lcd.print(F(
"Neuspjesno"
));
lcd.setCursor(0, 1);
lcd.print(F(
"ocitavanje :("
));
return
;
}
else
{
lcd.clear();
//Ocisti LCD prije svakog novog ispisa
lcd.print(F(
"T:"
)); lcd.print(temperature, 1); lcd.print(F(
"C H:"
)); lcd.print(humidity, 1); lcd.print(
'%'
);
lcd.setCursor(0, 1);
lcd.print(sgp.TVOC); lcd.print(F(
"ppb"
)); lcd.print(sgp.eCO2); lcd.print(F(
"ppm"
));
}
tipkaTrenutno =
digitalRead
(TIPKA);
if
(!tipkaTrenutno && tipkaStaro) {
//Ako je tipka pritisnuta, ocitaj i ispisi baseline vrijednosti na LCDu
uint16_t TVOC_base, eCO2_base;
//Varijable u koje spremamo baseline vrijednosti.
if
(! sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) {
lcd.clear();
//Ocisti LCD prije svakog novog ispisa
lcd.println(F(
"Ne mogu ocitati"
));
lcd.setCursor(0, 1);
lcd.print(F(
"baseline"
));
return
;
}
else
{
lcd.clear();
//Ocisti LCD prije svakog novog ispisa
lcd.print(F(
"Base CO2:0x"
)); lcd.print(eCO2_base, HEX);
lcd.setCursor(0, 1);
lcd.print(F(
"Base TVOC:0x"
)); lcd.print(TVOC_base, HEX);
delay
(1000);
}
}
tipkaStaro = tipkaTrenutno;
//Zapamti proslo stanje tipke, da bi znali kada je tipka pritisnuta (tipka je pritisnuta ako je staro stanje 1, a novo 0)
delay
(1000);
}