Ввод данных через HTML-форму на веб-сервере ESP32/ESP8266 с использованием Arduino IDE
В этом руководстве вы узнаете, как создать веб-сервер ESP32/ESP8266 с тремя полями ввода для передачи значений на ваш ESP с помощью HTML-формы. Затем вы сможете использовать эти значения как переменные в вашем коде. Мы будем использовать Arduino IDE для программирования плат.
Обзор проекта
В этом уроке мы создадим асинхронный веб-сервер с использованием библиотеки ESPAsyncWebServer, который отображает три поля ввода для передачи значений, которые вы можете использовать в своем коде для обновления переменных.
Мы рассмотрим два похожих примера. Следующий рисунок иллюстрирует, как работает первый пример.
У вас есть веб-страница с тремя полями ввода, к которой вы можете получить доступ с помощью любого браузера в вашей сети. Когда вы вводите новое значение и нажимаете кнопку «Submit», ваш ESP обновляет переменную новым значением.
Если вам когда-либо требовалось обновить переменную через веб-сервер ESP, вам следует использовать этот проект. С помощью этого метода вы избегаете жесткого кодирования переменных, потому что можете создать поле ввода на веб-странице для обновления любой переменной новым значением. Это может быть особенно полезно для установки пороговых значений, задания SSID/пароля, изменения API-ключей и т.д.
Позже мы также покажем вам, как сохранить эти переменные на постоянной основе с помощью LittleFS и как получить к ним доступ. Вот как работает второй пример.
Эта веб-страница позволяет вводить три типа переменных: String, Int и Float. Затем каждый раз, когда вы отправляете новое значение, это значение сохраняется в файле в файловой системе LittleFS. Эта веб-страница также содержит заполнители для отображения текущих значений.
Предварительные требования
Убедитесь, что вы выполнили все предварительные требования из этого раздела, прежде чем приступать к проекту, чтобы код компилировался.
1. Установка платы ESP32/ESP8266 в Arduino IDE
Мы будем программировать ESP32 и ESP8266 с использованием Arduino IDE. Поэтому у вас должно быть установлено дополнение для ESP32 или ESP8266. Следуйте одному из следующих руководств для установки дополнения ESP:
2. Установка библиотек
Для создания веб-сервера вам необходимо установить следующие библиотеки:
ESP32: установите библиотеки ESPAsyncWebServer и AsyncTCP (от ESP32Async).
ESP8266: установите библиотеки ESPAsyncWebServer и ESPAsyncTCP (от ESP32Async).
Вы можете установить эти библиотеки в менеджере библиотек Arduino IDE. Перейдите в Sketch > Include Library > Manage Libraries и найдите названия библиотек.
3. Необходимые компоненты
Для выполнения этого урока вам понадобится только ESP32 или ESP8266 (читайте ESP32 vs ESP8266). Схема подключения для этого проекта не требуется.
1. ESP32/ESP8266: обработка полей ввода на веб-странице с HTML-формой
Скопируйте следующий код в Arduino IDE. Затем введите свои сетевые учетные данные (SSID и пароль), чтобы код работал у вас.
/*********
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/esp32-esp8266-input-data-html-form/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
#include <Arduino.h>
#ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h>
#else
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#endif
#include <ESPAsyncWebServer.h>
AsyncWebServer server(80);
// REPLACE WITH YOUR NETWORK CREDENTIALS
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
const char* PARAM_INPUT_1 = "input1";
const char* PARAM_INPUT_2 = "input2";
const char* PARAM_INPUT_3 = "input3";
// HTML web page to handle 3 input fields (input1, input2, input3)
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html><head>
<title>ESP Input Form</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head><body>
<form action="/get">
input1: <input type="text" name="input1">
<input type="submit" value="Submit">
</form><br>
<form action="/get">
input2: <input type="text" name="input2">
<input type="submit" value="Submit">
</form><br>
<form action="/get">
input3: <input type="text" name="input3">
<input type="submit" value="Submit">
</form>
</body></html>)rawliteral";
void notFound(AsyncWebServerRequest *request) {
request->send(404, "text/plain", "Not found");
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("WiFi Failed!");
return;
}
Serial.println();
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
// Send web page with input fields to client
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", index_html);
});
// Send a GET request to <ESP_IP>/get?input1=<inputMessage>
server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
String inputMessage;
String inputParam;
// GET input1 value on <ESP_IP>/get?input1=<inputMessage>
if (request->hasParam(PARAM_INPUT_1)) {
inputMessage = request->getParam(PARAM_INPUT_1)->value();
inputParam = PARAM_INPUT_1;
}
// GET input2 value on <ESP_IP>/get?input2=<inputMessage>
else if (request->hasParam(PARAM_INPUT_2)) {
inputMessage = request->getParam(PARAM_INPUT_2)->value();
inputParam = PARAM_INPUT_2;
}
// GET input3 value on <ESP_IP>/get?input3=<inputMessage>
else if (request->hasParam(PARAM_INPUT_3)) {
inputMessage = request->getParam(PARAM_INPUT_3)->value();
inputParam = PARAM_INPUT_3;
}
else {
inputMessage = "No message sent";
inputParam = "none";
}
Serial.println(inputMessage);
request->send(200, "text/html", "HTTP GET request sent to your ESP on input field ("
+ inputParam + ") with value: " + inputMessage +
"<br><a href=\"/\">Return to Home Page</a>");
});
server.onNotFound(notFound);
server.begin();
}
void loop() {
}
Как работает код
Давайте кратко рассмотрим код и разберемся, как он работает.
Подключение библиотек
Сначала подключите необходимые библиотеки. Вы подключаете разные библиотеки в зависимости от используемой платы. Если вы используете ESP32, код загружает следующие библиотеки:
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
Если вы используете ESP8266, подключите эти библиотеки:
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
Сетевые учетные данные
Не забудьте ввести свои сетевые учетные данные в следующие переменные, чтобы ESP32 или ESP8266 мог подключиться к вашей сети.
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
HTML-формы и поля ввода
Сначала давайте рассмотрим HTML-код, который нам нужен для отображения полей ввода.
В нашем примере мы отображаем три поля ввода, и каждое поле имеет кнопку «Submit». Когда пользователь вводит данные и нажимает кнопку «Submit», это значение отправляется на ESP и обновляет переменную.
Для этого создайте три формы:
<form action="/get">
input1: <input type="text" name="input1">
<input type="submit" value="Submit">
</form><br>
<form action="/get">
input2: <input type="text" name="input2">
<input type="submit" value="Submit">
</form><br>
<form action="/get">
input3: <input type="text" name="input3">
<input type="submit" value="Submit">
</form>
В HTML тег <form> используется для создания HTML-формы для пользовательского ввода. В нашем случае форма должна содержать поле ввода и кнопку отправки.
Давайте рассмотрим первую форму, чтобы понять, как она работает (остальные формы работают аналогично).
<form action="/get">
input1: <input type="text" name="input1">
<input type="submit" value="Submit">
</form>
Атрибут action указывает, куда отправлять данные, введенные в форму, после нажатия кнопки отправки. В данном случае выполняется HTTP GET-запрос к /get?input1=value. Значение value относится к тексту, который вы вводите в поле ввода.
Затем мы определяем два элемента ввода: текстовое поле и кнопку отправки.
Следующая строка определяет однострочное текстовое поле ввода.
input1: <input type="text" name="input1">
Атрибут type указывает, что мы хотим текстовое поле ввода, а атрибут name задает имя элемента ввода.
Следующая строка определяет кнопку для отправки данных HTML-формы.
<input type="submit" value="Submit">
В данном случае атрибут type указывает, что вам нужна кнопка отправки, а атрибут value задает текст на кнопке.
Подключение к сети
В функции setup() подключитесь к вашей локальной сети.
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("WiFi Failed!");
return;
}
Serial.println();
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
Обработка HTTP GET-запросов
Затем вам нужно обработать HTTP GET-запросы.
Когда вы обращаетесь к корневому URL, клиенту отправляется HTML-страница. В данном случае HTML-текст сохранен в переменной index_html.
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", index_html);
});
Затем вам нужно обработать, что происходит при получении запроса на маршруты /get.
server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
Мы создаем две переменные: inputMessage и inputParam для сохранения значения ввода и поля ввода.
Затем нам нужно проверить, содержит ли HTTP GET-запрос параметры input1, input2 или input3. Они сохранены в переменных PARAM_INPUT_1, PARAM_INPUT_2 и PARAM_INPUT_3.
Если запрос содержит PARAM_INPUT_1 (т.е. input1), мы устанавливаем inputMessage в значение, введенное в поле input1.
inputMessage = request->getParam(PARAM_INPUT_1)->value();
Теперь у вас есть значение, которое вы только что ввели в первую форму, сохраненное в переменной inputMessage.
Затем установите переменную inputParam в PARAM_INPUT_1, чтобы мы знали, откуда пришло входное значение.
Когда вы отправляете форму, вы получаете сообщение, в котором указано введенное вами значение и в какое поле. Мы также отображаем ссылку для возврата на корневой URL (домашнюю страницу).
request->send(200, "text/html", "HTTP GET request sent to your ESP on input field ("
+ inputParam + ") with value: " + inputMessage +
"<br><a href=\"/\">Return to Home Page</a>");
Если вы делаете запрос к недействительному URL, вызывается функция notFound(), определенная в начале скетча.
void notFound(AsyncWebServerRequest *request) {
request->send(404, "text/plain", "Not found");
}
Наконец, запустите сервер для обслуживания клиентов.
server.begin();
Демонстрация
После загрузки кода на вашу плату откройте монитор последовательного порта Arduino IDE на скорости 115200 бод, чтобы узнать IP-адрес ESP.
Затем откройте браузер и введите IP-адрес. Должна загрузиться следующая веб-страница:
Например, введите 123456 в поле input1, затем нажмите кнопку «Submit». Должна загрузиться новая страница, сообщающая, что значение 123456 было отправлено на ваш ESP:
2. ESP32/ESP8266: сохранение полей ввода в LittleFS
Теперь перейдем ко второму примеру. Этот пример сохраняет данные, введенные в поля ввода, на постоянной основе в LittleFS. Мы также добавили заполнители на веб-странице для отображения текущих значений.
Скопируйте следующий скетч в Arduino IDE. Затем, перед загрузкой, введите свои сетевые учетные данные (SSID и пароль).
/*********
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/esp32-esp8266-input-data-html-form/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
#include <Arduino.h>
#ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h>
#include <LittleFS.h>
#else
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <Hash.h>
#include <LittleFS.h>
#endif
#include <ESPAsyncWebServer.h>
AsyncWebServer server(80);
// REPLACE WITH YOUR NETWORK CREDENTIALS
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
const char* PARAM_STRING = "inputString";
const char* PARAM_INT = "inputInt";
const char* PARAM_FLOAT = "inputFloat";
// HTML web page to handle 3 input fields (inputString, inputInt, inputFloat)
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html><head>
<title>ESP Input Form</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
function submitMessage() {
alert("Saved value to ESP LittleFS");
setTimeout(function(){ document.location.reload(false); }, 500);
}
</script></head><body>
<form action="/get" target="hidden-form">
inputString (current value %inputString%): <input type="text" name="inputString">
<input type="submit" value="Submit" onclick="submitMessage()">
</form><br>
<form action="/get" target="hidden-form">
inputInt (current value %inputInt%): <input type="number " name="inputInt">
<input type="submit" value="Submit" onclick="submitMessage()">
</form><br>
<form action="/get" target="hidden-form">
inputFloat (current value %inputFloat%): <input type="number " name="inputFloat">
<input type="submit" value="Submit" onclick="submitMessage()">
</form>
<iframe style="display:none" name="hidden-form"></iframe>
</body></html>)rawliteral";
void notFound(AsyncWebServerRequest *request) {
request->send(404, "text/plain", "Not found");
}
String readFile(fs::FS &fs, const char * path){
Serial.printf("Reading file: %s\r\n", path);
File file = fs.open(path, "r");
if(!file || file.isDirectory()){
Serial.println("- empty file or failed to open file");
return String();
}
Serial.println("- read from file:");
String fileContent;
while(file.available()){
fileContent+=String((char)file.read());
}
file.close();
Serial.println(fileContent);
return fileContent;
}
void writeFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Writing file: %s\r\n", path);
File file = fs.open(path, "w");
if(!file){
Serial.println("- failed to open file for writing");
return;
}
if(file.print(message)){
Serial.println("- file written");
} else {
Serial.println("- write failed");
}
file.close();
}
// Replaces placeholder with stored values
String processor(const String& var){
//Serial.println(var);
if(var == "inputString"){
return readFile(LittleFS, "/inputString.txt");
}
else if(var == "inputInt"){
return readFile(LittleFS, "/inputInt.txt");
}
else if(var == "inputFloat"){
return readFile(LittleFS, "/inputFloat.txt");
}
return String();
}
void setup() {
Serial.begin(115200);
// Initialize LittleFS
#ifdef ESP32
if(!LittleFS.begin(true)){
Serial.println("An Error has occurred while mounting LittleFS");
return;
}
#else
if(!LittleFS.begin()){
Serial.println("An Error has occurred while mounting LittleFS");
return;
}
#endif
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("WiFi Failed!");
return;
}
Serial.println();
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
// Send web page with input fields to client
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/html", index_html, processor);
});
// Send a GET request to <ESP_IP>/get?inputString=<inputMessage>
server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
String inputMessage;
// GET inputString value on <ESP_IP>/get?inputString=<inputMessage>
if (request->hasParam(PARAM_STRING)) {
inputMessage = request->getParam(PARAM_STRING)->value();
writeFile(LittleFS, "/inputString.txt", inputMessage.c_str());
}
// GET inputInt value on <ESP_IP>/get?inputInt=<inputMessage>
else if (request->hasParam(PARAM_INT)) {
inputMessage = request->getParam(PARAM_INT)->value();
writeFile(LittleFS, "/inputInt.txt", inputMessage.c_str());
}
// GET inputFloat value on <ESP_IP>/get?inputFloat=<inputMessage>
else if (request->hasParam(PARAM_FLOAT)) {
inputMessage = request->getParam(PARAM_FLOAT)->value();
writeFile(LittleFS, "/inputFloat.txt", inputMessage.c_str());
}
else {
inputMessage = "No message sent";
}
Serial.println(inputMessage);
request->send(200, "text/text", inputMessage);
});
server.onNotFound(notFound);
server.begin();
}
void loop() {
// To access your stored values on inputString, inputInt, inputFloat
String yourInputString = readFile(LittleFS, "/inputString.txt");
Serial.print("*** Your inputString: ");
Serial.println(yourInputString);
int yourInputInt = readFile(LittleFS, "/inputInt.txt").toInt();
Serial.print("*** Your inputInt: ");
Serial.println(yourInputInt);
float yourInputFloat = readFile(LittleFS, "/inputFloat.txt").toFloat();
Serial.print("*** Your inputFloat: ");
Serial.println(yourInputFloat);
delay(5000);
}
Как работает код
Этот код очень похож на предыдущий с несколькими изменениями. Давайте кратко его рассмотрим и разберемся, как он работает.
Подключение библиотек
Код загружает следующие библиотеки, если вы используете ESP32. Вам необходимо загрузить библиотеку LittleFS для записи в файловую систему LittleFS.
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <AsyncTCP.h>
#include <LittleFS.h>
Если вы используете ESP8266, вам нужно подключить следующие библиотеки.
#include <ESP8266WiFi.h>
#include <ESPAsyncWebServer.h>
#include <ESPAsyncTCP.h>
#include <Hash.h>
#include <LittleFS.h>
HTML-форма
В этом примере, когда вы отправляете значения, открывается окно с сообщением о том, что значение сохранено в LittleFS, вместо перенаправления на другую страницу, как в предыдущем примере.
Для этого нам нужно добавить JavaScript-функцию, в данном случае она называется submitMessage(), которая выводит предупреждающее сообщение о том, что значение было сохранено в LittleFS. После этого всплывающего окна страница перезагружается, чтобы отобразить текущие значения.
<script>
function submitMessage() {
alert("Saved value to ESP LittleFS");
setTimeout(function(){ document.location.reload(false); }, 500);
}
</script>
Формы также немного отличаются от предыдущих. Вот форма для первого поля ввода.
<form action="/get" target="hidden-form">
inputString (current value %inputString%): <input type="text" name="inputString">
<input type="submit" value="Submit" onclick="submitMessage()">
</form>
В данном случае атрибут target и элемент <iframe> используются для того, чтобы вы оставались на той же странице после отправки формы.
Название, которое отображается для поля ввода, содержит заполнитель %inputString%, который затем будет заменен текущим значением переменной inputString.
Атрибут onclick=»submitMessage()» вызывает JavaScript-функцию submitMessage() после нажатия кнопки «Submit».
Чтение и запись в LittleFS
Затем у нас есть несколько функций для чтения и записи в LittleFS.
Функция readFile() читает содержимое файла:
String readFile(fs::FS &fs, const char * path){
Serial.printf("Reading file: %s\r\n", path);
File file = fs.open(path, "r");
if(!file || file.isDirectory()){
Serial.println("- empty file or failed to open file");
return String();
}
Serial.println("- read from file:");
String fileContent;
while(file.available()){
fileContent+=String((char)file.read());
}
Serial.println(fileContent);
return fileContent;
Функция writeFile() записывает содержимое в файл:
void writeFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Writing file: %s\r\n", path);
File file = fs.open(path, "w");
if(!file){
Serial.println("- failed to open file for writing");
return;
}
if(file.print(message)){
Serial.println("- file written");
} else {
Serial.println("- write failed");
}
}
Функция processor()
Функция processor() отвечает за поиск заполнителей в HTML-тексте и замену их фактическими значениями, сохраненными в LittleFS.
String processor(const String& var){
//Serial.println(var);
if(var == "inputString"){
return readFile(LittleFS, "/inputString.txt");
}
else if(var == "inputInt"){
return readFile(LittleFS, "/inputInt.txt");
}
else if(var == "inputFloat"){
return readFile(LittleFS, "/inputFloat.txt");
}
return String();
}
Обработка HTTP GET-запросов
Обработка HTTP GET-запросов работает так же, как и в предыдущем примере, но на этот раз мы сохраняем переменные в LittleFS.
Например, для поля inputString:
if (request->hasParam(PARAM_STRING)) {
inputMessage = request->getParam(PARAM_STRING)->value();
writeFile(LittleFS, "/inputString.txt", inputMessage.c_str());
}
Когда запрос содержит inputString (т.е. PARAM_STRING), мы устанавливаем переменную inputMessage в значение, отправленное из формы inputString.
inputMessage = request->getParam(PARAM_STRING)->value();
Затем сохраняем это значение в LittleFS.
writeFile(LittleFS, "/inputString.txt", inputMessage.c_str());
Аналогичный процесс происходит для остальных форм.
Доступ к переменным
В функции loop() мы демонстрируем, как можно получить доступ к переменным.
Например, создайте строковую переменную yourInputString, которая читает содержимое файла inputString.txt в LittleFS.
String yourInputString = readFile(LittleFS, "/inputString.txt");
Затем выведите эту переменную в монитор последовательного порта.
Serial.println(yourInputString);
Демонстрация
После загрузки кода на вашу плату откройте монитор последовательного порта Arduino IDE на скорости 115200 бод, чтобы узнать IP-адрес ESP.
Откройте браузер и введите IP-адрес. Должна загрузиться подобная веб-страница (изначально ваши текущие значения будут пустыми).
Введите строку (String) в первое поле ввода и нажмите «Submit», повторите тот же процесс для значений Int и Float. Каждый раз, когда вы нажимаете кнопку Submit для поля, вы увидите предупреждающее сообщение, подобное этому:
Нажмите кнопку «OK», чтобы перезагрузить веб-страницу и увидеть обновленные текущие значения.
Если у вас открыт монитор последовательного порта Arduino IDE, вы увидите, что сохраненные значения выводятся снова и снова:
Для финального проекта вы можете удалить все строки в функции loop(), которые выводят все сохраненные значения каждые 5 секунд – мы оставили их намеренно для отладки.
Заключение
В этом уроке вы узнали, как обрабатывать поля ввода на веб-странице для обновления значений переменных ESP. Вы можете модифицировать этот проект для установки пороговых значений, изменения значений API-ключей, установки значения ШИМ, изменения таймера, задания SSID/пароля и т.д.
Вот другие проекты, которые могут вас заинтересовать:
Источник: Input Data on HTML Form ESP32/ESP8266 Web Server using Arduino IDE