Web Server Control

ICT 360: Introduction to IoT

Mr. Seng Theara

Learning Objective

Learning Objectives

  • Read distance data from an ultrasonic sensor
  • Display sensor data on an I2C LCD

  • Explain how a web server works on an ESP32

  • Create a basic HTTP web server

  • Control ESP32 behavior via web buttons

  • Display real-time sensor data on a webpage

Ultrasonic Sensor

Ultrasonic Sensor

Expansion Board or External Shield

Ultrasonic refers to sound waves with frequencies that exceed the range of what human ears can hear. 

An ultrasonic sensor is an electronic device used to measure distance by sending out high-frequency sound waves (ultrasound) and calculating how long it takes for the sound to bounce back from an object.

Distance Formula

\text{Distance} = \frac{t \times v}{2}

Where

\begin{aligned} t &= \text{time of flight (seconds)} \\ v &= \text{speed of sound in air} \approx 343\,\text{m/s} \end{aligned}

Divide by 2 because the sound travels to the object and back

Connection

Signal

Signal

Vcc/5V

Vcc/5V

GND

GND

Flowchart & Code

Signal

Signal

Vcc/5V

Vcc/5V

GND

GND

from machine import Pin, time_pulse_us
import time

# Pin configuration
TRIG = Pin(27, Pin.OUT)
ECHO = Pin(26, Pin.IN)

def get_distance_cm():
    # Ensure trigger is LOW
    TRIG.value(0)
    time.sleep_us(2)

    # Send 10µs pulse
    TRIG.value(1)
    time.sleep_us(10)
    TRIG.value(0)

    # Measure echo pulse duration
    duration = time_pulse_us(ECHO, 1, 30000)  # timeout = 30ms

    # Check for timeout
    if duration < 0:
        return None

    # Distance calculation (cm)
    distance = (duration * 0.0343) / 2
    return distance

# Main loop
while True:
    dist = get_distance_cm()
    if dist is not None:
        print("Distance: {:.2f} cm".format(dist))
    else:
        print("Out of range")

    time.sleep(1)

LCD I2C

An I²C LCD is a character display (16×2 or 20×4) that communicates with the ESP32 using only two wires:

  • SDA → Data

  • SCL → Clock

LCD 16×2 can display 16 column and 2 row

LCD 20x4  can display 20 column and 4 row

Connection

LCD I2C with ESP32

LCD I2C Library 

DC- 

DC+

Signal Trigger

GND

Vcc

If HIGH

-> Common connect to 

Normally Open

First, you create two files and save them in the microPython device

You can find the library code in the link below

Testing LCD I2C with ESP32

from machine import Pin, SoftI2C
from machine_i2c_lcd import I2cLcd
from time import sleep

I2C_ADDR = 0x27
i2c = SoftI2C(sda=Pin(21), scl=Pin(22), freq=400000)
lcd = I2cLcd(i2c, I2C_ADDR, 2, 16)

lcd.clear()
lcd.move_to(0, 0)         # first row
lcd.putstr("IoT course")
lcd.move_to(0, 1)         # second row
lcd.putstr("Welcome to AUPP")

We set the column and row of index 0 to display the text "IoT course"

We set the column of index 0 and row of index 1 to display the text "Welcome to AUPP"

Practice

Read the distance using the ultrasonic sensor and display it on the LCD I2C

Introduction to Web Server

from machine import Pin
import time

# Initialize relay pin (GPIO15 / D15)
relay = Pin(15, Pin.OUT)

print("Relay control started...")

while True:
    # Turn relay ON
    relay.value(1)
    print("Relay ON")
    time.sleep(2)

    # Turn relay OFF
    relay.value(0)
    print("Relay OFF")
    time.sleep(2)

Introduction to Web Server

from machine import Pin
import time

# Initialize relay pin (GPIO15 / D15)
relay = Pin(15, Pin.OUT)

print("Relay control started...")

while True:
    # Turn relay ON
    relay.value(1)
    print("Relay ON")
    time.sleep(2)

    # Turn relay OFF
    relay.value(0)
    print("Relay OFF")
    time.sleep(2)

A web server is essentially a specialized computer with one main job: storing websites and delivering them to users who request them. When you type a website address into your browser or click on a link, you’re essentially placing an order with a webserver.

When the web server receives your request, it quickly searches through its stored files to find the webpage you asked for (like HTML documents, images, videos, and other content that makes up the website).

If it finds them, the web server gathers all these components together, packages them into an HTTP response, and sends them back to your browser.

ESP32 Wi-Fi Operating Modes

For the ESP32 to function as a web server and serve a webpage, it must first establish a network connection using one of its three Wi-Fi modes: Station (STA) mode, Access Point (AP) mode, and Dual (AP+STA) mode.

  • Station (STA) Mode

When your ESP32 is in STA mode, it acts like any other device, such as your laptop or smartphone, that wants to connect to an existing Wi-Fi network. It searches for a Wi-Fi network, connects to it using the network name (SSID) and password, and then becomes part of that network.

  • Access Point (AP) Mode

