Pensum · Protokol · IoT

Forstå MQTT som en IoT-protokol

Message Queuing Telemetry Transport er designet til ustabile netværk, begrænset båndbredde og tusindvis af små enheder. Her får du teorien bag pub/sub — og en live Opla-demo på samme side.

~2 B
Min. header
1883
TCP-port
3
QoS-niveauer
Pub/Sub
Mønster

01 Hvad er MQTT?

MQTT er en letvægts publish/subscribe-protokol, der kører oven på TCP/IP. Den blev skabt i 1999 af IBM og Arcom (Andy Stanford-Clark & Arlen Nipper) til at sende telemetri fra oliepipelines over satellit — altså netværk med høj latency, pakketab og dyr båndbredde.

I dag er MQTT de facto-standarden for mange IoT-systemer: smarte bygninger, industri (IIoT), wearables, home automation og cloud-platforme som AWS IoT, Azure IoT Hub og HiveMQ. OASIS har standardiseret protokollen; MQTT 3.1.1 og MQTT 5.0 er de mest udbredte versioner i undervisning og produktion.

PUB

Publish

En klient sender en besked med topic + payload til brokeren. Den kender ikke sine modtagere.

SUB

Subscribe

En klient beder brokeren om at levere fremtidige beskeder, der matcher ét eller flere topics.

BRK

Broker

Central router, der holder forbindelser, matcher topics og leverer beskeder til de rigtige klienter.

Kerneidé: Afsender og modtager er løst koblede. Arduino Opla behøver ikke vide, at pensum-siden, C#-dashboardet eller andre klienter lytter — den publicerer bare til et topic.

02 Hvorfor MQTT er god til IoT

IoT-enheder har typisk lidt RAM/flash, kører på batteri eller ustabilt WiFi, og skal kunne håndtere tusindvis af samtidige forbindelser. MQTT er bygget til præcis det — i modsætning til klassisk HTTP.

HTTP Request/Response

  • Klienten spørger gentagne gange (polling)
  • Stor header-overhead pr. request
  • Kortvarige forbindelser = mere TCP-handshake
  • Server svarer kun når den bliver spurgt
  • God til web-API’er, tung til små sensorer

MQTT Event-drevet

  • Push når noget sker — ingen unødvendig polling
  • Minimal fixed header (ofte 2 bytes)
  • Langvarig TCP-session + keep-alive
  • Broker pusher til alle subscribers med det samme
  • Designet til telemetri og kommandoer

Konkrete IoT-fordele

01

Lav overhead

En temperatur kan sendes som få bytes payload. Mindre radio = længere batteritid og billigere data.

02

Many-to-many

Én Opla kan forsyne web, C# og logging samtidigt — bare flere subscribers på samme topic.

03

Ustabilt net

QoS, clean/persistent session og keep-alive hjælper, når WiFi falder ud midlertidigt.

04

Bidirektionelt

Samme forbindelsen brugt til telemetri og kommandoer (fx RGB-LED fra websiden).

05

Skalérbarhed

Brokere er bygget til mange samtidige klienter. Topics organisere data uden hårde API-kontrakter.

06

Status-awareness

Last Will fortæller resten af systemet, når en enhed mister forbindelsen uventet.

Hvornår MQTT ikke er det bedste valg

03 Sådan virker protokollen

MQTT er en binær protokol. Hver “pakke” (control packet) har en lille header, valgfri variable header og payload. Klient og broker taler ved hjælp af et fast sæt pakettyper.

Vigtigste pakettyper

Pakke Retning Formål
CONNECT Klient → Broker Opret session: Client ID, keep-alive, username/password, Last Will, Clean Session
CONNACK Broker → Klient Bekræft (eller afvis) forbindelsen med return code
PUBLISH Begge veje Selve beskeden: topic, payload, QoS, retain-flag
SUBSCRIBE / SUBACK Klient ↔ Broker Tilmeld topics + bekræft maksimalt QoS
UNSUBSCRIBE Klient → Broker Afmeld topics
PINGREQ / PINGRESP Begge veje Keep-alive: “er du der endnu?”
DISCONNECT Klient → Broker Pæn afkobling (uden at trigge Last Will)
PUBACK / PUBREC / PUBREL / PUBCOMP Begge veje QoS 1 og QoS 2 bekræftelser

Forbindelses-livscyklus

1. TCP-forbindelse

Klient åbner TCP til broker (typisk port 1883 eller 8883 for TLS).

2. CONNECT + CONNACK

Klient sender Client ID. Broker accepterer (rc=0) eller afviser (fx forkert auth).

3. SUBSCRIBE / PUBLISH

Klienten tilmelder topics og/eller publicerer data. Broker router beskeder.

4. Keep-alive

Hvis der er stille for længe, sendes PINGREQ. Svarer brokeren ikke, anses forbindelsen for død.

5. DISCONNECT eller brud

Pæn lukning med DISCONNECT — eller abrupt brud, som kan udløse Last Will.

Ports & transport

Denne pensumservers browser-demo bruger WebSocket på port 8888. Arduino Opla bruger klassisk TCP på 1883.

04 Pub/Sub-arkitektur

I modsætning til HTTP’s request/response er MQTT event-drevet. Enheder publicerer, når noget sker — de poller ikke en server.

Publisher
Arduino Opla
PUBLISH
demo/opla/temp
Broker
:1883
DELIVER
demo/opla/temp
Subscriber
Web / C#

Tre roller i jeres projekt

Publisher — Opla

Læser sensorer og sender temp, fugt, lys. Subscribe også på demo/opla/cmd/# for LED-kommandoer.

Broker — denne server

Aedes-broker i Docker. Router beskeder mellem alle klienter på TCP og WebSocket.

Subscriber — pensumsiden

Browser-MQTT via WebSocket. Viser live data og publicerer RGB-kommandoer tilbage.

Decoupling i praksis: I kan starte/stoppe web-demoen uden at Opla “går i panik”. Når siden forbinder igen, får den nye PUBLISH’er — og med retain kan den endda få seneste status med det samme.

05 Topics & wildcards

Topics er UTF-8-strenge, der fungerer som “adresser” for beskeder. Hierarkiet adskilles med /. Brokeren matcher dem — den fortolker ikke payload’en.

demo/
opla/status
opla/temp
opla/humidity
opla/light
opla/button
opla/cmd/led/0 … /4
opla/cmd/buzzer
skole/
rum/101/temp
rum/101/humidity
rum/102/temp

Naming conventions (god praksis)

Wildcards (kun ved subscribe)

Wildcard Betydning Eksempel
+ Præcis ét niveau demo/opla/+ matcher demo/opla/temp, men ikke demo/opla/cmd/led/0
# Nul eller flere niveauer (kun sidst) demo/opla/# matcher alt under Opla — også kommandoer og dybe topics

I live-demoen subscriber I automatisk til demo/opla/#, så både sensorer og status dukker op.

06 Quality of Service i dybden

QoS er en kontrakt mellem afsender og broker — og mellem broker og subscriber. De to kan have forskellige niveauer; den lavere “vinder” typisk i den samlede leveringsgaranti.

QoS 0 At most once — “Fire and forget”. Ingen bekræftelsespakke. Hurtigst og billigst i radio. Besked kan gå tabt ved pakketab.
QoS 1 At least once — PUBLISH → PUBACK. Brokeren bekræfter. Beskeden kommer frem mindst én gang, men kan duplikeres (DUP-flag).
QoS 2 Exactly once — 4-vejs handshake: PUBLISH → PUBREC → PUBREL → PUBCOMP. Ingen tab og ingen dobbeltbehandling. Dyrst i traffic og kompleksitet.

QoS 2-flow (forenklet)

PUBLISHAfsender sender besked med Packet ID
PUBRECModtager: “modtaget — gemt midlertidigt”
PUBRELAfsender: “du må frigive / committe den”
PUBCOMPModtager: “færdig — præcis én levering”

Hvornår bruger man hvad?

Tommelfingerregel til Opla-projektet: sensorer på QoS 0, LED-kommandoer på mindst QoS 0/1, Last Will/status gerne med retain.

07 Retain, Last Will & session

Retained messages

Sætter du retain-flaget på en PUBLISH, gemmer brokeren den seneste besked for det topic. Nye subscribers får den med det samme — uden at vente på næste publish. Perfekt til status = online/offline eller “nuværende setpunkt”.

Last Will and Testament (LWT)

Ved CONNECT kan klienten registrere en “testament”-besked. Hvis forbindelsen dør uden DISCONNECT (WiFi drop, strømafbrydelse, crash), publicerer brokeren testamentet. I jeres Opla-sketch sættes LWT til demo/opla/status = offline.

// Konceptuelt (PubSubClient):
mqtt.connect(clientId, NULL, NULL,
             "demo/opla/status",  // will topic
             0,                  // will QoS
             true,               // will retain
             "offline");         // will payload

Clean Session / Persistent Session

Demo-browseren bruger clean session (simpelt). Batteridrevne noder, der kun vågner lejlighedsvis, bruger ofte persistent session.

Keep-alive

Et sekund-interval aftalt i CONNECT. Er der ingen trafik inden for ca. 1,5 × keep-alive, forventes PINGREQ/PINGRESP — ellers antages forbindelsen tabt.

Sikkerhed (kort)

08 Live MQTT Demo

Forbind til brokeren og oplev MQTT’s styrker live: fan-out, wildcards, retain, QoS, Last Will — plus Opla-sensorer og LED-styring.

MQTT Lab

Hver blok nedenfor er sin egen live-demo mod den rigtige broker. Tryk Forbind øverst, læs forklaringen, og prøv knapperne — det er samme protokol som Arduino Opla bruger.

01

Decoupling · Many-to-many

Én publish → mange subscribers

I klassisk HTTP kalder afsender ofte hver modtager direkte. I MQTT sender du kun til et topic. Brokeren finder alle, der har subscribed — dashboard, mobil, logger osv.

Derfor behøver Arduino Opla ikke kende pensumsiden eller dit C#-program. Den publicerer; hvem der lytter, er brokerens sag.

  1. Sørg for at du er forbundet
  2. Tryk Publish “alarm”
  3. Se de tre “apps” lyse op — samme besked, tre filtre

Hvorfor det er fedt til IoT: du kan tilføje nye forbrugere uden at ændre firmware på sensoren.

demo/lab/alarm
⇣ broker fan-out
Dashboard
demo/lab/#
Mobil-app
demo/lab/alarm
Logger
demo/+/alarm

Afventer publish…

02

Topics · Filtrering

Wildcards live

Topics er hierarkiske stier. Ved subscribe kan du bruge wildcards:

  • + — præcis ét niveau (fx ethvert rum, men kun temp)
  • # — alt under det punkt (hele undertræet)
  • Eksakt topic — kun den ene kanal

