My first "real" IoT project

I've been experimenting with Arduino and RaspberryPi for a while. I got a pretty decent collection of sensors, LEDs, basicly everything that can be plugged to GPIO. But I didn't have a chance to build something useful with it until recently.

The idea

The idea came from demand - a dashboard of available toilets within a building. Please, don't ask about motivations :). So, essentially something like this:

Building

Greens show if the cabin is available, reds if it is occupied and gray if the monitoring module is offline or faulted.

This in turn brings all kind of possibilities - usage statistics (either for fun or for, let's say, analyzing how clean the place is), notifications when cabin becomes available, etc.

This is how project Enttoi was born. At least I will learn from it something, I thought...

Theory

Most IoT systems will have similar to this architecture:
Typical IoT
(Image: Niall Colling)

Sensor/s connected to board (Arduino, RPi, etc.) which capable to constantly read the sensor state and broadcast it via either LAN, WiFi or other methods to Internet gateway (e.g. router), which then gets into some centralized service to be persisted and processed. When you have the data in nice format, you can create UI and API tools around it.

I highly recommend watching Niall Coolling speach at NDC to get an idea of various details of the above architecture.

So this is the plan. Besides the final result itself, I wanted to code all parts of the system myself (avoid SaaS solution for IoT).

Building blocks

If you look at the diagram below, you may think here is another Architecture Astronaut:

Enttoi architecture

Please don't. Let's see why we need all this.

On site installation

So, how to detect that the cabin is occupied or not? There are many ways, using various types of sensors, but I noticed that in 99% the door will remain not completely closed when there is no one inside. So I used reed switch to detect if the door is completely shutted or not. Simple.

The hardware part

Each restroom contains:

  • Raspberry Pi board (AKA client from now on)
  • WiFi dongle. Here is the one I used and it worked great.
  • 2A power adapter (with WiFi dongle it should be at least 2A even if you use RPi 1)
  • Reed switch for each door. In my case it was 2-3 doors. I used those.
  • I also added LED, to indicate some basic debug information

And everything is constructed in the following way:

Board schematic

The software part

The core part is pretty simple - we will read each sensor's state every X ms. After reading, we will report on each change of state immediately or at least once every Y seconds (even if the state wasn't changed) to the gateway (more about it later).

This will provide an efficient way of getting real-time state of sensors and track of online/offline clients (due to network or other issues).

So the defaults I came up with are:

  • x - read sensor every 200ms
  • y - report every 30 seconds, unless the state changed or previous reporting failed, then we do it immediately

The reporting part is just a plain HTTP POST method with the following payload:

{
  "sensorType": "cabin_door", // type of the sensor that reporting
  "sensorId": 1, // the identifier of specific sensor connected to board
  "state":1 // the current state of the sensor; 0 - door open or 1 - door closed
}

You can find the source at Github repository.

Besides, there is some plumbing related to starting this application on boot of the board and exposing status via LED. You can find more details in readme.md of the same repository.

The gateway

The gateway is a web-server to whom all boards reports the state of sensors. Once the endpoint hitted, we do authorization part, some basic validations and check whether the state of the sensor was in fact changed since last report. Then we persist the state and, if it was changed, write a message to MQ topic.

Nothing fancy here. The source of gateway available at Github repository.

Background jobs

There are some task that happens constantly on the background. They are implemented as Azure WebJobs and source can be found in GitHub repository.

  • Clients availability - this job constantly checks when the last time board reported either heartbeat or sensor state. If nothing was reported during x period of time, then we'll mark the board as offline and write a message to MQ topic.

  • History writer - it is triggered by message in MQ topic and just writes historical data of sensors and boards state changes to Azure Table Storage. We will use this data later to analyze and produce statistics.

  • Stats - currently there is only one type of statistics implemented - hourly availability report. The job just reads raw historical data and normalize it to queryable format.

API

In this application we actually start to see the results. This is a web-server which has two types of APIs - REST and websocket. They both expose the data that written by either Gateway or by Jobs. The source code is here.

REST

With REST API it is simple. There is swagger integrated, so you get the idea of what you can get in "pull" manner:

Enntoi API

For instance, you can call /clients/all and get the list of all boards and whether they are online and which sensors they have. Here is the response:

[...
{
    "id": "762740f0-98f3-45c8-a6ee-760dc590c3ae",
    "tags": [
      "right",
      "men",
      "floor-4"
    ],
    "isOnline": false,
    "isOnlineChanged": "2016-03-23T20:19:31.809407Z",
    "sensors": [
      {
        "sensorId": 1,
        "sensorType": "cabin_door"
      },
      {
        "sensorId": 2,
        "sensorType": "cabin_door"
      }
    ]
  }
...]

And then, if you want to be more specific on sensors, you call /sensors/{clientId} and get:

[...
  {
    "clientId": "762740f0-98f3-45c8-a6ee-760dc590c3ae",
    "state": 1, 
    "stateUpdatedOn": "2016-03-23T20:18:30.89Z",
    "sensorId": 2,
    "sensorType": "cabin_door"
  }
...]

This alone can provide you with ability to build any type of UI application with near real-time ability to display the status.

Websocket

I used SignalR to abstract the transport layer. Upon connect the initial/current state of clients (boards) and sensors will be streamed through couple of the hub events, and then updates will be streamed via same events.

Depending on the network, I measured that time it takes for event to arrive from the moment the door is closed/opened is the less than 300ms.

Like I mentioned there are two types of events. sensorStatePush which has the following structure:

{
  "clientId": "762740f0-98f3-45c8-a6ee-760dc590c3ae",
  "sensorId": 2,
  "sensorType": "cabin_door",
  "newState": 1, 
  "timestamp": "2016-03-23T20:18:30.89Z" // when the state was received at the gateway
}

and clientStatePush with structure:

{
  "clientId": "762740f0-98f3-45c8-a6ee-760dc590c3ae",
  "newState": true, // true for online, false for offline
  "timestamp": "2016-03-23T20:18:30.89Z" // when the state was changed according to webjob
}

UI client

This app consumes API's and displays a nice interface. There is nothing special, except for the part that I wanted to try out Aurelia to build it. This turns to be an enjoyable adventure :)

There are a few views, pub-sub to separate WebComponents from views, DI to inject services, and there is also notifications functionality to notify when cabin become available:
board details UI

Actually, the most trickiest part was (at least for me) to make the dashboard responsive. Remember the building at the top? This is how it looks on mobile screen:
Mobile building

Which is cool.

Misc

All source code is available under this GiHub organization. You will notice that I got a help from a few contributors, which is great when you build something open sourced.

There is even totally different type of on site installation based on dual Arduinos communicating via RF receiver and transmitter. You can find it here.