Go Down

Topic: MQ2 - Reading different gases (Read 20080 times) previous topic - next topic


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?

Code: [Select]
/*******************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)

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
  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");

void loop(){
   Serial.print(MQGetGasPercentage(MQRead(MQ_PIN)/Ro,GAS_LPG) );
   Serial.print( "ppm" );
   Serial.print("    ");   
   Serial.print(MQGetGasPercentage(MQRead(MQ_PIN)/Ro,GAS_CO) );
   Serial.print( "ppm" );
   Serial.print("    ");   
   Serial.print(MQGetGasPercentage(MQRead(MQ_PIN)/Ro,GAS_SMOKE) );
   Serial.print( "ppm" );

/****************** 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));
  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));

  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
int  MQGetPercentage(float rs_ro_ratio, float *pcurve){
  return (pow(10, (((log(rs_ro_ratio)-pcurve[1])/pcurve[2]) + pcurve[0])));



Could you try changing this:
Code: [Select]

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

into this:
Code: [Select]

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.



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



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 ( http://sandboxelectronics.com/store/ ), 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.


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.


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

Code: [Select]
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 http://forum.mysensors.org/topic/147/air-quality-sensor/9 I've done this "calibration" on several sensors and have surprising values (in kilo-ohms):


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.

Code: [Select]
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 http://davidegironi.blogspot.fr/2014/01/cheap-co2-meter-using-mq135-sensor-with.html 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.

Code: [Select]

//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 ?



  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


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:

Here is the link for the sensor: 


I'm usually the one needing 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 =(



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.


Go Up