/* plantower_SHT30_timer.ino, D. Brooks, January 2022 PMS5003 with SHT30, timer on/off module to minimize power. See plantower_SHT30_log for code to add more data, including AQI calculations, to the output. */ char filename[]="PM_TIMER.CSV"; #define KNT_MAX 50 #define WARMUP 60000L #include SoftwareSerial pmsSerial(2, 3); #include Adafruit_SHT31 sht31 = Adafruit_SHT31(); #include #include #define SDpin 10 #include #include RTC_DS1307 rtc; // "old" data logger shield int knt=0,YR,MON,DY,HR,MIN,SEC; const int donePIN=5; float pm1=0,pm2_5=0,pm10=0,T,RH; File logfile; struct pms5003data { uint16_t framelen; uint16_t pm10_standard, pm25_standard, pm100_standard; uint16_t pm10_env, pm25_env, pm100_env; uint16_t particles_03um, particles_05um, particles_10um; uint16_t particles_25um, particles_50um, particles_100um; uint16_t unused; uint16_t checksum; }; struct pms5003data data; void setup() { pinMode(donePIN,OUTPUT); digitalWrite(donePIN,LOW); delay(WARMUP); Serial.begin(9600); // just for testing... Wire.begin(); rtc.begin(); pmsSerial.begin(9600); if (! sht31.begin(0x44)) { // Set to 0x45 for alternate i2c addr Serial.println("Couldn't find SHT31"); while (1) delay(1); } Serial.print("Initializing SD card..."); pinMode(SDpin,OUTPUT); if (!SD.begin(SDpin)) {Serial.println("Card failed."); delay(50);exit(0);} Serial.println(F("card initialized.")); logfile=SD.open(filename,FILE_WRITE); //--------------------------------------- StartRead: if (readPMSdata(&pmsSerial)) { // reading data was successful! pm1+=data.pm10_env; pm2_5+=data.pm25_env; pm10+=data.pm100_env; //Serial.println(pm2_5); knt++; if (knt==KNT_MAX) { DateTime now=rtc.now(); YR=now.year(); MON=now.month(); DY=now.day(); HR=now.hour(); MIN=now.minute(); SEC=now.second(); pm1/=knt; pm2_5/=knt; pm10/=knt; logfile.print(YR); logfile.print(','); logfile.print(MON); logfile.print(','); logfile.print(DY); logfile.print(','); logfile.print(HR); logfile.print(','); logfile.print(MIN); logfile.print(','); logfile.print(SEC); logfile.print(','); logfile.print(DY+HR/24.+MIN/1440.+SEC/86400.,5); logfile.print(','); T=sht31.readTemperature(); RH=sht31.readHumidity(); logfile.print(T,1); logfile.print(','); logfile.print(RH,1); logfile.print(','); logfile.print(pm1,1); logfile.print(','); logfile.print(pm2_5,1); logfile.print(','); logfile.print(pm10,1); logfile.println(); logfile.flush(); Serial.println("finished..."); goto ShutDown; } } goto StartRead; //--------------------------------------- ShutDown: delay(50); digitalWrite(donePIN,HIGH); } void loop() {} 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];} // 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("Checksum failure"); return false;} // success! return true; }