IOT Enabled Air Monitoring Station

Part 1: Overview

Acknowledgement: My Colleague Madhusudhan Anand, co-founder and CTO of Ambee, came up with this project idea and guided me through its implementation.

Humans are today exposed to air pollution of all kinds, and affected by the harmful effects of the pollutants in the air they breathe. There are many causes of air pollution. One of the causes is vehicular traffic. Smoke generated by factories is also let out into the air. The burning of fossil fuels like coal also leads to harmful smoke.

The high levels of air pollution cause respiratory and cardiac problems, and affect other parts of the human body. It also causes other diseases like cancer. Millions of people around the world die each year due to air pollution. And it is not just a problem for countries like China and India. In fact, London reached its entire year’s of air pollution quota within the first 5 days of 2017, and Paris is now routinely choked in smog.

Me as a Hardware Intern and an Engineer, I was really excited to build the Air Monitoring Station. From my office at Church Street, it is sometimes hard to see buildings across street due to heavy smog. It is estimated that living in Bangalore is equivalent to smoking six cigarettes per day.

Smog in Delhi, India

Most of the Air monitoring System will cost you around ₹20k25k, So when me and my colleague discussed about this idea of a very inexpensive, internet connected, personal air quality monitor, I was immediately intrigued. For an IoT device that costs less than $55, it is not only a science project, but also a potential game changer for healthcare (including public health research), as we can now track an individual’s accurate exposure to smog and study how it correlates with health problems.

Smog particulates above 350ug/m³ is considered extreme pollution. The “normal” range should be under 30ug/m³. In Beijing, it sometimes reaches 500ug/m³.

This article is the first of a 4-part series of tutorials on IoT enabled AMS (Air Monitoring Station) development.

Part 1: Overview
Part 2: Set up NodeMCU
Part 3: NodeMCU IOT Applications for Air Monitoring Station
Part 4: Set up web server to receive data and store in Database

Prototype Device

Here is how our assembled device looks like. Bring the device to anywhere with Wifi coverage, and it will send air quality measurements to an online web server every 10 seconds.


At the heart of the project is a small module called NodeMCU. Costing ₹335 at retail, NodeMCU is an amazing device that is Moderate to program and interact with (in the LUA scripting language), making it an ideal choice for makers, or school science projects.

NodeMCU Module

NodeMCU contains an ultra-cheap (₹335) yet very powerful chip that is essentially a complete modern computer. It can be powered and programmed via USB. Like an Arduino or a Raspberry Pi, NodeMCU can connect to a wide variety of sensors, and run programs to read data from those sensors. It also has a built in Wifi chip and antenna to connect to any wifi network. This way, the device can send its sensor data to any internet database (e.g. via MQTT services), or to host its own web server to display its sensor data on a web page.
To learn more about how to set up a NodeMCU development environment,
Please refer to Part 2: Set up NodeMCU.

Air Quality Sensor(Sharp Sensor – GP2Y1010AU0F)

GP2Y1010AU0F is a dust sensor by optical sensing
system. An infrared emitting diode (IRED) and an phototransistor are diagonally arranged into this device. It detects the reflected light of dust in air. Especially, it is effective to detect very fine particle like the cigarette smoke. In addition it can distinguish smoke from house dust
by pulse pattern of output voltage.
1. Connection of case and GND
Case material use conductive resin as cover case {printed model No.} and metal {test terminal side} as bottom cover. The metal case connects with GND in sensor.
2. Cleaning
Please don’t do cleaning, because there is a case that this device is not satisfied with its characteristics by cleaning.
3. Pulse input range
Please subject to recommendation as regard input condition for LED in order to keep reliability.
4. Dust adhesion
There is a case that this product does not detect the dust density correctly, since the dust adhered to the inside of the dust through hole may project into the detecting space which consist of emitter and detector light axis. Please take the structure and mechanism of the equipment into consideration to avoid the influence of adhered dust. And when the dust is adhered, please consider the maintenance such as vacuuming or blowing off the dust by air. In addition, please pay attention to structure and placing location of the application to avoid any adhesive particle like oil, etc. to gets into the device. If it sticks to optical part, malfunction may occur.
5. Light output
In circuit designing, make allowance for the degradation of the light emitting diode output that results from long continuous operation. (50% degradation/5 years)
6. Sensitivity adjustment VR
VR for sensitivity adjustment is set up at shipping from sharp. Please do not touch the VR or Electro-optical characteristics specified on the specification will be invalid.
7. Resolution
Please do not disassemble the device such as removing tapping screw and so on. Even if the device is reassembled, it may not satisfy the specification.
8. Application to fire alarm
Please do not use this device for a fire alarm application. When using this device to application other than air purifying and equipment with air purifying function, please inform us before usage.
9. Noise influence
If the sensor is located close to noise generator (ex. Electric dust collector, etc. ), the sensor output may be affected by leaded noise. On top of that noise from power supply line also may affect the sensor output. When desinging the system, please consider the effect from noise.
10. Vibration influence
The sensor may change its value under mechanical oscillation. Before usage, please make sure
that the device works normally in the application.
11. Incident light influence
There is a case that the sensor output may be affected when outer-light comes through dust through hole on printed side. In order to avoid any influence from outer-light, please locate the printed side of sensor facing to inside of the application.
12. When inside of the sensor is moisturized, this product does not keep its proper function. Please design the application so that moisturization of the sensor does not happen.

The choice of long wavelength infrared light is to minimize Rayleigh scattering by air molecules.

To learn more about how to write a LUA application to read air quality data from air quality sensors and send the data to an online web server,
Please refer to Part 3: NodeMCU IoT Applications for Air Quality Sensors.

Online Web Server

One of the chief benefits of IoT sensors is that their data can be aggregated, stored, analyzed, and displayed on an Internet database. There are two ways to send IoT sensor data to an online destination.

First, the IoT device could make an HTTP web service call (typically a RESTful service) and pass in the data. That is how we will build our online Web server and Database application for the air quality sensor.
See more in Part 4: Set up web server to receive data and store in Database

Second, it could utilize a special “machine-to-machine” messaging protocol called MQTT. MQTT goes over TCP/IP and is lighter than regular HTTP. It has some very nice features that are specific to IoT. For example, the device can not only send messages to a MQTT server, but also subscribe to messages from the server. Compared with HTTP web services, MQTT does not require the device to pull from the server all the time.

Most IoT device platforms, including the NodeMCU, supports MQTT out of the box (or as a firmware option). However, building our own MQTT server is a hassle. Cloud services, such as IBM Watson IoT and Microsoft Azure IoT Hub, are great solutions.

Part 2: Set up NodeMCU

In this Part, I will discuss how to setup and program NodeMCU from scratch. If you want to use NodeMCU for a science project or product prototyping, this is a good place to start.

This article is the second of a 4-part series of tutorials on IoT Enabled AMS(Air Monitoring Station) development.

The most interesting thing about NodeMCU is that it is based on a ultra-cheap ₹335 chip (ESP8266) that contains all crucial elements of the modern computer: CPU, RAM, networking (WIFI), and even a modern operating system and SDK. The NodeMCU development kit is an open source hardware/software project that provides a PCB board and some additional hardware to make the ESP8266 more accessible in an development environment (i.e., to put on a breadboard, or to connect via USB, or to reset via a button), and more importantly, to provide a LUA scripting language-based software interface for the system. In essence, NodeMCU provides an easy-to-use development platform for anyone to experiment and prototype with the ESP8266 chip.

PINs on the NodeMCU board. For example, A0 is the analog voltage input, D0 also controls the on-board red LED.

This article is based on a late model Mac OS X computer. If you use a Linux machine, the instructions are essentially the same. For Windows users, please install python and pip first.

Step 1:
Download and install a “USB to serial port(ch340g)” driver application on your computer. It is needed for our computer to communicate with the NodeMCU board via a standard USB cable. Make sure you are using a good or new usb cable and also make sure the cable should be atleast 1 meter long.

Step 2:
Let’s create our own custom NodeMCU firmware. To do that, go to this web site and pick and choose the options you want. The Modules that must be included in our firmware are ADC, File, End User Setup, GPIO, HTTP, NET, NODE, TIMER, UART, WIFI.

Once the firmware is created, the system will email you a link to download the firmware binary file. Choose the build with float number support unless we know our application will only deal with integer numbers. Besides the standard / default choices, the additional options I selected are:
* ADC: Support for measuring analog input (voltage level) on the NodeMCU board’s A0 pin.
* HTTP: Support for writing code to handle HTTP requests.
* SSL / TLS: Support for HTTPS secure connections.

