Ввод данных через HTML-форму на веб-сервере ESP32/ESP8266 с использованием Arduino IDE

В этом руководстве вы узнаете, как создать веб-сервер ESP32/ESP8266 с тремя полями ввода для передачи значений на ваш ESP с помощью HTML-формы. Затем вы сможете использовать эти значения как переменные в вашем коде. Мы будем использовать Arduino IDE для программирования плат.

ESP32 ESP8266 ввод данных через HTML-форму веб-сервера с использованием Arduino IDE

Обзор проекта

В этом уроке мы создадим асинхронный веб-сервер с использованием библиотеки ESPAsyncWebServer, который отображает три поля ввода для передачи значений, которые вы можете использовать в своем коде для обновления переменных.

Мы рассмотрим два похожих примера. Следующий рисунок иллюстрирует, как работает первый пример.

Обзор проекта ESP32 ESP8266 HTML-форма для ввода данных

У вас есть веб-страница с тремя полями ввода, к которой вы можете получить доступ с помощью любого браузера в вашей сети. Когда вы вводите новое значение и нажимаете кнопку «Submit», ваш ESP обновляет переменную новым значением.

Если вам когда-либо требовалось обновить переменную через веб-сервер ESP, вам следует использовать этот проект. С помощью этого метода вы избегаете жесткого кодирования переменных, потому что можете создать поле ввода на веб-странице для обновления любой переменной новым значением. Это может быть особенно полезно для установки пороговых значений, задания SSID/пароля, изменения API-ключей и т.д.

Позже мы также покажем вам, как сохранить эти переменные на постоянной основе с помощью LittleFS и как получить к ним доступ. Вот как работает второй пример.

Обзор проекта ESP32 ESP8266 HTML-форма для ввода данных LittleFS

Эта веб-страница позволяет вводить три типа переменных: String, Int и Float. Затем каждый раз, когда вы отправляете новое значение, это значение сохраняется в файле в файловой системе LittleFS. Эта веб-страница также содержит заполнители для отображения текущих значений.

Предварительные требования

Убедитесь, что вы выполнили все предварительные требования из этого раздела, прежде чем приступать к проекту, чтобы код компилировался.

1. Установка платы ESP32/ESP8266 в Arduino IDE

Мы будем программировать ESP32 и ESP8266 с использованием Arduino IDE. Поэтому у вас должно быть установлено дополнение для ESP32 или ESP8266. Следуйте одному из следующих руководств для установки дополнения ESP:

2. Установка библиотек

Для создания веб-сервера вам необходимо установить следующие библиотеки:

Вы можете установить эти библиотеки в менеджере библиотек 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-формы для пользовательского ввода. В нашем случае форма должна содержать поле ввода и кнопку отправки.

Веб-сервер ESP32 ESP8266 с полями ввода

Давайте рассмотрим первую форму, чтобы понять, как она работает (остальные формы работают аналогично).

<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>");
ESP32 ESP8266 HTML-форма ввода данных HTTP GET-запрос

Если вы делаете запрос к недействительному URL, вызывается функция notFound(), определенная в начале скетча.

void notFound(AsyncWebServerRequest *request) {
  request->send(404, "text/plain", "Not found");
}

Наконец, запустите сервер для обслуживания клиентов.

server.begin();

Демонстрация

После загрузки кода на вашу плату откройте монитор последовательного порта Arduino IDE на скорости 115200 бод, чтобы узнать IP-адрес ESP.

IP-адрес ESP32 ESP8266 в мониторе последовательного порта Arduino IDE

Затем откройте браузер и введите IP-адрес. Должна загрузиться следующая веб-страница:

HTML-форма ESP32 ESP8266 с 3 полями ввода для сохранения данных на ESP

Например, введите 123456 в поле input1, затем нажмите кнопку «Submit». Должна загрузиться новая страница, сообщающая, что значение 123456 было отправлено на ваш ESP:

Демонстрация работы HTML-формы ввода данных ESP32 ESP8266

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-адрес ESP32 ESP8266 в мониторе последовательного порта Arduino IDE

Откройте браузер и введите IP-адрес. Должна загрузиться подобная веб-страница (изначально ваши текущие значения будут пустыми).

HTML-форма ESP32 ESP8266 с 3 полями ввода для сохранения данных в LittleFS

Введите строку (String) в первое поле ввода и нажмите «Submit», повторите тот же процесс для значений Int и Float. Каждый раз, когда вы нажимаете кнопку Submit для поля, вы увидите предупреждающее сообщение, подобное этому:

Предупреждающее сообщение HTML-формы ESP32 ESP8266 о сохранении данных в LittleFS

Нажмите кнопку «OK», чтобы перезагрузить веб-страницу и увидеть обновленные текущие значения.

Если у вас открыт монитор последовательного порта Arduino IDE, вы увидите, что сохраненные значения выводятся снова и снова:

Демонстрация работы полей ввода ESP32 ESP8266 в мониторе последовательного порта Arduino IDE

Для финального проекта вы можете удалить все строки в функции loop(), которые выводят все сохраненные значения каждые 5 секунд – мы оставили их намеренно для отладки.

Заключение

В этом уроке вы узнали, как обрабатывать поля ввода на веб-странице для обновления значений переменных ESP. Вы можете модифицировать этот проект для установки пороговых значений, изменения значений API-ключей, установки значения ШИМ, изменения таймера, задания SSID/пароля и т.д.

Вот другие проекты, которые могут вас заинтересовать:


Источник: Input Data on HTML Form ESP32/ESP8266 Web Server using Arduino IDE