MQ135 Arduino library

A project log for Sniffing Trinket

A room climate monitor

Georg KrockerGeorg Krocker 01/01/2015 at 14:5720 Comments

This is the first of two logs about the software needed for the SniffingTrinket project. Because I guess that a general Arduino library for the MQ135 might be of use for other projects, I decided to split the software in a dedicated library and a sketch specifically for the SniffingTrinket. So, this is solely about the MQ135 lib, which, btw. you can find in my GitHub.

First, some theory: My interest in those cheap gas sensors was sparked by a video from Ben Krasnow who takes a look at a cheap breathalyzer with a very similar sensor. He links a paper in his blog post that goes into the theory of the operation of these sensors. In short, they consist of a heated piece of tin dioxide (doped with other stuff) that changes resistance as it reacts with the ambient gas (the heat accelerates the chemical reaction). There are several versions of these sensor, sensitive to several gas types. However, no sensor material is sensitive to a single gas only! I did some more research and found in principle two sensor of immediate interest to me: The MQ135 for "air quality" and the MQ811 for CO2. The MQ811 is much more expensive (30$ compared to 3$).

The datasheet claims that the MQ135 is sensitive for CO2, Alkohol, Benzene, NOx, NH3 and the Fig 3 shows the change in resistance depending on the concentration of these gases in the ambient air in ppm (of the total gas volume). It turns out the the general sensitivity is the roughly same for all the gases. This is where you think: Wait a minute! CO2 is the 4th most abundant trace gas in the earths atmosphere with about 400ppm concentration (N2, O2, and Ar are on place 1-3). All of the other gases the sensor detects are much less common than CO2 and luckily so, as they are harmful. This means, in a normal atmosphere the sensor mostly detects CO2 and with the right calibration we can use it as a cheap replacement for the MQ811 (which by the way is also sensitive to other gases). Turns out, somebody already hat a go at this: I will leave you with great explanation of David Gironi for the details, but mostly he just extracts data from the figures in the datasheet, throws some math at the problem and cross-checks the results with a different kind of sensor for CO2.

The Library I have written basically just implements his approach for the Arduino. It's pretty easy to use. Copy it to your Arduino library folder and put

#include "MQ135.h"

MQ135 gasSensor = MQ135(ANALOGPIN);

to your sketch. ANALOGPIN is the ADC input for the sensor readings (refer to the post with the SniffingTrinket schematic for explanation).

Before you can use the sensor, it has to be calibrated. For this, connect the sensor to your circuit and leave it powered on for 12-24 h to burn it in. Then put it into outside air, preferably at 20°C/35% rel. hum. (humidity is not so crucial). Read out the calibration value as such

float rzero = gasSensor.getRZero();

Wait until the value has somewhat settled (30min-1h). Remember, this is an ADC measurement so you might not want to wait some time between reading the sensor and also do some averaging. Once you have determined your RZero, put it into the MQ135.h. Note: Different sensors will likely have different RZero!

#define RZERO 76.63

Congrats, you have calibrated the sensor and can now read the CO2 ppm value in your sketch

float ppm = gasSensor.getPPM();
The library also provides functions to apply the temperature/humidity correction that is shown in Fig 4 of the datasheet, but I do not trust the datasheet there, so this should not be used for the moment.

If you want to know more, look at the code. I tried to make it very readable and self explaining and provided lots of comments. If you do not understand something, ask: I will happily explain it.


alexandru-2016 wrote 05/02/2020 at 22:39 point

Very good post, considering there is so little original content for using this module, and most people just duplicate what the datasheet says, without understanding it.

May I suggest including this post in the readme of github. I first found your library there but this post was useful to understand it.

  Are you sure? yes | no

Robert Wisbey wrote 04/25/2020 at 16:14 point

Hi George,

Great post, and useful info for obtaining the R0 value of the MQ135.

However, I have a question based on the data extrapolation taken by  David Gironi (who I also need to contact).

He extrapolates to 116.6020682 and -2.769034857 (i.e. ppm = 116.6020682 (Rs/Ro)^-2.769034857), which you implement with your PARA and PARB definitions.

However, the is other work on Github :, where we see in their data extrapolation of the MQ135, they obtain 110.47 and -2.862 for CO2 (see below) (they also extrapolate for other gases).

  MQ135.setA(110.47); MQ135.setB(-2.862); // Configurate the ecuation values to get CO2 concentration

  float CO2 = MQ135.readSensor(); // Sensor will read PPM concentration using the model and a and b values setted before or in the setup

I'm curious to know the reasons to the differences of these data extrapolations.

Have you followed this, and do you know why they have deduced different extrapolations for CO2 ?

With Well Wishes,


  Are you sure? yes | no

Hakan Gurkanli wrote 07/14/2019 at 10:31 point

Hello, I am not feeling comfortable with electronics and still got tons of things to learn. I would be very happy if you can help me out. I made my sensor gather data for 24 hours but as soon as I get the RZero value, there is unexpected thing for me happened. When I breath on the sensor or use my lighter gas on it the value decreases for instance, in normal mode it gives me the value of 7233, when I use my lighter on it it decreases to 400. Is it normal or am I doing something wrong?

  Are you sure? yes | no

maurodipa wrote 02/26/2019 at 15:15 point

Hey, anybody there that can help me? I am blocked on this issue. Once I calibrate the sensor and find the RZERO, I would expect to find the ppm I used for calculating RZERO (ie 410) if I do a reading right after the calibration. Instead I get an important different value (around 330 ppm). Where I am wrong? 

  Are you sure? yes | no

maurodipa wrote 02/24/2019 at 01:33 point

Hi Georg,

I just did all the work: I attached an MQ135 to an external power and the Arduino Uno to the usb port. Than, I downloaded the library, I changed the RLOAD value to 1.0 (for my MQ135 board comes from aliexpress and has the 1 kohms RLOAD), I corrected the formula for get.Resistance as you indicated and the line #define ATMOCO2 410.92 with the current value I got from Than I run the sketch in plain air for 1 hour in order to find the right value of RZERO. Once I found RZERO=47.28, I changed line 27 in the MQ135.h file with the line #define RZERO 47.28.
Finally, running the script
        float ppm = gasSensor.getPPM();
        Serial.print("ppm = "); Serial.println(ppm);
I expected to find the 410.92 value ppm should have in plain air.
Instead I get a 297 value for ppm.
Can you help me to understand where I am wrong, please? 
Many thanks.

  Are you sure? yes | no

Jimmy Xiong wrote 12/25/2018 at 12:52 point

The unit of RLOAD and RZERO are not important.  The value of RZERO is just calibrated relative to RLOAD, and those absolute values don't affect ppm. So we can put RLOAD to 10.0, or just 1.0, that's okay.

  Are you sure? yes | no

dipkaushik12 wrote 07/17/2018 at 03:22 point

Can someone post the code because I don't have any idea about where and when to use those commands, its not because I don't understand English or new to Arduino, its because I have not used such libraries before. Thanks for this livesaving tutorial and THANK YOU! in advance who ever helps me out! 

  Are you sure? yes | no

dediggefedde wrote 04/26/2018 at 21:08 point

Hi! Thanks for the great tutorial!

After burning in and measuring outside for an hour to get a better RZero (+94 kOhm) I get negative values from getPPM(). Sometimes it becomes positive for some time and around 150 to 50... I made sure to apply 5V to the VC and used the library
I also tried using, but then RZero also got negative (-270 kOhm)...

Is there still a user-error or is this normal behavior and I should put more averaging & dropping negative values?

  Are you sure? yes | no

Tanya wrote 02/23/2017 at 14:53 point

How to calibrate my MQ135 sensor to give no2 concentration ?

  Are you sure? yes | no

Tanya wrote 02/23/2017 at 14:53 point


  Are you sure? yes | no

Bub Rascal wrote 01/13/2017 at 12:08 point

Any updates on the issue with the alleged "wrong sensor resistance calculation" in the formula in your library ?

((1023./(float)val) - 1.)*RLOAD


((1023./(float)val) * 5. - 1.)*RLOAD;

the ppm results vary enormously

could it be the explanation to the comment in the code "his currently relies on the datasheet but the information there seems to be wrong. "

  Are you sure? yes | no

Swen wrote 12/22/2016 at 15:40 point


rzero bring me a value of 1000... If i use this i get ppm value more than 4000-5000. If i read out analog value i get 130 in room.

I don't understand to calibrate it right. I put this out of window for more then 30min and i get a value of 1000. If i use this, i get totally strange values for ppm.

Are my sensor bad or did i something wrong? I sit here 5 days and i don't get any well value indoor.


In the most libs there are wrong resistor calculation. We would find the resistor of measuring.

But we get voltage. In both libs also in Vilius. 

float MQ135::getResistance() {
  int val = analogRead(_pin);
  return ((1023./(float)val) - 1.)*RLOAD;

This is right now! You don't need 5. 1023 is max digital reading of analog pin. And the readed val is the now readed digital value. 1023 is max 5V so there is no need for calc with 5!

The right formular was R1 = (5V/(sensorValue * (5.0 / 1023.0))-1)*RLOAD

Heureka to much complicated i think.

So you get the write resistance with this:

return ((1023./(float)val) - 1.)*RLOAD;

That is one point! Another Point is R2. R2 is on my PCB 1K. You calculate with ohms not with kilo ohms so it's important that you type not 10 in settings... This are 10 ohms. RLOAD is then 10 ohms. That is much too less... I use 1000! 1k = 1000. RZERO is then very very high. You get something about 25000... The calculated resistance is round about 16k

So I get this:

MQ135 RZero: 25434.99 Resistance: 16338.98 PPM: 397.13

I use at this time Vilius lib and the values looks now well!

But! I think the measures in room will be wrong and too low. Will test this now over few days but i get very low measures. I have a reference and what i think is that this MQ135 reads well if you breathe direct into it. It's nice to build a alcoholic tester. But i will wait a few days if CO2 will be increase to the right values i measure with my weather station from Netatmo.

Netatmo use IR for measuring and i get complete far away values from MQ135. As this artical writes there are more sensors much expensive and you can riddle now why. ;)

  Are you sure? yes | no

Vilius Kraujutis wrote 12/17/2016 at 12:27 point

I've merged all changes from other repositories and made a PR to main repository. George, please merge my changes from


  Are you sure? yes | no

Christian KUNST wrote 07/31/2016 at 14:52 point

Hi to all of you. I have added a small print with the sensor mounted and connectd it to 

the arduino pin 1. THen used the step as described, but get only out values around 0.2.

If RL is different can I just change the value in the library?

How can I transmit air temperature und humidity to the function. Is there any exaple?
Best regards and thanks from Vienna

  Are you sure? yes | no

christian.gassner wrote 08/01/2016 at 18:54 point

Started yesterday and had also very low readings between 0.2 and 0.7. I let it burn in for about 24h as described and did the calibration afterwards, which gave me a RZERO value of 1018.5. Way more than it was initially. With this calibration the getPPM() now returns reasonable values of about 500 ppm indoors (but very fresh air with open windows), which quickly increase to 800-1100 if I breath at the sensor. Can you experience similar behaviour ? Outdoors it shows values between 380 and 400 ppm (20°C at night)

EDIT: It seems that my sensor needed a few days for the burn in. Now the readings with the 1018.5 rzero are ways to high (4000-5000+). I will do the calibration again today at evening and expect a much lower rzero.

Greetings from Munich :)


  Are you sure? yes | no

Duarte wrote 04/25/2016 at 23:17 point

Hello, I did everything that was supposed to with regard to calibration but when I read the CO2 ppm value, sometimes the Arduino show small values (2 to 3 ppm) and a seconds after, show great values (moreless 680ppm, that's more realistic, I think)... It returns to show a small values and so on... I don't understand why, can you help me?

  Are you sure? yes | no

jasonrlester wrote 02/28/2016 at 20:39 point

Has anybody been able to confirm the accuracy of the MQ135?

  Are you sure? yes | no wrote 10/16/2015 at 19:07 point


how can i correct ppm with dht22?

  Are you sure? yes | no

nicomueller1991 wrote 09/09/2015 at 20:44 point

how can i do calibration with another temperature?

  Are you sure? yes | no

stano.horvath wrote 02/24/2015 at 22:15 point


I would like to thank you for the guidance of cheap CO2 sensor, but I have few questions. 

I have followed your instructions and my value rzero = 583.12. My senzor is maybe little different, but is it not too much?

In your code is RLOAD = 10.0.  How do I know my value? On the board is potentiometer. Is it a constant for all sensors or adjustable value?

When I change RLOAD I need change PARA and PARB?

What is range ppm for CO2 for this sensor? (10 - 1000ppm?)

My sensor show value in range 400-12000. 

12000 is the maximum and it is too much for the living room.

Please help me calibrate my sensor for my little meteostation.

If you give me an email I will send screenshots and a link to my meteostation.

Thank you for your help .

Sincerely Stano

  Are you sure? yes | no