Projects Research & Design Hub

Build an Urban Air Quality Monitoring System

Written by Dhairya A. Parikh

Using IoT Technologies

Metropolitan cities face their own unique challenges when it comes to air pollution. In this project article, Dhairya details his design of a fully equipped, low-cost urban air quality monitoring system for cities in developing countries, powered by Soracom and AWS IoT Service. It also supports email alerts.

  • How to build a low-cost urban air quality monitoring system

  • What hardware and software components to us

  • How to set up the IoT monitoring node

  • How to set up Soracom Console

  • How to implement the hardware code and Android app

  • Soracom IoT Starter Kit with Raspberry Pi

  • Soracom Global SIM Card:

  • Raspberry Pi Zero W and Official Case

  • Huawei 3G USB Modem (MS2131i) and USB OTG Adapter Cable

  • 8GB Micro-SD card and Micro-SD card reader

  • Perma-Proto breadboard and color-coded wires

  • USB-A to Micro-USB cable

  • NXP Semiconductor’s Rapid IoT prototyping kit [

Metropolitan cities all around the world are facing a lot of problems due to increasing population size. Population increases are causing an exponential rise in pollution—a very serious problem that needs immediate attention. Unfortunately, there’s a lack of proper monitoring devices for these urban environments, which makes it hard to gather any reliable data. Although some such devices are available, they are in limited number due to their costs and they provide poor reliability and low accuracy.

With that in mind, I have developed my Soracom-powered Urban Quality Monitor (SUQM). It is designed specifically for urban environments. Please note that I’ve designed this device with cities in India in mind. Metropolitan areas like Delhi (the urban territory in India containing New Delhi) is one of the most polluted areas in the world when it comes to air pollution. The final system without any enclosure is shown in Figure 1.

FIGURE 1 – This image shows my final node. It runs on a Raspberry Pi 3 SBC because I didn’t have a mini-HDMI cable for my Raspberry Pi Zero. I taped the components on a wooden base so that I could set it up anywhere I want.

So, let’s get started with the project. First, let’s have a look at the project diagram, which is shown in Figure 2. The diagram shows how the flow of data takes place in the whole system. We use various Soracom services—services that come with the SIM card access. The “brain” of our monitoring node will be a Raspberry Pi SBC. You can use any Raspberry Pi, including the Pi Zero W. But I used the Raspberry Pi 3 board for this demo because I didn’t have a mini-HDMI cable with me. But the source code for this project will work identically on any other Pi device that has on-board Bluetooth.

FIGURE 2 – The project diagram. It shows the basic workflow of our project. It depicts how the sensor data reaches our application and dashboard from our sensor node using the Soracom network.

Let’s have a look at the required components for the project. The hardware components required for this project are pretty straight forward because they are available as a kit. We will be using the Soracom IoT Starter Kit with Raspberry Pi for this project. It is shown in Figure 3 and it contains the following components:

FIGURE 3 – Shown here are the required hardware components to build this project. Please note that the image does not contain the Rapid IoT prototyping kit.

Soracom Global SIM Card: The Soracom Global SIM provides cellular data connectivity and SMS functionality on 2G, 3G, 4G LTE and Cat M1 networks in over 120 countries around the world. It’s fully managed right from your browser on the Soracom User Console (also included as part of the starter kit)

Raspberry Pi Zero W and Official Case: Small, easy-to-use Linux computer that supports a wide variety of programming environments for IoT development.

Huawei 3G USB Modem (MS2131i) and USB OTG Adapter Cable: A great modem that provides 2G and 3G cellular connectivity, designed especially for IoT projects, plus a USB OTG cable to hook it up to the Raspberry Pi

Ultrasonic Range Finder (HC-SR04): This simple yet reliable sensor measures distance using ultrasonic vibrations, and is accurate up to 10′. I love this sensor because I can quickly capture data for easy visualization, just by waving a hand in front of it!

8GB Micro-SD card and Micro-SD card reader: Provides storage for your Raspberry Pi, plus a card reader so you can edit its contents on your computer.

Perma-Proto breadboard and color-coded wires: This is for connecting the Ultrasonic Range Finder to your Raspberry Pi, without soldering anything.

USB-A to Micro-USB cable: To power your Raspberry Pi from your computer or other USB power source

Beyond those kit components, Soracom also provided me with $30 worth of Soracom SIM credits along with $25 worth AWS of credits to use the required AWS services. Apart from the Soracom kits, we will also be using NXP Semiconductor’s Rapid IoT prototyping kit [1]. It is the perfect all in one sensor for our project. It measures all the required values for urban quality monitoring, which includes temperature monitoring (in degrees Celsius), humidity monitoring (in percentage), ambient light monitoring (in lux), pressure monitoring (in kPa) and air quality monitoring (in parts per million or ppm).

On top of this, it has on-chip Bluetooth connectivity, which makes it very easy for our Raspberry Pi SBC to receive the sensor data wirelessly. In addition to the two kits, we will be using a couple additional components including an Android device, for app development and testing and a battery bank to power our device and make in portable.

There several of software components used in this project. The software components used are:

Raspberry Pi OS—Operating system for Raspberry Pi (previously called Raspbian)
Soracom Harvest—Data collection and visualization services for the IoT device
Soracom Funnel—An adapter that transfers data from the device directly to a specific cloud service
Soracom Lagoon—An easy-to-use, personalized dashboarding tool that uses data collected with Soracom Harvest services
AWS IoT Core—A managed cloud service that lets connected devices easily and securely interact with cloud applications and other devices
Android Studio—This will be used for application development and testing.


Now that we’re aware of the components to be used in the project, let’s get started with building our monitoring system. First, we’ll start by setting up our hardware—or monitoring node. For this, we will have to first set up our Soracom IoT Starter Kit, which includes installation of Raspberry Pi OS, setting up the Pi to support the Huawei 3G Modem (SIM support) and, finally, connecting to the required Soracom services.

The process is quite long and tedious but a very good step-by-step documentation for this is provided by Soracom itself. The link [2] to that document can be found in RESOURCES. Follow it step by step and, if everything goes as planned, by the end of the setup process you will have successfully connected your Raspberry Pi to Soracom Harvest and you will be publishing the ultrasonic sensor data in real time.

We will be using the ultrasonic sensor Python code as the basis for developing our own project code in which we will connect to our Rapid IoT prototyping kit via Bluetooth with Python and sending different sensor data values every 30 seconds.


The next step is to set up Soracom Console for our project. We will also set up AWS IoT core because we want to show this data in a mobile app too. Please note that this article only deals with Android app development. First, let’s look at the Soracom Console, which includes the setup of software components Soracom Harvest, Soracom Funnel and Soracom Lagoon.

Soracom Console setup: After you login to your Soracom Console, you will see the homepage where your SIM card is listed. If your device is on and running the demo ultrasonic code, you will see that it shows that the device session is online. The next step is to set up “actions.” You will find an Actions tab beside the Register SIM and Details button. Just press that and select the Change Group option. After that, you will be redirected to a new page. There, create a new group. Choose a name of your liking. Then you will be prompted to select which services to use for this project. Select Harvest and Funnel.

The basic flow goes like this: Our node sends the sensor data to the Soracom Harvest platform and this data is sent to Soracom Lagoon (a dashboard service) and the Soracom Funnel IoT Adapter is used to send this data to AWS IoT. Now, just create the group. The Harvest setup has already been done in the tutorial. So, now we just have to setup the Funnel IoT service to send our data to AWS IoT. The whole step-by-step tutorial is provided in the Soracom tutorial at [2].

Soracom Lagoon setup: Now we will set up the Soracom Lagoon for data visualization. From the homepage of the Console, tap on the menu tab situated on the top left corner, search for Soracom Lagoon and click on it. Then create a username and select the “Free or Maker” account so that you can subscribe successfully to this service. Next, just open the Soracom Lagoon dashboard and create a new dashboard. You will find all the same sensor names that you found in your Harvest data. That’s because it’s a tool that uses Soracom Harvest data as its source.

Now, create a dashboard and insert the graphs and all the visual elements you want on your dashboard. Using the dashboard is very easy, and adding widgets and graphs is even easier. You just have to use the buttons provided on the top right corner. Just hover on them and you will know the functionality of each button. This will finally complete our dashboard. When we start our monitoring system, it will provide almost real time data, with a deficit of 30 seconds—a limitation of the Free or Maker plan. The dashboard I created is shown in Figure 4.

FIGURE 4 – My final Soracom Lagoon dashboard. It contains graphs for different sensors and also the current (and some past) sensor data values in a tabular format.

Next, we set up an alert system using Lagoon, which sends an email alert when a particular sensor value goes beyond the programmed threshold. A typical email notification is shown in Figure 5. To set this up, we have to do the following: First, go to the Lagoon console and select any of the graphs. Now from the drop-down menu, press the Edit button. After this, as the menu opens, go to the Alerts tab and create a new alert by setting the threshold value and other parameters. Next, we have to attach a notification to this alert. But, to do so we first need to create a new notification channel. So, from the leftmost menu, go to the Alerting tab and select the Notification Channels tab. Now, create a new channel by pressing on the green New Channel button. Fill in all the required details and select your desired notification source. I’ve used email for this purpose. Finally, just go back to the dashboard and the Alert section and attach the notification channel to the datapoint.

FIGURE 5 – This is a sample email notification that the Soracom Lagoon system sends when the value of any sensor goes above the specified threshold value.

This completes the Soracom console setup for our project. Note that you may also export this data from the Harvest data screen, which stores up to 40 days of data. That could be useful if you would like to store this data locally for further analysis or for training a machine learning model. A machine learning model can easily run on a Raspberry Pi SBC thanks to the latest tflite (TensorFlow Lite) models and TinyML.

AWS IoT Core setup: The next step is to set up the AWS IoT Core for our project. First, create an AWS account and verify your payment method to activate your account. Don’t worry about automatic transactions. You will get to use a majority of the AWS services, with some limits, for free for the first 12 months. Next, sign in as the root user of the account and you will be taken to the AWS management console. After that, go to the IoT Core from the Services tab on top of the screen and you will come to the AWS IoT Core home screen. The home screen of AWS IoT Core is shown in Figure 6. Now, to set up this for our project, just follow the tutorial documentation at [3]—the same document you used while setting up Soracom Funnel earlier.

FIGURE 6 – This is the home screen of your AWS IoT core. It’s one of the various services provided by AWS, and it is used to connect the AWS platform to different IoT devices.

Now let’s discuss hardware code. To make it easier to follow, I’ve broken the Python code into small snippets. These snippets, along with descriptions of the purpose of each, are detailed in Listing 1Listing 2 and Listing 3. Note that this code is written in Python 2, but writing it in Python 3 is pretty straightforward. You just have to change the print statements for the most code.

LISTING 1 – Code snippets for importing and downloading the required libraries

These are the required libraries that need to be imported:

# Import required libraries, including python-requests
import sys, time, requests, json
import pygatt
import geocoder
import struct

These pip commands are needed to download the libraries:

#sys, time and json are inbuilt python libraries
#use sudo if you face problems
pip install python-requests #The requests library
pip install pygatt #Python Module for Bluetooth LE Generic Attribute Profile (GATT).
pip install struct #available in python 3.x,used for packing and unpacking byte array
pip install geocoder #Library to obtain Location data from internet

The pygatt library above is used to fetch the Rapid IoT kit data via Bluetooth, which is obtained in byte array format. We require the struct library to unpack and convert this data to normal ASCII data.

This next snippet is easy to follow via its comments:

BLE_ADDRESS = '00:60:37:0A:06:2F' #NXP device mac address
adapter = pygatt.GATTToolBackend() #bluetooth adapter creation
locate = geocoder.ip('me') #obtain location data from internet
locate1 = locate.latlng #latitude and longitude data
print "Starting sensor value measurement! Press Ctrl+C to stop this script."
adapter.start() #Starting the BLE Adapter
device = adapter.connect("00:60:37:0A:06:2F") #connect to the Rapid IoT prototying kit

LISTING 2 – Shown here is the code for fetching the sensor data from the Rapid IoT kit and printing the obtained values in the Python shell.

This next code snippet fetches the sensor data from the Rapid IoT kit in byte array format, and after that it converts it to normal readable string format by unpacking this data using the struct.unpack command.

while True: #Loop to continuously fetch the values
# Value fetching, can be done with a loop too but I did this for easy interpretation
light_initial = device.char_read("1493dd8e-8c3e-4e79-a4ff-6f0cd50005f9")
#Ambient Light Value in bytearray
temp_initial = device.char_read("1493dd8e-8c3e-4e76-a4ff-6f0cd50005f9")
#Temperature Value in bytearray
humidity_initial = device.char_read("1493dd8e-8c3e-4e77-a4ff-6f0cd50005f9")
#Humidity Value in bytearray
air_initial = device.char_read("1493dd8e-8c3e-4e75-a4ff-6f0cd50005f9")
#Air Quality Value in bytearray
pressure_initial = device.char_read("1493dd8e-8c3e-4e78-a4ff-6f0cd50005f9")
#Pressure Value in bytearray
battery_initial = device.char_read("964bf77c-9f4d-4b27-9340-7eb81c1dfbd5")
#Battery Level value in bytearray
state_initial = device.char_read("964bf77c-9f4d-4b27-9340-7eb81c1dfbd6")
#Charging state value in bytearray
# Location information
lat = locate1[0] lon = locate1[1] #converting bytearray to normal value and accessing the actual value(in Tuple)
light_value = struct.unpack('i',light_initial)
light_value1 = light_value[0] temp_value = struct.unpack('f',temp_initial)
temp_value1 = temp_value[0] temp_value1 = round(temp_value1, 2)
humidity_value = struct.unpack('f',humidity_initial)
humidity_value1 = humidity_value[0] humidity_value1 = round(humidity_value1, 2)
air_value = struct.unpack('i',air_initial)
air_value1 = air_value[0] pressure_value = struct.unpack('i',pressure_initial)
pressure_value1 = pressure_value[0]

This code snippet simply prints the obtained values in the Python shell:

if True:
print "--------UQM - The Urban Quality Monitoring Device---------------"
print "--------------- Rapid IoT Sensor Values-------------------------"
print "Ambient Light Value:"+str(light_value1)
print "Temperature Light Value:"+str(temp_value1)
print "Humidity Value:"+str(humidity_value1)
print "Air Quality Value (TVOC):"+str(air_value1)
print "Pressure Value:"+str(pressure_value1)
print "----------------------------------------------------------------"

LISTING 3 – Code for converting the sensor data into json format and sending the data

This code converts the sensor data into json format. That format is required when sending the data. Raw format is not acceptable.

# Set the HTTP request header and payload content
headers = {"Content-Type": "application/json"}
payload = {
"Ambient Light Value": light_value1,
"Temperature" : temp_value1,
"Humidity" : humidity_value1,
"Air Quallity(ppm)" : air_value1,
"Pressure" : pressure_value1,
"coordinates" :
"Latitude" : lat,
"Longitude" : lon
# Send the HTTP request to Harvest
print "Sending data %s to Funnel..." % (json.dumps(payload))

The next step will be to send this data, which is done by the following code snippet:


Advertise Here

response ="", data=json.dumps(payload), headers=headers, timeout=5)
except requests.exceptions.ConnectTimeout:
print "Error: Connection timeout. Is the modem connected?"
# Display HTTP request response
if response.status_code == 201:
print "Response 201: Success!"
elif response.status_code == 400:
print "Error 400: Harvest did not accept the data. Is Harvest enabled?"

Note that we send it to the unified endpoint provided by Soracom, which sends the data to Harvest, Funnel and Beam all at once by default. We are only using the Funnel and Harvest services for this project.

Next, we move on to the Android app part, which is the final part of our project. We will create a very simple app that lets us view the raw data that we send to AWS IoT. Creating visualizations for this data is an option, and probably quite easy for someone with some experience in app development. You can find a link to the GitHub repository [4] for this project In RESOURCES at the end of the article. Our android app will act as a MQTT client and receive the data sent to AWS by Soracom Funnel. For now, we will just display the raw message in json format, but you can also use interactive Android tools to visualize this data.

Now let’s move on with the app development. I would like to thank Felipe Ramos Da Silva for assisting me with this development. We will start with an already-developed Android app in my GitHub repository [4]. We will delve into this project explaining each part, and how we can prepare the app for be used. Go to the AndroidPubSubWebSocket folder in
my GitHub repository. Download the necessary files. Be sure that you have an AWS account to settle up all the configurations on cloud side.

Let’s work first in the AWS side and set up our cloud environment. On the AWS side, there are two main steps to be followed. First, we will have to create some roles on IAM (Identity Access Management), that allow you to securely manage access to AWS services and resources. Because we aren’t using any kind of authentication with the app, we have to create a role to specify to AWS that some devices can use services without being authenticated. Even though this article is a theorical and educational project, it’s not recommended that you enable unauthenticated devices to use the services of your AWS account.

In your AWS console, go to Cognito > Manage Identity Pools > Federated Identities > Create New Identity Pools. Here you will be creating a federated identity pool to give access to your unauthenticated devices. Now you can give a name to the pool and select the option: Enable access to unauthenticated identities.

Once you’ve done that, while creating the pool the console will request that you to create two new roles on IAM. Create these roles and go to IAM > Roles and select the roles that you have created. In the role menu that you’ve created, you will have to attach a policy giving some permissions to this role. Furthermore, in the existing roles, search for AWSIoTFullAccess and attach it. The final platform after setup looks as shown in (Figure 7). Now we are good to go to AWS IoT Core. But don’t forget to note the Cognito Pool ID of the federated identity pool that you’ve created. We will use it in the android app to provide the authentication path to the application.

FIGURE 7 – This image shows how our IAM of AWS IoT looks like after we’ve set it up to connect our AWS IoT platform to our Android app via MQTT.

In order to make your first IoT device in AWS services, you will have to go to IoT Core from the AWS Management Console. We will create a Thing for our project. To do that, go to IoT Core console and on the sidebar go to Manage > Things, and on the right corner click Create. Next, select the option Create a Single Thing. Because we are doing this project only for learning purposes, just give a name to the device and don’t change any other option. And don’t create a certificate at this time. Once the device is created, you can go into the device’s options. Among a lot of options here, there are two that are important for us:

Shadow: A json document used to store and retrieve current state information for a device. Simply put, it’s MQTT topics that you can publish messages in JSON format.
Interact: In this option you will able to see the REST endpoint to communicate with your device, and some default MQTT topics for your shadow.

Take note of the device endpoint and the update shadow topic. Figure 8 shows both these values. Now, we will move onto the Android app development part. The android app will only have the AWS SDK package as a dependency. If you are using Android Studio, it will download automatically for you when you import the project in your IDE. But if you are using Eclipse, you will have to download it manually. You will need to download the AWS SDK for Android [5] and extract and copy these jars into the libs directory for the project.

FIGURE 8 – This image shows our AWS IoT Thing Shadow Rest API Endpoint, which we will use to connect our app to AWS IoT It also shows the MQTT topic we need to listen to in order to fetch the incoming sensor data from the node.

Because this is a simple project, it has only a layout xml file and an activity. In the layout file (activity_main.xml) you will not have to change anything—unless you would like to change the actual GUI of the main activity. Summarizing, this layout is only some buttons and edit texts that will handle the subscribed MQTT topic, or publish a message, where each of them has an ID to be bonded with some object in the main activity class. While this project is binding view elements in the common and tiring way, you should take a look at Butter Knife. Butter Knife is a view binding method that makes the binding easier and faster—making your code more readable and cleaner.

See the Layout file by downloading the necessary files from my GitHub repository [4] and opening the android application folder in Android Studio. Now, let’s move on to the Main Activity of the app.

In the main activity, there are some variables to be changed, they are:

CUSTOMER_SPECIFIC_ENDPOINT—This is the endpoint of your AWS IoT Core created Thing.

COGNITO_POOL_ID—This the ID of the Cognito Identity Pool that you have created.

MY_REGION—Your Thing was created in some of the AWS availability zones. Note where you’ve created it.

This Java code is very simple and can be extended for many IoT android projects. In the onCreate method, all the view elements will be bound with the attributes of the activity Java class, and two important classes for our project will be instantiated.

CognitoCachingCredentialsProvider—This is the class from AWS SDK that will grant access to all your unauthenticated devices. If the Cognito ID was passed correctly, a token will be given to the device.

AWSIotMqttManager—The MQTT manager that will handle all the connections. If the access to AWS services is granted by CognitoCachingCredentialsProvider, the connect method will be called, passing a callback that monitors the status change of the connection. Note that, in this part of the code, the process when the status changes will not block the UI, so it can be running on UI Thread.

In the onCreate method, the buttons for subscribing and publishing a topic will be assigned to a callback to trigger these events:

subscribeClick—When the subscribe button is pressed, a topic will be subscribed and a callback for new arriving messages will be created. Now you can receive messages from other devices!


Advertise Here

publishClick—When this button is pressed, the message will be published on the correspondent topic.

disconnectClick—Used to disconnect from your endpoint, and connect in another.

The activity code is part of the app source code that is included in my GibHib repository for this project [4]. Run the code, and if everything goes well, you will now have a simple way to establish communication between your hardware and the android app, which was the aim of this project. The final app screen is shown in Figure 9. This app is just a simple example that demonstrates how can we get our AWS data on Android in real-time. But, unlike the raw message we obtain in this app in json format, we can easily parse this data to get intense visualization tools in android like graphs, charts and so forth.

FIGURE 9 – The main screen of our Android app. It will show the status of our connection to AWS IoT and also lets us specify the topic we want to listen to, which we obtained from the previous image (Figure 8). It will show the received data to that topic in real time.

First, we have to parse the raw json data. That can be done easily by following the tutorial at [6]. Now, for visualization purposes, we can use the AnyChart-Android library or package. The whole package is open source and its GitHub link is at [7]. Using that, you can easily make you app more interactive and user friendly. For my part, I haven’t attempted it because of my lack of proficiency in Android and Java programming.


In conclusion, let me emphasize that urban areas are at a massive risk with their increasing population and pollution. And I think this system can be very useful in stopping the current scenario, or at the very least, slowing down its effects. This is just the beginning. With the collected data and the real-time data, we can design a number of applications to improve this situation, or at least figure out the worst affected areas and the reasons behind it.

As for future plans, I have some points in mind that I think will improve this project a lot. First, I’d like to develop a low-cost device that can act as a sensor node. To do this, I’d use the Raspberry Pi Zero and the Soracom connectivity discussed in this article. My second idea is to develop a network of such nodes, so that we can get plenty of data points. This will provide sufficient data to visualize on advanced tools like Heat Maps. Finally, one of the areas of improvement is security for this system. This can be easily achieved by a more in-depth study into the features of AWS Security protocols and credentials.

My thanks to Soracom and Hackster for their wonderful contest that was the basis of this project. My thanks to Soracom for providing me with such wonderful hardware and developer support too. Thank you to Hackster for providing a great platform for Soracom to host its contest, allowing us to interact with people from all around the world and share innovative projects. 

[1] Rapid IoT Prototyping Kit:
[2] Starter Kit setup:
[3] AWS IoT Core setup with Soracom Harvest:
[4] GitHub Repository link:
[5] AWS SDK for Android
[7] AnyChart-Android library or package. The whole package is opensource and its GitHub link is at

NXP Semiconductors | www.nxp.coM
Raspberry Pi Foundation |
Soracom |


Keep up-to-date with our FREE Weekly Newsletter!

Don't miss out on upcoming issues of Circuit Cellar.

Note: We’ve made the Dec 2022 issue of Circuit Cellar available as a free sample issue. In it, you’ll find a rich variety of the kinds of articles and information that exemplify a typical issue of the current magazine.

Would you like to write for Circuit Cellar? We are always accepting articles/posts from the technical community. Get in touch with us and let's discuss your ideas.

Sponsor this Article

Dhairya Parikh is an Electronics Engineer and an avid project developer. Dhairya makes projects that can bring a positive impact in a person’s life by making it easier. He is currently working as an IoT engineer. His projects are mainly related to IoT and machine learning. Dhairya can be contacted on

Supporting Companies

Upcoming Events

Copyright © KCK Media Corp.
All Rights Reserved

Copyright © 2024 KCK Media Corp.

Build an Urban Air Quality Monitoring System

by Dhairya A. Parikh time to read: 20 min