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###################################################