Skip to content
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo
  1. Home
  2. Announcements
  3. 💬 Gas Sensor

💬 Gas Sensor

Scheduled Pinned Locked Moved Announcements
18 Posts 9 Posters 4.4k Views 9 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • skywatchS Offline
    skywatchS Offline
    skywatch
    wrote on last edited by
    #5

    HI Gohan,

    Yes I did, seems that they are very expensive wherever I look. I'll wait on this one ofr now.

    Thanks for the suggestion though :)

    1 Reply Last reply
    0
    • gohanG Offline
      gohanG Offline
      gohan
      Mod
      wrote on last edited by
      #6

      I believe it is on the cheap side. These price ranges are mainly for hobbyist sensor. The real calibrated ones are usually way more expensive. The cheap ones are only good to measure trends, not for the actual value.

      1 Reply Last reply
      0
      • A Offline
        A Offline
        APL2017
        wrote on last edited by
        #7

        Please advise what would be curve for MQ135 sensor for CO2. Thank you.

        mfalkviddM 1 Reply Last reply
        0
        • A APL2017

          Please advise what would be curve for MQ135 sensor for CO2. Thank you.

          mfalkviddM Offline
          mfalkviddM Offline
          mfalkvidd
          Mod
          wrote on last edited by
          #8

          @apl2017 itsn't the curve in the datasheet linked on the build page sifficient?

          1 Reply Last reply
          0
          • A Offline
            A Offline
            APL2017
            wrote on last edited by
            #9

            Sure, the curve is there. I am trying to make sense out of two points curve that is used in sketch for MQ2 sensor, CO gas. "point1: (lg200, 0.72), point2: (lg10000, 0.15)" while I see on the curve from datasheet (lg200, 5.2) and (lg10000, 1.3). That is why I am asking for help, sorry for not making it clear.

            1 Reply Last reply
            1
            • A Offline
              A Offline
              APL2017
              wrote on last edited by
              #10

              Lack of support makes you think, so I figured out the "hidden" calculations in the sketch above and proposing the code modification to allow users to port the code to a different gas sensor using data taken directly from the sensor chart. Here are modified fragments of the code showing only CO curve for MQ2 sensor:

              float CO2Curve[4] = {0.8,200,2.2,10};
              //two end points are taken from the curve with two coordinates for each point
              //in form of{Y1, X1,Y2, X2) where X is PPM axis, Y is Rs/Ro axis from the sensor chart for specifc gas
              //then equation X=(Y-b)/m is used,
              //where m is slope of the curve, b is Y axis intersept point
              float Slope = (log(pcurve(1)-log(pcurve(0))/(log(pcurve(3)-log(pcurve(2));
              float Y_Intercept=log(pcurve(1)-Slope*log(pcurve(3);

              int MQGetPercentage(float rs_ro_ratio, float *pcurve)
              {
              { return (pow(10,((log(rs_ro_ratio)-Y_Intercept)/Slope))); }
              }

              1 Reply Last reply
              1
              • gohanG Offline
                gohanG Offline
                gohan
                Mod
                wrote on last edited by
                #11

                I have found this sketch for MQ7 sensors

                /*
                This code was developed by the_3d6 from Ultimate Robotics (http://ultimaterobotics.com.ua).
                License: you can use it for any purpose as long as you don't claim that you are its author and
                you don't alter License terms and formulations (lines 1-10 of this file).
                You can share modifications of the code if you have properly tested their functionality,
                including confirming correct sensor response on CO concentrations of 0-30ppm, 100-300ppm and 1000-10000ppm.
                If you can improve this code, please do so!
                You can contact me at aka.3d6@gmail.com
                */
                
                
                //WARNING! Each sensor is different!
                //You MUST calibrate sensor manually and
                //set proper sensor_reading_clean_air value before using
                //it for any practical purpose!
                
                int time_scale = 8; //time scale: we altered main system timer, so now all functions like millis(), delay() etc 
                					//will think that time moves 8 times faster than it really is
                					//to correct that, we use time_scale variable:
                					//in order to make delay for 1 second, now
                					//we call delay(1000*time_scale)
                
                void setTimer0PWM(byte chA, byte chB) //pins D5 and D6
                {
                	TCCR0A = 0b10110011; //OCA normal,OCB inverted, fast pwm
                	TCCR0B = 0b010; //8 prescaler - instead of system's default 64 prescaler - thus time moves 8 times faster
                	OCR0A = chA; //0..255
                	OCR0B = chB;
                }
                
                
                void setTimer2PWM(byte chA, byte chB) //pins D11 and D3
                {
                	TCCR2A = 0b10100011; //OCA,OCB, fast pwm
                	TCCR2B = 0b001; //no prescaler
                	OCR2A = chA; //0..255
                	OCR2B = chB;
                }
                
                void setTimer1PWM(int chA, int chB) //pins D9 and D10
                {
                	TCCR1A = 0b10100011; //OCA,OCB, fast pwm
                	TCCR1B = 0b1001; //no prescaler
                	OCR1A = chA; //0..1023
                	OCR1B = chB;
                }
                
                float opt_voltage = 0;
                byte opt_width = 240; //default reasonable value
                
                void pwm_adjust()
                //this function tries various PWM cycle widths and prints resulting
                //voltage for each attempt, then selects best fitting one and
                //this value is used in the program later
                {
                	float previous_v = 5.0; //voltage at previous attempt
                	float raw2v = 5.0 / 1024.0;//coefficient to convert Arduino's 
                							   //analogRead result into voltage in volts
                	for (int w = 0; w < 250; w++)
                	{
                		setTimer2PWM(0, w);
                		float avg_v = 0;
                		for (int x = 0; x < 100; x++) //measure over about 100ms to ensure stable result
                		{
                			avg_v += analogRead(A1);
                			delay(time_scale);
                		}
                		avg_v *= 0.01;
                		avg_v *= raw2v;
                		Serial.print("adjusting PWM w=");
                		Serial.print(w);
                		Serial.print(", V=");
                		Serial.println(avg_v);
                		if (avg_v < 3.6 && previous_v > 3.6) //we found optimal width
                		{
                			float dnew = 3.6 - avg_v; //now we need to find if current one
                			float dprev = previous_v - 3.6;//or previous one is better
                			if (dnew < dprev) //if new one is closer to 1.4 then return it
                			{
                				opt_voltage = avg_v;
                				opt_width = w;
                				return;
                			}
                			else //else return previous one
                			{
                				opt_voltage = previous_v;
                				opt_width = w - 1;
                				return;
                			}
                		}
                		previous_v = avg_v;
                	}
                }
                
                float alarm_ppm_threshold = 100; //threshold CO concentration for buzzer alarm to be turned on,
                float red_threshold = 40; //threshold when green LED is turned on red turns on
                float reference_resistor_kOhm = 10.0; //fill correct resisor value if you are using not 10k reference
                
                float sensor_reading_clean_air = 600; //fill raw sensor value at the end of measurement phase (before heating starts) in clean air here! That is critical for proper calculation
                float sensor_reading_100_ppm_CO = -1; //if you can measure it 
                									  //using some CO meter or precisely calculated CO sample, then fill it here
                									  //otherwise leave -1, default values will be used in this case
                
                float sensor_100ppm_CO_resistance_kOhm; //calculated from sensor_reading_100_ppm_CO variable
                float sensor_base_resistance_kOhm; //calculated from sensor_reading_clean_air variable
                
                byte phase = 0; //1 - high voltage, 0 - low voltage, we start from measuring
                unsigned long prev_ms = 0; //milliseconds in previous cycle
                unsigned long sec10 = 0; //this timer is updated 10 times per second,
                						 //when it will overflow, program might freeze or behave incorrectly. 
                						 //It will happen only after ~13 years of operation. Still, 
                						 //if you'll ever use this code in industrial application,
                						 //please take care of overflow problem 
                unsigned long high_on = 0; //time when we started high temperature cycle
                unsigned long low_on = 0; //time when we started low temperature cycle
                unsigned long last_print = 0; //time when we last printed message in serial
                
                float sens_val = 0; //current smoothed sensor value
                float last_CO_ppm_measurement = 0; //CO concentration at the end of previous measurement cycle
                
                float raw_value_to_CO_ppm(float value)
                {
                	if (value < 1) return -1; //wrong input value
                	sensor_base_resistance_kOhm = reference_resistor_kOhm * 1023 / sensor_reading_clean_air - reference_resistor_kOhm;
                	if (sensor_reading_100_ppm_CO > 0)
                	{
                		sensor_100ppm_CO_resistance_kOhm = reference_resistor_kOhm * 1023 / sensor_reading_100_ppm_CO - reference_resistor_kOhm;
                	}
                	else
                	{
                		sensor_100ppm_CO_resistance_kOhm = sensor_base_resistance_kOhm * 0.5;
                		//This seems to contradict manufacturer's datasheet, but for my sensor it 
                		//looks quite real using CO concentration produced by CH4 flame according to
                		//this paper: http://www.iafss.org/publications/fss/8/1013/view/fss_8-1013.pdf 
                		//my experiments were very rough though, so I could have overestimated CO concentration significantly
                		//if you have calibrated sensor to produce reference 100 ppm CO, then
                		//use it instead    
                	}
                	float sensor_R_kOhm = reference_resistor_kOhm * 1023 / value - reference_resistor_kOhm;
                	float R_relation = sensor_100ppm_CO_resistance_kOhm / sensor_R_kOhm;
                	float CO_ppm = 100 * (exp(R_relation) - 1.648);
                	if (CO_ppm < 0) CO_ppm = 0;
                	return CO_ppm;
                }
                
                void startMeasurementPhase()
                {
                	phase = 0;
                	low_on = sec10;
                	setTimer2PWM(0, opt_width);
                }
                
                void startHeatingPhase()
                {
                	phase = 1;
                	high_on = sec10;
                	setTimer2PWM(0, 255);
                }
                void setLEDs(int br_green, int br_red)
                {
                	if (br_red < 0) br_red = 0;
                	if (br_red > 100) br_red = 100;
                	if (br_green < 0) br_green = 0;
                	if (br_green > 100) br_green = 100;
                
                	float br = br_red;
                	br *= 0.01;
                	br = (exp(br) - 1) / 1.7183 * 1023.0;
                	float bg = br_green;
                	bg *= 0.01;
                	bg = (exp(bg) - 1) / 1.7183 * 1023.0;
                	if (br < 0) br = 0;
                	if (br > 1023) br = 1023;
                	if (bg < 0) bg = 0;
                	if (bg > 1023) bg = 1023;
                
                	setTimer1PWM(1023 - br, 1023 - bg);
                }
                void buzz_on()
                {
                	setTimer0PWM(128, 128);
                }
                void buzz_off()
                {
                	setTimer0PWM(255, 255);
                }
                void buzz_beep()
                {
                	byte sp = sec10 % 15;
                	if (sp == 0)
                		buzz_on();
                	if (sp == 1)
                		buzz_off();
                	if (sp == 2)
                		buzz_on();
                	if (sp == 3)
                		buzz_off();
                	if (sp == 4)
                		buzz_on();
                	if (sp == 5)
                		buzz_off();
                }
                
                void setup() {
                
                	//WARNING! Each sensor is different!
                	//You MUST calibrate sensor manually and
                	//set proper sensor_reading_clean_air value before using
                	//it for any practical purpose!
                
                	pinMode(5, OUTPUT);
                	pinMode(6, OUTPUT);
                	pinMode(3, OUTPUT);
                	pinMode(9, OUTPUT);
                	pinMode(10, OUTPUT);
                	pinMode(A0, INPUT);
                	pinMode(A1, INPUT);
                	setTimer1PWM(1023, 0);
                	analogReference(DEFAULT);
                	Serial.begin(115200);
                
                	pwm_adjust();
                
                	Serial.print("PWM result: width ");
                	Serial.print(opt_width);
                	Serial.print(", voltage ");
                	Serial.println(opt_voltage);
                	Serial.println("Data output: raw A0 value, heating on/off (0.1 off 1000.1 on), CO ppm from last measurement cycle");
                	//beep buzzer in the beginning to indicate that it works
                	buzz_on();
                	delay(100 * time_scale);
                	buzz_off();
                	delay(100 * time_scale);
                	buzz_on();
                	delay(100 * time_scale);
                	buzz_off();
                	delay(100 * time_scale);
                	buzz_on();
                	delay(100 * time_scale);
                	buzz_off();
                	delay(100 * time_scale);
                
                	startMeasurementPhase(); //start from measurement
                }
                
                void loop()
                {
                
                	//WARNING! Each sensor is different!
                	//You MUST calibrate sensor manually and
                	//set proper sensor_reading_clean_air value before using
                	//it for any practical purpose!
                
                	unsigned long ms = millis();
                	int dt = ms - prev_ms;
                
                	//this condition runs 10 times per second, even if millis() 
                	//overflows - which is required for long-term stability
                	//when millis() overflows, this condition will run after less
                	//than 0.1 seconds - but that's fine, since it happens only once
                	//per several days
                	if (dt > 100 * time_scale || dt < 0)
                	{
                		prev_ms = ms; //store previous cycle time
                		sec10++; //increase 0.1s counter
                		if (sec10 % 10 == 1) //we want LEDs to blink periodically
                		{
                			setTimer1PWM(1023, 1023); //blink LEDs once per second
                									  //use %100 to blink once per 10 seconds, %2 to blink 5 times per second
                		}
                		else //all other time we calculate LEDs and buzzer state
                		{
                			int br_red = 0, br_green = 0; //brightness from 1 to 100, setLEDs function handles converting it into timer settings
                			if (last_CO_ppm_measurement <= red_threshold) //turn green LED if we are below 30 PPM
                			{//the brighter it is, the lower concentration is
                				br_red = 0; //turn off red
                				br_green = (red_threshold - last_CO_ppm_measurement)*100.0 / red_threshold; //the more negative is concentration, the higher is value
                				if (br_green < 1) br_green = 1; //don't turn off completely
                			}
                			else //if we are above threshold, turn on red one
                			{
                				br_green = 0; //keep green off
                				br_red = (last_CO_ppm_measurement - red_threshold)*100.0 / red_threshold; //the higher is concentration, the higher is this value
                				if (br_red < 1) br_red = 1; //don't turn off completely
                			}
                
                			if (last_CO_ppm_measurement > alarm_ppm_threshold) //if at 50 seconds of measurement cycle we are above threshold 
                				buzz_beep();
                			else
                				buzz_off();
                
                			setLEDs(br_green, br_red); //set LEDs brightness
                		}
                	}
                	if (phase == 1 && sec10 - high_on > 600) //60 seconds of heating ended?
                		startMeasurementPhase();
                	if (phase == 0 && sec10 - low_on > 900) //90 seconds of measurement ended?
                	{
                		last_CO_ppm_measurement = raw_value_to_CO_ppm(sens_val);
                		startHeatingPhase();
                	}
                
                	float v = analogRead(A0); //reading value
                	sens_val *= 0.999; //applying exponential averaging using formula
                	sens_val += 0.001*v; // average = old_average*a + (1-a)*new_reading
                	if (sec10 - last_print > 9) //print measurement result into serial 2 times per second
                	{
                		last_print = sec10;
                		Serial.print(sens_val);
                		Serial.print(" ");
                		Serial.print(0.1 + phase * 1000);
                		Serial.print(" ");
                		Serial.println(last_CO_ppm_measurement);
                	}
                }
                

                from this guide http://www.instructables.com/id/Arduino-CO-Monitor-Using-MQ-7-Sensor/
                I was wondering if it could be used on a MySensors node even with all those timers modifications

                1 Reply Last reply
                0
                • B Offline
                  B Offline
                  borneo1910
                  wrote on last edited by
                  #12

                  Does anyone know if any of these sensors can be used to detect vaporized white mineral oil?

                  1 Reply Last reply
                  0
                  • gohanG Offline
                    gohanG Offline
                    gohan
                    Mod
                    wrote on last edited by
                    #13

                    Vaporized oil is very messy, I don't think they would any good as it is not volatile

                    1 Reply Last reply
                    0
                    • B Offline
                      B Offline
                      borneo1910
                      wrote on last edited by
                      #14

                      Does it change things if it's actually atomized and not vaporized?

                      1 Reply Last reply
                      0
                      • gohanG Offline
                        gohanG Offline
                        gohan
                        Mod
                        wrote on last edited by
                        #15

                        What I'm saying is that it is not a gas that dissolves in air, it is more particulate matter suspended in the air so an optical sensor is probably better given that the sensor could last as oil tends to stick to stuff

                        1 Reply Last reply
                        0
                        • U Offline
                          U Offline
                          user2684
                          Contest Winner
                          wrote on last edited by
                          #16

                          I spent the last few days trying to get a sense out of the black magic behind the calculations used for this sensor.
                          I got the math now but still there is something I feel like it is not completely accurate with the current implementation.
                          First of all the resulting ppm seems like a sort of normalized value; since I know e.g. CO2 ppm in clean air is around 400, the code seems not to take this into consideration. Also, configure it with a different MQ sensor doesn't seem an easy task.
                          For these reasons I've tried starting from scratch, inspired by http://davidegironi.blogspot.com/2014/01/cheap-co2-meter-using-mq135-sensor-with.html#.WyLQo6qFNn5.
                          Starting point is the power function y = a*x^b. The way to make the code more generic is to let the user provide the coordinates of two points (like @APL2017 was suggesting a while ago) which is an easy task and let the code solve the two equations and derive the values of a and b. Then, since ppm = a(rs/ro)^b, with a known value of ppm (e.g. the concentration in clear air of the gas, for co2 is 411), the equation can be solved for Ro by measuring Rs from the adc. Once a, b and Ro are known, the ppm comes naturally by solving again ppm = a(rs/ro)^b.
                          I'm not sure this is better than the other methods but at least I get the same results from the blog above, both in terms of values of a, b and Ro as well as a real value of CO2 ppm measured with a MQ135.
                          The downside of this of course is the difficulty to provide a known value of ppm for the calibration for other gas like e.g. Ch4, but if I claim I'm showing a ppm value, I want to be sure this is a real ppm :-)
                          The code is here, within the dev branch of NodeManager, any feedback would be appreciated!
                          https://github.com/user2684/NodeManager/blob/19e37a45792be4d698a1316bf6eb4f954a8455f5/NodeManagerLibrary.ino#L2441

                          1 Reply Last reply
                          0
                          • F Offline
                            F Offline
                            FL74
                            wrote on last edited by
                            #17

                            Hi all,
                            which of the above sensors (listed in https://www.mysensors.org/build/gas) is the most suitable to detect a relevant leak of gasoline inside an engine bay?

                            1 Reply Last reply
                            1
                            • gohanG Offline
                              gohanG Offline
                              gohan
                              Mod
                              wrote on last edited by
                              #18

                              I'd say the ones with benzene

                              1 Reply Last reply
                              0
                              Reply
                              • Reply as topic
                              Log in to reply
                              • Oldest to Newest
                              • Newest to Oldest
                              • Most Votes


                              17

                              Online

                              11.7k

                              Users

                              11.2k

                              Topics

                              113.1k

                              Posts


                              Copyright 2025 TBD   |   Forum Guidelines   |   Privacy Policy   |   Terms of Service
                              • Login

                              • Don't have an account? Register

                              • Login or register to search.
                              • First post
                                Last post
                              0
                              • MySensors
                              • OpenHardware.io
                              • Categories
                              • Recent
                              • Tags
                              • Popular