New MQ7 library

Hi all,

I was looking for a library for MQ7 carbon monoxide gas sensor but I don't found somethings usefull for me. Perhaps I don't see it...

So, I've created a new library. Open to discussion and ameliorations.

The hardware :

I used MQ7 gas sensor v1.3 (see attachment), thinking it was ready to use. But not. Reverse engineering is necessary to found electronic plan (see attachment). The board must be modified to respect documentation of MQ7 like indicated on the plan and the photo.

Dual power supply (+5V and +1.4V) can be realised with LM317 regulator voltage (see attachment).
The output is connected to VCC of the MQ7 board. So, VRL will be powered by 5V AND 1.4 V. Like measures are done on just before the low heating phase, it's not important.

The library :

/*
  MQ7_LG.h - Library for MQ7 Monoxide Gas Sensor v1.3 hardware.
  Created by Laurent Gibergues, January 21, 2015
  Released into the public domain.
  Hardware v1.3 need modification : see documentation please
  
*/


#ifndef MQ7_LG_h
#define MQ7_LG_h

#include <inttypes.h>



class MQ7_LG {
public :
  MQ7_LG(uint8_t VRLPin, uint8_t HeaterCtrlPin, int8_t DOutPin = -1, int8_t HeaterVoltagePin = -1);
  void init();
  void run();
  int read();
  float getVRL();
  int getDOut();
  float getHeaterVoltage();
  typedef enum states {HIGH_HEATING = 0, LOW_HEATING} state_t;
  
private :
  int  _ppmCO;
  uint8_t _VRLPin;
  uint8_t _heaterCtrlPin;
  int8_t _DOutPin;
  int8_t _heaterVoltagePin;
  bool _HeaterVoltageIsConnected;
  bool _DOutIsConnected;
  state_t _state;
  unsigned long _prevTime;

  //functions
  int toPpm(float VRL);
  
  // Specifics data
  const static unsigned long HIGH_HEATING_TIME = 60 * 1000L;    // ms
  const static unsigned long LOW_HEATING_TIME = 90 * 1000L;     // ms
  const static unsigned RL = 10000;              // Ohms
  const static unsigned R0 = 2000;               // Ohms
  const static float    VCC_HEATER = 5.29;       // Volts
  const static float    COEF_A = -1.6;          // See MQ7 documentation :
  const static float    CONSTANT_B = 100;        // log(ppmCO) = COEF_A * log(Rs/R0) + log(CONSTANT_B)
};

#endif

And the implementation :

#include <Arduino.h>
#include "MQ7_LG.h"


MQ7_LG::MQ7_LG(uint8_t VRLPin, uint8_t HeaterCtrlPin, int8_t DOutPin, int8_t HeaterVoltagePin)
    : _VRLPin(VRLPin), _heaterCtrlPin(HeaterCtrlPin), _DOutPin(DOutPin), _heaterVoltagePin(HeaterVoltagePin) {
}

void MQ7_LG::init() {
  _ppmCO = -1;
  pinMode(_heaterCtrlPin, OUTPUT);

  if (_DOutPin != -1) {
    pinMode(_DOutPin, INPUT);
    _DOutIsConnected = true;
  }
  else
    _DOutIsConnected = false;
  if (_heaterVoltagePin != -1)
    _HeaterVoltageIsConnected = true;
  else
    _HeaterVoltageIsConnected = false;

  // Start High Heating
  digitalWrite(_heaterCtrlPin, LOW);
  _state = HIGH_HEATING;
  _prevTime = millis();
}

void MQ7_LG::run() {
  unsigned long time = millis();
  
  if (_state == HIGH_HEATING) {
    if (time - _prevTime >= HIGH_HEATING_TIME) {
      _ppmCO = toPpm(getVRL());
      _state = LOW_HEATING;
      digitalWrite(_heaterCtrlPin, HIGH);
      _prevTime = time;
    }
  }
  else {
     if (time - _prevTime >= LOW_HEATING_TIME) {
      _state = HIGH_HEATING;
      digitalWrite(_heaterCtrlPin, LOW);
      _prevTime = time;
    }
  }
}
 

float MQ7_LG::getVRL() {
  float v;
  
  v = analogRead(_VRLPin);
  v = map(v, 0, 1024, 0, (int)(VCC_HEATER * 1000));
  return v / 1000; 
}
  
int MQ7_LG::getDOut() {
  if (_DOutIsConnected)
    return digitalRead(_DOutPin);
  else
    return -1;
}

float MQ7_LG::getHeaterVoltage() {
  float v;
  
  if (_HeaterVoltageIsConnected) {
    v = analogRead(_heaterVoltagePin);
    v = map(v, 0, 1024, 0, 10000);      // Heater voltage measure is divided by 2 by hardware
    v /= 1000;
  }
  else
    v = -1;
  return v;
}

int MQ7_LG::read() {
  return _ppmCO;    // -1 if data not available (during 60s after initialisation)
}

int MQ7_LG::toPpm(float VRL) {
  return (int)(CONSTANT_B * pow((((float)RL / R0) * ((VCC_HEATER / VRL) - 1)), COEF_A));
}

An example :

/*
 MQ7_LG_test.ino

 MQ7_LG test library

 The circuit:
 * MQ7 gas sensor v1.3 or other
 * VRL at analog input A0
        * digital controlled supply on output pin 4
        Optionaly :
        * 1/2 of VCC connected on A3 by a resistor divider
        * DOUT on pin 5

 Created 21 january 2015
 By Laurent Gibergues (laurent.gibergues (at) free.fr

 http://forum.arduino.cc

*/
#include <Arduino.h>
#include "MQ7_LG.h"


