import network
import socket
import time
from machine import Pin
# Hardware setup
led = Pin(14, Pin.OUT)
relay = Pin(26, Pin.OUT)
buzzer = Pin(25, Pin.OUT)
# Initialize outputs
led.off()
relay.off()
buzzer.off()
# Configuration
PASSWORD = "1234"
AP_SSID = "ESP32_DoorLock"
AP_PASSWORD = "doorlock123" # WiFi password (min 8 chars)
# Statistics
successful_attempts = 0
failed_attempts = 0
# Functions
def blink_once():
led.on()
time.sleep(0.5)
led.off()
def alarm(duration_sec=5):
start = time.time()
while time.time() - start < duration_sec:
led.on()
buzzer.on()
time.sleep(0.3)
led.off()
buzzer.off()
time.sleep(0.3)
def open_door():
global successful_attempts
print("✓ Access Granted")
successful_attempts += 1
relay.on()
blink_once()
time.sleep(2)
relay.off()
def deny_access():
global failed_attempts
print("✗ Access Denied")
failed_attempts += 1
alarm(5)
# HTML webpage
def get_html():
html = """<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ESP32 Door Lock</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.container {
background: white;
padding: 40px;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
max-width: 400px;
width: 100%;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 10px;
font-size: 28px;
}
.subtitle {
text-align: center;
color: #666;
margin-bottom: 30px;
font-size: 14px;
}
.stats {
display: flex;
justify-content: space-around;
margin-bottom: 30px;
padding: 15px;
background: #f5f5f5;
border-radius: 10px;
}
.stat {
text-align: center;
}
.stat-value {
font-size: 24px;
font-weight: bold;
color: #667eea;
}
.stat-label {
font-size: 12px;
color: #666;
margin-top: 5px;
}
.form-group {
margin-bottom: 25px;
}
label {
display: block;
margin-bottom: 8px;
color: #333;
font-weight: bold;
}
input[type="password"] {
width: 100%;
padding: 15px;
border: 2px solid #ddd;
border-radius: 10px;
font-size: 18px;
text-align: center;
letter-spacing: 5px;
transition: border-color 0.3s;
}
input[type="password"]:focus {
outline: none;
border-color: #667eea;
}
button {
width: 100%;
padding: 15px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 10px;
font-size: 18px;
font-weight: bold;
cursor: pointer;
transition: transform 0.2s;
}
button:hover {
transform: translateY(-2px);
}
button:active {
transform: translateY(0);
}
.message {
margin-top: 20px;
padding: 15px;
border-radius: 10px;
text-align: center;
font-weight: bold;
display: none;
}
.success {
background: #d4edda;
color: #155724;
border: 2px solid #c3e6cb;
}
.error {
background: #f8d7da;
color: #721c24;
border: 2px solid #f5c6cb;
}
.keypad {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
margin-bottom: 20px;
}
.key {
padding: 20px;
background: #f5f5f5;
border: 2px solid #ddd;
border-radius: 10px;
font-size: 24px;
font-weight: bold;
cursor: pointer;
transition: all 0.2s;
}
.key:hover {
background: #667eea;
color: white;
border-color: #667eea;
}
.key:active {
transform: scale(0.95);
}
</style>
</head>
<body>
<div class="container">
<h1>🔐 Door Lock</h1>
<div class="subtitle">ESP32 Security System</div>
<div class="stats">
<div class="stat">
<div class="stat-value">""" + str(successful_attempts) + """</div>
<div class="stat-label">Success</div>
</div>
<div class="stat">
<div class="stat-value">""" + str(failed_attempts) + """</div>
<div class="stat-label">Failed</div>
</div>
</div>
<form method="POST" action="/unlock">
<div class="form-group">
<label>Enter Password:</label>
<input type="password" id="password" name="password" maxlength="10" autofocus required>
</div>
<div class="keypad">
<button type="button" class="key" onclick="addDigit('1')">1</button>
<button type="button" class="key" onclick="addDigit('2')">2</button>
<button type="button" class="key" onclick="addDigit('3')">3</button>
<button type="button" class="key" onclick="addDigit('4')">4</button>
<button type="button" class="key" onclick="addDigit('5')">5</button>
<button type="button" class="key" onclick="addDigit('6')">6</button>
<button type="button" class="key" onclick="addDigit('7')">7</button>
<button type="button" class="key" onclick="addDigit('8')">8</button>
<button type="button" class="key" onclick="addDigit('9')">9</button>
<button type="button" class="key" onclick="clearPassword()">C</button>
<button type="button" class="key" onclick="addDigit('0')">0</button>
<button type="button" class="key" onclick="backspace()">⌫</button>
</div>
<button type="submit">UNLOCK</button>
</form>
<div id="message" class="message"></div>
</div>
<script>
function addDigit(digit) {
var input = document.getElementById('password');
if (input.value.length < 10) {
input.value += digit;
}
}
function clearPassword() {
document.getElementById('password').value = '';
}
function backspace() {
var input = document.getElementById('password');
input.value = input.value.slice(0, -1);
}
// Auto-refresh page every 5 seconds to update stats
setTimeout(function() {
if (!document.getElementById('message').style.display) {
location.reload();
}
}, 5000);
</script>
</body>
</html>"""
return html
# Setup WiFi Access Point
def setup_ap():
ap = network.WLAN(network.AP_IF)
ap.active(True)
ap.config(essid=AP_SSID, password=AP_PASSWORD)
while not ap.active():
time.sleep(0.1)
print("\n" + "="*50)
print("WiFi Access Point Created")
print("="*50)
print(f"SSID: {AP_SSID}")
print(f"Password: {AP_PASSWORD}")
print(f"IP Address: {ap.ifconfig()[0]}")
print("="*50)
print(f"\nConnect to WiFi and visit: http://{ap.ifconfig()[0]}")
print("="*50 + "\n")
return ap
# Web server
def start_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(5)
print("Web server started on port 80")
while True:
try:
cl, addr = s.accept()
print(f"Client connected from {addr}")
request = cl.recv(1024).decode()
# Handle POST request (password submission)
if "POST /unlock" in request:
# Extract password from POST data
try:
post_data = request.split('\r\n\r\n')[1]
password_input = post_data.split('password=')[1].split('&')[0]
print(f"Password attempt received")
if password_input == PASSWORD:
open_door()
response = """HTTP/1.1 200 OK
Content-Type: text/html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="refresh" content="3;url=/">
<style>
body { font-family: Arial; text-align: center; padding: 50px; background: #d4edda; }
h1 { color: #155724; font-size: 48px; }
p { font-size: 20px; color: #155724; }
</style>
</head>
<body>
<h1>✓ ACCESS GRANTED</h1>
<p>Door opening...</p>
<p>Redirecting in 3 seconds...</p>
</body>
</html>"""
else:
deny_access()
response = """HTTP/1.1 200 OK
Content-Type: text/html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="refresh" content="3;url=/">
<style>
body { font-family: Arial; text-align: center; padding: 50px; background: #f8d7da; }
h1 { color: #721c24; font-size: 48px; }
p { font-size: 20px; color: #721c24; }
</style>
</head>
<body>
<h1>✗ ACCESS DENIED</h1>
<p>Wrong password!</p>
<p>Redirecting in 3 seconds...</p>
</body>
</html>"""
except:
response = "HTTP/1.1 400 Bad Request\r\n\r\nError processing request"
# Handle GET request (show main page)
else:
response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n" + get_html()
cl.send(response)
cl.close()
except OSError as e:
print(f"Connection error: {e}")
cl.close()
# Main program
def main():
print("\n" + "="*50)
print("ESP32 Door Lock - Web Interface")
print("="*50)
# Test hardware
print("\nTesting hardware...")
led.on()
time.sleep(0.2)
led.off()
print("✓ LED OK")
buzzer.on()
time.sleep(0.1)
buzzer.off()
print("✓ Buzzer OK")
relay.on()
time.sleep(0.2)
relay.off()
print("✓ Relay OK")
# Setup WiFi AP
ap = setup_ap()
# Start web server
start_server()
if __name__ == "__main__":
main()