Parsing your IoT

April 13th, 2015 6 min read

Last week, I started experimenting with Parse.com. Just like Azure, Parse is an online platform which can be used for hosting cloud applications including data storage. It offers a lot of powerful tools which will make IoT development much more fun, but has one major pitfall … until today!

While Parse.com offers fantastic SDK’s for easy implementation in any language and even offers IoT solutions, it has one major problem for Arduino and ESP8266 lovers: Their REST API will only work over HTTPS. And since the ESP8266 and Arduino aren’t powerful enough to encode your requests using SSL, HTTPS request aren’t an option for these platforms.

Luckily, there is a workaround! And the good news is: you don’t need any other service besides Parse.

Besides the REST API (and other awesome features like Push services and analytics), Parse allows you to host a website on their servers. Better yet: they allow you to built your own webserver using Node.js and Express. And the best part? This webserver is capable of talking to their Data service. Everything we need to built our own HTTP API! :)

Just like with the Azure example, I wanted to try to make a service which is able to store my IoT sensor states, and generate a state log. To improve it a bit I want to create a Sensor table with the most recent state. Additionally I wan’t to add info to a Log table.

First, I set the subdomain for my app:

image

Second, I create the two object classes using Parse’s online backend.

Sensor:

- objectId (String)
- identifier (String)
- state (Boolean)
- createdAt (Date)
- updatedAt (Date)

Log:

- objectId (String)
- sensor (Pointer<Sensor>)
- identifier (String)
- state (Boolean)
- createdAt (Date)
- updatedAt (Date)

Note that the Log class also contains an identifier. Of course this isn’t really necessary because it already contains a Pointer to the Sensor object, but for debugging purposes, it’s very convenient if I can quickly check the identifier of a Log object.

Fire up your coding!

Now, lets start the fun part … building the custom REST API. After setting up the Cloud Code using the Parse Command Line Tool, we need to create two files in our cloud folder:

main.js

This file is extremely simple just points to the api.js file by adding one line:

require('cloud/app.js');

app.js

The app.js will contain the express webserver and server logic to accomplish the desired API.

First, initialize Express in Cloud Code:

express = require('express');
app = express();

Next set the required API key to use for authentication. I just used the regular API key. Unfortunately, I was unable to figure out how to read out the the key from the environment variables, so it needs to be coded into this app.

var apiKey = 'supersecretapikey';

To simplify the parsing of the request body, we anable the express body parser.

app.use(express.bodyParser());

Now, lets add a route for posts requests…

app.post('/sensor', function(req, res) {

Within this request, get the request variables.

var requestApiKey = req.headers['x-parse-rest-api-key'];
var identifier = req.body.identifier;
var state = req.body.state;

Before we continue storing anything, let’s validate these variables. If any of the validations fail, report it back to the client.

// Check the API key.
if (requestApiKey === undefined || requestApiKey !== apiKey) {
    res.status(401).send({error:"Missing or incorrect API-key."});
    return;
}

// Validate the identifier.
if (identifier === undefined || identifier.length <= 0 || typeof identifier !== "string") {
    res.status(400).send({error:"Identifier missing or not a valid string."});
    return;
}

// Validate the state.
if (state === undefined || typeof state !== "boolean") {
    res.status(400).send({error:"State missing or not a boolean."});
    return;
}

To prevent unnecessary double database entries, convert the identifier to lowercase.

identifier = identifier.toLowerCase();

Next, create the Sensor and Log object so we can use it in our code.

var Sensor = Parse.Object.extend("Sensor");
var Log = Parse.Object.extend("Log");

Using our identifier, look up the sensor object in the Sensor table.

var query = new Parse.Query(Sensor);
query.equalTo("identifier", identifier);
query.first({
    success: function(sensor) {

If the Sensor for the requested identifier is not found. Create a new object.

        if (!sensor) {
            var sensor = new Sensor();
            sensor.set('identifier', identifier);
        }

Set the requested state for the found or newly created sensor.

        sensor.set('state', state);

Save the found or newly created sensor with updated state.

        sensor.save(null, {
            success: function (sensor) {

If saving finished, create a new log object with a pointer to the sensor. If everything goes well, report a succes message to the client. If we run into any issues, inform the clients with a error message …

                    var log = new Log();
                    log.set('sensor', sensor);
                    log.set('identifier', identifier);
                    log.set('state', state);
                    log.save(null, {
                        success: function (log) {
                            // Everything ok. Report back to the client.
                            res.send({success:true, identifier:identifier, state:state});
                        },

                        error: function (error) {
                            // Error saving the log. Report error to client.
                            res.status(400).send({error:"Log save error: " + error.code + " " + error.message});
                        }
                    });

                },
                error: function (error) {
                    // Error saving the sensor object. Report error to client.
                    res.status(400).send({error:"Sensor save error: " + error.code + " " + error.message});
                }
            });



        },
        error: function(error) {

            // Error performing the initial query. Report error to client.
            res.status(400).send({error:"Query error: " + error.code + " " + error.message});;
        }
    });

});

Last but not least, enable the express webserver.

app.listen();

And with this in place, we can now send http requests to our HTTP API using the url we configured …

Request:

POST /sensor HTTP/1.1
X-Parse-REST-API-Key: supersecretapikey
Content-Type: application/json
Host: homesensor.parseapp.com
Content-Length: 40

{"identifier":"dishwasher","state":true}

Response:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Mon, 13 Apr 2015 18:11:58 GMT
Server: nginx/1.6.0
Content-Length: 55
Connection: Close

{"success":true,"identifier":"dishwasher","state":true}

After checking Parse’s data browser, we once again solved a first world problem: Parse is now able to talk with simple MCUs like the Arduino and ESP8266.

image
image

Cool! Can I give it a try?

If you’re interested in giving this a spin, check out the complete app.js code on GitHub. And if you like to try it out using a ESP8266 via the Arduino IDE check out this example sketch.

If you have any questions or suggestions, leave a comment down below. Happy Parsing!

Loading comments …
©2021 - MichaelTeeuw.nl