The Air Quality Index (IAQ) is fairly easy to determine using an Arduino (or ESP32, ESP8266) and a DSM501 sensor. The DSM501A is a detector capable of measuring the presence of fine particles in suspension in the atmosphere according to PM2.5 and PM10 standards.
The DSM501A is a very economical sensor (about € 4.80) which can be used as a base to build an air quality measurement station connected with an ESP8266. The DSM501A is able to carry out measurements to the PM2.5 and PM10 standards. That is, it is capable of detecting particles whose diameter is less than 2.5 μm for PM2.5 and less than 1 μm for PM1.0.
Principle of the DSM501 sensor
The DSM501 is a Led optical detector that is more economical to manufacture than Laser models. The operating principle is similar to the smoke detectors used in fire detectors. The led illuminates the particles in a dark room. A photo-detector collects light. The measured signal is then proportional to the size and the quantity of particles in suspension.
source : http://doc.lijun.li/misc-dust-detector-with-arduino-serial-comm.html
The DSM501 is capable of classifying and measuring airborne particles less than 1 micron (PM1.0) and 2.5 micrometres (PM2.5) in size.
Principle of communication with the Arduino / ESP8266 / ESP32
The DSM501 does not send its measurement directly. It uses the PWM system. It will therefore be necessary to measure the ratio between the time spent in the high state (+ 4.5V) and the time spent in the low state (0V). Then, thanks to this curve, it is possible to determine the quantity of particles in the air.
source : http://www.samyoungsnc.com/products/3-1%20Specification%20DSM501.pdf
Can I use the DSM501 on an ESP8266 or ESP32 ?
Some of you will certainly yell at the 4.5V outputs of the sensors plugged into the ESP8266. Theoretically, the inputs of the ESP8266 or ESP32 must not exceed 3.3V. This is perfectly correct, but the ESP8266 / ESP32 seems to tolerate a voltage overrun if the intensity remains low according to this study published by Digital Me. One risks especially to deteriorate its ESP by directly plug the + 5V on terminal 3, 3V or a GPIO output by mistake.
Determination of particle concentration
Fortunately, Makers have done the job for us! Here is the function that retrieves the PWM signal and determine the ratio. Knowing the ratio, it is possible to deduce the concentration of particles in suspension in the air in mg/m3.
long getPM(int DUST_SENSOR_DIGITAL_PIN) { starttime = millis(); while (1) { duration = pulseIn(DUST_SENSOR_DIGITAL_PIN, LOW); lowpulseoccupancy += duration; endtime = millis(); if ((endtime-starttime) >= sampletime_ms) { ratio = lowpulseoccupancy / (sampletime_ms*10.0); //ratio = (lowpulseoccupancy-endtime+starttime)/(sampletime_ms*10.0); // Integer percentage 0=>100 -> Not working long concentration = 1.1 * pow( ratio, 3) - 3.8 *pow(ratio, 2) + 520 * ratio + 0.62; // using spec sheet curve Serial.println(ratio); lowpulseoccupancy = 0; return(concentration); } } }
Sources : https://github.com/mysensors/MySensors/blob/master/examples/DustSensorDSM/DustSensorDSM.ino
How to calculate the Air Quality Index (AQI)
The French Air Quality Index (AQI) or Air Quality Index (AIQ) in English is an indicator for determining an air quality level. Several pollutants are taken into account. (NO2), oxides of nitrogen (NOx), sulfur dioxide (SO2), lead (Pb), fine particles less than 10μm (PM10), less than 2.5μm Carbon (CO), Benzene (C6H6) and ozone (O3).
The big problem is that there is no international standard. In France, for example, the ATMO. The air quality is determined by an index ranging from 1 (very good) to 10 (dangerous). The ATMO is derived from the European system. In Europe, the Common Air Quality Index (CAQI) ranges from 0 to 100 (5 levels). In the USA and China, the AQI index has 6 levels ranging from 0 to 500 (or more).
To find the thresholds that correspond to your country, go to this Wikipedia page.
How to calculate the French ATMO index
In France, two indices have been developed to make air quality more visible. Everything is explained in detail on this page Wikipedia:
- The ATMO index for agglomerations with populations exceeding 100,000. It is the maximum threshold of four pollutants. The index varies from 1 to 10.
- The IQA index for agglomerations of less than 100,000 inhabitants. It is a simplified index, which can be based on the measurement of a smaller number of pollutants (from 1 to 3). According to Article 4 of the French decree JORF No 274 of 25 November 2004, the simplified air quality index (IQA / AQI) is equal to the largest of the sub-indices of the polluting substances actually measured in the Geographical area. The index includes 6 levels (very good, good, average, mediocre, bad, dangerous).
To determine the AQI, we need only have at least one measure. If we have several sensors at our disposal, it will be the worst sub-index that will be used as an IQA index. The thresholds are imposed by Directive 2008/50/EC of 21 May 2008.
This summary table (taken from the Wikipedia article) makes it possible to determine the ATMO index. In the absence of thresholds for PM2.5, I used those of England, very close to European levels.
For example, if a PM10 = 15.5 is found, the simplified ATMO (AQI) will be 3. If there are several measures, the worst index (the highest score) becomes the ATMO.
ATMO index | O3 Hourly av. | SO2 Hourly av. | NO2 Hourly av. | PM2,5 Daily av. | PM10 Daily av. | Level |
---|---|---|---|---|---|---|
1 | 0 – 29 | 0 – 39 | 0 à 29 | 0 à 11 | 0 à 6 | Très bon |
2 | 30 – 54 | 40 – 79 | 30 à 54 | 12 à 23 | 7 à 13 | Très bon |
3 | 55 – 79 | 80 – 119 | 55 à 84 | 24 à 35 | 14 à 20 | Bon |
4 | 80 – 104 | 120 – 159 | 85 à 109 | 36 à 41 | 21 à 27 | Bon |
5 | 105 – 129 | 160 – 199 | 110 à 134 | 42 à 47 | 28 à 34 | Moyen |
6 | 130 – 149 | 200 à 249 | 135 à 164 | 48 à 53 | 35 à 41 | Médiocre |
7 | 150 – 179 | 250 à 299 | 165 à 199 | 54 à 58 | 42 à 49 | Médiocre |
8 | 180 – 209 | 300 à 399 | 200 à 274 | 59 à 64 | 50 à 64 | Mauvais |
9 | 210 – 239 | 400 à 499 | 275 à 399 | 65 à 69 | 65 à 79 | Mauvais |
10 | ≥ 240 | ≥ 500 | ≥ 400 | > 70 | ≥ 80 | Très mauvais |
How to determine the European CAQI index (also called Citeair)
The calculation is similar to the French ATMO. It is obtained using the following grid published on the website airqualitynow.eu. At a minimum, the measurement should last 1 hour (PM10 or PM2.5).
Source : https://www.airqualitynow.eu/about_indices_definition.php
How to calculate AQI in China and the USA
China and the US have the same calculation system. The level of air quality has 6 levels (good, moderate, mediocre, bad for health, very bad for health, dangerous). The threshold therefore ranges from 0 to 500 (or more) instead of going from 1 to 10 in Europe (ATMO). It is very often the method of calculation that one finds in the examples of codes Arduino. There is also an online calculator here.
Air Quality Index (AQI) Values | Levels of Health Concern | Colors |
0 to 50 | Good | Green |
51 to 100 | Moderate | Yellow |
101 to 150 | Unhealthy for Sensitive Groups | Orange |
151 to 200 | Unhealthy | Red |
201 to 300 | Very Unhealthy | Purple |
301 to 500 | Hazardous | Maroon |
To calculate the threshold, use this formula.
O3 (ppb) | O3 (ppb) | PM2.5 (µg/m3) | PM10 (µg/m3) | CO (ppm) | SO2 (ppb) | NO2 (ppb) | AQI | AQI |
Clow – Chigh (avg) | Clow – Chigh (avg) | Clow– Chigh (avg) | Clow – Chigh (avg) | Clow – Chigh (avg) | Clow – Chigh (avg) | Clow – Chigh (avg) | Ilow – Ihigh | Categorie |
0-54 (8-hr) | – | 0.0-12.0 (24-hr) | 0-54 (24-hr) | 0.0-4.4 (8-hr) | 0-35 (1-hr) | 0-53 (1-hr) | 0-50 | Good |
55-70 (8-hr) | – | 12.1-35.4 (24-hr) | 55-154 (24-hr) | 4.5-9.4 (8-hr) | 36-75 (1-hr) | 54-100 (1-hr) | 51-100 | Moderate |
71-85 (8-hr) | 125-164 (1-hr) | 35.5-55.4 (24-hr) | 155-254 (24-hr) | 9.5-12.4 (8-hr) | 76-185 (1-hr) | 101-360 (1-hr) | 101-150 | Unhealthy for Sensitive Groups |
86-105 (8-hr) | 165-204 (1-hr) | 55.5-150.4 (24-hr) | 255-354 (24-hr) | 12.5-15.4 (8-hr) | 186-304 (1-hr) | 361-649 (1-hr) | 151-200 | Unhealthy |
106-200 (8-hr) | 205-404 (1-hr) | 150.5-250.4 (24-hr) | 355-424 (24-hr) | 15.5-30.4 (8-hr) | 305-604 (24-hr) | 650-1249 (1-hr) | 201-300 | Very Unhealthy |
– | 405-504 (1-hr) | 250.5-350.4 (24-hr) | 425-504 (24-hr) | 30.5-40.4 (8-hr) | 605-804 (24-hr) | 1250-1649 (1-hr) | 301-400 | Hazardous |
– | 505-604 (1-hr) | 350.5-500.4 (24-hr) | 505-604 (24-hr) | 40.5-50.4 (8-hr) | 805-1004 (24-hr) | 1650-2049 (1-hr) | 401-500 |
Circuit
Connect the DSM501 to an Arduino or ESP8266 using the following marking
DSM501 Pin | Coolor | Signification |
2 | Yellow | Vout2 (PM1.0) |
3 | White | Vcc (+5V Arduino or ESP8266) |
4 | Red | Vout1 (MP2.5) |
5 | Black | GND |
Source : http://www.samyoungsnc.com/products/3-1%20Specification%20DSM501.pdf
Arduino Code for the DSM501 (compatible ESP32 and ESP8266)
It is therefore impossible to propose a method of universal calculation. You will need to adapt the Arduino code to your country. Here is a work base that already calculates the French ATMO, the CAQI European and the American and Chinese AQI. The source code will be published on GitHub to allow each one to propose the calculation method corresponding to its country sound.
It is therefore necessary to carry out a measure long enough to be able to estimate correctly the AQI. You can change the measurement time using the variable sampletime_ms (in milliseconds). The measurement is then extrapolated to 24 hours. The longer the measurement time, the better the estimate of the AQI index. You will need to be patient at startup.
#include <SimpleTimer.h> /* Connect the DSM501 sensor as follow * https://www.elektronik.ropla.eu/pdf/stock/smy/dsm501.pdf * 1 green vert - Not used * 2 yellow jaune - Vout2 - 1 microns (PM1.0) * 3 white blanc - Vcc * 4 red rouge - Vout1 - 2.5 microns (PM2.5) * 5 black noir - GND */ #define DUST_SENSOR_DIGITAL_PIN_PM10 D3 // DSM501 Pin 2 of DSM501 (jaune / Yellow) #define DUST_SENSOR_DIGITAL_PIN_PM25 D5 // DSM501 Pin 4 (rouge / red) #define COUNTRY 0 // 0. France, 1. Europe, 2. USA/China #define EXCELLENT "Excellent" #define GOOD "Bon" #define ACCEPTABLE "Moyen" #define MODERATE "Mediocre" #define HEAVY "Mauvais" #define SEVERE "Tres mauvais" #define HAZARDOUS "Dangereux" unsigned long duration; unsigned long starttime; unsigned long endtime; unsigned long lowpulseoccupancy = 0; float ratio = 0; unsigned long SLEEP_TIME = 2 * 1000; // Sleep time between reads (in milliseconds) unsigned long sampletime_ms = 5 * 60 * 1000; // Durée de mesure - sample time (ms) struct structAQI{ // variable enregistreur - recorder variables unsigned long durationPM10; unsigned long lowpulseoccupancyPM10 = 0; unsigned long durationPM25; unsigned long lowpulseoccupancyPM25 = 0; unsigned long starttime; unsigned long endtime; // Sensor AQI data float concentrationPM25 = 0; float concentrationPM10 = 0; int AqiPM10 = -1; int AqiPM25 = -1; // Indicateurs AQI - AQI display int AQI = 0; String AqiString = ""; int AqiColor = 0; }; struct structAQI AQI; SimpleTimer timer; void updateAQILevel(){ AQI.AQI = AQI.AqiPM10; } void updateAQI() { // Actualise les mesures - update measurements AQI.endtime = millis(); float ratio = AQI.lowpulseoccupancyPM10 / (sampletime_ms * 10.0); float concentration = 1.1 * pow( ratio, 3) - 3.8 *pow(ratio, 2) + 520 * ratio + 0.62; // using spec sheet curve if ( sampletime_ms < 3600000 ) { concentration = concentration * ( sampletime_ms / 3600000.0 ); } AQI.lowpulseoccupancyPM10 = 0; AQI.concentrationPM10 = concentration; ratio = AQI.lowpulseoccupancyPM25 / (sampletime_ms * 10.0); concentration = 1.1 * pow( ratio, 3) - 3.8 *pow(ratio, 2) + 520 * ratio + 0.62; if ( sampletime_ms < 3600000 ) { concentration = concentration * ( sampletime_ms / 3600000.0 ); } AQI.lowpulseoccupancyPM25 = 0; AQI.concentrationPM25 = concentration; Serial.print("Concentrations => PM2.5: "); Serial.print(AQI.concentrationPM25); Serial.print(" | PM10: "); Serial.println(AQI.concentrationPM10); AQI.starttime = millis(); // Actualise l'AQI de chaque capteur - update AQI for each sensor if ( COUNTRY == 0 ) { // France AQI.AqiPM25 = getATMO( 0, AQI.concentrationPM25 ); AQI.AqiPM10 = getATMO( 1, AQI.concentrationPM10 ); } else if ( COUNTRY == 1 ) { // Europe AQI.AqiPM25 = getACQI( 0, AQI.concentrationPM25 ); AQI.AqiPM10 = getACQI( 1, AQI.concentrationPM10 ); } else { // USA / China AQI.AqiPM25 = getAQI( 0, AQI.concentrationPM25 ); AQI.AqiPM10 = getAQI( 0, AQI.concentrationPM10 ); } // Actualise l'indice AQI - update AQI index updateAQILevel(); updateAQIDisplay(); Serial.print("AQIs => PM25: "); Serial.print(AQI.AqiPM25); Serial.print(" | PM10: "); Serial.println(AQI.AqiPM10); Serial.print(" | AQI: "); Serial.println(AQI.AQI); Serial.print(" | Message: "); Serial.println(AQI.AqiString); } void setup() { // put your setup code here, to run once: Serial.begin(115200); pinMode(DUST_SENSOR_DIGITAL_PIN_PM10,INPUT); pinMode(DUST_SENSOR_DIGITAL_PIN_PM25,INPUT); // wait 60s for DSM501 to warm up for (int i = 1; i <= 60; i++) { delay(1000); // 1s Serial.print(i); Serial.println(" s (wait 60s for DSM501 to warm up)"); } Serial.println("Ready!"); AQI.starttime = millis(); timer.setInterval(sampletime_ms, updateAQI); } void loop() { AQI.lowpulseoccupancyPM10 += pulseIn(DUST_SENSOR_DIGITAL_PIN_PM10, LOW); AQI.lowpulseoccupancyPM25 += pulseIn(DUST_SENSOR_DIGITAL_PIN_PM25, LOW); timer.run(); } /* * Calcul l'indice de qualité de l'air français ATMO * Calculate French ATMO AQI indicator */ int getATMO( int sensor, float density ){ if ( sensor == 0 ) { //PM2,5 if ( density <= 11 ) { return 1; } else if ( density > 11 && density <= 24 ) { return 2; } else if ( density > 24 && density <= 36 ) { return 3; } else if ( density > 36 && density <= 41 ) { return 4; } else if ( density > 41 && density <= 47 ) { return 5; } else if ( density > 47 && density <= 53 ) { return 6; } else if ( density > 53 && density <= 58 ) { return 7; } else if ( density > 58 && density <= 64 ) { return 8; } else if ( density > 64 && density <= 69 ) { return 9; } else { return 10; } } else { if ( density <= 6 ) { return 1; } else if ( density > 6 && density <= 13 ) { return 2; } else if ( density > 13 && density <= 20 ) { return 3; } else if ( density > 20 && density <= 27 ) { return 4; } else if ( density > 27 && density <= 34 ) { return 5; } else if ( density > 34 && density <= 41 ) { return 6; } else if ( density > 41 && density <= 49 ) { return 7; } else if ( density > 49 && density <= 64 ) { return 8; } else if ( density > 64 && density <= 79 ) { return 9; } else { return 10; } } } void updateAQIDisplay(){ /* * 1 EXCELLENT * 2 GOOD * 3 ACCEPTABLE * 4 MODERATE * 5 HEAVY * 6 SEVERE * 7 HAZARDOUS */ if ( COUNTRY == 0 ) { // Système ATMO français - French ATMO AQI system switch ( AQI.AQI) { case 10: AQI.AqiString = SEVERE; break; case 9: AQI.AqiString = HEAVY; break; case 8: AQI.AqiString = HEAVY; break; case 7: AQI.AqiString = MODERATE; break; case 6: AQI.AqiString = MODERATE; break; case 5: AQI.AqiString = ACCEPTABLE; break; case 4: AQI.AqiString = GOOD; break; case 3: AQI.AqiString = GOOD; break; case 2: AQI.AqiString = EXCELLENT; break; case 1: AQI.AqiString = EXCELLENT; break; } } else if ( COUNTRY == 1 ) { // European CAQI switch ( AQI.AQI) { case 25: AQI.AqiString = GOOD; break; case 50: AQI.AqiString = ACCEPTABLE; break; case 75: AQI.AqiString = MODERATE; break; case 100: AQI.AqiString = HEAVY; break; default: AQI.AqiString = SEVERE; } } else if ( COUNTRY == 2 ) { // USA / CN if ( AQI.AQI <= 50 ) { AQI.AqiString = GOOD; } else if ( AQI.AQI > 50 && AQI.AQI <= 100 ) { AQI.AqiString = ACCEPTABLE; } else if ( AQI.AQI > 100 && AQI.AQI <= 150 ) { AQI.AqiString = MODERATE; } else if ( AQI.AQI > 150 && AQI.AQI <= 200 ) { AQI.AqiString = HEAVY; } else if ( AQI.AQI > 200 && AQI.AQI <= 300 ) { AQI.AqiString = SEVERE; } else { AQI.AqiString = HAZARDOUS; } } } /* * CAQI Européen - European CAQI level * source : http://www.airqualitynow.eu/about_indices_definition.php */ int getACQI( int sensor, float density ){ if ( sensor == 0 ) { //PM2,5 if ( density == 0 ) { return 0; } else if ( density <= 15 ) { return 25 ; } else if ( density > 15 && density <= 30 ) { return 50; } else if ( density > 30 && density <= 55 ) { return 75; } else if ( density > 55 && density <= 110 ) { return 100; } else { return 150; } } else { //PM10 if ( density == 0 ) { return 0; } else if ( density <= 25 ) { return 25 ; } else if ( density > 25 && density <= 50 ) { return 50; } else if ( density > 50 && density <= 90 ) { return 75; } else if ( density > 90 && density <= 180 ) { return 100; } else { return 150; } } } /* * AQI formula: https://en.wikipedia.org/wiki/Air_Quality_Index#United_States * Arduino code https://gist.github.com/nfjinjing/8d63012c18feea3ed04e * On line AQI calculator https://www.airnow.gov/index.cfm?action=resources.conc_aqi_calc */ float calcAQI(float I_high, float I_low, float C_high, float C_low, float C) { return (I_high - I_low) * (C - C_low) / (C_high - C_low) + I_low; } int getAQI(int sensor, float density) { int d10 = (int)(density * 10); if ( sensor == 0 ) { if (d10 <= 0) { return 0; } else if(d10 <= 120) { return calcAQI(50, 0, 120, 0, d10); } else if (d10 <= 354) { return calcAQI(100, 51, 354, 121, d10); } else if (d10 <= 554) { return calcAQI(150, 101, 554, 355, d10); } else if (d10 <= 1504) { return calcAQI(200, 151, 1504, 555, d10); } else if (d10 <= 2504) { return calcAQI(300, 201, 2504, 1505, d10); } else if (d10 <= 3504) { return calcAQI(400, 301, 3504, 2505, d10); } else if (d10 <= 5004) { return calcAQI(500, 401, 5004, 3505, d10); } else if (d10 <= 10000) { return calcAQI(1000, 501, 10000, 5005, d10); } else { return 1001; } } else { if (d10 <= 0) { return 0; } else if(d10 <= 540) { return calcAQI(50, 0, 540, 0, d10); } else if (d10 <= 1540) { return calcAQI(100, 51, 1540, 541, d10); } else if (d10 <= 2540) { return calcAQI(150, 101, 2540, 1541, d10); } else if (d10 <= 3550) { return calcAQI(200, 151, 3550, 2541, d10); } else if (d10 <= 4250) { return calcAQI(300, 201, 4250, 3551, d10); } else if (d10 <= 5050) { return calcAQI(400, 301, 5050, 4251, d10); } else if (d10 <= 6050) { return calcAQI(500, 401, 6050, 5051, d10); } else { return 1001; } } }
You can now track the level of fine particulate pollution at your home. We will see in the next tutorials how to add other sensors to constitute a true monitoring station of air quality 100% DIY!
- Get Started with HC-SR04, measure distance by ultrasound. Arduino code example
- ESP32, GPIO pins and associated functions. I/O, PWM, RTC, I2C, SPI, ADC, DAC
- ESP32-CAM pinout and equipments. ESP-EYE, AI Thinker, TTGO T-Camera, M5Stack Timer Camera …
- ESP32-CAM. Which model to choose? ESP-EYE, AI Thinker, TTGO T-Camera, M5Stack Timer Camera …
- M5Stack Atomic GPS. ESP32 TinyGPS++ tracker, GPX export on SD card, visualization on Google Maps or VSCode
Good job mate 🙂 Thanks!
Thank you very much Tomasz. I hope the code has been useful for your project. Do not hesitate to share your experience with us. See you soon
firstly, i want say thank you very much cause i’ve tired to search reference this sensor. 😀
result data from this system already compared with the standard tool for measurement pm10/2.5?
i hope you can reply this soon 😀
Hello Ibnu and thank you very much. I’m sorry but I do not have any lab equipment that allows me to check the validity of the measurements. See you soon.
me too, I don’t have any lab equipment. But, base on the calculation in the program, how much error do you think if compare with lab equipment? Is it close to real value?
I can only refer to the technical documentation to give you an answer. Yes it is a good sensor. I think we should rather consider the value as an indicator of air quality (very bad, bad, average, good, very good). I have a doubt about the PM1 value that varies a lot too much. For the measurement to be correct, it is necessary to mix the air with a small fan for example. Do not measure during air renewal. See you soon.
long time no see 😀
how do you test this tool to detect pm10?
I’m sorry but I did not understand your problem. You can tell me a little more. thank you very much
/sorry about my bad english , have no WA ?
i mean, how to use this sensor? if lm 35 temperature sensor, we use to detect the temperatur in the somewhere
Why am i getting strange output? I just copy the code into Arduino.
Why am i getting strange output? I just copy the code into Arduino and change the pin to 9 and 10.
1) How can i shorten the measurement total time? I want to get a quick result of AQI.
2) Will the AQI result print on the serial monitor of Arduino IDE?
Question — For US/China (Lines 86 and 87) you call getAQI both times with a sensor value of 0. I think on 87 you meant for the value to be 1. Correct?
First of all, thanks for great article and sample of code. If I understand correctly, DSM501A can detect dust particles of PM 1.0 (1 μm) and PM 2.5 (2.5 μm), it can’t detect PM 10 (10 μm). But you use sensor results for PM 1.0 in air quality calculations for PM 10. Is it correct or wrong?
I also asked myself the same question. PM10 is calculated, but the sensor data provides values for PM1.0. How can this be explained?
I also asked myself the same question. PM10 is calculated, but the sensor data provides values for PM1.0. How can this be explained?