Steve Spencer's Blog

Blogging on Azure Stuff

Windows 10 IoT core project issues when upgrading to VS2015RTM

Just updated my VS2015 to RTM and tried to load in my blinky Iot Project for Raspberry Pi 2. It didn’t load and I was informed that the project required updating

clip_image001

Right clicking on the project offers the option to download updates

Selecting this takes you to:

https://msdn.microsoft.com/en-us/library/windows/apps/xaml/mt188198.aspx

It looks like all I can do is to create a new blank project and copy the existing project files over.

Created a new project and copied the contents of MainPage.xaml.cs and MainPage.Xaml over the contents of the files created in the new project. I found it was quicker to do this than to copy the files over manually. Also, change the namespace (if you created a project with a different name) in both MainPage.xaml.cs and MainPage.xaml. Add in all other files you are using by right clicking on the project and clicking Add Existing Items…

Need to add the following reference:

clip_image002

In the project properties: I selected remote debugger and entered the ip address of my raspberry pi.

When I tried to debug the deployment failed because the version of the remote debugger on the raspberry pi2 was out of date. In order to upgrade it I needed to also upgrade my Windows 10 to the latest version.( https://ms-iot.github.io/content/en-US/win10/SetupPCRPI.htm ) then reflash my raspberry pi 2 sd card ( https://ms-iot.github.io/content/en-US/win10/SetupRPI.htm)

I first updated my Win 10 VM but when I ran the WindowsIoTImageHelper it would not recognise the SD card of the host machine and I couldn’t seem to force it to use the SD card on the host. I then updated my surface Pro to the latest Windows 10 and repeated the process to reflash my Pi.

With all the upgrades completed my project now deploys and runs fine on my updated Raspberry PI2.

Issues setting up Raspberry Pi, Windows 10 IoT core and Visual Studio on a Windows 10 VM

After setting up my Surface Pro with Windows 10 and IoT core I decided that in order to demo it all I needed a Windows 10 VM with it all on. I had a couple of issues that I didn’t get on my Surface Pro.

The first issue I had was that the Windows IoT core watcher application would not run properly and kept shutting down. This is a known bug and has a work around:

Launch the "Developer Command Prompt for VS2015" as Administrator
change the working directory over to "C:\Program Files (x86)\Microsoft IoT"
sn -Vr WindowsIoTCoreWatcher.exe
corflags WindowsIoTCoreWatcher.exe /32BIT+ /FORCE

 

The second issue was Visual Studio couldn’t connect to TFS online. When I tried to manage connections I got the following error:

SplitterDistance must be between Panel1MinSize and Width - Panel2MinSize.

This seems to happen on both VS 2015 Enterprise RC and Community RC editions. I found a work around as follows:

Open up Team Foundation Server online at <youraccount>.visualstudio.com. Click code, then navigate to the project you want to open, click on the solution file which then opens the solution in the web editor. Click the visual studio icon and VS opens with the team project now in team explorer. Close VS and open it again and your team project should still be  connected to team explorer

 

Now with Visual Studio working I needed to set Windows into developer mode. This can be done as follows:

Start->settings->Update & Security -> For Developers. However, when I tried this the setting page kept closing. You can also use the Group Policy editor (Gpedit.msc) as follows:

https://msdn.microsoft.com/en-us/library/windows/apps/dn706236.aspx

clip_image001

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bdP+vLLLwcNGjR79uytW7eWlZXJHCorK7/77rv777//lVdekZW/7bbbfH19VeXrr79+4sSJb775pqz8zp07Xfcmr6+vl5f88Y9/LCkpUSUn1Lt37zvvvHPHjh2y2p988olr3WQ+2dnZf/vb32QdVIm44YYbZB2kieQlK1eudP0WXnl5+TvvvGOumZSUFBISsmnTpoMHD+qiRq1fnIeHx/jx46UlP/roo1/+8pfp6emqJeUl8+fPl7a99tprVU0AAAAAaE/O9qgZ+IFkWN7OzJ8/X7br3nvvra6u1kUtrFixQp3jIzZs2KBLGzkcjqVLl6prtURiYuLgwYNVLC+ZPXt2s9meYnGFhYXqpJ5mi/jiiy9c85d5Xn755REREWry1KstampqHnnkEVW5JZntZ599Jpugazfau3evum+RIpsjG6ViWT1ZSV3P6Xz66aelULZITzeqrKw0L1EWcemll+qJFpuWn58/depU/VxjZaFic83i4uLJkyercpmb1HE9e1qLM7ektGFKSooE0qQLFiyYN2+exN/bngAAAAAAwLoOd4mcMmLEiHvuuUdP/DcPD48bb7zxyy+//POf/yzVDh8+nJqaeumll/72t7+V4Mknn/xhd+A2Gz58uGv+FRUVmzZtioqKuuWWW957773nnnvu1PP39/eXF27evFnWZ9iwYSpN1rt3b/VyKR87dqxsgqqsyLMrVqx466235Cmpr24jNWHChNdff33u3Lknu8O3S1BQ0F/+8pd169bdeeediYmJ+/btO3jwoCxaViAtLU1dp+YiG/L222+vXbvWVbmgoGDy5MmybubTvsLDw//6178+9thjUmfbtm2dOnVSGyJOa3HSkitXrpw9e7a8QcXFxYWFhT/72c+kbW+99dZmjQAAAAAAAM4eD+f33U35HFuUf/zORJOjf8hvtAEAAAAA0L4xaoZ7crsEk8fqCnnkglIAAAAAAFpi1Az31EEvkQMAAAAAAMCZQoIJAAAAAAAAlpBgAgAAAAAAgCUkmAAAAAAAAGCJ2yWYJkd7czN8AAAAAABOiFEz3JPb/YocAAAAAAAA2hYukQMAAAAAAIAlJJgAAAAAAABgidslmKak1cifngAAAAAAACaMmuGe3O4eTB6rK+TROTpETQIAAAAAABdGzXBPXCIHAAAAAAAAS0gwAQAAAAAAwBISTAAAAAAAALCEB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AAAACwhwQQAAAAAAABLSDABAAAAAADAEhJMAAAAAAAAsIQEEwAAAAAAACwhwQQAAAAAAABLSDABAAAAAADAEhJMAAAAAAAAsIQEEwAAAAAAACwhwQQAAAAAAABLSDABAAAAAADAEhJMAAAAAAAAsIQEEwAAAAAAACwhwQQAAAAAAABLSDABAAAAAADAEhJMAAAAAAAAsIQEEwAAAAAAACwhwQQAAAAAAABLSDABAAAAAADAEhJMAAAAAAAAsIQEEwAAAAAAACwhwQQAAAAAAABLSDABAAAAAADAEhJMAAAAAAAAsIQEEwAAAAAAACwhwQQAAAAAAABLSDABAAAAAADAEhJMAAAAAAAAsIQEEwAAAAAAACwhwQQAAAAAAABLSDABAAAAAADAEhJMAAAAAAAAsIQEEwAAAAAAACwhwQQAAAAAAABLSDABAAAAAADAEhJMAAAAAAAAsIQEEwAAAAAAACwhwQQAAAAAAABLSDABAAAAAADAEhJMAAAAAAAAsIQEEwAAAAAAACwhwQQAAAAAAABLSDABAAAAAADAEhJMAAAAAAAAsIQEEwAAAAAAACwhwQQAAAAAAABLSDABAAAAAADAEhJMAAAAAAAAsIQEEwAAAAAAACwhwQQAAAAAAABLSDABAAAAAADAEhJMAAAAAAAAsIQEEwAAAAAAACwhwQQAAAAAAABLSDABAAAAAADAEhJMAAAAAAAAsIQEEwAAAAAAACwwjP8PCHxZluw67wUAAAAASUVORK5CYII=">

Click None Selected, pick users from the list and click Select. These users have now been given access to your application. However, as I mentioned earlier all users who are part of your Azure AD currently are able to login to your web app, we need to now configure the app so that only assigned users can access it.

Click Properties in your enterprise application and set User Assignment required to yes and click Save. (repeat this for your other application)

Home Default Directory 
My New app I 
Enterprise Applicati'H 
Overview 
> Enterprise applications I All applications > My New app I Properties 
Properties 
Save Discard Delete 
Enabled for users to sign-in? O 
Diagnose and solve problems 
Manage 
properties 
Owners 
Users and groups 
provisioning 
Application proxy 
Self-service 
Security 
Conditional Access 
permissions 
Token encryption 
Name * (D 
Homepage URL G) 
Logo @ 
Application ID O 
Object ID O 
User assignment required? C) 
Visible to users? O 
My New app 
MN 
Select a file

