IoT Gas Sensing

Team Member

  • Donghoon Shin
  • Elisha Earley
  • Pranshu Trivedi
  • Matthew Kern

Overview:

IoT Gas Sensing is a project that uses MQ gas sensors to check for harmful gases and emissions in realtime in order to monitor the danger of a enclosed area. It records the gas values from the Analog In signals of the mbed, converts the signals to ppm readings, and sends the information to a pi zero which publishes the data to a database. There is a web page hosted on an AWS server where the user can see graphs of the different readings in the database. In addition to displaying the readings, the website gives the user the opportunity to set a threshold ppm value. If the gas readings from the sensors exceed this threshold ppm value, the user will receive a text message/email informing them of the high reading and allowing them to act accordingly. The mbed code is built in with default alarm ppm values (the ppm when that amount of gas is harmful to the human body) that will triggered a speaker and LED to notify those near the sensor gas values are at a harmful level. This project is modular as any number of mbed and pi zero devices can be added to send data to the website as well as each individual sensor can be substituted.

/media/uploads/laughatm2/screen_shot_2017-12-14_at_6.35.56_am.png

Design Step

1. Converting gas sensor analog values to ppm values with datasheet information.

2. Set up serial port communication from mbed to Raspberry Pi to get gas sensor ppm values.

3. Set up code that pushes data to the AWS SQS.

4. Set up code that pulls data from SQS and pushes to a AWS DynamoDB table.

5. Add the notifications system using AWS SNS that alerts subscribed users if the threshold has been exceeded with text and email alerts.

6. Build the Flask application that hosts the monitoring website and pulls data from the DynamoDB table.

Parts needed:

  • mbed LPC 1726
  • raspberry pi zero
  • Sensor MQ135
  • Sensor MQ2
  • Sensor MQ3
  • Sensor MQ4
  • Sensor MQ5
  • Sensor MQ6
  • Sensor MQ7
  • Sensor MQ8
  • Sensor MQ9
  • Speaker
  • registers
  • 2N3904(transister)
  • LED

Functionality:

Functionality: Functionality could be organized by functions such as detecting, alarming and informing & managing. Detecting:

  • Gas sensors used have a certain sensitivity to specific gases and output their measurement as analog signals.
  • Analog signals converted to ppm in mbed.

Alarming:

  • Threshold for alarms can be set through the website. These thresholds are user specified and help to monitor environments. Notifications are sent if threshold has been exceeded. Notifications specify which sensor has breached the threshold.
  • Email Notification
  • SMS Notification
  • Speaker alarm and LED triggered when gases breach life threatening levels.

Informing & Managing:

  • Monitor live stream data of gas concentrations of specific sensors on website.
  • Scalability: multiple raspberry pi with mbed configurations can be added and monitored with specific sensors through the website.
  • Device registration and deletion on website.

Wiring:

Sensor

Can have up to 6 mq sensors per Mbed AD needs a voltage divider circuit because it sends a 5v signal and the Mbed AnalogIn only reads 3.3 Volts

MbedMQ Sensor
VUVCC
GNDGND
NC (not connected)DO
AnalogIn Pin (p15, p16,p17,p18, p19, or p20) with Voltage Divider CircuitAD

/media/uploads/laughatm2/voltage_divider.png

Speaker

/media/uploads/laughatm2/2n3904.jpg

/media/uploads/laughatm2/_scaled_speakerdriverschem.png

MbedSpeaker2N3904
P26 (Any PwmOut pin)BASE
GNDEMITTER
-COLLECTOR
external 5V+

LED

MbedLED
P23 (Any PwmOut pin) through 180 ohm resistor+ (longer pin)
GND- (shorter pin)

/media/uploads/laughatm2/let_resister.png

Pi Zero Wiring

USB mini to USB conenctor for Mbed Power cord Mini SD Card with Pi operating system

Code

code goes into mbed

#include "mbed.h"

//Pi mbed USB Slave function
// connect mbed to Pi USB
RawSerial pi(USBTX, USBRX);
//mbed LED
DigitalOut led1(LED1);

//check pin assignments with sensor names to match your setup
//AnalogIn sensorMQ2(p15);
//AnalogIn sensorMQ3(p16);
//AnalogIn sensorMQ4(p17);
//AnalogIn sensorMQ5(p18);
//AnalogIn sensorMQ6(p19);
AnalogIn sensorMQ7(p19);
//AnalogIn sensorMQ9(p20);
AnalogIn sensorMQ135(p20);
PwmOut red(p23);
//PwmOut green(p22);
//PwmOut blue(p21);
PwmOut speaker(p26);

