Device Twin
Overview​
For managing Edge Devices, each device synchronizes its Device Twin configuration to a compatible cloud platform. Every single setting of the Edge software can be controlled via its Device Twin from the central hopit Portal or any other compatible cloud portal. This way, scalable deployments and configuration roll-outs can be realized.
Currently supported platforms with Device Twin configuration are:
- hopit. Portal
- Azure IoT Hub
- Magenta IoT Hub
- ThingsBoard
- Custom MQTT Broker
The Target specific documentation can be found in the Targets section.
Flow​
A sample sequence diagram is shown in the following graph. First, Alice updates a setting on the Edge via the web interface. The new settings are reported to all enabled Portals and every client gets informed. As a next step, Bob changes a setting in the Portal, which is then sent to the Edge as a desired settings update. The Edge validates the settings, performs the update, informs its clients and reports the new settings back to the Portal.
Configuration​
The Device Twin synchronization can be enabled for one or more Targets.
Remote updates override unsaved edits to the settings they change. See Editing settings.
Corresponding Edge configuration and Device Twin definition for the Device Twin settings:
- Edge-UI
- Device Twin
The Device Twin service can be enabled with the Enabled checkbox. The Targets section lists all compatible Targets, which can be used to synchronize the Device Twin. To enable one or more Targets, check the checkbox and click Submit.

The Device Twin service can be enabled by setting the Enabled property to true. The Targets section is a list of the name of all compatible Targets, which can be used to synchronize the Device Twin. To enable one or more Targets, set the property to true.
{
"DeviceTwin": {
"Targets": {
"hopit": false,
"IoT Hub": false
},
"Enabled": true
}
}
Secret sanitization​
An unsanitized reported Device Twin contains the full settings of the Edge device. This can include Target settings marked as secret, such as passwords, private keys, and connection strings. Because a Target reports the Device Twin to its own endpoint, each Broker or Hub could otherwise receive marked secrets belonging to other Targets.
To prevent this, each reporting Target (MQTT, Azure, Magenta, ThingsBoard) has a Sanitize reported secrets option. When enabled, settings marked as secret are removed from the Device Twin before it is reported. This option is enabled by default.

