Control Xiao ESP32-C3 LEDs with Django from Anywhere in the World

In this tutorial, we will learn how to remotely control LEDs connected to a Xiao ESP32-C3 using a Django web application. This allows you to toggle LEDs from anywhere in the world using a web interface.

Project Overview

  1. Django Backend:
    • Stores LED states in a database.
    • Provides an API endpoint to fetch LED statuses.
    • Offers a web interface to toggle LEDs.
  2. ESP32-C3 Firmware:
    • Connects to WiFi.
    • Requests the LED status from Django.
    • Updates LED states accordingly.

Step 1: Setting Up Django Backend

1.1 Install Django and Required Packages

Ensure you have Python installed, then install Django:

pip install django

1.2 Create a Django Project & App

django-admin startproject led_control_project
cd led_control_project
python manage.py startapp led_control

Add led_control to INSTALLED_APPS in settings.py:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'led_control',  # Add this line
]

1.3 Create the LED Model

In led_control/models.py, define a model to store the LED states:

from django.db import models

class LedState(models.Model):
    led1_status = models.BooleanField(default=False)
    led2_status = models.BooleanField(default=False)
    led3_status = models.BooleanField(default=False)
    led4_status = models.BooleanField(default=False)

    def __str__(self):
        return f"LED1: {'ON' if self.led1_status else 'OFF'}, " \
               f"LED2: {'ON' if self.led2_status else 'OFF'}, " \
               f"LED3: {'ON' if self.led3_status else 'OFF'}, " \
               f"LED4: {'ON' if self.led4_status else 'OFF'}"

Run migrations:

python manage.py makemigrations
python manage.py migrate

1.4 Create Views to Control LEDs

In led_control/views.py, create views to update and fetch LED statuses:

import json
from django.http import JsonResponse
from .models import LedState
from django.shortcuts import render,redirect


def led_control(request):
    # Get or create the LedState instance
    led_state, created = LedState.objects.get_or_create(id=1)

    # Handling LED ON/OFF toggle for each LED individually
    if request.method == 'POST':
        if 'led1' in request.POST:
            led_state.led1_status = not led_state.led1_status
        elif 'led2' in request.POST:
            led_state.led2_status = not led_state.led2_status
        elif 'led3' in request.POST:
            led_state.led3_status = not led_state.led3_status
        elif 'led4' in request.POST:
            led_state.led4_status = not led_state.led4_status

        # Save the updated LED state
        led_state.save()

        return redirect('led_control')  # Redirect to the same page to update state

    return render(request, 'led_control.html', {'led_state': led_state})


def get_led_status(request):
    """Fetch the status of all LEDs"""
    led_state = LedState.objects.first()
    if led_state:
        return JsonResponse({
            "led1_status": int(led_state.led1_status),
            "led2_status": int(led_state.led2_status),
            "led3_status": int(led_state.led3_status),
            "led4_status": int(led_state.led4_status)
        })
    return JsonResponse({"error": "No LED state found"}, status=404)



1.5 Configure URLs

In led_control/urls.py, add the API routes:

from django.urls import path
from .views import get_led_status, led_control

urlpatterns = [
    path('', led_control, name='led_control'),
    path('led-status/', get_led_status, name='get_led_status'),
    
]

Include them in led_control_project/urls.py:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('led_control.urls')),
]

