MQTT & Wi-Fi: the bench becomes a network
420-302-VA · WEEK 9 · FALL 2026

Utility · keep open during the lab

Troubleshoot

Networked systems fail in layers, and the layers fail differently: a firewall drops silently, a missing listener refuses loudly, a topic typo delivers nothing while everything reports success. Climb in order, each layer has a one-command test, and read the failure's signature before changing anything.

Debug in layers

Four debugging layers in order: A Wi-Fi, is the node on the network; B Reach, can it get to port 1883 on the Pi; C Session, does the MQTT connect succeed; D Messages, do the right topics carry the right payloads to the right callbacks. A · Wi-Fi on the network? ifconfig()[0] B · Reach UFW + listener walls timeout vs refused C · Session connect() succeeds? client id, anonymous D · Messages topics, payloads, callbacks, loops A lower layer's failure makes every higher layer fail too: always confirm A before touching D.
The one-command test per layer: A, did connect_wifi print an address; B, does mosquitto_sub -h <pi-address> from another machine connect (and which way does it fail); C, does the client's connect() return without an exception; D, does mosquitto_sub -t "#" -v on the Pi show your message, with the exact topic you think.

Symptoms and fixes

LayerSymptomLikely causeFix
AThe bench/home SSID never appears to the node; phones see it fine5 GHz-only network: the ESP32's radio is 2.4 GHz onlyUse a 2.4 GHz network or enable the router's 2.4 GHz band; hotspots often have a "compatibility" toggle
Aconnect_wifi times out with the right SSIDWrong password; or the network needs a login page (captive portal), which the node cannot completeRe-enter credentials in config.py; use the bench network, not a portal network
BConnecting hangs, then ETIMEDOUT / "no route"Dropped silently: UFW on the Pi has no 1883 rule, or wrong BROKER_IP, or different networkssudo ufw allow 1883; re-check hostname -I; confirm both machines are on the same LAN
BECONNREFUSED, immediatelyReached the Pi; no listener accepted: bench.conf missing, mistyped, or Mosquitto not restarted/failedRe-check /etc/mosquitto/conf.d/bench.conf, restart, then systemctl status mosquitto and journalctl -u mosquitto -e
CConnects, then drops at once (older setups: connect error 5)Listener without allow_anonymous true: the broker demands credentials nobody sendsAdd the line to bench.conf, restart, re-check status
CTwo stations take turns dying every few secondsDuplicate client id: each connect kicks the other offStation-unique ids: node-s07, monitor-s07
CImportError: no module named 'umqtt'Firmware without the frozen moduleAfter Wi-Fi is up: import mip; mip.install("umqtt.simple"), or Thonny's package manager; see the node page
DNode publishes happily; subscriber prints nothing; both report connectedTopic mismatch: case, a typo, s7 vs s07, or a stray space; topics are exact stringsRun the firehose, mosquitto_sub -t "#" -v, and compare the printed topic to the subscription, character by character
DPython subscriber never fires callbacks, script just endsNo network loop runningEnd with loop_forever() (or run a background loop); callbacks only fire inside the loop
DMonitor goes permanently silent after a broker restart, yet shows connectedSubscribed outside on_connect: the auto-reconnect carried no subscriptionsMove subscribe() inside on_connect, the reconnect-safe home
DPrints b'42.3', or float() raises on a healthy-looking payloadPayload is bytes, not strmsg.payload.decode() before parsing; the node's .encode() is the other half of the contract
DEvery new subscriber instantly receives a stale or test valueGhost retained message left on the topicClear it with an empty retained publish: mosquitto_pub -t "bench/s07/light" -r -n
DPasted tutorial code raises TypeError about callback arguments, or paho warns about a deprecated APIOnline examples mostly predate paho-mqtt 2.x; the callback signatures changedConstruct with mqtt.Client(mqtt.CallbackAPIVersion.VERSION2) and use this hub's signatures
DNode ignores commands, telemetry fineNo check_msg() in the loop, or subscribed after messages were sent with nothing retainedAdd client.check_msg() each cycle; re-send the command after the subscriber is up

Before you ask for help

  1. Name your layer: which single-command test is the first one that fails?
  2. Name the signature: timeout or refused? Silent or an exception? Copy the exact message.
  3. Show the firehose: mosquitto_sub -t "#" -v output while your publisher runs settles most "it sends but nothing arrives" disputes in one line.
  4. Say what you changed last, config files and restarts included; systemctl status mosquitto output in hand.