Other useful options include:
* MQTT: Support for MQTT protocol to send data to other devices or servers using a publish/subscribe model over TCP/IP.
* websocket: A convenience library to access websocket-based web services.
* DHT: A convenience library to read data from DHT family of environmental sensors.
* enduser setup: Support a “capture portal” to let the user enter her own wifi password, without having to hardcode wifi credentials in application code.

Step 3:
Install the esptool Python library, and use it to flash the firmware we just downloaded to the NodeMCU device.

With Python and Pip installed on our computer, we can run the following command to install esptool and all its dependencies.
Note: python2.7 and pip will be installed in mac OS by default.

Connect the NodeMCU device to the computer using a New or good USB cable. The blue light on NodeMCU will flash briefly upon connection. Run the following command to flash the firmware you just downloaded (the *.bin file) to the NodeMCU.

On Mac, the port is /dev/cu.SLAB_USEtoUART as described above; On windows, the port could be COM8; On Linux, the port could be /dev/ttyUSB0.
In my case tty.wchusbserialfd120 is the port.

In order to find your serial port, run the following command

Step 4:
Now we will Connect our NodeMCU and run some LUA code! When we installed esptool , the system also installed miniterm as a dependency. So,we will go ahead and run the following command.
Press the RESET button on the NodeMCU device.

Now, Press the RESET button again on the NodeMCU device and then Press RETURN(ENTER) on the computer keyboard a few times. We will see some random characters, and finally an interactive command prompt!

--- Miniterm on /dev/cu.SLAB_USBtoUART 115200,8,N,1 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---

On the command prompt, we can now run LUA scripts. Try this:

> print ("Hello World")
Hello World

Now, we can experiment with it. A good place to start is simple code snippets from the “Examples” section of the NodeMCU web site. (For Eg. choose the blink code)

As an alternative to miniterm , we could use the ESPlorer GUI tool. We will need to have Java installed on this machine. (we will be using ESPLORER for the lua code.)

Step 5:
Whenever the NodeMCU board powers up or resets, it executes the init.lua script. This script is the starting point of your application. Below is an example init.lua script, which blinks the on-board red LED light every second.

-- D0 is the LED on the NodeMCU board
lpin = 0
-- timer to run every 1000ms
tmr.alarm(1, 1000, tmr.ALARM_AUTO, function()
-- Turn on the LED
gpio.write(lpin, gpio.LOW)
-- Keep it on for 100ms
-- Turn off the LED
gpio.write(lpin, gpio.HIGH)

Step 6:
From my experience, we will need to run miniterm first and see the LUA command line prompt. Then, use CTRL-] to exit miniterm and then run Use the luatool program to install the init.lua script onto the device. We can download luatool here. Then, run the following command from the same folder as the init.lua file. The reason is that luatool is less tolerant to noise in the serial port. So, we should run miniterm first to clear up the channel.

$ python --port /dev/cu.SLAB_USBtoUART --src init.lua --dest init.lua --verbose

Next Steps:
In Part 3 of this article series, we will discuss how to write LUA applications to read data from air quality sensors, and send the data to a cloud-based data dashboard.

Part 3: NodeMCU IOT Applications for Air Monitoring Station.

In this Part, I will discuss how to connect air quality sensors to a NodeMCU device, and write applications for capturing and uploading the data.

This article is the third of a 4-part series of tutorials on IoT Enabled AMS(Air Monitoring Station) development.

In a typical NodeMCU system, NodeMCU is powered by a USB connection, and peripheral sensors draw 3.3V or 5V power supply from appropriate NodeMCU pins.

The Air Quality Sensor we will be using is “Sharp Sensor GP2Y1010AU0F”. It costs ₹599 on Amazon.

Connecting Dust sensor to NodeMCU

Dust sensor - NodeMCU
V-LED - Vin(With a 150ohm resistor)
I-LED - D5
VCC - 5V(Vin)

The control sequence of the sensor is somewhat complicated.(Must go through Dust sensor GP2Y1010AU0F Datasheet in order to understand the working of Dust Sensor). The sensor is turned on by a pulse from the ILED (yellow) input wire. This pulse must remain in the ON state for 0.32ms. At 0.28ms into the pulse’s ON state, we are supposed to measure the voltage on the AOUT (blue) wire to determine the PM density.

