Geolocating your iPhone through the Internet of Things (iOS, Node and MQTT)

(Code on GitHub – follow the installation instructions on the README.md file)

The word most often encountered when reading about the Internet of Things (from here on referred to as IoT) is “underhyped” [1] [2] (and even ze Germans use it! [3]). This might mean its worth checking out!

Internet of what?

…of things, like phones, thermostats, smart fridges and some say, even cups. Smart devices connecting to the internet and sending updates on their status. As you can imagine, the possibilities are endless and one after the other, appliances are starting to add the word “smart” in front of their names.

Obviously phones are already smart, especially those running on iOS 😉 and they are ready to send their data though the various protocols that being developed and used for this purpose. One of them is MQTT.

What is MQTT and why do we need to hack it?

Originally designed to cater for other needs*, the protocol is widely used for IoT applications. You can (and probably should) look into the official specification if you want to bend the protocol to your will, but for now I am going to try and give you all that you need to start.

* MQTT was designed for the needs low powered devices communicating over slow networks, so it is as minimal, in terms of data transmitted and computations required, as possible. It also means it is binary, which makes it hard to read a a packet trace.

MQTT is a PUB/SUB protocol (much more convenient than writing publish/subscribe) which means that any connection between devices (i.e. MQTT client) is indirected through a central server node called the MQTT broker. What is important to understand is that if we have a thermostat sending its readings through the protocol and a web client connecting to this feed, both thermostat and browser are equal members in the MQTT network and they can both publish and subscribe to messages via the broker.

The PUB/SUB model means that the messages will never be guaranteed a response like in HTTP which is a request/response protocol. There is no 2-way connection between the clients, making the communication not as direct as we might have wanted. There is still the possibility for a client to “send” something to another however, as long as the received subscribes to a channel that the publisher sends data in.

How are clients identified on the network? Well… on the network they are not. A client will send a client id to the broker upon connection but a broker does not route messages based on their client id – only through their topic.  This is huge (hence the underlining and bolding) as it makes any kind of connection between the clients even harder.  To solve this problem we will create composite topics of the form:

A client can then subscribe to device123/status and get the status messages for that particular device.

NOTE: keep in mind that the payload of a message is a byte stream and not am ascii string, so in javascript we will need add the appropriate conversions.

Lets Build Something

We will have a our iOS devices broadcast their location to make themselves discoverable via the web. There are 3 components to our app: the iOS application, the MQTT Broker (written in Node) and the front-end Javascript based MQTT client.

NOTE: There are multiple libraries for each of these environments and I do not feel strongly of any of them in particular. The ones used here work well.

iOS Client

Download MQTTKit and follow the installation instructions.

The UI will allow the user to connect to an MQTT broker and start pinging the phone’s location by turning the switch for Location Services on. When the client connects, it will immediately publish an “Online”message to the status topic. My reasoning is that a device is not necessarily “live” until it has transmitted something… anything! Also, it should be able to go offline, publishing an “Offline” to status and do some work in the background before actually disconnecting.

The code looks like this:

IMPORTANT: (and this is something not mentioned in the repository) that the callbacks happen in a background thread, so to update the UI you need to call the main thread via GCD. I suspect it is a bug that might change in subsequent versions.

Notice the retain:YES in the status string published. This is sending a retained message to the broker which will be kept in memory by the broker whether or not there are subscribers at the current moment. Any future subscriber will automatically receive the last message published on the topic upon subscription. This makes retained messages into a sort of  key-value store where the topic is the variable and the message the value.

Sending the Location

We need to link the CoreLocation framework and import it in our code:

you

Location services imply an iOS device rather than a simulator as a Mac does not have a GPS. The simulator can fake the service however by setting the Debug > Location Services into something other than ‘Don’t simulate location’.

Screen Shot 2015-08-11 at 10.05.08

We also have the choice of sending a custom location, like the karmadust entry you see on the table above. This is done by creating a GPX file and pressing the ‘Add GPX…’ On the table above. Ours looks like this:

Also, on iOS 8 one must set two keys on the Info.plist file. There are

  • NSLocationWhenInUseUsageDescription | NSString | “Any Explanation will do”
  • NSLocationAlwaysUsageDescription          | NSString | “Any Explanation will do”

The connect the on/off switch from the UI with:

The, upon the delegate call we publish a message to the broker like so:

Be careful about the formatting of the string. JSON is not as loose as Javascript and requires double quotes for all keys, so better leave the details to the JSON formatter.

 

The Broker

The library we will be using is called Mosca and it is written by Matteo Collina.

The library offers a lot of great features like the ability to cluster more than one instance (through a central data store) and web socket support. Last but not least, it comes as an executable that can run as a broker out of the box and that is what we are going to start with. On a terminal:

This will create an instance of the broker running on the standard MQTT port of 1883 for TCP connections and on the port defined in the arguments (3000) for WebSocket connections.

If you run the iOS client as coded above (or from the repo of this project) you should see that the client is getting connected with the client id defined.

Web Client and Map Display

Create a standard index.html page. Download the Paho file locally rather than linking it directly from its remote location (the creators have politely asked us not to). Link the Google Map api.

We are hard coding the client id as “D101” for demo purposes only.

We need to run the code above through an HTTP server of some sort, simplest solution:

Then we just hit http://localhost:8080 and get the page. Click connect and then subscribe and you should see:

Screen Shot 2015-08-11 at 14.14.40

And Yes! It is in London 😉

 

Advanced Broker

Download Mosca locally through npm. Install MongoDB as per below.

Running the standalone Mosca app is great, but we probably need more control of our messages besides routing them around to the correct subscribers and so we will build our own broker using the library.

Mosca is built for clustering, i.e. many instances working together as one, and to achieve this it must use an external data store for inter-communication. The store can be Redis, MongoDB or RabbitMQ as the connectors to these systems are offered out of the box by the library.

On a Mac, install MongoDB by running

Create a “mongo” folder locally* for use by Mongo.

*it is already added to .gitignore in the project

Start MongoDB in the background through the –fork argument

Test if it is alive by connecting to the terminal

it should show at least the local db.

NOTE: To install the example in the example repo run npm install inside the /broker folder.

Now, create a broker.js file and copy pretty much what is in the example on the Mosca GitHub page provided by the author of the library. We use the http module to create an HTTP server that will listen to the WebSocket connections from Paho. Mosca is designed to get such a server attached as shown below:

Finally start the mosca broker.

What this does for us is add the ability to add logic. In the short example above we make the broker send an “Offline” message when a client disconnects, normalising the process on the web client front.

More importantly, we can also push messages in other places than the connected clients. By handling the callbacks we can dump each packet received in a database (remember, what you read as persistence in the code above is just for intercommunication of multiple broker instances!) or we can use RabbitMQ to push messages for added processing. This can be extremely helpful because when talking about IoT applications we should think it terms of tens of thousands or millions  of concurrent connections rather than a few hundreds.

4 thoughts on “Geolocating your iPhone through the Internet of Things (iOS, Node and MQTT)

  1. thanks for writing this article!!
    I use OSX to build this project
    but after I follow your github installation step
    and I run localhost and the web client cannot connect to 127.0.0.1 and don’t subscribe
    also cannot receive geolocation on google map
    please tell me how to solve it ??

    1. It definitely works. I just checked it. What I also did was update the GitHub README file with additional info. Have a look. You need to run a MongoDB instance, you need to run npm install and pod install to get all the dependencies and then you need to click on both connect and subscribe on the web interface. Run through the steps and see if you missed anything. Let me know.

      1. When I run git clone git@bitbucket.org:mmick66/mqttiosexample.git
        I got “Permission denied (publickey).
        fatal: Could not read from remote repository.
        Please make sure you have the correct access rights
        and the repository exists.”

        And after I run web on Chrome and click connect to Broker, it show “WebSocket connection to ‘ws://127.0.0.1:3000/mqtt’ failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED”
        and click subscribe to client 101, show “Uncaught Error: AMQJS0011E Invalid state not connected.”

Leave a Reply

Your email address will not be published. Required fields are marked *