Arduino CO Monitor Using MQ-7 Sensor

85,336

89

188

Introduction: Arduino CO Monitor Using MQ-7 Sensor

A few words why this instructable was created: one day my girlfriend's mother phoned us at the middle of the night because she felt really sick - she had dizziness, tachycardia, nausea, high blood pressure, she even fainted for unknown time (probably ~5 minutes, but there is no way to tell), all without any apparent reason. She lives in a small village far away from hospitals (60 km from our place, 30 km to the closest hospital, 10 km without any normal road in between), so we rushed to her and got there soon after the ambulance. She got hospitalized and in the morning she felt almost well, but doctors weren't able to find the cause of it. The next day we had an idea: it could have been CO poisoning, since she has gas water boiler (on the photo), and was sitting close to it for the whole evening when it happened.
We recently bought MQ-7 CO sensor, but never had time to assemble a schematics for it, so this was the perfect time to do so. After an hour of searching internet for any instructions, I realized that I can't find any guide that at the same time follows sensor manufacturer's instructions provided in its datasheet and explains anything at all (one example seemed to have quite good code, but it wasn't clear how to apply it, others were oversimplified and wouldn't work well). So we spent about 12 hours for developing schematics, making and printing 3d case, testing and calibrating the sensor, and the next day went to the suspicious boiler. It turned out that CO levels there were extremely high, and could be fatal if CO exposure time were longer. So I believe anyone who has similar situation (like gas boiler or other combustion happening inside a living space) should get such sensor to prevent something bad from happening.

All that happened two weeks ago, since then I improved schematics and program quite a lot, and now it seems to be reasonably good and relatively simple (not 3-lines-of-code simple, but still). Although I hope that someone with precise CO meter will provide me some feedback on default calibration that I put in the sketch - I suspect it is far from good.
Here is a complete guide with some experimental data.

Step 1: Bill of Materials

You will need:

0. Arduino board. I prefer Chinese clone of Arduino Nano for its outstanding price of $3, but any 8-bit arduino will work here. Sketch uses some advanced timers operation, and was tested only on atmega328 microcontroller - although probably it will work well on others too.

1. MQ-7 CO sensor. Most commonly available with this Flying Fish sensor module, it has to run through a small modification, details in the next step, or you can use a separaten MQ-7sensor.

2. NPN bipolar transistor. Virtually any NPN transistor that can handle 300 mA or more will work here. PNP transistor won't work with a mentioned Flying Fish module (because it has heater pin soldered to sensor's output), but can be used with a discrete MQ-7 sensor.

3. Resistors: 2 x 1k (from 0.5k to 1.2k will work fine), and 1 x 10k (that one is best kept precise - although if you absolutely must use a different value, adjust reference_resistor_kOhm variable in the sketch accordingly).

4. Capacitors: 2 x 10uF or more. Tantalum or ceramic ones are required, electrolytic won't work well due to high ESR (they won't be able to provide enough current to smooth high-current ripple).

5. Green and red LEDs to indicate current CO level (you can also use a single dual-color LED with 3 terminals, as we used in our yellow box prototype).

6. Piezo buzzer to indicate high CO level.

7. Breadboard and wires (you also can solder everything to Nano pins or squeeze into Uno sockets, but it's easy to make a mistake this way).

Step 2: Module Modification or Discrete Sensor Wiring

For module, you must desolder resistor and capacitor, as shown on the photo. You can desolder basically everything if you want - module electronics is totally useless, we use it only as holder for the sensor itself, but these two components will prevent you from getting correct readings,

If you are using discrete sensor, attach heater pins (H1 and H2) to 5V and transistor's collector correspondingly. Attach one sensing side (any of A pins) to 5V, another sensing side (any of B pins) to 10k resistor, just like the analog pin of the module in schematics.

Step 3: Operation Principle

Why we need all these complications at all, why not to attach 5V, ground, and just get readings?
Well, you won't get anything useful this way, unfortunately.
According to MQ-7 datasheet, sensor has to run through high- and low-heating cycles in order to get proper measurements. During low temperature phase, CO is absorbed on the plate, producing meaningful data. During high temperature phase, absorbed CO and other compounds evaporate from the sensor plate, cleaning it for the next measurement.

So in general operation is simple:

1. Apply 5V for 60 seconds, don't use these readings for CO measurement.

2. Apply 1.4V for 90 seconds, use these readings for CO measurement.

3. Go to step 1.

But here's the problem: Arduino can't provide enough power to run this sensor from its pins - sensor's heater requires 150 mA, while Arduino pin can provide no more than 40 mA, so if attached directly, Arduino pin will burn and sensor still won't work. So we must use some kind of current amplifier that takes small input current to control large output current.
Another problem is getting 1.4V. The only way to reliably get this value without introducing a lot of analog components is to use PWM (Pulse Width Modulation) approach with feedback that will control output voltage.

NPN transistor solves both problems: when it is constantly turned on, voltage across the sensor is 5V and it is heating for high-temperature phase. When we apply PWM to its input, current is pulsing, then it is smoothed by the capacitor, and the average voltage is kept constant. If we use high frequency PWM (in the sketch it has frequency of 62.5KHz) and average a lot of analog readings (in the sketch we average over ~1000 readings), then the result is quite reliable.

It is critical to add capacitors according to schematics. Images here illustrate difference in signal with and without C2 capacitor: without it, PWM ripple is clearly visible and it significantly distorts readings.

Step 4: Schematics and Breadboard

Here is the schematics and breadboard assembly.

WARNING! Modification of a standard breakout module is required! Without modification module is useless. Modification is described in the second step.

It is important to use pins D9 and D10 for LEDs, since there we have outputs of hardware Timer1, it will allow to smoothly change their colors.
Pins D5 and D6 are used for buzzer, because D5 and D6 are outputs of hardware Timer0. We will configure them to be inverse one to another, so they will switch between (5V, 0V) and (0V, 5V) states, thus producing sound on buzzer. Warning: this affects Arduino's main timing interrupt, so all time-dependent functions (like millis() ) won't produce correct results in this sketch (more on this later).
Pin D3 has hardware Timer2 output connected to it (as well as D11 - but it's less convenient to put wire on D11 than on D3) - so we are using it to provide PWM for voltage controlling transistor.
Resistor R1 is used to control brightness of LEDs. It can be anywhere from 300 to 3000 Ohm, 1k is rather optimal in brightness/power consumption.
Resistor R2 is used to limit transistor's base current. It shouldn't be lower than 300 Ohms (to not overload Arduino pin), and not higher than 1500 Ohms. 1k there is a safe choice.

Resistor R3 is used in series with sensor's plate in order to create a voltage divider. Voltage on sensor's output is equal to R3 / (R3 + Rs) * 5V, where Rs is current sensor's resistance. Sensor resistance depends on CO concentration, so voltage changes accordingly.
Capacitor C1 is used to smooth input PWM voltage on MQ-7 sensor, the higher is its capacitance the better, but also it has to have low ESR - so ceramic (or tantalum) capacitor is preferred here, electrolytic one won't perform well.

Capacitor C2 is used to smooth sensor's analog output (output voltage depends on input voltage - and we have quite a high current PWM here, that affects all schematics, so we need C2). The simplest solution is to use the same capacitor as C1.
NPN transistor either conducts current all the time to provide high current on sensor's heater, or works in PWM mode thus reducing heating current.

Step 5: Arduino Program

WARNING: SENSOR REQUIRES MANUAL CALIBRATION FOR ANY PRACTICAL USE. WITHOUT CALIBRATION, DEPENDING ON PARAMETERS OF YOUR PARTICULAR SENSOR, THIS SKETCH MIGHT TURN ON ALARM IN CLEAN AIR OR NOT DETECT LETHAL CARBON MONOXIDE CONCENTRATION.

Calibration is described in the following steps. Rough calibration is very simple, precise is quite complex.

On the general level, program is rather simple:

First we calibrate our PWM in order to produce stable 1.4V required by sensor (proper PWM width depends on a lot of parameters like exact resistor values, this particular sensor's resistance, transistor's VA curve etc etc - so the best way is to try various values and use one that fits best).
Then, we continuously run through cycle of 60 seconds heating and 90 seconds measurement.
In implementation it gets somewhat complicated. We have to use hardware timers because everything that we have here needs high-frequency stable PWM in order to function properly.

The code is attached here and can be downloaded from our github, as well as schematics source in Fritzing.

In the program there are 3 functions that handle timers: setTimer0PWM, setTimer1PWM, setTimer2PWM.
Each of them sets timer in PWM mode with given parameters (commented in the code), and sets pulse width according to input values.
Measurement phases are switched using functions startMeasurementPhase and startHeatingPhase, they handle everything inside. and set proper timer values for switching between 5V and 1.4V heating.
LEDs state is set by function setLEDs which accepts green and red brightness on its input (in linear 1-100 scale) and converts it into corresponding timer setting.

Buzzer state is controlled using functions buzz_on, buzz_off, buzz_beep. On/off functions turn sound on and off, beep function produces specific beeping sequence with period of 1.5 seconds if it is periodically called (this function returns immediately so it doesn't pause the main program - but you have to call it again and again to produce beeping pattern).

Program first runs function pwm_adjust that finds out proper PWM cycle width in order to achieve 1.4V during measurement phase. Then it beeps a few times to indicate that sensor is ready, switches into measurement phase, and starts the main loop.

In the main loop, program checks if we spent enough time in current phase (90 seconds for measurement phase, 60 seconds for heating phase) and if yes, then changes current phase. Also it constantly updates sensor readings using exponential smoothing: new_value = 0.999*old_value + 0.001*new_reading. With such parameters and measuring cycle, it averages signal over approximately last 300 milliseconds.

WARNING: SENSOR REQUIRES MANUAL CALIBRATION FOR ANY PRACTICAL USE. WITHOUT CALIBRATION, DEPENDING ON PARAMETERS OF YOUR PARTICULAR SENSOR, THIS SKETCH MIGHT TURN ON ALARM IN CLEAN AIR OR NOT DETECT LETHAL CARBON MONOXIDE CONCENTRATION.

Step 6: First Run: What to Expect

If you assembled everything properly, after running sketch you will see something like this in Serial monitor:

adjusting PWM w=0, V=4.93

...

adjusting PWM w=17, V=3.57
PWM result: width 17, voltage 3.57

and then a series of numbers representing current sensor readings.
This part is adjusting PWM width in order to produce sensor's heater voltage as close to 1.4V as possible, measured voltage is deducted from 5V, so our ideal measured value is 3.6V. If this process never ends or ends after a single step (resulting in width equal to 0 or 254) - then something is wrong. Check if your transistor is really NPN and is properly connected (make sure you used base, collector, emitter pins right - base goes to D3, collector to MQ-7 and emitter to ground, don't count on Fritzing breadboard view - it is wrong for some transistors) and make sure that you connected sensor's input to Arduino's A1 input.

If everything is fine, you should see in Serial Plotter from Arduino IDE something similar to the image. Heating and measurement cycles of 60 and 90 seconds length are running one after another, with CO ppm measured and updated at the end of each cycle. You can take some open flame close to the sensor when measurement cycle is almost finished and see how it will affect readings (depending on flame type, it can produce up to 2000 ppm CO concentration in open air - so even though only a small portion of it actually goes into sensor, it still will turn on the alarm, and it won't go off until the end of the next cycle). I showed it on the image, as well as the response to fire from the lighter.

Step 7: Sensor Calibration

According to manufacturer's datasheet, sensor should be running heating-cooling cycles for 48 hours in a row before it can be calibrated. And you should do it if you intend to use it for a long time: in my case, sensor reading in clean air changed for about 30% over 10 hours. If you won't take this into account, you can get 0 ppm result where there is actually 100 ppm of CO. If you don't want to wait for 48 hours, you can monitor sensor output at the end of measurement cycle. When over an hour it won't change for more than 1-2 points - you can stop heating there.

Rough calibration:

After running sketch for at least 10 hours in clean air, take raw sensor value in the end of the measurement cycle, 2-3 seconds before heating phase starts, and write it into sensor_reading_clean_air variable (line 100). That's it. Program will estimate other sensor parameters, they won't be precise, but should be enough to distinguish between 10 and 100 ppm concentration.

Precise calibration:

I highly recommend to find a calibrated CO meter, make 100 ppm CO sample (this can be done by taking some flue gas into syringe - CO concentration there can easily be in the range of several thousands ppm - and slowly putting it into closed jar with calibrated meter and MQ-7 sensor), take raw sensor reading at this concentration and put it into sensor_reading_100_ppm_CO variable. Without this step, your ppm measurement can be wrong several times in either direction (still ok if you need alarm for dangerous CO concentration at home, where normally there should be no CO at all, but not good for any industrial application).

As I didn't have any CO meter, I used a more sophisticated approach. First I prepared high concentration of CO using combustion in isolated volume (first photo). In this paper I found the most useful data, including CO yield for different flame types - it isn't in the photo, but the final experiment used propane gas combustion, with the same setup, resulting in ~5000 ppm CO concentration. Then it was diluted 1:50 in order to achieve 100 ppm, as illustrated in the second photo, and used to determine sensor's reference point.

Step 8: Some Experimental Data

In my case, sensor worked quite well - it is not very sensitive for really low concentrations, but good enough for detecting anything higher than 50ppm. I tried to increase concentration gradually, taking measurements, and built a set of charts. There are two sets of 0ppm lines - pure green before CO exposure and yellow green after. Sensor seems to slightly change its clean air resistance after exposure, but this effect is small. It doesn't seem to be able to clearly distinguish between 8 and 15, 15 and 26, 26 and 45 ppm concentrations - but the trend is very clear, so it can tell whether concentration is in 0-20 or 40-60 ppm range. For higher concentrations dependence is much more distinctive - when exposed to exhaust of an open flame, curve goes up from the start without going down at all, and its dynamics is totally different. So for high concentrations there is no doubt that it works reliably, although I can't confirm its precision since I don't have any rated CO meter.
Also, this set of experiments was done using 20k load resistor - and after that I decided to recommend 10k as the default value, it should be more sensitive this way.

That's it. If you have a reliable CO meter and will have assembled this board, please share some feedback about sensor precision - it would be great to collect statistics over various sensors and improve default sketch assumptions.

7 People Made This Project!

Recommendations

  • STEM Contest

    STEM Contest
  • Role Playing Game Challenge

    Role Playing Game Challenge
  • CNC Contest

    CNC Contest

188 Comments

0
Immortal213
Immortal213

Question 8 days ago

Hi! Thanks for your time, explanations & everything.
Sorry I'm a newcomer to the case, so by removing the capacitor and resistor for module, you mean R3 and C2 or...?

0
the_3d6
the_3d6

Answer 5 days ago

The module I had had no markings on it - so not sure how they are marked. But if it looks visually the same, just with markings - then I bet it's them

0
GanzorigC2
GanzorigC2

1 year ago

Hi, this is a nice job. I'm going to use discrete MQ7 sensor and Arduino Uno. Can you upload or send schematics? Thanks. I tried to use IRF540N. But failed. Save me.

0
MedMuffin
MedMuffin

Reply 6 weeks ago

Look at the datasheet of IRF540N its a NPN Mosfet, but you need a Gate Voltage of around 10V.... Arduino just has 5V to offer...

0
the_3d6
the_3d6

Reply 6 weeks ago

Looking at figure 7 of its datasheet, I'd say it kinda could work (that figure is for very low duty cycle though, but current here is like 2 orders of magnitude less than there), but R2 must be replaced with a wire in that case - with mosfets, it's best to drive gate directly from Arduino pin. Still, this mosfet is a really wrong choice here, something with rated current of several amps would be much more fitting

0
murthybalaji
murthybalaji

Question 2 months ago

Can we use capacitors less than 10uF?

0
the_3d6
the_3d6

Answer 2 months ago

Yes, 1uF should work well enough, as for less - still better than nothing :)

0
BCL Beacon
BCL Beacon

5 months ago

Hello, Thanks for your instructable. I learned a lot. But after trying and reading various manuals I came upon the following. According to all datasheets the voltage on the measuring side should be 5 V during both pases. So the circuits for the heating side and the measuring side can not be the same. The datasheets also mention a slope of the measurement. This slope can be seen in your recordings. It looks like building up a layer of gasproducts during the measurement phase. That leads to the conclusion that not the endpoint but the slope or speed is a measure for the gas content in the air. My measurements on this basis are much more stable than with your method.

0
the_3d6
the_3d6

Reply 5 months ago

If the voltage would be 5V in "both phases" then there won't be any phases at all :) The whole purpose of changing voltage is to change the temperature of the sensitive element.
Not sure what datasheets you've read, but the only one I saw mentioned that quite clearly. Almost all examples showing this sensor indeed use only 5V without any change - but all of them are wrong, unfortunately.

Speed of change is more informative than the equilibrium point, you are right about that - but for using it, several precisely measured CO samples and multiple sensors are required to (a) well, measure the speed :) and (b) check their repeatability and standard deviation. Instead, I was relying on equilibrium point from the sensor datasheet.

Please do share the documents you are referring to and your data! If there are reliable measurements of the slope for a variety of sensors for precisely known concentrations, it would make possible to make the system much more responsive (and more energy efficient if needed)

0
BCL Beacon
BCL Beacon

Reply 5 months ago

In the WINSEN MQ-7 manual a basic circuit is shown. Two circuits can be distinguished: a heating element and a measuring element. The changing of voltage regards only the heating element. Also the HANWEI datasheet shows two circuits.
Please give details of the datasheets you used.
The measurement of one point of the process disregards the starting point after the heating phase. When the process is stable the starting point will always have the same value so one point is then enough as reference for the CO content. But why not choose the better way? You only have to measue twice: some time after starting the measuring phase to avoid the starting process and the second measurement at the end.

0
the_3d6
the_3d6

Reply 5 months ago

The datasheet I've used is actually in the text, this one: https://www.sparkfun.com/datasheets/Sensors/Biometric/MQ-7.pdf , but it's basically the same. New one states exactly the same thing - heating voltage must be switched between high and low states for cleaning the sensor and then performing the measurement. Measuring would be simpler if it was a standalone MQ7 sensor, but I had only an MQ7 module at hand which had heating and measuring sides internally connected - so had to work with 1.4V while measuring. Still, it doesn't really change anything - just a different reference voltage in conversion formula, we are measuring resistance of the sensitive element after all.

A better way to measure is absolutely possible - but I didn't have anything close to reliable CO source, nor time for more experiments (I spent 4 hours initially since I was in a hurry and that was only CO measuring tool available to me at the moment - I had to go into that house, adjust the boiler and make sure it won't try to kill anyone else, successful so far), and then I had one day to document the process and perform experiments. All the attention went far beyond the effort - as I see it, not because my solution is good, but because a lot of popular ones are plain wrong.

