Project : Measure air quality
The purpose is to build a system which can measure some elements of air quality
Requirements
- Measure dust
- Measure temperature
- Measure humidity
- Measure gases/smoke
- Report realtime measurement on a local display
- Upload data to an external site, where data can be shown in graphs
- Log data locally, so the program can collect data without an internet connection.
- LEDs to indicate a healthy operation (green) or errors (red)
- Show data in local LCD
Due to requirements to keep it simple to put together and code, I chose to do this on the Grove platform, which sits on top of a Raspberry Pi and has a wide range of sensors, which plugs straight in. Further more most of the sensors has a RPi library making coding much simpler.
Resources
Links to sources of information for the components needed beside the RPi:
Grove Pi start kit
Grove Pi Dust sensor
Grove Pi Multichannel gas sensor (does not have a RPi library, so I will deal with this last)
Grove Pi MQ2 gas sensor
Grove Pi light sensor
Grove Pi sound sensor
Grove LCD display
Preparation of Raspberry Pi & Grove Pi
***as of 13th of April 2020***
- Use a standard installation of the RPi and update/upgrade.
- Install the Grove Pi software using the guide
- Upgrade firmware on the Grove Pi
- Install the libraries needed for uploading data to Thingspeak:
- Paho MQTT
sudo pip3 install paho-mqtt
- Psutil
sudo pip3 install psutil
- Paho MQTT
- Connect sensors to ports as mentioned in the code or as you see fit, just remember to connect digital to digital, analog to analog and that the Dust sensor has to be connected to D2
Solution to upload data
Thingspeak will be used to upload data to. I did search for examples, but most used httplib and urllib, which has changed with Python3, so the examples didn’t work.
So I decided on using another method.Example used for uploading data
Current build and requirements developed (v8)
Sensors:
- Temperature
- Humidity
- Dust
- light
- Sound
- Gas/smoke
Requirements:
- Present data on LCD
- Upload data to Thingspeak
- Send email warnings
- Log data in a file
Code is listed below
Issues noted and worked around
- For some reason the data from the light sensor and sound sensor has to be collected before the Temperature & Humidity sensor, otherwise the data is invalid and the same.
- Most examples of uploading data to Thingspeak use libraries not working with Python 3
- There were mismatches between what sensors were supported on the RPi and which were not in the Seeedstudio documentation. Check online and on Dexterindustries website & Github
Live data from a sensor
#######################################importing libraries#################################### from __future__ import print_function import paho.mqtt.publish as publish import psutil import grovepi import string import random import time import atexit from grove_rgb_lcd import * from grovepi import * import smtplib import logging #######################################Setting up sensors and values############################ atexit.register(grovepi.dust_sensor_dis) HTsensor = 7 #D7 light_sensor = 1 #A1 SoundSensor = 0 #A0 gas_sensor = 2 #A2 LED_green = 4 #D4 LED_red = 3 #D3 pinMode(LED_green,"OUTPUT") pinMode(LED_red,"OUTPUT") grovepi.dust_sensor_en() grovepi.pinMode(gas_sensor,"INPUT") SMTP_SERVER = 'smtp.gmail.com' #Email Server (don't change!) SMTP_PORT = 587 #Server Port (don't change!) GMAIL_USERNAME = 'XXXXXXXXX@gmail.com' #change this to match your gmail account GMAIL_PASSWORD = 'XXXXXXXXX' #change this to match your gmail password MaxDust=5000 # Max level of dust. If levels above is registered a warning email is sent emailsend=0 logging.basicConfig(filename='airquality.log',format='%(asctime)s %(message)s',datefmt='%d-%m-%Y %H:%M:%S',level=logging.DEBUG) #############################Setting up data to write data to Thingspeak######################## string.alphanum='XXXXXXXXX'#insert yours channelID = "XXXXX" #insert yours writeAPIKey = "XXXXXXXXX" #insert yours mqttHost = "mqtt.thingspeak.com" mqttUsername = "XXXXXXXXX" #insert yours mqttAPIKey ="XXXXXXXXX" #insert yours tTransport = "websockets" tPort = 80 topic = "channels/" + channelID + "/publish/" + writeAPIKey #############################Setting up email class############################################ class Emailer: def sendmail(self, recipient, subject, content): #Create Headers headers = ["From: " + GMAIL_USERNAME, "Subject: " + subject, "To: " + recipient, "MIME-Version: 1.0", "Content-Type: text/html"] headers = "\r\n".join(headers) #Connect to Gmail Server session = smtplib.SMTP(SMTP_SERVER, SMTP_PORT) session.ehlo() session.starttls() session.ehlo() #Login to Gmail session.login(GMAIL_USERNAME, GMAIL_PASSWORD) #Send Email & Exit session.sendmail(GMAIL_USERNAME, recipient, headers + "\r\n\r\n" + content) session.quit sender = Emailer() sendTo = 'XXXXX@gmail.com' #email to get warnings emailSubject = "Hello World" emailContent = "This is a test of my Emailer Class" #####################################Program start############################################# while True: try: #Set green operation digitalWrite(LED_green,1) digitalWrite(LED_red,0) #Gather and convert data from sensors sound_level = grovepi.analogRead(SoundSensor) light_intensity = grovepi.analogRead(light_sensor) sensor_value = grovepi.analogRead(gas_sensor) density = (float)(sensor_value / 1024.0)# Calculate gas density - large value means more dense gas density = round(density,3) #Rounding a very long number to something short [new_val,other,lowpulseoccupancy] = grovepi.dust_sensor_read() lowpulseoccupancy = round(lowpulseoccupancy,1)#Rounding a very long number to something short [temp,hum] = grovepi.dht(HTsensor,0) #Print data to concole print ("Light =" , light_intensity) print ("Sound =", sound_level ) print ("Gas sensor data: value =", sensor_value, " Gas density =", density) print ("Dust sensor data : new_val=" , new_val, " other=", other, " lowpulseoccupancy=",lowpulseoccupancy) print ("Temperature =", temp, "C") print ("Humidity =", hum,"%") print ("Light =",light_intensity) print ("Sound =",sound_level) #Prep connection to Thingspeak clientID='' # Create a random clientID. for x in range(1,16): clientID+=random.choice(string.alphanum) #Prepare data for use on LCD & Thingspeak t = str(temp) h = str(hum) d = str(lowpulseoccupancy) Md = str(MaxDust) l = str(light_intensity) s = str(sound_level) g = str(density) #To avoid posting empty data as "0" from the dust sensor. New_val means there is new data if new_val: print ("**New data from the dustsensor - publish to Thingspeak & send email**") #build the payload string for Thingspeak payload = "field1=" + t + "&field2=" + h + "&field3=" + d + "&field4=" + l + "&field5=" + s + "&field6=" + g # attempt to publish data to the topic on Teamspeak. try: publish.single(topic, payload, hostname=mqttHost, transport=tTransport, port=tPort,auth={'username':mqttUsername,'password':mqttAPIKey}) print ("Published to Teamspeak to host:" ,mqttHost , " clientID=" ,clientID) except (KeyboardInterrupt): break except: print ("There was an error while publishing the data.") digitalWrite(LED_green,0) digitalWrite(LED_red,1) #Sending emails if (emailsend==0 and lowpulseoccupancy>MaxDust): emailSubject = "Dust level is above limit" emailContent = "At " + time.ctime() + " dust was registered at " + d + " with a warning level of " + Md sender.sendmail(sendTo, emailSubject, emailContent) print ("Warning eMail has been sent") emailsend=1 elif (emailsend==1 and lowpulseoccupancy<=MaxDust): emailSubject = "Dust level has dropped below limit" emailContent = "At " + time.ctime() + " dust was registered at " + d + " with a warning level of " + Md sender.sendmail(sendTo, emailSubject, emailContent) print ("Email stating dust is below max has been sent") emailsend=0 elif (emailsend==1 and lowpulseoccupancy>MaxDust): print ("Dust above MAX, but email has already been sent...") else: print ("Dust was registered at " + d + " with a warning level of " + Md + ". No need for a warning email") emailsend=0 #logging data log_entry = "Temp=" + t + " & Humidity=" + h + " & Dust=" + d + " & Light=" + l + " & Sound=" + s + " & Gas=" + g logging.info(log_entry) #Print on LCD setText("T:" + t + "C" + " H:" + h + "%" + " Dust:" + d + "psc/L") #Set LCD color for day and night time, so day is Blue and night is dark Red if (light_intensity>120): print ("Daytime") setRGB(0,0,255) else: print ("Nighttime") setRGB(100,0,0) print ("**************************************************************") #Wait 60 seconds time.sleep(60) except (IOError,TypeError) as e: print("Error") digitalWrite(LED_green,0) digitalWrite(LED_red,1) #################################Program end###################################################