Components for Plantower PM monitoring system
Component | source |
Plantower PMS5003 particulate sensor |
www.adafruit.com, ID 3686, $40
(includes cable and breakout board for breadboard) |
Arduino UNO or compatible (various sources) |
www.adafruit.com, METRO 328, ID 2488, $18,
plus A/Micro-B USB cable, ID 898, $3
www.allelectronics.com, UNO R3, ARD-21, $14
(includes USB A/B cable)
www.allelectronics.com,
SparkFun RedBoard, ARD-22, $20,
plus A/Mini-B USB cable, CB-422, $2.25
|
DHT temperature/relative humidity sensor with
10 kΩ pull-up resistor |
www.allelectronics.com, ID 385, $10 |
data logging shield |
www.adafruit.com, ID 1141, $14,
plus CR1220 coin cell battery, ID 380, $1 |
mini (170 contacts) breadboard |
www.allelectronics.com, PB-170, $2.50
| assorted M/M jumper wires |
www.allelectronics.com, JMM6-10, $2 |
SD card or micro SD card with adapter |
www.allelectronics.com, MSD-8, $6.25,
SDR-6, $1.25
(Any SD or micro SD card with standard SD card adapter will do.)
|
|
|
/* plantower_DHT22_log_2, D. Brooks, March 2018
Averages 100 sets of PM data sent from Plantower.
Reads T/RH from DHT22 and saves data to SD
card with a date/time stamp.
*/
#define KNT_MAX 100 // average this many samples
#include <SoftwareSerial.h>
SoftwareSerial pmsSerial(2, 3);
#include <DHT.h>
#define DHTPIN 8
#define DHTTYPE DHT22
DHT dht(DHTPIN,DHTTYPE);
#include <Wire.h>
#include <SD.h>
#define SDpin 10
#include <SPI.h>
#include <RTClib.h>
RTC_DS1307 rtc; // old data logger shield
//RTC_PCF8523 rtc; // new data logger shield (different clock module)
int knt=0;
int yr,mon,dy,hr,minute,sec;
float pm1=0,pm25=0,pm10=0;
File logfile;
void setup() {
Wire.begin(); rtc.begin(); Serial.begin(9600);
pmsSerial.begin(9600);
Serial.print(F("Initializing SD card..."));
pinMode(SDpin,OUTPUT);
if (!SD.begin(SDpin)) {Serial.println(F("Card failed."));
delay(50);exit(0);}
Serial.println(F("card initialized."));
logfile=SD.open("PM25_DHT.CSV",FILE_WRITE);
}
struct pms5003data {
uint16_t framelen;
uint16_t pm10_standard, pm25_standard, pm100_standard;
// use "environmental" data, not "standard"
uint16_t pm10_env, pm25_env, pm100_env;
uint16_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um;
uint16_t unused;
uint16_t checksum;
};
struct pms5003data data;
void loop() {
if (readPMSdata(&pmsSerial)) { // reading data was successful!
pm1+=data.pm10_env; pm25+=data.pm25_env;
pm10+=data.pm100_env;
knt++;
if (knt==KNT_MAX) {
DateTime now=rtc.now();
yr=now.year(); mon=now.month(); dy=now.day();
hr=now.hour(); minute=now.minute(); sec=now.second();
pm1/=knt; pm25/=knt; pm10/=knt;
// store data in buffer
logfile.print(yr); logfile.print(',');
logfile.print(mon); logfile.print(',');
logfile.print(dy); logfile.print(',');
logfile.print(hr); logfile.print(',');
logfile.print(minute); logfile.print(',');
logfile.print(sec); logfile.print(',');
// day as decimal fraction
logfile.print(dy+hr/24.+minute/1440.+sec/86400.,5);
logfile.print(',');
logfile.print(pm1,1); logfile.print(',');
logfile.print(pm25,1); logfile.print(',');
logfile.print(pm10,1); logfile.print(',');
logfile.print(dht.readTemperature(),1);logfile.print(',');
logfile.println(dht.readHumidity(),1);
logfile.flush(); // write from buffer to SD card
pm1=0; pm25=0; pm10=0; // reset PM totals
knt=0; // reset counter
}
}
}
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("Checksum failure");
return false;
}
// success!
return true;
}