When your ESP32 is in AP mode, it creates its own Wi-Fi network, assigning it an SSID (the network’s name), a password, and an IP address. Other devices, like your smartphone or computer, can then search for this network and connect to it using a password you’ve set. With the IP address it assigns itself, your ESP32 can serve web pages directly to all the devices that have connected to its newly created network.

  • Dual Mode

What makes the ESP32 particularly versatile is that it can actually operate in both modes simultaneously, which is called AP+STA mode or dual mode. In this configuration, your ESP32 can connect to an existing Wi-Fi network as a station while also creating its own access point for other devices to join. This creates interesting possibilities, such as building a device that can extend Wi-Fi coverage or create a bridge between different networks.

Web Server with LED Control

Web Server with LED Control

import network
import socket
from machine import Pin
import time

# ==============================
# LED SETUP
# ==============================
led = Pin(12, Pin.OUT)
led.off()

# ==============================
# WIFI SETUP (Station Mode)
# ==============================
ssid = "project"
password = "1234567890"

wifi = network.WLAN(network.STA_IF)
wifi.active(True)
wifi.connect(ssid, password)

print("Connecting to WiFi...")
while not wifi.isconnected():
    time.sleep(1)

ip = wifi.ifconfig()[0]
print("Connected!")
print("ESP32 IP address:", ip)

# ==============================
# WEB SERVER SETUP
# ==============================
addr = socket.getaddrinfo("0.0.0.0", 80)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)

print("Web server running...")

# ==============================
# HTML PAGE
# ==============================
def web_page():
    html = """
    <html>
    <head>
        <title>ESP32 LED Control</title>
        <style>
            body { font-family: Arial; text-align: center; }
            button { width: 120px; height: 50px; font-size: 20px; }
        </style>
    </head>
    <body>
        <h1>ESP32 LED Control</h1>
        <p><a href="/on"><button>ON</button></a></p>
        <p><a href="/off"><button>OFF</button></a></p>
    </body>
    </html>
    """
    return html

# ==============================
# MAIN LOOP
# ==============================
while True:
    conn, addr = s.accept()
    request = conn.recv(1024).decode()

    print("Request:", request)

    if "/on" in request:
        led.on()
        print("LED ON")

    if "/off" in request:
        led.off()
        print("LED OFF")

    response = web_page()
    conn.send("HTTP/1.1 200 OK\n")
    conn.send("Content-Type: text/html\n")
    conn.send("Connection: close\n\n")
    conn.sendall(response)
    conn.close()


Testing

Webserver Response Back 

import network
import socket
from machine import Pin
import time

# ==============================
# LED SETUP
# ==============================
led = Pin(2, Pin.OUT)
led.off()
led_state = False  # False = OFF, True = ON

# ==============================
# WIFI SETUP (Station Mode)
# ==============================
ssid = "project"
password = "1234567890"

wifi = network.WLAN(network.STA_IF)
wifi.active(True)
wifi.connect(ssid, password)

print("Connecting to WiFi...")
while not wifi.isconnected():
    time.sleep(1)

ip = wifi.ifconfig()[0]
print("Connected!")
print("ESP32 IP address:", ip)

# ==============================
# WEB SERVER SETUP
# ==============================
addr = socket.getaddrinfo("0.0.0.0", 80)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)

print("Web server running...")

# ==============================
# HTML PAGE WITH LED STATUS
# ==============================
def web_page(state):
    if state:
        color = "green"
        status = "LED is ON"
    else:
        color = "red"
        status = "LED is OFF"

    html = f"""
    <html>
    <head>
        <title>ESP32 LED Control</title>
        <style>
            body {{
                font-family: Arial;
                text-align: center;
            }}
            .circle {{
                width: 80px;
                height: 80px;
                background-color: {color};
                border-radius: 50%;
                margin: 20px auto;
            }}
            button {{
                width: 120px;
                height: 50px;
                font-size: 20px;
            }}
        </style>
    </head>
    <body>
        <h1>ESP32 LED Control</h1>

        <div class="circle"></div>
        <h2>{status}</h2>

        <p><a href="/on"><button>ON</button></a></p>
        <p><a href="/off"><button>OFF</button></a></p>
    </body>
    </html>
    """
    return html

# ==============================
# MAIN LOOP
# ==============================
while True:
    conn, addr = s.accept()
    request = conn.recv(1024).decode()

    print("Request:", request)

    if "/on" in request:
        led.on()
        led_state = True
        print("LED ON")

    if "/off" in request:
        led.off()
        led_state = False
        print("LED OFF")

    response = web_page(led_state)
    conn.send("HTTP/1.1 200 OK\n")
    conn.send("Content-Type: text/html\n")
    conn.send("Connection: close\n\n")
    conn.sendall(response)
    conn.close()

Turn Green when ON

Turn Red

when OFF

Testing