Although one radical improvement would be to introduce temperature and humidity sensor - those parameters affect readings _really_ a lot, like +-30 ppm CO concentration or something around that.

0
BCL Beacon
BCL Beacon

Reply 5 months ago

I don't blame you. Please feel not offended. You did a great job. I accept that your solution is good four your purpose and in the time you had. And I appreciate very much your efforts to correct the false solutions, make an working instrument including calibrations and to communicate it to the world. My words are just some reflections and a result of more means and more time for experiments. You are absolutely right regarding the need to correct the measurements for factors in the environment.

0
FLYINGSC0T
FLYINGSC0T

1 year ago

Hi, this is a great project. Can anyone help in porting it across to an ESP32 ?
Thanks in advance for any help/advice.

0
MidasG1
MidasG1

1 year ago

Hello there, I'm building this and I'm impressed with the code you posted.

There is one thing I don't quite understand. While there is a heating and a measurement phase, why do you still take samples in the main loop during the heating phase? The whole averaging of analog readings of the A0 pin continues in both phases. Shouldn't you just take the samples during measuring phase?

0
haczyk84
haczyk84

1 year ago

Check your ventilation, especially fresh air inlet. Also some boilers have temperature sensor that should sense reverse exhaust gases moving to room, check if you have and if it works. Anyway I will try to run my sensor, I plan to add some inductor for better 1,4V also I'm not sure if keying GND is proper way for this board.

