# Air Quality Sensor

• @alexsh1 which is a good idea, also a temp/him is quite usefull though I didn't added the impact in the formulas so far.

• @epierre I have combined the two sketches - works just fine, but need to find a spare temp sensor.The hardware setup looks messy so adding an extra i2c sensor will complicate it even more. I'll come back with the combined three-in-one sketch shortly.

BTW, this

``````gw.send(msgPM25.set("ppm"));
``````

still throws the following error. What version MySensors are you using?

`````` MySensors: Unknown/Invalid sensor type (43)
``````

• @epierre I started looking at the formula you have used in ppmv calculation. Why do you need ppmv? All values in EPA or Europe are in μg/m3.

The ppmv equation is:

``````ppmv = mg/m^3 * (0.08205*T)/M
``````

T = atmospheric temperature in kelvins = 273.15 + °C
M = molecular weight of the air pollutant = 28.97
(http://www.engineeringtoolbox.com/molecular-mass-air-d_679.html - this is a good link)
0.08205 = Universal Gas Law constant in atm·l/(mol·K)

Your code for this equation is:

``````ppmv=(float)(((concentrationPM25*0.0283168)/100) * ((0.08205*temp)/0.01))/1000;
``````

Now, I think it should be as follows:

1. temp = °C + 273.15
``````int temp=273.15 + 22;

``````

22C - is a typical temp inside though the intention is to use a sensor

1. The amended ppmv equation is going to be:
``````ppmv=(((concentrationPM25*0.0283168)/100) * ((0.08205*temp)/28.97))/1000;
``````

I have not changed 0.0283168 /100 - not sure that this is. And the whole thing is divided by 1000? why?

IMPORTANT UPDATE:

I have just received the following result:

``````
PM10: 7373

send: 11-11-0-0 s=0,c=1,t=37,pt=7,l=5,sg=0,st=ok:1.744

``````

If I use the following web-site http://www.herramientasingenieria.com/onlinecalc/ppm-mg_m3.php and the result is
mg/m3 = 7373 * 0.283168/100 = 2.08 mg/m3
The molecular weight is 28.97 for dry air

``````2.08 mg/m3 is equivalent to 1.74ppm for a gas with molecular weight=28.97 Pressure=1013.25, Temperature=22C
``````

Success!
Now I have found a spare BMP280 (temp and pressure sensor) which I can use here.
Additionally, I need to use 24-h and 1 year averages - https://www3.epa.gov/airquality/particlepollution/2012/decfsstandards.pdf

Stay tuned!

• @alexsh1 said:

@epierre I started looking at the formula you have used in ppmv calculation. Why do you need ppmv? All values in EPA or Europe are in μg/m3.

because domoticz only knows ppm... and many AIQ like use only that except for particles.

1. temp = °C + 273.15
int temp=273.15 + 22;
22C - is a typical temp inside though the intention is to use a sensor

not for me

1. The amended ppmv equation is going to be:
ppmv=(((concentrationPM250.0283168)/100) * ((0.08205temp)/28.97))/1000;
``````I have not changed 0.0283168 /100 - not sure that this is. And the whole thing is divided by 1000? why?
``````

was a volume conversion

The molecular weight is 28.97 for dry air

``````2.08 mg/m3 is equivalent to 1.74ppm for a gas with molecular weight=28.97 Pressure=1013.25, Temperature=22C
``````

Success!

success for the mysensors value ?

``````MySensors: Unknown/Invalid sensor type (43)
``````

Domoticz doesn't recognize this command... I use 1.5.x but I have my own gateway to domoticz

• @epierre Yes, all works fine now. I modified the combined sketch and here is the final result.

• Which one to use for detection of fire smoke? Currently have got several MQ-7 but they don't seem so good at it

• @moskovskiy82 said:

Which one to use for detection of fire smoke? Currently have got several MQ-7 but they don't seem so good at it

as discussed just above, a particle sensor could be good for smoke is a particle concentration, coupled with heat this would be a good indicator

``````if (concentration > 315000) {
Serial.println("Smokes from cigarettes detected! Or It might be a huge fire! Beware!");
``````

• What about mq2 or mq135? Any experience? As a gas sensor will be more suitable detecting early fire

• @moskovskiy82 said:

What about mq2 or mq135? Any experience? As a gas sensor will be more suitable detecting early fire

please see what it does, this is not a gas sensor this only detect particle size whatever the gas...

• @epierre
It still detects concentration. Both state CO detection. So in case of fire won't they detect the increase in concentration much faster that the particle sensor like Sharp’s GP2Y1010AU0F or alternative?

• @moskovskiy82 For a fire smoke, you can use pretty much any gas or particle sensor - there are a quite few gases formed during the burning process. MQ2 is highly sensitivity and has a fast response time. I can recommend it for a fire detection usage. However, I have been disappointed in MQ* sensors in general - there are not accurate, require 24h heat-up time, consume a lot of power etc. The only advantage is the price.

To detect fire to can use a flame sensor - http://www.instructables.com/id/Flame-detection-using-Arduino-and-flame-sensor/

• Can someone tell me how to read this line?

``````float mq135_ro = 10000.0;    // this has to be tuned 10K Ohm
``````

Do I have to messure the sensor and adjust the variable or do I have to tune the resistance? If I have to do the first thing, when do I have to messure it? In warm state and clean air with a multimeter?

• @epierre Did you check out the airbeam, which is based on a more expensive sensor Shinyei PPD60PV?
http://www.takingspace.org/airbeam-technical-specifications-operation-performance/

They made a step-by-step manual about building the same on Shinyei PPD42NS. I took their code and stripped a few parts and this is what I am left with:

``````#include  <SoftwareSerial.h>
#include  <FlexiTimer2.h>

int pin = 3;

volatile  double  rawParticalCount;
volatile  double  totalParticles  = 0;
volatile  double  particleCountToDisplay  = 0;
volatile  double  ratio = 0;
volatile  uint16_t  timeCounter = 0;

#define         numberOfPeaksRecording                            5
volatile        uint32_t        previousPeaks[numberOfPeaksRecording];
volatile        uint32_t        sumOfPreviousPeaks          = 0;
volatile        uint32_t        instantGoal                         = 0;
volatile        int32_t         delta                                     = 0;
volatile        uint32_t        slowMovingAverage             = 0;

void  setup() {
Serial.begin(115200);
pinMode(pin,INPUT);

FlexiTimer2::start();

}
void  loop()  {

Serial.print(rawParticalCount,  DEC);
Serial.print("  Raw Particle  Count (0-10000) ");
Serial.print(ratio, DEC);
Serial.print("  Ratio (0-100%)  ");
Serial.print(particleCountToDisplay,  DEC);
Serial.print("  Particle  Count");
Serial.println("");

}

}

rawParticalCount++;
}
timeCounter++;
if  (timeCounter  ==  10000)
{
timeCounter=0;

//Changes are made  here  based on  Chris Nafis's code: http://www.howmuchsnow.com/arduino/airquality/grovedust/
ratio = rawParticalCount/100.0;
//Convert to  percentage, the shinyei reads 10milliseconds  to  90milliseconds  duration  for particles.  Basing on 10milliseconds, smallest  particle  assumingly  from  specification sheet.
//FlexiTimer2,  reads 10,000  readings  per second, which would be  1 reading per 100 microseconds. 100 readings  would be  10  milliseconds. Since Shinyei runs  at minimal 10  millisecond range.  I divided 10,000  readings  by  100 to  get 100.
//Good  example would be  rawPArticalCount  is  5000  half  of  the 10,000  readings were  active. 5000/100  would be  50  which translate to  50% low pulse occupancy.
totalParticles  = (1.1*pow(ratio,3)-3.8*pow(ratio,2)+520*ratio+0.62);
rawParticalCount  = 0;

//  shift counters  over, code  adapted from  template  provided  by  Mike  Taylor  and Joshua  Schapiro  from  Carnegie  Mellon  University's  CREATE  Lab
for (uint8_t  i = 0;  i < (numberOfPeaksRecording-1); i++)  {
previousPeaks[i]  = previousPeaks[i+1];
}
previousPeaks[numberOfPeaksRecording  - 1]  = totalParticles;
sumOfPreviousPeaks  = 0;
for (uint8_t  i = 0;  i < numberOfPeaksRecording; i++)  {
sumOfPreviousPeaks  +=  previousPeaks[i];
}
instantGoal = 2*sumOfPreviousPeaks;

delta = instantGoal - slowMovingAverage;

if  (delta  < -5000){
slowMovingAverage = slowMovingAverage - 250;
} else  if  (delta  < -2500){
slowMovingAverage = slowMovingAverage - 120;
} else  if(delta  < -1200){
slowMovingAverage = slowMovingAverage - 60;
} else  if(delta  < -500){
slowMovingAverage = slowMovingAverage - 25;
} else  if(delta  < -5){
slowMovingAverage = slowMovingAverage - 5;
} else  if(delta  < -1){
slowMovingAverage = slowMovingAverage - 1;
} else  if(delta  > 5000) {
slowMovingAverage = slowMovingAverage + 500;
} else  if(delta  > 2500){
slowMovingAverage = slowMovingAverage + 250;
} else  if(delta  > 1200){
slowMovingAverage = slowMovingAverage + 120;
} else  if(delta  > 500){
slowMovingAverage = slowMovingAverage + 50;
} else  if(delta  > 5){
slowMovingAverage = slowMovingAverage + 5;
} else  if(delta  > 1){
slowMovingAverage = slowMovingAverage + 1;
}

particleCountToDisplay  = slowMovingAverage;
}
}
``````

I have not adopted it for MySensors yet.
I like moving average they use, but the values do not make sense to me:

``````0.0000000000  Raw Particle  Count (0-10000) 0.0000000000  Ratio (0-100%)  53470.0000000000  Particle  Count
0.0000000000  Raw Particle  Count (0-10000) 27.0000000000  Ratio (0-100%)  53970.0000000000  Particle  Count
0.0000000000  Raw Particle  Count (0-10000) 0.0000000000  Ratio (0-100%)  54470.0000000000  Particle  Count
0.0000000000  Raw Particle  Count (0-10000) 21.6200008392  Ratio (0-100%)  54970.0000000000  Particle  Count
0.0000000000  Raw Particle  Count (0-10000) 0.0000000000  Ratio (0-100%)  55470.0000000000  Particle  Count
0.0000000000  Raw Particle  Count (0-10000) 24.2800006866  Ratio (0-100%)  55970.0000000000  Particle  Count
0.0000000000  Raw Particle  Count (0-10000) 0.0000000000  Ratio (0-100%)  56470.0000000000  Particle  Count
0.0000000000  Raw Particle  Count (0-10000) 24.1200008392  Ratio (0-100%)  56970.0000000000  Particle  Count
0.0000000000  Raw Particle  Count (0-10000) 0.0000000000  Ratio (0-100%)  57470.0000000000  Particle  Count
0.0000000000  Raw Particle  Count (0-10000) 22.3199996948  Ratio (0-100%)  57970.0000000000  Particle  Count
0.0000000000  Raw Particle  Count (0-10000) 0.0000000000  Ratio (0-100%)  58470.0000000000  Particle  Count
0.0000000000  Raw Particle  Count (0-10000) 20.3600006103  Ratio (0-100%)  58970.0000000000  Particle  Count
0.0000000000  Raw Particle  Count (0-10000) 0.0000000000  Ratio (0-100%)  59470.0000000000  Particle  Count
0.0000000000  Raw Particle  Count (0-10000) 17.6299991607  Ratio (0-100%)  59970.0000000000  Particle  Count
0.0000000000  Raw Particle  Count (0-10000) 0.0000000000  Ratio (0-100%)  60220.0000000000  Particle  Count
0.0000000000  Raw Particle  Count (0-10000) 19.0499992370  Ratio (0-100%)  60720.0000000000  Particle  Count
0.0000000000  Raw Particle  Count (0-10000) 0.0000000000  Ratio (0-100%)  60715.0000000000  Particle  Count
0.0000000000  Raw Particle  Count (0-10000) 20.6599998474  Ratio (0-100%)  61215.0000000000  Particle  Count
``````

• @alexsh1 said:

I took their code and stripped a few parts and this is what I am left with:

@alexsh1 the PPDN42 is for 1 micron and 2.5 micron , this one is for 0.5 micron

or I've not seen ?

• @epierre this is not the air beam code. They introduced "do it yourself" consept similar to the airbeam but with a different sensor (shinyei ppd42ns)

• Hey guys, I am struggeling to get the MQ135 to work. Is there a step by step howto? Or can someone answer my question from 3 days ago?
I would appreciate it very much.

• @rollercontainer I followed David Gironi method to calibrate - it is described as follows:

Before you can use the sensor, it has to be calibrated. For this, connect the sensor to your circuit and leave it powered on for 12-24 h to burn it in. Then put it into outside air, preferably at 20°C/35% rel. hum. (humidity is not so crucial). Read out the calibration value as such

float rzero = gasSensor.getRZero();
Wait until the value has somewhat settled (30min-1h). Remember, this is an ADC measurement so you might not want to wait some time between reading the sensor and also do some averaging. Once you have determined your RZero, put it into the MQ135.h. Note: Different sensors will likely have different RZero!

#define RZERO 76.63

Congrats, you have calibrated the sensor and can now read the CO2 ppm value in your sketch

float ppm = gasSensor.getPPM();

• @alexsh1 Thx
Is "RZERO" the same as "mq135_ro" or is it a seperate variable? (ro = R0 = RZERO?)

• So, obvious these are two variables. Wherefore is the mq135_ro? Is it the load resistor from signal to ground?

• @rollercontainer let's start from the basic - which MQ-135 do you have? A bare unit or with a logic controller and a variable resistor?

• @alexsh1 I bought a breakout board and desoldered everything but the sensor and the 33Ohm resistor for the heating. Then I soldered a 10kOhm pulldown from analog signal out to ground.

• @rollercontainer

Apologies for the confusion. @epierre did not use this library, but I have used MQ135.h library, but have not adopted it for MySensors and I did not like the sensor's reading jumping up and down.

In MQ135.h:

``````// Calibration resistance at atmospheric CO2 level
#define RZERO 394.5 //RZERO 76.63
``````

I think the principle of RZERO is the same as mq135_ro

• @rollercontainer I have do not done any changes to my breakout board. Just tuning the variable resistor and that's it.

One thing I noticed is that the sensor performs much better in enclosed premises (no windows and no doors). Alternatively, it has to be in the box or something. Any small light draft dramatically changing the readings.

• @alexsh1 I dont think so, in epierres sketch both values appear:

So, what the heck is mq135_ro? And what to tune to?

• @epierre OK, I found a mistake in my Shinyei ppd42ns code. Basically, it produces pcs/0.01cf values (normally, anything from 2000 to 10000 per a 30 secs reading).

``````This sensor use counting method to test dust concentration but not weight method, and the unit it pcs/L or pcs/0.01cf.
``````

This has to be converted into ug/m3 (the EPA standard)and then we can convert it into ppmv. I do not think dividing it by a volume factor is enough. Please see below updated code, which combines both ppd42ns and MH-Z14A sensors.

``````/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Copyright (C) 2013-2015 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
*
*******************************
* DESCRIPTION
*
* Dust Sensor for Shinyei ppd42ns
*
* 1 : COMMON(GND)
* 2 : OUTPUT(P2)
* 3 : INPUT(5VDC 90mA)
* 4 : OUTPUT(P1)
* 5 : INPUT(T1)･･･FOR THRESHOLD FOR [P2] - not used
*
* http://www.seeedstudio.com/wiki/images/4/4c/Grove_-_Dust_sensor.pdf
*
*   connect the sensor as follows :
*    Pin 4 of dust sensor PM1      -> Digital 6
*    Pin 3 of dust sensor          -> +5V
*    Pin 2 of dust sensor PM25    -> Digital 3
*    Pin 1 of dust sensor          -> Ground
* Contributor: epierre and alexsh1
**/

#include <MySensor.h>
#include <SPI.h>

// Define a static node address, remove if you want auto address assignment

#define CHILD_ID_DUST_PM10            0
#define CHILD_ID_DUST_PM25            1
#define CHILD_ID_DUST_PM10_MG_M3      2
#define CHILD_ID_DUST_PM25_MG_M3      3

#define DUST_SENSOR_DIGITAL_PIN_PM10  6
#define DUST_SENSOR_DIGITAL_PIN_PM25  3

#define CHILD_ID 0
#define CO2_SENSOR_PWM_PIN 2

unsigned long SLEEP_TIME = 30*1000; // Sleep time between reads (in milliseconds)
//VARIABLES
int val = 0;           // variable to store the value coming from the sensor
float valDUSTPM25 =0.0;
float lastDUSTPM25 =0.0;
float valDUSTPM10 =0.0;
float lastDUSTPM10 =0.0;
unsigned long duration;
unsigned long starttime;
unsigned long endtime;
unsigned long sampletime_ms = 30000;
unsigned long lowpulseoccupancy = 0;
float ratio = 0;
long concentrationPM25 = 0;
long concentrationPM10 = 0;
float concentration = 0;
float concentrationPM25_ugm3;
float concentrationPM10_ugm3;

int temp=273.5+22; //external temperature, if you can replace this with a DHT11 or better
float ppmvPM25;
float ppmvPM10;

float valAIQ =0.0;
float lastAIQ =0.0;
unsigned long duration_co2;
long co2ppm;

MySensor gw;
MyMessage dustMsgPM10(CHILD_ID_DUST_PM10, V_LEVEL);
MyMessage msgPM10(CHILD_ID_DUST_PM10_MG_M3, V_LEVEL);
MyMessage dustMsgPM25(CHILD_ID_DUST_PM25, V_LEVEL);
MyMessage msgPM25(CHILD_ID_DUST_PM25_MG_M3, V_LEVEL);

MySensor gw1;
MyMessage msg(CHILD_ID, V_LEVEL);

void setup()
{

// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Dust Sensor PPD42NS", "1.5");

// Register all sensors to gateway (they will be created as child devices)
gw.present(CHILD_ID_DUST_PM10, S_DUST);
//gw.send(msgPM10.set("ppm"));
gw.present(CHILD_ID_DUST_PM25, S_DUST);
//gw.send(msgPM25.set("ppm"));
gw.present(CHILD_ID_DUST_PM10_MG_M3, S_DUST);
gw.present(CHILD_ID_DUST_PM25_MG_M3, S_DUST);

pinMode(DUST_SENSOR_DIGITAL_PIN_PM10,INPUT);
pinMode(DUST_SENSOR_DIGITAL_PIN_PM25,INPUT);

gw1.sendSketchInfo("AIQ Sensor CO2 MH-Z14A", "1.0");
// Register all sensors to gateway (they will be created as child devices)
gw1.present(CHILD_ID, S_AIR_QUALITY);

pinMode(CO2_SENSOR_PWM_PIN, INPUT);

}

void loop()
{

//get PM 2.5 density of particles over 2.5 μm.
concentrationPM25=(long)getPM(DUST_SENSOR_DIGITAL_PIN_PM25);
Serial.print("PM25: ");
Serial.print(concentrationPM25);
Serial.println(" pcs/0.01cf");
concentrationPM25_ugm3 = conversion25(concentrationPM25);
Serial.print("PM25: ");
Serial.print(concentrationPM25_ugm3);
Serial.println(" ug/m3");
Serial.print("\n");
//ppmv=mg/m3 * (0.08205*Tmp)/Molecular_mass
//0.08205   = Universal gas constant in atm·m3/(kmol·K)
ppmvPM25=((concentrationPM25_ugm3) * ((0.08205*temp)/28.97));

if ((concentrationPM25 != lastDUSTPM25)&&(concentrationPM25>0)) {
gw.send(dustMsgPM25.set(ppmvPM25,2));
gw.send(msgPM25.set(concentrationPM25_ugm3,2));
lastDUSTPM25 = ceil(concentrationPM25);
}
//get PM 1.0 - density of particles over 1 μm.
concentrationPM10=getPM(DUST_SENSOR_DIGITAL_PIN_PM10);
Serial.print("PM10: ");
Serial.print(concentrationPM10);
Serial.println(" pcs/0.01cf");
concentrationPM10_ugm3 = conversion10(concentrationPM10);
Serial.print("PM10: ");
Serial.print(concentrationPM10_ugm3);
Serial.println(" ug/m3");
Serial.print("\n");
//ppmv=mg/m3 * (0.08205*Tmp)/Molecular_mass
//0.08205   = Universal gas constant in atm·m3/(kmol·K)
ppmvPM10=((concentrationPM10_ugm3) * ((0.08205*temp)/28.97));

if ((ceil(concentrationPM10) != lastDUSTPM10)&&((long)concentrationPM10>0)) {
gw.send(dustMsgPM10.set(ppmvPM10,2));
gw.send(msgPM10.set(concentrationPM10_ugm3,2));
lastDUSTPM10 = ceil(concentrationPM10);
}

//wait for the pin to go HIGH and measure HIGH time
duration_co2 = pulseIn(CO2_SENSOR_PWM_PIN, HIGH, 2000000);
co2ppm = 5000 * ((duration_co2/1000) - 2)/1000;
Serial.print("CO2: ");
Serial.print(co2ppm);
Serial.print(" ppm");
Serial.print("\n");
if ((co2ppm != lastAIQ)&&(abs(co2ppm-lastAIQ)>=10)) {
gw1.send(msg.set((long)ceil(co2ppm)));
lastAIQ = ceil(co2ppm);
}
gw.sleep(SLEEP_TIME);
gw1.sleep(SLEEP_TIME);

}

float conversion25(long concentrationPM25) {
double pi = 3.14159;
double density = 1.65 * pow (10, 12);
double r25 = 0.44 * pow (10, -6);
double vol25 = (4/3) * pi * pow (r25, 3);
double mass25 = density * vol25;
double K = 3531.5;
return (concentrationPM25) * K * mass25;
}

float conversion10(long concentrationPM10) {
double pi = 3.14159;
double density = 1.65 * pow (10, 12);
double r10 = 0.44 * pow (10, -6);
double vol10 = (4/3) * pi * pow (r10, 3);
double mass10 = density * vol10;
double K = 3531.5;
return (concentrationPM10) * K * mass10;
}

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-endtime+starttime)/(sampletime_ms*10.0);  // Integer percentage 0=>100
concentration = 1.1*pow(ratio,3)-3.8*pow(ratio,2)+520*ratio+0.62; // using spec sheet curve
Serial.print("lowpulseoccupancy:");
Serial.print(lowpulseoccupancy);
Serial.print("\n");
Serial.print("ratio:");
Serial.print(ratio);
Serial.print("\n");
//Serial.print("PPDNS42:");
//Serial.println(concentration);
//Serial.print("\n");

lowpulseoccupancy = 0;
return(concentration);
}
}
}
``````

Please see the terminal printout below. The lowpulseoccupancy and ratio are correct. um/m3 to ppmv conversion is correct as well. I had to dive into physics and a bit of googling to find out how to convert pcs/0.01cf into ug/m3 - this is a tricky one!

``````send: 11-11-0-0 s=1,c=1,t=37,pt=7,l=5,sg=0,st=ok:5.39
send: 11-11-0-0 s=3,c=1,t=37,pt=7,l=5,sg=0,st=ok:6.45
lowpulseoccupancy:3339978
ratio:11.03
PM10: 6751 pcs/0.01cf
PM10: 10.53 ug/m3

send: 11-11-0-0 s=0,c=1,t=37,pt=7,l=5,sg=0,st=ok:8.80
send: 11-11-0-0 s=2,c=1,t=37,pt=7,l=5,sg=0,st=ok:10.53
CO2: 960 ppm
send: 13-13-0-0 s=0,c=1,t=37,pt=4,l=4,sg=0,st=ok:960
lowpulseoccupancy:1983280
ratio:6.51
PM25: 3527 pcs/0.01cf
PM25: 5.50 ug/m3

send: 11-11-0-0 s=1,c=1,t=37,pt=7,l=5,sg=0,st=ok:4.60
send: 11-11-0-0 s=3,c=1,t=37,pt=7,l=5,sg=0,st=ok:5.50
lowpulseoccupancy:3430970
ratio:11.34
PM10: 7008 pcs/0.01cf
PM10: 10.93 ug/m3

send: 11-11-0-0 s=0,c=1,t=37,pt=7,l=5,sg=0,st=ok:9.13
send: 11-11-0-0 s=2,c=1,t=37,pt=7,l=5,sg=0,st=ok:10.93
CO2: 960 ppm
lowpulseoccupancy:1789340
ratio:5.86
PM25: 3141 pcs/0.01cf
PM25: 4.90 ug/m3

send: 11-11-0-0 s=1,c=1,t=37,pt=7,l=5,sg=0,st=ok:4.09
send: 11-11-0-0 s=3,c=1,t=37,pt=7,l=5,sg=0,st=ok:4.90
lowpulseoccupancy:3271804
ratio:10.80
PM10: 6563 pcs/0.01cf
PM10: 10.23 ug/m3

send: 11-11-0-0 s=0,c=1,t=37,pt=7,l=5,sg=0,st=ok:8.55
send: 11-11-0-0 s=2,c=1,t=37,pt=7,l=5,sg=0,st=ok:10.23
CO2: 970 ppm
send: 13-13-0-0 s=0,c=1,t=37,pt=4,l=4,sg=0,st=ok:970
``````

I still have not connected the BMP280 to get the pressure and temp readings into the equation.

What do you think?

• @rollercontainer said:

@alexsh1 I dont think so, in epierres sketch both values appear:

So, what the heck is mq135_ro? And what to tune to?

is identical to

https://github.com/GeorgK/MQ135/blob/master/MQ135.h#L27 multiplied by a factor is 1000

Maybe @epierre can clarify it?

• Hm, in the loop, mq135_ro will be read and used. As written in line 78, the MQ135_DEFAULTRO can be overwritten by the messured mq135_ro. Ok, but where is the 10kOhm voltage divider in this calculation? Included in the measurement of mq135_ro?
https://github.com/empierre/arduino/blob/master/AirQuality-MQ135.ino#L78

• @rollercontainer you are correct - mq135_ro is overwritten in the loop and I do not see a point in float mq135_ro = 10000.0; unless the mq135_ro function is deactivated during calibration. However, #define MQ135_DEFAULTRO 68550 this is the parameter you should change during the calibration process. Try to calibrate with the function mq135_ro and deactivating it though I am sure the result will be the same.

• Hi all,

I have my library for reading sensor. It is open source and its name is OpenSensor. I think that it's useful for every body.

Besides, I have my a monitoring system in real time using Arduino, MQ2, MQ135, MG811, GP2Y10 sensors and shield wifi ESP8266. It use OpenSensor library to read value from sensor and push it to database of WebServer by ESP826.

Good job,

• @tantt2810 Thanks

``````#define RSRO_CLEAN_AIR_FACTOR        3.7                                    //The value of Rs/Ro in clean air. According chart in datasheet.
#define RL_VALUE                     20                                     //The value of the load resistance on the board, in kilo ohms.
#define GET_RO_SAMPLE_TIMES          10                                     //The times of calibrating
#define GET_RS_SAMPLE_TIMES          10
``````

Can you please elaborate on how to calibrate the sensor?
Also how do you connect it if it comes on the board like this?

http://i.ebayimg.com/00/s/NjAwWDYwMA==/z/vMIAAOxycD9TTOVW/\$_1.JPG

• @alexsh1 said:

Also how do you connect it if it comes on the board like this?
http://i.ebayimg.com/00/s/NjAwWDYwMA==/z/vMIAAOxycD9TTOVW/\$_1.JPG

easy here, the AOUT is the one you will put on an analog one.

the DOUT (Digital OUT) is just an alarm linked to a level you could change on the board)

• @epierre said:

the DOUT (Digital OUT) is just an alarm linked to a level you could change on the board)

So DOUT is not used. Just AOUT?

• @alexsh1 said:

@epierre said:

the DOUT (Digital OUT) is just an alarm linked to a level you could change on the board)

So DOUT is not used. Just AOUT?

yes !

• @epierre For the air quality purpose which sensor would you recommend TSG-2600 or TS2602? They are slightly different, but both detect aiir contaminants (based on ppm of H2 and EtOH).

So far, I got MH-Z14A and the Dust sensor working with OLED screen and Domoticz and I just want to add 1-2 sensors to finish the project.

• @alexsh1 well any I would think for they are similar technology

• How do i get the Airquality sensor to show in domoticz? I use the AirQuality example from the library and MQ2.
I can see that it works in the serial, and it shows as a node under the mysensors hardware, but nothing in log or the device list when i expose it for gas.

• @Tore-André-Rosander said:

ut nothing in log or the device list when i expose it for gas.

huh you're on which version of mysensors ? have you tried the console (in arduino programmer) to see if there are some readings or a good wiring ?

• @epierre Hi! The dev branch (v 2) and the raspberry pi gateway (nrf24 directly connected to RPi GPIO). This is what i get in the serial console when i test with gas from a lighter:

``````Starting sensor (RNNNA-, 2.0.0-beta)
find parent
send: 255-255-255-255 s=255,c=3,t=7,pt=0,l=0,sg=0,st=bc:
find parent
send: 255-255-255-255 s=255,c=3,t=7,pt=0,l=0,sg=0,st=bc:
parent=0, d=1
req id
send: 255-255-0-0 s=255,c=3,t=3,pt=0,l=0,sg=0,st=ok:
req id
send: 255-255-0-0 s=255,c=3,t=3,pt=0,l=0,sg=0,st=ok:

send: 5-5-0-0 s=255,c=3,t=15,pt=0,l=2,sg=0,st=ok:
send: 5-5-0-0 s=255,c=0,t=17,pt=0,l=10,sg=0,st=ok:2.0.0-beta
send: 5-5-0-0 s=255,c=3,t=6,pt=1,l=1,sg=0,st=fail:0
id=5
Init complete, id=5, parent=0, distance=1
0
LPG:0ppm    CO:0ppm    SMOKE:0ppm
0
LPG:8ppm    CO:47ppm    SMOKE:18ppm
send: 5-5-0-0 s=0,c=1,t=37,pt=2,l=2,sg=0,st=ok:95
0
LPG:0ppm    CO:0ppm    SMOKE:0ppm
send: 5-5-0-0 s=0,c=1,t=37,pt=2,l=2,sg=0,st=ok:0`````````

• @Tore-André-Rosander said:

t=37

I don't know, would it be possible to have someone knowing the difference of the gateway with domoticz for 2.0 ? here it seems correct to me, some value are well seend from the sensor, so it is between the gateway and domoticz for me.

• @epierre I am attempting to apply this code to work with the MQ-8. I am trying to get rid of all of the radio and gw stuff that is in the code. Do you have anything that just calibrates the sensor and reads out the value without of all these other libraries?

• @epierre Also, what is the purpose of #define MQ135_MAXRSRO 2.428 //for CO2
#define MQ135_MINRSRO? I don't see them being referenced after they're defined?

• @tuimanso said:

@epierre Also, what is the purpose of #define MQ135_MAXRSRO 2.428 //for CO2
#define MQ135_MINRSRO? I don't see them being referenced after they're defined?

Datasheet defines min and max values they should guarantee, this is an application of it.

``````if(validinterval<MQ135_MAXRSRO && validinterval>MQ135_MINRSRO) {
``````

Hello,

I have a question about the MQ-8 sensor module . The potentiometer that is soldered onto the board, is it to control the sensitivity of the LED light or for the sensor itself? According to the datasheet for the sensor, the RL (load resistance) for the sensor should be at minimum 10k Ohm, yet I only see an SMD 102 resistor on the board, which is a 1K Ohm resistor. I have used a multimeter to connect the 5V pin and the AOUT pin, and I get precisely a measurement of about 1K Ohm... Does that mean that I should connect a load resistor to this?

This value seems very important since it is referenced in the code, the RL is part of the calculations, yet it is already pre-written 10K ohms...

Thanks

• Hello everyone,

I have a few of these sensors, but lately I have been concentrating on a MQ131 ozone sensor module from SainSmart. I am building an array utilizing MQ gas sensors and an Arduino that can be attached to a drone to measure vertical concnentration profiles of ground level ozone.

I am using an ozone generator and a 2B Technologies Ozone Monitor for the calibration of the sensor.

I have done the 24 hour burn in period on my sensors.
I have warmed them up to get steady values on my serial monitor.

The problem that I am having is that I am creating nearly 20,000 ppb O3 and the analog output is only around 25 to 30 units. It works out to around 922 ppb per unit.

Example before ozone is added the sensor reading is steady at 41. After raising the concentration of O3 to 18,727.5 ppb the reading drops to around 17-18.

Recently I have added a 22,000 ohm resistor in series with the analog output so that my readings start out at 971. But I am still only dropping around 20 units to 951 when ozone levels rise to nearly 20,000 ppb.

My question is how do I use a larger range of the units. There are 1023 possible values and I would like to see my reading drop from 971 to maybe the 200 area so that I am using more of the range. I would like each unit to be worth about 0.5 ppb. Ambient outdoor concentrations very rarely go higher than maybe 150 ppb so with the setup I have that would not even register on the serial output.

Thank you in advance for any help.

• @AtmosCheme the datasheet gives the MQ131from 1 to 1000 ppb, so you reach the limit obviously

http://eph.ccs.miami.edu/precise/GasSensorSpecs/Ozone.pdf

• @epierre

Thank you for the reply epierre.

I was going off of the SainSmart website that says the range for their module is 10 ppb to 200 ppm. That would cover 10 ppb to 200,000 ppb.

http://www.sainsmart.com/sensors/eco/sainsmart-mq131-gas-sensor-ozone-module-for-arduino-uno-mega2560-r3-raspberry-pi.html

The values keep dropping down as more and more ozone is added, I'm not thinking it is a problem with range as much as a problem with resolution. I have gone as high as around 50,000 ppb and continue to get response from the sensor.

Ive thought of using mapping functions in my code as well as resolution functions. But I just wasn't sure if there was a component I could add to the circuit to improve resolution.

In my latest sketch I have used a calibration code that analog reads the sensor for 6 minutes, and then remaps the min and max values observed during that time. I though this would get a broader range of values. The problem is that the longer the sensor is on it becomes stable so the min and max values become the same and there is nothing to map. So the longer it runs the smaller the range of values in the serial monitor.

• @AtmosCheme said:

I was going off of the SainSmart website that says the range for their module is 10 ppb to 200 ppm. That would cover 10 ppb to 200,000 ppb.

check their datasheet (or ask for it !)

The values keep dropping down as more and more ozone is added, I'm not thinking it is a problem with range as much as a problem with resolution. I have gone as high as around 50,000 ppb and continue to get response from the sensor.

yes it should be possible but you don't know from the datasheet the value. On a power regression you make an estimation with error, but that may change outside of know datapoints.

Ive thought of using mapping functions in my code as well as resolution functions. But I just wasn't sure if there was a component I could add to the circuit to improve resolution.

some as the Particle Photon has a better ADC that goes up to 4096

• Hi,

I just try to connect 7 sensors to an nano.
MQ-2, MQ-3, MQ-4,MQ-6, MQ-7,MQ-9, MQ-135

With sketch form robosensor https://forum.mysensors.org/topic/1595/mq2-sensor-w-airquality-sketch/2

I get data like MQ-7:1020 ,MQ135: 294, MQ-2:191

So it looks really good.
As i would like to have also the correct names an value names of the sensor and a correct calibration I think I should use a sketch like AirQuality-Multiple_Gas_Sensor1_4.ino
But I only see 3 of my 7 sensors in this sketch.

What would be a good sketch for my 7 sensors ??

Thanks
Jorg

• Hello everybody,

I am working with an Arduino Uno and a MQ135 module arduino : http://www.sainsmart.com/sainsmart-mq135-sensor-air-quality-sensor-hazardous-gas-detection-module-arduino.html

I can get back data from this sensor, I got 100 ppm when I launched serial terminal after compiling and uploading this code below.

``````int sensorValue;

void setup()
{
Serial.begin(9600);      // sets the serial port to 9600
}

void loop()
{
Serial.println(sensorValue, DEC);  // prints the value read
delay(100);                        // wait 100ms for next reading
}
``````

But, I would like to get back data from gases like NH3, NOx, alcohol, benzene, smoke and CO2 . I read a lot of topics and the datasheets of the sensor but I don't see how to do.

If someone could help me, it will be great.

Thanks.

• @walk_porto

I think that the only way you are going to be able to distinguish between different gases is to have a known concentration of that gas and adjust load resistance. This sensor will detect all of these gase because of cross sensitivity to the gases mentioned in the filament. You can not use just one MQ135 sensor and get readings for all of the gases.

• Hi Guys,

Nice work. I'm based in Hong Kong, and what matters to us is PM2.5, NO2 and Ozone (and indoor CO2/VOC).

Have you guys checked the laser based particle sensors like the Plantower G1-G3-G5, Innovafitness SDS011/021 ?
http://aqicn.org/sensor

• @Thanh-Viet Hello. Yes, I have Plantower G3 on order from Aliexpress and won't be able to test it before September. I think adopting it for MySensors is going to be easy

• Hello, there are statements if the values increase the MQ135 when a room ventilated, without people and is furnished? I I have several rooms here an increase over time. Perhaps this is also by the increase in temperature, sunlight or gases from the furniture. I have not found any literature on this. Is this behavior with other users as well?

• Has the type of installation (horizontal or vertical) have an impact on the values of MQ135? I have found no photos of built-in sensors already exist in the data sheet a statement.

• @paqor yes, in fact my MQ-135 was so unreliable that I had to move on to a different sensor

• @paqor
I'm interested, but i'm using mysensors 2.0. Can you update your sketch?

• @gieljnssns said:

@paqor
I'm interested, but i'm using mysensors 2.0. Can you update your sketch?

Yes, sure but I need time. I'm not a good programmer and need a lot of testing. My Englich is not particularly good. As some patience please, because I have to house, garden, woman and taking care of animals.

• @gieljnssns said in Air Quality Sensor:

@paqor
I'm interested, but i'm using mysensors 2.0. Can you update your sketch?

• v0.8 korrigierte Lib von RSTOICA
• EEPROM set R0Cor
• amended mq135.h required!
• Temp, Hum --> CO2Cor
• Api 2.0
• Motion is not yet tested
• Auto base value set r0corr if Co2cor <390
• R0Cor set by TextField runs (incoming message)
• Redesign

• @paqor
Probably one should not speak of a sensor CO2 value as well as responding to many other particles at the MQ135. The most accurate CO2 measurement we certainly be possible only with a MH-Z14. I now have to buy me a part and will then calibrate the MQ135 so. With the MQ135 can z. B. only say something about the air quality in relation to the outside air. It is rather a relative value.

Here the comparison of MH-14T (yellow) and MQ135 (red). One sees the calibration and as yet are both curves to each other.
[https://smarthome.gleisnetze.de/38-mhz-14-erste-erfahrungen-mit-dem-co2-sensor-fuer-mysensors-arduino-fhem]

Wahrscheinlich sollte man beim MQ135 nicht von einem CO2 Wert sprechen da der Sensor auch auf viele andere Partikel reagiert. Die genaueste CO2 Messung wir sicher nur mit einem MH-Z14 möglich sein. Ich habe mir jetzt so ein Teil bestellt und werde dann den MQ135 damit kalibrieren. Mit dem MQ135 lässt sich z. B. nur etwas zur Luftgüte im Verhältnis zur Außenluft sagen. Es ist also eher ein relativer Wert.

• @tuimanso
There seems a general misunderstanding about how those gas sensors really work: the "heater" must be switched on (5v) and "off"(1.4v) for the air sample going in and out of the sensor, when heating the air sample expands and goes out of the sensor, then in the cool down period the remaining air contracts and sucks in a new sample.
Those boards with mq... sensors only provide fixed 5v heating, so this can never work reliable
the 1.4v is needed to keep the sensor on a minimum temperature,example 40'c for the catalyst to react, the 5v will heat it up to example 80'c, to clean the catalyst and air flows out of the sensor
http://i49.tinypic.com/2rzdqgn.png something like this will work
Soon I will report my results together with a modified sketch for mysensors 2.0

• @epierre I am working on a dust sensor project using Samyoung DSM501A. I am confused with the identical calculation formula used in the Samyoung DSM501A and Shinyei PPD42NS dust sketch.

concentration = 1.1pow(ratio,3)-3.8pow(ratio,2)+520*ratio+0.62;

My understanding is that the formula is based on the specific product's data sheet curve. Is the formula for DSM501A, PPD42NS, or both?

• @bezeefly said:

Samyoung DSM501A and Shinyei PPD42NS

This is formula for Shinyei, you're right, I'searched back but don't know if a formula was made out : https://groups.google.com/forum/#!topic/airqualityegg/A1F9padoW10

strangely here it is used too : http://learn.linksprite.com/arduino/shields/use-dust-sensor-dsm501-with-arduino/

a formula by Chris Nafis here: https://github.com/OSBSS/Samyoung_Dust_Sensor/blob/master/Samyoung_Dust_Sensor.ino

• @epierre I obtained the following equation by polynomial fit the low ratio vs particle (pcs/283ml) graph in the DSM501 datasheet.

concentration = (0.1 * pow(ratio, 2)) + (619 * ratio) + 50;

• @bezeefly said:

@epierre I obtained the following equation by polynomial fit the low ratio vs particle (pcs/283ml) graph in the DSM501 datasheet.

concentration = (0.1 * pow(ratio, 2)) + (619 * ratio) + 50;

looks correct for the red curve

using http://www.xuru.org/rt/PR.asp with:
4 2500
8 5000
12 7500
16 10000
20 12500
22 15000
gives =7,613212955POWER(B3;2) + 467,3617082B3 + 631,9862425

a bit better

and on the previous one datasheet http://www.mouser.com/ds/2/744/Seeed_101020012-838657.pdf:
2 1000
3.9 2000
5.8 3000
7.2 4000
9 5000
10 6000
11.3 7000
12.6 8000
y = 19.09464953 x2 + 383.1041412 x + 174.9189116

• Hello,
I bought the classic MQ sensors (MQ2, MQ4 and MQ135) and I'm building a multisensor node with arduino nano v3:
@epierre can I use sketch https://github.com/empierre/arduino/blob/master/AirQuality-Multiple_Gas_Sensor1_4.ino, is it suitable?
thanks

• @jumping you will need to scale down the script obviously, in term of power it will be too short to have more than one.

• @epierre said:

@jumping you will need to scale down the script obviously, in term of power it will be too short to have more than one.

• float CO_secCurve[2] = {726.7809737, -4.040111669}; //MQ135

I understand the sketch in the "Gas Detection - Example", but not the previous.
sorry for my poor english

• @jumping see just above on power regressin curves, this is the result from a power regression based on the data sheet provided by one maker. Sometime I leave the data points in the sketch, sometime not (based on history

• @epierre Hi I am working on the MQ2 sensor, I want to know CO level. Did you do it before with this sensor?

I am using this module and just start work with this sensor.

• @epierre is it possible that the lastest github for Samyoung 501a is not correct? Flashed it and the results are way too high (157000 mg/m3)?

• Ppm percentage calculations in the code seems wrong. Instead of providing rs/ro ratio to the function is transfered rs value not the ratio.

• @ambuj please see the begining of the discussion regarding MQ like reliability

• @lukács-attila check wiring first, seems there are several sub version around thay may need other values from datasheet

• @surepic Which sensor? Which code? Would you please be more specific?

• @alexsh1
Hello, I am working recently with tgs8100 sensors, and I am developing a project, I would like to know how it was in the results? How did you acquire them? Any arduino code? Thank you

• @rosmel said in Air Quality Sensor:

tgs8100

That's also quite a non-precise sensor if I understand correctly. Useful for general 'how's the air', but because it will respond to so many different 'polutants' you never know which of those polutant is the culprit.

You will find code in the forum for good CO, CO2 and fine dust sensors.