CoAP and MQTT, the two titans of IoT messaging, might seem like chalk and cheese, but they’re both fundamentally about efficiently getting small messages from constrained devices to where they need to go.
Let’s see CoAP in action. Imagine a smart thermostat (thermostat-1) reporting its temperature.
$ mosquitto_pub -h mqtt.eclipseprojects.io -t "thermostat/1/temperature" -m "22.5"
Now, let’s poke at that with Wireshark. You’d filter for coap or mqtt. If you see a CoAP POST request to /temp_report with a payload of 22.5, that’s CoAP doing its thing. If you filter for mqtt, you’d see a PUBLISH packet to the topic thermostat/1/temperature with the exact same payload. The magic is how they get there.
CoAP (Constrained Application Protocol) is built for the UDP world. It’s like a super-lightweight HTTP. It has request methods (GET, POST, PUT, DELETE), response codes (2.05 Content, 4.04 Not Found), and even options for things like content format. Because it’s UDP, it doesn’t have the built-in reliability of TCP. CoAP adds its own reliability layer with acknowledgment messages and retransmissions. You can configure these retransmission timeouts, which is crucial for flaky networks. The CON (Confirmable) message type is CoAP’s way of saying "I need an ACK," while NON (Non-confirmable) means "send it and forget it."
MQTT (Message Queuing Telemetry Transport) is TCP-based. It’s a publish-subscribe protocol. Devices don’t talk directly to each other; they talk to a central broker. A device publishes a message to a "topic" (like thermostat/1/temperature), and any other device subscribed to that topic receives the message. This decouples publishers from subscribers, which is fantastic for scalability. MQTT has Quality of Service (QoS) levels: QoS 0 (fire and forget), QoS 1 (at least once), and QoS 2 (exactly once). QoS 1 and 2 use acknowledgments, similar to CoAP’s confirmable messages, but built into the TCP stream.
The most surprising thing about both CoAP and MQTT is how much you can tune their network behavior without touching the application code. You can change CoAP’s retransmission timeouts or MQTT’s keep-alive intervals, and drastically alter how responsive your devices are on a poor connection.
Here’s a CoAP GET request for a resource on a device, and the subsequent 2.05 Content response. Notice the TKL (Token Length) and Code fields.
# Wireshark Packet Capture Snippet (CoAP GET)
Frame 1:
CoAP Protocol:
Version: 1
Type: Confirmable (0)
Token Length: 1 (0x1)
Code: GET (0x01)
Message ID: 12345
Options:
Option Delta: 1 (Content-Format)
Option Length: 1 (0x1)
Option Value: UTF-8 (42)
Token: 0xaa
Payload: <empty>
Frame 2:
CoAP Protocol:
Version: 1
Type: Acknowledgement (2)
Token Length: 1 (0x1)
Code: 2.05 Content (0x45)
Message ID: 12345 (matches request)
Options:
Option Delta: 1 (Content-Format)
Option Length: 2 (0x2)
Option Value: UTF-8 (42)
Token: 0xaa
Payload: "22.5"
And here’s a corresponding MQTT PUBLISH from a broker to a subscriber. You’d see this if you were subscribed to thermostat/1/temperature.
# Wireshark Packet Capture Snippet (MQTT PUBLISH)
Frame 1:
MQTT Protocol:
Header: PUBLISH (0x30)
Remaining Length: 23
Topic Name: thermostat/1/temperature
Packet Identifier: 56789 (if QoS > 0)
Payload: 22.5
The one thing most people don’t realize is how CoAP’s "reliability" is actually a form of rate-limiting. When a CoAP server receives a confirmable message, it must send an acknowledgment back. If that ACK is lost, the client will retransmit the original message. If the network is congested, these ACKs can get lost, leading to a cascade of retransmissions that can completely choke a link. You can see this in Wireshark by looking for duplicate CON packets from the client, followed by ACK packets from the server that might be delayed or arrive out of order relative to the retransmissions.
The next concept you’ll want to dive into is message queuing and persistence within MQTT brokers, and how CoAP’s observe mechanism allows for efficient state synchronization.