Now only users who are assigned to your application can login. You can test this now. Go to the first application url and login with one of the users you assigned. Then go to the second app (you shouldn't have assigned any users just yet.) and login. This time you will get an error.

You can now assign users to the second application and the error should go away when you attempt to login.

We’ve now set up our applications in Azure AD and limited access to each application. In my next post I’ll show you how you can then add users from outside of your organisation to these applications.

Exporting Logs from Application Insights using Continuous Export

This is the fifth post of my series of posts about Log Analytics and Application Insights. The previous post talked about adding custom logging to you code using Application Insights. Now you’ve got your logging into Application Insights you can run log analytics queries and build dash boards, alerts etc. Sometime though you want to use this data in other systems and it would be useful if you could export the data and use it else where. This post will show you how you can regularly export the data from Application Insights into Azure Storage. Once it is in Storage it can easily be moved into other systems or used else where such as PowerBI. This can be achieved using the Continuous Export feature of Application Insights

To enable Continuous Export, login to the Azure management portal and navigate to Application Insights. Click on the instance you want Continuous Export enabled. The scroll down the options on the left until you find the Configure section and click on Continuous Export

image

To use Continuous Export you will need to configure a storage account. Click Add:

image

Click Data types to export:

image

I was only interested in the logs I emitted from my custom logging, so selected Custom Event, Exception and Trace then clicked OK

image

Next pick a storage location. Make sure you use one where your Application Insights instance is located otherwise you will be charged egress fees to move the data to a new datacentre.

image

You can now pick an existing storage account or create a new one. Upon selecting a storage account you can pick an existing  container or create a new one.Once a blob container is selected click OK.

Continuous Export is now configured. You will not see anything in the storage container until the next set of logs are sent to Application Insights.

My log analytics query shows the following logs have been generated:

image

If you look at the continuous Export configuration page you will see that the last updated date has changed.

image

Now look in blob storage. You should see a folder that is<ApplicationInsights_ServiceName>_<ApplicationInsights_InstrumentationKey

image

Click through and you will see a number of folders. One for each of the logs that I enabled when setting up Continuous Export. Click through one of them and you will see a folder for the date and then a folder for the hour of the logs. Then a file containing the logs for that hour.

image

You will get a row of json data for each row output for the log query. Note the whole logs emitted will be in each of the folders.

e.g.

{
     "event": [
         {
             "name": "Some Important Work Completed",
             "count": 1
         }
     ],
     "internal": {
         "data": {
             "id": "a guid is here",
             "documentVersion": "1.61"
         }
     },
     "context": {
         "data": {
             "eventTime": "2020-03-22T18:12:30.2553417Z",
             "isSynthetic": false,
             "samplingRate": 100.0
         },
         "cloud": {},
         "device": {
             "type": "PC",
             "roleInstance": "yourcomputer",
             "screenResolution": {}
         },
         "session": {
             "isFirst": false
         },
         "operation": {},
         "location": {
             "clientip": "0.0.0.0",
             "continent": "Europe",
             "country": "United Kingdom",
             "province": "Nottinghamshire",
             "city": "Nottingham"
         },
         "custom": {
             "dimensions": [
                 {
                     "CustomerID": "4df16004-2f1b-48c0-87d3-c1251a5db3f6"
                 },
                 {
                     "OrderID": "5440d1cf-5d06-4b0e-bffb-fad522af4ad1"
                 },
                 {
                     "InvoiceID": "a7d5a8fb-2a2e-4697-8ab4-f7bf8b8dbe18"
                 }
             ]
         }
     }
}

As the data is now out of Application Insights you can move it where ever you need it. You will also need to manage the blob storage data too otherwise you will end up with the logs stored in two places and the storage costs will be doubled.

One example of subsequent usage is exporting the data to Event Hub. As the data is in blob storage you can use a function with a blob trigger to read the blob in a row at a time and publish the data onto Event Hub:

[FunctionName("ContinuousExport")]
public static async void Run([BlobTrigger("logs/{name}", Connection = "ConitnuousExportBlobSetting")]Stream myBlob, string name,
     [EventHub("logging", Connection = "EventHubConnectionAppSetting")] IAsyncCollector<string> outputEvents,TraceWriter log)
{
     log.Info($"C# Blob trigger function Processed blob\n Name:{name} \n Size: {myBlob.Length} Bytes");
     StreamReader textReader = new StreamReader(myBlob);
     while (!textReader.EndOfStream)
     {
         string line = textReader.ReadLine();
         log.Info(line);
         await outputEvents.AddAsync(line);
     }
}

Note: This is an example, so will need additional code to make sure that you don’t exceed the Event Hub maximum message size

So with Continuous Export you can extract your log data from Application insights and move it to other systems for processing.

Processing data from IoT Hub in Azure Functions

If you have been following my previous posts (Part 1, part 2, part 3) you will know that I’m using an ESP 8266 to send data to the Azure IoT hub. This post will show you how to receive that data and store it in Azure Storage and also show how you can also forward the data onto the Azure Service Bus.

I’m going to use Visual Studio and C# to write my function. If you are unfamiliar with Azure functions you can setup bindings to a variety of Azure resources. These bindings make it easy to interface without needing to write a lot of boiler plate code. These bindings allow your function to be triggered when something happens on the resource or also use the output bindings to write data to these resources. For example, there are bindings for Blob and Table storage, Service bus, Timers etc. We’re interested in the IoT hub binding. The IoT hub trigger will be fired when an event is sent to the underlying Event hub. You can also use an output binding to put messages into the IoT hub event stream. We’re going to use the Table storage and Service bus output bindings.

To get started you need to create a new Function project in Visual Studio.

image

Select IoT hub trigger and browse to a storage account you wish to use (for logging) plus add in the setting name you want to use to store the IoT hub connection string.

image

This will generate your empty function with you preconfigured IoT hub trigger.

You need to add your IoT hub connection string to your setting file. Open local.settings.json and add in a new line below the AzureWebjobs settings with the same name you entered in the dialog. ConnectionStringSetting in my example.Your connection string can be found in the Azure Portal.

Navigate to your IoT hub, then click Shared Access Policies

image

Select the user you want to use to access the IoT hub and click the copy icon next to the primary key connection string.

image

You can run this in the Visual Studio debugger and when messages are sent to your IoT hub you should see a log appearing in the output window.

What I want to do is to receive the temperature and humidity readings from my ESP 8266 and store the data in Azure storage so that we can process it later.

For that I need to use the Table storage output binding. Add the binding attribute to your function below the FunctionName binding.

[return: Table("MyTable", Connection = "StorageConnectionAppSetting")]

Again, you will need to add the storage setting into your config file. Find your storage account in the Azure portal, click Access keys then copy the key1 connection string and paste it in your config file

image

To use Azure Storage Output binding you will need to create a class that represents the columns in you table.

image

I included a device id so that I can identify which device the reading we associated to. You will need to change the return type of your function to be TempHumidityIoTTableEntity then add the code to extract the data from the message.

Firstly, I changed the python code in my ESP8266 to send the data as json so we can process it easier. I’ve also added a message identifier so that we can send different messages from the ESP8266 and be able to process them differently.

sensor.measure()

dataDict = {'partitionKey': 'r',

      'rowkey':'recneptiot'+str(utime.ticks_ms()),

      'message':'temphumidity',

      'temperature':str(sensor.temperature()),

      'humidity': str(sensor.humidity())}

mqtt.publish(sendTopic,ujson.dumps(dataDict),True)

That means we can serialise the Iot Hub message into something we can easily access. So the whole function is below:

[FunctionName("Function1")]
[return: Table("yourtablename", Connection = "StorageConnectionAppSetting")]
public static TempHumidityIoTTableEntity Run([IoTHubTrigger("messages/events", Connection = "ConnectionStringSetting")]EventData message, TraceWriter log)
{
     var messageAsJson = Encoding.UTF8.GetString(message.GetBytes());
     log.Info($"C# IoT Hub trigger function processed a message: {messageAsJson}");

    var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(messageAsJson);

    var deviceid = message.SystemProperties["iothub-connection-device-id"];

    return new TempHumidityIoTTableEntity
     {
         PartitionKey = deviceid.ToString(),
         RowKey = $"{deviceid}{message.EnqueuedTimeUtc.Ticks}",
         DeviceId = deviceid.ToString(),
         Humidity = data.ContainsKey("humidity") ? data["humidity"] : "",
         Temperature = data.ContainsKey("temperature") ? data["temperature"] : "",
         DateMeasured = message.EnqueuedTimeUtc.ToString("O")
     };

}

Providing your config is correct you should be able to run this in the Visual Studio debugger and view your data in Table Storage:

image

I mentioned at the start that I wanted to pass some messages onto the Azure Service bus. For example we may want to do something if the humidity goes above 60 percent. In this example we could add a HighHumidity message to service bus for some other service or function to respond to. We’ll send the message as a json string so that we can action it later in a different service. You can easily add a Service Bus output binding to your function. However, this binding documentation shows it as another return value. There is an alternative binging that allows you to set a message string out parameter with the message contents. This can be used as follows:

    [FunctionName("Function1")]
     [return: Table("yourtablename", Connection = "StorageConnectionAppSetting")]
     public static TempHumidityIoTTableEntity Run([IoTHubTrigger("messages/events", Connection = "ConnectionStringSetting")]EventData message,
         [ServiceBus("yourQueueOrTopicName", Connection = "ServiceBusConnectionSetting", EntityType = EntityType.Topic)]out string queueMessage,
         TraceWriter log)
     {
         var messageAsJson = Encoding.UTF8.GetString(message.GetBytes());
         log.Info($"C# IoT Hub trigger function processed a message: {messageAsJson}");

        var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(messageAsJson);

        var deviceid = message.SystemProperties["iothub-connection-device-id"];

        queueMessage = null;
         if (data.ContainsKey("humidity"))
         {
             int humidity = int.Parse(data["humidity"]);

            if (humidity > 60)
             {
                 Dictionary<string, string> overHumidityThresholdMessage = new Dictionary<string, string>
                 {      
                     { "deviceId",deviceid.ToString()},
                     { "humidity", humidity.ToString()},
                     {"message", "HighHumidityThreshold" }
                 };
                 queueMessage = JsonConvert.SerializeObject(overHumidityThresholdMessage);
             }
         }

        return new TempHumidityIoTTableEntity
         {
             PartitionKey = deviceid.ToString(),
             RowKey = $"{deviceid}{message.EnqueuedTimeUtc.Ticks}",
             DeviceId = deviceid.ToString(),
             Humidity = data.ContainsKey("humidity") ? data["humidity"] : "",
             Temperature = data.ContainsKey("temperature") ? data["temperature"] : "",
             DateMeasured = message.EnqueuedTimeUtc.ToString("O")
         };

    }
}

We now have a function that reads the device temperature and humidity reading into table storage and then sends a message to a Service Bus Topic if the temperature goes above a threshold value.

Generating your IoT Hub Shared Access Signature for your ESP 8266 using Azure Functions

In my last 2 posts I showed how you can connect your ESP 8266 to the IoT hub to receive messages from the hub and also to send messages. One of the issue I had was generating the Shared Access Signature (SAS) which is required to connect to the IoT hub. I was unable to generate this on the device so I decided to use Azure Functions. The code required is straight forward and can be found here: https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-security#security-tokens

To create an Azure Function, go to the Azure management portal click the menu icon in the top left and select “Create a Resource”

image

Search for “Function”

image

and select “Function App” and click Create

image

Complete the form

image

And click Review and Create to accept the defaults or click next and work through the wizard if you want to change from the default values.

image

Click create to kick of the deployment of your new Azure Function. Once the deployment is complete navigate to the Function by clicking “Go To Resource”. You now need to create your function.

Click the + sign next to “Functions”. I used the In-portal editor as it was the easiest to use at the time as I already had most of the code copied from the site mentioned above.

image

Click In-Portal, then Continue and choose the Webhook + API template and click Create

image

Your function is now ready for editing. It will have some default code in there to give you an idea how to start

image


We’re going to use the previous SAS code in here and modify it to accept a json payload with the parameters you need for the SAS to be created.

The json we’ll use is as follows:

{
     "resourceUri":"[Your IOT Hub Name].azure-devices.net/devices/[Your DeviceId]",
     "expiryInSeconds":86400,
     "key":"[SAS Key from IoT hub]"
}

You can get you SAS key from the IoT hub in the Azure Portal in the devices section. Click on the device

image

Then copy the Primary or Secondary key.

Back to the function. In the editor Paste the following code:

C# function

#r "Newtonsoft.Json"

using System;

using System.Net;

using Microsoft.AspNetCore.Mvc;

using Microsoft.Extensions.Primitives;

using Newtonsoft.Json;

using System.Globalization;

using System.Net.Http;

using System.Security.Cryptography;

using System.Text;

public static async Task<IActionResult> Run(HttpRequest req, ILogger log)

{

     log.LogInformation("C# HTTP trigger function processed a request.");

     string token = "";

     try

     {

          string requestBody = await new StreamReader(req.Body).ReadToEndAsync();

          dynamic data = JsonConvert.DeserializeObject(requestBody);

          int expiryInSeconds = (int)data?.expiryInSeconds;

          string resourceUri = data?.resourceUri;

          string key = data?.key;

          string policyName = data?.policyName;

          TimeSpan fromEpochStart = DateTime.UtcNow - new DateTime(1970, 1, 1);

          string expiry = Convert.ToString((int)fromEpochStart.TotalSeconds + expiryInSeconds);

          string stringToSign = WebUtility.UrlEncode(resourceUri) + "\n" + expiry;

          HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(key));

          string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));

          token = String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}", WebUtility.UrlEncode(resourceUri), WebUtility.UrlEncode(signature), expiry);

          if (!String.IsNullOrEmpty(policyName))

          {

               token += "&skn=" + policyName;

          }

     }

     catch(Exception ex)

     {

          return (ActionResult)new OkObjectResult($"{ex.Message}");

     }

     return (ActionResult)new OkObjectResult($"{token}");

}

Click Save and Run and make sure that there are no compilation errors. To use the function you need to post the json payload to the following address:

https://[your Function Name].azurewebsites.net/api/HttpTrigger1?code=[your function access key]

To retrieve your function access key, click Manage and copy your key from the Function Keys section

image

We’re now ready to use this in micropython on your ESP 8266. I created a function to retrieve the SAS

def getsas(hubname, deviceid, key):

    import urequests

    import ujson

    dict = {}

    dict["resourceUri"] = hubname+'.azure-devices.net/devices/'+deviceid

    dict["key"] = key

    dict["expiryInSeconds"]=86400

    payload = ujson.dumps(dict)

    response = urequests.post('https://[your function name].azurewebsites.net/api/HttpTrigger1?code=[your function access key]', data=payload)

    return response.text

In my connectMQTT() function from the first post I replaced the hard coded SAS string with a call to the getsas function. The function returns a SAS which is valid for 24 hours so you will need to retrieve a new SAS once 24 hours has elapsed.


I can now run my ESP 8266 code without modifying it to give it a new SAS each time I want to use it. I always forgot and wondered why it never worked the next time I used it. I can now both send and receive data from/to the ESP 8266 and also generate a SAS to access the IoT hub. The next step is to use the data received by the hub in an application and send action messages back to the ESP 8266 if changes are made. I look forward to letting you know how I got on with that in a future post.

