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.
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
Mbed | MQ Sensor |
---|---|
VU | VCC |
GND | GND |
NC (not connected) | DO |
AnalogIn Pin (p15, p16,p17,p18, p19, or p20) with Voltage Divider Circuit | AD |
Speaker
Mbed | Speaker | 2N3904 |
---|---|---|
P26 (Any PwmOut pin) | BASE | |
GND | EMITTER | |
- | COLLECTOR | |
external 5V | + |
LED
Mbed | LED |
---|---|
P23 (Any PwmOut pin) through 180 ohm resistor | + (longer pin) |
GND | - (shorter pin) |
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:
Demo video:
Please log in to post comments.