0
clauDIUX_7
clauDIUX_7

1 year ago

Hola pueden proporcionarme codigo PWM ?

0
clauDIUX_7
clauDIUX_7

1 year ago

No sabía este detalle de este sensor. Daba por seguro que el montaje ya era seguro.. Bueno, estoy tratando de generar PWM, para una aplicación similar de otro sensor TGS8100 pero tuve pequeños problemas a la hora de implementar; soy nuevo en el tema, alguien me puede apoyar con el ejemplo de PWM con los temporizadores internos del arduino ?. Saludos

0
avianshgaur
avianshgaur

1 year ago

I have made a connection as shown in the circuit. And I have uploaded the same code. But, despite that the values of voltage is 1.99 V and 4.1 V(not 1.4 V and 5 V(measured by a multimeter between Vcc and GND of MQ-7) ).
I have tried changing values of 1k ohm resistor and have removed the connection as shown in step 2. B0139 is the transistor.
Is this correct?

0
evoh1
evoh1

1 year ago

Hi! Really nice project! I want to add few CO meters in my house (you never know) and I was wondering, if I could send readings through serial or it will disturb timers? Sorry, I don't know much about them.
It would send readings during heating period to esp (via serial) and esp would publish to mqtt server and display readings in HASS. Later maybe also trigger some push notifications or flash ceiling lights, just thinkig...
I am prepared to share code, PCB and STL files then if you want.
Again, thanks for a great project!

0
sumitkanu13
sumitkanu13

1 year ago

Hi! Thanks for the great instructions. Can you provide some data proving that the circuit that you designed is better than the circuit provided by the sensor. Thanks.