IoT lab viva notes

Tavish Gupta, 23BCP064

Experiment 10: Raspberry Pi LED control with MQTT

Experiment 8 used MQTT hidden inside Arduino IoT Cloud. Here you use it directly: a phone publishes “on” or “off” to a topic on a public broker, and a Python program on the Pi, subscribed to that topic, switches the LED. The lab also asks you to explain why a public broker is unsafe, which is the part examiners like to probe.

Objective

Problem
Switch an LED on a Raspberry Pi from a phone, through an MQTT broker, so neither side needs the other’s address.
Interface
broker broker.hivemq.com, topic pdeu/led, payload on or off (plain text).
Behaviour
on → LED on, off → LED off, within a fraction of a second; MQTT Explorer shows the latest value.
Learn
publish/subscribe, brokers and topics, and the security weaknesses of open brokers (asked in the Observation section).

System

PhoneMQTT Dashboard, publisherbroker.hivemq.comtopic pdeu/ledRaspberry PiPython subscriberLEDGPIO pin + resistorLaptopMQTT Explorer, watcherPUBLISH on/offdeliverHIGH/LOWdeliver
Three clients, one broker. The phone, the Pi and the laptop each open their own outbound connection to the broker; they never talk to each other directly.

Design decisions

MQTT publish/subscribe instead of HTTP

Why
The Pi keeps one outbound connection open and the broker pushes messages to it, so it works behind NAT and reacts at once. Any number of clients can publish or listen on the same topic.
Alternative
An HTTP server on the Pi (Exp 6), which only works on the same LAN unless you open ports.
Switch when
HTTP for request/response APIs and web pages; MQTT for device commands and telemetry.

A public broker (HiveMQ)

Why
Free, no setup, reachable from anywhere: ideal for a lab demo.
Alternative
Your own broker (Mosquitto on the Pi or a server) with authentication, or a managed service (HiveMQ Cloud, AWS IoT Core).
Switch when
Anything beyond a demo: a public broker gives no privacy and no access control (see Security).

Plain-text payloads “on”/“off”

Why
Easy to type in any client and easy to parse.
Alternative
JSON such as {"led": true}, or 1/0.
Switch when
JSON once a topic carries several fields or needs versioning.

Python on the Pi

Why
A few lines with an MQTT client library and a GPIO library; easy to change over SSH.
Alternative
An ESP32 subscriber in C++ (PubSubClient).
Switch when
Use a microcontroller when the node is battery powered or must be very cheap.

A phone dashboard app as the publisher

Why
Buttons bound to topic + payload give a control panel with no coding.
Alternative
MQTT Explorer’s publish box, or mosquitto_pub in a terminal.
Switch when
Command line for scripts and testing.

What we did

  1. Connected the LED anode to a GPIO pin through a current-limiting resistor, cathode to GND.

    Same LED circuit as Exp 1; Pi GPIO is 3.3 V and should stay under about 16 mA per pin, so the resistor is required.

  2. Logged in over SSH and installed the Python GPIO and MQTT client libraries.

    The Pi is headless (Exp 9). The program needs a library to speak MQTT and one to drive the pins.

  3. Chose the public broker broker.hivemq.com and the topic pdeu/led.

    Every client must agree on broker and topic; the topic is the only “address” in the system.

  4. Wrote and ran a Python subscriber: set up the pin, connect, subscribe to pdeu/led, switch the LED on each message.

    It stays in a listening loop; the work happens in the message callback.

  5. Created two buttons in the MQTT Dashboard app, both on topic pdeu/led, with values on and off.

    Each press publishes one message.

  6. Connected MQTT Explorer on the laptop to the same broker.

    Shows every message on the topic, so you can tell whether a problem is on the publishing or the receiving side.

  7. Pressed on/off and watched the LED and MQTT Explorer.

    Checks the whole path end to end.

How MQTT works

ConceptMeaningIn this experiment
Brokerthe server that accepts connections and routes messagesbroker.hivemq.com
Clientanything that connects to the broker; can publish, subscribe or bothphone, Pi, MQTT Explorer
Topica /-separated string used to route messages; no need to create it firstpdeu/led
Wildcards+ matches one level, # matches everything belowMQTT Explorer shows the tree pdeu ▸ led
Payloadthe message bytes; the broker does not look insideon, off
QoS0: at most once; 1: at least once (acknowledged); 2: exactly once (4-step handshake)MQTT Explorer’s publish panel shows QoS 0
Retainthe broker keeps the last message on a topic and gives it to new subscriberswould let the Pi learn the current state after a restart
Keepalivethe client pings when idle so both sides detect a dead connection60 s is typical
Ports1883 plain TCP, 8883 TLS; WebSocket ports for browsers1883: unencrypted

One message’s path: the phone sends CONNECT and gets CONNACK, then PUBLISH (topic pdeu/led, payload on). The Pi had earlier sent SUBSCRIBE pdeu/led. The broker matches the topic against its subscriptions and forwards the PUBLISH to the Pi and to MQTT Explorer. The phone never learns who received it.

The subscriber program

  • Two callbacks: on_connect runs after the broker accepts the connection; on_message runs for every message on a subscribed topic.
  • Subscribe inside on_connect, so a reconnect after a Wi-Fi drop subscribes again automatically.
  • Normalise the payload (decode, strip, lower), because the payload is just bytes: “ON”, “on ” and “on” should behave the same.
  • loop_forever() handles the network, keepalive pings and reconnects, and calls the callbacks: the same job as ArduinoCloud.update() in Exp 8.
import paho.mqtt.client as mqtt
from gpiozero import LED

led = LED(17)                        # example pin: use the one you wired
BROKER, TOPIC = "broker.hivemq.com", "pdeu/led"

def on_connect(client, userdata, flags, reason_code, properties):
    client.subscribe(TOPIC)          # subscribe again after every reconnect

def on_message(client, userdata, msg):
    cmd = msg.payload.decode().strip().lower()
    if cmd == "on":
        led.on()
    elif cmd == "off":
        led.off()
    print(msg.topic, cmd)

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
client.on_connect = on_connect
client.on_message = on_message
client.connect(BROKER, 1883, 60)     # plain TCP, keepalive 60 s
client.loop_forever()

Written for paho-mqtt 2.x. With paho-mqtt 1.x (for example the Debian package), create the client with mqtt.Client() and use on_connect(client, userdata, flags, rc). gpiozero is preinstalled on Raspberry Pi OS and uses BCM pin numbers.

Reading the outputs

Complete setup: Pi with display, laptop running MQTT Explorer, phone running MQTT Dashboard.
Complete setup: Pi with display, laptop running MQTT Explorer, phone running MQTT Dashboard.
  • Right: the Raspberry Pi with its display, powered and booted.
  • Laptop: MQTT Explorer connected to the broker, with its Publish panel open.
  • Phone, held in front: the MQTT Dashboard app with the two buttons.
After pressing "on": MQTT Explorer shows pdeu ▸ led = on.
After pressing “on”: MQTT Explorer shows pdeu ▸ led = on.
  • The tree is broker ▸ topic levels: broker.hivemq.com ▸ pdeu ▸ led, with the last value on.
  • The phone shows two cards, both “Topic: pdeu/led”, with button values on and off. The green cloud icon means the app is connected to the broker.
  • The laptop received the phone’s message without knowing the phone exists: publish/subscribe working.
After pressing "off": led = off.
After pressing “off”: led = off.
  • Same topic, new value off: each publish replaces what the explorer shows as the latest value.
  • The Pi, subscribed to the same topic, received the same two messages and switched the LED.

Security of open-source and public MQTT brokers

This is the lab’s Observation question. Learn it as: what is missing, what an attacker does with that, and the fix.

WeaknessWhat an attacker can doFix
No encryption (port 1883)Sniff the network (ARP spoofing, rogue Wi-Fi) and read topics, payloads and any username/password in clear text.TLS on port 8883.
Anonymous accessConnect with no credentials and publish fake commands (switch your LED).allow_anonymous false, passwords, or client certificates (mutual TLS).
No access control listsSubscribe to # and read all traffic; publish to any device’s topic.Per-client ACLs: each device may only use its own topics.
Denial of serviceOpen thousands of connections or flood messages (QoS 1/2 costs the broker memory), so real clients are dropped.Connection and rate limits, message size limits, a firewall.
Tampering and replayChange a message in transit, or re-send a captured “on” later.TLS, plus a signed payload (HMAC) with a timestamp or counter.
Client ID clashesConnect with the same client ID as your device and the broker kicks your device off.Unique IDs plus authentication tied to the ID.

Limits and redesign

  • Security: everything in the table above. Redesign: Mosquitto on the Pi or a server with TLS, user accounts and ACLs, or a managed broker with per-device certificates.
  • State after restart: the subscriber only knows what it receives. Publish commands with retain, and have the Pi publish its actual state to pdeu/led/state so the dashboard shows the truth.
  • Delivery: QoS 0 can lose a message on a bad link. QoS 1 retries until acknowledged; make the command idempotent (“on” twice is still on), which it already is.
  • Liveness: set a Last Will message (for example pdeu/led/status = offline) so the dashboard knows when the Pi drops off.
  • Running unattended: start the script as a systemd service so it restarts on boot and after a crash.

Say this in the viva

Viva questions

Why

Why does the Pi not need a public IP address?

It connects out to the broker and keeps the connection open; messages come back over that connection. NAT allows outbound connections.

Why is MQTT called lightweight?

A 2-byte minimum header, one long-lived TCP connection instead of a new request each time, and small keepalive pings.

Why did MQTT Explorer also see the messages?

It is just another subscriber to the topic. The broker delivers each message to every matching subscription.

How is this different from Experiment 8?

Same protocol, but there Arduino IoT Cloud hid it and added TLS and per-device credentials. Here you choose the broker, topic and payload yourself, with no security by default.

What if

What if a classmate also uses topic pdeu/led?

Their messages switch your LED and yours switch theirs: on a public broker topics are shared by everyone.

What if the Pi is off when “on” is published?

With QoS 0 and no retain the message is gone. With retain, the broker gives the last value to the Pi when it subscribes again.

What if the payload is “ON” in capitals?

A strict comparison would ignore it; normalise the payload with lower().

Design

Secure this for a real product.

Own broker or managed service; TLS on 8883; a certificate or credentials per device; ACLs so each device uses only its own topics; retained state topic; Last Will for status; rate limits.

QoS 0, 1 or 2 for a door lock command?

At least QoS 1 so it is not lost, with an idempotent command and a state topic to confirm. QoS 2 costs an extra round trip and rarely adds much if commands are idempotent.