Sending data from the ESP 8266 to the Azure IoT hub using MQTT and MicroPython

In my previous post I showed you how to connect your ESP 8266 to the Azure IoT hub and be able to receive messages from the IoT hub to turn on a LED. In this post I'll show you how to send data to the IoT hub. For this I need to use a sensor that I will read at regular intervals and then send the data back to the IoT hub. I picked a temperature and humidity sensor I had from the kit of sensors I bought

image

This sensor is compatible with the DHT MicroPython library. I order to connect to the IoT hub use the same connect code that is in my previous post. The difference with sending is you need a end point for MQTT to send you temperature and humidity data to. The topic to send to is as follows:

devices/<your deviceId>/messages/events/

So using the same device id as in the last post then my send topic would be devices/esp8266/messages/events/

To send a message to the IoT hub use the publish method. This needs the topic plus the message you want to send. I concatenated the temperature and humidity and separated them with a comma for simplicity

import dht

import time

sensor = dht.DHT11(machine.Pin(16))

mqtt=connectMQTT()

sendTopic = 'devices/<your deviceId>/messages/events/'

while True:

    sensor.measure()

    mqtt.publish(sendTopic,str(sensor.temperature())+','+str(sensor.humidity()),True)

    time.sleep(1)

The code above is all that is required to read the sensor every second and send the data to the IoT hub.

