https://smazee.com/uploads/blog/Get-Started-with-Building-Your-Own-IoT-Dashboard-from-Scratch.jpg https://smazee.com/blog/get-started-with-building-your-own-iot-dashboard-from-scratch

Get Started with Building Your Own IoT Dashboard from Scratch

Creating an IoT dashboard from scratch is an exciting and rewarding project that allows you to monitor and control your IoT devices in real-time. In this blog, we'll focus on setting up a Django channel for WebSocket communication, a simple webpage to display data, and a button to toggle a real-time IoT device (in this case, a Pico microcontroller). We'll also use Redis to handle in-memory caching.

Prerequisites

Before we begin, make sure you have the following installed:

  • Python 3.8+
  • Django 3.2+
  • Django Channels 3.0+
  • Redis server
  • Blog on implementing django channel - Django Channel

Step 1: Setting Up the Django Project

First, create a new Django project and application.

django-admin startproject <project_name>
cd <project_name>
django-admin startapp <app_name>

Next, install Django Channels and Redis.

pip install 'channels[daphne]' channels-redis

Step 2: Configure Django Channels

In your settings.py file, add the following configurations:

INSTALLED_APPS = [
    ...,
    'daphne',
    <app_name>,
]

ASGI_APPLICATION = '<project_name>.asgi.application'

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            "hosts": [('127.0.0.1', 6379)],
        },
    },
}

Create an asgi.py file in your project directory (<project_name>) with the following content:

import  os
from  channels.auth  import  AuthMiddlewareStack
from  channels.routing  import  ProtocolTypeRouter, URLRouter
from  channels.security.websocket  import  AllowedHostsOriginValidator
from  django.core.asgi  import  get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', '<project_name>.settings')

django_asgi_app  =  get_asgi_application()

from  project.routing  import  websocket_urlpatterns

application  =  ProtocolTypeRouter(
    {
        "http": django_asgi_app,
        "websocket": AllowedHostsOriginValidator(
                AuthMiddlewareStack(URLRouter(websocket_urlpatterns))
                    ),
    }
)

Step 3: Setting Up WebSocket Routing

Create a routing.py file in your <app> application directory with the following content:

from django.urls import re_path
from . import consumers

websocket_urlpatterns = [
    re_path(r"ws/(?P<room_name>\w+)/$", consumers.ChatConsumer.as_asgi()),
]

Step 4: Creating the WebSocket Consumer

In your dashboard application, create a consumers.py file with the following content:

import json
from channels.generic.websocket import AsyncWebsocketConsumer

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name  =  self.scope["url_route"]["kwargs"]["room_name"]
        self.room_group_name  =  f"chat_{self.room_name}"        
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        await self.accept()

    async def disconnect(self, close_code):
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        text_data_json["type"] =  "chat.message"
        text_data_json["sender_channel_name"] =  self.channel_name
        print(text_data_json)
        await  self.channel_layer.group_send(
            self.room_group_name, text_data_json
        )

    async def iot_message(self, event):
        #To avoid message to send back to sender itself
        if  self.channel_name  !=  event['sender_channel_name']:
            await  self.send(text_data=json.dumps(event))

Step 5: Creating the Simple Webpage

In your <app> application, create a templates directory and an data.html file within it with the following content:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket Listener</title>
    <style>
        #toggle-container {
            position: absolute;
            top: 20px;
            right: 20px;
        }

        .toggle-button {
            padding: 10px 20px;
            color: white;
            border: none;
            cursor: pointer;
        }

        .off {
            background-color: red;
        }

        .on {
            background-color: green;
        }
    </style>
</head>
<body>
    <h1>WebSocket Data</h1>
    <ul id="data-list"></ul>
    <div id="toggle-container">
        <button id="toggle-button" class="toggle-button off">Off</button>
    </div>

    {{ name|json_script:"name" }}
    <script>
        // Replace with your WebSocket URL
        const name = JSON.parse(document.getElementById('name').textContent);

        const socket = new WebSocket(
                    'ws://'
                    + window.location.host
                    + '/ws/'
                    + name
                    + '/'
                );


        // When a message is received from the WebSocket
        socket.onmessage = function(event) {
            const dataObj = JSON.parse(event.data);
            const temp = dataObj.temperature;
            const hum = dataObj.humidity;
            if(temp && hum){
                const timestamp = dataObj.timestamp;

                // Create a new Date object from the timestamp
                const date = new Date(timestamp);

                // Format the date and time
                const formattedDate = date.toLocaleString();

                // Find the unordered list element
                const ul = document.getElementById("data-list");

                // Create a new list item element
                const li = document.createElement("li");

                // Set the text of the list item to the data and formatted date/time
                li.textContent = `Temperature:${temp}-----Humidity:${hum} (Received at: ${formattedDate})`;

                // Add the list item to the unordered list
                ul.appendChild(li);
            }
        };

        // Handle WebSocket connection errors
        socket.onerror = function(error) {
            console.error("WebSocket Error: ", error);
        };

        // Handle WebSocket connection open
        socket.onopen = function() {
            console.log("WebSocket connection opened.");
        };

        // Handle WebSocket connection close
        socket.onclose = function() {
            console.log("WebSocket connection closed.");
        };


        const toggleButton = document.getElementById("toggle-button");
        toggleButton.addEventListener("click", function() {
            const isOn = toggleButton.classList.contains("on");
            const toggleState = !isOn;
            const timestamp = new Date().getTime();

            // Update button appearance and text
            if (toggleState) {
                toggleButton.classList.remove("off");
                toggleButton.classList.add("on");
                toggleButton.textContent = "On";
            } else {
                toggleButton.classList.remove("on");
                toggleButton.classList.add("off");
                toggleButton.textContent = "Off";
            }

            // Create JSON object
            const message = JSON.stringify({
                button: toggleState,
                timestamp: timestamp
            });

            // Send JSON object through WebSocket
            socket.send(message);
        });
    </script>
</body>
</html>

Step 6: Creating the View

In your <app> application, create a views.py file with the following content:

from django.shortcuts import render

def data(request, name):
    return render(request, "data.html", {"name": name})

Step 7: Updating URLs

In your <project> project directory, update the urls.py file with the following content:

from django.contrib import admin
from django.urls import path, include
from <project_name> import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('<str:name>/', views.data, name='data'),
]

Step 8: Running Redis Server

Make sure your Redis server is running. You can start it with the following command with docker:

docker run --rm -p 6379:6379 redis:7

Step 9: Running the Django Server

Finally, run your Django development server:

python manage.py runserver 0.0.0.0:8000

Open your web browser and navigate to http://127.0.0.1:8000/ to see your IoT dashboard in action.

Test with postman:

Integrating with Microcontroller Pico W

Now that we have our Django backend and web interface set up, it's time to integrate our IoT device. For this example, we'll use a Raspberry Pi Pico W microcontroller. We'll write a simple script to connect the Pico W to the WiFi, establish a WebSocket connection with our Django server, and control an onboard LED based on the messages received from the server.

Prerequisites

Ensure you have the following:

  • A Raspberry Pi Pico W
  • Thonny IDE installed on your computer
  • The uwebsockets module installed on your Pico W - (ping me on linkedin for the package)

Step 10: Connecting Pico W to WiFi and WebSocket Server

We'll use a script to connect the Pico W to your WiFi network and establish a WebSocket connection with the Django server.

Script: led_tog.py

Below is the script for the Pico W. This script will connect to your WiFi, establish a WebSocket connection with the Django server, and toggle the onboard LED based on the received WebSocket messages.

import network
import ujson
import time
from machine import Pin
import uwebsockets as websockets

# WiFi credentials
SSID = 'your_wifi_ssid'
PASSWORD = 'your_wifi_password'

# WebSocket server address
WS_URL = 'ws://your_server_ip:8000/ws/iot/'

# Setup LED and Button
led = Pin('LED', Pin.OUT)  # Assuming the onboard LED pin

def connect_to_wifi(ssid, password):
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(ssid, password)

    while not wlan.isconnected():
        print("Connecting to WiFi...")
        time.sleep(1)
    print('Connected to WiFi:', wlan.ifconfig())

def websocket_handler():
    while True:
        try:
            print('Connecting to WebSocket server...')
            ws = websockets.connect(WS_URL)
            print('Connected to WebSocket server')

            while True:
                message = ws.recv()
                if message:
                    print('Received message:', message)
                    msg = ujson.loads(message)
                    if 'button' in msg:
                        led.value(msg['button'])
                    else:
                        print("No Button Data")

        except OSError as e:
            print('WebSocket connection failed:', e)
            time.sleep(5)  # Retry after 5 seconds

def main():
    connect_to_wifi(SSID, PASSWORD)
    websocket_handler()

if __name__ == '__main__':
    main()

Explanation of the Script

  1. Connecting to WiFi: The connect_to_wifi function handles the WiFi connection using the provided SSID and password.

  2. WebSocket Handler: The websocket_handler function connects to the WebSocket server and listens for messages. Upon receiving a message, it toggles the LED based on the button value in the received JSON message.

  3. Main Function: The main function calls the WiFi connection function and then starts the WebSocket handler.

Step 11: Uploading the Script to Pico W

  1. Open Thonny IDE.
  2. Connect your Pico W to your computer via USB.
  3. Select the Pico W as the target device in Thonny.
  4. Open a new file in Thonny and copy-paste the led_tog.py script into it.
  5. Save the file as led_tog.py on your Pico W.
  6. Run the script.

Step 12: Testing the Integration

With the script running on the Pico W and your Django server running, you should be able to toggle the onboard LED on the Pico W using the button on your web interface.

Navigate to your Django server's web interface, click the toggle button, and see the LED on your Pico W respond accordingly.

Conclusion

In this blog, we've set up a Django project with Django Channels and Redis for WebSocket communication. We've created a simple webpage to display data from an IoT device and a button to toggle the device. This is just the beginning of your IoT dashboard. In this extended guide, we've integrated a Raspberry Pi Pico W with our Django-based IoT dashboard. By setting up a WebSocket connection, we can control the Pico W's onboard LED in real-time from our web interface. This setup can be extended to include more sensors and actuators, providing a robust platform for various IoT applications.

Future Work

Add Database and Display in Webpage:

  • Implement a database to store button press data and other relevant information.
  • Create a web interface to display the stored data dynamically, allowing users toview historical and real-time data.

Enable Two-Way Communication Using Threads on Pico:

  • Enhance the Pico's functionality by enabling two-way communication through the useof threading.
  • This will allow simultaneous sending and receiving of data, improvingresponsiveness and interaction with the WebSocket server.

Integrate Sensor in Pico and Periodically Send Data to Webpage:

  • Incorporate a sensor (e.g., temperature, humidity, or motion sensor) into the Pico.
  • Periodically collect sensor data and transmit it to the webpage, providingreal-time monitoring and visualization of sensor readings.

By on
iot diy
Smazee https://smazee.com/uploads/blog/Get-Started-with-Building-Your-Own-IoT-Dashboard-from-Scratch.jpg

Read also

  1. Build Train and Deploy your Rasa Bot
  2. Basic terms in UXD!
  3. Automate build and deployment for Flutter Apps