title | description | author | ms.author | ms.date | ms.topic | ms.service | services |
---|---|---|---|---|---|---|---|
IoT Plug and Play conventions | Microsoft Docs |
Description of the conventions IoT Plug and Play expects devices to use when they send telemetry and properties, and handle commands and property updates. |
rido-min |
rmpablos |
05/11/2022 |
conceptual |
iot-develop |
iot-develop |
IoT Plug and Play devices should follow a set of conventions when they exchange messages with an IoT hub. IoT Plug and Play devices use the MQTT protocol to communicate with IoT Hub, AMQP is supported by IoT Hub and available in some device SDKs.
Devices can include modules, or be implemented in an IoT Edge module hosted by the IoT Edge runtime.
You describe the telemetry, properties, and commands that an IoT Plug and Play device implements with a Digital Twins Definition Language v2 (DTDL) model. There are two types of model referred to in this article:
- No component - A model with no components. The model declares telemetry, properties, and commands as top-level properties in the contents section of the main interface. In the Azure IoT explorer tool, this model appears as a single default component.
- Multiple components - A model composed of two or more interfaces. A main interface, which appears as the default component, with telemetry, properties, and commands. One or more interfaces declared as components with additional telemetry, properties, and commands.
For more information, see IoT Plug and Play modeling guide.
To announce the model it implements, an IoT Plug and Play device or module includes the model ID in the MQTT connection packet by adding model-id
to the USERNAME
field.
To identify the model that a device or module implements, a service can get the model ID from:
- The device twin
modelId
field. - The digital twin
$metadata.$model
field. - A digital twin change notification.
- Telemetry sent from a no component device doesn't require any extra metadata. The system adds the
dt-dataschema
property. - Telemetry sent from a device using components must add the component name to the telemetry message.
- When using MQTT add the
$.sub
property with the component name to the telemetry topic, the system adds thedt-subject
property. - When using AMQP add the
dt-subject
property with the component name as a message annotation.
Note
Telemetry from components requires one message per component.
A device or module can send any valid JSON that follows the DTDL v2 rules.
DTDL:
{
"@context": "dtmi:dtdl:context;2",
"@id": "dtmi:example: Thermostat;1",
"@type": "Interface",
"contents": [
{
"@type": "Property",
"name": "temperature",
"schema": "double"
}
]
}
Sample reported property payload:
"reported" :
{
"temperature" : 21.3
}
The device or module must add the {"__t": "c"}
marker to indicate that the element refers to a component.
DTDL that references a component:
{
"@context": "dtmi:dtdl:context;2",
"@id": "dtmi:com:example:TemperatureController;1",
"@type": "Interface",
"displayName": "Temperature Controller",
"contents": [
{
"@type" : "Component",
"schema": "dtmi:com:example:Thermostat;1",
"name": "thermostat1"
}
]
}
DTDL that defines the component:
{
"@context": "dtmi:dtdl:context;2",
"@id": "dtmi:com:example:Thermostat;1",
"@type": "Interface",
"contents": [
{
"@type": "Property",
"name": "temperature",
"schema": "double"
}
]
}
Sample reported property payload:
"reported": {
"thermostat1": {
"__t": "c",
"temperature": 21.3
}
}
The device or module should confirm that it received the property by sending a reported property. The reported property should include:
value
- the actual value of the property (typically the received value, but the device may decide to report a different value).ac
- an acknowledgment code that uses an HTTP status code.av
- an acknowledgment version that refers to the$version
of the desired property. You can find this value in the desired property JSON payload.ad
- an optional acknowledgment description.
When reporting writable properties the device should compose the acknowledgment message, using the four fields described above, to indicate the actual device state, as described in this table
Status(ac) | Version(av) | Value(value) | Description(av) |
---|---|---|---|
200 | Desired version | Desired value | Desired property value accepted |
202 | Desired version | Value accepted by the device | Desired property value accepted, update in progress (should finish with 200) |
203 | 0 | Value set by the device | Property set from the device, not reflecting any desired |
400 | Desired version | Actual value used by the device | Desired property value not accepted |
500 | Desired version | Actual value used by the device | Exception when applying the property |
When a device starts up, it should request the device twin, and check for any writable property updates. If the version of a writable property increased while the device was offline, the device should send a reported property response to confirm that it received the update.
When a device starts up for the first time, it can send an initial value for a reported property if it doesn't receive an initial desired property from the hub. In this case, the device can send the default value with av
to 0
and ac
to 203
. For example:
"reported": {
"targetTemperature": {
"value": 20.0,
"ac": 203,
"av": 0,
"ad": "initialize"
}
}
A device can use the reported property to provide other information to the hub. For example, the device could respond with a series of in-progress messages such as:
"reported": {
"targetTemperature": {
"value": 35.0,
"ac": 202,
"av": 3,
"ad": "In-progress - reporting current temperature"
}
}
When the device reaches the target temperature it sends the following message:
"reported": {
"targetTemperature": {
"value": 20.0,
"ac": 200,
"av": 4,
"ad": "Reached target temperature"
}
}
A device could report an error such as:
"reported": {
"targetTemperature": {
"value": 120.0,
"ac": 500,
"av": 3,
"ad": "Target temperature out of range. Valid range is 10 to 99."
}
}
If a writable property is defined as an object, the service must send a complete object to the device. The device should acknowledge the update by sending sufficient information back to the service for the service to understand how the device has acted on the update. This response could include:
- The entire object.
- Just the fields that the device updated.
- A subset of the fields.
For large objects, consider minimizing the size of the object you include in the acknowledgment.
The following example shows a writable property defined as an Object
with four fields:
DTDL:
{
"@type": "Property",
"name": "samplingRange",
"schema": {
"@type": "Object",
"fields": [
{
"name": "startTime",
"schema": "dateTime"
},
{
"name": "lastTime",
"schema": "dateTime"
},
{
"name": "count",
"schema": "integer"
},
{
"name": "errorCount",
"schema": "integer"
}
]
},
"displayName": "Sampling range"
"writable": true
}
To update this writable property, send a complete object from the service that looks like the following:
{
"samplingRange": {
"startTime": "2021-08-17T12:53:00.000Z",
"lastTime": "2021-08-17T14:54:00.000Z",
"count": 100,
"errorCount": 5
}
}
The device responds with an acknowledgment that looks like the following:
{
"samplingRange": {
"ac": 200,
"av": 5,
"ad": "Weighing status updated",
"value": {
"startTime": "2021-08-17T12:53:00.000Z",
"lastTime": "2021-08-17T14:54:00.000Z",
"count": 100,
"errorCount": 5
}
}
}
When a device receives multiple desired properties in a single payload, it can send the reported property responses across multiple payloads or or combine the responses into a single payload.
A device or module can send any valid JSON that follows the DTDL v2 rules:
DTDL:
{
"@context": "dtmi:dtdl:context;2",
"@id": "dtmi:example: Thermostat;1",
"@type": "Interface",
"contents": [
{
"@type": "Property",
"name": "targetTemperature",
"schema": "double",
"writable": true
},
{
"@type": "Property",
"name": "targetHumidity",
"schema": "double",
"writable": true
}
]
}
Sample desired property payload:
"desired" :
{
"targetTemperature" : 21.3,
"targetHumidity" : 80,
"$version" : 3
}
Sample reported property first payload:
"reported": {
"targetTemperature": {
"value": 21.3,
"ac": 200,
"av": 3,
"ad": "complete"
}
}
Sample reported property second payload:
"reported": {
"targetHumidity": {
"value": 80,
"ac": 200,
"av": 3,
"ad": "complete"
}
}
Note
You could choose to combine these two reported property payloads into a single payload.
The device or module must add the {"__t": "c"}
marker to indicate that the element refers to a component.
The marker is sent only for updates to properties defined in a component. Updates to properties defined in the default component don't include the marker, see Sample no component writable property
When a device receives multiple reported properties in a single payload, it can send the reported property responses across multiple payloads or combine the responses into a single payload.
The device or module should confirm that it received the properties by sending reported properties:
DTDL that references a component:
{
"@context": "dtmi:dtdl:context;2",
"@id": "dtmi:com:example:TemperatureController;1",
"@type": "Interface",
"displayName": "Temperature Controller",
"contents": [
{
"@type" : "Component",
"schema": "dtmi:com:example:Thermostat;1",
"name": "thermostat1"
}
]
}
DTDL that defines the component:
{
"@context": "dtmi:dtdl:context;2",
"@id": "dtmi:com:example:Thermostat;1",
"@type": "Interface",
"contents": [
{
"@type": "Property",
"name": "targetTemperature",
"schema": "double",
"writable": true
}
]
}
Sample desired property payload:
"desired": {
"thermostat1": {
"__t": "c",
"targetTemperature": 21.3,
"targetHumidity": 80,
"$version" : 3
}
}
Sample reported property first payload:
"reported": {
"thermostat1": {
"__t": "c",
"targetTemperature": {
"value": 23,
"ac": 200,
"av": 3,
"ad": "complete"
}
}
}
Sample reported property second payload:
"reported": {
"thermostat1": {
"__t": "c",
"targetHumidity": {
"value": 80,
"ac": 200,
"av": 3,
"ad": "complete"
}
}
}
Note
You could choose to combine these two reported property payloads into a single payload.
No component interfaces use the command name without a prefix.
On a device or module, multiple component interfaces use command names with the following format: componentName*commandName
.
Now that you've learned about IoT Plug and Play conventions, here are some additional resources: