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
-
Connecting to WiFi: The
connect_to_wifi
function handles the WiFi connection using the provided SSID and password. -
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 thebutton
value in the received JSON message. -
Main Function: The
main
function calls the WiFi connection function and then starts the WebSocket handler.
Step 11: Uploading the Script to Pico W
- Open Thonny IDE.
- Connect your Pico W to your computer via USB.
- Select the Pico W as the target device in Thonny.
- Open a new file in Thonny and copy-paste the
led_tog.py
script into it. - Save the file as
led_tog.py
on your Pico W. - 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.