ٱلْحَمْدُ لِلّٰهِ رَبِّ ٱلْعَالَمِينَ، وَٱلصَّلَاةُ وَٱلسَّلَامُ عَلىٰ خَاتَمُ ٱلْأَنْبِيَاءِ وَٱلْمُرْسَلِينَ
ESP8266 is a versatile microcontroller with Wi-Fi and Bluetooth capabilities. We purchased an ESP based relay module. It needs only 7V to 12V supply and has everything else on board to act as an IoT relay. We used micro-python to control it. Then made a web-server in micro-python that allows any LAN device to access this relay and turn it on and off remotely.
The ESP8266 is a small and low-cost microcontroller with built-in WiFi and Bluetooth. It is widely used in IoT (Internet of Things) projects.
In this project, we used an ESP8266-based relay module (HW-622). This module needs only a 7V to 12V DC supply. All required components such as the relay driver, voltage regulator, and opto-isolation are already available on the board. This makes it a ready-to-use IoT relay solution.
We programmed the module using MicroPython and created a web server that allows any device on the same WiFi network (mobile, laptop, tablet) to turn the relay ON or OFF using a web browser.
ESP8266 HW-622 Relay Module (Image Courtesy of https://ayatec.eu)
GPIO 0
Flash
Shares a jumper with ground pin, (P6)
Short jumper for programming mode. LEave opened for normal operation
GPIO 1
TXD0
COM0 serial transmitter pin
3.3V UART
GPIO 2
LED
Onboard blue LED
Usually used for WiFi status
GPIO 3
RXD0
COM0 serial receiver pin
3.3V UART
GPIO 4
Relay Switch
Active High
Write 1 to trigger the relay
GPIO 5
External Opto-isolated Switch
Active High
High when current passes through P4 terminals
First downloaded the generic Micropython Firmware from https://micropython.org. At the time of writing, the latest version was v1.27.0 (2025-12-09) .bin
To clean the ESP of any previous ROM images, we first cleaned it using esptool.
esptool --port /dev/ttyUSB0 --baud 115200 erase_flash
To burn MicroPython to esp8266, we again used the esptool command as follows:
esptool --port /dev/ttyUSB0 --baud 115200 write_flash --flash_size=detect 0 ESP8266_GENERIC-20251209-v1.27.0.bin
Long and loose jumper wires can cause flashing errors. Use short wires
Reduce baud rate if flashing fails
After flashing, remove the programming jumper (GPIO0) and reset the board.
We used Thonny IDE to communicate with ESP8266.
Open Thonny
Go to Tools → Options → Interpreter
Select MicroPython (ESP8266)
Choose the correct USB port
You can find the port using:
lsusb
If everything is correct, you will see:
MicroPython v1.27.0 on 2025-12-09; ESP module with ESP8266
Type "help()" for more information.
>>>
We used following commands in MicroPython interpreter to turn the relay switch ON:
>>> from machine import Pin
>>> relay = Pin(4, Pin.OUT, value=1)
The relay turns ON and the red LED on the board lights up.
We wrote a MicroPython script file that:
Starts ESP8266 in AP mode if connection to Wi-Fi fails
Allows user to enter Wi-Fi credentials from a webpage
Saves Wi-Fi details in flash
Connects to Wi-Fi routes and starts a web server
Displays page to control the relay using ON / OFF buttons
This script file can be run directly from Thonny, no need to burn it permanently.
ESP Starts in AP Mode if attempt to connect Wi-Fi failed during boot up.
After connecting to Wi-Fi, the relay control webpage is visible.
Clicking on Motor ON and OFF buttons switches the relays.
Connection from ('192.168.100.37', 41288)
Request: GET /?led=on
LED ON
Connection from ('192.168.100.37', 41294)
Request: GET /?led=off
LED OFF
ESP starts in AP mode if Wi-Fi fails
Web interface works on mobile and laptop
Relay switches instantly
No cloud, full local control
Simple, reliable, and practical IoT solution
# ======================
# ESP AS WEBSERVER
# ======================
import network
import socket
import time
import ujson
import uos
from machine import Pin
# ======================
# INITIALISE
# ======================
DEFAULT_SSID = "YOUR_WIFI"
DEFAULT_PASSWORD = "YOUR_PASSWORD"
AP_SSID = "ESP8266-Setup"
AP_PASSWORD = "12345678"
LED_PIN = 2 # Built-in LED on most ESP8266 boards
RELAY_PIN = 4 # Relay on ESP8266 HW-622 module
WIFI_FILE = "wifi.json"
# ======================
# FILESYSTEM SETUP
# ======================
def setup_filesystem():
"""Ensure filesystem is ready and wifi.json exists"""
try:
# Check if wifi.json exists, create default if not
files = uos.listdir()
if WIFI_FILE not in files:
print("Creating default wifi.json")
save_wifi(DEFAULT_SSID, DEFAULT_PASSWORD)
else:
print(f"{WIFI_FILE} exists")
except Exception as e:
print(f"Filesystem error: {e}")
# Try to create file anyway
try:
save_wifi(DEFAULT_SSID, DEFAULT_PASSWORD)
except:
print("Could not create wifi.json")
# ======================
# HARDWARE SETUP
# ======================
led = Pin(LED_PIN, Pin.OUT, value=0) # Start with LED off
relay = Pin(RELAY_PIN, Pin.OUT, value=0) # Start with LED off
mode = "ap" # "ap" or "sta"
sta = network.WLAN(network.STA_IF)
ap = network.WLAN(network.AP_IF)
# =======================
# SAVE WIFI SSID & PASSWD
# =======================
def save_wifi(ssid, password):
"""Save WiFi credentials to filesystem"""
try:
with open(WIFI_FILE, "w") as f:
ujson.dump({"ssid": ssid, "password": password}, f)
print(f"Saved WiFi: {ssid}")
return True
except Exception as e:
print(f"Error saving WiFi: {e}")
# Try to create file with raw write
try:
import json
data = json.dumps({"ssid": ssid, "password": password})
with open(WIFI_FILE, "wb") as f:
f.write(data)
return True
except:
print("Failed to save credentials")
return False
# =======================
# READ WIFI SSID & PASSWD
# =======================
def load_wifi():
"""Load WiFi credentials from filesystem"""
try:
with open(WIFI_FILE, "r") as f:
data = ujson.load(f)
ssid = data.get("ssid")
password = data.get("password")
if ssid and password:
print(f"Loaded WiFi: {ssid}")
return ssid, password
else:
print("Invalid wifi.json format")
return None, None
except Exception as e:
print(f"Error loading WiFi: {e}")
return None, None
# ======================
# WIFI CONNECT
# ======================
def try_connect(ssid, password, timeout=15):
"""Attempt to connect to WiFi"""
print(f"Connecting to {ssid}...")
# Deactivate AP if active
if ap.active():
ap.active(False)
# Ensure STA is active
sta.active(True)
time.sleep(0.5)
# Connect
sta.connect(ssid, password)
# Wait for connection
start = time.time()
while not sta.isconnected():
if time.time() - start > timeout:
print("Connection timeout")
sta.active(False)
return False
# Blink LED while connecting
led.value(not led.value())
time.sleep(0.3)
# LED steady on when connected
led.value(0) # LED ON
print(f"Connected! IP: {sta.ifconfig()[0]}")
return True
# ======================
# START UP
# ======================
def start_ap():
"""Start Access Point mode"""
print("Starting AP mode...")
# Deactivate STA if active
if sta.active():
sta.active(False)
time.sleep(0.5)
# Activate AP
ap.active(True)
time.sleep(0.5)
# Configure AP
ap.config(essid=AP_SSID, password=AP_PASSWORD, authmode=network.AUTH_WPA_WPA2_PSK)
# Wait for AP to start
for _ in range(30):
if ap.active():
break
time.sleep(0.1)
if ap.active():
print(f"AP Mode: {AP_SSID}")
print(f"AP IP: {ap.ifconfig()[0]}")
# Blink LED slowly in AP mode
led.value(1)
time.sleep(0.5)
led.value(0)
else:
print("Failed to start AP")
# ======================
# WIFI SETUP PAGE
# ======================
def wifi_page(msg=""):
return f"""<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {{font-family: Arial; margin: 20px;}}
input {{padding: 8px; margin: 5px 0; width: 200px;}}
button {{padding: 10px 20px; background: #4CAF50; color: white; border: none; cursor: pointer;}}
.msg {{color: {'red' if msg else 'green'}; padding: 10px;}}
</style>
</head>
<body>
<h2>ESP8266 WiFi Setup</h2>
<div class="msg">{msg}</div>
<form action="/connect">
SSID:<br>
<input name="ssid" required><br>
Password:<br>
<input type="password" name="password"><br><br>
<input type="submit" value="Connect">
</form>
<p>Current mode: {mode.upper()}</p>
</body>
</html>
"""
# ======================
# RELAY CONTROL PAGE
# ======================
def control_page():
led_state = "OFF" if led.value() else "ON"
ip_info = sta.ifconfig()[0] if sta.isconnected() else "Not connected"
return f"""<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {{font-family: Arial; margin: 20px;}}
button {{padding: 15px 30px; margin: 10px; font-size: 16px; cursor: pointer;}}
.on {{background: #4CAF50; color: white;}}
.off {{background: #f44336; color: white;}}
.reset {{background: #FF9800; color: white; padding: 10px 20px;}}
.status {{padding: 10px; background: #f0f0f0; margin: 10px 0;}}
</style>
</head>
<body>
<h2>ESP8266 Controller</h2>
<div class="status">
<p>MOTOR is <b>{led_state}</b></p>
<p>IP: {ip_info}</p>
</div>
<a href="/?led=on"><button class="on">MOTOR ON</button></a>
<a href="/?led=off"><button class="off">MOTOR OFF</button></a>
<br><br>
<a href="/reset"><button class="reset">Reset WiFi Settings</button></a>
</body>
</html>
"""
# ======================
# START UP
# ======================
print("\n=== ESP8266 WiFi Setup ===")
print("Initializing filesystem...")
setup_filesystem()
# Try to load saved WiFi
ssid, password = load_wifi()
if ssid and password:
print(f"Attempting saved WiFi: {ssid}")
if try_connect(ssid, password):
mode = "sta"
else:
print("Saved WiFi failed, starting AP")
start_ap()
mode = "ap"
else:
print("No saved WiFi, starting AP")
start_ap()
mode = "ap"
# ======================
# WEB SERVER
# ======================
print("Starting web server...")
addr = socket.getaddrinfo("0.0.0.0", 80)[0][-1]
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(1)
print(f"Server listening on {addr}")
def handle_request(req, conn):
"""Handle HTTP requests"""
global mode, ssid, password
# Parse request
try:
request_line = req.split('\n')[0]
method, path, _ = request_line.split(' ')
except:
return
# Debug
print(f"Request: {method} {path}")
# Handle different endpoints
if path.startswith('/connect?'):
# Extract credentials from query string
params = {}
query = path.split('?')[1]
for pair in query.split('&'):
if '=' in pair:
key, value = pair.split('=', 1)
params[key] = value.replace('%20', ' ').replace('+', ' ')
ssid_new = params.get('ssid', '')
password_new = params.get('password', '')
if ssid_new:
print(f"Attempting to connect to: {ssid_new}")
conn.send("HTTP/1.1 200 OK\n\n")
conn.sendall(wifi_page("Connecting..."))
conn.close()
if try_connect(ssid_new, password_new):
mode = "sta"
save_wifi(ssid_new, password_new)
ap.active(False)
print(f"Connected and saved: {ssid_new}")
else:
start_ap()
mode = "ap"
else:
conn.send("HTTP/1.1 200 OK\n\n")
conn.sendall(wifi_page("SSID required"))
conn.close()
elif path == '/reset':
# Reset WiFi settings
conn.send("HTTP/1.1 200 OK\n\n")
conn.sendall(wifi_page("Settings reset to default"))
conn.close()
save_wifi(DEFAULT_SSID, DEFAULT_PASSWORD)
print("WiFi settings reset")
import machine
machine.reset() # Reboot to apply changes
elif path.startswith('/?'):
# LED control
if 'led=on' in path:
led.value(0) # LED ON
relay.value(1) # LED ON
print("LED ON")
elif 'led=off' in path:
led.value(1) # LED OFF
relay.value(0)
print("LED OFF")
conn.send("HTTP/1.1 200 OK\n\n")
conn.sendall(control_page())
conn.close()
else:
# Default page based on mode
conn.send("HTTP/1.1 200 OK\nContent-Type: text/html\n\n")
if mode == "ap":
conn.sendall(wifi_page())
else:
conn.sendall(control_page())
conn.close()
# ======================
# MAIN LOOP
# ======================
print("\n=== System Ready ===")
print(f"Mode: {mode.upper()}")
if mode == "sta" and sta.isconnected():
print(f"IP: {sta.ifconfig()[0]}")
else:
print(f"AP IP: {ap.ifconfig()[0]}")
while True:
try:
conn, addr = s.accept()
print(f"Connection from {addr}")
# Receive request
req = conn.recv(1024).decode('utf-8')
if req:
handle_request(req, conn)
else:
conn.close()
except Exception as e:
print(f"Server error: {e}")
try:
conn.close()
except:
pass
time.sleep(1)
Console messages after running the above script, ESP starts in AP mode as Wi-Fi is not set first time.
=== ESP8266 WiFi Setup ===
Initializing filesystem...
Creating default wifi.json
Saved WiFi: YOUR_WIFI
Loaded WiFi: YOUR_WIFI
Attempting saved WiFi: YOUR_WIFI
Connecting to YOUR_WIFI...
#4 ets_task(4020f540, 28, 3fff8e28, 10)
Connection timeout
Saved WiFi failed, starting AP
Starting AP mode...
#5 ets_task(4020f578, 29, 3fff9660, 10)
#6 ets_task(4020f578, 29, 3fff8e00, 10)
AP Mode: ESP8266-Setup
AP IP: 192.168.4.1
Starting web server...
Server listening on ('0.0.0.0', 80)
=== System Ready ===
Mode: AP
AP IP: 192.168.4.1
After entering the WiFi credentials in the webpage. The ESP connects to LAN.
=== System Ready ===
Mode: AP
AP IP: 192.168.4.1
Connection from ('192.168.4.2', 40034)
Request: GET /
Connection from ('192.168.4.2', 40040)
Request: GET /favicon.ico
Connection from ('192.168.4.2', 54340)
Request: GET /connect?ssid=****** &password=******
Attempting to connect to: ******
Connecting to ******...
#7 ets_task(4020f540, 28, 3fff8e00, 10)
Connected! IP: 192.168.100.29
Saved WiFi: ******
Connected and saved: *****