The Air Quality Monitoring project
[[File:Dust monitoring.jpg|640px|Dust monitoring.jpg]]
Goal
Many of our activities generate dust or particles that aren’t great for your health. The goal of the Air Quality Monitoring project is to gather data about the dust in our space and establish a baseline.
Proof of concept
A proof of concept was setup using hardware provided by members. It consisted of:
- a Dylos DC1100 meter
- a raspberry pi
- a carbon backend with a grafana dashboard to collect and display the data
The proof of concept was working but had one big issue : the Dylos DC1100 is an expensive ($250+) device which makes it difficult to place multiple sensors in the space.
The code to rebuild the raspberry pi image is available at [https://github.com/pgrandin/AMT-workshop-monitoring https://github.com/pgrandin/AMT-workshop-monitoring]
The long term solution
The long term solution uses :
- a Shinyei PPD42 dust sensor : [https://www.amazon.com/SuperiParts-SHINYEI-sensor-PPD42NJ-PPD42NS/dp/B076FGVMJF https://www.amazon.com/SuperiParts-SHINYEI-sensor-PPD42NJ-PPD42NS/dp/B076FGVMJF]
- an arduino device
The Shinyei PPD42 is less than 10x the price of the DC1100 which makes it affordable to build multiple monitoring devices.
The Shinyei PPD42 device reports the dust concentration
Things to know
According to our tests there are 2 things that will affect the sensor’s readings :
- orientation of the sensor
- exposure to light
The first one isn’t obvious but a note about it is buried in the manufacturer’s documentation. This sensors uses the heat produced by a resistor to move particules inside the sensor. If the sensor isn’t oriented correctly the air flow won’t work as intended.
The second one is related to the way the sensor measures the amount of particle in the air, using a photodiode. Direct exposure to light will alter the readings and the sensor will report a lot of “0”
Pinout
- JST Pin 1 (Black Wire) => Arduino GND
- JST Pin 3 (Red wire) => Arduino 5VDC
- JST Pin 4 (Yellow wire) => Arduino Digital Pin 12
The full spec sheet is available at [http://wiki.timelab.org/images/f/f9/PPD42NS.pdf http://wiki.timelab.org/images/f/f9/PPD42NS.pdf]
Calibration
Values for this sensors have been calibrated against a DC1100 for accuracy.
Example readings
Readings for the 1µm channel
[[File:Aqm.png|RTENOTITLE]]
Code
int pin = 12; unsigned long duration; unsigned long starttime; unsigned long sampletime_ms = 30 * 1000; unsigned long lowpulseoccupancy = 0; float ratio = 0; float concentration = 0; const int datapoints = 10; float reads[datapoints]; #include #define OLED_RESET 4 Adafruit_SSD1306 display (OLED_RESET); void dump_chart () { for (int i = 0; i < datapoints; i++) { display.setCursor ((i % 2) * 64, 8 + round (i / 2) * 8); if (reads[i] > 0) { display.println (reads[i]); } else { display.println ("---"); } } } void setup () { pinMode (pin, INPUT); starttime = millis (); //get the current time; display.begin (SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C (for the 128x32) display.clearDisplay (); display.setTextSize (1); display.setTextColor (WHITE); display.setCursor (0, 0); display.println ("reading sensor..."); dump_chart (); display.display (); for (int i = 0; i < datapoints; i++) { reads[i] = 0; } Serial.begin (115200); Serial.println ("Dust sendor ready"); } float push (float value) { int count = 0; float sum = 0; for (int i = datapoints - 1; i > 0; i--) { reads[i] = reads[i - 1]; if (reads[i] > 0) { sum = sum + reads[i]; count++; } } reads[0] = value; if (value > 0) { sum = sum + value; count++; } return sum / count; } void loop () { duration = pulseIn (pin, LOW); lowpulseoccupancy = lowpulseoccupancy + duration; float avg = 0; display.drawLine (64, 3, 64 + (millis () - starttime), 6, WHITE); if ((millis () - starttime) >= sampletime_ms) { ratio = lowpulseoccupancy / (sampletime_ms * 10.0); concentration = 1.1 * pow (ratio, 3) - 3.8 * pow (ratio, 2) + 520 * ratio + 0.62; if (concentration > 1) { Serial.println (concentration); avg = push (concentration); } else { Serial.println ("-1"); avg = push (0); } display.clearDisplay (); dump_chart (); display.setCursor (0, 0); display.print (avg); display.display (); lowpulseoccupancy = 0; starttime = millis (); } }