Creating an API Server with ESP32 and CircuitPython to Control LEDs

thumbnail

 

 

 

 

 

Welcome back, everyone! In this blog post, we’re diving into the second part of our CircuitPython series, where we will set up an API server on the ESP32. Our focus is on controlling LEDs via HTTP requests. This post will cover everything from hardware connections to the code you need to get your ESP32 up and running.

 

Table of Contents

 

Getting Started with ESP32

We’ll be using the ESP32-C6, a tiny yet powerful microcontroller with built-in Wi-Fi capabilities. Its compact size makes it a great choice for IoT projects. To kick things off, let’s take a look at how we’ll connect the LED to the ESP32.

 

Wiring of LED to ESP32

 

Wiring the LED

To wire the LED, connect the long leg (anode) through a 330-ohm resistor to pin D2 on the ESP32. The short leg (cathode) should go to ground. This simple setup will allow us to control the LED with our API server.

 

Setting Up CircuitPython

Now that we have our hardware set up, let’s jump into CircuitPython. First, we'll need to modify our program slightly to include the necessary libraries for networking and digital I/O.

 

Code Overview

In our code, we will define our network credentials and set up the server. Here’s a brief overview:

  • We’ll create a settings.toml file to store the Wi-Fi SSID, password, and port.
  • We’ll establish a connection to the Wi-Fi network.
  • We’ll start the server on port 80 (though, for production, consider using port 443 with SSL).
  • Finally, we’ll define endpoints to turn the LED on and off.

 

Connecting to Wi-Fi

Once we have the credentials set up, we’ll attempt to connect to our Wi-Fi network. It’s crucial to use a separate IoT network for security reasons, so consider setting up a guest network specifically for your IoT devices.

 

Connecting to Wi-Fi

 

Starting the API Server

After successfully connecting to Wi-Fi, we start our API server. Here’s the essential part of the code:



In Settings.tmol file:

SID_VS="YOUR SID"
PAS_VS="YOUR PASSWORD"
PORT=80

/////////////

import socketpool
import wifi
import board
import os
import digitalio

# Connect to WiFi
network_name = os.getenv("SID_VS")
network_password = os.getenv("PAS_VS")

print("Connecting to WiFi...")
wifi.radio.connect(network_name, network_password)
device_ip = wifi.radio.ipv4_address
print("Connected to WiFi with IP:", device_ip)

# Set up LED
led = digitalio.DigitalInOut(board.D2)
led.direction = digitalio.Direction.OUTPUT


# Prepare socket
pool = socketpool.SocketPool(wifi.radio)
server_socket = pool.socket(pool.AF_INET, pool.SOCK_STREAM)
server_socket.bind((str(device_ip), os.getenv("PORT")))  # Bind to device IP
server_socket.listen(1)

print("Listening for connections on port 80")

def receive_request(connection):
    buffer = bytearray(1024)
    request = ""
    while True:
        try:
            num_bytes = connection.recv_into(buffer)
            if num_bytes == 0:
                break
            request += buffer[:num_bytes].decode('utf-8')
        except OSError as e:
            break
    return request

def respond(connection, response):
    response_text = (
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: text/html\r\n"
        "Connection: close\r\n"
        "\r\n" + response
    )
    connection.send(response_text.encode('utf-8'))
    connection.close()

while True:
    print("Waiting for a connection...")
    connection, address = server_socket.accept()
    print(f"Connection from {address}")

    try:
        # Read the request using recv_into
        request = receive_request(connection)
        print("Request:", request)


        if "GET /led/on" in request:
            led.value = True
            print("LED turned on")
            respond(connection, "

LED is now ON

") elif "GET /led/off" in request: led.value = False print("LED turned off") respond(connection, "

LED is now OFF

") else: respond(connection, "

Welcome! Use /led/on or /led/off

") finally: connection.close()

This code establishes a socket pool and starts listening for incoming connections. When a request is received, it will check the endpoint and control the LED accordingly.

 

Controlling the LED

Now, let’s look at how to control the LED using HTTP requests. We’ll define two endpoints:

  • /LED/on - This will turn the LED on.
  • /LED/off - This will turn the LED off.

When the server receives a request at these endpoints, it will change the LED’s state and respond with a confirmation message.

 

Testing LED control via API

 

Feedback Mechanisms with the LED

One enhancement we can make is using the LED to provide feedback on the Wi-Fi connection status. Instead of just turning the LED on and off, we can flash it to indicate whether the connection was successful or not.

 

Implementing LED Feedback

I’ve added a function to flash the LED a specific number of times based on the connection status:



import socketpool
import wifi
import board
import digitalio
import time  # Import the time module to use time.sleep()

# Connect to WiFi
network_name = os.getenv("SID_VS")
network_password = os.getenv("PAS_VS")

# Set up LED
led = digitalio.DigitalInOut(board.IO2)
led.direction = digitalio.Direction.OUTPUT

def flash_led(times, interval):
    """Function to flash the LED a given number of times with a specified interval."""
    for _ in range(times):
        led.value = True
        time.sleep(interval)
        led.value = False
        time.sleep(interval)

print("Connecting to WiFi...")

try:
    wifi.radio.connect(network_name, network_password)
    device_ip = wifi.radio.ipv4_address
    print("Connected to WiFi with IP:", device_ip)
    # Flash LED 3 times, once per second, for successful connection
    flash_led(3, 1)
except Exception as e:
    print("Failed to connect to WiFi:", e)
    # Flash LED 5 times, 0.2 second intervals, for failed connection
    flash_led(5, 0.2)

# Prepare socket only if WiFi connection is successful
if wifi.radio.ipv4_address:
    pool = socketpool.SocketPool(wifi.radio)
    server_socket = pool.socket(pool.AF_INET, pool.SOCK_STREAM)
    server_socket.bind((str(device_ip), os.getenv("PORT")))  # Bind to device IP
    server_socket.listen(1)

    print("Listening for connections on port 80")

    def receive_request(connection):
        buffer = bytearray(1024)
        request = ""
        while True:
            try:
                num_bytes = connection.recv_into(buffer)
                if num_bytes == 0:
                    break
                request += buffer[:num_bytes].decode('utf-8')
            except OSError as e:
                break
        return request

    def respond(connection, response):
        response_text = (
            "HTTP/1.1 200 OK\r\n"
            "Content-Type: text/html\r\n"
            "Connection: close\r\n"
            "\r\n" + response
        )
        connection.send(response_text.encode('utf-8'))
        connection.close()

    while True:
        print("Waiting for a connection...")
        connection, address = server_socket.accept()
        print(f"Connection from {address}")

        try:
            # Read the request using recv_into
            request = receive_request(connection)
            print("Request:", request)

            if "GET /led/on" in request:
                led.value = True
                print("LED turned on")
                respond(connection, "

LED is now ON

") elif "GET /led/off" in request: led.value = False print("LED turned off") respond(connection, "

LED is now OFF

") else: respond(connection, "

Welcome! Use /led/on or /led/off

") finally: connection.close()

If the board connects to Wi-Fi, it will flash the LED three times slowly; if it fails, it will flash five times quickly. This provides immediate feedback on the connection status.

 

Testing the API Server

With everything in place, it’s time to test our API server. You can use your smartphone or a web browser to send requests to the server.

When you access http://_YOUR IP_/LED/on, the LED should turn on, and you should see a response indicating that the LED is now on. Similarly, accessing http://_YOUR IP_/LED/off will turn the LED off.

 

API response for LED on

 

Conclusion and Next Steps

This setup provides a solid foundation for controlling devices via HTTP requests using the ESP32 and CircuitPython. In our next video, we’ll explore how to integrate this setup with Meshtastic, allowing for expanded communication capabilities.

Thank you for following along! Stay tuned for more exciting projects and tutorials in the world of IoT!

 

FAQ

What is CircuitPython?

CircuitPython is an education-focused derivative of MicroPython designed to make it easier to get started with programming and electronics.

Can I use other microcontrollers for this project?

Yes, while this guide uses the ESP32, you can adapt the code for other microcontrollers that support CircuitPython.

How do I secure my API server?

For production, it’s essential to use SSL and authentication tokens to secure your API endpoints.

What are the advantages of using a separate IoT network?

Using a separate network for IoT devices improves security by isolating them from your main network, reducing the risk of unauthorized access.

 

Share this article

I don't have an account,
I want to subscribe

I already have an account