Esp32之网站站点的生成
生活中WiFi热点随处可见,为我们提供着各种各样的便利,但限于硬件的原因大多数热点只能提供网络连接及后台管理(管理员)的功能,即使是管理页也不能自定义修改页面内容。那么有没有一种办法既可以使用一种硬件生成热点的同时又可以为我们提供自定义的站点内容呢?当然有,这就是本篇要讲的如何使用诸如Esp32、Esp8266等硬件模块生成WiFi热点并能搭建一个自定义的小型网站。
开发硬件:Esp32
烧录固件:MicroPython
开发语言:Python 3
开发环境:Mac、Windows、Linux
开发工具:Xpython(自己写的IDE,并未公开,可以用其他IDE代替)
1. 烧录固件
固件烧录可以参考MicroPython(Esp32)
2. 站点后台
新建wifimr.py
#codeing:utf-8
import network
import socket
import ure
import time
ap_ssid = "Kuture-Esp32"
ap_password = ""
ap_authmode = 1 # WPA2
NETWORK_PROFILES = 'wifi.dat'
wlan_ap = network.WLAN(network.AP_IF)
wlan_sta = network.WLAN(network.STA_IF)
server_socket = None
def get_connection():
if wlan_sta.isconnected():
return wlan_sta
connected = False
try:
time.sleep(3)
if wlan_sta.isconnected():
return wlan_sta
profiles = read_profiles()
wlan_sta.active(True)
networks = wlan_sta.scan()
AUTHMODE = {0: "open", 1: "WEP", 2: "WPA-PSK", 3: "WPA2-PSK", 4: "WPA/WPA2-PSK"}
for ssid, bssid, channel, rssi, authmode, hidden in sorted(networks, key=lambda x: x[3], reverse=True):
ssid = ssid.decode('utf-8')
encrypted = authmode > 0
print("ssid: %s chan: %d rssi: %d authmode: %s" % (ssid, channel, rssi, AUTHMODE.get(authmode, '?')))
if encrypted:
if ssid in profiles:
password = profiles[ssid]
connected = do_connect(ssid, password)
else:
print("skipping unknown encrypted network")
else:
connected = do_connect(ssid, None)
if connected:
break
except OSError as e:
print("exception", str(e))
if not connected:
connected = start()
return wlan_sta if connected else None
def read_profiles():
with open(NETWORK_PROFILES) as f:
lines = f.readlines()
profiles = {}
for line in lines:
ssid, password = line.strip("\n").split(";")
profiles[ssid] = password
return profiles
def write_profiles(profiles):
lines = []
for ssid, password in profiles.items():
lines.append("%s;%s\n" % (ssid, password))
with open(NETWORK_PROFILES, "w") as f:
f.write(''.join(lines))
def do_connect(ssid, password):
wlan_sta.active(True)
if wlan_sta.isconnected():
return None
print('Trying to connect to %s...' % ssid)
wlan_sta.connect(ssid, password)
for retry in range(100):
connected = wlan_sta.isconnected()
if connected:
break
time.sleep(0.1)
print('.', end='')
if connected:
print('\nConnected. Network config: ', wlan_sta.ifconfig())
else:
print('\nFailed. Not Connected to: ' + ssid)
return connected
def send_header(client, status_code=200, content_length=None ):
client.sendall("HTTP/1.0 {} OK\r\n".format(status_code))
client.sendall("Content-Type: text/html\r\n")
if content_length is not None:
client.sendall("Content-Length: {}\r\n".format(content_length))
client.sendall("\r\n")
def send_response(client, payload, status_code=200):
content_length = len(payload)
send_header(client, status_code, content_length)
if content_length > 0:
client.sendall(payload)
client.close()
def handle_root(client):
wlan_sta.active(True)
ssids = sorted(ssid.decode('utf-8') for ssid, *_ in wlan_sta.scan())
send_header(client)
client.sendall("""\
<html>
<head>
<meta charset="UTF-8">
</head>
<h1 style="color: #5e9ca0; text-align: center;">
<span style="color: #ff0000;">
WiFi-热点
</span>
</h1>
<form action="configure" method="post">
<table style="margin-left: auto; margin-right: auto;">
<tbody>
""")
while len(ssids):
ssid = ssids.pop(0)
client.sendall("""\
<tr>
<td colspan="2">
<input type="radio" name="ssid" value="{0}" />{0}
</td>
</tr>
""".format(ssid))
client.sendall("""\
<tr>
<td>密码:</td>
<td><input name="password" type="password" /></td>
</tr>
</tbody>
</table>
<p style="text-align: center;">
<input type="submit" value="连接" />
</p>
</form>
<p> </p>
<hr />
<h5>
<span style="color: #ff0000;">
热点名称与密码信息将会保存在ESP模块的
"%(filename)s"文件中,请谨慎处理!
</span>
</h5>
<hr />
<h2 style="color: #2e6c80;">
Info:
</h2>
<ul>
<li>
CopyRight© AustinKuture
</li>
<li>
我的博客地址<a href="http://www.kuture.com.cn"
target="_blank" rel="noopener">Kuture's Blog</a>.
</li>
</ul>
</html>
""" % dict(filename=NETWORK_PROFILES))
client.close()
def handle_configure(client, request):
match = ure.search("ssid=([^&]*)&password=(.*)", request)
if match is None:
send_response(client, "Parameters not found", status_code=400)
return False
try:
ssid = match.group(1).decode("utf-8").replace("%3F", "?").replace("%21", "!")
password = match.group(2).decode("utf-8").replace("%3F", "?").replace("%21", "!")
except Exception:
ssid = match.group(1).replace("%3F", "?").replace("%21", "!")
password = match.group(2).replace("%3F", "?").replace("%21", "!")
if len(ssid) == 0:
send_response(client, "SSID must be provided", status_code=400)
return False
if do_connect(ssid, password):
response = """\
<html>
<center>
<br><br>
<h1 style="color: #5e9ca0; text-align: center;">
<span style="color: #ff0000;">
ESP successfully connected to WiFi network %(ssid)s.
</span>
</h1>
<br><br>
</center>
</html>
""" % dict(ssid=ssid)
send_response(client, response)
try:
profiles = read_profiles()
except OSError:
profiles = {}
profiles[ssid] = password
write_profiles(profiles)
time.sleep(5)
return True
else:
response = """\
<html>
<center>
<h1 style="color: #5e9ca0; text-align: center;">
<span style="color: #ff0000;">
ESP could not connect to WiFi network %(ssid)s.
</span>
</h1>
<br><br>
<form>
<input type="button" value="Go back!" onclick="history.back()"></input>
</form>
</center>
</html>
""" % dict(ssid=ssid)
send_response(client, response)
return False
def handle_not_found(client, url):
send_response(client, "Path not found: {}".format(url), status_code=404)
def stop():
global server_socket
if server_socket:
server_socket.close()
server_socket = None
def start(port=80):
global server_socket
addr = socket.getaddrinfo('0.0.0.0', port)[0][-1]
stop()
wlan_sta.active(True)
wlan_ap.active(True)
wlan_ap.config(essid=ap_ssid, password=ap_password, authmode=ap_authmode)
server_socket = socket.socket()
server_socket.bind(addr)
server_socket.listen(1)
print('Connect to WiFi ssid ' + ap_ssid + ', default password: ' + ap_password)
print('and access the ESP via your favorite web browser at 192.168.4.1.')
print('Listening on:', addr)
while True:
if wlan_sta.isconnected():
return True
client, addr = server_socket.accept()
print('client connected from', addr)
try:
client.settimeout(5.0)
request = b""
try:
while "\r\n\r\n" not in request:
request += client.recv(512)
except OSError:
pass
print("Request is: {}".format(request))
if "HTTP" not in request:
try:
url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).decode("utf-8").rstrip("/")
except Exception:
url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).rstrip("/")
print("URL is {}".format(url))
if url == "":
handle_root(client)
elif url == "configure":
handle_configure(client, request)
else:
handle_not_found(client, url)
finally:
client.close()
新建main.py 文件,Esp32启动时会自动加载main.py
import wifimgr
def run():
wlan = wifimgr.get_connection()
if wlan is None:
print("网络连接初始化失败")
while True:
pass
print("ESP OK")
run()
3. 刷入文件
程序编写完成后,保存并刷入硬件
4. 连接热点
热点名称、密码、加密方式可以在wifimr.py进行修改,启动后连接热点
5. 访问站点
默认IP为192.168.4.1 默认端口为80 打开浏览器访问:192.168.4.1