Create a new HTML file inside your templates folder:
📂 templates/led_control.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>LED Control</title>
    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
    <style>
        body {
            font-family: 'Roboto', sans-serif;
            background-color: #111;
            color: #fff;
            margin: 0;
            padding: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            background: linear-gradient(45deg, #3e8e41, #004d00);
        }

        .container {
            background-color: #222;
            border-radius: 20px;
            padding: 40px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
            text-align: center;
            width: 450px;
        }

        h1 {
            font-size: 28px;
            margin-bottom: 20px;
            color: #fff;
            letter-spacing: 2px;
        }

        .led-button {
            padding: 15px 30px;
            margin: 15px;
            cursor: pointer;
            font-size: 22px;
            color: white;
            border: none;
            border-radius: 50px;
            transition: background-color 0.3s ease, transform 0.3s ease;
            width: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 10px;
        }

        .on {
            background-color: #28a745;
        }

        .off {
            background-color: #dc3545;
        }

        .led-button:hover {
            transform: scale(1.1);
        }

        .led-button:active {
            transform: scale(0.95);
        }

        .led-button:focus {
            outline: none;
        }

        .status-label {
            font-size: 18px;
            margin-bottom: 15px;
            color: #ccc;
            display: block;
        }

        .led-control-wrapper {
            margin-bottom: 25px;
        }

        .icon {
            font-size: 32px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>LED Control Dashboard</h1>
        <form method="post">
            {% csrf_token %}
            <div class="led-control-wrapper">
                <span class="status-label">LED 1:</span>
                <button type="submit" name="led1" class="led-button {% if led_state.led1_status %}on{% else %}off{% endif %}">
                    <i class="fas {% if led_state.led1_status %}fa-lightbulb{% else %}fa-lightbulb-slash{% endif %} icon"></i>
                    <span>{% if led_state.led1_status %}ON{% else %}OFF{% endif %}</span>
                </button>
            </div>
            <div class="led-control-wrapper">
                <span class="status-label">LED 2:</span>
                <button type="submit" name="led2" class="led-button {% if led_state.led2_status %}on{% else %}off{% endif %}">
                    <i class="fas {% if led_state.led2_status %}fa-lightbulb{% else %}fa-lightbulb-slash{% endif %} icon"></i>
                    <span>{% if led_state.led2_status %}ON{% else %}OFF{% endif %}</span>
                </button>
            </div>
            <div class="led-control-wrapper">
                <span class="status-label">LED 3:</span>
                <button type="submit" name="led3" class="led-button {% if led_state.led3_status %}on{% else %}off{% endif %}">
                    <i class="fas {% if led_state.led3_status %}fa-lightbulb{% else %}fa-lightbulb-slash{% endif %} icon"></i>
                    <span>{% if led_state.led3_status %}ON{% else %}OFF{% endif %}</span>
                </button>
            </div>
        </form>
    </div>
</body>
</html>

Run the server:

python manage.py runserver 0.0.0.0:8000

Step 2: ESP32-C3 Code to Control LEDs

2.1 Install Required Libraries

Ensure you have the Arduino IDE and the ESP32 board package installed.

2.2 ESP32-C3 Code

Upload the following code to your Xiao ESP32-C3:

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>  // JSON Parsing Library

const char* ssid = "ApyCoder";
const char* password = "Asif12345";
const char* serverURL = "http://10.129.101.93:8000/led-status/";

#define LED1_PIN 10  
#define LED2_PIN 9  
#define LED3_PIN 8  
#define LED4_PIN 7  

bool lastLedState[4] = {LOW, LOW, LOW, LOW};  // Store last LED states

void setup() {
    Serial.begin(115200);
    
    // Initialize LED pins
    pinMode(LED1_PIN, OUTPUT);
    pinMode(LED2_PIN, OUTPUT);
    pinMode(LED3_PIN, OUTPUT);
    pinMode(LED4_PIN, OUTPUT);

    // Set LEDs to LOW initially
    digitalWrite(LED1_PIN, LOW);
    digitalWrite(LED2_PIN, LOW);
    digitalWrite(LED3_PIN, LOW);
    digitalWrite(LED4_PIN, LOW);

    // Connect to WiFi
    Serial.print("Connecting to WiFi...");
    WiFi.begin(ssid, password);
    
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.println("\nConnected to WiFi!");
}

void loop() {
    // Check WiFi connection
    if (WiFi.status() != WL_CONNECTED) {
        Serial.println("WiFi Disconnected! Reconnecting...");
        WiFi.reconnect();
        delay(1000);
        return;
    }

    // Make HTTP GET request
    HTTPClient http;
    http.begin(serverURL);
    int httpResponseCode = http.GET();

    // If request is successful
    if (httpResponseCode == 200) {
        String payload = http.getString();
        Serial.println("Server Response: " + payload);

        // Parse JSON using ArduinoJson
        StaticJsonDocument<200> doc;
        DeserializationError error = deserializeJson(doc, payload);
        
        if (!error) {
            int ledStates[4] = {
                doc["led1_status"], 
                doc["led2_status"], 
                doc["led3_status"], 
                doc["led4_status"]
            };

            // Debug: Print LED states
            Serial.print("LED States: ");
            for (int i = 0; i < 4; i++) {
                Serial.print(ledStates[i]);
                Serial.print(" ");
            }
            Serial.println();

            // Check if LED states have changed and update the LEDs
            for (int i = 0; i < 4; i++) {
                int pin = (i == 0) ? LED1_PIN : (i == 1) ? LED2_PIN : (i == 2) ? LED3_PIN : LED4_PIN;
                if (ledStates[i] != lastLedState[i]) {
                    Serial.print("Changing LED ");
                    Serial.print(i + 1);
                    Serial.print(" to ");
                    Serial.println(ledStates[i] ? "ON" : "OFF");
                    
                    // Update the LED pin based on the state
                    digitalWrite(pin, ledStates[i] ? HIGH : LOW);
                    lastLedState[i] = ledStates[i];  // Update the last state
                }
            }
        } else {
            Serial.println("JSON Parsing Error!");
        }
    } else {
        Serial.print("HTTP Error: ");
        Serial.println(httpResponseCode);
    }
    
    http.end();

    // Add delay for stability
    delay(1000);  // Increase delay to 1 second for stability
}

Step 3: Running the System

  1. Start your Django server (python manage.py runserver 0.0.0.0:8000).
  2. Power up the Xiao ESP32-C3.
  3. Open the Django Web Interface to toggle LEDs.
  4. The ESP32 will fetch the LED states and update accordingly.

Conclusion

You have successfully built an IoT system to control LEDs on a Xiao ESP32-C3 from anywhere in the world using Django. This setup can be extended to control other IoT devices such as relays, motors, or sensors. 🚀

🔗 Subscribe to my YouTube channel
💻 Check out the code on GitHub

Leave a Comment

Your email address will not be published. Required fields are marked *


Shopping Basket
Scroll to Top