Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Report a reading only when it changes enough to matter. For example, with a change threshold of 1, readings of 30, 30.5, and 31 produce reports of 30 and 31.
Use this pattern when a consumer needs meaningful changes rather than every fluctuation. Small changes accumulate against the last reported value, rather than the last received value. All examples are synthetic.
Prerequisites
Start with an existing Azure IoT Operations deployment and MQTT broker access. Follow Create a data flow graph to configure a data flow profile and MQTT endpoint.
Complete the Azure Samples deployment instructions for registry setup and single-instance deployment. Then, use the standalone sample setup. The examples use namespace azure-iot-operations. Configure the clock publisher for heartbeats during sensor silence.
The default data flow profile must already exist with one instance. The examples reuse the existing factory-mqtt MQTT endpoint and factory-artifacts registry endpoint for ghcr.io; they don't create profiles or endpoints.
The Azure CLI installed on your development machine. Check Available Azure CLI extensions for the minimum required version to use the azure-iot-ops extension. Use
az --versionto check your version andaz upgradeto update if necessary. For more information, see Install the Azure CLI.The Azure IoT Operations extension for the Azure CLI. Use the following command to add the extension or update it to the latest version:
az extension add --upgrade --name azure-iot-ops
The Azure CLI examples in this article use environment variables so that you can set each value once and then copy and paste the commands as-is. If you're using the Azure IoT Operations Codespaces environment from the quickstart, these variables are already set for you and you can skip this step. Otherwise, set the following environment variables in your shell before you run the commands.
The following scripts set the most commonly used environment variables:
| Environment variable | Description |
|---|---|
SUBSCRIPTION_ID |
The ID of the subscription that contains your Azure IoT Operations instance. |
RESOURCE_GROUP |
The name of the resource group that contains your Azure IoT Operations instance. |
AIO_INSTANCE_NAME |
The name of your Azure IoT Operations instance. To list your instances, run az iot ops list -o table. |
CLUSTER_NAME |
The name of the Azure Arc-enabled Kubernetes cluster that hosts your instance. |
LOCATION |
The Azure region to use for new resources, for example eastus. |
SUBSCRIPTION_ID=<subscription-id>
RESOURCE_GROUP=<resource-group-name>
AIO_INSTANCE_NAME=<instance-name>
CLUSTER_NAME=<cluster-name>
LOCATION=<region>
You only need to set the variables that this article uses. This article might use additional environment variables for resource names that you choose. The article explains how to set them where they're introduced.
Deploy the complete graph
This sample uses a custom artifact from ghcr.io. The operations experience supports custom artifacts from Azure Container Registry (ACR), not GHCR. Use the Azure CLI or Bicep tab to deploy this example unchanged.
To use the operations experience, first copy the graph and its referenced WASM modules to ACR and configure a registry endpoint. Then follow Deploy custom WASM transforms for the supported UI workflow. The graph configuration in the Azure CLI tab shows the required topics and settings.
The manifest subscribes to factory/report-readings and factory/report-ticks, and publishes to factory/changes. Allow sensor publishers to publish readings, but allow only the authorized clock publisher to publish on factory/report-ticks.
The example reports the first reading, then changes of at least 1 from the last reported value. A clock event also reports the latest observation if at least 1 second has passed since the previous report. This periodic report is called a heartbeat.
Check value changes
For a fresh deployment, publish each line separately on factory/report-readings before the heartbeat interval elapses:
{"kind":"reading","assetId":"sensor-1","messageId":"r0","sequence":0,"value":30}
{"kind":"reading","assetId":"sensor-1","messageId":"r1","sequence":1,"value":30.5}
{"kind":"reading","assetId":"sensor-1","messageId":"r2","sequence":2,"value":31}
| Reading | Comparison | Result |
|---|---|---|
30 |
First accepted reading | Report with reason: "initial"; last reported value becomes 30 |
30.5 |
Difference from 30 is 0.5 |
Keep the observation for a later heartbeat; last reported value stays 30 |
31 |
Difference from 30 is exactly 1 |
Report with reason: "change"; last reported value becomes 31 |
Each report preserves the reading's fields, replaces kind with reported, and adds reason, emittedAtMs, receivedAtMs, observationAgeMs, and isFreshMeasurement. Reading-triggered reports have age 0 and isFreshMeasurement: true. In a live deployment, times are processing-clock epoch milliseconds.
Report the latest observation periodically
Example timeline for a fresh deployment. Publish readings on factory/report-readings; the clock publisher sends events on factory/report-ticks:
| Elapsed time | Input | Result |
|---|---|---|
0 s |
r0, sequence 0, value 10 |
Initial report; last reported value is 10 |
0.1 s |
r1, sequence 1, value 10.5 |
Latest observation becomes 10.5 |
1 s |
Clock event: {"kind":"tick","assetId":"sensor-1"} |
Heartbeat reports 10.5; last reported value becomes 10.5 |
1.1 s |
r2, sequence 2, value 11 |
Difference 0.5; retain observation |
1.2 s |
r3, sequence 3, value 11.5 |
Difference exactly 1; report change |
The heartbeat output at 1 second is:
{"kind":"reported","assetId":"sensor-1","messageId":"r1","sequence":1,"value":10.5,"reason":"heartbeat","emittedAtMs":1000,"receivedAtMs":100,"observationAgeMs":900,"isFreshMeasurement":false}
The processor computes the time fields automatically; the example shows milliseconds relative to the timeline's start. Live outputs use processing-clock epoch milliseconds. A heartbeat reports an older observation, not a new measurement. Its timing depends on clock-event delivery and processing. Use offline detection to track sensor availability.
Advanced: timing and restart
Heartbeats require clock events; readings alone don't trigger them. Monitor the clock publisher and its route if you depend on periodic reports.
Keep messageId unique for each asset, including across restarts, and keep sequence strictly increasing. Retry a message only with its original ID, sequence, and unchanged content. A restart clears the remembered values and retry tracking. The graph reports the next accepted reading as an initial value. If you replay saved readings, ensure consumers don't process replayed outputs twice.