Det er sådan en C#-logger kan lytte på skole/+/temp uden at kende hvert enkelt rum-ID på forhånd.

  1. Vælg et filter (+, # eller eksakt)
  2. Publish til forskellige under-topics
  3. Inboxen viser hits — og gule linjer når noget ikke matcher

Prøv: vælg demo/lab/+/temp og publish a/humidity — den skal ikke matche.

Ingen hits endnu — vælg filter og publish
03

State · Late join

Retained messages

Med retain-flaget gemmer brokeren den seneste besked for et topic. Når en ny klient subscriber, får den straks den gemte værdi — uden at vente på næste publish.

Perfekt til ting som “nuværende setpoint”, “lampens tilstand” eller Opla’s demo/opla/status (online/offline).

  1. Skriv en payload og tryk Gem retained
  2. Tryk Simuler sen subscriber — en helt ny MQTT-klient oprettes
  3. Den får værdien med det samme ved subscribe
  4. Ryd retained sender en tom retained besked (MQTT-måden at slette på)

Hvorfor det er fedt til IoT: et dashboard der lige er åbnet, ved med det samme hvad verdens tilstand er.

Broker gemmer topic: demo/lab/retained
Modtaget ved connect tryk knappen
04

Leveringsgaranti

Quality of Service — 0, 1 og 2

QoS er aftalen mellem klient og broker om, hvor sikker levering skal være. Flere bekræftelsespakker = mere trafik, men stærkere garanti.

  • QoS 0 At most once — hurtigst, kan gå tabt
  • QoS 1 At least once — PUBACK, kan komme dobbelt
  • QoS 2 Exactly once — fuld handshake, langsomst
  1. Send samme type besked med hvert QoS-niveau
  2. Sammenlign tiden: QoS 0 er “fire and forget”, 1 og 2 venter på ack
  3. Tænk: hvad passer til temp hvert 5. sek vs. en kritisk kommando?

Tommelfingerregel: sensortelemetri = 0, events = 1, kritiske kommandoer = 2.

0
1
2
05

Presence · Fejlhåndtering

Last Will & Testament

Ved CONNECT kan en klient registrere et testament: topic + payload. Hvis forbindelsen dør uden pæn DISCONNECT (WiFi drop, crash, strømafbrydelse), publicerer brokeren det automatisk.

Opla gør det allerede: Last Will sætter demo/opla/status til offline. Dashboardet behøver ikke gætte — brokeren fortæller det.

  1. Start klient m. LWT — registrerer will = offline
  2. Kill forbindelse — abrupt close → testament udgives
  3. Prøv igen med Pæn DISCONNECT — så udføres testamentet ikke

Læg mærke til: forskellen mellem “kill” og “pæn disconnect” er hele pointeren med LWT.

Ingen LWT-klient

Klar — start en LWT-klient på demo/lab/will (retained).

06

Telemetri + kommandoer

Bidirektionelt på samme forbindelse

MQTT er ikke kun sensor → cloud. Samme åbne socket kan både modtage telemetri og sende kommandoer (LED-farve, buzzer). Ingen ekstra HTTP-request per kommando.

Det er derfor Opla både publicerer temp/fugt/lys og subscriber på demo/opla/cmd/# — web → broker → enhed, med det samme.

  1. Tjek Opla-status nedenfor (kommer fra retained LWT/status)
  2. Åbn Arduino Opla-fanen
  3. Send en RGB-farve eller bip — se resultatet på hardwaren

Én session: keep-alive, QoS og auth gælder begge veje.

Opla ukendt
↑ sensors · ↓ cmd/led · ↓ cmd/buzzer

Broker: TCP mqtt://138.199.155.78:1883 · WebSocket ws://138.199.155.78:8888 (lokal: localhost)

09 Arduino Opla i praksis

MKR WiFi 1010 + IoT Carrier bruger WiFiNINA, PubSubClient og Arduino_MKRIoTCarrier. Credentials ligger i gitignored secrets.h.

#include <WiFiNINA.h>
#include <PubSubClient.h>
#include <Arduino_MKRIoTCarrier.h>
#include "secrets.h"

WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);
MKRIoTCarrier carrier;

void setup() {
  carrier.begin();
  WiFi.begin(SECRET_SSID, SECRET_PASS);
  mqtt.setServer(MQTT_SERVER, MQTT_PORT);
  // LWT: status → offline hvis WiFi dør
  mqtt.connect(MQTT_CLIENT_ID, NULL, NULL,
               "demo/opla/status", 0, true, "offline");
  mqtt.publish("demo/opla/status", "online", true);
  mqtt.subscribe("demo/opla/cmd/#");
}

void loop() {
  mqtt.loop();
  // Publisér sensorer med jævne mellemrum…
}

10 C# med MQTTnet

Med MQTTnet kan I subscribe på samme topics som web-demoen og bygge et desktop-/server-dashboard.

// dotnet add package MQTTnet

using MQTTnet;
using MQTTnet.Client;

var factory = new MqttFactory();
var client = factory.CreateMqttClient();

var options = new MqttClientOptionsBuilder()
    .WithTcpServer("138.199.155.78", 1883)
    .WithClientId("csharp-dashboard")
    .Build();

client.ApplicationMessageReceivedAsync += e => {
    var topic   = e.ApplicationMessage.Topic;
    var payload = e.ApplicationMessage.ConvertPayloadToString();
    Console.WriteLine($"{topic}: {payload}");
    return Task.CompletedTask;
};

await client.ConnectAsync(options);
await client.SubscribeAsync("demo/opla/#");
Console.WriteLine("Lytter…");
Console.ReadLine();

11 Øvelser

  1. Forklar forskellen på HTTP-polling og MQTT-push med et eksempel fra Opla-projektet.
  2. Tegn CONNECT → SUBSCRIBE → PUBLISH → DISCONNECT for jeres setup (Opla + web).
  3. Subscribe til demo/opla/+ vs demo/opla/# — hvad mangler i den første?
  4. Send LED-farve 0,0,255 til LED 0 og verificér både web-log og Opla-skærm.
  5. Sluk Opla abrupt (USB ud). Bekræft at retained LWT sætter status til offline.
  6. Argumentér for QoS 0 vs 1 for temperatur og for LED-kommandoer.
  7. Udvid C#-klienten til også at publicere en LED-kommando.