Your MQTT message appeared on screen. Did the device actually reply?

You click a button in a control panel and a message appears in the log. That looks encouraging, but which part of your prototype produced it? The computer might be showing its own outgoing message, a copy returned by the broker, or a response from the device. Those are three different checkpoints.

For a team connecting its first hardware prototype to an app, separating those checkpoints makes debugging much easier. Before adding a motor, a lighting scene or a remotely adjustable setting, make one harmless message travel to the board and back. Then make the reply unmistakable.

On our bench we did this with a W5500 Ethernet board running MicroPython and the MQTTX desktop client. The computer sends text, the board receives it, and the board publishes a reply on a different MQTT topic.

Follow the topic, not the color of the message

An MQTT broker routes messages between clients. A topic is the label used for that routing. In our test the board subscribed to a request topic ending in /sub and published replies to a topic ending in /pub. The computer did the opposite.

Message Sender Topic What to look for
Test request Computer request topic (.../sub) Your outgoing text
Copy of that request Broker to subscribed computer request topic (.../sub) The same text, without the board's reply prefix
Device reply Board, through broker reply topic (.../pub) Text beginning with recv:

The distinction matters because our computer subscribed to both topics. Seeing its own request come back on the request topic is expected MQTT 3.1.1 behavior; it does not require the board to be running. Read the topic and payload together, rather than treating every received bubble as device feedback.

When you add the request-topic subscription in MQTTX, keep the reply-topic subscription too; with only the new one you will miss the board's replies. Different display colors help you scan the log, but they do not change message routing.

Make the first exchange deliberately simple

The board ran MicroPython 1.23.0 with a W5500 Ethernet interface and a locally modified MQTT client library. It first tries DHCP, with a static network configuration as a fallback, then connects to the broker and registers a message callback: the function to run when a message arrives. If network connectivity itself is in doubt, check DHCP and DNS separately before debugging MQTT.

Our test connected to the public broker broker.emqx.io on port 1883, with QoS 0. Those settings were for a bench test, not a deployment configuration for real commands. For your own trial, use a broker and topic namespace you control, distinct client IDs, and harmless test text. A shared public topic is not reliable evidence of device identity.

In MQTTX we selected Plaintext for displaying and sending messages. That keeps the first experiment focused on routing rather than JSON parsing; the display selector does not change which topic a message reaches. We published a short fixed test string to the request topic with the reply subscription still active. The topic field in MQTTX's publishing area is independent of the subscription list, so check both before clicking Send.

The board's terminal first reported its network configuration (192.168.1.20 in one run), the broker connection and its subscription. Reaching that point shows that initialization and subscription completed. It does not yet show that the application processed your test text.

What the board does with the message

The callback decodes the topic and message as UTF-8, checks that the topic is the request topic, increments a receive counter, and publishes a text reply of the form recv: <message> | total receive count: <n> to the reply topic. Non-matching messages are ignored without incrementing the count.

There is no motor movement, relay operation or setting change in that callback. Its useful job is to show the return path from application code. The main loop waits until ten matching messages have been received, then disconnects and stops the heartbeat timer. That stopping condition is part of the exercise, so an eleventh request should not be treated as evidence of an unexplained network failure.

Our runs came from two versions of the program. An earlier run replied without the counter; a later recorded run showed counted replies, and its network configuration differed. In the earlier terminal log, the send and recv lines were the reliable guide to direction. A count since startup is also not a unique command identifier. Together these runs illustrate the message exchange; they do not measure long-term reliability, prove every retry path or demonstrate physical actuation.

Turn a reply into useful product feedback

There is another step after receipt: deciding what the interface tells the user. A Home Assistant user reported in December 2020 that lock commands worked while the displayed state did not update. They resolved their configuration issue the same day. It is a historical example of command and state mapping drifting apart, not evidence of a current defect.

For a prototype control panel, decide which feedback you actually have:

Feedback available An honest interface message
Only the computer sent the request "Request sent"
Device callback returned a matching receipt "Device received request"
Application validated and applied the setting "Setting applied"
A suitable sensor verified the physical result A specific observed result, such as "Position reached"

Our test only reaches the second row. A commanded position and a measured position need different evidence. For an operation that takes time, keep a visible pending state and define what happens if confirmation never arrives.

Home Assistant's MQTT switch documentation illustrates the interface choice: optimistic mode updates the displayed state after a command, whereas a configured state topic can provide confirmation. The device still needs to publish a meaningful state; a topic name alone cannot establish what happened physically.

A useful next test for your own prototype

Keep the harmless exchange, but send distinguishable requests one at a time and record the outgoing topic, the board's receive log and the reply. Then deliberately stop the board while leaving the computer connected. Your interface should no longer present the computer's own returned message as a device response. We have not run these checks on this setup yet.

Before allowing real actions, agree on how requests are matched to replies, how rejected or malformed input is reported, and how a timeout appears to the user. Our counter resets with the program, and the callback assumes UTF-8 text. The modified client also contains retry paths that can wait indefinitely, so a short successful run is not enough to establish a response deadline.