Step 4: Build Python Device Simulator
Now let’s build the device simulator. It will publish telemetry every 2 seconds with realistic sensor values.
Install Dependencies
First, install the MQTT client library:
pip install paho-mqtt
Or add to requirements.txt:
paho-mqtt==1.6.1
Complete Simulator Code
Here’s the complete simulator:
#!/usr/bin/env python3
"""
MQTT IoT Device Simulator
Publishes telemetry data to MQTT broker every 2 seconds.
"""
import json
import time
import random
from datetime import datetime, timezone
import paho.mqtt.client as mqtt
# Configuration
BROKER_HOST = "localhost"
BROKER_PORT = 1883
DEVICE_ID = "sensor-01"
TOPIC = f"iot/devices/{DEVICE_ID}/telemetry"
PUBLISH_INTERVAL = 2 # seconds
# Simulated sensor state
temperature = 20.0
humidity = 50.0
battery = 100.0
sequence = 0
def create_telemetry_message():
"""Create a telemetry message following our schema."""
global temperature, humidity, battery, sequence
# Simulate realistic sensor drift
temperature += random.uniform(-0.5, 0.5)
temperature = max(15.0, min(30.0, temperature)) # Clamp to reasonable range
humidity += random.uniform(-2.0, 2.0)
humidity = max(30.0, min(80.0, humidity))
# Battery slowly drains
battery -= random.uniform(0.01, 0.05)
battery = max(0.0, min(100.0, battery))
sequence += 1
return {
"deviceId": DEVICE_ID,
"ts": datetime.now(timezone.utc).isoformat(),
"metrics": {
"temperatureC": round(temperature, 1),
"humidityPct": round(humidity, 1),
"batteryPct": round(battery, 1)
},
"seq": sequence
}
def on_connect(client, userdata, flags, rc):
"""Callback when connected to broker."""
if rc == 0:
print(f"✅ Connected to MQTT broker at {BROKER_HOST}:{BROKER_PORT}")
else:
print(f"❌ Connection failed with code {rc}")
def on_publish(client, userdata, mid):
"""Callback when message is published."""
print(f"📤 Published message {mid}")
def on_disconnect(client, userdata, rc):
"""Callback when disconnected."""
if rc != 0:
print(f"⚠️ Unexpected disconnection (rc={rc})")
else:
print("✅ Disconnected cleanly")
def main():
"""Main loop."""
# Create MQTT client
client = mqtt.Client(client_id=DEVICE_ID)
client.on_connect = on_connect
client.on_publish = on_publish
client.on_disconnect = on_disconnect
try:
# Connect to broker
print(f"🔌 Connecting to {BROKER_HOST}:{BROKER_PORT}...")
client.connect(BROKER_HOST, BROKER_PORT, keepalive=60)
client.loop_start()
# Wait for connection
time.sleep(1)
# Publish loop
print(f"📡 Publishing to topic: {TOPIC}")
print("Press Ctrl+C to stop
")
while True:
message = create_telemetry_message()
payload = json.dumps(message)
# Publish with QoS 1 (at least once delivery)
result = client.publish(TOPIC, payload, qos=1)
if result.rc == mqtt.MQTT_ERR_SUCCESS:
print(f"📊 [{message['seq']}] "
f"Temp: {message['metrics']['temperatureC']}°C, "
f"Humidity: {message['metrics']['humidityPct']}%, "
f"Battery: {message['metrics']['batteryPct']}%")
else:
print(f"❌ Failed to publish: {result.rc}")
time.sleep(PUBLISH_INTERVAL)
except KeyboardInterrupt:
print("
🛑 Stopping simulator...")
except Exception as e:
print(f"❌ Error: {e}")
finally:
client.loop_stop()
client.disconnect()
print("👋 Simulator stopped")
if __name__ == "__main__":
main()
Understanding the Code
1. MQTT Client Setup
client = mqtt.Client(client_id=DEVICE_ID)
Creates a client with a unique ID. The broker uses this to track the device.
2. Connection Callbacks
on_connect: Called when connected (or fails)on_publish: Called when message is publishedon_disconnect: Called when disconnected
3. Sensor Simulation The simulator generates realistic values:
- Temperature drifts slowly (15-30°C)
- Humidity varies (30-80%)
- Battery drains gradually (0-100%)
4. Message Publishing
client.publish(TOPIC, payload, qos=1)
qos=1: At least once delivery (guaranteed, may duplicate)- Topic follows our pattern:
iot/devices/{deviceId}/telemetry
Run the Simulator
Save the code as simulator.py and run it:
python simulator.py
You should see:
🔌 Connecting to localhost:1883...
✅ Connected to MQTT broker at localhost:1883
📡 Publishing to topic: iot/devices/sensor-01/telemetry
Press Ctrl+C to stop
📊 [1] Temp: 20.3°C, Humidity: 50.2%, Battery: 99.8%
📊 [2] Temp: 20.1°C, Humidity: 51.5%, Battery: 99.6%
📊 [3] Temp: 19.8°C, Humidity: 49.8%, Battery: 99.4%
...
Verify Messages are Published
In another terminal, subscribe to see the messages:
mosquitto_sub -h localhost -p 1883 -t iot/devices/+/telemetry
You should see JSON messages appearing every 2 seconds:
{"deviceId":"sensor-01","ts":"2025-12-16T10:30:45.123Z","metrics":{"temperatureC":20.3,"humidityPct":50.2,"batteryPct":99.8},"seq":1}
{"deviceId":"sensor-01","ts":"2025-12-16T10:30:47.456Z","metrics":{"temperatureC":20.1,"humidityPct":51.5,"batteryPct":99.6},"seq":2}
Test the Simulator
Try this interactive example:
Multiple Devices
To simulate multiple devices, run multiple instances with different DEVICE_ID:
# Terminal 1
DEVICE_ID=sensor-01 python simulator.py
# Terminal 2
DEVICE_ID=sensor-02 python simulator.py
# Terminal 3
DEVICE_ID=sensor-03 python simulator.py
Subscribe to all devices:
mosquitto_sub -h localhost -p 1883 -t iot/devices/+/telemetry
Checkpoint ✅
You should have:
- ✅ Simulator running and publishing messages
- ✅ Messages visible in
mosquitto_sub - ✅ Messages following the schema
- ✅ Realistic sensor values changing over time
If something’s wrong:
- Check broker is running:
docker-compose ps - Check connection: Look for “Connected to MQTT broker” message
- Check topic: Verify topic matches subscription
What’s Next?
In the next page, you’ll set up Node-RED to subscribe to these messages, validate the data, and create a dashboard. This is where the “digital twin” comes to life!