Secret sanitization affects only the outbound reported Device Twin. It does not block incoming desired updates, so a remote endpoint can still submit changes to secret settings. Sanitized values are not echoed back in the reported Device Twin.
Definition​
The following section shows an explanatory Device Twin configuration:
Device Twin definition:
{
"License": {
"DeviceId": "***",
"LicenseKey": "***"
},
"Authentication": {
"Windows": {
"Enabled": true
},
"MicrosoftEntra": {
"Enabled": false,
"Id": null
}
},
"Deployment": {
"RegistryUrl": "https://repo.<server>.hopit.at/repository/nuget-group/",
"RegistryUser": "hopit-client",
"RegistryPassword": "***",
"Packages": {
"hopit.edge": {
"InstalledVersion": "1.0.0",
"DesiredVersion": "1.0.0",
"Status": "INSTALLED"
},
"plc-application": {
"InstalledVersion": "2.1.4",
"DesiredVersion": "2.1.4",
"Status": "INSTALLED"
},
"microsoft-edge": {
"InstalledVersion": "95.0.1020.30",
"DesiredVersion": "",
"Status": "INSTALLED"
}
},
"Enabled": true
},
"Tasks": {
"ProvisioningChain": [
{
"Command": "Discovery",
"Arguments": {},
"Status": "FINISHED"
},
{
"Command": "Disable_USB_Ports",
"Arguments": {},
"Status": "FINISHED"
}
],
"Enabled": true
},
"Vpn": {
"Id": 124,
"PrivateKeyEncrypted": "***",
"PublicKeyEncrypted": "***",
"Port": 50122,
"Server": "<server>.hopit.at",
"Enabled": true
},
"DeviceTwin": {
"Targets": {
"hopit": false,
"IoT Hub": false
},
"Enabled": true
},
"SystemTime": {
"NtpServer": "time.windows.com",
"Timezone": "UTC",
"Enabled": true
},
"SystemMonitor": {
"Enabled": true
},
"InsightsCollector": {
"DeviceName": "Edge-97223",
"ScrapeInterval": 30,
"MaxDiskUsage": 6,
"Enabled": true
},
"DataLake": {
"MaxDiskUsage": 2,
"RetentionTime": "2y",
"Enabled": false
},
"Dashboard": {
"Enabled": false
},
"Alerting": {
"SmtpFromMail": "alerting@heap-engineering.at",
"SmtpUser": "alerting@heap-engineering.at",
"SmtpPassword": "***",
"SmtpHost": "mail.heap-engineering.at:587",
"Recipients": [],
"Enabled": false
},
"Router": {
"SignalRoutes": {
"CloudStream": {
"Enabled": true,
"Sources": [
{
"Enabled": true,
"TargetName": "PLC",
"Signals": [
"GVL.Line1"
],
"Conditions": {}
},
{
"Enabled": true,
"TargetName": "Separator",
"Signals": [
"DB3.0,b"
],
"Conditions": {}
},
{
"Enabled": false,
"TargetName": "Extruder",
"Signals": [
"ns=3;s=\"Tracking\".\"ACK\""
],
"Conditions": {}
}
],
"Destinations": [
{
"Enabled": true,
"TargetName": "IoT Hub",
"ScrapeInterval": 30,
"ScrapeTrigger": "OnChange",
"Constants": {
"Timestamp": "[[TIME]]"
},
"Properties": {},
"SignalMap": {
"DB3.0,b": "Separation_Interval",
"/^GVL[.].*[.]/": "PLC_"
}
},
{
"Enabled": true,
"TargetName": "DataLake-edge",
"ScrapeInterval": 30,
"ScrapeTrigger": "Periodic",
"Constants": {},
"Properties": {},
"SignalMap": {
"DB3.0,b": "seperation_interval",
"/^GVL[.]Line1/": "[[SKIP_WRITE]]"
}
}
]
}
},
"Enabled": true
},
"AdsRouter": {
"AmsNetId": "1.2.3.4.5.6",
"Enabled": false
},
"Log": {
"LogLevel": "Information"
},
"LogCollector": {
"Enabled": true
},
"Targets": {
"PLC": {
"Port": 851,
"AmsNetId": "127.0.0.1.1.1",
"Host": null,
"TargetType": "ADS",
"MessagesPerSecondLimit": 50.0,
"BufferSize": 1,
"Constants": {},
"Enabled": true
},
"Separator": {
"Port": 102,
"Host": "192.168.1.20",
"Rack": 0,
"Slot": 1,
"ConnectionType": "Pg",
"TargetType": "Siemens",
"MessagesPerSecondLimit": 1.0,
"BufferSize": 1,
"Constants": {},
"Enabled": true
},
"IoT Hub": {
"TransportType": "Mqtt",
"DeviceConnectionString": "HostName=hopit.azure-devices.net;DeviceId=heap-office-plc;SharedAccessSignature=SharedAccessSignature sr=hopit.azure-devices.net%2Fdevices%2Fheap-office-plc&sig=***",
"DeviceConnectionStringRotate": null,
"TargetType": "Azure",
"SanitizeReportedSecrets": true,
"Properties": {},
"MessagesPerSecondLimit": 2.0,
"BufferSize": 100,
"Constants": {},
"Enabled": true
},
"Extruder": {
"Host": "opc.tcp://192.168.1.20:4840/",
"Username": "User",
"Password": "***",
"MaxNodesPerRequest": 0,
"TargetType": "OpcUa",
"Name": "Extruder",
"MessagesPerSecondLimit": 10.0,
"BufferSize": 1,
"Constants": {},
"Enabled": true
}
}
}
The IoT Hub destination's Signal Map replaces the prefix from GVL. through the last dot with PLC_. For example, GVL.Line1.test becomes PLC_test.
The Device Twin above represents the following data streaming architecture:
The packages:
hopit.edgeplc-applicationmicrosoft-edge
are installed and the Provisioning Chain with the Tasks Discovery and Disable_USB_Ports was successfully executed.
For descriptions and detailed definitions please have a look at the corresponding documentation section.
Target or Route deletion​
To delete a Target or a Route via the Device Twin, it has to be set to null.
To delete a Target or a Route via the Azure IoT Hub Device Twin, it must be available in the desired section before setting it to null. Otherwise this null property will not be sent to the Edge device. This is the standard behaviour of the Azure IoT Hub.
Sources and destinations of a route​
The Sources and Destinations of a route are lists. The Edge applies each entry to the source or destination at the same position, and removes the ones beyond the end of the list. A field that an entry leaves out keeps the value of the source or destination that was at that position, and Conditions, Constants, Properties and SignalMap keep every key that the entry does not set to null. An empty value does not remove a key: in a SignalMap it skips the signal. A source or destination that the list adds is enabled unless its entry says "Enabled": false.
When you remove a source or destination that is not the last one, give the ones after it with all of their fields, Enabled included. Otherwise they take over fields of the one that was at their position before. The Device Twin that the Edge reports, and the export on the Routes page, always contain every field.
In the example above, the source Extruder is disabled: it is not read, and the route reads its other two sources. See Disabling Sources and Destinations. The destination DataLake-edge does not send the signals of GVL.Line1, which the destination IoT Hub sends: its Signal Map skips them, see Skipping Signals.