ESP32/ESP8266 Веб-сервер: Управление выходами с таймером

В этом руководстве вы создадите веб-сервер для управления выходами ESP32 или ESP8266 NodeMCU с помощью импульса, используя Arduino IDE. Длительность импульса («таймер») можно настраивать с помощью слайдера на веб-странице. Когда вы нажимаете кнопку ON, ESP устанавливает состояние выхода в HIGH на количество секунд, заданное слайдером. Это может быть особенно полезно для управления устройствами, которым требуется сигнал HIGH на заданное количество секунд для срабатывания.

ESP32 ESP8266 NodeMCU Веб-сервер: Управление выходами с таймером используя Arduino IDE

Платы ESP32/ESP8266 будут программироваться с использованием Arduino IDE. Убедитесь, что у вас установлены эти платы:

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

На следующем изображении показан обзор работы данного проекта.

ESP32 ESP8266 NodeMCU Управление выходами с таймером Веб-сервер Обзор проекта
  • ESP32/ESP8266 размещает веб-сервер, который позволяет управлять выходом с помощью импульса;

  • Веб-сервер содержит слайдер, позволяющий задать длительность импульса (сколько секунд выход должен быть в состоянии HIGH);

  • Есть кнопка ON/OFF. Установите её в ON, чтобы отправить импульс. После этого вы увидите обратный отсчёт таймера на протяжении длительности импульса;

  • Когда таймер завершается, выход устанавливается в LOW, а кнопка на веб-сервере возвращается в состояние OFF;

  • Этот веб-сервер может быть полезен для управления устройствами, которым нужен импульс для активации, например, открыватели гаражных ворот.

Установка библиотек – Асинхронный веб-сервер

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

Вы можете установить эти библиотеки через Менеджер библиотек Arduino IDE. Перейдите в Sketch > Include Library > Manage Libraries и найдите нужные библиотеки по имени.

Код

Скопируйте следующий код в вашу Arduino IDE.

/*********
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-esp8266-web-server-timer-pulse/
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*********/

// Import required libraries
#ifdef ESP32
  #include <WiFi.h>
  #include <AsyncTCP.h>
#else
  #include <ESP8266WiFi.h>
  #include <ESPAsyncTCP.h>
#endif
#include <ESPAsyncWebServer.h>

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

const char* PARAM_INPUT_1 = "state";
const char* PARAM_INPUT_2 = "value";

const int output = 2;

String timerSliderValue = "10";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>ESP Web Server</title>
  <style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    h2 {font-size: 2.4rem;}
    p {font-size: 2.2rem;}
    body {max-width: 600px; margin:0px auto; padding-bottom: 25px;}
    .switch {position: relative; display: inline-block; width: 120px; height: 68px}
    .switch input {display: none}
    .slider {position: absolute; top: 0; left: 0; right: 0; bottom: 0;
      background-color: #ccc; border-radius: 34px}
    .slider:before {position: absolute; content: ""; height: 52px; width: 52px;
      left: 8px; bottom: 8px; background-color: #fff; -webkit-transition: .4s;
      transition: .4s; border-radius: 68px}
    input:checked+.slider {background-color: #2196F3}
    input:checked+.slider:before {-webkit-transform: translateX(52px);
      -ms-transform: translateX(52px); transform: translateX(52px)}
    .slider2 { -webkit-appearance: none; margin: 14px; width: 300px;
      height: 20px; background: #ccc;
      outline: none; -webkit-transition: .2s; transition: opacity .2s;}
    .slider2::-webkit-slider-thumb {-webkit-appearance: none; appearance: none;
      width: 30px; height: 30px; background: #2f4468; cursor: pointer;}
    .slider2::-moz-range-thumb { width: 30px; height: 30px;
      background: #2f4468; cursor: pointer; }
  </style>
</head>
<body>
  <h2>ESP Web Server</h2>
  <p><span id="timerValue">%TIMERVALUE%</span> s</p>
  <p><input type="range" onchange="updateSliderTimer(this)" id="timerSlider"
    min="1" max="20" value="%TIMERVALUE%" step="1" class="slider2"></p>
  %BUTTONPLACEHOLDER%
<script>
function toggleCheckbox(element) {
  var sliderValue = document.getElementById("timerSlider").value;
  var xhr = new XMLHttpRequest();
  if(element.checked){
    xhr.open("GET", "/update?state=1", true); xhr.send();
    var count = sliderValue, timer = setInterval(function() {
      count--;
      document.getElementById("timerValue").textContent = count;
      if(count == 0){
        clearInterval(timer);
        document.getElementById("timerValue").textContent =
          document.getElementById("timerSlider").value;
      }
    }, 1000);
    sliderValue = sliderValue*1000;
    setTimeout(function(){
      xhr.open("GET", "/update?state=0", true);
      document.getElementById(element.id).checked = false;
      xhr.send();
    }, sliderValue);
  }
}
function updateSliderTimer(element) {
  var sliderValue = document.getElementById("timerSlider").value;
  document.getElementById("timerValue").textContent = sliderValue;
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "/slider?value="+sliderValue, true);
  xhr.send();
}
</script>
</body>
</html>
)rawliteral";

// Replaces placeholder with button section in your web page
String processor(const String& var){
  //Serial.println(var);
  if(var == "BUTTONPLACEHOLDER"){
    String buttons = "";
    String outputStateValue = outputState();
    buttons+= "<p><label class=\"switch\"><input type=\"checkbox\" "
      "onchange=\"toggleCheckbox(this)\" id=\"output\" " + outputStateValue +
      "><span class=\"slider\"></span></label></p>";
    return buttons;
  }
  else if(var == "TIMERVALUE"){
    return timerSliderValue;
  }
  return String();
}

String outputState(){
  if(digitalRead(output)){
    return "checked";
  }
  else {
    return "";
  }
  return "";
}

void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);

  pinMode(output, OUTPUT);
  digitalWrite(output, LOW);

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  // Print ESP Local IP Address
  Serial.println(WiFi.localIP());

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/html", index_html, processor);
  });

  // Send a GET request to <ESP_IP>/update?state=<inputMessage>
  server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String inputMessage;
    // GET input1 value on <ESP_IP>/update?state=<inputMessage>
    if (request->hasParam(PARAM_INPUT_1)) {
      inputMessage = request->getParam(PARAM_INPUT_1)->value();
      digitalWrite(output, inputMessage.toInt());
    }
    else {
      inputMessage = "No message sent";
    }
    Serial.println(inputMessage);
    request->send(200, "text/plain", "OK");
  });

  // Send a GET request to <ESP_IP>/slider?value=<inputMessage>
  server.on("/slider", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String inputMessage;
    // GET input1 value on <ESP_IP>/slider?value=<inputMessage>
    if (request->hasParam(PARAM_INPUT_2)) {
      inputMessage = request->getParam(PARAM_INPUT_2)->value();
      timerSliderValue = inputMessage;
    }
    else {
      inputMessage = "No message sent";
    }
    Serial.println(inputMessage);
    request->send(200, "text/plain", "OK");
  });

  // Start server
  server.begin();
}

void loop() {

}

Просмотреть исходный код на GitHub

Вам нужно только указать свои сетевые учётные данные (SSID и пароль), и веб-сервер сразу заработает. Код совместим с обеими платами ESP32 и ESP8266 и управляет встроенным светодиодом GPIO 2 – вы можете изменить код для управления любым другим GPIO.

Как работает код

Мы уже подробно объясняли, как работают подобные веб-серверы в предыдущих руководствах (Веб-сервер температуры DHT или Веб-сервер реле), поэтому рассмотрим только соответствующие части для данного проекта.

Сетевые учётные данные

Как было сказано ранее, вставьте свои сетевые учётные данные в следующие строки:

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Метка слайдера

Над слайдером расположено число, отображающее текущее значение слайдера.

<p><span id="timerValue">%TIMERVALUE%</span> s</p>

По умолчанию значение слайдера установлено в placeholder %TIMERVALUE%.

%TIMERVALUE% – это placeholder, который будет заменён значением, хранящимся в переменной timerSliderValue, которая по умолчанию равна 10. Но вы можете изменить это в следующей строке:

String timerSliderValue = "10";

Это значение также будет обновляться при перемещении слайдера. Когда слайдер перемещается, вызывается JavaScript-функция, обновляющая значение слайдера.

Слайдер

Следующая строка создаёт слайдер.

<input type="range" onchange="updateSliderTimer(this)" id="timerSlider" min="1" max="20" value="%TIMERVALUE%" step="1" class="slider2">

Давайте разберём это на более мелкие части.

В HTML слайдер – это тип ввода (input type). Тег <input> определяет поле ввода, в которое пользователь может вводить данные. Слайдер – это поле ввода типа range. Существует множество других типов полей ввода.

<input type="range">

Диапазон слайдера по умолчанию – от 0 до 100. Вы можете использовать следующие атрибуты для настройки параметров слайдера:

  • max: задаёт максимально допустимое значение. В нашем примере мы установили его в 20, но вы можете изменить это значение.

  • min: задаёт минимальное значение. В данном случае мы установили его в 1.

  • step: задаёт интервал между числами. Установлен в 1.

  • value: задаёт значение слайдера по умолчанию. В данном случае оно равно %TIMERVALUE%.

<input type="range" onchange="updateSliderTimer(this)" id="timerSlider" min="1" max="20" value="%TIMERVALUE%" step="1" class="slider2">

%TIMERVALUE% – это placeholder, который будет заменён фактическим значением. В коде он будет заменён значением переменной timerSliderValue, которая по умолчанию равна 10. Но вы можете изменить это в следующей строке:

String timerSliderValue = "10";

У слайдера есть ещё два атрибута: id и onchange.

  • id: задаёт уникальный идентификатор для HTML-элемента (слайдера). Идентификатор позволяет нам манипулировать элементом с помощью CSS или JavaScript.

  • onchange: это атрибут события, которое происходит при изменении значения элемента (слайдера). Когда вы перемещаете слайдер, вызывается функция updateSliderTimer().

Обновление значения слайдера (JavaScript)

При перемещении слайдера выполняется функция updateSliderTimer().

Она получает текущее значение слайдера, обращаясь к его id timerSlider:

var sliderValue = document.getElementById("timerSlider").value;

Обновляет метку слайдера текущим значением, обращаясь к его id timerValue:

document.getElementById("timerValue").textContent = sliderValue;

Затем выполняется запрос по URL /slider?value=sliderValue, где sliderValue равно текущему значению слайдера.

После этого ESP32/ESP8266 обрабатывает действия при получении запроса по этому URL.

Управление выходом с таймером (JavaScript)

Когда вы нажимаете кнопку ON/OFF для управления выходом, вызывается JavaScript-функция toggleCheckbox().

Эта функция получает текущее значение метки слайдера:

var sliderValue = document.getElementById("timerSlider").value;

Выполняет запрос по URL /update?state=1, чтобы ESP знал, что нужно установить выход в HIGH.

if(element.checked){ xhr.open("GET", "/update?state=1", true); xhr.send();

Следующие строки уменьшают значение метки слайдера каждую секунду, создавая обратный отсчёт таймера.

var count = sliderValue, timer = setInterval(function() {
  count--;
  document.getElementById("timerValue").textContent = count;
  if(count == 0){
    clearInterval(timer);
    document.getElementById("timerValue").textContent =
      document.getElementById("timerSlider").value;
  }
}, 1000);

Когда таймер достигает нуля, значение метки возвращается к исходному значению, и выполняется запрос по URL /update?state=0, чтобы ESP знал, что пора установить выход в LOW. Кнопка на веб-сервере возвращается в состояние off.

setTimeout(function(){ xhr.open("GET", "/update?state=0", true);
document.getElementById(element.id).checked = false; xhr.send(); }, sliderValue);

Обработка запросов

ESP32/ESP8266 должен обработать действия при получении запроса по определённому URL.

Корневой URL

При доступе к корневому URL / отправляется HTML-текст, сохранённый в переменной index_html. Все placeholder’ы заменяются фактическими значениями с помощью функции processor().

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/html", index_html, processor);
});

Управление состоянием выхода

Следующие строки обрабатывают действия при получении запросов по URL /update?state=1 и /update?state=0. Они устанавливают состояние выхода в HIGH или LOW соответственно.

server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
  String inputMessage;
  // GET input1 value on <ESP_IP>/update?state=<inputMessage>
  if (request->hasParam(PARAM_INPUT_1)) {
    inputMessage = request->getParam(PARAM_INPUT_1)->value();
    digitalWrite(output, inputMessage.toInt());
  }
  else {
    inputMessage = "No message sent";
  }
  Serial.println(inputMessage);
  request->send(200, "text/plain", "OK");
});

Состояние выхода обновляется в следующей строке:

digitalWrite(output, inputMessage.toInt());

Обновление значения слайдера

Каждый раз, когда вы перемещаете слайдер, ESP получает запрос с новым значением. Мы сохраняем новое значение слайдера и выводим его в Serial Monitor.

// Send a GET request to <ESP_IP>/slider?value=<inputMessage>
server.on("/slider", HTTP_GET, [] (AsyncWebServerRequest *request) {
  String inputMessage;
  // GET input1 value on <ESP_IP>/slider?value=<inputMessage>
  if (request->hasParam(PARAM_INPUT_2)) {
    inputMessage = request->getParam(PARAM_INPUT_2)->value();
    timerSliderValue = inputMessage;
  }
  else {
    inputMessage = "No message sent";
  }
  Serial.println(inputMessage);
  request->send(200, "text/plain", "OK");
});

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

Загрузите код в вашу плату ESP32 или ESP8266 NodeMCU. Затем откройте Serial Monitor и нажмите кнопку RST/EN на плате, чтобы получить IP-адрес.

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

ESP32 ESP8266 NodeMCU Веб-сервер Управление выходами с таймером демонстрация

Переместите слайдер, чтобы настроить длительность импульса, а затем нажмите кнопку ON/OFF. Выход (в данном случае GPIO 2 – встроенный светодиод) будет оставаться включённым в течение периода времени, установленного на слайдере.

Плата ESP32 Встроенный светодиод включён HIGH

Заключение

В этом руководстве вы узнали, как создать веб-сервер для управления выходом с помощью импульса. Длительность импульса можно настроить с помощью слайдера на странице веб-сервера. Это может быть особенно полезно для управления определёнными устройствами, которым нужен сигнал HIGH на заданное количество секунд для срабатывания, например, открыватели гаражных ворот.

У нас есть другие проекты веб-серверов, которые могут вам понравиться:

Узнайте больше о платах ESP32 и ESP8266 с нашими ресурсами:

Примечание

Данная статья является переводом материала с сайта Random Nerd Tutorials. Все права на оригинальный контент принадлежат авторам Rui Santos и Sara Santos.