Hazardous Gas Monitor

Contributors: jenfoxbot
Favorited Favorite 17


Phant is No Longer in Operation

Unfortunately Phant, our data-streaming service, is no longer in service. The system has reached capacity and, like a less-adventurous Cassini, has plunged conclusively into a fiery and permanent retirement. There are several other maker-friendly, data-streaming services and/or IoT platforms available as alternatives. The three we recommend are Blynk, ThingSpeak, and Cayenne. You can read our blog post on the topic for an overview and helpful links for each platform. The code in this tutorial will need to be adjusted to work with the other data streams.

As an example, try looking at the Photon Remote Water Level Sensor Tutorial which uses ThingSpeak.

Build a portable gas monitor to check for dangerous levels of hazardous gases in your home, community, or on the go and prevent your friends from lighting a cigarette during a gasoline fight.*

**Please note that this is solely a movie reference -- gasoline fights should probably be avoided in real life.*

Hazardous Gas Monitor

This tutorial shows you how to build a web-connected "canary" monitor for three hazardous gases: Liquid Propane Gas ("LPG"), Methane (aka natural gas), and Carbon Monoxide ("CO"). Using the Particle Photon microcontroller, the sensor readings are converted into parts-per-million ("PPM") and uploaded to the data.sparkfun.com web service.

Check out the video below to see the Hazardous Gas Monitor in action:

Helpful Background Info

  1. How to set up the Particle Photon.
  2. Pushing data to the data.sparkfun.com web server.
  3. New to relays? Check out this a handy reference.
  4. Here's a helpful overview on the N-Channel MOSFET.
  5. For powering the Photon, here's a thorough guide on the Photon Battery Shield.
  6. Highly recommended to peruse the datasheets for the three gas sensors.
    1. LPG sensor datasheet
    2. Methane sensor datasheet
    3. CO sensor datasheet

Choosing a Battery

The gas sensors used in this project require a fair amount of current, about 0.17 mA each at 5V. To make the system portable, we'll need a high capacity battery. One easy, and affordable, option is to use four (rechargeable) AA batteries in series. These batteries will last about 4 hours.

Another option is to use a lithium ion battery ("LIB"). LIBs have a higher capacity than AAs, but typically run at a lower voltage. If you go with this option, you may need to include a correction factor when you calculate the sensor value or boost the battery voltage with a transistor or other component.

Here's a table that shows the approximate lifetime of a few different battery options.


If all of this sounds confusing, here's a more thorough tutorial.


To follow along with this tutorial, you'll need the following:


Microcontroller and Accessory Components

Gas Sensor Circuit

LPG (MQ6) Gas Sensor
Methane (MQ4) Gas Sensor
Carbon Monoxide (MQ7) Gas Sensor


  • Soldering Iron
  • Wire cutters/strippers
  • Drill
  • Screwdriver
  • Epoxy (or hot glue)

Build It

Below is the Fritzing diagram for the Hazardous Gas Monitor circuit. Since we are using the Photon Battery Shield breakout, make sure to connect the wires to the shield's pinouts instead of directly to the Particle Photon's header pins shown in the image. Additionally, the LiPo battery will be connected to the Photon battery Shield's JST connector underneath the Particle Photon.

Fritzing Diagram

Click the image for a closer look.

  1. Solder gas sensor breakout boards to gas sensors. Orientation doesn't matter, just be sure that the silkscreen (aka labels) are facing down so that you can read them (had to learn that one the hard way..). Solder wires to the gas sensor breakout board.


  2. Solder three voltage regulators to the PCB board. For each regulator, connect positive battery output to the regulator input, and connect middle voltage regulator pin to ground.


  3. Connect the LPG (MQ6) and Methane (MQ4) sensors.

    MQ6, MQ4

    For each sensor:

    1. Connect H1 and A1 to the output of one of the voltage regulators (recommended to use an electrical connector).
    2. Connect GND to ground.
    3. Connect B1 to Photon analog pin (LPG goes to A0, Methane to A1)
    4. Connect a 4.7 kΩ resistor from B1 to ground.

    alt text

  4. Connect the CO (MQ7) gas sensor.

    alt text

    Aside: The MQ7 sensor requires cycling the heater voltage (H1) between 1.5V (for 90s) and 5V (for 60s). One way to do this is to use a relay triggered by the Photon (with the aid of a MOSFET and potentiometer) -- when the relay is not powered, the voltage across H1 is 5V, and when the relay is powered the voltage across H1 is ~ 1.5V.

    1. Connect GND to ground.
    2. Connect B1 to Photon analog pin (A2). Connect 4.7 kΩ resistor from B1 to ground.
    3. Connect A1 to third voltage regulator output (5V source).
    4. Connect Photon 3.3V pin to positive relay input.
    5. Connect Photon Digital Pin D7 to left MOSFET pin, and a 10 kΩ resistor to ground.
    6. Connect middle MOSFET pin to relay ground pin. Connect right MOSFET pin to ground.
    7. Connect relay Normally Open ("NO") pin to A1, and the Normally Closed ("NC") pin to middle potentiometer pin.
    8. Connect right potentiometer pin to ground, and left pin to A1.
    9. Connect relay Common ("COM") pin to H1.
    10. Adjust potentiometer resistance until it changes the relay output to ~ 1.5V when the relay receives power.
  5. Connect an LED and 10 kΩ resistor to each of the Photon digital pins D0, D1, and D2. Connect buzzer to Photon digital pin D4.

    alt text

  6. Connect toggle switch between battery pack and PCB board power. Recommended to include an electrical connector for the battery pack to make it easier to switch out batteries.

    alt text

  7. Solder wires to Photon Battery Shield breakout.

    alt text

  8. Connect lamp switch between LIB and Photon battery shield -- recommended to use an extra JST cable for this to keep the LIB battery cable in tact (and make it easier to install the lamp switch).

  9. Build a case for the electronics!

    alt text

    1. Drill hole for toggle switch on case lid.
    2. Drill 3 holes in the case lid for the LED lights to shine through, and 3 holes for the gas sensors to have air contact. Adhere components on the inside of the lid.

      alt text

    3. Drill hole in the side of the case for barrel jack USB cord to connect to the Photon Battery Shield.

      alt text

    4. Drill two small holes on the side of the case for the lamp switch cable. Adhere lamp switch to side of case.

    5. Label the LEDs with its corresponding gas sensor on the outside of the case.

    alt text

  10. Check electrical connections and, if everything is good to go, coat electrical connections in epoxy or hot glue.

    alt text

Calculate Gas Sensor PPM

Each of the gas sensors outputs an analog value from 0 to 4095. To convert this value into voltage, use the following equation:

Sensor Voltage = AnalogReading * 3.3V / 4095

Once you have the sensor voltage, you can convert that into a parts per million ("PPM") reading using the sensitivity calibration curve on page 5 of the gas sensor datasheets. To do this, recreate the sensitivity curve by picking data points from the graph or using a graphical analysis software like Engauge Digitizer.

Plot PPM on the y-axis and V_RL on the x-axis, where V_RL is the sensor voltage. There is a lot of room for error with this method, but it will give us enough accuracy to identify dangerous levels of hazardous gases. Estimated error bars are around 20 PPM for the LPG and Methane sensors, and about 5 PPM for the CO sensor.


Next, find an approximate equation for the PPM vs. V_RL curve. I used an exponential fit (e.g. y = e^x) and got the following equations:

LPG sensor: PPM = 26.572*e^(1.2894*V_RL)

Methane sensor: PPM = 10.938*e(1.7742*V_RL)

CO sensor: PPM = 3.027*e^(1.0698*V_RL)

Program It

First, set up a data stream on the data.sparkfun.com service. Next, write a program to read in the analog value of each gas sensor, convert it to PPM, and check it against known safe thresholds. Based on OSHA safety standards, the thresholds for the three gases are as follows:

  • LPG: 1,000 PPM
  • Methane: 1,000 PPM
  • CO: 50 PPM

If you want to get up and running quickly, or are new to programming, feel free to grab the code from below, or you can get the most up to date files from the GitHub repository. Use it as-is or modify to suit your particular needs.

// This #include statement was automatically added by the Particle IDE.
//This library is used to push data to the data.sparkfun.com server.
#include "SparkFunPhant/SparkFunPhant.h"
#include "math.h" 

//This code was written by Jennifer Fox <jenfoxbot@gmail.com>
 * ----------------------------------------------------------------------------
 * "THE Coffee-WARE LICENSE" (Revision 42):
 * <jenfoxbot@gmail.com>  wrote this file.  As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a coffee in return.
 * ----------------------------------------------------------------------------

//Variables to push data to data.sparkfun.com host -- Change publicKey[] and privateKey[]
const char server[] = "data.sparkfun.com"; // Phant destination server
const char publicKey[] = "INSERT_PUBLIC_KEY_HERE"; // Phant public key
const char privateKey[] = "INSERT_PRIVATE_KEY_HERE"; // Phant private key

Phant phant(server, publicKey, privateKey); // Create a Phant object

const unsigned long postingRate = 20000; //Post rate to data.sparkfun.com (time in milliseconds)
unsigned long lastPost = millis(); //Keeps track of posting rate

//Define analog pins on Photon to use for sensors
const int LPG = A0;
const int NG = A1;
const int CO = A2;

//Define digital pins on Photon to use for LEDs,  buzzer, and MQ7 (CO sensor) heater
const int LPGled = D0;
const int NGled = D1;
const int COled = D2;
const int buzzer = D3;
const int CORelayPin = D6;
const int COVoltPin = D7;

//Set up raw signal and PPM variables for each gas sensor
int LPGRaw;
int NGRaw;
int CORaw;

int LPGppm;
int NGppm;
int COppm;

//Set safety threshold levels for each  hazardous gas
const int  LPGthresh = 1000;
const int NGthresh = 1000;
const int COthresh = 50;

//Set variables for CO sensor (MQ7) voltage cycle
unsigned long startMillis;        
unsigned long switchTimeMillis;
const int CO_5V_Interval = 60000; //60s for 5V interval
const int CO_1_5V_Interval = 90000; //90s for 1.5V interval
bool heaterInHighPhase;

void setup() {

    //Initialize LED and buzzer output pins
    pinMode(LPGled, OUTPUT);
    pinMode(NGled, OUTPUT);
    pinMode(COled, OUTPUT);
    pinMode(buzzer, OUTPUT);

    //Initialize CO sensor heater pins
    pinMode(CORelayPin, OUTPUT);
    pinMode(COVoltPin, OUTPUT);

    //Set start time (for CO sensor heater voltage)
    startMillis = millis();

void loop() {
    //Cycle CO sensor (MQ7) heater voltage

    // 5V phase - check to switch
        if(millis() > switchTimeMillis) {
    else {
    // 1.4V phase - check to switch
        if(millis() > switchTimeMillis) {

    //Read in analog value from each gas sensor -- use function defined below to measure CO sensor at end of voltage cycle
    LPGRaw = analogRead(LPG); 
    NGRaw = analogRead(NG);
    CORaw = measureCOSensor();

    //Caclulate the PPM of each gas sensor using the funtions defined below            
    LPGppm = LPG_ppm(LPGRaw); 
    NGppm = NG_ppm(NGRaw); 
    COppm = CO_ppm(CORaw);

    //Serial monitor print for debugging and checking data 

    //Check gas sensor measurements against safety thresholds
    checkThreshold(LPGppm, NGppm, COppm);

    //Wait to post until ~ 20s has lapsed
    if (lastPost + postingRate < millis()) {  

        postToPhant(LPGppm, NGppm, COppm); //Post gas sensor readings and unit (PPM) to your data stream at data.sparkfun.com

        lastPost = millis();


//Functions to calculate PPM from Photon analog reading
//Each equation is determined by visually picking points, plotting PPM v. V_RL, then fitting a trendline to the curve (exponential)
//Calculate LPG PPM
int LPG_ppm(double rawValue){

    double ppm = 26.572*exp(1.2894*(rawValue*3.3/4095)); //Multiply raw analog value by 3.3/4095 to convert to a voltage
    return ppm;

//Calculate NG PPM
int NG_ppm(double rawValue){

    double ppm = 10.938*exp(1.7742*(rawValue*3.3/4095)); 
    return ppm;

//Calculate CO PPM
int CO_ppm(double rawValue){

    double ppm = 3.027*exp(1.0698*(rawValue*3.3/4095));
    return ppm;

//Function to check PPM reading with maximum safe PPM threshold
//Include a margin of error (currently 10%)
void checkThreshold(int lpgppm, int ngppm, int coppm){
    int led1;
    int led2;
    int led3;

    if (lpgppm >= LPGthresh*0.9){
        digitalWrite(LPGled, HIGH);
        led1 = TRUE;
        digitalWrite(LPGled, LOW);
        led1 = FALSE;

    if (ngppm >= NGthresh*0.9){
        digitalWrite(D1, HIGH);
        led2 = TRUE;
        digitalWrite(NGled, LOW);
        led2 = FALSE;

    if (coppm >= COthresh*0.9){
        digitalWrite(D2, HIGH);
        led3 = TRUE;
        digitalWrite(COled, LOW);
        led3 = FALSE;

    if(led1 | led2 | led3){
        digitalWrite(buzzer, HIGH);

    else{digitalWrite(buzzer, LOW);}


//Functions to switch heater voltage on MQ7 (CO) sensor
void turnHeaterHigh(){
  // 5v phase
  digitalWrite(COVoltPin, LOW);
  digitalWrite(CORelayPin, HIGH);

  heaterInHighPhase = true;
  switchTimeMillis = millis() + CO_5V_Interval;

void turnHeaterLow(){
  // 1.4v phase
  digitalWrite(COVoltPin, HIGH);
  digitalWrite(CORelayPin, LOW);

  heaterInHighPhase = false;
  switchTimeMillis = millis() + CO_1_5V_Interval;

//Function to read CO sensor voltage (just before switching to 1.5V)
int measureCOSensor(){
  unsigned int gasLevel = analogRead(CO);
  unsigned int time = (millis() - startMillis) / 1000;

  return gasLevel;

//Function to post data to data.sparkfun.com host
//Many thanks to Jim Lindblom <jim@sparkfun.com> for the sample code and Phant library.
int postToPhant(int lpg, int ng, int co){

    phant.add("lpg", lpg); //Data stream field name "sensorvalue1"
    phant.add("ng", ng); //Data stream field name "sensorvalue2"
    phant.add("co", co); //Data stream field name "sensorvalue3"

    TCPClient client;
    char response[512];
    int i = 0;
    int retVal = 0;

    if (client.connect(server, 80)) // Connect to the server
        // Post message to indicate connect success

        // phant.post() will return a string formatted as an HTTP POST.
        // It'll include all of the field/data values we added before.
        // Use client.print() to send that string to the server.
        // Now we'll do some simple checking to see what (if any) response
        // the server gives us.
        while (client.available())
            char c = client.read();
            Serial.print(c);    // Print the response for debugging help.
            if (i < 512)
                response[i++] = c; // Add character to response string
        // Search the response string for "200 OK", if that's found the post
        // succeeded.
        if (strstr(response, "200 OK"))
            Serial.println("Post success!");
            retVal = 1;
        else if (strstr(response, "400 Bad Request"))
        {   // "400 Bad Request" means the Phant POST was formatted incorrectly.
            // This most commonly ocurrs because a field is either missing,
            // duplicated, or misspelled.
            Serial.println("Bad request");
            retVal = -1;
            // Otherwise we got a response we weren't looking for.
            retVal = -2;
    {   // If the connection failed, print a message:
        Serial.println("connection failed");
        retVal = -3;
    client.stop();  // Close the connection to server.
    return retVal;  // Return error (or success) code.

Change the following in the code:

  1. Copy and paste your data stream public key to the array called publicKey[].

    const char publicKey[] = "INSERT_PUBLIC_KEY_HERE";

2.Copy and paste your data stream private key to the array called privateKey[].

`const char privateKey[] = "INSERT_PRIVATE_KEY_HERE";`

To monitor the Photon output, use the Particle driver downloaded as described in the "Connecting Your Device" Photon tutorial. Once this is installed, in the command prompt, type particle serial monitor. This is super helpful for debugging and checking that the Photon is posting data to the web.

Be a Citizen Scientist


Now we get to test and employ our gas monitor! Turn the batteries for the gas sensors on using the toggle switch, wait about 3 - 5 minutes, then turn the Photon on with the lamp switch (the gas sensor heater coils take some time to heat up). Check that the Photon is connected to WiFi (on-board LED will slowly pulse light blue) and is uploading data to the server. Also check that the gas sensor readings increase when in proximity to hazardous gases -- one easy, and safe, way is to hold a lighter and/or a match close to the sensors.

Once up and running, use the sensor to monitor for dangerous gas leaks around your home, school, workplace, neighborhood, etc. You can install the sensor in one location permanently, or use it to check gas levels in different locations (e.g. SoCal..).

Educator Extension!

This project is a perfect excuse for a hands-on chemistry lesson! Use the monitor to learn the fundamentals of various gases -- what kinds of gases are in our environment, how are different gases produced, and what makes some of them hazardous or dangerous.

Study the local environment and use a lil' math to record and plot LPG, Methane, and CO in specific locations over time to see how the levels change. Use the data to help determine what causes changes in the gas levels and where/when gas concentrations are the highest.

Resources and Going Further

Monitor hazardous gas concentrations around your neighborhood or city and use the results to identify problem areas and improve public safety.

Use Bluetooth, or your smartphone WiFi, to connect to the Photon and upload data to the web wherever you are!

Include other sensors, gaseous or otherwise, to create a more comprehensive environmental monitoring system.

For more IoT SparkFun tutorial fun, check out the links below:

Photon Remote Water Level Sensor

Learn how to build a remote water level sensor for a water storage tank and how to automate a pump based off the readings!

Photon Remote Temperature Sensor

Learn how to build your own Internet-connect, solar-powered temperature collection station using the Photon from Particle.

IoT Industrial Scale

What does a baby elephant weigh? How much impact force does a jump have? Answer these questions and more by building your very own IoT industrial scale using the SparkFun OpenScale.