Arduino CO Monitor Using MQ-7 Sensor

59,135

70

173

About: I love cyberpunk and work as an electronics engineer, not much to add here

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.

Teacher Notes

Teachers! Did you use this instructable in your classroom?
Add a Teacher Note to share how you incorporated it into your lesson.

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.

6 People Made This Project!

Recommendations

  • Assistive Tech Contest

    Assistive Tech Contest
  • Reuse Contest

    Reuse Contest
  • Made with Math Contest

    Made with Math Contest

173 Discussions

0
None
clauDIUX_7

4 months ago

Hola pueden proporcionarme codigo PWM ?

0
None
clauDIUX_7

4 months 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
None
avianshgaur

5 months 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
None
evoh1

5 months 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
None
sumitkanu13

5 months 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.

1 reply
0
None
the_3d6sumitkanu13

Reply 5 months ago

Hi! I don't know where to start. Have you read sensor's datasheet and have you looked at the module schematics? I did both and found no way to make measurements in a way described in the datasheet using this module - and so I've developed a way to follow datasheet's procedure and described it here

0
None
ciakki_g

Question 6 months ago

Hello, thank you so much for your instructable!
I'm trying to use the discrete version of the MQ-7 sensor along with the Particle Photon and its weather shield; I also have its breakout board, but it's optional. The point is, the Photon runs on 3.3V, while your contraption seems to rely on a 5V source in order to power the sensor heater. I'm also powering my project with a single-cell LiPo battery, which outputs 3.7V. How can I get it to work?

2 answers
2
None
the_3d6ciakki_g

Answer 6 months ago

You will need a significantly different schematics for this case to make it optimal.
With discrete sensor, you want to completely separate heating and measuring circuits.

For measuring part, it's simple: you connect Particle's 3.3V to one side of sensing wire, connect other side of sensing wire to analog input and to the ground via reference 10k resistor (R3 in current schematics). To get more stable readings you can add small (100-1000 nF) capacitor in parallel with this resistor.

Heating part is more tricky. You want to connect one side of heating coil directly to LiPo positive side (to avoid power loss, LiPo voltage should be enough for the heating phase even when it's discharged). Another side of the heating coil should be connected to transistor that further goes to the ground (Q1 in current schematics), but you can't connect this side directly to Particle's analog input: voltage there will be too high and it would damage it. You need to divide it with two 100k resistors: one resistor is connected to heating coil and analog input, second connected to that analog input and ground. And also you will need a capacitor (~1uF) connected in parallel with this second resistor.
In this schematics, measured voltage should be multiplied by 2 in the program to get real voltage on the heating coil.

0
None
ciakki_gthe_3d6

Reply 6 months ago

Thanks!
How's the power consumption for the sensor heater on this version of the circuit? The battery I'm using is 6600mAh and recharged by a 3.5W solar panel through the Sunny Buddy: is my project feasible for continuous usage without any external (USB) recharge?

0
None
th3o_s

Question 6 months ago

Hi there! I am actually trying to test your project because it might really helpful for my work. It looks like that there is no mistake on my montage (I even check twice the datasheet of the NPN transistor) but my value is kind of strange, as you can see on the graph's screenshot. I don't really understand why my value is around 840 at higher level and at ~70 at the lowest level. I do believe that the drop on the blue line is not normal, right?

Graph screenshot.png
10 answers
0
None
the_3d6th3o_s

Answer 6 months ago

Yes, it definitely is wrong. It looks like you have some connection between sensing pin and heating coil. Have you removed resistor from the sensor module as in step (2)?

0
None
th3o_sthe_3d6

Reply 6 months ago

here is a picture of my sensor did I do something wrong ?

sensor picture.jpg
0
None
the_3d6th3o_s

Reply 6 months ago

No, it is fine, problem is elsewhere. And what it prints during initial adjustment phase?

0
None
the_3d6th3o_s

Reply 6 months ago

Yes, and it looks good... Try different R3 values - 1k and 100k instead of its current value. Maybe your sensor has parameters that are way too different from mine, then it could help

0
None
th3o_sthe_3d6

Reply 6 months ago

okay so i did what you said and it looks like the 1K resistor is the worst one and 100K the best one, i think that i will try higher to be sure. (pic 1: 1k and pic 2: 100K)

Sensor 1K resistor.pngSensor 100K resistor.png
0
None
th3o_sth3o_s

Reply 6 months ago

but it also looks like that my value increase too :/

0
None
the_3d6th3o_s

Reply 6 months ago

...I think I see the problem. What capacitors you have used as C1, C2? Value and type? If they are large electrolytic caps - then most probably they just don't work

0
None
th3o_sthe_3d6

Reply 6 months ago

I am using two capacitors of 10 UF, I use the same capacitor as the picture I think it is what you are talking about, (I am french so I am not sure of the name :))

capacitor.jpg
0
None
the_3d6th3o_s

Reply 6 months ago

These capacitors don't work here. They have too high internal resistance to provide filtering. Please use tantalum ones, or if you don't have any - ceramic. If you don't have 10uF, try using smaller value, probably 1uF still would work.

Also, another important point: how you are powering the whole setup? If you use Arduino's Vin pin and battery - then it might not work depending on exact Arduino model - heating coil requires too much current and it will distort analog readings. Use USB power instead