The World Health Organization estimates that 4.2 million people die each year from causes directly attributed to air pollution. With that in mind, even being aware of air particulates and toxins wage a difficult battle could help reduce the 7 million premature deaths every year from exposure to air pollution. Many people often don’t even realize that their own home’s indoor air quality is polluted.
However, in this day and age of powerful IoT technologies, any person can combat bad air quality. There are devices specifically made to detect particulate matter (PM) using PM sensors and CO2 sensors, send the information to a central hub using Bluetooth or WiFi, and display this data to users through devices such as home assistants like Amazon Alexa. Air purifiers are also used to help combat poor air quality by filtering the polluted air.
If you want to create your own DIY air quality sensor and monitoring system, sensors and microcontrollers have gotten so cheap, small, and simple to implement, that practically anyone can install any type of monitor or sensor in their very own home. Among other possibilities, these sensors can trigger things like alerts and notifications to spur user action.
In this tutorial, you'll learn how easy it is to create an air quality sensor and monitoring system that obtains air quality data using a few low-cost electronics and Functions. The sensor will extract air quality information with multiple sensors, graph that data in real time, and even set off a speaker alarm if the air quality reaches a harmful critical point, effectively creating your own DIY air quality monitor.
Listed below are the required materials needed to complete this tutorial. You can purchase this hardware from the links provided below or from online retailers such as Amazon and AliExpress.
Arduino Uno is one of the cheapest open-source microcontrollers available today. Since the Uno has an onboard analog-to-digital converter adapter, the analog signals produced from the sensors used in this tutorial can be properly read.
A breadboard is a tool used to prototype and test circuits. Breadboards have two long rails (red rail for power and blue rail for ground) on each of the sides that are each connected vertically down the board. This is where engineers typically plug in a power supply and ground so that the whole rail can be easily used as the power and ground connector. At the center of the board, there are groups of 5-holed horizontal lines. Each of these lines is horizontally connected (5-hole connection) which is used for the circuit itself. You can even connect one grouping to another to make a 10-hole connection and so on and so forth.
You can purchase a breadboard with some wires typically in a bundled kit.
A speaker is essentially a piece of vibrating metal that moves up by a magnet when fed power and falls down when unpowered. When the magnet is turned on and off at a specific frequency, a tone can be played. Purchase a cheap speaker from an online retailor mentioned before.
Each of the sensors you're going to use throughout this tutorial operates on the same fundamental principle. A specific type of material is placed inside a sensor and an electric current is fed through it. When certain environmental conditions occur that react with the sensor’s specific material, the material’s resistance to the current increases or decreases. The current then will be affected and the sensor can translate that change into proper sensor units.
You'll need to pick up the following sensors needed for this tutorial:
The wiring of each of these sensors follows the same pattern:
Connect the ground pin of the sensor to the ground of the Arduino.
Connect the power pin of the sensor to the 5.5V pin of the Arduino.
Connect the data pin of the sensor to a GPIO pin on the Arduino.
The sensors were connected in the following schematic.
The code for this logic is set up in three parts: Arduino sketch, publishing code, and graphing HTML code.
The Arduino sketch will handle the direct communication between the sensors and the Arduino.
The publishing code will relay the data received by the Arduino to the PubNub Arduino SDK for publishing, which is used to communicate with PubNub’s APIs.
The HTML code will then visualize the data into a graph in real time on an HTML webpage.
Note: Please note that the PubNub Arduino SDK is no longer officially supported.
Before you begin implementing the code behind, make sure you sign up for a free PubNub account, as you'll need your publish/subscribe keys to send information across the PubNub Network.
For the Arduino code, you will be developing in the Arduino IDE in order to utilize its serial bus capabilities and simple method call structure.
Begin by importing all of the necessary libraries for each of our sensors so you can communicate with them in the Arduino IDE.
To do this, download the .zip file from each library’s GitHub and add them to your Sketch.
GitHub links: DHT11, MQ135, MQ7, and MQ2.
#include <dht.h> #include <MQ135.h> #include <MQ7.h> #include <MQ2.h>
Then you will need to create variables and instances for each of your sensors.
DHT11 #define DHT11_PIN 16 //place this line below the include statements //DHT int chk; dht DHT; //MQ135 float M135; MQ135 gasSensor = MQ135(A3); //MQ2 int pin = A0; MQ2 mq2(pin); //MQ7 float ppm; MQ7 mq7(A1, 5.0);
Next, you will need to create a setup function to initialize your Arduino’s baud rate as well as initiate any sensor’s necessary setup modules (in this case the MQ2).
void setup() { Serial.begin(9600); mq2.begin(); }
To keep the program continuously running, create a loop method with a delay of one second.
void loop() { //rest of the code delay(1000) }
Inside the loop is where you will implement the code to communicate with the sensor’s data. For each sensor, you use the methods defined in their libraries to extract the data from the sensor and format the raw voltage reading to a recognizable unit of measure.
The code for each sensor is as follows:
MQ7 = analogRead(A1); ppm = mq7.getPPM(); //Serial.print(" MQ7 = "); //Serial.println(ppm,1); //----------------------------------- M135 = gasSensor.getPPM(); //Serial.print(" MQ135 = "); //Serial.println(M135,1); //----------------------------------- //MQ2 float lpg = mq2.readLPG(); float smoke = mq2.readSmoke(); //Serial.print(" lpg = "); //Serial.print(lpg,1); //Serial.print(" Smoke = "); //Serial.println(smoke,1); //----------------------------------- //DHT11 chk = DHT.read11(DHT11_PIN); if(DHT.temperature > -500) //don’t read any absurd readings { //Serial.print("Temperature = "); Serial.println(DHT.temperature); } if(DHT.humidity > -500) //don’t read any absurd readings { //Serial.print("Humidity = "); Serial.println(DHT.humidity); }
Note: You need to comment out the serial.print
lines except for temperature and humidity. This makes future steps simpler. But if you’d like to test your devices, uncomment them. You can view the serial prints in the Arduino serial monitor. If you do this, make sure each sensor is publishing to a new line each time.
Next, you'll connect the speaker to the program. As discussed before, you need to implement the speaker to operate at a certain frequency to play a specific tone.
Create a new file named pitches.h
and include the pitches.h file via #include "pitches.h"
.
#define NOTE_B0 31 #define NOTE_C1 33 #define NOTE_CS1 35 #define NOTE_D1 37 #define NOTE_DS1 39 #define NOTE_E1 41 #define NOTE_F1 44 #define NOTE_FS1 46 #define NOTE_G1 49 #define NOTE_GS1 52 #define NOTE_A1 55 #define NOTE_AS1 58 #define NOTE_B1 62 #define NOTE_C2 65 #define NOTE_CS2 69 #define NOTE_D2 73 #define NOTE_DS2 78 #define NOTE_E2 82 #define NOTE_F2 87 #define NOTE_FS2 93 #define NOTE_G2 98 #define NOTE_GS2 104 #define NOTE_A2 110 #define NOTE_AS2 117 #define NOTE_B2 123 #define NOTE_C3 131 #define NOTE_CS3 139 #define NOTE_D3 147 #define NOTE_DS3 156 #define NOTE_E3 165 #define NOTE_F3 175 #define NOTE_FS3 185 #define NOTE_G3 196 #define NOTE_GS3 208 #define NOTE_A3 220 #define NOTE_AS3 233 #define NOTE_B3 247 #define NOTE_C4 262 #define NOTE_CS4 277 #define NOTE_D4 294 #define NOTE_DS4 311 #define NOTE_E4 330 #define NOTE_F4 349 #define NOTE_FS4 370 #define NOTE_G4 392 #define NOTE_GS4 415 #define NOTE_A4 440 #define NOTE_AS4 466 #define NOTE_B4 494 #define NOTE_C5 523 #define NOTE_CS5 554 #define NOTE_D5 587 #define NOTE_DS5 622 #define NOTE_E5 659 #define NOTE_F5 698 #define NOTE_FS5 740 #define NOTE_G5 784 #define NOTE_GS5 831 #define NOTE_A5 880 #define NOTE_AS5 932 #define NOTE_B5 988 #define NOTE_C6 1047 #define NOTE_CS6 1109 #define NOTE_D6 1175 #define NOTE_DS6 1245 #define NOTE_E6 1319 #define NOTE_F6 1397 #define NOTE_FS6 1480 #define NOTE_G6 1568 #define NOTE_GS6 1661 #define NOTE_A6 1760 #define NOTE_AS6 1865 #define NOTE_B6 1976 #define NOTE_C7 2093 #define NOTE_CS7 2217 #define NOTE_D7 2349 #define NOTE_DS7 2489 #define NOTE_E7 2637 #define NOTE_F7 2794 #define NOTE_FS7 2960 #define NOTE_G7 3136 #define NOTE_GS7 3322 #define NOTE_A7 3520 #define NOTE_AS7 3729 #define NOTE_B7 3951 #define NOTE_C8 4186 #define NOTE_CS8 4435 #define NOTE_D8 4699 #define NOTE_DS8 4978
Next, copy and paste these methods that are responsible for playing a sound using the tone referenced in pitches.h.
int melody[] = {NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3, 0, NOTE_B3, NOTE_C4}; // note durations: 4 = quarter note, 8 = eighth note, etc.: int noteDurations[] = {4, 8, 8, 4, 4, 4, 4, 4}; void playTones() { // iterate over the notes of the melody: for (int thisNote = 0; thisNote < 8; thisNote++) { // to calculate the note duration, take one second divided by the note type. //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc. int noteDuration = 1000 / noteDurations[thisNote]; tone(8, melody[thisNote], noteDuration); } }
Now you can have the sound trigger if any of the sensor values reach above a certain threshold. You will need to create your own critical variables through trial and error as each sensor will come with a noticeable margin of error.
if((lg > lg_crit) || (smoke > smoke_crit) || (ppm > ppm_crit) || (DHT.temperature > temp_crit) || (DHT.humidity > hum_crit) || (M135 > M135_crit)) { playTones(); }
Once you have finished, select your Arduino’s USB port, hit the green checkmark to compile, and the green right arrow to upload it to your connected Arduino.
Tip: You can find the name of the port your Arduino is connected to by going into the top menu: Tools->Port.
You might be wondering, what is the need to separate the code just to publish messages from the Arduino? Isn't it easier to do it straight from the sketch?
To simplify a complicated answer, the PubNub Arduino SDK requires the use of a WiFi shield to enable Internet capabilities. Since that is out of the scope of this project, we will revert to an alternative method: pySerial.
pySerial is a library that tunes in on the Arduino’s serial monitor. Anything printed over the serial monitor can be read by pySerial. Thus, we can extract the data from the serial bus and publish that data through PubNub.
To install pySerial, type the following command into your terminal.
pip install pyserial
Then create a python script by giving it a name and include the necessary PubNub libraries to be able to use the PubNub Arduino SDK.
import serial from pubnub.callbacks import SubscribeCallback from pubnub.enums import PNStatusCategory from pubnub.pnconfiguration import PNConfiguration from pubnub.pubnub import PubNub from time import sleep pnconfig = PNConfiguration()
Then you can set up your pySerial port connection.
//Then set up your Pyserial port connection with the following line: Arduino_Serial = serial.Serial('YOUR PORT NAME',9600, timeout=1) //Now create a PubNub Instance pnconfig.subscribe_key = "YOUR SUBSCRIBE KEY" pnconfig.publish_key = "YOUR PUBLISH KEY" pnconfig.ssl = False pubnub = PubNub(pnconfig) //Create a Publishing Callback def publish_callback(envelope, status): pass
Next, create the infinite loop of the program that continuously extracts and publishes data. Be careful, as pySerial’s serial bus monitor can only see data coming in serially. It cannot distinguish names or variables, only numbers separated by the new line character.
Since you are going to work with two different sensor readings (temperature and humidity), we can expect the first line of data to be from sensor A, the second by Sensor B, the third by Sensor A, the fourth from Sensor B, and so on.
Going back to the Arduino sketch covered earlier, the temperature sensor is printing first in the code, so you can expect that the pattern will go temperature, then humidity, then repeat. You should capture the data in that pattern.
#read in data from the serial bus 5(depends on the length of target data) #characters at a time #NOTE: you must decode the data as serial code is different from human code! temp = str(Arduino_Serial.readline().decode('ascii')) hum = str(Arduino_Serial.readline().decode('ascii'))
Next, print the values to the terminal before publishing using PubNub in order to see if you’re getting valid data.
print(temp+ ' ') print(hum + ' ')
If everything is running smoothly, your terminal should look as follows. Please note that the alternation of data – 29 corresponds to temperature and 34 corresponds to humidity).
Note: Please note that the rest of the tutorial discusses publishing this data to PubNub's real-time data visualization framework, PubNub EON. PubNub EON and the PubNub EON Chart SDK which is used to communicate with Project EON, are no longer officially supported.
Finally, you're going to publish this data to our real-time data visualization framework: PubNub EON. In order to send the data that EON can parse and graph, you need to format the data first into JSON format.
dictionary = {"eon": {"temp": temp, "hum": hum }}
Then you can publish to the same channel that the chart is subscribed.
pubnub.publish().channel("eon-chart").message(dictionary).pn_async(publish_callback)
You will now use PubNub EON to display the readings in real time on a live chart.
To add PubNub EON to any HTML page, add a
tag in your body, which will inject the actual chart. Then import the script tag SDK:
<script src="https://pubnub.github.io/eon/v/eon/1.0.0/eon.js"></script> <link rel="stylesheet" href="https://pubnub.github.io/eon/v/eon/1.0.0/eon.css"/>
Then subscribe to the eon-chart channel where you will be posting data.
eon.chart({ channels: ['eon-chart'], history: true, flow: true, pubnub: pubnub, generate: { bindto: '#chart', data: { labels: false } } });
You now have a connected device collecting and streaming air quality readings and publishing these results to a live dashboard, all in real time. You'll be alerted with a sound to indicate that these thresholds you set up earlier have been reached.
If you would like to learn more about how to power your IoT applications and smart home systems, take a look at our IoT resources.
If you have any other questions or concerns, please feel free to reach out to devrel@pubnub.com.
There are common underlying technologies for a dating app, and in this post, we’ll talk about the major technologies and designs...
Michael Carroll
How to use geohashing, JavaScript, Google Maps API, and BART API to build a real-time public transit schedule app.
Michael Carroll
How to track and stream real-time vehicle location on a live-updating map using EON, JavaScript, and the Mapbox API.
Michael Carroll