Introduction: Air Quality Measuring With REST Api

In this instructable we are building a simple Arduino set-up that will connect with our own RESTful API. The arduino will measure the Air Quality with a Sainsmart MQ135 Air Quality sensor and send them over the web to one of our own servers which will be hosting our web service.

Requirements/What I used:

  • Arduino Yun
  • Sainsmart MQ135 Air Quality Sensor
  • Webserver with PHP >= 5.5.9, OpenSSL PHP Extension, Mbstring PHP Extension, Tokenizer PHP Extension installed

Software

  • Arduino
  • IDE (e.g. Sublime Text )

Libraries

Once you've installed and downloaded all the requirements head on to step 1 where we will be building our own RESTful API.

Step 1: Step 1: Coding the Cloud

Great! You've made it to step 1. Here we will be building the web service to which our Arduino Yun will be sending the (POST) data. The idea is that it will read the data from our gas sensor and send that to the server so we can use that data as we see fit.

I will not be explaining the setup of the webserver. In my case I'm using a DigitalOcean VPS running a LEMP stack. In order to get this to work you should pay careful attention to the PHP modules your webserver has installed.

Installing Lumen

First up install a fresh install of the Lumen micro framework on your server. I've added the Lumen installer to the package installer "composer". So all I have to do is:

$ lumen new api

Alternatively you can copy Lumen through FTP by downloading it's files on the Lumen website.

Next up we have to edit the ".env" file. Edit the correct login credentials for your MYSQL database, username and password. This will make Lumen able to access the database on your server.

Important: Check if Lumen is correctly installed by going to the public folder on your server. It should display "Lumen".

Coding the API

I wrote this part as a copy paste tutorial. Why? Because if you can code, you'll get what it all means. If you can't code it's no use to explain this. Might there be a great demand I'll update it accordingly.

We chose Lumen because it's a really small framework (hence the "micro" part) and it's really easy to make a simple API.

First up. Create a new migration file so create the tables in the database. You can call it anything you like. I'll call it "data", because of a lack of inspiration.

$ php artisan make:migration create_data_table –create=data

Go to the "database/migrations" folder and open the migration php file you've just created. Edit it to resemble this.

<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration;

class CreateData extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('data', function (Blueprint $table) { $table->increments('id'); $table->float('pollution'); $table->double('longitude',8,6); $table->double('latitude',8,6); $table->timestamps(); }); }

/** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('data'); } }

Next we'll create a model for the database by creating a new file in the "app/models" folder. (The folder isn't there so you should make that too). Again: replace data by the name you've chosen.

<?php use Illuminate\Database\Eloquent\Model;
class Data extends Model { protected $fillable = ['pollution', 'longitude', 'latitude']; }

Now we need to create a controller that manages the data input. Create a new file in the "app/http/controllers" folder called {name}controller.php. Mine's called DataController.php

<?php namespace App\Http\Controllers;

use Laravel\Lumen\Routing\Controller as BaseController; use App\Models\Data; use Illuminate\Http\Request;

class DataController extends BaseController { public function index(){ $data = Data::all(); return response()->json($data);

}

public function createData(Request $request){ $find = Data::where('longitude', $request->longitude)->where('latitude', $request->latitude)->get(); if($find->isEmpty()){ $data = Data::create($request->all()); return response()->json($find); }else{ $find = Data::where('longitude', $request->longitude)->where('latitude', $request->latitude)->first(); $update = Data::find($find->id); $update->pollution = $request->pollution; $update->save(); return response()->json($update); } }

public function deleteData($id){ $data = Data::find($id);

$data->delete();

return response()->json('success'); }

public function getCoords($lat, $lon){ $data = Data::whereBetween('longitude', [($lon-0.2), ($lon + 0.2)])->whereBetween('latitude', [($lat - 0.2), ($lat + 0.2)])->get(); return response()->json($data); }

}

Last step is to update the routes.php file so that our api can be approached from the outside. Head over the "app/http" folder and edit the "routes.php" file. This will make the api accessible by going to "www.domain.tld/api/v1/data".

$app->group(['prefix' => 'api/v1','namespace' => 'App\Http\Controllers'], function($app) { $app->get('data','DataController@index');

$app->get('places/{lat}/{lon}','DataController@getCoords');

$app->post('data','DataController@createData');

$app->delete('data/{id}','DataController@deleteData');

});

That's it! You're done with your API.

?>

Step 2: Step 2: Wiring Your Arduino

The cheapest, fastest, and most reliable components are those that aren’t there.

The wiring is dead simple. The sainsmart MQ135 has 4 pins, and we'll be using 3. Get out a small breadboard and plug in the MQ135.

  • Wire 1 cable from the Arduino's 5V to the V pin on the MQ135.
  • Wire 1 cable from the Arduino's GROUND to the G pin on MQ135.
  • Wire 1 cable from the Arduino's Analog0 to the AO pin on the MQ135.

Step 3: Step 3: Coding Your Arduino

For this part, I'll assume you've already connected your arduino Yun to the interwebz. Be it ethernet or wifi.

Open up arduino and load a new sketch. For this part we'll need some simple libraries so load up these into your sketch.

#include <Bridge.h>;
#include <Console.h>;
#include <HttpClient.h>;
#include <Process.h>;
#include <YunClient.h>;
#include <SPI.h>;

Next up we'll be adding some variables into the mix. The important ones you'll need to edit

  • int MQ135. This is the analogPin your sensor is hooked up to. For me it was 0.
  • String longitude. This is the longitude of my current location.
  • String latitude. This is the latitude of my current location.
  • IPAddress server(xx,xx,xx,xx). This is the IP address of your server. Note: The separators are commas and not dots.

The Setup!

void setup() {
Serial.begin(9600); // initialize serial communication while (!Serial); // do nothing until the serial monitor is opened

Serial.println("Starting bridge...\n"); pinMode(13, OUTPUT); digitalWrite(13, LOW); Bridge.begin(); // make contact with the linux processor digitalWrite(13, HIGH); // Led on pin 13 turns on when the bridge is ready

delay(2000); // wait 2 seconds }

The Loop! Each time the loop runs it will send a post request to our server. In this example we send on every few seconds. In real life (production) environments you should not do this if you have a lot of Yuns connection to your server.

void loop() {
// put your main code here, to run repeatedly: if (client.connect(server, 80)) { Serial.println("connected"); Serial.println(getReading(MQ135)); pollution = dtostrf(getReading(MQ135), 4, 2, buf); //convert float to string for post request

delay(2500); values="pollution="+pollution+"&latitude="+latitude+"&longitude="+longitude;

//set up the post request to our server client.println("POST /api/v1/data HTTP/1.1"); client.println("Host: www.domain.tld"); client.print("Content-length:"); client.println(values.length()); Serial.println(values); client.println("Connection: Close"); client.println("Content-Type: application/x-www-form-urlencoded;"); client.println(); client.println(values); }else{ Serial.println("connection failed"); delay(1000); } if(client.connected()){ client.stop(); //disconnect from server } delay(2000); }

float getReading(int pin) { return (analogRead(pin) * 0.004882814); // This equation converts the 0 to 1023 value that analogRead() // returns, into a 0.0 to 5.0 value that is the true voltage // being read at that pin. }

Step 4: Step 4:???

Let the magic happen....

Step 5: Step 5: Profit!

If all goes well you've just hooked up an arduino to the interwebz and it'll be spewing it's data towards your server.

Some things to think about

The API is not production ready. If you want to deploy this for whatever reasons you can think of, stop for a minute and think of some security measures and input data checks. While Lumen's Eloquent ORM takes care of nasty input that could destroy your DB, it doesn't know what to do if it doesn't receive the correct type of data.

The readings that are send to the server are plain voltages. If you want to do something with this data you should convert it to something you see fit. PPM is a logical next step for that data.