//constants
const int numReadings = 500;

//MQ2
const float airRatioMQ2 = 10.0;
const float slopeMQ2 = -0.4687;
const float interceptMQ2 = 1.3969;
//MQ3
const float airRatioMQ3 = 60.0;
const float slopeMQ3 = -0.6701;
const float interceptMQ3 = -0.2696;
//MQ4
const float airRatioMQ4 = 4.4;
const float slopeMQ4 = -0.3485;
const float interceptMQ4 = 1.0544;
//MQ5
const float airRatioMQ5 = 6.5;
const float slopeMQ5 = -0.2592;
const float interceptMQ5 = 0.8036;

//MQ6
const float airRatioMQ6 = 10.0;
const float slopeMQ6 = -0.4236;
const float interceptMQ6 = 1.2747;
//MQ7
const float airRatioMQ7 = 28.0;
const float slopeMQ7 = -0.7794;
const float interceptMQ7 = 1.5475;
//MQ9
const float airRatioMQ9 = 10.0;
const float slopeMQ9 = -0.4825;
const float interceptMQ9 = 1.4479;
////MQ135
const float airRatioMQ135 = 10.0;
const float slopeMQ135 = -0.3701;
const float interceptMQ135 = 0.7484;

//globals for the sensor readings
float mq2sensorPPM = 0;
float mq3sensorPPM = 0;
float mq4sensorPPM = 0;
float mq5sensorPPM = 0;
float mq6sensorPPM = 0;
float mq7sensorPPM = 0;
float mq9sensorPPM = 0;
float mq135sensorPPM = 0;

//globals for the R0 values
float r0MQ2 = 0.83142;
float r0MQ3 = 1.152465;
float r0MQ4 = 1.826011;
float r0MQ5 = 0.964339;
float r0MQ6 = 0.606199;
float r0MQ7 = 0.929365;
float r0MQ9 = 0.641456;
float r0MQ135 = 0.500467;


//gloabals for alarm values
float alarmMQ2 = 1000;
float alarmMQ3 = 1000;
float alarmMQ4 = 1000;
float alarmMQ5 = 1000;
float alarmMQ6 = 1000;
float alarmMQ7 = 30;
float alarmMQ9 = 60;
float alarmMQ135 = 30;

float calculateR0(AnalogIn s, float ratio) {
        float sensor_volt;
        float rs;
        float sensorValue = 0.0;
        float r0;
        
        //take 500 sensor readings and add them together
        for(int i = 0; i < numReadings; i++) {
            sensorValue = sensorValue + s.read();    
        }
        
        sensorValue = sensorValue/numReadings;//average sensor value
        sensor_volt = sensorValue * 3.3;
        rs = ((3.3-sensor_volt)/sensor_volt);
        r0 = rs/ratio;
        pi.printf("RO VALUE: %f \n\n", r0);
        return r0;
}

float determinePPM(AnalogIn sensor, float R0, float m, float b) {
        //Slope and y-intercept of ppm graph line, and R0 from previous calculations
        float voltage = sensor.read() * 3.3;
        float RS_gas = ((3.3-voltage)/voltage);
        float ppmRatio = RS_gas/R0;
        float ppm_log = (log10(ppmRatio)-b)/m;
        float ppm = pow(10, ppm_log);
        if(ppm<0){
            ppm = 0.0;
        }
        if(ppm>10000){
            ppm = 10000;
        }
        return ppm;
}

void sendInfo()
{
    char temp = 0;
    led1 = !led1;
    while(pi.readable()) {
        temp = pi.getc();
        if (temp == 'w')
        {
            pi.printf("{\"MQ7\":%f, \"MQ135\":%f}\n", mq7sensorPPM, mq135sensorPPM);
        }
    }
}

// main() runs in its own thread in the OS
int main() {
    //Uncomment if youd like to reset R0 from default to your environment
//    r0MQ2 = calculateR0(sensorMQ2, airRatioMQ2);
//    r0MQ3 = calculateR0(sensorMQ3, airRatioMQ3);
//    r0MQ4 = calculateR0(sensorMQ4, airRatioMQ4);
//    r0MQ5 = calculateR0(sensorMQ5, airRatioMQ5);
//    r0MQ6 = calculateR0(sensorMQ6, airRatioMQ6);
//    r0MQ7 = calculateR0(sensorMQ7, airRatioMQ7);
//    r0MQ9 = calculateR0(sensorMQ9, airRatioMQ9);
//    r0MQ135 = calculateR0(sensorMQ135, airRatioMQ135);

    pi.attach(&sendInfo, Serial::RxIrq);
    pi.baud(9600);
    
    while (1) {
        
        //mq2sensorPPM = determinePPM(sensorMQ2, r0MQ2, slopeMQ2, interceptMQ2);
        //mq3sensorPPM = determinePPM(sensorMQ3, r0MQ3, slopeMQ3, interceptMQ3);
        //mq4sensorPPM = determinePPM(sensorMQ4, r0MQ4, slopeMQ4, interceptMQ4);
        //mq5sensorPPM = determinePPM(sensorMQ5, r0MQ5, slopeMQ5, interceptMQ5);
        //mq6sensorPPM = determinePPM(sensorMQ6, r0MQ6, slopeMQ6, interceptMQ6);
        mq7sensorPPM = determinePPM(sensorMQ7, r0MQ7, slopeMQ7, interceptMQ7);
        //mq9sensorPPM = determinePPM(sensorMQ9, r0MQ9, slopeMQ9, interceptMQ9);
        mq135sensorPPM = determinePPM(sensorMQ135, r0MQ135, slopeMQ135, interceptMQ135);
        if(mq2sensorPPM>alarmMQ2) {
            speaker.period(1.0/500.0); //frequency of sound
            speaker = 1.0; //volume
            red = 1.0f;
        } else if(mq3sensorPPM>alarmMQ3) {
            speaker.period(1.0/500.0); //frequency of sound
            speaker = 1.0; //volume
            red = 1.0f;
        } else if(mq4sensorPPM>alarmMQ4) {
            speaker.period(1.0/500.0); //frequency of sound
            speaker = 1.0; //volume
            red = 1.0f;
        } else if(mq5sensorPPM>alarmMQ5) {
            speaker.period(1.0/500.0); //frequency of sound
            speaker = 1.0; //volume
            red = 1.0f;
        } else if(mq6sensorPPM>alarmMQ6) {
            speaker.period(1.0/500.0); //frequency of sound
            speaker = 1.0; //volume
            red = 1.0f;
        } else if(mq7sensorPPM>alarmMQ7) {
            speaker.period(1.0/500.0); //frequency of sound
            speaker = 1.0; //volume
            red = 1.0f;
        } else if(mq9sensorPPM>alarmMQ9) {
            speaker.period(1.0/500.0); //frequency of sound
            speaker = 1.0; //volume
            red = 1.0f;
        } else if(mq135sensorPPM>alarmMQ135) {
            speaker.period(1.0/500.0); //frequency of sound
            speaker = 1.0; //volume
            red = 1.0f;
        } else {
            speaker = 0.0;
            red = 0.0f;    
        }
    }
}

serial_to_sqs.py

import signal
import time
import sys
import serial

import json
import re
import boto3
from elasticsearch import Elasticsearch, RequestsHttpConnection
from requests_aws4auth import AWS4Auth
import time
import datetime


AWS_KEY=""
AWS_SECRET=""
REGION="us-east-1"

PI_ID = 1


sqs = boto3.resource('sqs', aws_access_key_id=AWS_KEY,
                            aws_secret_access_key=AWS_SECRET,
                            region_name=REGION)

queue = sqs.create_queue(QueueName="SensorData")

sensors = ["MQ2", "MQ3", "MQ4", "MQ5", "MQ6", "MQ7", "MQ8", "MQ9", "MQ135"]



def run_program(ser):
    while True:
        data = getData(ser)
        print data
        publish_data(data)
        time.sleep(1)

def getData(ser):
    ser.write('w')
    jsonString = ser.readline()
    sensorDict = json.loads(jsonString)
    
    for item in sensors:
        if item not in list(sensorDict.keys()):
            sensorDict[item] = -1
        
    jsonDict = {}
    jsonDict["Sensors"] = sensorDict

    ts = time.time()
    timestamp = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
    tms = int(ts*1000)
    jsonDict["timestamp"] = timestamp
    jsonDict["timestampNum"] = tms
    jsonDict["ID"] = PI_ID
    return jsonDict

def publish_data(data):
    queue.send_message(MessageBody=json.dumps(data)) 

def exit_gracefully(signum, frame):
    signal.signal(signal.SIGINT, original_sigint)
    
    ser.close()
    signal.signal(signal.SIGINT, exit_gracefully)

if __name__ == '__main__':
    original_sigint = signal.getsignal(signal.SIGINT)
    signal.signal(signal.SIGINT, exit_gracefully)
    ser = serial.Serial('/dev/ttyACM0')
    run_program(ser)

sqs_to_dynamo.py

import boto3
import time
import datetime
import ast
import decimal
import os


AWS_KEY=""
AWS_SECRET=""
REGION="us-east-1"


sqs = boto3.resource('sqs', aws_access_key_id=AWS_KEY,
                            aws_secret_access_key=AWS_SECRET,
region_name=REGION)

# Get the queue
queue = sqs.get_queue_by_name(QueueName='SensorData')

dynamodb = boto3.resource('dynamodb', aws_access_key_id=AWS_KEY,
                            aws_secret_access_key=AWS_SECRET,
                            region_name=REGION)

table = dynamodb.Table('SensorTable')

threshTable = dynamodb.Table('ThresholdTable')

sns = boto3.client('sns', aws_access_key_id=AWS_KEY,
                            aws_secret_access_key=AWS_SECRET,
                            region_name=REGION)

start = 0
end = 0
elapsed = 0

def thresholdLogic(deviceId, sensors):
    response = threshTable.get_item(Key={"ID": deviceId})
    print response
    if "Item" not in response:
        return
    thresDict = response["Item"]
    thresDict.pop('ID', None)

    breached = []
    
    for key in thresDict:
        if int(thresDict[key]) == -1:
            continue    
        if float(sensors[key]) > float(thresDict[key]):
            breached.append(key)

    if len(breached) > 0:
        sendMessage(breached, deviceId)



def sendMessage(breached, deviceId):
    global start
    global end
    global elapsed
    end = time.time()
    elapsed = end - start
    if elapsed < 30:
        return
    message = "The following sensor(s) exceeded the threshold on Device " + str(deviceId) + ": "
    for item in breached:
        attach = "\n" + item
        message += attach
    response = sns.publish(TopicArn='arn:aws:sns:us-east-1:320738006138:ece4180-sensor', Message=message)
    start = time.time()
    print response


while True:
    for message in queue.receive_messages(MaxNumberOfMessages=1):
        #print message.body
        data = ast.literal_eval(message.body)
        thresholdLogic(data["ID"], data["Sensors"])
        for key in data["Sensors"]:
            data["Sensors"][key] = decimal.Decimal(str(data["Sensors"][key]))
        print type(data)
        ts = time.time()
        data['Timestamp'] = data['timestamp']
        print data
        table.put_item(Item=data)
        message.delete()

Website_code.py

#!/usr/bin/env python
import flask
import boto3
import cPickle as pickle
import datetime
import time
import json
import sys
from flask import Flask, abort, request, make_response, url_for, render_template, redirect, jsonify
from boto3.dynamodb.conditions import Key, Attr
from random import *

application = Flask(__name__)

#Enter AWS Credentials


# Get the table
dynamodb = boto3.resource("dynamodb", aws_access_key_id=AWS_KEY,
                            aws_secret_access_key=AWS_SECRET,
                            region_name=REGION)

sensorTable = dynamodb.Table("SensorTable")
deviceTable = dynamodb.Table("DeviceTable")
thresholdTable = dynamodb.Table("ThresholdTable")

data = {
    "MQ2": -1, "MQ3": -1, "MQ4": -1, "MQ5": -1,
    "MQ6": -1, "MQ7": -1, "MQ8": -1, "MQ9": -1,
    "MQ135": -1
}

def getDevices():
    response = deviceTable.scan()
    items = response["Items"]
    devices = []
    if len(items) > 0:
        for item in items:
            devices.append(int(item["ID"]))
    devices.sort()
    return devices

def getThresholds(id):
    response = thresholdTable.get_item(
        Key = {"ID" : id}
    )
    if "Item" in response:
        item = response["Item"]
        thresholds = [
            float(item["MQ2"]),
            float(item["MQ3"]),
            float(item["MQ4"]),
            float(item["MQ5"]),
            float(item["MQ6"]),
            float(item["MQ7"]),
            float(item["MQ8"]),
            float(item["MQ9"]),
            float(item["MQ135"])
        ]
        return thresholds
    else:
        return []


@application.route("/", methods=["GET"])
def index():
    #print getDevices()
    return render_template("dashboard.html", devices=getDevices())

@application.route("/register", methods=["GET", "POST"])
def register():

    if request.method == "POST":
        deviceTable.put_item(
            Item={
                "ID" : int(request.form["ID"])
            }
        )

        thresholdTable.put_item(
            Item={
                "ID": int(request.form["ID"]),
                "MQ2": -1,
                "MQ3": -1,
                "MQ4": -1,
                "MQ5": -1,
                "MQ6": -1,
                "MQ7": -1,
                "MQ8": -1,
                "MQ9": -1,
                "MQ135": -1
            }
        )

        return redirect("/")
    else:
        return render_template("register.html", devices=getDevices())

@application.route("/delete/<int:id>", methods=["GET"])
def delete(id):

    deviceTable.delete_item(
        Key={
            "ID": id
        }
    )
    thresholdTable.delete_item(
        Key={
            "ID": id
        }
    )

    return redirect("/")


@application.route("/device/<int:id>", methods=["GET"])
def device(id):
    print getThresholds(id)
    return render_template("device.html", ID=id, devices=getDevices(), thresholds=getThresholds(id))


@application.route("/data/<int:id>")
def get_data(id):

    if False:
        for sensor, value in data.items():
            data[sensor] = randint(-100,100)
        #print("Data --> ", data)
        return json.dumps(data)


    try:

        #timestampold = datetime.datetime.fromtimestamp(ts-100).strftime("%Y-%m-%d %H:%M:%S")


        ts=time.time()
        time_ms = int((ts-5)*1000);
        timestamp = datetime.datetime.fromtimestamp(ts).strftime("%Y-%m-%d %H:%M:%S")

        print("Time_ms: ", time_ms)
        response = sensorTable.query(
            KeyConditionExpression=Key('ID').eq(id) & Key('timestampNum').gt(time_ms),
            #ScanIndexForward=False
        )
        items = response["Items"]

        print("Items Length: ", len(items))
        print("")
        #print("Items Length: ", items)

        if len(items) > 0:
            for item in items:
                data["MQ2"] = float(item["Sensors"]["MQ2"])
                data["MQ3"] = float(item["Sensors"]["MQ3"])
                data["MQ4"] = float(item["Sensors"]["MQ4"])
                data["MQ5"] = float(item["Sensors"]["MQ5"])
                data["MQ6"] = float(item["Sensors"]["MQ6"])
                data["MQ7"] = float(item["Sensors"]["MQ7"])
                data["MQ8"] = float(item["Sensors"]["MQ8"])
                data["MQ9"] = float(item["Sensors"]["MQ9"])
                data["MQ135"] = float(item["Sensors"]["MQ135"])
                print("Time_ms: ", time_ms)
                print("===>          {}".format(item["timestampNum"]))
                print("ID: {}".format(item["ID"]))
                print("Data: {}".format(data))
                break

            sec = int(time.strftime("%S"))
            #print("Second: ", sec)

        #
        print("================================================================")

        return jsonify(data)

    except Exception as err:
        print("Unexpected error:", err)
        pass

    return json.dumps(data)


@application.route("/threshold/<int:id>", methods=["GET", "POST"])
def threshold(id):
    if request.method == "POST":
        item = {
                "ID" : id,
                "MQ2" : request.form["th0"],
                "MQ3" : request.form["th1"],
                "MQ4" : request.form["th2"],
                "MQ5" : request.form["th3"],
                "MQ6" : request.form["th4"],
                "MQ7" : request.form["th5"],
                "MQ8" : request.form["th6"],
                "MQ9" : request.form["th7"],
                "MQ135": request.form["th8"]
        }
        #print item
        thresholdTable.put_item(
            Item=item
        )
        return redirect("/device/{}".format(id))
    else:
        return render_template("threshold.html")

if __name__ == "__main__":
    application.run(host="0.0.0.0", port=8080)

Project Pictures:

/media/uploads/laughatm2/img_7052.jpg

Demo video:


Please log in to post comments.