What an IoT platform does

An IoT platform is the software that connects a fleet of devices and lets people and other systems work with them as a whole: it accepts their connections, knows which devices exist and what they may do, keeps track of their state, routes their data to storage and applications, and helps update and manage them. It can be a cloud service, a product you install on your own servers, or a set of open-source parts you put together.

The building blocks of an IoT platform. Devices connect to the platform's connectivity layer, an MQTT broker or similar. Next to it are identity and the device registry, and twins or shadows holding desired and reported state. Messages go through a rules engine, which routes them to storage, alerts and applications. Fleet management sends updates and jobs back to the devices. Devices a fleet IoT platform Connectivity broker, TLS Device registry Twins, shadows Rules engine filter, route Fleet jobs, OTA updates, settings, commands Storage Alerts Applications
The usual parts. Cloud providers sell them as separate services; a self-hosted setup combines a broker, a database and your own code.

The data devices send about themselves and their measurements is telemetry, in the same sense as in observability. The other direction, from the platform to the device, carries settings, commands and firmware updates. Most of this page is about keeping those two directions in order for hundreds or millions of devices that are often offline.

Two running examples show the two ends of the scale. Lotte Kortenhoeve's home setup is a "platform" of one: Mosquitto and Home Assistant on a Raspberry Pi 5. The Kestrel Home pilot is a fleet of about 250 P1 dongles, ESP32-C3-based devices sold under Kestrel's name, that have been sending live power to Kestrel's own MQTT broker since September 2026, each with its own certificate (see Provisioning and Over-the-Air Updates).

The device registry

A device registry is the platform's list of every device it knows: a stable identifier per device, its credentials or certificate fingerprint, metadata such as model, firmware version, location or owner, and its status (active, disabled, retired). It answers the questions operations asks every day: which devices exist, which are on firmware 1.3, which have not connected for a week, which belong to this customer?

For the pilot, each dongle's registry entry links its factory serial to a pilot home number, such as h0017 for Lotte's home; the provisioning page shows a complete entry. Two design rules matter more than the exact fields:

  • The registry is the source of truth for access. A dongle that is not active in the registry cannot connect, and one that is retired stays blocked even if someone finds it in a drawer.
  • Keep personal data out of identifiers. The registry stores the home number; the link from home number to customer lives in Kestrel's customer systems, with their own access rules. Topics, logs and dashboards see only h0017.

Device twins and shadows

Devices are offline a lot: asleep to save power, on a bad connection, unplugged. A platform therefore cannot just send a setting and assume it arrived. The common answer is a JSON document per device, kept in the cloud, with two halves:

  • Desired: the state the platform or an operator wants, written by the back end.
  • Reported: the state the device says it is in, written by the device.

The difference between the two is what the device still has to do. When a device connects, it reads the document, applies what differs and reports back; when an application needs a device's state, it reads the document instead of waking the device. Microsoft's Azure IoT Hub calls this document a device twin: per device, it holds tags (metadata only the back end sees), desired properties and reported properties. Amazon's AWS IoT Core calls it a device shadow, with desired, reported and a computed delta section; a device can have one unnamed (classic) shadow and several named shadows. An AWS-style shadow document looks like this (illustrative):

{
  "state": {
    "desired": { "report_interval_s": 10, "led": "off" },
    "reported": { "report_interval_s": 60, "led": "off", "firmware": "1.4.0" },
    "delta": { "report_interval_s": 10 }
  },
  "version": 42
}

The pilot dongles send a 10-second average of the power by default. A dongle that has just been reset to its factory settings reports 60 seconds. The delta is the list of desired values the device has not reported yet:

import json

# Twin of a pilot dongle that was just reset to its factory settings.
desired = {"report_interval_s": 10, "led": "off"}
reported = {"report_interval_s": 60, "led": "off",
            "firmware": "1.4.0", "wifi_rssi_dbm": -67}


def delta(desired, reported):
    """Desired values the device has not reported yet."""
    return {key: value for key, value in desired.items()
            if reported.get(key) != value}


print("delta:", json.dumps(delta(desired, reported), sort_keys=True))

# The dongle applies the setting and reports its new state.
reported["report_interval_s"] = 10
print("delta:", json.dumps(delta(desired, reported), sort_keys=True))
delta: {"report_interval_s": 10}
delta: {}

Values the back end does not set, such as the firmware version and the Wi-Fi signal strength, appear only under reported, and never in the delta. An empty delta means the device is where the platform wants it. A delta that stays non-empty for days is worth an alert: the device is offline, or it receives the setting but cannot apply it.

Device twin versus digital twin

The names are confusingly close. A device twin or shadow is a small state document about one device's configuration and status. A digital twin is a model of a physical asset, process or network, kept in step with live data so that people can simulate and predict: a model of a transformer, a building or a whole grid. A digital twin may use the data in many device twins, but a JSON document with "led": "off" simulates nothing.

Messages to the device

A cloud-to-device message is a message the platform sends to one device, such as a command ("reboot", "send a status report now") or a notification. Platforms offer several ways to reach a device, and choosing the right one avoids most problems:

Mechanism Good for If the device is offline
Desired state in the twin or shadow Settings that should simply be true: report interval, LED, schedule Applied when the device next connects
Queued message to the device One-off actions that may wait a little Kept for a limited time, then expired
Direct call that expects an answer Interactive actions: "blink now" while a technician watches Fails at once; the caller must handle that
Job across many devices Firmware updates, certificate rotation, reboots in waves Picked up when each device connects; progress tracked per device

Prefer desired state over commands where you can. "The report interval should be 10 seconds" stays correct however late it arrives and however often it is applied; "lower your interval by 50 seconds" does damage if it is applied twice. Give every command an expiry, so a "reboot" queued during an outage does not reach a device three days later at the worst possible moment.

Rules and data pipelines

A rules engine is the part of a platform that evaluates every incoming message against a set of rules and acts on the matches: it filters, transforms and routes messages to storage, queues, functions or alerts, without code on the devices changing. In AWS IoT Core the rules are written in a SQL-like language over MQTT topics. A plausibility rule for the pilot could look like this (illustrative):

SELECT topic(3) AS home, watts, time
FROM 'kestrel/home/+/p1/power'
WHERE watts > 30000

The rule picks the home number out of the topic (the third level) and sends implausible values to a data-quality queue for a person to look at; a second rule sends every message to a time-series store. Azure IoT Hub does the same with message routing to other Azure services. In a self-hosted setup a rules engine can be a small service that subscribes to the broker, or the automations in Home Assistant.

From there on, IoT data is ordinary data engineering: a streaming pipeline into a store built for timestamps. What is special about device data (event time versus arrival time, late and missing values, retention and downsampling) is on IoT Data: Time Series and Streams.

The big cloud platforms

The two largest cloud providers both offer IoT services, built from the same parts but under different names. They change often, so the status below is dated.

AWS IoT Core is Amazon Web Services' managed service for connecting devices: an MQTT message broker with X.509 certificate authentication, a registry, device shadows and a rules engine. Around it sit AWS IoT Jobs for firmware updates, reboots and certificate rotation, fleet provisioning for giving devices their certificates at scale, and AWS IoT Greengrass v2, an open-source runtime for running cloud-managed software on edge devices.

Azure IoT Hub is Microsoft Azure's managed service for connecting devices: device-to-cloud and cloud-to-device messaging, device twins, direct methods and message routing, with the Device Provisioning Service (DPS) for enrolling devices. It speaks MQTT 3.1.1 on port 8883 and over WebSockets on port 443, but Microsoft's own documentation states that IoT Hub is not a full-featured MQTT broker: it does not support QoS 2, and retained messages are not kept. For a standards-based broker, Azure offers the MQTT broker in Event Grid (MQTT 3.1.1 and 5, also without QoS 2).

Service Status as of October 2026
AWS IoT Core, Jobs, Greengrass v2 Current
AWS IoT Greengrass V1 End of support on 7 October 2026; move to v2
AWS IoT Analytics Ended on 15 December 2025
AWS IoT Events Ended on 20 May 2026
AWS IoT FleetWise Closed to new customers since 30 April 2026
Azure IoT Hub, DPS, Event Grid MQTT broker Current
Azure IoT Central No new applications since 23 September 2026; existing ones run until 20 September 2029. Microsoft points to IoT Hub with DPS and Microsoft Fabric instead.
Google Cloud IoT Core Discontinued in August 2023

Cloud platform or self-hosted?

The alternative to a cloud IoT service is running the parts yourself: an MQTT broker such as Mosquitto, a time-series database, your own registry and rules, or an open-source IoT platform that bundles them. Lotte's home runs this way, and so does the pilot: Kestrel runs its own broker. ADR-0011, still proposed, records the pilot's design (MQTT over TLS with a certificate per dongle) and the options it weighed for a larger rollout:

Question Cloud IoT service Self-hosted
Getting started Quick: certificates, registry and rules are built in Quick for one broker; slow for registry, provisioning and jobs
Scale and availability The provider's problem, within service limits Yours: clustering, failover, upgrades, on-call
Cost Per message, connection time and state operation; grows with the fleet Servers and staff; mostly fixed
Lock-in Twins, rules and jobs are provider-specific Standard MQTT; you own the data model
Data location Choose an EU region and check the contract Wherever you run it
Service lifetime The provider may retire a service You decide, and you maintain it

Cost depends mostly on how many messages the fleet sends, so work that out before comparing prices. The pilot's numbers, for the payload size alone (MQTT headers and TLS add more on the wire):

DONGLES = 250
DAYS = 30
PAYLOAD = '{"time":"2026-10-07T19:30:15+02:00","watts":1234}'
size = len(PAYLOAD.encode("utf-8"))   # bytes; MQTT and TLS add overhead
print(f"payload: {size} bytes")

for interval_s in (1, 10, 60):
    per_day = DONGLES * 86_400 // interval_s
    per_month = per_day * DAYS
    megabytes = per_month * size / 1_000_000
    print(f"every {interval_s:2} s: {per_day:>10,} messages/day, "
          f"{per_month:>11,}/month, {megabytes:8,.1f} MB/month")
payload: 49 bytes
every  1 s: 21,600,000 messages/day, 648,000,000/month, 31,752.0 MB/month
every 10 s:  2,160,000 messages/day,  64,800,000/month,  3,175.2 MB/month
every 60 s:    360,000 messages/day,  10,800,000/month,    529.2 MB/month

At one message per dongle every 10 seconds the pilot sends 64.8 million messages a month; at every second it would be ten times as many. Put those numbers into each provider's pricing calculator, and do the same sum for a full rollout to all dynamic-tariff customers. For live power data the data residency question matters as much as the price: one-second or ten-second power use is personal data under the GDPR, wherever it is stored. The general trade-off is the one on vendor lock-in: the more of the provider's own features (twins, rules, jobs) you use, the more work it is to leave.