[IoT] Aquarium monitor; Raspberry & Azure IoT hub
Hi there! This is post #3 in my Aquarium monitor series. In the previous post I showed how I linked some sensors to my Arduino Nano and coupled that with a Raspberry Pi 2 via I2C. However, I didn’t yet show you how you can now get the data that the Arduino is sending. We’ll get to that in this post, along with details on how to push this data into the cloud, being an Azure IoT event hub. Enjoy!
Ok, so I am going to assume that you have done some work on Windows IoT running on a Raspberry Pi 2. If you haven’t, I would suggest following this Getting Started guide from Microsoft. There are some one-time steps to get it all working and after that deploying and debugging from Visual Studio is as easy as pressing F5!
Once you have a simple program running and deploying to the Raspberry, the next thing you want to do is read out the values the Arduino is reading. In post #2 I talked about I2C as the protocol used to communicate between the two devices. As with the Arduino, there is a nice library to help us out, which in this case is called Windows.Devices.I2c. Using it takes two steps, first one is initializing:
public bool InitI2C() { string aqs = I2cDevice.GetDeviceSelector("I2C1"); var dis = DeviceInformation.FindAllAsync(aqs); Task<DeviceInformationCollection> getDis = dis.AsTask(); getDis.Wait(); if (dis.GetResults().Count == 0) return false; else return true; }
So basically this method checks whether it can actually find any devices on the bus. Yes, devices in plural because the protocol supports talking to multiple devices. In this case, there will be only one.
Next up is reading the bus. You might remember from the Arduino code that we’re sending bytes, so that’s exactly what we’ll be receiving on the other end:
private async Task<I2CResponse> ReadI2CBus( int slaveAddress, byte[] buffer, int responseLength) { try { string aqs = I2cDevice.GetDeviceSelector("I2C1"); var dis = await DeviceInformation.FindAllAsync(aqs); if (dis.Count == 0) { return new I2CResponse { State = "No I2C bus found" }; } var settings = new I2cConnectionSettings(slaveAddress); using (var device = await I2cDevice.FromIdAsync(dis[0].Id, settings)) { device.Write(buffer); var responseToFill = new byte[responseLength]; device.Read(responseToFill); return new I2CResponse { Response = responseToFill }; } } catch (Exception ex) { return new I2CResponse { State = ex.Message }; } } private class I2CResponse { public byte[] Response { get; set; } public string State { get; set; } }
The I2CResponse typed object returned will contain the message received from the client as a byte array. You probably want to convert those values into parameters and wrap those in a reusable object. You can use code like this example;
if (string.IsNullOrEmpty(response.State)) { float temp1 = System.BitConverter.ToSingle(response.Response, 0); float temp2 = System.BitConverter.ToSingle(response.Response, 4); float pH = System.BitConverter.ToSingle(response.Response, 8); DateTime timestamp = DateTime.UtcNow; device.Temp1 = temp1; device.Temp2 = temp2; device.pH = pH; return device; }
As you can see, converting the value from a byte array back to a float is pretty simple. Don’t worry about the device object you see in the above code, you can store the values in any object you want, this is just the one I use (and it will get more clear when I release a full source code sample somewhere in the future).
That’s all there is to receiving and unwrapping the message sent from the Arduino. Not that hard either, right? Right!
Azure IoT hub
Ok so now you have your Raspberry in charge of the measured values. Now what? Well probably you’ll be looking to do something with those values. Whether it is storing them, analyzing them or triggering things. In my case, I have the following requirements:
- I want to be able to create graphs with the measured values. This, of course, requires storing them somewhere for future use.
- Whenever certain thresholds are met, I want alarms to trigger. For instance when temperature in my tank is too high or too low, this should trigger an alarm (in the form of a notification on my phone).
- Last but not least, I want to be able to create a mobile phone app to display the actual values. This requires some kind of endpoint (web service) which the app can query to get these values.
To gather messages from these kinds of devices, Microsoft introduced the IoT hub in Azure. This in basis is a service bus (which Azure already had) set-up specifically to communicate with IoT devices and process a lot of data. This is one thing to note; some of the samples out there are built to show the power of the Azure IoT solution, capable of sending tons of messages from tons of different devices. I won’t be doing that, but the basic principles still apply. Luckily most of the parts used are also available in low-cost (and some free) editions so you don’t need limitless credits to get going (I am using my enterprise MSDN subscription which is more then sufficient up to now).
Setting up the hub
Before we dive into the code, you first need to create an actual instance of an IoT hub in Azure. There is a good how-to detailing the steps (find it here), so I won’t go into too much detail. The how-to consists of three parts which I do want to explain:
- Create an IoT hub; this creates the core component, the hub itself. It’s important to keep in mind that this is a service bus without any input or output. So any message your device puts in there, goes nowhere unless you hook up things on the other end.
- Create a device identity; to connect to the hub, the device is required to have an identity. This is how the device identifies itself with the hub and how the hub knows which device is connecting. As said, an IoT hub have connections to tons of devices at the same time, but we’ll only need one identity for now.
- Receive device-to-cloud messages; this console app will act as a receiver for messages sent to the hub. It will do not much more than listen for messages and print them to screen, but when it does you’ll know your message successfully made it up there.
Having completed the how-to, you should now be able to start sending messages from your Raspberry to Azure.
Connecting the Raspberry to Azure IoT hub
It’s probably not a surprise any more that there is a nice little library we can leverage to communicate with the Azure IoT hub. To include it, add the Microsoft.Azure.Devices.Client NuGet package to your project. This will give you the components you need to set-up the connection. Another thing you need is the connection string specific to the device instance you created in the tutorial from the previous step. A nice tool to remember is the Device Explorer for Iot hub devices. This open-sourced tool allows you to quite easily find out which devices are registered with your hub and what their connection strings are. It’s this connection string that you’ll need in the code to connect:
Use the “Copy connection string for selected device” option to copy the connection string you’ll need.
With the connection string copied, you can now initiate a connection to Azure:
_deviceClient = DeviceClient.CreateFromConnectionString(deviceConnectionString);
And sending messages isn’t that hard either:
/// <summary> /// Sends an event to the IoT Hub /// </summary> /// <param name="device"></param> /// <param name="eventData"></param> /// <returns></returns> public Task SendDeviceToCloudMessagesAsync(dynamic eventData) { var eventId = Guid.NewGuid(); return SendDeviceToCloudMessagesAsync(eventId, eventData); } private async Task SendDeviceToCloudMessagesAsync(Guid eventId, dynamic eventData) { byte[] bytes; try { bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(eventData)); var message = new Microsoft.Azure.Devices.Client.Message(bytes); message.Properties["EventId"] = eventId.ToString(); await _deviceClient.SendEventAsync(message); Debug.WriteLine("{0} > Sending message: {1}", DateTime.Now, bytes); } catch (Exception ex) { // no error handling at this point yet } }
As you can see, the code first serializes the object as a json string and then converts that string to a byte array (hey! another byte array!). That byte array is what is sent up to the cloud and processed by the service bus. When you send a message through with the receiving console app attached, it should now show up on your screen!
Below is a screen dump of the console app I’m using, displaying messages that are being sent by my Raspberry:
Notice two different kind of messages. The first message is to identify the device with the service. This can help keeping track of when your devices have last booted for instance. The three messages after that contain the actual measurements coming from the sensors.
By the way, if you’re interested in a fully working sample; head over to the IoT Remote Monitoring sample on GitHub, which has everything you need (and probably more than you actually need). I’ve used that one as inspiration for the code I’m writing myself (ok you got me, I copy/pasted quite some bits too).
What now?
So now that we have messages being sent to the IoT hub, the “device” part of the story is pretty much done. And as you’ve seen, it wasn’t that hard; you just need to put all the pieces together in the right way to finish the puzzle.
Now that we have data being sent into the cloud, we can start hooking up things to the IoT hub to actually do things with that data. But that my friends, is for the next blog post to detail 😉 Cheers!
Nice to knows
Whenever I think of things that are helpful to know, I’ll add them below. Hope it helps!
- Whenever you get the error “The specified SAS token is expired”, you might want to check the datetime setting on your Raspberry. The SAS token is used in the authentication handshake and with a faulty datetime, Azure will reply that it’s invalid. You can simply correct the time on your device with the following command (run it in a remote powershell prompt):
set-date -date “1/5/2016 5:47PM”
January 5, 2016 at 7:57 pm |
[…] [IoT] Aquarium monitor; Raspberry & Azure IoT hub […]
March 14, 2016 at 10:04 am |
[…] [IoT] Aquarium monitor; Raspberry & Azure IoT hub […]
September 18, 2016 at 12:36 pm |
Jasper is het mogelijk dat je mij wat op weg helpt, ik heb alle hardware gisteren binnen gekregen ( Raspberry Pi 3 met 7″ touchscreen – Arduino Nano 2x – Relais bord – 3x ds18b20 temps)
Heb alles aangesloten en win10 iot geinstalleerd op de Raspberry Pi 3.
Ik heb veel ervaring met arduino en dat werkt nu prima temperaturen krijg ik netjes binnen maar de raspberry Pi is voor mij onbekende en leek mij juist een mooi project om hierin bekend te raken maar ik loop hier op vast.
Ik weet niet of je me hier een beetje op weg wil helpen zodat ik hiermee verder kan want ik ben erg tevreden met de mogelijkheden met de raspberry.
Ik hoor graag of je hier zin in heb mijn email adress is ernst.kroeze@live.nl
Alvast bedankt ik wist even geen andere manier om je te benaderen 😉
Vindt het een onwijs gaaf project en de opzet voor mij om me te verdiepen in de raspberry Pi.