How to implement realtime Communication between Device and Azure Function over IoT Hub

asked 2021-04-21T09:57:08.5+00:00
Patrick Schneider 86 Reputation points

Hello,

i would like to implement a low-delay (near-realtime) control loop over Azure IoT Hub.
(I am aware of the problems and challenges of the time relevant control via the cloud)

Control Loop:
The microcontroller sends the current position of an actuator to the IoT Hub.

Input:
{"deviceId":"MyDevice","position_x":14.00,"position_y":24.00,"position_z":75.00}

The cloud service automatically execute a short calculation and send a response to the microcontroller with the new velocity.

Return:
{"deviceId":"MyDevice","velocity":3.00}

The service should run through this loop with a minimum of delay.

Current status:

As a first Step i created an IoT Hub and ran the given Quickstart-Examples (in python).
(https://learn.microsoft.com/en-us/azure/iot-hub/quickstart-send-telemetry-python)

In the next stage I used the MSP432E401Y Microcontroller from Texas Instruments instead of the SimulatedDevice.py.

After that I added Eventgrid + Azure Function.

Overview:
89917-image.png

The Azure Function is in python and looks like:

function.json:

{  
    "scriptFile": "init.py",  
    "bindings": [  
    {  
         "type": "eventGridTrigger",  
         "name": "eventGridEvent",  
         "direction": "in"  
    }  
    ]  
}  

init.py:

 import json  
 import logging  
 import azure.functions as func  
 import datetime  
  
 import random  
 import sys  
 from azure.iot.hub import IoTHubRegistryManager  
      
 MESSAGE_COUNT = 1  
 AVG_WIND_SPEED = 10.0  
 MSG_TXT = "{\"service client sent a message\": %.2f}"  
      
 CONNECTION_STRING = "HostName=XXXX.azure-devices.net;SharedAccessKeyName=service;SharedAccessKey=XXXXX"  
 DEVICE_ID = "XXXX"  
      
 def iothub_messaging_sample_run():  
     try:  
         # Create IoTHubRegistryManager  
         registry_manager = IoTHubRegistryManager(CONNECTION_STRING)  
      
         for i in range(0, MESSAGE_COUNT):  
             print ( 'Sending message: {0}'.format(i) )  
             data = MSG_TXT % (AVG_WIND_SPEED + (random.random() * 4 + 2))  
      
             props={}  
             # optional: assign system properties  
             props.update(messageId = "Return_message_%d" % i)  
             props.update(correlationId = "correlation_%d" % i)  
             props.update(contentType = "application/json")  
      
             # optional: assign application properties  
             prop_text = "PropMsg_%d" % i  
             props.update(testProperty = prop_text)  
      
             registry_manager.send_c2d_message(DEVICE_ID, data, properties=props)  
      
     except Exception as ex:  
         print ( "Unexpected error {0}" % ex )  
         return  
     except KeyboardInterrupt:  
         print ( "IoT Hub C2D Messaging service sample stopped" )  
      
 def main(eventGridEvent: func.EventGridEvent) -> None:  
      
     result = json.dumps({  
         'id': eventGridEvent.id,  
         'data': eventGridEvent.get_json(),  
         'topic': eventGridEvent.topic,  
         'subject': eventGridEvent.subject,  
         'event_type': eventGridEvent.event_type,  
     })  
      
     logging.info('Python EventGrid trigger processed an event: %s', result)  
      
     iothub_messaging_sample_run()  
      
     msg_data = eventGridEvent.get_json()  
     logging.info(msg_data)  
          
     if 'properties' in msg_data and 'position' in msg_data['properties']:  
         logging.info(msg_data['properties']['position'])  

It works! But i am sure there is an easier or more efficient way to go (like Input + Output Binding in EventGrid). I tried it but didn't get it to work.

Question:
After I used the Provision Service to connect the device to cloud and used the authentication type "X.509 CA Signed" a "Connection String" isn't longer available.

Therefore my workaround over the connection string no longer work.

Could somebody explain me, how to fix it.

With best regards
Patrick

Azure Functions
Azure Functions
An Azure service that provides an event-driven serverless compute platform.
2,573 questions
Azure IoT Hub
Azure IoT Hub
An Azure service that enables bidirectional communication between internet of things (IoT) devices and applications.
680 questions
Azure Event Grid
Azure Event Grid
An Azure event routing service designed for high availability, consistent performance, and dynamic scale.
190 questions
No comments
{count} votes

Accepted answer
  1. answered 2021-05-04T11:53:13.523+00:00
    Sander van de Velde 15,131 Reputation points Microsoft MVP

    Hi @Patrick Schneider ,

    I have limited experience with Python too.

    I was able to get a Python Azure Function triggered:

    93593-image.png

    the function.json looks like this:

    {  
     "scriptFile": "__init__.py",  
     "bindings": [  
       {  
       "type": "eventHubTrigger",  
       "name": "events",  
       "direction": "in",  
       "eventHubName": "samples-workitems",  
       "connection": "mybeerliftconnection",  
       "consumerGroup": "pfa"  
       }  
     ]  
    }  
    

    in the portal:

    93537-image.png

    The mybeerliftconnection references the public eventhub-compatible endpoint of the IoT hub:

    Endpoint=sb://iothub-ns-[hidden].servicebus.windows.net/;SharedAccessKeyName=iothubowner;SharedAccessKey=[secret]=;EntityPath=beerlift-weu-ih  
    

    It is stored in the application settings of the Azure Function App:

    93549-image.png

    No comments

5 additional answers

Sort by: Most helpful
  1. answered 2021-04-21T18:40:21.84+00:00
    Sander van de Velde 15,131 Reputation points Microsoft MVP

    Hello @Patrick Schneider ,

    I see two questions:

    1. is this the least viable architecture?
    2. How do I connect using a Device Provisioning Service

    Regarding question 1:

    You make use of this solution: IoT Hub -> EventGrid -> Azure Function.

    Although this is a correct use of EventGrid, you can remove the EventGrid from the solution and connect an Azure Function directly to the IoT Hub (built-in eventhub compatible endpoint).

    Just select the iot hub (event hub) trigger:

    90015-image.png

    Connect to your iot hub:

    90074-image.png

    Note the Consumer group I added for the built-in endpoint in the IoT Hub (named 'fa').

    The 'Event Hub name' can be ignored.

    This is a bit less flexible but takes away the cost of the eventgrid.

    Question 2:

    In the code example, you use a symmetric key.

    There is an equivalent security mechanism in a DPS, check the individual enrollment with symmetric keys:

    90075-image.png

    Note: symmetric keys are considered less secure compared to the other options. Therefore, only use it in DEV and Test environment. The Group enrollment equivalent is also interesting.

    No comments

  2. answered 2021-05-07T11:10:45.737+00:00
    Patrick Schneider 86 Reputation points

    Hello @Sander van de Velde ,

    it works!

    Thank you very much.

    No comments

  3. answered 2021-04-27T06:35:02.72+00:00
    Patrick Schneider 86 Reputation points

    Hello @Sander van de Velde ,

    thank you very much. This helped!

    I created a function app on windows and .net as shown in your description. It works :-)

    However, I still have two questions:

    1)

    Problem:
    I would like to use python (-> Linux) instead of .net + windows.
    But editing functions in the Azure portal is not supported for Linux Consumption Function Apps.

    91595-image.png

    Therefore i created a python function in Visual Studio Code (with the same function.json):

    function.json:

    {  
      "scriptFile": "__init__.py",  
      "bindings": [  
        {  
          "type": "eventHubTrigger",  
          "name": "myIoTHubMessage",  
          "direction": "in",  
          "eventHubName": "samples-workitems",  
          "connection": "<iothub_connection>",  
          "consumerGroup": "$Default"  
        }  
      ]  
    }  
    

    But the python function doesn't work. I don't understand the difference and why it's not working.

    2)

    Now it's possible to send data from the iot device to the function app. What is the correct way to send a response to the iot device?

    Thank you very much! I hope you could help me farther.

    With best regards
    Patrick


  4. answered 2021-04-27T08:40:18.857+00:00
    Patrick Schneider 86 Reputation points

    For question 2)

    Because this doesn't work:

    91696-image.png

    function.json:

    {  
      "bindings": [  
        {  
          "type": "eventHubTrigger",  
          "name": "myIoTHubMessage",  
          "direction": "in",  
          "eventHubName": "samples-workitems",  
          "connection": "XXXXX_events_IOTHUB",  
          "consumerGroup": "consumergroup-001"  
        },  
        {  
          "name": "outputEventHubMessage",  
          "connection": "XXXXX_events_IOTHUB",  
          "eventHubName": "outeventhub",  
          "direction": "out",  
          "type": "eventHub"  
        }  
      ]  
    }  
    

    run.csx:

    using System;  
      
    public static void Run(string myIoTHubMessage, out string outputEventHubMessage, ILogger log)  
    {  
        log.LogInformation($"C# IoT Hub trigger function processed a message: {myIoTHubMessage}");  
        outputEventHubMessage = $"HelloWorld";  
    }  
    

    I found this https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-event-iot-output?tabs=csharp but i am not sure how to use the examples.