In Visual Studio Code with the Azure IoT Hub Toolkit extension installed, you can monitor the messages that are sent to your IoT hub. In the devices view, right click on the device that has sent the data and select “Start Monitoring Built-in Event Endpoint”

v NO FOLDER OPENED 
You have not yet opened 
Open Fol 
> OUTLINE 
v AZURE IOT HUB 
v o recnepsiotu)l 
> Modules 
> Interfaces (Preview) 
Send D2C Message to 10T Hub 
Send C2D Message to Device 
Invoke Device Direct Method 
Edit Device Twin 
Start Monitoring Built-in Event Endpoint 
Start Receiving C2D Message 
Generate Code 
Generate SAS Token for Device 
Get Device Info 
Copy Device Connection 
Delete Device 
> Distributed Tracing Setting (Preview) 
> Endpoints 
"body": "23 54 
"applicationPro 
"mqtt-retain" 
[ 10THubFbni tor]

This then displays the messages that are received by your IoT hub in the output window

PROBLEMS 
OUTPUT DEBUG coNSOLE 
Azure IOT Hub Toolkit 
[10THub"bnitor] Created partition receiver [1] for consumerGroup [$Defau1t] 
[10THub"bnitor] [9:12:39 PM] Message received from [recnepsiotoøl] : 
"body": "23 54 
"applicationproperties 
"mqtt-retain": "true 
[10THubFbnitor] [9:14:28 PM] Message received from [recnepsiotoøl] : 
"body": "23,54 
"applicationproperties 
"mqtt-retain": "true

You can see in the body of the received message the temperature and humidity values that were sent.

I still need to sort out generating the Shared Access Signature and also programmatically access the data I send to the IoT hub. I hope to have blog posts for these soon.

Connecting the ESP 8266 to Azure IoT Hub using MQTT and MicroPython

Recently  was introduced to the ESP 8266 processor which is a low cost IoT device with built in Wi-Fi, costing around £3 - £4 for a development board. The thing that interested me (apart from price) was the device is Arduino compatible and will also run MicroPython. The version I purchased from Amazon was the NodeMcu variant with built in power and serial port via a microUsb port, so it makes an ideal board to start with as there are no additional components required.

clip_image001

This board however did not have MicroPython installed and that required a firmware change. The instructions were fairly straight forward and I followed this tutorial.

After installing MicroPython you can connect to the device using a terminal emulator via the USB serial port. Check in Device Manager to find the COM port number and the default baud rate is 115200. I used the Arduino Serial Monitor tool. In the terminal emulator you can press enter and you should get back the python REPL prompt. If not then you have the COM port or Baud rate wrong.

image

You can write you python directly into here but its easier to write the python in you PC then run it on the device. For this I use ampy

In Command Prompt install ampy using:

pip install adafruit-ampy

This allows you to connect to your device. Close the terminal emulator to free up the COM port then type the following to list the files on your device:

ampy --port COM4 --baud 115200 ls

The MicroPython Quick Ref will summarise how to access the GPIO ports etc but in order to connect to the IoT hub you will need to configure the Wi-Fi on the device. This can be done using the network module.

So create a new text file on your PC and write the code to connect to your Wi-Fi. To test this you can use ampy to run the python on the device:

ampy --port COM4 --baud 115200 run networking.py

Its a good idea to use print statements to help debug as once the run has complete the output will be reflected back in your Command Prompt.

Now you are connected to Wi-Fi we can start to look at connecting to the IoT hub. I am assuming that you already have your IoT hub set up. We now need to configure you new device. Navigate to the IoT hub in your Azure Portal. In Explorers click IoT Devices, then New

image

Enter your device id, the name your device will be known as. All your devices need a name that is unique to your IoT hub. Then click Save. This will auto generate the keys needed to generate the shared access signature needed to access the IoT hub later.

image

Once created you may need to click refresh in the devices list to see you new device. Click the device and copy the primary key, you will ned this for later to generate the Shared Access Signature used in the connection string. In order to generate a new Shared Access Token you can use Visual Studio Code with the Azure IoT Hub Toolkit extension installed. This puts a list of devices and endpoints in the explorer view and allows you to create a new Shared Access Token. find your device in the Devices list, Right click and select Generate SAS Token For Device

image

You will be prompted to enter the number of hours the token is valid for and the new SAS token will appear in the output window:

image

SharedAccessSignature sr=[your iothub name].azure-devices.net%2Fdevices%2Fesp8266&sig=bSpX6UMM5hdUKXHfTagZF7cNKDwKnp7I3Oi9LWTZpXI%3D&se=1574590568

The shared access signature is made up of the full address of your device, a time stamp indicating how long the signature is valid for and the whole thing is signed. You can take this an use it to test your access to IoT hub, so make sure you make the time long enough to allow you to test. The ESP8266 doesn't have a clock that can be used to generate the correct time so you will need to create the SAS off board. I’m going to use an Azure function with the code here to generate it.

Back to Python now. In order to connect to the IoT hub you will need to use the MQTT protocol. MicroPython uses umqtt.simple.

There are a few things required before you can connect.

Firstly the Shared Access Signature that you created above.

Next you will need to get the DigiCert Baltimore Root certificate that Iot Hub uses for SSL. This can be found here. Copy the text from -----BEGIN CERTIFICATE----- to -----END CERTIFICATE-----, including both the Begin and End lines. Remove the quotes and replace the \r\n with real new line in your text editor then save the file as something like baltimore.cer.

Next you will need a ClientId. For IoT hub the ClientId is the name of your device in IoT Hub. In this example it is esp8266

Next you will new a Username. For IoT hub, this is the full cname of your IoT Hub with your client id and a version. e.g. [your iothub name].azure-devices.net/esp8266//?api-version=2018-06-30

The following code should allow you to connect to the IoT Hub:

def connectMQTT():
     from umqtt.simple import MQTTClient

    CERT_PATH = "baltimore.cer"
     print('getting cert')
     with open(CERT_PATH, 'r') as f:
         cert = f.read()
     print('got cert')
     sslparams = {'cert':cert}

   CLIENT_ID='esp8266'
     Username='yourIotHub.azure-devices.net/esp8266/?api-version=2018-06-30'
     Password='SharedAccessSignature sr=yourIotHub.azure-devices.net%2Fdevices%2Fesp8266&sig=bSpX6UMM5hdUKXHfTagZF7cNKDwKnp7I3Oi9LWTZpXI%3D&se=1574590568'

   

    mqtt=MQTTClient(client_id=CLIENT_ID,server='yourIotHub.azure-devices.net',port=8883,user=Username,password=Password, keepalive=4000, ssl=True, ssl_params=sslparams)


     mqtt.set_callback(lightLed)
     mqtt.connect(False)

    mqtt.subscribe('devices/esp8266/messages/devicebound/#')
     flashled(4,0.1, blueled)


    return mqtt

set_callback requires a function which will be called when there is a device message sent from the IoT Hub. Mine just turns a Led on or off

def lightLed(topic, msg):
     if msg== b'on':
         statusled.on()
     else:
         statusled.off()

connect(False) means that the topic this device subscribes to will persist after the device disconnects.

I’ve also configured the device to connect to its bound topics so that any message sent to the device will call the callback function.

Now we need to have a process loop so that we can receive the messages. The ESP8266 does not seem to run async code so we need to call the wait_msq function to get any message back from the IoT hub

mqtt=connectMQTT()
print('connected...')
while True:
     mqtt.wait_msg()

save your python as networking.py (and make sure that all the code you wrote initially to connect to Wi-Fi is included) then run ampy again:

ampy --port COM4 --baud 115200 run networking.py

Your device should run now. I’ve used the Led flash to show me progress for connecting to Wi-Fi then connecting to IoT Hub and also through to receiving a message. There is a blue LED on the board which I’ve been using as well as a standard LED which is turned on/off based upon the device message received from the IoT Hub. The blue LED is GPIO 2.

In order to send a message from the IoT hub to your device then you can do this from the Azure Portal in the devices view. Click on the device then click Message To Device

image

Enter the Message Body (on or off) and click Send Message

image

Alternatively you can do this in Visual Studio Code by right clicking the device and selecting Send C2D Message To Device and enter the message in the box that pops up

image

In my example the Led lights when I enter on and turns off when I enter off. ampy is likely to timeout during this process, but that’s ok as the board will still be running. As we’ve put the message retrieval inside a loop then the board will continue to run. To stop it running you will need to reset the board by pressing the reset button.

My next step is to sort out automatically generating the Shared Access Signature  and then I’ll look at sending data to the IoT Hub