Duration : 45 minutes | Difficulty : Beginner
Learning Objectives :
Understand publish/subscribe messaging pattern
Connect to an MQTT broker
Send and receive messages programmatically
Observe message flow in real-time
Materials Needed :
Computer with Python 3.8+
Internet connection
Text editor or IDE
Prerequisites : Basic Python knowledge
Step 1: Environment Setup (10 min)
# Install the MQTT client library
pip install paho-mqtt
# Verify installation
python -c "import paho.mqtt.client as mqtt; print('MQTT library ready!')"
Step 2: Connect to Public Broker (10 min)
Create a file called mqtt_subscriber.py:
import paho.mqtt.client as mqtt
# Callback when connected
def on_connect(client, userdata, flags, rc):
print (f"Connected with result code { rc} " )
# Subscribe to a test topic
client.subscribe("iotclass/lab1/temperature" )
# Callback when message received
def on_message(client, userdata, msg):
print (f"Received: { msg. topic} -> { msg. payload. decode()} " )
# Create client and set callbacks
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
# Connect to public broker (test.mosquitto.org)
client.connect ("test.mosquitto.org" , 1883 , 60 )
# Start listening
print ("Waiting for messages... Press Ctrl+C to stop" )
client.loop_forever()
MQTT publish/subscribe messaging adds protocol overhead to every message. For a 20-byte sensor payload, the total packet size includes MQTT headers.
\[
\text{Total Size} = \text{MQTT Fixed Header (2 bytes)} + \text{Topic Length} + \text{Payload}
\]
Worked example : Topic "iotclass/lab1/temperature" = 27 bytes. Payload = 5 bytes ("25.3C"). MQTT QoS 0 overhead = 2-byte fixed header. Total packet size = 2 + 27 + 5 = 34 bytes . For 500 sensors publishing every 15 minutes: 500 × 96 msgs/day × 34 bytes = 1.632 MB/day . At $0.10/MB cellular data cost = $0.16/day or $4.90/month for the entire network.
Try it yourself:
Show code
viewof topic_name = Inputs. text ({label : "Topic name" , value : "iotclass/lab1/temperature" , width : 300 })
viewof payload_text = Inputs. text ({label : "Payload" , value : "25.3C" , width : 150 })
viewof num_sensors = Inputs. range ([1 , 1000 ], {value : 500 , step : 1 , label : "Number of sensors" })
viewof publish_interval = Inputs. range ([1 , 1440 ], {value : 15 , step : 1 , label : "Publish interval (minutes)" })
viewof data_cost = Inputs. range ([0.01 , 1.0 ], {value : 0.10 , step : 0.01 , label : "Data cost ($/MB)" })
Show code
mqtt_calc = {
const fixed_header = 2 ;
const topic_length = topic_name. length ;
const payload_length = payload_text. length ;
const total_packet = fixed_header + topic_length + payload_length;
const msgs_per_day = (24 * 60 ) / publish_interval;
const bytes_per_day = num_sensors * msgs_per_day * total_packet;
const mb_per_day = bytes_per_day / 1_000_000 ;
const cost_per_day = mb_per_day * data_cost;
const cost_per_month = cost_per_day * 30 ;
return {total_packet, msgs_per_day, mb_per_day, cost_per_day, cost_per_month};
}
Show code
html `<div style="background: var(--bs-light, #f8f9fa); padding: 1rem; border-radius: 8px; border-left: 4px solid #3498DB; margin-top: 0.5rem;">
<p><strong>Total packet size:</strong> ${ mqtt_calc. total_packet } bytes</p>
<p><strong>Messages per day per sensor:</strong> ${ mqtt_calc. msgs_per_day . toFixed (0 )} </p>
<p><strong>Data volume:</strong> ${ mqtt_calc. mb_per_day . toFixed (3 )} MB/day</p>
<p><strong>Network cost:</strong> $ ${ mqtt_calc. cost_per_day . toFixed (2 )} /day or $ ${ mqtt_calc. cost_per_month . toFixed (2 )} /month</p>
</div>`
Run the subscriber: python mqtt_subscriber.py
You should see: “Connected with result code 0”
If you see a different code, check your internet connection.
Step 3: Publish Messages (15 min)
Create a file called mqtt_publisher.py:
import paho.mqtt.client as mqtt
import time
import random
client = mqtt.Client()
client.connect ("test.mosquitto.org" , 1883 , 60 )
# Simulate temperature readings
for i in range (5 ):
temperature = round (20 + random.uniform(- 5 , 10 ), 1 )
message = f" { temperature} C"
client.publish("iotclass/lab1/temperature" , message)
print (f"Published: { message} " )
time.sleep(2 )
client.disconnect()
print ("Done!" )
With subscriber running in one terminal, run publisher in another:
You should see messages appear in the subscriber terminal.
Step 4: Experiment (10 min)
Try these modifications:
Change the topic : Use iotclass/lab1/yourname/temperature
Add QoS : Modify publish to use qos=1 for at-least-once delivery, then watch for duplicate-handling implications
JSON payload : Send {"temp": 25.5, "unit": "C"} instead of plain text
Lab Validation
You have completed this lab when you can: