Friday, March 11, 2022

How to present real-time IOT telemetry data with IOT Hub, Azure Functions and Azure SignalR

IOT is very common these days. We can solve many business problems with IOT and related tooling. 

How we can easily listen to live telemetry feed and present a real-time dashboard?

In this article I will show, how we can achieve it with Azure IOT Hub, Azure Functions and Azure SignalR

I will use an existing setup I used in this example I wrote previously. I have used Raspberry Pi Azure IoT Online Simulator to simulate as an IOT device and connected it to my IOT hub. Let's start from there.









I will break this exercise into three parts

  1. Create Azure Function with IOT Hub trigger
  2. Integrate Azure SignalR to the Azure Function
  3. Consume real-time data in a client application and plot data in a graph 

01. Azure Function with IOT Hub trigger

Step 01: Create a Function App












Step 02:  Select IOT Hub as the trigger








Step 03: Navigate to IOT Hub and locate the ConnectionString. Specify the ConnectionString in local.settings.json file



















Step 04: Change the function

[FunctionName("GetIoTEventData")]
public static async Task Run( [IoTHubTrigger("messages/events", Connection = "ConnectionString")]EventData message, ILogger log) { log.LogInformation($"C# IoT Hub trigger function processed a message: {Encoding.UTF8.GetString(message.Body.Array)}"); }


Step 05: Let's try out by running the function locally and starting the raspberry Pi simulator















I can see the function is executed when telemetry generated





Great! now the first part of our exercise is completed!

02. Integrate Azure SignalR to the Azure Function

Step 01: Create Azure SignalR service














Step 02: Get SignalR ConnectionString and update it in local.settings.json file



















{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "ConnectionString":"",
    "AzureSignalRConnectionString": ""
  },
  "Host": {
    "LocalHttpPort": 7072,
    "CORS": "*"
  }
}

Step 03: Let's add SignalR package

dotnet add package Microsoft.Azure.WebJobs.Extensions.SignalRService

Step 04: Add the negotiate endpoint

[FunctionName("negotiate")]
public static SignalRConnectionInfo Negotiate(
    [HttpTrigger(AuthorizationLevel.Anonymous)] HttpRequest req,
    [SignalRConnectionInfo(HubName = "IOTHub")] SignalRConnectionInfo connectionInfo)
{
    return connectionInfo;
}

Step 05: Create a new class to hold the telemetry information

public class Telemetry{
    public int messageId {get; set;}
    public string deviceId {get; set;}
    public long temperature {get; set;}
    public long humidity {get; set;}
}

Step 06: Let's modify the main function

[FunctionName("GetIoTEventData")]
public static async Task Run([
    IoTHubTrigger("messages/events", Connection = "ConnectionString")]EventData message, 
    [SignalR(HubName = "IOTHub")]IAsyncCollector signalRMessages,      
    ILogger log)
{
    
    Telemetry telemetry = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(message.Body.Array));

    await signalRMessages.AddAsync(
    new SignalRMessage
    {
        Target = "iotClient",
        Arguments = new[] { telemetry.temperature.ToString() }
    })
    .ConfigureAwait(false);
}


03. Consume real-time data in a client application and plot data in a graph

Step 01: I use the JavaScript SDK for SignalR. We need to refer the following URL

https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.7/signalr.min.js"

Step 02:  Following is the code we use to plot the graph

        google.charts.load("current", {
            packages: ["corechart", "line"]
        });

        google.charts.setOnLoadCallback(drawChart);
        function drawChart() {

            let data = google.visualization.arrayToDataTable([
                ["Second", "Temperature"],
                [0, 0]
            ]);

            let options = {
                title: "Device Temperature",
                hAxis: {
                    title: "Time"
                },
                vAxis: {
                    title: "Temperature"
                }
            };

            let chart = new google.visualization.LineChart(
                document.getElementById("chart_div")
            );
            chart.draw(data, options);

            let index = 0;

            const apiBaseUrl = window.location.origin;
            const connection = new signalR.HubConnectionBuilder()
                .withUrl(apiBaseUrl + '/api')
                .configureLogging(signalR.LogLevel.Information)
                .build();
            connection.on('iotClient', (message) => {
                data.addRow([index, parseInt(message)]);
                chart.draw(data, options);
                index++;
            });

            connection.start()
                .catch(console.error);
        }

That's it!. Following is the end result

I have pushed the code to GitHub


No comments: