IoT with Azure Serverless and UWP

IoT with Azure Serverless and UWP

Recently I had a case where SignalR service was used toghether with ASP .NET Core Web API. I thought that it would be nice to check how SignalR service works with Azure Functions and UWP applications. This is why I created sample scenario where real time IoT data is displayed in the UWP application.

UPDATE!

Thanks to Anthony Chu who informed me that I do not have to use “SignalRAccessTokenProvider” to retrieve information about SignalR connection in the UWP app. The only thing to do is to provide URL of Azure function with “negotiate” endpoint to the “WithUrl” method of “HubConnectionBuilder”class.

Remember that Function URL has to be without “negotiate” suffix - SDK will add it to the URL automatically. You can read my conversation with Anthony here on Twitter.

Solution architecture

Here is the solution architecture. IoT device with sensors sends data to the Azure IoT Hub. Then Function app is triggered each time new data is sent to the IoT hub. Once data is received, Function app connected with SignalR service is triggered. At the end UWP application can display real time data generated by the IoT device’s sensors.

Image not found

Image not found

IoT Hub config

Let’s start from the Azure IoT Hub configuration. New IoT Hub instance has to be created - free plan is totally fine. Once IoT Hub is created, new device should be registered:

Image not found

Copy connection string because we will use it in the next section to connect IoT device:

Image not found

IoT device config

For this project I used Azure IoT DevKit board (MxChip). It has many sensors including temperature and humidity. You can find instructions how to setup your machine to develop for MxChip and create sample project I used in this article. Device will send humidity and temperature data to the Azure IoT Hub.

SignalR service config

Azure SignalR service enables sending real time messages. You can read more about it in the official documentation. When creating Azure SignalR service remember to set “ServiceMode” to “Serverless”. I encourage you to read more about serice modes here.

Once service is created copy “Primary connection string” from the “Keys” tab. We will use it later in the article.

Device-data-trigger function config

Once data is received in the IoT Hub, “Device-data-trigger” function is triggered. In the source code of this function we can access device telemetry data (temperature or humidity):

    public static class DeviceDataTriggerFunction
    {
        private static HttpClient client = new HttpClient();

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

            HttpContent messageContent = new StringContent(messageBody, Encoding.UTF8, "application/json");
            var broadcastFunctionUrl = Environment.GetEnvironmentVariable("DeviceDataTriggerFunctionUrl", EnvironmentVariableTarget.Process);
            await client.PostAsync(broadcastFunctionUrl, messageContent);
        }
    }

As you can see first sensor’s data is retrieved and then this data is passed to the next function - “Messages”. Url of this next function is stored in the app settings under “DeviceDataTriggerFunctionUrl” setting name.

Messages function config

“Messages” function app is used to broadcast messages to all clients connected to the SignalR service.

        [FunctionName("messages")]
        public static Task SendMessage(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post")] string message,
            [SignalR(HubName = "devicedata")] IAsyncCollector<SignalRMessage> signalRMessages)
        {
            return signalRMessages.AddAsync(
                new SignalRMessage
                {
                    Target = "newMessage",
                    Arguments = new[] { message }
                });
        }

Negotiate function config

Negotiate Azure Function is used to connect to the SignalR service and get connection info. This connection info will be used by UWP application to receive real time data.

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

Source code for the function apps is available on my GitHub.

Azure resource group overview

My final resource group looks like below:

Image not found

UWP application config

Image not found

I created Universal Windows 10 Platform application to display real time data from the IoT device. It is available on my GitHub. UWP application first calls “negotiate” function app and once connection info is returned, app is connected to the SignalR service hub. This is done using below class:

ClientSignalR - this class is responsible for connection with SignalR service (including messages subscription)

    public class ClientSignalR : IClientSignalR
    {
        private HubConnection _hub;
        public HubConnection Hub
        {
            get
            {
                return _hub;
            }
        }

        public ClientSignalR()
        {
        }

        public async Task Initialize()
        {
            _hub = new HubConnectionBuilder()
                .WithUrl(AppConfig.DeviceDataBroadcastFunctionUrl)
                .Build();

            await _hub.StartAsync();
        }


        public void SubscribeHubMethod(string methodName)
        {
            _hub.On<string>(methodName, (data) =>
            {
                OnMessageReceived?.Invoke(data);
            });
        }

        public async Task SendHubMessage(string methodName, string data)
        {
            await _hub?.InvokeAsync(methodName, data);
        }

        public async Task CloseConnection()
        {
            await _hub.DisposeAsync();
        }

        public event Action<string> OnMessageReceived;
    }

Remember that Azure Function URL provided to the “WithUrl” method has to be without “negotiate” suffix.

Summary

In this article I showed how to connect together Azure serverless services - Azure Functions and SignalR service to provide real time data to the UWP application from the IoT device. Of course this is a sample scenario - you could use these services in the different way (live updates about users registrations or location changes) - the main purpose was to show how to use them together.

Updated: