Components and supplies
ADAFRUIT FEATHER M0 ADALOGGER
Jumper wires (generic)
MONOCHROME 0.96" 128X64 OLED GRAPHIC DISPLAY
ADAFRUIT CCS811 AIR QUALITY SENSOR BREAKOUT - VOC AND ECO2
DHT11 Temperature & Humidity Sensor (4 pins)
Tools and machines
Soldering iron (generic)
Apps and platforms
Arduino IDE
Project description
Code
Software version 2.0.3
arduino
This is the version 2.0.3 of the software. Has better time handling and improved display of data. Date and time now displayed on the main screen. Please inform if you have problems.
1/* Air Quality Sensor V2.0.3 2018/03/27 Luis Sousa */ 2 3#include <SPI.h> //Standard lib 4#include <SD.h> //author=Arduino, SparkFun version=1.2.2 5#include <DHT.h>//author=Adafruit version=1.3.0 6//#include <Wire.h> 7#include <RTCZero.h> //author=Arduino version=1.5.2 8#include <RTClib.h> //author=Adafruit version=1.2.1 9#include <Adafruit_GFX.h> //author=Adafruit version=1.2.3 10#include <Adafruit_SSD1306.h> //author=Adafruit version=1.1.2 11#include <Adafruit_CCS811.h>//author=Adafruit version=1.0.0 12 13//Levels 14//temp levels 15#define low_temp_level_1 18 16#define low_temp_level_2 12 17#define low_temp_level_3 8 18#define high_temp_level_1 29 19#define high_temp_level_2 35 20#define high_temp_level_3 40 21//rh levels 22#define low_rh_level_1 40 23#define low_rh_level_2 30 24#define low_rh_level_3 20 25#define high_rh_level_1 70 26#define high_rh_level_2 80 27#define high_rh_level_3 90 28//co2 levels 29#define co2_level_1 1000 30#define co2_level_2 1500 31#define co2_level_3 3000 32//TVOC levels 33#define tvoc_level_1 100 34#define tvoc_level_2 500 35#define tvoc_level_3 1000 36 37//Status LEDS 38#define green_led 14 39#define yellow_led 15 40#define red_led 16 41 42//OLED Display If using software SPI (the default case): 43#define OLED_MOSI 6 44#define OLED_CLK 10 45#define OLED_DC 11 46#define OLED_CS 12 47#define OLED_RESET 5 48#define display_refresh_interval 1 //2 seconds refresh 49Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS); 50 51//DHT Sensor 52#define DHTPIN 19 // what pin DHT is connected to (19=A5 on this Board) 53#define DHTTYPE DHT11 // DHT 11 54#define DHT_read_interval 3 //Read temp every X seconds 55DHT dht(DHTPIN, DHTTYPE); //object dht creation 56 57//SD Card 58#define cardSelect 4 //SD chip select pin 59#define ficheiro "dados.csv" // nome do ficheiro de dados 60//#define Serial_Header "Date,Hour,Vbat,Temp,%RH,eCO2,TVOC,STATUS" 61#define SD_Header "Date,Hour,Vbat,min_Temp,Max_Temp,min_RH,Max_RH,min_eCO2,Max_eCO2,min_TVOC,Max_TVOC,STATUS" 62#define SD_write_interval 300 //seconds between SD writes 63#define SD_led 8 //Green LED 64 65//RTC 66RTCZero rtc; // Create an rtc object / 67 68//Vbat read 69#define vbatpin A7 //pin to read Bat Voltage 70#define vbat_read_interval 30 //seconds between vbat readings 71float measuredvbat; 72 73//Set up variables using the SD utility library functions: 74Sd2Card card; 75SdVolume volume; 76SdFile root; 77 78//Set up CCS811 object ccs 79Adafruit_CCS811 ccs; 80#define CCS811_read_interval 3 //Read data every X seconds 81 82//variable declaration 83unsigned long nseconds; //seconds since start of day. 84unsigned long SD_last_write = 0; 85unsigned long vbat_last_read = 0; 86unsigned long DHT_last_read = 0; 87unsigned long CCS811_last_read = 0; 88unsigned long display_last_refresh = 0; 89boolean error = false; //clear errors 90boolean alarm = false; //clear alarms 91boolean alarm_level_0 = false; 92boolean alarm_level_1 = false; 93boolean alarm_level_2 = false; 94boolean alarm_level_3 = false; 95float eco2 = 0.0; 96float max_eco2 = 0.0; 97float min_eco2 = 10000; 98float tvoc = 0.0; 99float max_tvoc = 0.0; 100float min_tvoc = 10000; 101float temp = 0.0; 102float max_temp = 0.0; 103float min_temp = 100; 104float rh = 0.0; 105float max_rh = 0.0; 106float min_rh = 100; 107String errorString = ""; 108String alarmString = ""; 109//byte day = 27; 110//byte month = 03; 111//byte year = 18; 112//byte seconds = 00; 113//byte minutes = 00; 114//byte hours = 12; 115 116void setup() { 117 pinMode(SD_led, OUTPUT);// initialize digital pin 8 as an output. 118 pinMode(green_led, OUTPUT);// initialize digital pin as an output. 119 pinMode(yellow_led, OUTPUT);// initialize digital pin as an output. 120 pinMode(red_led, OUTPUT);// initialize digital pin as an output. 121 122 //Serial Port Initialization 123 Serial.begin(57600); 124 delay (300);//wait for the serial to be ready 125 /* 126 while (!Serial) { 127 ; // wait for serial port to connect. Needed for native USB port only 128 } 129 */ 130 131 //RTC initialization 132 DateTime now; 133 now = (DateTime(F(__DATE__), F(__TIME__))); //Compilation time 134 now=now+20; //compensation for Compile + Reset time 135 rtc.begin(); // initialize RTCZero 136 rtc.setTime(now.hour(), now.minute(), now.second()); 137 rtc.setDate(now.day(), now.month(), now.year()-2000); 138 139 //DHT Initialisation 140 dht.begin(); 141 142 // CCS811 test 143 if (!ccs.begin()) { 144 Serial.println("Failed to start CCS811 sensor! Please check your wiring."); 145 errorString = "CCS811 Error"; 146 //while (1); 147 } 148 //calibrate CCS811 temperature sensor 149 while (!ccs.available()); 150 temp = ccs.calculateTemperature(); 151 ccs.setTempOffset(temp - 25.0); 152 153 //Display initialization 154 display.begin(SSD1306_SWITCHCAPVCC);// by default, we'll generate the high voltage from the 3.3v line internally! 155 display.clearDisplay(); // Clear the buffer. 156 157 //SD Initialization and Test 158 Serial.print("\ 159Initializing SD card..."); 160 // we'll use the initialization code from the utility libraries 161 // since we're just testing if the card is working! 162 if (!card.init(SPI_HALF_SPEED, cardSelect)) { 163 Serial.println("initialization failed. Things to check: "); 164 Serial.println("* is a card inserted ? "); 165 Serial.println("* is your wiring correct ? "); 166 Serial.println("* did you change the chipSelect pin to match your shield or module ? "); 167 //while (1); 168 errorString = "SD Card Error"; 169 } else { 170 Serial.println("Wiring is correct and a card is present."); 171 } 172 // print the type of card 173 Serial.println(); 174 Serial.print("Card type : "); 175 switch (card.type()) { 176 case SD_CARD_TYPE_SD1: 177 Serial.println("SD1"); 178 break; 179 case SD_CARD_TYPE_SD2: 180 Serial.println("SD2"); 181 break; 182 case SD_CARD_TYPE_SDHC: 183 Serial.println("SDHC"); 184 break; 185 default: 186 Serial.println("Unknown"); 187 } 188 // Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32 189 if (!volume.init(card)) { 190 Serial.println("Could not find FAT16 / FAT32 partition.\ 191Make sure you've formatted the card"); 192 //while (1); 193 errorString = "SD Format Error"; 194 } 195 Serial.print("Clusters: "); 196 Serial.println(volume.clusterCount()); 197 Serial.print("Blocks x Cluster: "); 198 Serial.println(volume.blocksPerCluster()); 199 Serial.print("Total Blocks: "); 200 Serial.println(volume.blocksPerCluster() * volume.clusterCount()); 201 Serial.println(); 202 // print the type and size of the first FAT-type volume 203 uint32_t volumesize; 204 Serial.print("Volume type is: FAT"); 205 Serial.println(volume.fatType(), DEC); 206 volumesize = volume.blocksPerCluster(); // clusters are collections of blocks 207 volumesize *= volume.clusterCount(); // we'll have a lot of clusters 208 volumesize /= 2; // SD card blocks are always 512 bytes (2 blocks are 1KB) 209 Serial.print("Volume size (Kb): "); 210 Serial.println(volumesize); 211 Serial.print("Volume size (Mb): "); 212 volumesize /= 1024; 213 Serial.println(volumesize); 214 Serial.print("Volume size (Gb): "); 215 Serial.println((float)volumesize / 1024.0); 216 Serial.println("\ 217Files found on the card (name, date and size in bytes): "); 218 root.openRoot(volume); 219 // list all files in the card with date and size 220 root.ls(LS_R | LS_DATE | LS_SIZE); 221 if (!SD.begin(cardSelect)) { 222 errorString = ("No SD Card"); 223 } 224 else { 225 File logfile = SD.open(ficheiro, FILE_WRITE); 226 if (!logfile) 227 errorString = ("File Error"); 228 else { 229 Serial.print("Gravando em: "); 230 Serial.println(ficheiro); 231 logfile.println(SD_Header); 232 logfile.close(); 233 } 234 } 235} 236 237void loop() { 238 //How many seconds since 0H (start of day) 239 nseconds = 3600 * rtc.getHours() + 60 * rtc.getMinutes() + rtc.getSeconds(); 240 241 //measure Vbat 242 if (abs(nseconds - vbat_last_read >= vbat_read_interval)) //time to read vbat 243 { 244 measuredvbat = analogRead(vbatpin); 245 measuredvbat *= 2; // we divided by 2, so multiply back 246 measuredvbat *= 3.3; // Multiply by 3.3V, our reference voltage 247 measuredvbat /= 1024; // convert to voltage 248 vbat_last_read = nseconds; 249 } 250 // Reading temperature and humidity 251 //if (nseconds % DHT_read_interval == 0) //Time to read temp 252 if (abs(nseconds - DHT_last_read >= DHT_read_interval)) //time to read DHT 253 { 254 rh = dht.readHumidity(); 255 temp = dht.readTemperature(); // Read temperature as Celsius (the default) 256 float f = dht.readTemperature(true); // Read temperature as Fahrenheit (isFahrenheit = true) 257 258 if (isnan(rh) || isnan(temp) || isnan(f)) { 259 Serial.println("Failed to read from DHT sensor!");// Check if any reads failed and exit early (to try again). 260 error = true; 261 errorString = ("DHT Error"); 262 //return; 263 } 264 DHT_last_read = nseconds; 265 } 266 // Reading eCO2 & TVOC 267 //if (nseconds % CCS811_read_interval == 0) //Time to read data 268 if (abs(nseconds - CCS811_last_read >= CCS811_read_interval)) //time to read CCS811 269 { 270 if (ccs.available()) { 271 //temp = ccs.calculateTemperature(); 272 if (!ccs.readData()) { 273 eco2 = ccs.geteCO2(); 274 tvoc = ccs.getTVOC(); 275 } 276 else { 277 error = true; 278 Serial.println("CCS811 sensor error!"); 279 errorString = "CCS811 error"; 280 //while(1); 281 } 282 CCS811_last_read = nseconds; 283 } 284 } 285 286 //Alarm levels 287 alarm_level_3 = temp > high_temp_level_3 || temp < low_temp_level_3 || rh > high_rh_level_3 || rh < low_rh_level_3 || eco2 > co2_level_3 || tvoc > tvoc_level_3; 288 alarm_level_2 = temp > high_temp_level_2 || temp < low_temp_level_2 || rh > high_rh_level_2 || rh < low_rh_level_2 || eco2 > co2_level_2 || tvoc > tvoc_level_2; 289 alarm_level_1 = temp > high_temp_level_1 || temp < low_temp_level_1 || rh > high_rh_level_1 || rh < low_rh_level_1 || eco2 > co2_level_1 || tvoc > tvoc_level_1; 290 alarm_level_0 = !alarm_level_3 & !alarm_level_2 & !alarm_level_2; 291 292 //Alarm LEDS & Alarm Strings 293 if (alarm_level_3) { 294 alarm = true; 295 digitalWrite(green_led, LOW); 296 digitalWrite(yellow_led, LOW); 297 digitalWrite(red_led, HIGH); 298 if (eco2 > co2_level_3) alarmString = "CO2 High ++"; 299 else if (tvoc > tvoc_level_3) alarmString = "TVOC High ++"; 300 else if (rh > high_rh_level_3) alarmString = "RH High ++"; 301 else if (rh < low_rh_level_3) alarmString = "RH Low --"; 302 else if (temp > high_temp_level_3) alarmString = "Temp High ++"; 303 else if (temp < low_temp_level_3) alarmString = "Temp Low --"; 304 } 305 else if (alarm_level_2) { 306 alarm = true; 307 digitalWrite(green_led, LOW); 308 digitalWrite(yellow_led, HIGH); 309 digitalWrite(red_led, LOW); 310 if (eco2 > co2_level_2) alarmString = "CO2 High +"; 311 else if (tvoc > tvoc_level_2) alarmString = "TVOC High +"; 312 else if (rh > high_rh_level_2) alarmString = "RH High +"; 313 else if (rh < low_rh_level_2) alarmString = "RH Low -"; 314 else if (temp > high_temp_level_2) alarmString = "Temp High +"; 315 else if (temp < low_temp_level_2) alarmString = "Temp Low -"; 316 } 317 else if (alarm_level_1) { 318 alarm = true; 319 digitalWrite(green_led, LOW); 320 digitalWrite(yellow_led, HIGH); 321 digitalWrite(red_led, LOW); 322 if (eco2 > co2_level_1)alarmString = ("CO2 High"); 323 else if (tvoc > tvoc_level_1) alarmString = "TVOC High"; 324 else if (rh > high_rh_level_1) alarmString = "RH High"; 325 else if (rh < low_rh_level_1) alarmString = "RH Low"; 326 else if (temp > high_temp_level_1) alarmString = "Temp High"; 327 else if (temp < low_temp_level_1) alarmString = "Temp Low"; 328 } 329 else if (alarm_level_0) { 330 alarm = false; 331 digitalWrite(green_led, HIGH); 332 digitalWrite(yellow_led, LOW); 333 digitalWrite(red_led, LOW); 334 alarmString = "OK"; 335 } 336 //Max and min determination 337 if (temp > max_temp) max_temp = temp; 338 if (temp < min_temp) min_temp = temp; 339 if (rh > max_rh) max_rh = rh; 340 if (rh < min_rh) min_rh = rh; 341 if (eco2 > max_eco2) max_eco2 = eco2; 342 if (eco2 < min_eco2) min_eco2 = eco2; 343 if (tvoc > max_tvoc) max_tvoc = tvoc; 344 if (tvoc < min_tvoc) min_tvoc = tvoc; 345 346 //Relatorio serial 347 Serial.print("Data:" ); 348 serial2digits(rtc.getDay()); 349 Serial.print("/"); 350 serial2digits(rtc.getMonth()); 351 Serial.print("/"); 352 Serial.print(2000 + rtc.getYear(), DEC); 353 Serial.print(" Hora:" ); 354 serial2digits(rtc.getHours()); 355 Serial.print(":"); 356 serial2digits(rtc.getMinutes()); 357 Serial.print(":"); 358 serial2digits(rtc.getSeconds()); 359 Serial.print(" "); 360 Serial.print("VBat:" ); Serial.print(measuredvbat); 361 Serial.print(" Temp:"); Serial.print(temp); 362 Serial.print(" Humid:"); Serial.print(rh); 363 Serial.print(" CO2:"); Serial.print(eco2); 364 Serial.print(" ppm, TVOC:"); Serial.print(tvoc); Serial.print(" ppb:"); 365 Serial.print(" Status:"); 366 if (error) 367 Serial.println(errorString); 368 else if (alarm) 369 Serial.println(alarmString); 370 else Serial.println("OK"); 371 372 //Data display 373 if (abs(nseconds - display_last_refresh >= display_refresh_interval)) //time to display information 374 { 375 display.clearDisplay();// Clean Display 376 display.setTextSize(1); 377 display.setTextColor(WHITE); 378 display.setCursor(0, 0); 379 display.println("Air Quality Analyzer"); 380 display.setCursor(0, 8); 381 display2digits(rtc.getDay()); 382 display.print("/"); 383 display2digits(rtc.getMonth()); 384 display.print("/"); 385 display.print(2000 + rtc.getYear(), DEC); 386 display.print(" "); 387 display2digits(rtc.getHours()); 388 display.print(":"); 389 display2digits(rtc.getMinutes()); 390 /*display.print(":"); 391 display.print(rtc.getSeconds(), DEC);*/ 392 display.setCursor(0, 16); 393 display.print("Vbat:"); 394 display.setCursor(50, 16); 395 display.print(measuredvbat); display.print(" V"); 396 display.setCursor(0, 24); 397 display.print("Temp:"); 398 display.setCursor(50, 24); 399 display.print(temp); display.print(" C"); 400 display.setCursor(0, 32); 401 display.print("RH:"); 402 display.setCursor(50, 32); 403 display.print(rh); display.print(" %"); 404 display.setCursor(0, 40); 405 display.print("eCO2:"); 406 display.setCursor(50, 40); 407 display.print(eco2); display.print(" ppm"); 408 display.setCursor(0, 48); 409 display.print("TVOC:"); 410 display.setCursor(50, 48); 411 display.print(tvoc); display.print(" ppb"); 412 display.setCursor(0, 56); 413 display.print("Status:"); 414 display.setCursor(50, 56); 415 if (error) { 416 display.print(errorString); 417 } else if (alarm) { 418 display.print(alarmString); 419 } 420 else 421 { 422 display.print("OK"); 423 } 424 display.display(); 425 display_last_refresh = nseconds; 426 } 427 //SD datalog 428 //if (nseconds % SD_write_interval == 0) //Time to log data 429 if (abs(nseconds - SD_last_write >= SD_write_interval)) //Time to log data 430 { 431 File logfile = SD.open(ficheiro, FILE_WRITE); 432 if (logfile) { 433 digitalWrite(SD_led, HIGH); //SD Write begins 434 logfile2digits(rtc.getDay(), logfile); 435 logfile.print("/"); 436 logfile2digits(rtc.getMonth(), logfile); 437 logfile.print("/"); 438 logfile.print(2000 + rtc.getYear(), DEC); 439 logfile.print(","); 440 logfile2digits(rtc.getHours(), logfile); 441 logfile.print(":"); 442 logfile2digits(rtc.getMinutes(), logfile); 443 logfile.print(":"); 444 logfile2digits(rtc.getSeconds(), logfile); 445 logfile.print(","); 446 logfile.print(measuredvbat, 2); 447 logfile.print(","); 448 logfile.print(min_temp, 2); 449 logfile.print(","); 450 logfile.print(max_temp, 2); 451 logfile.print(","); 452 logfile.print(min_rh, 2); 453 logfile.print(","); 454 logfile.print(max_rh, 2); 455 logfile.print(","); 456 logfile.print(min_eco2, 2); 457 logfile.print(","); 458 logfile.print(max_eco2, 2); 459 logfile.print(","); 460 logfile.print(min_tvoc, 2); 461 logfile.print(","); 462 logfile.print(max_tvoc, 2); 463 logfile.print(","); 464 if (error) 465 logfile.println(errorString); 466 else if (alarm) 467 logfile.println(alarmString); 468 else logfile.println("OK"); 469 logfile.close(); 470 SD_last_write = nseconds; 471 digitalWrite(SD_led, LOW); //SD Write ends 472 } 473 else 474 { 475 error = true; 476 errorString = ("logfile error"); 477 } 478 //Max and min RESET 479 max_eco2 = 0.0; 480 min_eco2 = 10000; 481 max_tvoc = 0.0; 482 min_tvoc = 10000; 483 max_temp = 0.0; 484 min_temp = 100; 485 max_rh = 0.0; 486 min_rh = 100; 487 } 488 delay(200); //don't use values higher than 1000 or nseconds won't be properly updated 489} 490 491void display2digits(int number)// create a leading zero for month/day/hour/minute when <10 492{ 493 if (number >= 0 && number < 10) { 494 display.write('0'); 495 } 496 display.print(number); 497} 498 499void logfile2digits(int number, File logfile) // create a leading zero for month/day/hour/minute when <10 500{ 501 if (number >= 0 && number < 10) { 502 logfile.write('0'); 503 } 504 logfile.print(number); 505} 506 507void serial2digits(int number)// create a leading zero for month/day/hour/minute when <10 508{ 509 if (number >= 0 && number < 10) { 510 Serial.write('0'); 511 } 512 Serial.print(number); 513} 514
Software version 2.0.3
arduino
This is the version 2.0.3 of the software. Has better time handling and improved display of data. Date and time now displayed on the main screen. Please inform if you have problems.
1/* Air Quality Sensor V2.0.3 2018/03/27 Luis Sousa */ 2 3#include 4 <SPI.h> //Standard lib 5#include <SD.h> //author=Arduino, SparkFun version=1.2.2 6#include 7 <DHT.h>//author=Adafruit version=1.3.0 8//#include <Wire.h> 9#include <RTCZero.h> 10 //author=Arduino version=1.5.2 11#include <RTClib.h> //author=Adafruit version=1.2.1 12 13#include <Adafruit_GFX.h> //author=Adafruit version=1.2.3 14#include <Adafruit_SSD1306.h> 15 //author=Adafruit version=1.1.2 16#include <Adafruit_CCS811.h>//author=Adafruit 17 version=1.0.0 18 19//Levels 20//temp levels 21#define low_temp_level_1 18 22#define 23 low_temp_level_2 12 24#define low_temp_level_3 8 25#define high_temp_level_1 29 26#define 27 high_temp_level_2 35 28#define high_temp_level_3 40 29//rh levels 30#define low_rh_level_1 31 40 32#define low_rh_level_2 30 33#define low_rh_level_3 20 34#define high_rh_level_1 35 70 36#define high_rh_level_2 80 37#define high_rh_level_3 90 38//co2 levels 39#define 40 co2_level_1 1000 41#define co2_level_2 1500 42#define co2_level_3 3000 43//TVOC 44 levels 45#define tvoc_level_1 100 46#define tvoc_level_2 500 47#define tvoc_level_3 48 1000 49 50//Status LEDS 51#define green_led 14 52#define yellow_led 15 53#define 54 red_led 16 55 56//OLED Display If using software SPI (the default case): 57#define 58 OLED_MOSI 6 59#define OLED_CLK 10 60#define OLED_DC 11 61#define OLED_CS 62 12 63#define OLED_RESET 5 64#define display_refresh_interval 1 //2 seconds 65 refresh 66Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS); 67 68//DHT 69 Sensor 70#define DHTPIN 19 // what pin DHT is connected to (19=A5 on this Board) 71#define 72 DHTTYPE DHT11 // DHT 11 73#define DHT_read_interval 3 //Read temp every X seconds 74DHT 75 dht(DHTPIN, DHTTYPE); //object dht creation 76 77//SD Card 78#define cardSelect 79 4 //SD chip select pin 80#define ficheiro "dados.csv" // nome do ficheiro de 81 dados 82//#define Serial_Header "Date,Hour,Vbat,Temp,%RH,eCO2,TVOC,STATUS" 83#define 84 SD_Header "Date,Hour,Vbat,min_Temp,Max_Temp,min_RH,Max_RH,min_eCO2,Max_eCO2,min_TVOC,Max_TVOC,STATUS" 85#define 86 SD_write_interval 300 //seconds between SD writes 87#define SD_led 8 //Green LED 88 89//RTC 90RTCZero 91 rtc; // Create an rtc object / 92 93//Vbat read 94#define vbatpin A7 //pin 95 to read Bat Voltage 96#define vbat_read_interval 30 //seconds between vbat readings 97float 98 measuredvbat; 99 100//Set up variables using the SD utility library functions: 101Sd2Card 102 card; 103SdVolume volume; 104SdFile root; 105 106//Set up CCS811 object ccs 107Adafruit_CCS811 108 ccs; 109#define CCS811_read_interval 3 //Read data every X seconds 110 111//variable 112 declaration 113unsigned long nseconds; //seconds since start of day. 114unsigned 115 long SD_last_write = 0; 116unsigned long vbat_last_read = 0; 117unsigned long DHT_last_read 118 = 0; 119unsigned long CCS811_last_read = 0; 120unsigned long display_last_refresh 121 = 0; 122boolean error = false; //clear errors 123boolean alarm = false; //clear 124 alarms 125boolean alarm_level_0 = false; 126boolean alarm_level_1 = false; 127boolean 128 alarm_level_2 = false; 129boolean alarm_level_3 = false; 130float eco2 = 0.0; 131float 132 max_eco2 = 0.0; 133float min_eco2 = 10000; 134float tvoc = 0.0; 135float max_tvoc 136 = 0.0; 137float min_tvoc = 10000; 138float temp = 0.0; 139float max_temp = 0.0; 140float 141 min_temp = 100; 142float rh = 0.0; 143float max_rh = 0.0; 144float min_rh = 100; 145String 146 errorString = ""; 147String alarmString = ""; 148//byte day = 27; 149//byte 150 month = 03; 151//byte year = 18; 152//byte seconds = 00; 153//byte minutes = 00; 154//byte 155 hours = 12; 156 157void setup() { 158 pinMode(SD_led, OUTPUT);// initialize digital 159 pin 8 as an output. 160 pinMode(green_led, OUTPUT);// initialize digital pin as 161 an output. 162 pinMode(yellow_led, OUTPUT);// initialize digital pin as an output. 163 164 pinMode(red_led, OUTPUT);// initialize digital pin as an output. 165 166 //Serial 167 Port Initialization 168 Serial.begin(57600); 169 delay (300);//wait for the serial 170 to be ready 171 /* 172 while (!Serial) { 173 ; // wait for serial port to 174 connect. Needed for native USB port only 175 } 176 */ 177 178 //RTC initialization 179 180 DateTime now; 181 now = (DateTime(F(__DATE__), F(__TIME__))); //Compilation time 182 183 now=now+20; //compensation for Compile + Reset time 184 rtc.begin(); // initialize 185 RTCZero 186 rtc.setTime(now.hour(), now.minute(), now.second()); 187 rtc.setDate(now.day(), 188 now.month(), now.year()-2000); 189 190 //DHT Initialisation 191 dht.begin(); 192 193 194 // CCS811 test 195 if (!ccs.begin()) { 196 Serial.println("Failed to start 197 CCS811 sensor! Please check your wiring."); 198 errorString = "CCS811 Error"; 199 200 //while (1); 201 } 202 //calibrate CCS811 temperature sensor 203 while (!ccs.available()); 204 205 temp = ccs.calculateTemperature(); 206 ccs.setTempOffset(temp - 25.0); 207 208 209 //Display initialization 210 display.begin(SSD1306_SWITCHCAPVCC);// by default, 211 we'll generate the high voltage from the 3.3v line internally! 212 display.clearDisplay(); 213 // Clear the buffer. 214 215 //SD Initialization and Test 216 Serial.print("\ 217Initializing 218 SD card..."); 219 // we'll use the initialization code from the utility libraries 220 221 // since we're just testing if the card is working! 222 if (!card.init(SPI_HALF_SPEED, 223 cardSelect)) { 224 Serial.println("initialization failed. Things to check: "); 225 226 Serial.println("* is a card inserted ? "); 227 Serial.println("* is your 228 wiring correct ? "); 229 Serial.println("* did you change the chipSelect pin 230 to match your shield or module ? "); 231 //while (1); 232 errorString = "SD 233 Card Error"; 234 } else { 235 Serial.println("Wiring is correct and a card 236 is present."); 237 } 238 // print the type of card 239 Serial.println(); 240 241 Serial.print("Card type : "); 242 switch (card.type()) { 243 case 244 SD_CARD_TYPE_SD1: 245 Serial.println("SD1"); 246 break; 247 case 248 SD_CARD_TYPE_SD2: 249 Serial.println("SD2"); 250 break; 251 case 252 SD_CARD_TYPE_SDHC: 253 Serial.println("SDHC"); 254 break; 255 default: 256 257 Serial.println("Unknown"); 258 } 259 // Now we will try to open the 'volume'/'partition' 260 - it should be FAT16 or FAT32 261 if (!volume.init(card)) { 262 Serial.println("Could 263 not find FAT16 / FAT32 partition.\ 264Make sure you've formatted the card"); 265 266 //while (1); 267 errorString = "SD Format Error"; 268 } 269 Serial.print("Clusters: 270 "); 271 Serial.println(volume.clusterCount()); 272 Serial.print("Blocks 273 x Cluster: "); 274 Serial.println(volume.blocksPerCluster()); 275 Serial.print("Total 276 Blocks: "); 277 Serial.println(volume.blocksPerCluster() * volume.clusterCount()); 278 279 Serial.println(); 280 // print the type and size of the first FAT-type volume 281 282 uint32_t volumesize; 283 Serial.print("Volume type is: FAT"); 284 Serial.println(volume.fatType(), 285 DEC); 286 volumesize = volume.blocksPerCluster(); // clusters are collections 287 of blocks 288 volumesize *= volume.clusterCount(); // we'll have a lot of 289 clusters 290 volumesize /= 2; // SD card blocks are always 291 512 bytes (2 blocks are 1KB) 292 Serial.print("Volume size (Kb): "); 293 Serial.println(volumesize); 294 295 Serial.print("Volume size (Mb): "); 296 volumesize /= 1024; 297 Serial.println(volumesize); 298 299 Serial.print("Volume size (Gb): "); 300 Serial.println((float)volumesize / 301 1024.0); 302 Serial.println("\ 303Files found on the card (name, date and size in 304 bytes): "); 305 root.openRoot(volume); 306 // list all files in the card with 307 date and size 308 root.ls(LS_R | LS_DATE | LS_SIZE); 309 if (!SD.begin(cardSelect)) 310 { 311 errorString = ("No SD Card"); 312 } 313 else { 314 File logfile 315 = SD.open(ficheiro, FILE_WRITE); 316 if (!logfile) 317 errorString = ("File 318 Error"); 319 else { 320 Serial.print("Gravando em: "); 321 Serial.println(ficheiro); 322 323 logfile.println(SD_Header); 324 logfile.close(); 325 } 326 } 327} 328 329void 330 loop() { 331 //How many seconds since 0H (start of day) 332 nseconds = 3600 * 333 rtc.getHours() + 60 * rtc.getMinutes() + rtc.getSeconds(); 334 335 //measure Vbat 336 337 if (abs(nseconds - vbat_last_read >= vbat_read_interval)) //time to read vbat 338 339 { 340 measuredvbat = analogRead(vbatpin); 341 measuredvbat *= 2; // we divided 342 by 2, so multiply back 343 measuredvbat *= 3.3; // Multiply by 3.3V, our reference 344 voltage 345 measuredvbat /= 1024; // convert to voltage 346 vbat_last_read 347 = nseconds; 348 } 349 // Reading temperature and humidity 350 //if (nseconds 351 % DHT_read_interval == 0) //Time to read temp 352 if (abs(nseconds - DHT_last_read 353 >= DHT_read_interval)) //time to read DHT 354 { 355 rh = dht.readHumidity(); 356 357 temp = dht.readTemperature(); // Read temperature as Celsius (the default) 358 359 float f = dht.readTemperature(true); // Read temperature as Fahrenheit (isFahrenheit 360 = true) 361 362 if (isnan(rh) || isnan(temp) || isnan(f)) { 363 Serial.println("Failed 364 to read from DHT sensor!");// Check if any reads failed and exit early (to try 365 again). 366 error = true; 367 errorString = ("DHT Error"); 368 //return; 369 370 } 371 DHT_last_read = nseconds; 372 } 373 // Reading eCO2 & TVOC 374 //if 375 (nseconds % CCS811_read_interval == 0) //Time to read data 376 if (abs(nseconds 377 - CCS811_last_read >= CCS811_read_interval)) //time to read CCS811 378 { 379 if 380 (ccs.available()) { 381 //temp = ccs.calculateTemperature(); 382 if (!ccs.readData()) 383 { 384 eco2 = ccs.geteCO2(); 385 tvoc = ccs.getTVOC(); 386 } 387 388 else { 389 error = true; 390 Serial.println("CCS811 sensor 391 error!"); 392 errorString = "CCS811 error"; 393 //while(1); 394 395 } 396 CCS811_last_read = nseconds; 397 } 398 } 399 400 //Alarm levels 401 402 alarm_level_3 = temp > high_temp_level_3 || temp < low_temp_level_3 || rh > high_rh_level_3 403 || rh < low_rh_level_3 || eco2 > co2_level_3 || tvoc > tvoc_level_3; 404 alarm_level_2 405 = temp > high_temp_level_2 || temp < low_temp_level_2 || rh > high_rh_level_2 || 406 rh < low_rh_level_2 || eco2 > co2_level_2 || tvoc > tvoc_level_2; 407 alarm_level_1 408 = temp > high_temp_level_1 || temp < low_temp_level_1 || rh > high_rh_level_1 || 409 rh < low_rh_level_1 || eco2 > co2_level_1 || tvoc > tvoc_level_1; 410 alarm_level_0 411 = !alarm_level_3 & !alarm_level_2 & !alarm_level_2; 412 413 //Alarm LEDS & Alarm 414 Strings 415 if (alarm_level_3) { 416 alarm = true; 417 digitalWrite(green_led, 418 LOW); 419 digitalWrite(yellow_led, LOW); 420 digitalWrite(red_led, HIGH); 421 422 if (eco2 > co2_level_3) alarmString = "CO2 High ++"; 423 else if (tvoc 424 > tvoc_level_3) alarmString = "TVOC High ++"; 425 else if (rh > high_rh_level_3) 426 alarmString = "RH High ++"; 427 else if (rh < low_rh_level_3) alarmString = 428 "RH Low --"; 429 else if (temp > high_temp_level_3) alarmString = "Temp High 430 ++"; 431 else if (temp < low_temp_level_3) alarmString = "Temp Low --"; 432 433 } 434 else if (alarm_level_2) { 435 alarm = true; 436 digitalWrite(green_led, 437 LOW); 438 digitalWrite(yellow_led, HIGH); 439 digitalWrite(red_led, LOW); 440 441 if (eco2 > co2_level_2) alarmString = "CO2 High +"; 442 else if (tvoc > 443 tvoc_level_2) alarmString = "TVOC High +"; 444 else if (rh > high_rh_level_2) 445 alarmString = "RH High +"; 446 else if (rh < low_rh_level_2) alarmString = 447 "RH Low -"; 448 else if (temp > high_temp_level_2) alarmString = "Temp High 449 +"; 450 else if (temp < low_temp_level_2) alarmString = "Temp Low -"; 451 452 } 453 else if (alarm_level_1) { 454 alarm = true; 455 digitalWrite(green_led, 456 LOW); 457 digitalWrite(yellow_led, HIGH); 458 digitalWrite(red_led, LOW); 459 460 if (eco2 > co2_level_1)alarmString = ("CO2 High"); 461 else if (tvoc > 462 tvoc_level_1) alarmString = "TVOC High"; 463 else if (rh > high_rh_level_1) 464 alarmString = "RH High"; 465 else if (rh < low_rh_level_1) alarmString = "RH 466 Low"; 467 else if (temp > high_temp_level_1) alarmString = "Temp High"; 468 469 else if (temp < low_temp_level_1) alarmString = "Temp Low"; 470 } 471 else 472 if (alarm_level_0) { 473 alarm = false; 474 digitalWrite(green_led, HIGH); 475 476 digitalWrite(yellow_led, LOW); 477 digitalWrite(red_led, LOW); 478 alarmString 479 = "OK"; 480 } 481 //Max and min determination 482 if (temp > max_temp) max_temp 483 = temp; 484 if (temp < min_temp) min_temp = temp; 485 if (rh > max_rh) max_rh 486 = rh; 487 if (rh < min_rh) min_rh = rh; 488 if (eco2 > max_eco2) max_eco2 = eco2; 489 490 if (eco2 < min_eco2) min_eco2 = eco2; 491 if (tvoc > max_tvoc) max_tvoc = tvoc; 492 493 if (tvoc < min_tvoc) min_tvoc = tvoc; 494 495 //Relatorio serial 496 Serial.print("Data:" 497 ); 498 serial2digits(rtc.getDay()); 499 Serial.print("/"); 500 serial2digits(rtc.getMonth()); 501 502 Serial.print("/"); 503 Serial.print(2000 + rtc.getYear(), DEC); 504 Serial.print(" 505 Hora:" ); 506 serial2digits(rtc.getHours()); 507 Serial.print(":"); 508 serial2digits(rtc.getMinutes()); 509 510 Serial.print(":"); 511 serial2digits(rtc.getSeconds()); 512 Serial.print(" 513 "); 514 Serial.print("VBat:" ); Serial.print(measuredvbat); 515 Serial.print(" 516 Temp:"); Serial.print(temp); 517 Serial.print(" Humid:"); Serial.print(rh); 518 519 Serial.print(" CO2:"); Serial.print(eco2); 520 Serial.print(" ppm, TVOC:"); 521 Serial.print(tvoc); Serial.print(" ppb:"); 522 Serial.print(" Status:"); 523 524 if (error) 525 Serial.println(errorString); 526 else if (alarm) 527 Serial.println(alarmString); 528 529 else Serial.println("OK"); 530 531 //Data display 532 if (abs(nseconds - display_last_refresh 533 >= display_refresh_interval)) //time to display information 534 { 535 display.clearDisplay();// 536 Clean Display 537 display.setTextSize(1); 538 display.setTextColor(WHITE); 539 540 display.setCursor(0, 0); 541 display.println("Air Quality Analyzer"); 542 543 display.setCursor(0, 8); 544 display2digits(rtc.getDay()); 545 display.print("/"); 546 547 display2digits(rtc.getMonth()); 548 display.print("/"); 549 display.print(2000 550 + rtc.getYear(), DEC); 551 display.print(" "); 552 display2digits(rtc.getHours()); 553 554 display.print(":"); 555 display2digits(rtc.getMinutes()); 556 /*display.print(":"); 557 558 display.print(rtc.getSeconds(), DEC);*/ 559 display.setCursor(0, 16); 560 561 display.print("Vbat:"); 562 display.setCursor(50, 16); 563 display.print(measuredvbat); 564 display.print(" V"); 565 display.setCursor(0, 24); 566 display.print("Temp:"); 567 568 display.setCursor(50, 24); 569 display.print(temp); display.print(" C"); 570 571 display.setCursor(0, 32); 572 display.print("RH:"); 573 display.setCursor(50, 574 32); 575 display.print(rh); display.print(" %"); 576 display.setCursor(0, 577 40); 578 display.print("eCO2:"); 579 display.setCursor(50, 40); 580 display.print(eco2); 581 display.print(" ppm"); 582 display.setCursor(0, 48); 583 display.print("TVOC:"); 584 585 display.setCursor(50, 48); 586 display.print(tvoc); display.print(" ppb"); 587 588 display.setCursor(0, 56); 589 display.print("Status:"); 590 display.setCursor(50, 591 56); 592 if (error) { 593 display.print(errorString); 594 } else if (alarm) 595 { 596 display.print(alarmString); 597 } 598 else 599 { 600 display.print("OK"); 601 602 } 603 display.display(); 604 display_last_refresh = nseconds; 605 } 606 607 //SD datalog 608 //if (nseconds % SD_write_interval == 0) //Time to log data 609 610 if (abs(nseconds - SD_last_write >= SD_write_interval)) //Time to log data 611 612 { 613 File logfile = SD.open(ficheiro, FILE_WRITE); 614 if (logfile) { 615 616 digitalWrite(SD_led, HIGH); //SD Write begins 617 logfile2digits(rtc.getDay(), 618 logfile); 619 logfile.print("/"); 620 logfile2digits(rtc.getMonth(), 621 logfile); 622 logfile.print("/"); 623 logfile.print(2000 + rtc.getYear(), 624 DEC); 625 logfile.print(","); 626 logfile2digits(rtc.getHours(), logfile); 627 628 logfile.print(":"); 629 logfile2digits(rtc.getMinutes(), logfile); 630 631 logfile.print(":"); 632 logfile2digits(rtc.getSeconds(), logfile); 633 634 logfile.print(","); 635 logfile.print(measuredvbat, 2); 636 logfile.print(","); 637 638 logfile.print(min_temp, 2); 639 logfile.print(","); 640 logfile.print(max_temp, 641 2); 642 logfile.print(","); 643 logfile.print(min_rh, 2); 644 logfile.print(","); 645 646 logfile.print(max_rh, 2); 647 logfile.print(","); 648 logfile.print(min_eco2, 649 2); 650 logfile.print(","); 651 logfile.print(max_eco2, 2); 652 logfile.print(","); 653 654 logfile.print(min_tvoc, 2); 655 logfile.print(","); 656 logfile.print(max_tvoc, 657 2); 658 logfile.print(","); 659 if (error) 660 logfile.println(errorString); 661 662 else if (alarm) 663 logfile.println(alarmString); 664 else logfile.println("OK"); 665 666 logfile.close(); 667 SD_last_write = nseconds; 668 digitalWrite(SD_led, 669 LOW); //SD Write ends 670 } 671 else 672 { 673 error = true; 674 errorString 675 = ("logfile error"); 676 } 677 //Max and min RESET 678 max_eco2 = 0.0; 679 680 min_eco2 = 10000; 681 max_tvoc = 0.0; 682 min_tvoc = 10000; 683 max_temp 684 = 0.0; 685 min_temp = 100; 686 max_rh = 0.0; 687 min_rh = 100; 688 } 689 690 delay(200); //don't use values higher than 1000 or nseconds won't be properly 691 updated 692} 693 694void display2digits(int number)// create a leading zero for 695 month/day/hour/minute when <10 696{ 697 if (number >= 0 && number < 10) { 698 display.write('0'); 699 700 } 701 display.print(number); 702} 703 704void logfile2digits(int number, File 705 logfile) // create a leading zero for month/day/hour/minute when <10 706{ 707 if 708 (number >= 0 && number < 10) { 709 logfile.write('0'); 710 } 711 logfile.print(number); 712} 713 714void 715 serial2digits(int number)// create a leading zero for month/day/hour/minute when 716 <10 717{ 718 if (number >= 0 && number < 10) { 719 Serial.write('0'); 720 } 721 722 Serial.print(number); 723} 724
Downloadable files
Air Quality Analyzer Connections V2.0
This is the second version of the Air Quality Analyzer Connections. Now we have 3 LEDs to display 3 levels of alarm, and a Sby/On switch. Also DHT11 sensor is connected to a new pin (A5) to avoid some instability caused by pin 13 internal LED circuit.
Air Quality Analyzer Connections V2.0
Comments
Only logged in users can leave comments