MicroPython 开发ESP32应用教程 之 WIFI配网 Web页面配网 softAP 配网 web页面控制WS2812灯珠

一、ESP32 WiFi模式

首先我们需要了解ESP32的WIFI模式。

一)、基础模式定义

  1. STA模式(Station)

    • 设备作为客户端连接现有WiFi网络(如路由器),获取IP地址后可与互联网通信
    • 适用场景:需要访问外网的物联网设备(如气象站上传数据到云端)
  2. AP模式(Access Point)

    • 设备自建WiFi热点供其他设备连接,形成本地网络
    • 默认IP地址为192.168.4.1,客户端分配192.168.4.x段地址
    • 适用场景:无路由器的局域网控制(如智能家居设备本地配置)
  3. STA+AP混合模式

    • 同时运行STA和AP接口,实现内外网双通道通信
    • 典型应用:物联网网关(本地控制+云端同步)

二)、模式特性对比

特性STA模式AP模式
网络角色客户端服务器
IP获取方式DHCP动态分配固定IP(手动设置)
最大连接数仅连接1个AP支持最多4个客户端
典型功耗低(仅接收数据)较高(需广播热点)
代码初始化network.WLAN(network.STA_IF)network.WLAN(network.AP_IF)

三)、MicroPython实现代码

        1、STA模式

import network
sta = network.WLAN(network.STA_IF)
sta.active(True)  # 激活STA接口
sta.connect("HomeWiFi", "password123")  # 连接目标网络
print("STA IP:", sta.ifconfig()[0])  # 输出动态分配的IP

        2、AP模式

ap = network.WLAN(network.AP_IF)
ap.config(
    essid="ESP32-AP", 
    password="ap_password",
    authmode=network.AUTH_WPA2_PSK  # 强制加密
)
ap.active(True)  # 激活AP接口
ap.ifconfig(('192.168.4.1', '255.255.255.0', '192.168.4.1', '8.8.8.8'))  # 固定IP

 authmode可设置为:

  • network.AUTH_OPEN(无密码)
  • network.AUTH_WEP(已过时)
  • network.AUTH_WPA_PSK 或 AUTH_WPA2_PSK(推荐)

默认情况下,AP会自动分配IP(如192.168.4.x),也可通过ap.ifconfig()自定义。

         3、混合模式

# 同时激活两个接口
sta.active(True)
ap.active(True)
# 分别配置参数(互不影响)
sta.connect("OfficeWiFi", "company123")
ap.config(essid="ESP32-Gateway", password="gateway888")

混合模式下需确保STA和AP使用不同信道(避免干扰) ,后面我们会详细讲解如何设置信道。 

四)、模式选择指南

  1. STA模式

    • 需访问互联网资源
    • 设备部署在已有WiFi覆盖环境
  2. AP模式

    • 无可用路由器时的设备直连场景
    • 需要本地快速配置参数(如Web配网界面)
  3. 混合模式

    • 需要同时支持本地控制与云端同步
    • 实现网络数据中继(如ESP32作为IoT网关)

 二、WIFI信道设置

        1、‌STA模式指定信道连接

import network
sta = network.WLAN(network.STA_IF)
sta.active(True)
sta.connect("HomeWiFi", "password", bssid=b'\xaa\xbb\xcc\xdd\xee\xff', channel=6)  # 指定MAC和信道

        2、AP模式固定信道

ap = network.WLAN(network.AP_IF)
ap.config(essid="ESP_AP", channel=11, authmode=network.AUTH_WPA2_PSK)  # 强制信道11
ap.active(True)

        3、动态信道选择

import network, time
sta = network.WLAN(network.STA_IF)
ap = network.WLAN(network.AP_IF)

def channel_optimize():
    sta.active(True)
    scan_res = sta.scan()  # 执行全信道扫描
    channel_load = {ch:0 for ch in range(1,14)}
    for ap_info in scan_res:
        channel_load[ap_info[2]] += 1
    best_ch = min(channel_load, key=channel_load.get)  # 选择负载最低信道
    ap.config(channel=best_ch)  # AP切换至最优信道
    print(f"AP切换至信道{best_ch}")

三、修改 AP 模式 Web 页面

这部分内容比较复杂,我们这里只是简单的用两个实例阐述ESP32的AP模式下web页面的应用。

HTML 页面设计与嵌入

  • 编写包含动态交互元素的 HTML 文件,如 GPIO 控制按钮或传感器数据显示模块
  • 将 HTML 代码直接嵌入 MicroPython 代码中,或通过文件系统托管静态资源(需使用 SPIFFS 或外部存储)

一)、GPIO 控制功能

以下是基于 MicroPython 的 AP 模式网页控制示例,整合了 HTML 页面交互与 GPIO 控制功能: 

1、基础代码框架

import network
import socket
from machine import Pin
from neopixel import NeoPixel
from ws2812 import WS2812,WS2812_POWEROFF,WS2812_POWERON

# AP 模式配置
ap = network.WLAN(network.AP_IF)
ap.active(True)
ap.config(essid='MyESP32AP', password='mypassword123', authmode=3)  # WPA2 加密
print("AP IP:", ap.ifconfig()[0])  # 输出 IP (通常为 192.168.4.1)
# GPIO 初始化
led_state = "OFF"
wsled = WS2812(Pin(48, Pin.OUT),1,3,1)
wsled.ws2812_Power(WS2812_POWEROFF)

关于WS2812灯珠控制,请参考:MicroPython 开发基于ESP32S3控制ws2812灯带的程序 

 2、HTTP 服务器与页面处理

html ="""
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    body {font-family: Arial; text-align: center; margin-top: 50px;}
    .btn {padding: 10px 20px; font-size: 16px;}
  </style>
</head>
<body>
  <h1>ESP32 LED 控制</h1>
  <p>当前状态: <strong>{state}</strong></p>
  <form action="/submit">
    <button class="btn" name="cmd" value="on">打开LED</button>
    <button class="btn" name="cmd" value="off">关闭LED</button>
  </form>
</body>
</html>
"""
#以上是我们通常的web页面代码
# 🔄 请求处理逻辑
def handle_request(client_sock):
    global led_state
    request = client_sock.recv(1024).decode()
    
    if "GET /submit?cmd=on" in request:
        # 解析控制指令
#        print (request)
        wsled.ws2812_Power(1)
        led_state = "ON"
    elif 'GET /submit?cmd=off' in request:
        wsled.ws2812_Power(0)
        led_state = "OFF"
    
    # 返回动态页面
    response = html.replace("{state}", led_state) #动态修改LED灯的状态
    client_sock.send("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n")
    client_sock.send(response)     #更新web页面
    client_sock.close()    

3、启动服务

# 启动 HTTP 服务器
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))  # 监听 80 端口
s.listen(3)        #设置最大监听

print("等待客户端连接...")
while True:
    client, addr = s.accept()
#    print('客户端来自:', addr)
    handle_request(client)

 完整代码请参考:Micropythonforesp32AP,web页面控制WS2812灯珠资源-优快云文库

 二)、web页面配网

 1、配网页面代码:

# Captive portal的HTML页面
HTML = """
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
    <title>ESP32 WiFi Setup</title>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            font-family: Arial, sans-serif;
        }
        form {
            display: flex;
            flex-direction: column;
            height: 100vh;
            width:600px;

            align-items: center;
            background-color: #f0f0f0;
            padding: 20px;
            border-radius: 5px;
        }
        label, input {
            font-size: 24px;
            font-weight: 700;
            display: flex;
            flex-direction: column;
            height: 60px;
            width:400px;
            margin-bottom: 10px;
        }
    </style>
</head>
<body>
    <form action="/connect" method="post">
        <label for="ssid">SSID:</label>
        <input type="text" id="ssid" name="ssid">
        <label for="password">Password:</label>
        <input type="password" id="password" name="password">
        <input type="submit" value="Connect" style="
              text-align: center;  /* 水平居中 */
              width: 150px;       /* 自定义宽度 */
              padding: 8px;       /* 对称内边距 */
            ">
    </form>
</body>
</html>

"""

2、配网成功返回页面


HTMLSuccess = """
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
    <title>ESP32 WiFi Setup</title>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            font-family: Arial, sans-serif;
        }
    </style>
</head>
<body>
<hl>已连接到WiFi。正在关闭AP模式……</h1>
</body>
</html>

3、配网失败返回页面


"""
HTMLFail = """
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
    <title>ESP32 WiFi Setup</title>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            font-family: Arial, sans-serif;
        }
    </style>
</head>
<body>
<hl>连接失败,请重试……</h1>
</body>
</html>

"""

4、完整代码

import network
import socket,json
from machine import Pin, Timer
import time
# 配置热点模式
AP_SSID = '913IOT'
AP_PASSWORD = '913AIRobot'
# Captive portal的HTML页面
HTML = """
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
    <title>ESP32 WiFi Setup</title>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            font-family: Arial, sans-serif;
        }
        form {
            display: flex;
            flex-direction: column;
            height: 100vh;
            width:600px;

            align-items: center;
            background-color: #f0f0f0;
            padding: 20px;
            border-radius: 5px;
        }
        label, input {
            font-size: 24px;
            font-weight: 700;
            display: flex;
            flex-direction: column;
            height: 60px;
            width:400px;
            margin-bottom: 10px;
        }
    </style>
</head>
<body>
    <form action="/connect" method="post">
        <label for="ssid">SSID:</label>
        <input type="text" id="ssid" name="ssid">
        <label for="password">Password:</label>
        <input type="password" id="password" name="password">
        <input type="submit" value="Connect" style="
              text-align: center;  /* 水平居中 */
              width: 150px;       /* 自定义宽度 */
              padding: 8px;       /* 对称内边距 */
            ">
    </form>
</body>
</html>

"""

HTMLSuccess = """
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
    <title>ESP32 WiFi Setup</title>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            font-family: Arial, sans-serif;
        }
    </style>
</head>
<body>
<hl>已连接到WiFi。正在关闭AP模式……</h1>
</body>
</html>

"""
HTMLFail = """
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
    <title>ESP32 WiFi Setup</title>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            font-family: Arial, sans-serif;
        }
    </style>
</head>
<body>
<hl>连接失败,请重试……</h1>
</body>
</html>

"""

wifi_conn = False
wifi_config = None
def start_ap():
    ap = network.WLAN(network.AP_IF)
    ap.active(False)
    ap.active(True)
    ap.config(essid=AP_SSID, password=AP_PASSWORD)
#     print("AP模式已启用: SSID '{}' 密码 '{}'".format(AP_SSID, AP_PASSWORD))
    return ap

# 从 JSON 文件中读取 WiFi 信息
def read_wifi_config():
    try:
        with open("wifi_config.json", 'r') as file:
            data = json.load(file)
            if data['ssid'] == "" and  data['password'] == "":
                return False
            return data['ssid'], data['password']
    except Exception as e:
        print(e)
        return False
    
def connect_wifi(ssid, password):
    global wifi_conn,wifi_config
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    try:
        if not wlan.isconnected():           
            wlan.connect(ssid, password)
            max_wait = 10
            while max_wait > 0:
                if wlan.isconnected():
                    break
                max_wait -= 1
#                print("等待连接……",max_wait)
                time.sleep(1)
            if not wlan.isconnected():
                return False
            else:
                wifi_conn = True
                wifi_config=wlan.ifconfig()
                server_socket.close()
                return True
    except Exception as e:
        return "wifi not find"
    
def handle_connection(client, addr, ap):
    global wifi_config
#     print('客户端连接来自', addr)
    request = client.recv(1024).decode()
#     print('请求内容:', request)

    if 'POST /connect' in request:
        # 从请求中提取SSID和密码
        ssid_start = request.find('ssid=') + 5
        ssid_end = request.find('&', ssid_start)
        password_start = request.find('password=') + 9
        ssid = request[ssid_start:ssid_end]
        password = request[password_start:]
#         print('尝试连接WiFi SSID:', ssid)
        wifi_data = {
            'ssid': ssid,
            'password': password
        }

        # 重置并连接到新WiFi网络
        if connect_wifi(ssid, password):
            response = HTMLSuccess
            client.send(response)
            client.close()
            # 关闭AP模式
#             print('关闭AP模式...')
            time.sleep(2)
            ap.active(False)
            with open("wifi_config.json", 'w') as file:
                json.dump(wifi_data, file)
            return True  # 表示连接成功
        else:
            response = HTMLFail
            client.send(response)
    else:
        response = 'HTTP/1.1 200 OK\nContent-Type: text/html\n\n' + HTML
        client.send(response)
    client.close()
    return False  # 表示连接失败

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connected = False
def conn_wifi():
    global connected 
    ap = start_ap()
    server_socket.bind(('0.0.0.0', 80))
    server_socket.listen(1)
    print('Web服务器已启动,等待客户端连接...')
    try:
        # 主程序部分
        while not connected:
            client_socket, addr = server_socket.accept()
            connected = handle_connection(client_socket, addr, ap)
    except KeyboardInterrupt:
        print("Exiting program...")
    finally:
        # 确保关闭服务器套接字
        server_socket.close()
        
#        led_pin.value(0)
def main():
    global wifi_conn,wifi_config
    while True:
        if read_wifi_config() ==False:
            conn_wifi()
        else:
            ssid,password = read_wifi_config()
            if wifi_conn != True:
                connect_wifi(ssid,password)
        if wifi_conn == True:
            return True
if __name__ == "__main__":
    print(main())

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

永远的元子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值