6.Develop a dashboard that receives sensor data through the MQTT protocol, stores the data in a cloud database (Supabase / Firebase), retrieves the stored records using a web application, and displays the sensor readings on a web dashboard.

pip install flask paho-mqtt supabase python-dotenv


iot_mqtt_dashboard/
│
├── app.py
├── publisher.py
├── .env
├── requirements.txt
│
└── templates/
    └── index.html


supabase:

CREATE TABLE mqtt_readings (
    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    temperature FLOAT NOT NULL,
    humidity FLOAT NOT NULL,
    created_at TIMESTAMPTZ DEFAULT NOW()
);


.env:

SUPABASE_URL=https://YOUR_PROJECT_ID.supabase.co
SUPABASE_KEY=YOUR_SERVICE_ROLE_KEY

publisher.py:

import paho.mqtt.client as mqtt
import random
import json
import time
import threading

# MQTT configuration
BROKER = "test.mosquitto.org"
PORT = 1883
TOPIC = "iot/sensor"

connected = threading.Event()


def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code.is_failure:
        connected.clear()
        print("MQTT connection failed:", reason_code)
    else:
        connected.set()
        print("Connected to MQTT Broker!")


def on_disconnect(client, userdata, disconnect_flags, reason_code, properties):
    connected.clear()
    print("Disconnected from MQTT Broker:", reason_code)


client = mqtt.Client(
    callback_api_version=mqtt.CallbackAPIVersion.VERSION2,
    client_id="sensor_publisher",
    protocol=mqtt.MQTTv311
)

client.on_connect = on_connect
client.on_disconnect = on_disconnect

try:
    print("Connecting to MQTT Broker...")
    client.connect(BROKER, PORT, 60)
    client.loop_start()

    while True:
        if not connected.wait(timeout=2):
            print("Waiting for MQTT connection...")
            continue

        temperature = round(random.uniform(20, 40), 2)
        humidity = round(random.uniform(40, 90), 2)

        data = {
            "temperature": temperature,
            "humidity": humidity
        }

        payload = json.dumps(data)

        result = client.publish(TOPIC, payload, qos=1)

        if result.rc == mqtt.MQTT_ERR_SUCCESS:
            try:
                result.wait_for_publish(timeout=10)

                if result.is_published():
                    print("Published successfully:", payload)
                else:
                    print("Publishing timed out")

            except RuntimeError as e:
                print("Publishing error:", e)
        else:
            print("Publish failed:", mqtt.error_string(result.rc))

        time.sleep(5)

except KeyboardInterrupt:
    print("\nPublisher stopped.")

except Exception as e:
    print("Error:", e)

finally:
    client.loop_stop()
    client.disconnect()
    print("MQTT connection closed.")


app.py:


from flask import Flask, jsonify, render_template
from supabase import create_client
from dotenv import load_dotenv
import paho.mqtt.client as mqtt
import os
import json
import threading

load_dotenv()

app = Flask(__name__)

# Supabase connection
SUPABASE_URL = os.getenv("SUPABASE_URL")
SUPABASE_KEY = os.getenv("SUPABASE_KEY")

if not SUPABASE_URL or not SUPABASE_KEY:
    raise ValueError(
        "SUPABASE_URL and SUPABASE_KEY must be set in the .env file"
    )

supabase = create_client(SUPABASE_URL, SUPABASE_KEY)

# MQTT configuration
BROKER = "test.mosquitto.org"
PORT = 1883
TOPIC = "iot/sensor"


# MQTT callback
def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code.is_failure:
        print("MQTT connection failed:", reason_code)
        return

    print("Connected to MQTT Broker!")

    result, mid = client.subscribe(TOPIC)

    if result == mqtt.MQTT_ERR_SUCCESS:
        print("Subscribed to:", TOPIC)
    else:
        print("Subscription failed:", mqtt.error_string(result))


# Receive MQTT messages
def on_message(client, userdata, msg):

    try:
        data = json.loads(msg.payload.decode())

        temperature = float(data["temperature"])
        humidity = float(data["humidity"])

        reading = {
            "temperature": temperature,
            "humidity": humidity
        }

        # Store in Supabase
        supabase.table("mqtt_readings").insert(reading).execute()

        print("Received and Stored:", reading)

    except Exception as e:
        print("Error:", e)


# MQTT subscriber
mqtt_client = mqtt.Client(
    callback_api_version=mqtt.CallbackAPIVersion.VERSION2,
    client_id="dashboard_subscriber"
)

mqtt_client.on_connect = on_connect
mqtt_client.on_message = on_message


# Start MQTT in background thread
def start_mqtt():
    while True:
        try:
            print("Connecting to MQTT broker...")
            mqtt_client.connect(BROKER, PORT, 60)
            mqtt_client.loop_forever()

        except Exception as e:
            print("MQTT connection error:", e)
            print("Retrying in 5 seconds...")
            import time
            time.sleep(5)


threading.Thread(target=start_mqtt, daemon=True).start()


# Dashboard
@app.route("/")
def dashboard():
    return render_template("index.html")


# Retrieve records from Supabase
@app.route("/api/readings")
def get_readings():

    result = (
        supabase.table("mqtt_readings")
        .select("*")
        .order("created_at", desc=True)
        .limit(50)
        .execute()
    )

    return jsonify(result.data)


if __name__ == "__main__":
    app.run(debug=True, use_reloader=False)


templates/index.html:


<!DOCTYPE html>
<html>
<head>
    <title>MQTT IoT Dashboard</title>

    <meta name="viewport" content="width=device-width, initial-scale=1">

    <style>
        body {
            font-family: Arial;
            background: #eef2f7;
            text-align: center;
            padding: 30px;
        }

        h1 {
            color: #1e293b;
        }

        .cards {
            display: flex;
            justify-content: center;
            gap: 20px;
            flex-wrap: wrap;
        }

        .card {
            background: white;
            padding: 25px;
            width: 220px;
            border-radius: 12px;
            box-shadow: 0 3px 10px #ccc;
        }

        .value {
            font-size: 35px;
            color: #2563eb;
            font-weight: bold;
        }

        table {
            margin: 30px auto;
            width: 90%;
            background: white;
            border-collapse: collapse;
        }

        th, td {
            padding: 12px;
            border: 1px solid #ddd;
        }

        th {
            background: #2563eb;
            color: white;
        }
    </style>
</head>

<body>

    <h1>MQTT IoT Sensor Dashboard</h1>

    <div class="cards">

        <div class="card">
            <h2>Temperature</h2>
            <div class="value" id="temperature">-- °C</div>
        </div>

        <div class="card">
            <h2>Humidity</h2>
            <div class="value" id="humidity">-- %</div>
        </div>

    </div>

    <h2>Sensor Reading History</h2>

    <table>
        <thead>
            <tr>
                <th>Temperature (°C)</th>
                <th>Humidity (%)</th>
                <th>Timestamp</th>
            </tr>
        </thead>

        <tbody id="readings"></tbody>
    </table>

    <script>
        async function loadReadings() {

            const response = await fetch("/api/readings");
            const data = await response.json();

            if (data.length > 0) {

                document.getElementById("temperature").innerText =
                    data[0].temperature + " °C";

                document.getElementById("humidity").innerText =
                    data[0].humidity + " %";
            }

            const table = document.getElementById("readings");

            table.innerHTML = "";

            data.forEach(item => {

                table.innerHTML += `
                    <tr>
                        <td>${item.temperature}</td>
                        <td>${item.humidity}</td>
                        <td>${new Date(item.created_at).toLocaleString()}</td>
                    </tr>
                `;
            });
        }

        loadReadings();

        setInterval(loadReadings, 5000);
    </script>

</body>
</html>