XYZ

How clean is the air you breathe? Custom Air Quality Maps with Arduino and HERE XYZ

By Harishjitu Saseendran, Mayuresh Sarang and Aamer Khan | 24 April 2019

Many air quality devices are statically mounted. We wondered, what if the tools and information needed to understand the severity of air pollution was available on the go? What if we have a system which gives live updates or live readings of the pollution level in a given area? Thus, came the idea to provide a hardware-based solution using HERE XYZ to provide a visual demographic interpretation of the pollution level.

Final Map

Overview

In this tutorial we will show you how to setup an Arduino Uno to become a GPS-enabled, Air Pollution Monitor. Then we will walk you through augmenting the generated data to include the street address nearest to the GPS positions using the HERE Reverse Geocoding API, then we will show you how to visualize the results in HERE XYZ Studio to publish an interactive map like this.

  View this map with HERE XYZ Studio.

Our Hackathon Problem

Many air quality devices are statically mounted. These devices typically cannot travel. We wondered, what if we made the tools and information needed to understand the severity of air pollution available on the go? How could we create a system that could give live updates or live readings of the pollution level in a given area? Thus, came the idea to prototype a hardware-based solution to collect the data and then use the HERE XYZ platform to provide a visual representation and interpretation of the pollution levels.

In this tutorial we will show you how to:

  1. Setup and configure an Arduino Uno with an Air Quality Sensor and GPS
  2. Calibrate the Air Quality Sensor
  3. Collect the data
  4. Augment the data with HERE's Reverse Geocoding API
  5. Visualize the results using HERE XYZ Studio

Hardware

 Many of these components can be found readily available through many outlets. We are not affiliated with any of the links below.

Arduino Schematic

Software

Calibrating the gas sensor

By default, the gas sensor gives out raw analog / digital values, however, to get PPM values we need to calibrate the gas sensor. To calibrate the sensor, we need to use a library (MQ135.h) which can be downloaded from the following link.

Next, we connected the gas sensor to the Arduino board as shown below and then ran the following code using Arduino Studio.  We monitored the results and let it run for a minute using the Serial Console.  We copied the last result (in our case 494.63 into the MQ135.h library file.  This value is then used to help the library produce PPM values that we will use for the visualization part of this tutorial.

Arduino and Air Qaulity Sensor Setup
#include "MQ135.h"

void setup (){
	//Baud rate might differ for your GPS module 
	Serial.begin (9600);
}
void loop() {
	MQ135 gasSensor = MQ135(A0);   // Attach sensor to pin A0
	float rzero = gasSensor.getRZero();
	Serial.println (rzero);
	//Setting 1 sec delay
	delay(1000);
}

 

Once you get a value, insert it into the MQ135 Library header file (MQ135.h). This will a provide a calibrated reading for Parts Per Million (PPM).

#define RZERO 494.63

Once we have the gas sensor calibrated, we connected the hardware and we used the following code and deploy it on the Arduino board using Arduino IDE (please see the hardware wiring diagram above).

Complete Setup

 

Arduino Code:

// This code is based on the TinyGPS++ Library example named "DeviceExample"

#include <tinygps++.h>
#include <SoftwareSerial.h>
#include "MQ135.h"

TinyGPSPlus gps;
SoftwareSerial ss(4,3); // Assigning pins 3 & 4 of arduino as TX & RX
void setup() {
  Serial.begin(9600); //setting baud rates
  ss.begin(9600);
  Serial.println("Latitude, Longitude, Date, PPM");
}

void loop() {

  while(ss.available() > 0) {
    
    // This section will print out the GPS Coordinates
    if(gps.encode(ss.read())) {
      Serial.print(gps.location.lat(), 6);
      Serial.print(F(","));
      Serial.print(gps.location.lng(), 6);
      Serial.print(F(","));
      
      if (gps.date.isValid()) {
        Serial.print(gps.date.year());
        Serial.print(F("/"));
        Serial.print(gps.date.month());
        Serial.print(F("/"));
        Serial.print(gps.date.day());
        Serial.print(" ");
        Serial.print(gps.time.hour());
        Serial.print(":");
        Serial.print(gps.time.minute());
        Serial.print(":");
        Serial.print(gps.time.second());
        Serial.print(",");
      }
      else {
        Serial.print(F("INVALID"));
      }

      //This section will print out the PPM Values from the MQ135
      MQ135 gasSensor = MQ135(A0); // Read values from A0 pin of arduino
      float air_quality = gasSensor.getPPM(); // Using MQ135.h library to get PPM values
      Serial.print(air_quality);
      Serial.println();
    }
  }
}

Working with captured readings and Preparing them for HERE XYZ Studio

This completes the hardware setup then began a little leg work. To begin collecting data, we use CoolTerm to output the values from the Arduino Uno to a CSV file.  You can configure CoolTerm to do this by

CoolTerm Save to File

Next, we connected to the Arduino and started recording our journey. We travelled around for about 30 mins to get the live readings of the pollution level along with latitude & longitude. Once complete we disconnected from the Arduino and the file was saved to our Desktop.

GPS Locations are useful, but we wanted to see the relative address of each data point. To get the addresses we need to do a "Reverse Geocode Lookup" so we created a REST client (Java code below). This program calls a HERE's Reverse GeoCoding API to conduct a reverse lookup and give us address information.

To use these API's, you need to obtain a HERE AppID & App_Code. You will need to create a HERE Developer Account. Sign up here.

 

Reverse Geocoding JAVA Program

Using your HERE Developer Account, insert your App ID & App Code below and then run this file from Eclipse. For a HERE Developer Account go to https://developer.here.com. You will also need to hardcode the location of the file you outputted while traveling around collecting data.

package geocoder;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;

import org.json.JSONArray;
import org.json.JSONObject;

/*
 * About Geocoder
 * 
 * This is an application that reads in a CSV file where the column format is as follows
 * [Latitude],[Longitude],[Date],[PPM]
 * 
 * This application reads the file in and then for each line creates a request to HERE's 
 * reverse Geocoder API and then waits for a request.
 * 
 * REQUIREMENTS to Run this code:
 * - You will need to hard code the location of the input file "csvFile" variable below.
 * - You will also need to hard code the location of the output file "outfile" variable below.
 * - Will also need to insert your HERE Developer AppID and AppCode in the "url" variable below.
 * 
 * Potential Extensions:
 * - Turn the REST calls into an Asynchronous process
 * - Try using HERE's Batch Reverse Geocoder API - https://developer.here.com/documentation/batch-geocoder/topics/quick-start-batch-geocode.html
 * - Open up a serial connection to the Arduino board and process the incoming data in real time while asynchronously emitting the data to XYZ.
 */

public class Geocoder {
	private static final  String USER_AGENT = "Mozilla/5.0";
	
	//Your csv file with captured data
    static String csvFile = "[INSERT PATH TO INPUT CSV]";

    public static void main(String[] args) {
    	
    	
        String line = "";
        String cvsSplitBy = ",";
        String[] capturedData = null ;
        
		writeToCsv("Latitude","Longitude","Date & Time","PPM Values","Address");

        try (BufferedReader br = new BufferedReader(new FileReader(csvFile))) {
        	
        	// will skip first line
        	String hearderLine = br.readLine();

           	while ((line=br.readLine()) != null) {
                // use comma as separator
           		capturedData = line.split(cvsSplitBy);

           		//URL that we are calling
        		String url = "https://reverse.geocoder.api.here.com/6.2/reversegeocode.json?prox="+capturedData[0]+","+capturedData[1]+"&mode=retrieveAddresses"+"&app_id=[APP ID]"+"&app_code=[APP CODE]";
        		
        		String latitude = capturedData[0];
        		String longitude = capturedData[1];
        		String datetime  = capturedData[2];
        		String ppmValues = capturedData[3];
        		
        		//Creating an object of URL Class
        		URL obj = new URL(url);
        		HttpURLConnection con = (HttpURLConnection) obj.openConnection();

        		// optional default is GET
        		con.setRequestMethod("GET");

        		//add request header
        		con.setRequestProperty("User-Agent", USER_AGENT);

        		//Print this response code if you are facing any issues
        		int responseCode = con.getResponseCode();
        		BufferedReader in = new BufferedReader(
        		        new InputStreamReader(con.getInputStream()));
        		String inputLine;
        		StringBuffer response = new StringBuffer();
        		
        		while ((inputLine = in.readLine()) != null) {
        			response.append(inputLine);
        		}
        		
        		//Getting the response in String
        		String jsonobj = response.toString();

        		//Reading JSON output using json.org library
        		//Its recommended to study the JSON response from server prior to extracting the required output
        		//Refer link https://developer.here.com/api-explorer/rest/geocoder/reverse-geocode
        		JSONObject json = new JSONObject(jsonobj);
        		JSONObject response1 = json.getJSONObject("Response");
        		JSONArray View = response1.getJSONArray("View");
        		JSONObject obj1 = View.getJSONObject(0);
        		JSONArray result = obj1.getJSONArray("Result");
        		JSONObject obj2 = result.getJSONObject(0);
        		JSONObject location = obj2.getJSONObject("Location");
        		JSONObject address = location.getJSONObject("Address");
        		
        		String label = address.getString("Label");
        		
        			in.close();
        		
        		//print result
        		System.out.println(label);
        		
        		//write to output file
        		writeToCsv(latitude,longitude,datetime,ppmValues,label);
                
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

	private static void writeToCsv(String latitude, String longitude, String datetime, String ppmValues, String label) {
		try {
			
			// Your output file path
    	    File outfile =new File("[INSERT PATH TO OUTPUT CSV]");

			FileWriter fw = new FileWriter(outfile,true);
			BufferedWriter bw = new BufferedWriter(fw);
			PrintWriter pw = new PrintWriter(bw);
			
			pw.println(latitude+","+longitude+","+datetime+","+ppmValues+","+"\""+label+"\"");
			pw.flush();
			pw.close();
			fw.close();
			bw.close();
		}catch(Exception E) {
			System.out.println(E);
		}
	}
}

Once the program completes, your data file will have been updated to include the Addresses nearest to the GPS coordinates captured while traveling.  Next, we will show you how to upload the data into HERE XYZ Studio and configure the visualization so that it is more easily interpreted.

About the Data Recorded

The MQ135 sensor is recording Benzene, Alcohol & NH3 to produce a single reading.  We don't know how much of each gas was detected, but we can still visualize the results with a few calculations. The sensor needs to be calibrated so that a PPM value can be used (this was in the Calibration step above).  For our visualization we determined the minimum, maximum and average PPM values from the recorded data to produce a color scheme that you see in the map below.  We used Excel to find these values. In our case the low was 250, the high was 2270 and the average was 500.  With this information we can complete our map.

Visualizing with XYZ Studio

After we obtained, assembled and augmented the data, the next step was to feed it into the HERE XYZ Studio for visualization. HERE XYZ supports CSV and other formats, so all we had to do was create a new map and upload our dataset.

Go ahead and log into HERE XYZ Studio (if you don't have an account signup, it is free) and click "Create new project" and give personalize the project's Name and Description. Next click "Add"

create new project

personalize the project

Next, we want to upload our data set.  Click "Upload Files" navigate to your dataset and once complete click "Done" to return to your map.

upload files

Your map will look something like ours does in the image below.  What you are seeing is a bunch of dots all bunched together without any color coding.  

rawdatamap

Our next step is to add color to represent different colors for the Low and High readings.  Then we will add some rules to represent the range of data between our High, Average and Low readings.

startcoloring

On the next screen tap "Points" to reveal the point configuration menu. Next click "Add new point style". This will reveal the style configuration menu where we will define our "Maximum" value like this.

MaximumStyle

Once you click "Confirm" your map will look like this.  What you are seeing here is a raw data representation where none of the data point have been styled.  

MaximumStyleResult

Next, we will change the colors on the Maximum value to purple so that we can easily see the data.  (If you don't see a color choose like below, click on "Data1" to reveal the Point Configuration menu. Next click Points and then "Maximum" to reveal the color chooser below.). Click on the colors and choose purple.

ColorConfig

You will want to do this for the minimum value of your dataset as well.  Next, we created more complex rules for color coding to represent ranges of data (below average, average and above average).  The average configuration look like this.  For Below and above average you would repeat this step with different values.

AverageStyleGroup

Soon your map will start coming together and look like this. (Depending on the order of the Color Configuration rules you may need to resort your rules to look like this so that the Minimum and Maximum rules are executed first.)

Screen Shot 2019-04-18 at 1.51.30 PM

You can also inspect each point by selecting it to see the Address, GPS position and PPM values, but first you'll need to select the "Cards" pulldown and then drag the "Address" field into the area with "PPM and Date".

Screen Shot 2019-04-18 at 1.57.43 PM Final Map

Now your map is complete! Let's show you how to publish and share it with your friends. Click on the "Publish" button in the bottom right of the main map screen, tap the button to the right of Publish.

publishconfig

Next make any configurations you like; we tend to turn on Description and Legend. Once complete scroll to the bottom and tap the "Copy" link next to the URL.  This will copy to your clipboard and you are now complete.

Conclusion

The goal of our project was to showcase the possibilities we can achieve by combining a simple mobile hardware setup with HERE APIs and HERE XYZ studio to develop a solution that could be helpful to a lot of sector and individuals. 

About the Authors

Harishjitu Saseendran, based out of HERE’s Mumbai office, holds engineering degrees in Electronics and Telecommunication. He also has experience in Java, php, MySQL, Python, Android and Arduino Programming. Most of his spare time is spent creating Android-based applications and IOT projects. He likes to get his hands on different tech, leading him to create and evolve innovative solutions like this Air Pollution Monitoring System.

Mayuresh Sarang, is an engineer in Computer Science. He spends most of his time developing Web Applications for HERE Technologies based out of Mumbai. His experience excels in Java EE, JSP Servlets, Spring Boot and Hibernate. He has sound knowledge of building Restful Web Services and Rest Clients and he likes creating smart IOT based solutions which gave him an idea of monitoring air pollution using Arduino.

Aamer Khan, based out of HERE’s Mumbai office has a bachelor's in computer science and is a strong believer in how modern technologies can better the world. He has experience in Visual Basic and excels in creating advanced Excel macros for automation. He enjoys writing and sharing experiences with his readers. He assisted in documenting this project and providing input to the team.

Editor's Note: This is a guest post from a HERE team based out of Mumbai, India. This project came from an internal Hackathon presentation and we would like to share it with you.

If you would like to contribute to the HERE Developer Blog, please reach out to us via DM @HEREDev on Twitter.