/* For a Nano Data Logger logging data from a Plantower 7003 particle sensor and a CCS811 gas sensor. @file NanoVOC7330v1.0.ino Plantower 7003 code based on https://learn.adafruit.com/pm25-air-quality-sensor/arduino-code CCS811 code based on https://github.com/adafruit/Adafruit_CCS811 Does not use a library for the Plantower 7003 C. Fastie 11/2018 */ #include // https://github.com/greiman/SdFat/ #include #include #include // library from https://github.com/adafruit/RTClib #include // https://github.com/adafruit/Adafruit_Sensor #include "Adafruit_CCS811.h" // https://learn.adafruit.com/adafruit-ccs811-air-quality-sensor/arduino-wiring-test #include // https://github.com/PaulStoffregen/SoftwareSerial SoftwareSerial pmsSerial(2, 3); Adafruit_CCS811 ccs; RTC_DS1307 RTC; // The real time clock object is "RTC" #define DS1307_I2C_ADDRESS 0x68 SdFat SD; // The SdFat object is "SD" #define MOSIpin 11 // For SD card #define MISOpin 12 // For SD card const int chipSelect = 10; // CS pin for the SD card char tmeStrng[ ] = "0000/00/00,00:00:00"; // a template for a data/time string float temp; float CO2; float TVOC; float temps; float CO2s; float TVOCs; float tempa; float CO2a; float TVOCa; long utc; int cycle=0; float n = 60.0; // ****Enter**** number of sample replicates for each mean uint16_t p10 = 0; uint16_t p25 = 0; uint16_t p100 = 0; uint16_t p10v = 0; uint16_t p25v = 0; uint16_t p100v = 0; uint16_t pt03 = 0; uint16_t pt05 = 0; uint16_t pt10 = 0; uint16_t pt25 = 0; uint16_t pt50 = 0; uint16_t pt100 = 0; float p10a = 0.0; float p25a = 0.0; float p100a = 0.0; float p10va = 0.0; float p25va = 0.0; float p100va = 0.0; float pt03a = 0.0; float pt05a = 0.0; float pt10a = 0.0; float pt25a = 0.0; float pt50a = 0.0; float pt100a = 0.0; int led = 3; // Assign LEDPin to a pin number. An LED on this pin will blink after data is saved to SD void setup() { pmsSerial.begin(9600); // PMS7003 sensor baud rate is 9600 Serial.begin(9600); // Open serial communications and wait for port to open: Wire.begin(); // initialize the I2C interface RTC.begin(); // initialize the RTC // Uncomment the line below and load the sketch to the Nano to set the RTC time. Then the line must // be commented out and the sketch loaded again or the time will be wrong. // RTC.adjust(DateTime((__DATE__), (__TIME__))); //sets the RTC to the time the sketch was compiled. pinMode(led, OUTPUT); // for the LED on pin "led" while (!Serial) { ; // wait for serial port to connect. Needed for native USB port only } // debugging display on the serial monitor /* if(!ccs.begin()){ // initialize and display the status of the gas sensor Serial.println("NO cjmcu"); while(1); } // initialize and display the status of the SD card: if (!SD.begin(chipSelect)) { Serial.println("SD fail"); while(1); } */ //print a header to the data file with column headings File dataFile = SD.open("datalog.txt", FILE_WRITE); if (dataFile) { // if the file is available, write to it: dataFile.println("Date,Time,UTCtime,Temp_C,CO2,TVOC,PM1,PM.2,PM10,PM1env,PM.2env,PM10env,03um,05um,10um,25um,50um,100um"); dataFile.close(); } while(!ccs.available()); //calibrate temperature sensor on gas sensor float temp = ccs.calculateTemperature(); ccs.setTempOffset(temp - 25.0); } // end of setup struct pms7003data { // define an object for the PMS7003 data uint16_t framelen; uint16_t pm10, pm25, pm100; uint16_t pm10v, pm25v, pm100v; uint16_t pt03, pt05, pt10, pt25, pt50, pt100; uint16_t unused; uint16_t checksum; }; struct pms7003data data; void loop() { if (readPMSdata(&pmsSerial)) { // if the Plantower 7003 is ready if(ccs.available()){ // if the gas sensor is ready temp = ccs.calculateTemperature(); if(!ccs.readData()){ // if we have gas sensor data cycle = cycle + 1; // start accumulating data to compute means p10 = p10 + data.pm10; p25 = p25 + data.pm25; p100 = p100 + data.pm100; p10v = p10v + data.pm10v; p25v = p25v + data.pm25v; p100v = p100v + data.pm100v; pt03 = pt03 + data.pt03; pt05 = pt05 + data.pt05; pt10 = pt10 + data.pt10; pt25 = pt25 + data.pt25; pt50 = pt50 + data.pt50; pt100 = pt100 + data.pt100; CO2s = CO2s + (ccs.geteCO2()); TVOCs = TVOCs + (ccs.getTVOC()); temps = temps + temp; // debugging output to the serial monitor // Serial.print("PM1a "); Serial.println(p10va); Serial.print(" PM2.5 "); Serial.println(data.pm25v); Serial.println(tmeStrng); } // end if } // end if } // end if if(cycle == n){ // if the desired number (n) of sensor readings have been accumulated cycle = 0; // zero the cycle variable and compute the means p10a = p10 / n; p25a = p25 / n; p100a = p100 / n; p10va = p10v / n; p25va = p25v / n; p100va = p100v / n; pt03a = pt03 / n; pt05a = pt05 / n; pt10a = pt10 / n; pt25a = pt25 / n; pt50a = pt50 / n; pt100a = pt100 / n; TVOCa = TVOCs/n; CO2a = CO2s/n; tempa = temps/n; p10 = 0; // zero the variables for the next mean p25 = 0; p100 = 0; p10v = 0; p25v = 0; p100v = 0; pt03 = 0; pt05 = 0; pt10 = 0; pt25 = 0; pt50 = 0; pt100 = 0; CO2s = 0; TVOCs = 0; temps = 0; DateTime now = RTC.now(); // read the time from the RTC utc = (now.unixtime()); sprintf(tmeStrng, "%04d/%02d/%02d,%02d:%02d:%02d", now.year(), now.month(), now.day(), now.hour(), now.minute(), now.second()); // [added seconds] // write the data to the SD card: File dataFile = SD.open("datalog.txt", FILE_WRITE); // if the file is available, write to it: dataFile.print(tmeStrng);dataFile.print(","); dataFile.print(utc);dataFile.print(","); dataFile.print(tempa);dataFile.print(","); dataFile.print(CO2a);dataFile.print(","); dataFile.print(TVOCa);dataFile.print(","); dataFile.print(p10a);dataFile.print(","); dataFile.print(p25a);dataFile.print(","); dataFile.print(p100a);dataFile.print(","); dataFile.print(p10va);dataFile.print(","); dataFile.print(p25va);dataFile.print(","); dataFile.print(p100va);dataFile.print(","); dataFile.print(pt03a);dataFile.print(","); dataFile.print(pt05a);dataFile.print(","); dataFile.print(pt10a);dataFile.print(","); dataFile.print(pt25a);dataFile.print(","); dataFile.print(pt50a);dataFile.print(","); dataFile.println(pt100a); dataFile.flush(); // make sure SD card writing is done dataFile.close(); // blink an LED so you don't unplug the device while it is writing to SD card digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level) delay(1000); // wait for a second digitalWrite(led, LOW); // turn the LED off by making the voltage LOW } // end if } // end of the MAIN LOOP // This stuff is usually in a library, but this sketch does not use one boolean readPMSdata(Stream *s) { if (! s->available()) { return false; } // Read a byte at a time until we get to the special '0x42' start-byte if (s->peek() != 0x42) { s->read(); return false; } // Now read all 32 bytes if (s->available() < 32) { return false; } uint8_t buffer[32]; uint16_t sum = 0; s->readBytes(buffer, 32); // get checksum ready for (uint8_t i=0; i<30; i++) { sum += buffer[i]; } /* debugging for (uint8_t i=2; i<32; i++) { Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", "); } Serial.println(); */ // The data comes in endian'd, this solves it so it works on all platforms uint16_t buffer_u16[15]; for (uint8_t i=0; i<15; i++) { buffer_u16[i] = buffer[2 + i*2 + 1]; buffer_u16[i] += (buffer[2 + i*2] << 8); } // put it into a nice struct :) memcpy((void *)&data, (void *)buffer_u16, 30); if (sum != data.checksum) { Serial.println("ChkFail"); return false; } // success! return true; }