In the init.lua(Main code) application, the NodeMCU first Configure the assigned wifi network.(i.e SSID & PASSWORD)

The below code does not support lua anymore. Since most of the link have used this code, i would like to mention it in this article and also provide the correct configuration code.

-- setup Wifi

The above code is not applicable in lua anymore.

This is the correct wifi configuration code.

We then take one measurement every 10 seconds in a loop.

tmr.alarm(1, 10000, tmr.ALARM_AUTO, function()

The measurement starts by setting a pulse on D5 connected to ILED wire

-- D5 is the driver PIN for the dust detector
dpin = 5

wait for 0.28ms, and then read the ADC voltage value from A0

-- turn on the red LED on the NodeMCU board
gpio.write(lpin, gpio.LOW)
-- Per the spec: Warm up for 10ms,
-- generate a pulse of 0.32ms wide,
-- take reading at 0.28ms into the pulse.

Once the voltage is measured and convert to a PM value, NodeMCU makes a HTTP request to a server (I will discuss this in Part 4) and sends in the data point as an HTTP GET parameter.

v =
pm = ((v/1024)*3.6 - 0.5) * 170
geturl = "" .. pm
http.get(geturl, nil, function(code, data)
-- Turn off the red LED on NodeMCU board
gpio.write(lpin, gpio.HIGH)

The code for Air Monitoring Station init.lua is listed below.

-- setup Wifi


-- PIN assignment
-- D5 is the driver PIN for the dust detector
dpin = 5
-- D0 is the LED on the NodeMCU board
lpin = 0

-- initialize the PIN state
gpio.mode(dpin, gpio.OUTPUT)

-- timer to send data
tmr.alarm(1, 10000, tmr.ALARM_AUTO, function()
if wifi.sta.getip() == nil then
print("Connecting to AP...\n")
-- turn on the red LED on the NodeMCU board
gpio.write(lpin, gpio.LOW)
-- Per the spec: Warm up for 10ms,
-- generate a pulse of 0.32ms wide,
-- take reading at 0.28ms into the pulse.
v =
pm = (((v/1024) * 3.6) - 0.5) * 170
geturl = "" .. pm
http.get(geturl, nil, function(code, data)
-- Turn off the red LED on NodeMCU board
gpio.write(lpin, gpio.HIGH)

The NodeMCU application sends measured data to a ambee Web Server. In the next part, I will discuss how to create a Web Server.

Part 4: Set up web server to receive data and store in Database

In this article, I will discuss how to build a web server in node.js to receive, store, and display data from our connected air quality sensor and we will deploy it on Heroku.

This article is the last of a 4-part series of tutorials on IoT Enabled AMS(Air Monitoring Station) development.

Let’s get started by creating a simple web server.

Firstly i created a directory and named it ambeeams. You could name it whatever you like

$ mkdir ambeeams
$ cd ambeeams/

I am using VS code as my code editor.

Now that i have created the directory i’ll initialize the node app by typing

$ npm init

Now that my node is setup i’ll install few packages that i would be requiring for setting up our server.

$ npm install express body-parser morgan cors moment mongoose request path ejs nodemon -- save

Once the installation is complete i added the following line to my package.json file “start”: “nodemon server.js”. It basically restarts the application whenever there is any change in the code, saves me from the pain of manually restarting my server.

Now i am going to create a file named server.js and now its time to finally write the server part to handle the data coming from the air quality sensor and store it in the database (will be using mongoDB).

'use strict'

const express = require('express');
const bodyParser = require('body-parser');
const morgan = require('morgan');
const cors = require('cors');
const path = require('path');
const mongoose = require('mongoose');
const moment = require('moment');
const PollutionData = require('./model/pm');

const app = express(); //initilizing the express app
const dbConfig = require('./configuration/dbConfig');
mongoose.connect(dbConfig.connectionString, { useNewUrlParser: true });
app.set('port', (process.env.PORT || 3000)); // set's the server to listen on port 3000 or the port that is configured in the environment variable.

app.use(morgan('dev')); //logs every request to the console,
app.use(bodyParser.urlencoded({ extended: false })) // it parses application/x-www-form-urlencoded.
app.use(bodyParser.json()); // parse application/json
app.set('trust proxy 1');
app.use(express.static(path.join(__dirname, '/public/')));
app.use('/css', express.static(path.join(__dirname, 'node_modules/bootstrap/dist/css')));
app.use('/js', express.static(path.join(__dirname, 'node_modules/bootstrap/dist/js')));
app.use('/js', express.static(path.join(__dirname, 'node_modules/jquery/dist')));
app.set('views', './views');
app.set('view engine', 'ejs');

mongoose.connection.on('connected', function() {
app.listen(app.get('port'), function() {
const url = 'http://localhost:' + app.set('port'); // only for quick access on test env.
console.log('Application startion on ' + app.set('port'));
console.log('click to launch in browser ' + url);


app.get('/ambee', function(req, res) {
let queryData = req.query.d;
let currentTime = moment().utcOffset(330).format();
const data = new PollutionData();
let dataView = [];
data.pm_level = queryData;
data.added_at = currentTime;
console.log(`DATA ${queryData} saved at ${currentTime}`); {
if (err) throw err;
console.log(`DATA ${queryData} saved at ${currentTime}`);
PollutionData.find({}, function(err, data){
if (err) throw err;
const size = data.length;
const i = size - 2;
res.render('index', {data : data[i]});

Now, i need to create a Database and i’ll be using mlab free version Sandbox tier.

Kindly follow the steps:
1. login to mlab
2. create a user with root priviliges

Now I need to go back to VS code and create a folder named configuration. Inside configuration folder i created a file named dbConfig.js. Once it was done i copied the url from mlab

const mongoose = require('mongoose');

const uriTemp = 'mongodb://<dbuser>:<dbpassword>'
const uri = 'mongodb://'
module.exports = {
    connectionString: uri
mongoose.connection.on('connected', function() {
    console.log('Mongo connected @ ', uriTemp)

mongoose.connection.on('error', function(err) {
    console.log('MONGODB error: ', err)

I replaced the username and password section according to my priviliges.

Now i will create a folder named models where i will be creating a schema. Inside models i created a file named pm.js

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const PollutionSchema = new Schema({
pm_level: {type: String},
added_at: {type: String}

const PollutionData = mongoose.model('pollution_data', PollutionSchema);
module.exports = PollutionData;

I am storing the pm level data with respect to timestamp i.e for every 10seconds.

Once i was done with the models folder, i had created a folder named views and inside views folder i had created index.ejs file.

In index.ejs file add the following code

<!DOCTYPE html>
    <script src=""></script>
    <div id="container" style="min-width: 910px; height: 400px; margin: 0 auto">
    <h1>Ambee Air monitoring station</h1>
    <p>Do not refesh the page will load data every 10-15 seconds.</p>
    <p>PM 2.5 READING: <%=data.pm_level %></p>
    <p>RECORDED AT: <%= data.added_at %></p>
      }, 20000);

Deploying on heroku

Now that i have created my server i need to deploy my code to heroku because our local server is not accessible to everyone on the internet and also it does not support HTTPS protocol.
Note: localhost or any kind of self signed certificate won’t work.

Visit Heroku → create account → login → create app.

The next step is to choose my deployment method which basically means that i can either use Heroku Git or Github. I will be using my GitHub repository instead of the Heroku Git. But before I connect my GitHub repository I have to create an empty repository on GitHub where I will be pushing all the code i have written so far.

$ git init
$ git add .
$ git commit -m "code push"
$ git remote add origin
$ git push -u origin master

Now that i have pushed my code to my respective repository i can go ahead and connect my GitHub.

Once i am done connecting my GitHub repository, i’ll enable automatic deploys so that the code is automatically deployed every time i push a new piece of code to my repository.

Now i will go to my heroku dashboard and click on open app, it will open a webpage, copy that url and add

/ambee?d='yoursensordata' and replace it in the lua code


Through this series of articles, I have build a simple IoT enabled air monitoring station and hosted a web server where the air quality sensor sends the data every 10seconds and using mlab the data is been stored to our database. On the device side, we focused on NodeMCU as an IoT development platform. On the server side, we developed a very simple RESTful web server on Node.js.

It is fairly Moderate to build an Internet-connected hardware device at very low cost using NodeMCU. Also while developing this station i realized the real power of NodeMCU, which can serve as an integration platform to connect almost any existing devices (sensors, lights, TVs, appliance controllers etc) to the Internet via Wifi.