MicroPython: Wi-Fi Менеджер для ESP32 (совместимо с ESP8266)
В этом руководстве мы покажем, как использовать Wi-Fi Manager на ESP32 с прошивкой MicroPython. Wi-Fi Manager позволяет подключать ESP32 к различным точкам доступа (разным сетям) без необходимости жёстко прописывать учётные данные и загружать новый код на плату.
Это руководство также полностью совместимо с платой ESP8266. Однако, поскольку библиотека Wi-Fi Manager использует довольно много памяти, вы можете столкнуться с ошибкой памяти при сохранении скрипта на плату. По нашему опыту, перезагрузка платы после загрузки скрипта устраняет ошибку, и проект начинает работать после этого. Мы рекомендуем использовать ESP32, но вы также можете продолжить это руководство, используя плату ESP8266.
Как работает Wi-Fi Manager
С Wi-Fi Manager вам больше не нужно жёстко прописывать учётные данные сети (SSID и пароль). ESP32 настроит точку доступа (Access Point), которую вы сможете использовать для настройки сетевых учётных данных, или автоматически подключится к известной сохранённой сети.
Вот как работает процесс:
При первой загрузке ESP32 настраивается как точка доступа (Access Point);
Вы можете подключиться к этой точке доступа, установив соединение с сетью WiFiManager и перейдя на IP-адрес 192.168.4.1;
Откроется веб-страница, позволяющая выбрать и настроить сеть;
ESP32 сохраняет учётные данные сети, чтобы позже подключиться к этой сети (режим Station);
После установки нового SSID и пароля ESP32 перезагружается, переходит в режим Station и пытается подключиться к ранее сохранённой сети;
Если соединение установлено, процесс завершён успешно. В противном случае ESP32 снова будет настроен как точка доступа для ввода новых учётных данных сети.
Для настройки Wi-Fi Manager на ESP32 с использованием MicroPython мы будем использовать библиотеку WiFiManager от tayfunulu. На странице библиотеки на GitHub вы можете найти следующую диаграмму, которая показывает обзор того, как всё работает.
Необходимые условия
Для выполнения этого руководства вам нужна прошивка MicroPython, установленная на вашу плату ESP. Вам также нужна IDE для написания и загрузки кода на плату. Мы предлагаем использовать Thonny IDE или uPyCraft IDE:
Thonny IDE:
uPyCraft IDE:
Необходимые компоненты
Для этого руководства вам понадобится ESP32 (или плата ESP8266):
Библиотека WiFiManager для MicroPython
Библиотека для настройки Wi-Fi Manager на ESP32 не входит в стандартную библиотеку MicroPython по умолчанию. Поэтому вам нужно загрузить следующую библиотеку на вашу плату ESP (сохраните её с точным именем wifimgr.py).
import network
import socket
import ure
import time
ap_ssid = "WifiManager"
ap_password = "tayfunulu"
ap_authmode = 3 # 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():
"""return a working WLAN(STA_IF) instance or None"""
# First check if there already is any connection:
if wlan_sta.isconnected():
return wlan_sta
connected = False
try:
# ESP connecting to WiFi takes time, wait a bit and try again:
time.sleep(3)
if wlan_sta.isconnected():
return wlan_sta
# Read known network profiles from file
profiles = read_profiles()
# Search WiFis in range
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: # open
connected = do_connect(ssid, None)
if connected:
break
except OSError as e:
print("exception", str(e))
# start web server for connection manager:
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(200):
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>
<h1 style="color: #5e9ca0; text-align: center;">
<span style="color: #ff0000;">
Wi-Fi Client Setup
</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>Password:</td>
<td><input name="password" type="password" /></td>
</tr>
</tbody>
</table>
<p style="text-align: center;">
<input type="submit" value="Submit" />
</p>
</form>
<p> </p>
<hr />
<h5>
<span style="color: #ff0000;">
Your ssid and password information will be saved into the
"%(filename)s" file in your ESP module for future usage.
Be careful about security!
</span>
</h5>
<hr />
<h2 style="color: #2e6c80;">
Some useful infos:
</h2>
<ul>
<li>
Original code from <a href="https://github.com/cpopp/MicroPythonSamples"
target="_blank" rel="noopener">cpopp/MicroPythonSamples</a>.
</li>
<li>
This code available at <a href="https://github.com/tayfunulu/WiFiManager"
target="_blank" rel="noopener">tayfunulu/WiFiManager</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
# version 1.9 compatibility
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)
time.sleep(1)
wlan_ap.active(False)
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)
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():
wlan_ap.active(False)
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
# Handle form data from Safari on macOS and iOS; it sends \r\n\r\nssid=<ssid>&password=<password>
try:
request += client.recv(1024)
print("Received form data after \\r\\n\\r\\n(i.e. from Safari on macOS or iOS)")
except OSError:
pass
print("Request is: {}".format(request))
if "HTTP" not in request: # skip invalid requests
continue
# version 1.9 compatibility
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()
Следуйте инструкциям для IDE, которую вы используете:
Загрузка библиотеки WiFiManager с помощью uPyCraft IDE
Загрузка библиотеки WiFiManager с помощью Thonny IDE
A. Загрузка библиотеки WiFiManager с помощью uPyCraft IDE
В этом разделе показано, как загрузить библиотеку с помощью uPyCraft IDE. Если вы используете Thonny IDE, читайте следующий раздел.
1. Создайте новый файл, нажав кнопку New File.
2. Скопируйте код библиотеки WiFiManager в этот файл. Код библиотеки WiFiManager можно скопировать отсюда.
3. После копирования кода сохраните файл, нажав кнопку Save.
4. Назовите этот новый файл «wifimgr.py» и нажмите ok.
5. Нажмите кнопку Download and Run.
Файл должен быть сохранён в папке устройства с именем «wifimgr.py», как выделено на следующем рисунке.
Теперь вы можете использовать функционал библиотеки в своём коде, импортировав библиотеку.
B. Загрузка библиотеки WiFiManager с помощью Thonny IDE
Если вы используете Thonny IDE, выполните следующие шаги:
1. Скопируйте код библиотеки в новый файл. Код библиотеки WiFiManager можно скопировать отсюда.
2. Сохраните этот файл как wifimgr.py.
3. Перейдите в Device > Upload current script with the current name.
Вот и всё. Библиотека загружена на вашу плату. Чтобы убедиться, что загрузка прошла успешно, в Shell введите:
%lsdevice
Команда должна вернуть файлы, сохранённые на вашей плате. Одним из них должен быть файл wifimgr.py.
После загрузки библиотеки на вашу плату вы можете использовать функционал библиотеки в своём коде, импортировав библиотеку.
Код – Настройка Wi-Fi Manager на ESP32
Следующий код реализует Wi-Fi Manager на ESP32. Мы добавим возможности Wi-Fi Manager к предыдущему проекту веб-сервера на MicroPython. К концу руководства вы сможете реализовать Wi-Fi Manager в своих собственных проектах.
Создайте файл main.py и скопируйте следующий код.
# Complete project details at https://RandomNerdTutorials.com
import wifimgr
from time import sleep
import machine
try:
import usocket as socket
except:
import socket
led = machine.Pin(2, machine.Pin.OUT)
wlan = wifimgr.get_connection()
if wlan is None:
print("Could not initialize the network connection.")
while True:
pass # you shall not pass :D
# Main Code goes here, wlan is a working network.WLAN(STA_IF) instance.
print("ESP OK")
def web_page():
if led.value() == 1:
gpio_state="ON"
else:
gpio_state="OFF"
html = """<html><head> <title>ESP Web Server</title> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,"> <style>html{font-family: Helvetica; display:inline-block; margin: 0px auto; text-align: center;}
h1{color: #0F3376; padding: 2vh;}p{font-size: 1.5rem;}.button{display: inline-block; background-color: #e7bd3b; border: none;
border-radius: 4px; color: white; padding: 16px 40px; text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}
.button2{background-color: #4286f4;}</style></head><body> <h1>ESP Web Server</h1>
<p>GPIO state: <strong>""" + gpio_state + """</strong></p><p><a href="/?led=on"><button class="button">ON</button></a></p>
<p><a href="/?led=off"><button class="button button2">OFF</button></a></p></body></html>"""
return html
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', 80))
s.listen(5)
except OSError as e:
machine.reset()
while True:
try:
if gc.mem_free() < 102000:
gc.collect()
conn, addr = s.accept()
conn.settimeout(3.0)
print('Got a connection from %s' % str(addr))
request = conn.recv(1024)
conn.settimeout(None)
request = str(request)
print('Content = %s' % request)
led_on = request.find('/?led=on')
led_off = request.find('/?led=off')
if led_on == 6:
print('LED ON')
led.value(1)
if led_off == 6:
print('LED OFF')
led.value(0)
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()
except OSError as e:
conn.close()
print('Connection closed')
Как работает код
Этот код основан на предыдущем проекте веб-сервера ESP32/ESP8266 на MicroPython. Мы лишь внесли несколько изменений для добавления Wi-Fi Manager.
Чтобы добавить Wi-Fi Manager, нужно импортировать библиотеку, которую вы ранее загрузили на плату.
import wifimgr
Следующие строки кода обрабатывают Wi-Fi Manager за вас:
wlan = wifimgr.get_connection()
if wlan is None:
print("Could not initialize the network connection.")
while True:
pass # you shall not pass :D
wlan – это рабочий экземпляр network.WLAN(STA_IF), который инициализируется библиотекой. Поэтому вам не нужно включать код для настройки ESP32 в режим Station.
Когда ESP32 впервые настраивается как точка доступа, он оставляет открытый сокет, что приводит к ошибке и аварийному завершению работы ESP32. Чтобы этого не произошло, мы инициализируем и привязываем сокет внутри конструкции try и except.
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', 80))
s.listen(5)
except OSError as e:
machine.reset()
В случае, если остался открытый сокет, мы получим OS error и сбросим ESP32 с помощью machine.reset(). Это «забудет» открытый сокет.
Когда код запускается второй раз, учётные данные сети уже сохранены, поэтому ESP32 не настраивается как точка доступа, проблем с открытыми сокетами нет, и код выполняется без ошибок.
Тестирование WiFiManager
Загрузите файл main.py на ESP32. После этого нажмите кнопку RST (EN) на плате ESP32, чтобы запустить программу.
В Python Shell вы должны увидеть похожее сообщение.
Это означает, что ESP32 был успешно настроен как точка доступа. Теперь вы можете подключиться к этой точке доступа, чтобы выбрать свою сеть и ввести учётные данные. Для этого выполните следующие шаги.
На вашем компьютере или смартфоне откройте настройки Wi-Fi и подключитесь к сети WifiManager.
Пароль – tayfunulu. Вы можете изменить SSID и пароль по умолчанию в коде библиотеки.
После успешного подключения к сети WiFiManager откройте браузер и введите 192.168.4.1. Должна загрузиться следующая страница:
Выберите свою сеть, введите пароль и нажмите Submit. Через несколько секунд вы должны получить сообщение об успехе.
Это сообщение означает, что ваш ESP32 настроен как Wi-Fi станция и подключён к вашей локальной сети. Теперь, чтобы получить доступ к ESP32, вернитесь в настройки Wi-Fi на компьютере или смартфоне и подключитесь к своей сети.
В Python Shell должен быть напечатан IP-адрес ESP32.
Примечание: в реальных условиях у вас, вероятно, не будет доступа к Python Shell. В такой ситуации мы рекомендуем выводить IP-адрес на OLED-дисплей.
Откройте браузер и введите этот IP-адрес. Вы получите доступ к следующему веб-серверу и сможете управлять GPIO ESP32.
Заключение
С библиотекой WiFiManager вам больше не нужно жёстко прописывать учётные данные сети. ESP32 настраивает точку доступа, которая отображает доступные сети Wi-Fi. Вам нужно лишь выбрать свою сеть и ввести пароль, чтобы настроить ESP32 как Wi-Fi станцию.
Надеемся, что это руководство было полезным. Вам также могут быть интересны: