MQ2 - Reading different gases

Hi all.

I am using this code example from Sandbox Electronics to read from the MQ2 gas sensor. However, all my readings are 0. If I use a simple analog reading on the specific pin, I get the expected analog values - from 0 to 1023, usual values around 250, and if I expose it to gas for example goes up to 1000. The reason I want to use the following code is because I want to extract the different properties from the sensor, and this code seems to handle this issue - except from the 0 output!

Does anyone has any idea why the code does not give any readings?

/*******************Demo for MQ-2 Gas Sensor Module V1.1*****************************
Author:  Tiequan Shao: tiequan.shao@sandboxelectronics.com
         Peng Wei:     peng.wei@sandboxelectronics.com
         
Lisence: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0)

Note:    This piece of source code is supposed to be used as a demostration ONLY. More
         sophisticated calibration is required for industrial field application. 
        
                                                    Sandbox Electronics    2013-05-01
************************************************************************************/
/************************Hardware Related Macros************************************/
#define         MQ_PIN                       (0)     //define which analog input channel you are going to use
#define         RL_VALUE                     (5)     //define the load resistance on the board, in kilo ohms
#define         RO_CLEAN_AIR_FACTOR          (9.83)  //RO_CLEAR_AIR_FACTOR=(Sensor resistance in clean air)/RO,
                                                     //which is derived from the chart in datasheet

/***********************Software Related Macros************************************/
#define         CALIBARAION_SAMPLE_TIMES     (50)    //define how many samples you are going to take in the calibration phase
#define         CALIBRATION_SAMPLE_INTERVAL  (500)   //define the time interal(in milisecond) between each samples in the
                                                     //cablibration phase
#define         READ_SAMPLE_INTERVAL         (50)    //define how many samples you are going to take in normal operation
#define         READ_SAMPLE_TIMES            (5)     //define the time interal(in milisecond) between each samples in 
                                                     //normal operation

/**********************Application Related Macros**********************************/
#define         GAS_LPG                      (0)
#define         GAS_CO                       (1)
#define         GAS_SMOKE                    (2)

/*****************************Globals***********************************************/
float           LPGCurve[3]  =  {2.3,0.21,-0.47};   //two points are taken from the curve. 
                                                    //with these two points, a line is formed which is "approximately equivalent"
                                                    //to the original curve. 
                                                    //data format:{ x, y, slope}; point1: (lg200, 0.21), point2: (lg10000, -0.59) 
float           COCurve[3]  =  {2.3,0.72,-0.34};    //two points are taken from the curve. 
                                                    //with these two points, a line is formed which is "approximately equivalent" 
                                                    //to the original curve.
                                                    //data format:{ x, y, slope}; point1: (lg200, 0.72), point2: (lg10000,  0.15) 
float           SmokeCurve[3] ={2.3,0.53,-0.44};    //two points are taken from the curve. 
                                                    //with these two points, a line is formed which is "approximately equivalent" 
                                                    //to the original curve.
                                                    //data format:{ x, y, slope}; point1: (lg200, 0.53), point2: (lg10000,  -0.22)                                                     
float           Ro           =  10;                 //Ro is initialized to 10 kilo ohms

void setup(){
  Serial.begin(9600);                               //UART setup, baudrate = 9600bps
  Serial.print("Calibrating...\n");                
  Ro = MQCalibration(MQ_PIN);                       //Calibrating the sensor. Please make sure the sensor is in clean air 
                                                    //when you perform the calibration                    
  Serial.print("Calibration is done...\n"); 
  Serial.print("Ro=");
  Serial.print(Ro);
  Serial.print("kohm");
  Serial.print("\n");
}

void loop(){
   Serial.print("LPG:"); 
   Serial.print(MQGetGasPercentage(MQRead(MQ_PIN)/Ro,GAS_LPG) );
   Serial.print( "ppm" );
   Serial.print("    ");   
   Serial.print("CO:"); 
   Serial.print(MQGetGasPercentage(MQRead(MQ_PIN)/Ro,GAS_CO) );
   Serial.print( "ppm" );
   Serial.print("    ");   
   Serial.print("SMOKE:"); 
   Serial.print(MQGetGasPercentage(MQRead(MQ_PIN)/Ro,GAS_SMOKE) );
   Serial.print( "ppm" );
   Serial.print("\n");
   delay(200);
}

/****************** MQResistanceCalculation ****************************************
Input:   raw_adc - raw value read from adc, which represents the voltage
Output:  the calculated sensor resistance
Remarks: The sensor and the load resistor forms a voltage divider. Given the voltage
         across the load resistor and its resistance, the resistance of the sensor
         could be derived.
************************************************************************************/ 
float MQResistanceCalculation(int raw_adc)
{
  return ( ((float)RL_VALUE*(1023-raw_adc)/raw_adc));
}


/***************************** MQCalibration ****************************************
Input:   mq_pin - analog channel
Output:  Ro of the sensor
Remarks: This function assumes that the sensor is in clean air. It use  
         MQResistanceCalculation to calculates the sensor resistance in clean air 
         and then divides it with RO_CLEAN_AIR_FACTOR. RO_CLEAN_AIR_FACTOR is about 
         10, which differs slightly between different sensors.
************************************************************************************/ 
float MQCalibration(int mq_pin){
  int i;
  float val=0;
  
  for (i=0;i<CALIBARAION_SAMPLE_TIMES;i++) {         //take multiple samples
    val += MQResistanceCalculation(analogRead(mq_pin));
    delay(CALIBRATION_SAMPLE_INTERVAL);
  }
  val = val/CALIBARAION_SAMPLE_TIMES;                   //calculate the average value
  
  val = val/RO_CLEAN_AIR_FACTOR;                        //divided by RO_CLEAN_AIR_FACTOR yields the Ro 
                                                        //according to the chart in the datasheet 
  return val; 
}
/*****************************  MQRead *********************************************
Input:   mq_pin - analog channel
Output:  Rs of the sensor
Remarks: This function use MQResistanceCalculation to caculate the sensor resistenc (Rs).
         The Rs changes as the sensor is in the different consentration of the target
         gas. The sample times and the time interval between samples could be configured
         by changing the definition of the macros.
************************************************************************************/ 
float MQRead(int mq_pin){
  int i;
  float rs=0;

  for (i=0;i<READ_SAMPLE_TIMES;i++) {
    rs += MQResistanceCalculation(analogRead(mq_pin));
    delay(READ_SAMPLE_INTERVAL);
  }
  
  rs = rs/READ_SAMPLE_TIMES;
 
  return rs;  
}

/*****************************  MQGetGasPercentage **********************************
Input:   rs_ro_ratio - Rs divided by Ro
         gas_id      - target gas type
Output:  ppm of the target gas
Remarks: This function passes different curves to the MQGetPercentage function which 
         calculates the ppm (parts per million) of the target gas.
************************************************************************************/ 
int MQGetGasPercentage(float rs_ro_ratio, int gas_id){
  if ( gas_id == GAS_LPG ) {
     return MQGetPercentage(rs_ro_ratio,LPGCurve);
  } else if ( gas_id == GAS_CO ) {
     return MQGetPercentage(rs_ro_ratio,COCurve);
  } else if ( gas_id == GAS_SMOKE ) {
     return MQGetPercentage(rs_ro_ratio,SmokeCurve);
  }    
  return 0;
}

/*****************************  MQGetPercentage **********************************
Input:   rs_ro_ratio - Rs divided by Ro
         pcurve      - pointer to the curve of the target gas
Output:  ppm of the target gas
Remarks: By using the slope and a point of the line. The x(logarithmic value of ppm) 
         of the line could be derived if y(rs_ro_ratio) is provided. As it is a 
         logarithmic coordinate, power of 10 is used to convert the result to non-logarithmic 
         value.
************************************************************************************/ 
int  MQGetPercentage(float rs_ro_ratio, float *pcurve){
  return (pow(10, (((log(rs_ro_ratio)-pcurve[1])/pcurve[2]) + pcurve[0]))); 
}

Thanks!

Could you try changing this:

return ( ((float)RL_VALUE*(1023-raw_adc)/raw_adc));

into this:

return ( ((float)RL_VALUE*(1023.0 - (float) raw_adc) / (float) raw_adc));

If that doesn't help, you could send Serial.println messages to the serial monitor, to follow the raw_adc to the calculated value.

I think I located the problem, which is the resistance values that are declared on the top.

However, I 'm not sure about the difference between the RL_VALUE (load resistance on the board), and Ro, and finally I'm not sure what is the Rs/Ro ration (Rs divided with Ro).

Can someone explain to me? I don't understand how these values are derived, and what to expect as a result.

hello,

i'm using the Mq-3 sensor, i need to have a curve like COCurve in your code.
where can i found it ?

thanks

Sorry for the late reply.

You can have a look in Sandbox Electronics they have some example code on how to create a curve that matches the sensor's characteristics ( Products | Sandbox Electronics ), however I don't think they have code for MQ3.

I would suggest to check the code they have posted for the MQ codes, and try to simulate your own.

louisbourdon:
I think I located the problem, which is the resistance values that are declared on the top.

However, I 'm not sure about the difference between the RL_VALUE (load resistance on the board), and Ro, and finally I'm not sure what is the Rs/Ro ration (Rs divided with Ro).

Can someone explain to me? I don't understand how these values are derived, and what to expect as a result.

Hello,

For the MQ2, it expects to be in clean air while measuring the initial resistive load, so the Ro value is modified at startup:

Ro = MQCalibration(MQ_PIN);

This is why you have 0 unless you add the polluant that would add up the value.

As I discussed there Air Quality Sensor | MySensors Forum I've done this "calibration" on several sensors and have surprising values (in kilo-ohms):

MQ2:5.28
MQ6:36.94
MQ131:3.61
TGS2600:0.04

Where I'm unclear is I don't know if turning the resistance wheel I would raise the resistance or lower it, but in every case I see two issues:

1- managing the resistance is not equivalent to calibration

2- if a sensor reboot while being in a polluant atmosphere with this method of initial calibration, it will report 0 all the time...

on the opposite doing the simpler way with a MQ135 (but I'm not yet able to know how to get the valued below), I can observe with a fixed Ro the CO2 variation when windows are closed or opened.

 float Vrl = val * ( 5.00 / 1024.0  );      // V
  float Rs = 20000 * ( 5.00 - Vrl) / Vrl ;   // Ohm 
  int ratio =  Rs/Ro;     
ppm = 37143 * pow (ratio, -3.178);

But this first method has some flaws, that this post Davide Gironi blog: Cheap CO2 meter using the MQ135 sensor with AVR ATmega shows, it should not report 0 but the cleai air value for CO2 for this gas.

ppm = 116.6020682 (Rs/Ro)^-2.769034857

we also know that the current amount of CO2 gas in atmosphere is (unfortunately) 392ppm, so, heating the sensor for 24 hours, and leaving it in open air, if we measure 26954ohm as the resistance output we can Ro should be 41763.

Imposing a Ro of 41000, obtained scaling factor in my experiment is: 56.0820, and exponent is -5.9603.

//define sensor resistance at 100ppm of NH3 in the clean air
//calibrate your sensor to obtain precise value
//Ro = Rs * sqrt(MQ135_SCALINGFACTOR/ppm, MQ135_EXPONENT)
//   = Rs * exp( ln(MQ135_SCALINGFACTOR/ppm) / MQ135_EXPONENT )
//#define MQ135_DEFAULTPPM 100 //default ppm of NH3 for calibration
//#define MQ135_DEFAULTRO 108251 //default Ro for MQ135_DEFAULTPPM ppm of NH3

//the graphic at fig.2 of datasheet (sensitivity characteristics of the MQ-135)
//seems a power function y = a*x^b
//so ppm = a*(Rs/Ro)^b
//using power regression, you can obtain scaling factor (a), and exponent (b) !for a specific GAS!
//points: (1.5 , 10) (0.75 , 100) (0.59 , 200)
//#define MQ135_SCALINGFACTOR 37.58805473 //for NH3
//#define MQ135_EXPONENT -3.235365807 //for NH3

//define the Rs/Ro valid interval, MQ135 detect from 10ppm to 300ppm
//look at the datasheet and use the helper to define those values
//#define MQ135_MAXRSRO 1.505 //for NH3
//#define MQ135_MINRSRO 0.56 //for NH3

#define MQ135_DEFAULTPPM 392 //default ppm of CO2 for calibration
#define MQ135_DEFAULTRO 41763 //default Ro for MQ135_DEFAULTPPM ppm of CO2
#define MQ135_SCALINGFACTOR 116.6020682 //CO2 gas value
#define MQ135_EXPONENT -2.769034857 //CO2 gas value
#define MQ135_MAXRSRO 2.428 //for CO2
#define MQ135_MINRSRO 0.358 //for CO2

In fact it is not NH3 but CH3 in this graph with the correct chinese sheet http://www.particle-sensor.com/immagini/MQ-135.pdf. So Following the first methode above I owuld get thir curve:
(10,1.5) (200,0.79) that gives a curve of {1,-0.82,0,55}

How can I close with davide's one ?

Hello,

Having sorted out how to make a power regression, I now have the sames values as Davide Gironi.

I applied this to MQ2, MQ6, MQ131, MQ135 and TGS2600 ith resistance calibration at start.

MQ2 :LPG :12ppm CO :0ppm SMOKE :21ppm
MQ6 :LPG :3ppm CH4 :39ppm
MQ131 :CL2 :2ppm O3 :3ppm
TGS2600:H2 :5191ppm C2H5OH:8825ppm C4H10 :16954ppm
MQ135 :CO2 :0ppm CO :0ppm CH3 :0ppm NH4 :0ppm
GP2Y1010AU0F Raw Signal Value (0-1023): 19 - Voltage: 0.09 - Dust Density: -84.23

the MQ135 is still doing its first heating period, do not care about its values.

Clearly the TGS shows abnormal values, I'm quite puzzled by this, using the previous method it was always 0...

Has anyone had any luck with MQ3 (MQ303a) sensor?

Hi all,
I too am interested in using MQ2 and MQ135 sensors. Does anyone have the sensitivity data table rather than the curves in pdf? I understand people use power regression to approximate the slope and the crossover. The original data used to plot the curve would be the best reference for me. The same request goes to the humidity/temperature sensitivity curve's original data as well.
Thanks for your help greatly.

Hi louisbourdon,

I am a newbie to Arduino and would like to use MQ135 Gas sensor as you discussed in your post of some time ago, did you get a sketch working, I am not up to that level just yet?

Thank you
Ray

I am also having a problem!!

I recently bought a mg811 co2 gas sensor from sandbox electronics for a science fair project and have been having some problems with it. I burned it in for 2 days and allowed 2 hours for it to heat up but the readings either jump around, are very inaccurate or steadily climb! The calibrated it properly and have done everything this guide told me to do:
Here is the link for the guide:

http://www.veetech.org.uk/Prototype_CO2_Monitor.htm

Here is the link for the sensor:

http://sandboxelectronics.com/?product=mg-811-co2-gas-sensor-module

PLEASE HELP!

The MQ sensors need very extreme smooth 5V regulator. Btw, it still give many jump values.

HI? can you teach me how to use this code please =(

Hi,

I am using the MQ2 calibration code, while reading the analogue values, I am getting readings, however with the code I only get 0s. Any clue/help will be appreciated.

Hi there,

How do I save/log the data that the calibration program reports. I want to do it programatically.

TIA.

@amvi. Save to EEPROM

How to get curve of gas?
I use that program too, but I use mq 136 for SO2 sensor, I still confused about SO2 curve?
Can you help me for solve this?
Thanks

After 2 years, I had success with these project.
There are several mistakes in many examples.

  1. The chinese FC-22 Gas Sensor Module use RL = 1 kohm.

  2. GetPPM function have to use log10() instead of log().

  3. To find a curve array value of any sensors can be read here:
    http://forum.naddalim.com/viewtopic.php?f=38&t=2488

amvi:
Hi,

I am using the MQ2 calibration code, while reading the analogue values, I am getting readings, however with the code I only get 0s. Any clue/help will be appreciated.

I'm having the same problem now. Do you remember the solution?