#define MQ7_VRL_PIN             A0
#define MQ7_HEATER_CTRL_PIN     4

// Optional parameter
#define MQ7_DOUT_PIN            5
#define MQ7_VOLTAGE_HEATER_PIN  A3


#define PRINT_PERIOD            2000

MQ7_LG  mq7(MQ7_VRL_PIN, MQ7_HEATER_CTRL_PIN, MQ7_DOUT_PIN, MQ7_VOLTAGE_HEATER_PIN);

unsigned long startTime;

void setup() {
  Serial.begin(57600);
  Serial.println("MQ7 CO gas sensor test");
  Serial.println("Time;VRL;ppm CO;Heater voltage;DOut;");
  
  mq7.init();
  startTime = millis();
}

void loop() {
  delay(PRINT_PERIOD);
  mq7.run();
  Serial.print((millis() - startTime) / 1000);
  Serial.print(';');
  Serial.print(mq7.getVRL());
  Serial.print(';');
  Serial.print(mq7.read());
  Serial.print(';');
  Serial.print(mq7.getHeaterVoltage());
  Serial.print(';');
  Serial.print(mq7.getDOut());
  Serial.println(';');
}

It was difficult for me to understand how to transform a tension (VRL) to a ppm value.
Here is my comprehension of the documentation :

Rs / Ro = (Vcc – VRL) / VRL

If X = Rs/Ro and Y = ppmCO

Sensitivity characteristics graph says :

| X | Y | log(X) | log(Y) |

| 1 | 100 | 0 | 2 |
| 0.1 | 4000 | -1 | 3.6 |

log(Y) = -1.6 log(X) + 2
= log(X^-1.6) + log(100)
= log (100 * X^-1.6)
Y = 100 * X^-1.6
ppmCO = 100 * (Rs/Ro)^-1.6

Rs = RL * (VCC / VRL – 1)

ppmCO = 100 * (RL/Ro * (VCC / VRL – 1))^-1.6

Ro needs calibration tests. It's could be 2kOhms.

The result :

See the two graphs attached.
At t = 256s, I've opened the gas in the kitchen... and the window at t=580 !

Thanks for return.

PS : sorry for my english, I've done the best !

1 Like

Thanks for sharing!

Had a very quick look at the .h and the formula and I think you should make

int _ppmCO;

an unsigned int - uint16_t as it is always positive or maybe even a long as I do not know the max value it can reach (e.g. measure exhaust of a car can be quite high)

Yes, you're right.

In the same time, MQ7 detecting range is 20 to 2000 ppm...
During the first heating cycle, _ppmCO = -1 to indicate "no data available". It could be 0 too.

Thanks for the welcome.

[ I don't know why graphics are attached again ! And I don't know how remove them !]

moderator: removed unneeded attachments

Hi,I don't understand

log(Y) = -1.6 log(X) + 2
= log(X^-1.6) + log(100)
= log (100 * X^-1.6)
Y = 100 * X^-1.6
ppmCO = 100 * (Rs/Ro)^-1.6

Why we need log to calculate PPM, and I use MQ7 Module but I don't know Ro and RL, how to measure Ro RL ??

thanks

Hi Laurent, thanks for this library. Please I don't understand a few things.

  1. MQ7_HEATER_CTRL_PIN and MQ7_VOLTAGE_HEATER_PIN ... where to connect them? MQ7 board has only VCC, GND, AOUT, DOUT.

  2. My PCB is little bit different, see here on Aliexpress. Do you think this is correct design or still need some re-work?

Thanks for a advice.

Hi, sorry for the delay...

@jay :
We need use log because the graph sensitivity (fig 3 of the manual MQ7) use log scale. The relationship between Rs/Ro and ppm value is a line equation but in a log scale.

Ro is not know. The documentation says that it's necessary to do experimentation to found it. I've used 2k because it seems to be good for mine...
RL is 10k (cf fig 3 of the MQ7 manual)

@dmnc :
MQ7_HEATER_CTRL_PIN and MQ7_VOLTAGE_HEATER_PIN are output and input of a the "dual power supply" board (see attachment). But your pcb sensor seems to be different so I don't know if this power supply board is necessary for you.

Dear all,

Attached you'll find my attempt. It includes schematics, source and equation derivations.

Arduino MQ7 5V14V toggle with resistor.pdf (849 KB)

Cbmblom

There are some errors in your otherwise good article about the theory.

RL is a reference resistor with a value (according to the datasheet) between 5 and 20 Ω. This value should be calibrated, however most resources present a value near 10 Ω. It is assumed here that RL = 10 Ω since it does not influence results that much.

RL is 10 KΩ, not 10 Ohm. The HANWEI data sheet for the MQ-7 mentions to use somewhere between 5K and 47K for RL.

Also the timing for the two phases in the loop function are incorrect. They should be 60 seconds for the purification phase and 90 seconds for the measurement phase. When you run your script, both phases are only 20 seconds each.

The circuit with the 2N2222 NPN transistor to create the 1.4V, will not open enough to create a short across the 140 Ohm resistor to apply 5V to the sensor. At best it will be 4V, most likely less. A better solution would be to use a logic level MOSFET, a small relais or a different circuit all together.

The 240 Ohm in series with the base of the transistor is there to create a base current that is needed to drive the collector current (through the beta), and not "to protect against drainage".