I am still pretty new to digital twins/IoT and am currently working on a digital twin solution using simulated data from the IoT Hub VS Code extension. I can successfully send the simulated telemetry data to my IoT Hub however when I try to forward it onto my ADT instance using a Function file it is not being received.
However I can see that the messages are being received in ADT and being executed in my function file while I run the code. However when I update my query in ADT it is not picking up these messages and displaying them within the explorer.
Any help will be greatly appreciated as I have hit a bit of a wall with it today!
From my thoughts there are 3 possible causes that might stop it from receiving the telemetry data.
The Azure Function code - I have checked this over a couple times and have even checked with chat gpt to ensure this is correct.
The DTDL model in ADT - I based this off of the working version of a twin I made before using a virtual raspberry pi and just added the simulated data sources which I was including.
The Event Grid - Not 100% sure about this being the cause as from what I know of the process however I did the exact same set up as I did with my previous working project.
I have tried setting up again in a new environment as well as changing the DTDL and Function file however that did not seem to change anything.
Here is the code used in my simulated device (Node.js):
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
// The device connection string to authenticate the device with your IoT hub.
//
// NOTE:
// For simplicity, this sample sets the connection string in code.
// In a production environment, the recommended approach is to use
// an environment variable to make it available to your application
// or use an HSM or an x509 certificate.
// https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-security
var connectionString = 'My Connection String';
// Using the Node.js Device SDK for IoT Hub:
// https://github.com/Azure/azure-iot-sdk-node
// Run 'npm install azure-iot-device-mqtt' to install the required libraries for this application
// The sample connects to a device-specific MQTT endpoint on your IoT Hub.
var Mqtt = require('azure-iot-device-mqtt').Mqtt;
var DeviceClient = require('azure-iot-device').Client;
var Message = require('azure-iot-device').Message;
var client = DeviceClient.fromConnectionString(connectionString, Mqtt);
// Print results.
function printResultFor(op) {
return function printResult(err, res) {
if (err) console.log(op + ' error: ' + err.toString());
if (res) console.log(op + ' status: ' + res.constructor.name);
};
}
// Create a message and send it to the IoT hub every second
setInterval(function(){
// Simulate telemetry.
var temperature = 20 + (Math.random() * 15);
var humidity = 60 + (Math.random() * 20);
var flowrate = 100 + (Math.random() * 50);
var wattage = 60 + (Math.random() * 40);
var oillevel = 50 + (Math.random() * 50);
// Add the telemetry to the message body.
var data = JSON.stringify({ temperature: temperature, humidity: humidity, flowrate: flowrate, wattage: wattage, oillevel: oillevel });
var message = new Message(data);
// Add a custom application property to the message.
// An IoT hub can filter on these properties without access to the message body.
message.properties.add('temperatureAlert', (temperature > 30) ? 'true' : 'false');
console.log('Sending message: ' + message.getData());
// Send the message.
client.sendEvent(message, printResultFor('send'));
}, 1000);
Here is the code from my Azure Function File (C#):
// Default URL for triggering event grid function in the local environment.
// http://localhost:7071/runtime/webhooks/EventGrid?functionName={functionname}
using System;
using Azure;
using System.Net.Http;
using Azure.Core.Pipeline;
using Azure.DigitalTwins.Core;
using Azure.Identity;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Azure.Messaging.EventGrid;
using System.Diagnostics;
using System.Text;
namespace IotHubtoTwins
{
public class IoTHubtoTwinsVSCode
{
private static readonly string adtInstanceUrl = Environment.GetEnvironmentVariable("ADT_SERVICE_URL");
private static readonly HttpClient httpClient = new HttpClient();
[FunctionName("IoTHubtoTwins")]
// While async void should generally be used with caution, it's not uncommon for Azure function apps, since the function app isn't awaiting the task.
#pragma warning disable AZF0001 // Suppress async void error
public async void Run([EventGridTrigger] EventGridEvent eventGridEvent, ILogger log)
#pragma warning restore AZF0001 // Suppress async void error
{
if (adtInstanceUrl == null) log.LogError("Application setting \"ADT_SERVICE_URL\" not set");
try
{
// Authenticate with Digital Twins
var cred = new DefaultAzureCredential();
var client = new DigitalTwinsClient(new Uri(adtInstanceUrl), cred);
log.LogInformation($"ADT service client connection created.");
if (eventGridEvent != null && eventGridEvent.Data != null)
{
log.LogInformation(eventGridEvent.Data.ToString());
// <Find_device_ID_and_telemetry>
JObject deviceMessage = (JObject)JsonConvert.DeserializeObject(eventGridEvent.Data.ToString());
string deviceId = (string)deviceMessage["systemProperties"]["iothub-connection-device-id"];
// <added for Base64 to string to object conversion>
var base64EncodedBytes = System.Convert.FromBase64String((string)deviceMessage["body"]);
string deviceMessagedecoded = System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
JObject decodedObject = (JObject)JsonConvert.DeserializeObject(deviceMessagedecoded);
var temperature = decodedObject["temperature"];
var humidity = decodedObject["humidity"];
var flowRate = decodedObject["flowrate"];
var oilLevel = decodedObject["oillevel"];
var wattage = decodedObject["wattage"];
// </Find_device_ID_and_telemetry>
log.LogInformation($"Device:{deviceId} Telemetry data: Temperature={temperature}, Humidity={humidity}, FlowRate={flowRate}, OilLevel={oilLevel}, Wattage={wattage}");
// <Update_twin_with_device_telemetry>
var updateTwinData = new JsonPatchDocument();
updateTwinData.AppendReplace("/Temperature", temperature.Value<double>());
updateTwinData.AppendReplace("/Humidity", humidity.Value<double>());
updateTwinData.AppendReplace("/FlowRate", flowRate.Value<double>());
updateTwinData.AppendReplace("/OilLevel", oilLevel.Value<double>());
updateTwinData.AppendReplace("/Wattage", wattage.Value<double>());
await client.UpdateDigitalTwinAsync(deviceId, updateTwinData).ConfigureAwait(false);
// </Update_twin_with_device_telemetry>
}
}
catch (Exception ex)
{
log.LogError($"Error in ingest function: {ex.Message}");
}
}
}
}