GeorgK/MQ135

Wrong sensor resistance calculation

Opened this issue · 8 comments

lorf commented

It seems that formula for calculating sensor resistance in getResistance() has an error, 5.0 multiplier (probably analog reference voltage) is unnecessary. The formula should read:

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

Why should it be unnecessary? If you have a look at the official web site you can see that they use the same formula
https://www.arduino.cc/en/Tutorial/ReadAnalogVoltage

lorf commented

The formula in the tutorial is for the voltage on the analog pin, but we are calculating resistance (of the one member of the voltage divider):

Sensor resistance and Rload form a voltage divider, which can be described with the following formula:

Rload/Vadc = (Rsensor+Rload)/Vref

where Vref is our 5 V reference voltage, Vadc is the voltage on the ADC pin. To find our sensor resistance:

Rsensor = Rload * (Vref/Vadc - 1)

We can replace Vref/Vadc relation with ADCmaxvalue/ADCvalue relation (where ADCmaxvalue is usually 1023 for Arduino and always represents ADC value when Vadc = Vref, ADCvalue is proportional to Vadc), so we get:

Rsensor = Rload * (ADCmaxvalue/ADCvalue - 1)

or

Rsensor = Rload * (1023/ADCvalue - 1)

The formulas for a voltage divider are described e.g. here.

Very good explanation, thank you! I wonder why this issue is open for over one year!?

After recalculation I agree, the 5V should vanish. Even simpler: Dimension analysis reveals, that within the bracket there should be no units.

a-x- commented

Are there any another well-known mistakes in the GeorgeK’s code?

Do you know the best fork?
Probably, https://github.com/Gazz-yniere/MQ135, isn’t?
UPD: Hm, @Gazz-yniere’s code had lorf’s fix, but currently it looks wrong again:
https://github.com/Gazz-yniere/MQ135/blob/master/MQ135.cpp#L63-L67

Are there any another well-known mistakes in the GeorgeK’s code?

Do you know the best fork? Probably, https://github.com/Gazz-yniere/MQ135, isn’t? UPD: Hm, @Gazz-yniere’s code had lorf’s fix, but currently it looks wrong again: https://github.com/Gazz-yniere/MQ135/blob/master/MQ135.cpp#L63-L67

I am also trying this code and using the following formula:
return ((1023./(float)val) * 5. - 1.)*RLOAD;

I get between 330 and 410ppm in the office where I am working.
If I remove the * 5., I get kind of weird 75000 to 86000+ values!
I'm not sure any more about the correct formula!

I get between 330 and 410ppm in the office where I am working.
If I remove the * 5., I get kind of weird 75000 to 86000+ values!
I'm not sure any more about the correct formula!

I'm also currently maintaining a fork. Is there any consensus yet on how the correct formula should look like? Unfortunately, I don't have a sensor module here to test it (ironically). @lorf's explanation definitely makes sense though. Another point that has bothered me for quite some time is that the ADC resolution is hard-coded here and thus only works if you use a 10-bit ADC. I don't think there is a way to read the used ADC resolution set via analogReadResolution(), is there?

the ADC resolution depends of you VCC value, because analogReadResolution() return the tension depending of VCC. In my project I use 5volts.
And if you want to have a good value of RS, the sensor needs to be ready after some time.
You have some informations on the datasheet https://www.olimex.com/Products/Components/Sensors/Gas/SNS-MQ135/resources/SNS-MQ135.pdf
other very useful informations for the sensor https://www.codrey.com/electronic-circuits/how-to-use-mq-135-gas-sensor/