Why is my azure digital twins model not picking up telemetry data when i run query?

Ho Kin Haw 25 Reputation points
2024-03-05T08:38:58.2366667+00:00

I have been trying to figure out why my telemetry data is not being reflected in my digital twins model when i run query. My sensor data is able to successfully send data to the IoT Hub (I am using a WeMos D1 R2 Ardunino board and a BME280 sensor here)

User's image

and based on the log stream from my azure function, it shows that the telemetry from IoT hub is being processed

User's image

the following is my azure function code:

using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using Azure.Core.Pipeline;
using Azure.DigitalTwins.Core;
using Azure.Identity;
using Microsoft.Azure.EventGrid.Models;
using Azure;
namespace DataIngestor
{
    public static class IoTHubToAzureDataTwinsFunction
    {
        private static readonly string adtInstanceUrl = Environment.GetEnvironmentVariable("ADT_SERVICE_URL") ?? string.Empty;
        private static readonly HttpClient httpClient = new HttpClient();
        [Function("IoTHubToADTFunction")]
        public static async Task RunAsync([EventGridTrigger] EventGridEvent eventGridEvent, FunctionContext context)
        {
            var logger = context.GetLogger("IoTHubToADTFunction");
            logger.LogInformation(eventGridEvent.Data.ToString());
            if (adtInstanceUrl == null)
            {
                logger.LogError("Application setting \"ADT_SERVICE_URL\" not set");
                return;
            }
            try
            {
                // Authenticate with Digital Twins
                var cred = new ManagedIdentityCredential("https://digitaltwins.azure.net");
                var client = new DigitalTwinsClient(
                    new Uri(adtInstanceUrl ?? string.Empty), // Provide a non-null default value
                    cred,
                    new DigitalTwinsClientOptions { Transport = new HttpClientTransport(httpClient) });
                logger.LogInformation($"ADT service client connection created.");
                if (eventGridEvent != null && eventGridEvent.Data != null)
                {
                    logger.LogInformation(eventGridEvent.Data.ToString());
                    // Parse the event data to a JObject
                    JObject eventData = JObject.Parse(eventGridEvent.Data.ToString() ?? string.Empty);
                    // Extract device ID and body
                    var systemProperties = eventData?["systemProperties"] as JObject;
                    var body = eventData?["body"] as JObject;
                    if (systemProperties != null && body != null)
                    {
                        string? deviceId = systemProperties["iothub-connection-device-id"]?.ToString();
                        var temperature = body["temperature"]?.Value
Azure Digital Twins
Azure Digital Twins
An Azure platform that is used to create digital representations of real-world things, places, business processes, and people.
231 questions
0 comments No comments
{count} votes

Accepted answer
  1. LeelaRajeshSayana-MSFT 16,281 Reputation points
    2024-03-05T17:13:48.86+00:00

    Hi @Ho Kin Haw Greetings! Welcome to Microsoft Q&A forum. Thank you for posting this question here.

    Analyzing the attached Azure function log image, I could tell that your function is not processing the data correctly. My first observation is that the message body is encoded. Please refer the below image for details.

    User's image

    The code is trying to extract device ID and the message properties from this body. Since it is encoded, it is failing to fetch the proper data, and I suspect you are getting null for these values. You can also confirm this behavior by observing the logs and identify that your following logger statement is not getting executed.

    logger.LogInformation($"Received telemetry data for device:{deviceId}. Temperature: {temperature}, Humidity: {humidity}");
    

    To resolve this issue, make sure that the telemetry message pushed from your simulator has utf-8 encoded and is in the content type form of JSON. Please refer the below sample code that shows how you can set this parameters.

    var telemetryDataPoint = new
    {
        Level_of_Material = currentTemperature
    };
    var messageString = JsonSerializer.Serialize(telemetryDataPoint);
    var message = new Microsoft.Azure.Devices.Client.Message(Encoding.UTF8.GetBytes(messageString))
    {
        ContentType = "application/json",
        ContentEncoding = "utf-8"
    };
    await deviceClient.SendEventAsync(message);
    Console.WriteLine($"{DateTime.Now} > Sending message: {messageString}");
    

    Once you have the ContentType and ContentEncoding for the message set as above, you can see that the message received by Azure function appears in plain text. Please find the below image of the event processed on my Azure function for reference.

    enter image description here

    Here is the snippet of the Azure function sample I tested for extracting the data and updating the Digital Twin. Kindly notice that there is a difference in the approach of extracting the data from the event.

    var credentials = new DefaultAzureCredential();
    DigitalTwinsClient client = new DigitalTwinsClient(
        new Uri(adtServiceUrl), credentials, new DigitalTwinsClientOptions
        { Transport = new HttpClientTransport(httpClient) });
    log.LogInformation($"ADT service client connection created.");
    
    if (eventGridEvent != null && eventGridEvent.Data != null)
    {
        try{
        log.LogInformation(eventGridEvent.Data.ToString());
    
        // Reading deviceId and temperature for IoT Hub JSON
        JObject deviceMessage = (JObject)JsonConvert.DeserializeObject(eventGridEvent.Data.ToString());
        string deviceId = (string)deviceMessage["systemProperties"]["iothub-connection-device-id"];
        var temperature = deviceMessage["body"]["Level_of_Material"];
    
        log.LogInformation($"Device:{deviceId} Level_of_Material is:{temperature}");
    
        //Update twin using device temperature
        var updateTwinData = new JsonPatchDocument();
        updateTwinData.AppendReplace("/Level_of_Material", temperature.Value<double>());
        await client.UpdateDigitalTwinAsync("deviceId", updateTwinData);
        }
        catch(Exception ex){
            Console.WriteLine($"Error : {ex.Message}");
        }
    }
    

    I could see my Digital Twin property updating without any issues.

    Please note that the method UpdateDigitalTwinAsync works when there is an initial value on the Digital Twin property. Please make sure you set an initial value either using the DTDL model of the Digital Twin explorer portal for smooth functioning.

    Hope this helps! Please let us know if you need any additional assistance on this.


    If the response helped, please do click Accept Answer and Yes for the answer provided. Doing so would help other community members with similar issue identify the solution. I highly appreciate your contribution to the community.

    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.