Всем привет! Сегодня разберем как написать wifi manager для esp32 с нуля и пользоваться им.
Если вы делали проекты на esp, то рано или поздно вы столкнетесь с проблемой, когда прописанные в скетче константы имени и пароля от wifi сети необходимо заменить, не имея доступа к скетчу. Например, вы сделали проект умной лампы на esp32 и продаете её для домашнего пользования. Чтобы пользователь мог управлять лампой из своей сети он должен указать свои реквизиты wifi. И тут нам на помощь приходит Wifi Manager. Как он работает?
При первом включении, esp раздает точку доступа, с заранее известным именем и паролем. Мы подключаемся к этой точке доступа и попадаем на локальную страничку, где нам предлагают сконфигурировать wifi. Там мы вводим пароль от своей сети и esp перезагрузившись подключается к нашему wifi. Если esp не может найти заданную сеть, то она опять раздает точку доступа. Таким образом мы можем подключать наше умное устройство к любому wifi не изменяя константы непосредственно в скетче.
Итак, перейдем к практике.
Создадим два html файла: index.html и conf.html. Первый будет отображаться после успешного подключения к wifi, а второй будет содержать форму настройки сети.
Хранить эти файлы мы будем в SPIFFS, если вы не знакомы с этой системой, рекомендую прочесть нашу предыдущую статью.
В файл index.html поместите содержимое:
<!DOCTYPE html> <html lang="ru"> <head> <title>MET ONLINE</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="bootstrap.min.css"> </head> <body class="bg-dark text-light"> <div class="position-absolute top-50 start-50 translate-middle"> <div class="container-fluid"> <div class="card text-light bg-success mb-3"> <div class="card-header"><h2><center><b>Успешно подключено!</b></center></h2></div> </div> </div> </div> </body> </html>
Далее файл с формой конфигурации conf.html:
<!DOCTYPE html> <html lang="ru"> <head> <title>Wi-Fi Manager</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="bootstrap.min.css"> </head> <body class="bg-dark text-light"> <div class="position-absolute top-50 start-50 translate-middle"> <div class="container-fluid"> <div class="card text-dark bg-info mb-3"> <div class="card-header"><h2><center><b>ESP32 Wi-Fi Manager</b></center></h2></div> <div class="card-body"> <h5 class="card-title"> <form method="POST"> <div class="input-group mb-3"> <span class="input-group-text">SSID</span> <input type="text" class="form-control" name="ssid" aria-describedby="inputGroup-sizing-default"> </div> <div class="input-group mb-3"> <span class="input-group-text">PSK</span> <input type="password" name="pass" class="form-control"> </div> <div class="input-group mb-3"> <span class="input-group-text">IP Addr</span> <input type="text" class="form-control" name="ip" aria-describedby="inputGroup-sizing-default" placeholder="192.168.0.100"> </div> <div class="input-group mb-3"> <span class="input-group-text">Gateway Addr</span> <input type="text" class="form-control" name="gateway" aria-describedby="inputGroup-sizing-default" placeholder="192.168.0.1"> </div> <center><button type="submit" class="btn btn-success">Submit</button></center> </form> </h5> </div> </div> </div> </div> </body> </html>
Сюда вы можете добавить свои поля для доп настройки, например соединения с mqqt-сервером.
Напишем скетч:
Для понимания посмотрим на схему:
Подключаем необходимые библиотеки:
#include <Arduino.h> #include <WiFi.h> #include <ESPAsyncWebServer.h> #include <AsyncTCP.h> #include "SPIFFS.h"
Для хранения имени, пароля, Ip адреса и адреса роутера мы создадим 4 файла в spiffs. И будем записывать в них пришедшие из формы данные.
//Пути файлов const char* ssidPath = "/ssid.txt"; const char* passPath = "/pass.txt"; const char* ipPath = "/ip.txt"; const char* gatewayPath = "/gateway.txt";
Для хранения данных с формы создадим переменные:
const char* PARAM_INPUT_1 = "ssid"; const char* PARAM_INPUT_2 = "pass"; const char* PARAM_INPUT_3 = "ip"; const char* PARAM_INPUT_4 = "gateway"; String ssid; String pass; String ip; String gateway;
Настраиваем объекты:
AsyncWebServer server(80); //Инициализируем сервер IPAddress localIP; IPAddress localGateway; IPAddress subnet(255, 255, 0, 0); //Маска
Переменные:
unsigned long previousMillis = 0; const long interval = 10000; //Сколько ждем перед открытием портала const int ledPin = 2; String ledState;
Первоначальная настройка (setup)
void setup() { Serial.begin(115200); //Открываем порт initSPIFFS(); //Инициализируем spiffs pinMode(ledPin, OUTPUT); //Светодиод на выход digitalWrite(ledPin, LOW); //Загружаем в переменные данные из SPIFFS ssid = readFile(SPIFFS, ssidPath); pass = readFile(SPIFFS, passPath); ip = readFile(SPIFFS, ipPath); gateway = readFile (SPIFFS, gatewayPath); //Если есть wifi if(initWiFi()) { // Открываем успешную страницу index.html server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(SPIFFS, "/index.html", "text/html"); }); server.serveStatic("/", SPIFFS, "/"); server.on("/bootstrap.min.css", HTTP_GET, [](AsyncWebServerRequest *request){ //Подключаем стили request->send(SPIFFS, "/bootstrap.min.css", "text/css"); }); server.begin(); } //Иначе else { Serial.println("Setting AP (Access Point)"); //Раздаем точку доступа WiFi.softAP("ESP-CONNECT", NULL); // Создаем открытую точку доступа с именем ESP-CONNECT IPAddress IP = WiFi.softAPIP(); Serial.print("AP IP address: "); Serial.println(IP); server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { //Запускаем сервер с формой конфигурации request->send(SPIFFS, "/conf.html", "text/html"); }); server.on("/bootstrap.min.css", HTTP_GET, [](AsyncWebServerRequest *request){ //Подключаем стили request->send(SPIFFS, "/bootstrap.min.css", "text/css"); }); server.serveStatic("/", SPIFFS, "/"); //Получаем данные из формы, если пришел запрос server.on("/", HTTP_POST, [](AsyncWebServerRequest *request) { int params = request->params(); for(int i=0;i<params;i++){ AsyncWebParameter* p = request->getParam(i); if(p->isPost()){ if (p->name() == PARAM_INPUT_1) { // Получаем имя сети из формы ssid = p->value().c_str(); Serial.print("SSID set to: "); Serial.println(ssid); // Write file to save value writeFile(SPIFFS, ssidPath, ssid.c_str()); } if (p->name() == PARAM_INPUT_2) { // Получаем пароль из формы pass = p->value().c_str(); Serial.print("Password set to: "); Serial.println(pass); // Write file to save value writeFile(SPIFFS, passPath, pass.c_str()); } if (p->name() == PARAM_INPUT_3) { // Получаем POST запрос про IP ip = p->value().c_str(); Serial.print("IP Address set to: "); Serial.println(ip); // Write file to save value writeFile(SPIFFS, ipPath, ip.c_str()); } if (p->name() == PARAM_INPUT_4) { // Получаем POST запрос про Gateway путь gateway = p->value().c_str(); Serial.print("Gateway set to: "); Serial.println(gateway); // Write file to save value writeFile(SPIFFS, gatewayPath, gateway.c_str()); } } } request->send(200, "text/plain", "Успешно, esp перезагрузиться и получит адрес:" + ip); delay(3000); ESP.restart(); //Перезагружаем esp }); server.begin(); //Запускаем сервер в любом случае } }
Часть loop-а не понадобиться, потому разберем некоторые функции, которые облегчат работу:
String readFile(fs::FS &fs, const char * path){ //Чтение файла из spiffs Serial.printf("Reading file: %s\r\n", path); File file = fs.open(path); if(!file || file.isDirectory()){ Serial.println("- failed to open file for reading"); return String(); } String fileContent; while(file.available()){ fileContent = file.readStringUntil('\n'); break; } return fileContent; }
Запись файла с константами:
void writeFile(fs::FS &fs, const char * path, const char * message){ //Функция записи файла в spiffs Serial.printf("Writing file: %s\r\n", path); File file = fs.open(path, FILE_WRITE); if(!file){ Serial.println("- failed to open file for writing"); return; } if(file.print(message)){ Serial.println("- file written"); } else { Serial.println("- frite failed"); } }
Инициализация wifi вынесена в отдельную функцию чтобы, быстро подключаться к данным из spiffs и получать bool true или false.
bool initWiFi() { //Функция инициализации wifi if(ssid=="" || ip==""){ Serial.println("Undefined SSID or IP address."); return false; } WiFi.mode(WIFI_STA); localIP.fromString(ip.c_str()); localGateway.fromString(gateway.c_str()); if (!WiFi.config(localIP, localGateway, subnet)){ Serial.println("STA Failed to configure"); return false; } WiFi.begin(ssid.c_str(), pass.c_str()); Serial.println("Connecting to WiFi..."); unsigned long currentMillis = millis(); previousMillis = currentMillis; while(WiFi.status() != WL_CONNECTED) { currentMillis = millis(); if (currentMillis - previousMillis >= interval) { Serial.println("Failed to connect."); return false; } } Serial.println(WiFi.localIP()); return true; }
Ну и функция инициализации spiffs:
void initSPIFFS() { if (!SPIFFS.begin(true)) { Serial.println("An error has occurred while mounting SPIFFS"); } Serial.println("SPIFFS mounted successfully"); }
Скачать полный скетч и файлы можно тут.
Загружаем...
Видео с демонстрацией работы:
На этом сегодня все, всем спасибо за внимание! Теперь вы знаете как усовершенствовать свое умное устройство на базе esp32. Удачи в проектах!
Данная статья является собственностью Amperkot.ru. При перепечатке данного материала активная ссылка на первоисточник, не закрытая для индексации поисковыми системами, обязательна.
Комментарии