Урок 1. Веб-сервер ESP32 (ESP8266) в среде Arduino IDE

Данным уроком начну серию уроков про ESP32 и ESP8266. ESP8266 и ESP32 стали достаточно популярными среди проектов, связанных с IoT или Wi-Fi. Это экономичные модули Wi-Fi, которые можно запрограммировать для создания автономного веб-сервера. Что очень радует!

Что такое веб-сервер и как он работает?

Веб-сервер - это место, где хранятся, обрабатываются и отсылаются веб-страницы веб-клиентам. Веб-клиент - это не что иное, как веб-браузер на наших ноутбуках и смартфонах. Связь между клиентом и сервером происходит с использованием специального протокола, называемого протоколом передачи гипертекста (HTTP).

Что такое веб-сервер и как он работает?

В этом протоколе клиент инициирует связь, отправляя запрос на конкретную веб-страницу с помощью HTTP, а сервер отдает содержимое этой веб-страницы, или сообщение об ошибке, если не может этого сделать (например: страница не найдена, ошибка 404). Страницы, которые отдает сервер, в основном представляют собой HTML-документы.

Режимы работы ESP32.

Одна из особенностей ESP32 заключается в том, что он может не только подключаться к существующей сети Wi-Fi и действовать как веб-сервер, но также может настраивать собственную сеть, позволяя другим устройствам напрямую подключаться к ней и получать доступ к веб-страницам. Это возможно, потому что ESP32 работает в трех разных режимах: режим станции (STA), режим точки доступа (AP) и оба режима одновременно.

Режим станции (STA).

ESP32 (ESP8266), который подключается к существующей сети Wi-Fi (созданной вашим беспроводным маршрутизатором), называется станцией (STA).

В режиме STA ESP32 получает IP от маршрутизатора, к которому он подключен.

В режиме STA ESP32 получает IP от маршрутизатора, к которому он подключен. С этим IP-адресом он может настроить веб-сервер и отдавать веб-страницы на все подключенные устройства в существующей сети Wi-Fi .

Режим точки доступа (AP).

ESP32 (ESP8266), который создает свою собственную сеть Wi-Fi и действует как концентратор (как Wi-Fi маршрутизатор) для одного или нескольких устройств, называется точкой доступа (AP). В отличие от Wi-Fi роутера, у него нет интерфейса для проводной сети. Итак, такой режим работы называется Soft Access Point (soft-AP). Также максимальное количество устройств, которые могут подключиться к нему, ограничено пятью.

ESP32, который создает свою собственную сеть Wi-Fi и действует как концентратор (как Wi-Fi маршрутизатор)

В режиме AP ESP32 создает новую сеть Wi-Fi и устанавливает для нее SSID (имя сети) и IP-адрес. С этим IP-адресом он может отдавать веб-страницы на все подключенные устройства в своей собственной сети.

Схема подключения двух светодиодов к ESP32.

Теперь, когда мы знаем основы работы веб-сервера, и в каких режимах ESP32 может создавать веб-сервер, пришло время подключить к ESP32 два светодиода, которыми мы планируем управлять через WiFi.

Установка, прошивка платы ESP32 в Arduino IDE (Windows, Mac OS X, Linux)

Начните с размещения ESP32 на макетной плате. Затем подключите два светодиода к цифровым GPIO 4 и 5 через ограничительный резистор 220 Ом.

Схема подключения двух светодиодов к ESP32.

Когда вы закончите, у вас получится что-то похожее на изображение, показанное ниже.

Принцип управления устройствами через веб-сервер ESP32.

Давайте разберемся, как же происходит управлениеустройствами через веб-сервер ESP32.

Когда вводите URL-адрес в веб-браузере и нажимаете ENTER, браузер отправляет HTTP-запрос (также известный как запрос GET) на веб-сервер. Задача веб-сервера - обработать этот запрос, сделав что-нибудь. Возможно, вы уже догадались, что мы собираемся контролировать устройство, обращаясь к определенному URL-адресу. Например, предположим, что мы ввели URL-адрес типа http://192.168.1.1/ledon в браузере. Затем браузер отправляет HTTP-запрос в ESP32 для обработки этого запроса. Когда ESP32 читает этот запрос, он знает, что пользователь хочет включить светодиод. Таким образом, он включает светодиод и отдает динамическую веб-страницу в браузер, показывая состояние светодиода: ВКЛ. Не так все это и сложно!

ESP32 в качестве HTTP-сервера с использованием режима точки доступа WiFi (AP).

А теперь перейдем к самому интересному!

Как следует из заголовка, этот пример демонстрирует, как превратить ESP32 в точку доступа (AP) и отдавать веб-страницы для любого подключенного клиента. Для начала подключите ESP32 к компьютеру и загрузите код, который затем разберем более подробно.

#include <WiFi.h>
#include <WebServer.h>
/* Установите здесь свои SSID и пароль */
const char* ssid = "ESP32";  
const char* password = "01234567";  
/* Настройки IP адреса */
IPAddress local_ip(192,168,2,1);
IPAddress gateway(192,168,2,1);
IPAddress subnet(255,255,255,0);
WebServer server(80);
uint8_t LED1pin = 4;
bool LED1status = LOW;
uint8_t LED2pin = 5;
bool LED2status = LOW;
void setup() {
  Serial.begin(115200);
  pinMode(LED1pin, OUTPUT);
  pinMode(LED2pin, OUTPUT);
  WiFi.softAP(ssid, password);
  WiFi.softAPConfig(local_ip, gateway, subnet);
  delay(100);
  server.on("/", handle_OnConnect);
  server.on("/led1on", handle_led1on);
  server.on("/led1off", handle_led1off);
  server.on("/led2on", handle_led2on);
  server.on("/led2off", handle_led2off);
  server.onNotFound(handle_NotFound);
  server.begin();
  Serial.println("HTTP server started");
}
void loop() {
  server.handleClient();
  if(LED1status)
  {digitalWrite(LED1pin, HIGH);}
  else
  {digitalWrite(LED1pin, LOW);}
  if(LED2status)
  {digitalWrite(LED2pin, HIGH);}
  else
  {digitalWrite(LED2pin, LOW);}
}
void handle_OnConnect() {
  LED1status = LOW;
  LED2status = LOW;
  Serial.println("GPIO4 Status: OFF | GPIO5 Status: OFF");
  server.send(200, "text/html", SendHTML(LED1status,LED2status)); 
}
void handle_led1on() {
  LED1status = HIGH;
  Serial.println("GPIO4 Status: ON");
  server.send(200, "text/html", SendHTML(true,LED2status)); 
}
void handle_led1off() {
  LED1status = LOW;
  Serial.println("GPIO4 Status: OFF");
  server.send(200, "text/html", SendHTML(false,LED2status)); 
}
void handle_led2on() {
  LED2status = HIGH;
  Serial.println("GPIO5 Status: ON");
  server.send(200, "text/html", SendHTML(LED1status,true)); 
}
void handle_led2off() {
  LED2status = LOW;
  Serial.println("GPIO5 Status: OFF");
  server.send(200, "text/html", SendHTML(LED1status,false)); 
}
void handle_NotFound(){
  server.send(404, "text/plain", "Not found");
}
String SendHTML(uint8_t led1stat,uint8_t led2stat){
  String ptr = "<!DOCTYPE html> <html>\n";
  ptr +="<meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
  ptr +="<title>Управление светодиодом</title>\n";
  ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
  ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
  ptr +=".button {display: block;width: 80px;background-color: #3498db;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";
  ptr +=".button-on {background-color: #3498db;}\n";
  ptr +=".button-on:active {background-color: #2980b9;}\n";
  ptr +=".button-off {background-color: #34495e;}\n";
  ptr +=".button-off:active {background-color: #2c3e50;}\n";
  ptr +="p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";
  ptr +="</style>\n";
  ptr +="</head>\n";
  ptr +="<body>\n";
  ptr +="<h1>ESP32 Веб сервер</h1>\n";
    ptr +="<h3>Режим точка доступа WiFi (AP)</h3>\n";
   if(led1stat)
  {ptr +="<p>Состояние LED1: ВКЛ.</p><a class=\"button button-off\" href=\"/led1off\">ВЫКЛ.</a>\n";}
  else
  {ptr +="<p>Состояние LED1: ВЫКЛ.</p><a class=\"button button-on\" href=\"/led1on\">ВКЛ.</a>\n";}
  if(led2stat)
  {ptr +="<p>Состояние LED2: ВКЛ.</p><a class=\"button button-off\" href=\"/led2off\">ВЫКЛ.</a>\n";}
  else
  {ptr +="<p>Состояние LED2: ВЫКЛ.</p><a class=\"button button-on\" href=\"/led2on\">ВКЛ.</a>\n";}
  ptr +="</body>\n";
  ptr +="</html>\n";
  return ptr;
}

Доступ к веб-серверу в режиме AP.

После загрузки скетча откройте Serial Monitor со скоростью 115200 бод. И нажмите кнопку RESET на ESP32. Если все в порядке, то отобразится сообщение о запуске HTTP-сервера.

И нажмите кнопку RESET на ESP32. Если все в порядке, то отобразится сообщение о запуске HTTP-сервера.

Затем найдите любое устройство, которое можно подключить к сети Wi-Fi - телефон, ноутбук и т. д. И найдите сеть под названием «ESP32». Подключитесь к сети с паролем «01234567».

И найдите сеть под названием «ESP32». Подключитесь к сети с паролем «01234567».

После подключения к сети ESP32 AP, загрузите браузер и укажите адрес 192.168.1.1. ESP32 должен открыть веб-страницу, показывающую текущее состояние светодиодов, и две кнопки для управления ими.

 ESP32 должен открыть веб-страницу, показывающую текущее состояние светодиодов

Если одновременно взглянуть на монитор последовательного порта, то можно увидеть состояние контактов GPIO ESP32.

Теперь нажмите кнопку, чтобы включить LED1, следя за URL-адресом. После того, как вы нажали кнопку, ESP32 получит запрос URL-адреса / led1on . Затем он включит LED1 и обновит веб-страницу с новым статусом LED.

Теперь нажмите кнопку, чтобы включить LED1, следя за URL-адресом.

Он также покажет состояние вывода GPIO в последовательном мониторе порта.

Он также покажет состояние вывода GPIO в последовательном мониторе порта.

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

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


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

Подробное объяснение кода точки доступа WiFi (AP).

Скетч начинается с подключения библиотеки WiFi.h. Эта библиотека предоставляет специальные методы WiFi для ESP32, которые мы вызываем для подключения к сети. После этого мы также подключаем библиотеку WebServer.h, в которой есть несколько доступных методов, которые помогут нам настроить сервер и обрабатывать входящие HTTP-запросы, не беспокоясь о деталях реализации на низком уровне.

#include <WiFi.h>
#include <WebServer.h>

Когда мы устанавливаем ESP32 в режим точки доступа (AP), он создает сеть Wi-Fi. Следовательно, нам нужно установить его SSID, пароль, IP-адрес, маску IP-подсети и IP-шлюз.

/* Установите здесь свои SSID и пароль */
const char* ssid = "ESP32";  // Enter SSID here
const char* password = "0123456";  //Enter Password here
/* Настройки IP адреса */
IPAddress local_ip(192,168,2,1);
IPAddress gateway(192,168,2,1);
IPAddress subnet(255,255,255,0);

Затем мы объявляем объект библиотеки WebServer, чтобы получить доступ к его функциям. Конструктор этого объекта принимает порт (который сервер будет слушать) в качестве параметра. Поскольку 80 - порт по умолчанию для HTTP, мы будем использовать это значение. Теперь вы можете получить доступ к серверу без необходимости указывать порт в URL-адресе.

WebServer server(80);

После чего мы объявляем выводы GPIO ESP32, к которым подключены светодиоды, и их начальное состояние.

uint8_t LED1pin = 4;
bool LED1status = LOW;
uint8_t LED2pin = 5;
bool LED2status = LOW;

Функция Setup ().

Мы настраиваем наш HTTP-сервер перед его запуском. Прежде всего, мы открываем последовательное соединение для отладки и устанавливаем для GPIO значение OUTPUT.

Serial.begin(115200);
pinMode(LED1pin, OUTPUT);
pinMode(LED2pin, OUTPUT);

Затем мы настраиваем программную точку доступа для создания сети Wi-Fi, проверяя SSID, пароль, IP-адрес, маску IP-подсети и IP-шлюз.

WiFi.softAP(ssid, password);
WiFi.softAPConfig(local_ip, gateway, subnet);
delay(100);

Чтобы обрабатывать входящие HTTP-запросы, нам нужно указать, какой код выполнять при вызове конкретного URL. Для этого мы используем метод on . Этот метод принимает два параметра. Первый - это URL-путь, а второй - имя функции, которую мы хотим выполнить при переходе по этому URL-адресу.

Например, первая строка приведенного ниже фрагмента кода указывает, что, когда сервер получает HTTP-запрос по корневому ( / ) пути, он запускает handle_OnConnect() функцию. Обратите внимание, что указанный URL-адрес является относительным путем.

Точно так же нам нужно указать еще 4 URL-адреса для обработки двух состояний 2 светодиодов.

server.on("/", handle_OnConnect);
server.on("/led1on", handle_led1on);
server.on("/led1off", handle_led1off);
server.on("/led2on", handle_led2on);
server.on("/led2off", handle_led2off);

Мы не указали, что должен делать сервер, если клиент запрашивает любой URL-адрес, отличный от указанного в server.on(). Он должен ответить HTTP-статусом 404 (Not Found) и сообщением для пользователя. Мы также помещаем в функцию и используем server.onNotFound(), чтобы сообщить, что она должна выполнить ее, когда получит запрос на URI, который не был указан в server.on.

server.onNotFound(handle_NotFound);

Теперь, чтобы запустить наш сервер, мы вызываем метод begin объекта server.

server.begin();
Serial.println("HTTP server started");

Основной цикл Loop().

Чтобы обрабатывать входящие HTTP-запросы, нам нужно вызвать handleClient() метод объекта сервера. Также мы меняем состояние светодиода по запросу.

void loop() {
  server.handleClient();
  if(LED1status)
  {digitalWrite(LED1pin, HIGH);}
  else
  {digitalWrite(LED1pin, LOW);}
  if(LED2status)
  {digitalWrite(LED2pin, HIGH);}
  else
  {digitalWrite(LED2pin, LOW);}
}

Затем нам нужно создать функцию, которую мы назначили корневому (/) URL с помощью server.on. В начале этой функции мы устанавливаем состояние обоих светодиодов на LOW (исходное состояние светодиодов) и выводим состояние светодиодов в последовательный монитор. Чтобы ответить на HTTP-запрос, мы используем метод send. Хотя метод может быть вызван с другим набором аргументов, его простейшая форма состоит из кода ответа HTTP.

В нашем случае мы отправляем код 200 (один из кодов состояния HTTP), который соответствует ответу OK - ответ получен. Затем мы указываем тип содержимого как «текст / html» и, наконец, вызываем пользовательскую функцию SendHTML (), которая создает динамическую HTML-страницу, содержащую состояние светодиодов.

void handle_OnConnect() {
  LED1status = LOW;
  LED2status = LOW;
  Serial.println("GPIO4 Status: OFF | GPIO5 Status: OFF");
  server.send(200, "text/html", SendHTML(LED1status,LED2status)); 
}

Точно так же нам нужно создать четыре функции для обработки запросов включения / выключения светодиодов и страницы ошибки 404.

void handle_led1on() {
  LED1status = HIGH;
  Serial.println("GPIO4 Status: ON");
  server.send(200, "text/html", SendHTML(true,LED2status)); 
}
void handle_led1off() {
  LED1status = LOW;
  Serial.println("GPIO4 Status: OFF");
  server.send(200, "text/html", SendHTML(false,LED2status)); 
}
void handle_led2on() {
  LED2status = HIGH;
  Serial.println("GPIO5 Status: ON");
  server.send(200, "text/html", SendHTML(LED1status,true)); 
}
void handle_led2off() {
  LED2status = LOW;
  Serial.println("GPIO5 Status: OFF");
  server.send(200, "text/html", SendHTML(LED1status,false)); 
}
void handle_NotFound(){
  server.send(404, "text/plain", "Not found");
}

Отображение веб-страницы HTML.

SendHTML() Функция отвечает за создание веб-страницы всякий раз, когда веб-сервер ESP32 получает запрос от веб-клиента. Он просто объединяет HTML-код в большую строку и возвращает server.send() функцию, которую мы обсуждали ранее. Функция принимает состояние светодиодов в качестве параметров для динамической генерации содержимого HTML.

Первый текст, который вы всегда должны отправить <! DOCTYPE>, который указывает на то, что мы посылаем HTML код.

String SendHTML(uint8_t led1stat,uint8_t led2stat){
String ptr = "<!DOCTYPE html> <html>\n";

Затем элемент <meta> viewport заставляет веб-страницу загружаться в любом браузере. В то время как тег title устанавливает заголовок страницы.

ptr +="<meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
ptr +="<title>Управление светодиодом</title>\n";

Стилизация веб-страницы.

Затем у нас есть CSS для стилизации кнопок и внешнего вида веб-страницы. Мы выбираем шрифт Helvetica, определяем контент, который будет отображаться, в виде встроенного блока, и выравниваться по центру.

ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";

Затем следующий код устанавливает цвет, шрифт и поля тегов H1, H3 и p.

ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
ptr +="p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";

Дальше прописывая стили, которые применяются к кнопкам, указав свойства: цвет, размер, поля и т. д. Кнопки ВКЛ. и ВЫКЛ. имеют разный цвет фона, в то время как :active selector для кнопок обеспечивает эффект нажатия кнопки.

ptr +=".button {display: block;width: 80px;background-color: #3498db;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";
ptr +=".button-on {background-color: #3498db;}\n";
ptr +=".button-on:active {background-color: #2980b9;}\n";
ptr +=".button-off {background-color: #34495e;}\n";
ptr +=".button-off:active {background-color: #2c3e50;}\n";

Установка заголовка веб-страницы.

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

ptr +="<h1>ESP32 Веб сервер</h1>\n";
ptr +="<h3>Режим точка доступа WiFi (AP)</h3>\n";

Отображение кнопок, и соответствующего их состояния

Чтобы динамически генерировать кнопки и статус светодиода, мы используем оператор if. Таким образом, в зависимости от состояния контактов GPIO отображается кнопка ВКЛ / ВЫКЛ.

if(led1stat)
  {ptr +="<p>Состояние LED1: ВКЛ.</p><a class=\"button button-off\" href=\"/led1off\">ВЫКЛ.</a>\n";}
  else
  {ptr +="<p>Состояние LED1: ВЫКЛ.</p><a class=\"button button-on\" href=\"/led1on\">ВКЛ.</a>\n";}
if(led2stat)
  {ptr +="<p>Состояние LED2: ВКЛ.</p><a class=\"button button-off\" href=\"/led2off\">ВЫКЛ.</a>\n";}
  else
  {ptr +="<p>Состояние LED2: ВЫКЛ.</p><a class=\"button button-on\" href=\"/led2on\">ВКЛ.</a>\n";}

ESP32 как HTTP-сервер в режиме WiFi Station (STA)

Теперь перейдем к нашему следующему примеру, который демонстрирует, как использовать ESP32 в режиме станции (STA) и выводить веб-страницы для любого подключенного клиента в сети.

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

ESP32 как HTTP-сервер в режиме WiFi Station (STA)

Должен получиться вот такое код.

#include <WiFi.h>
#include <WebServer.h>
/* Установите здесь свои SSID и пароль */
const char* ssid = "Wi-Fi";  
const char* password = "password";  
WebServer server(80);
uint8_t LED1pin = 4;
bool LED1status = LOW;
uint8_t LED2pin = 5;
bool LED2status = LOW;
void setup() {
  Serial.begin(115200);
  delay(100);
  pinMode(LED1pin, OUTPUT);
  pinMode(LED2pin, OUTPUT);
  Serial.println("Connecting to ");
  Serial.println(ssid);
  //connect to your local wi-fi network
  WiFi.begin(ssid, password);
  //check wi-fi is connected to wi-fi network
  while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected..!");
  Serial.print("Got IP: ");  Serial.println(WiFi.localIP());
  server.on("/", handle_OnConnect);
  server.on("/led1on", handle_led1on);
  server.on("/led1off", handle_led1off);
  server.on("/led2on", handle_led2on);
  server.on("/led2off", handle_led2off);
  server.onNotFound(handle_NotFound);
  server.begin();
  Serial.println("HTTP server started");
}
void loop() {
  server.handleClient();
  if(LED1status)
  {digitalWrite(LED1pin, HIGH);}
  else
  {digitalWrite(LED1pin, LOW);}
  if(LED2status)
  {digitalWrite(LED2pin, HIGH);}
  else
  {digitalWrite(LED2pin, LOW);}
}
void handle_OnConnect() {
  LED1status = LOW;
  LED2status = LOW;
  Serial.println("GPIO4 Status: OFF | GPIO5 Status: OFF");
  server.send(200, "text/html", SendHTML(LED1status,LED2status)); 
}
void handle_led1on() {
  LED1status = HIGH;
  Serial.println("GPIO4 Status: ON");
  server.send(200, "text/html", SendHTML(true,LED2status)); 
}
void handle_led1off() {
  LED1status = LOW;
  Serial.println("GPIO4 Status: OFF");
  server.send(200, "text/html", SendHTML(false,LED2status)); 
}
void handle_led2on() {
  LED2status = HIGH;
  Serial.println("GPIO5 Status: ON");
  server.send(200, "text/html", SendHTML(LED1status,true)); 
}
void handle_led2off() {
  LED2status = LOW;
  Serial.println("GPIO5 Status: OFF");
  server.send(200, "text/html", SendHTML(LED1status,false)); 
}
void handle_NotFound(){
  server.send(404, "text/plain", "Not found");
}
String SendHTML(uint8_t led1stat,uint8_t led2stat){
  String ptr = "<!DOCTYPE html> <html>\n";
  ptr +="<meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
  ptr +="<title>Управление светодиодом</title>\n";
  ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
  ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
  ptr +=".button {display: block;width: 80px;background-color: #3498db;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";
  ptr +=".button-on {background-color: #3498db;}\n";
  ptr +=".button-on:active {background-color: #2980b9;}\n";
  ptr +=".button-off {background-color: #34495e;}\n";
  ptr +=".button-off:active {background-color: #2c3e50;}\n";
  ptr +="p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";
  ptr +="</style>\n";
  ptr +="</head>\n";
  ptr +="<body>\n";
  ptr +="<h1>ESP32 Веб сервер</h1>\n";
    ptr +="<h3>Режим станции (STA)</h3>\n";
   if(led1stat)
  {ptr +="<p>Состояние LED1: ВКЛ.</p><a class=\"button button-off\" href=\"/led1off\">ВЫКЛ.</a>\n";}
  else
  {ptr +="<p>Состояние LED1: ВЫКЛ.</p><a class=\"button button-on\" href=\"/led1on\">ВКЛ.</a>\n";}
  if(led2stat)
  {ptr +="<p>Состояние LED2: ВКЛ.</p><a class=\"button button-off\" href=\"/led2off\">ВЫКЛ.</a>\n";}
  else
  {ptr +="<p>Состояние LED2: ВЫКЛ.</p><a class=\"button button-on\" href=\"/led2on\">ВКЛ.</a>\n";}
  ptr +="</body>\n";
  ptr +="</html>\n";
  return ptr;
}

Доступ к веб-серверу в режиме STA.

После загрузки скетча откройте Serial Monitor со скоростью 115200 бод. И нажмите кнопку RESET на ESP32. Если все в порядке, он выведет динамический IP-адрес, полученный от вашего маршрутизатора, и покажет сообщение о запуске HTTP-сервера.

Доступ к веб-серверу в режиме STA.

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

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

Если посмотреть в монитор последовательного порта, то можно увидеть состояние контактов GPIO ESP32.

 Если посмотреть в монитор последовательного порта, то можно увидеть состояние контактов GPIO ESP32.

Теперь нажмите кнопку, чтобы включить LED1, следя за URL-адресом. После того, как вы нажмете кнопку, ESP32 получит запрос URL-адреса / led1on . Затем он включает LED1 и обновляет веб-страницу с новым статусом LED.

ESP32 получит запрос URL-адреса / led1on . ESP32 получит запрос URL-адреса / led1on .

Данные также выводятся о состоянии вывода GPIO в последовательный монитор порта.

 Данные также выводятся о состоянии вывода GPIO в последовательный монитор порта.

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

Пояснение к коду.

Если вы сравните этот код с предыдущим кодом, единственное отличие состоит в том, что мы не устанавливаем программную точку доступа, а подключаемся к существующей сети с помощью WiFi.begin() функции.

//connect to your local wi-fi network
WiFi.begin(ssid, password);

Пока ESP32 пытается подключиться к сети, мы можем проверить статус подключения с помощью WiFi.status() функции.

//check wi-fi is connected to wi-fi network
while (WiFi.status() != WL_CONNECTED) 
{
delay(1000);
Serial.print(".");
}

Данная функция возвращает следующие статусы:

  • WL_CONNECTED: возвращается при подключении к сети Wi-Fi;
  • WL_NO_SHIELD: возвращается, когда Wi-Fi модуль не подключен;
  • WL_IDLE_STATUS: временное состояние, возвращаемое при вызове WiFi.begin(), и остающееся активным до тех пор, пока не истечет количество попыток подключения (что приводит к WL_CONNECT_FAILED), или пока не будет установлено соединение (что приводит к WL_CONNECTED);
  • WL_NO_SSID_AVAIL: возвращается, когда нет доступных SSID;
  • WL_SCAN_COMPLETED: возвращается, когда сканирование сетей завершено;
  • WL_CONNECT_FAILED: возвращается при неудаче подключения после всех попыток;
  • WL_CONNECTION_LOST: возвращается при потере соединения;
  • WL_DISCONNECTED: возвращается при отключении от сети.

Как только ESP32 подключен к сети, в мониторе порта вы увидите IP-адрес, который выводит функция WiFi.localIP.

Serial.println("");
Serial.println("WiFi connected..!");
Serial.print("Got IP: ");  Serial.println(WiFi.localIP());

Единственная разница между режимами AP и STA заключается в том, что один создает сеть, а другой присоединяется к существующей сети. Итак, остальная часть кода для обработки HTTP-запросов и обслуживания веб-страницы в режиме STA такая же, как и в режиме AP, описанном выше, которая включает в себя:

  • Объявление контактов GPIO ESP32, к которым подключены светодиоды
  • Определение нескольких методов server.on () для обработки входящих HTTP-запросов
  • Определение метода server.onNotFound () для обработки ошибки HTTP 404
  • Создание пользовательских функций, которые выполняются при переходе по определенным URL
  • Создание HTML-страницы
  • Стилизация веб-страницы
  • Создание кнопок и отображение их статуса

Создание простого веб-сервера на ESP8266 (NodeMCU) в Arduino IDE.

Создание веб-сервера на ESP32 мы с вами рассмотрели. Сейчас посмотрим, что нужно изменить, чтобы данный код был применим и для ESP8266. Для этого нам нужно рассмотреть схему подключения.

Что такое NodeMCU? Программируем в среде Arduino IDE

Схема подключения двух светодиодов к ESP8266 NodeMCU.

Не смотря на то, что распиновка плат отличается, в примере для обеих плат будем использовать GPIO D4 и D5. Это поможет объединить код в дальнейшем.

Схема подключения двух светодиодов к ESP8266 NodeMCU.

В итоге у нас должно получиться подключение как на схеме.

Использование ESP8266 (NodeMCU) в качестве HTTP сервера в режиме Wi-Fi Station (STA)

Давайте рассмотрим пример использования ESP8266 (NodeMCU) в качестве HTTP сервера в режиме Wi-Fi Station (STA). Прежде чем приступить к загрузке скетча, чтобы он у вас заработал, необходимо внести некоторые изменения. Чтобы ESP8266 мог установить соединение с существующей сетью, вам необходимо изменить две следующие переменные с учетными данными вашей сети.

Использование ESP8266 в качестве HTTP сервера в режиме Wi-Fi Station (STA)

После чего можно загрузить код в ESP8266 (NodeMCU).

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
// Определяем переменные wifi
const char* ssid = "Wi-Fi";  
const char* password = "password"; 
// Web интерфейс для устройства
ESP8266WebServer server(80);
uint8_t LED1pin = D4;
uint8_t LED2pin = D5;
bool LED1status = LOW;
bool LED2status = LOW;
void setup() {
  Serial.begin(115200);
  delay(100);
  pinMode(LED1pin, OUTPUT);
  pinMode(LED2pin, OUTPUT);
  Serial.println("Connecting to ");
  Serial.println(ssid);
  //connect to your local wi-fi network
  WiFi.begin(ssid, password);
  //check wi-fi is connected to wi-fi network
  while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected..!");
  Serial.print("Got IP: ");  Serial.println(WiFi.localIP());
  server.on("/", handle_OnConnect);
  server.on("/led1on", handle_led1on);
  server.on("/led1off", handle_led1off);
  server.on("/led2on", handle_led2on);
  server.on("/led2off", handle_led2off);
  server.onNotFound(handle_NotFound);
  server.begin();
  Serial.println("HTTP server started");
}
void loop() {
  server.handleClient();
  if(LED1status)
  {digitalWrite(LED1pin, HIGH);}
  else
  {digitalWrite(LED1pin, LOW);}
  if(LED2status)
  {digitalWrite(LED2pin, HIGH);}
  else
  {digitalWrite(LED2pin, LOW);}
}
void handle_OnConnect() {
  LED1status = LOW;
  LED2status = LOW;
  Serial.println("GPIO4 Status: OFF | GPIO5 Status: OFF");
  server.send(200, "text/html", SendHTML(LED1status,LED2status)); 
}
void handle_led1on() {
  LED1status = HIGH;
  Serial.println("GPIO4 Status: ON");
  server.send(200, "text/html", SendHTML(true,LED2status)); 
}
void handle_led1off() {
  LED1status = LOW;
  Serial.println("GPIO4 Status: OFF");
  server.send(200, "text/html", SendHTML(false,LED2status)); 
}
void handle_led2on() {
  LED2status = HIGH;
  Serial.println("GPIO5 Status: ON");
  server.send(200, "text/html", SendHTML(LED1status,true)); 
}
void handle_led2off() {
  LED2status = LOW;
  Serial.println("GPIO5 Status: OFF");
  server.send(200, "text/html", SendHTML(LED1status,false)); 
}
void handle_NotFound(){
  server.send(404, "text/plain", "Not found");
}
String SendHTML(uint8_t led1stat,uint8_t led2stat){
  String ptr = "<!DOCTYPE html> <html>\n";
  ptr +="<meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
  ptr +="<title>Управление светодиодом</title>\n";
  ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
  ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
  ptr +=".button {display: block;width: 80px;background-color: #3498db;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";
  ptr +=".button-on {background-color: #3498db;}\n";
  ptr +=".button-on:active {background-color: #2980b9;}\n";
  ptr +=".button-off {background-color: #34495e;}\n";
  ptr +=".button-off:active {background-color: #2c3e50;}\n";
  ptr +="p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";
  ptr +="</style>\n";
  ptr +="</head>\n";
  ptr +="<body>\n"; 
  ptr +="<h1>ESP8266 Веб сервер</h1>\n";
  ptr +="<h3>Режим станции (STA)</h3>\n";
   if(led1stat)
  {ptr +="<p>Состояние LED1: ВКЛ.</p><a class=\"button button-off\" href=\"/led1off\">ВЫКЛ.</a>\n";}
  else
  {ptr +="<p>Состояние LED1: ВЫКЛ.</p><a class=\"button button-on\" href=\"/led1on\">ВКЛ.</a>\n";}
  if(led2stat)
  {ptr +="<p>Состояние LED2: ВКЛ.</p><a class=\"button button-off\" href=\"/led2off\">ВЫКЛ.</a>\n";}
  else
  {ptr +="<p>Состояние LED2: ВЫКЛ.</p><a class=\"button button-on\" href=\"/led2on\">ВКЛ.</a>\n";}
  ptr +="</body>\n";
  ptr +="</html>\n";
  return ptr;
}

Доступ к веб-серверу в режиме STA при использовании ESP8266.

После загрузки кода в ESP8266 NodeMCU, как и в примере с ESP32, в мониторе порта увидим знакомую информацию.

После загрузки кода в ESP8266 NodeMCU, как и в примере с ESP32, в мониторе порта увидим знакомую информацию.

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

NodeMCU должен выдать веб-страницу, показывающую текущее состояние светодиодов, и две кнопки для управления ими.

Если в это время взглянуть на монитор последовательного порта, то можно увидеть состояние выводов GPIO NodeMCU.

Если в это время взглянуть на монитор последовательного порта, то можно увидеть состояние выводов GPIO NodeMCU.

В работе данного примера нет ни каких изменений в сравнении с работай ESP32.

Изменения в скетче для ESP8266 NodeMCU.

Первым делом нам нужно изменить библиотеки для работы с ESP8266.

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

Затем объявляем объект библиотеки ESP8266WebServer, чтобы получить доступ к ее функциям. Конструктор этого объекта в качестве параметра принимает номер порта (который сервер будет прослушивать).

// Web интерфейс для устройства
ESP8266WebServer server(80);

GPIO у данной платы также обозначаются по другому, их следует также изменить.

uint8_t LED1pin = D4;
uint8_t LED2pin = D5;

Осталось подправитьв HTML странице выводимую информацию. Здесь изменим заголовок страницы. Чтобы было понято, что данный пример работает на ESP8266.

  ptr +="<h1>ESP8266 Веб сервер</h1>\n";
  ptr +="<h3>Режим станции (STA)</h3>\n";

Изменить код для работы ESP8266 в режиме точки доступа (AP) будет не сложно. Это будет для вас домашнее задание.

Доступ к веб-серверу в режиме STA код для ESP32 end ESP8266.

Чтобы не плодить десятки версий кода для ESP32 и ESP8266, код можно объединить, что позволит, используя один скетч и прошивать ESP32 и ESP8266, достаточно только выбрать нужную плату в Arduino IDE.

#ifdef ESP8266
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#else
#include <WiFi.h>
#include <WebServer.h>
#endif
// Определяем переменные wifi
const char* ssid = "Wi-Fi";  
const char* password = "password";  
#ifdef ESP8266
// Web интерфейс для устройства
ESP8266WebServer server(80);
uint8_t LED1pin = D4;
uint8_t LED2pin = D5;
#else
// Web интерфейс для устройства
WebServer server(80);
uint8_t LED1pin = 4;
uint8_t LED2pin = 5;
#endif
bool LED1status = LOW;
bool LED2status = LOW;
void setup() {
  Serial.begin(115200);
  delay(100);
  pinMode(LED1pin, OUTPUT);
  pinMode(LED2pin, OUTPUT);
  Serial.println("Connecting to ");
  Serial.println(ssid);
  //connect to your local wi-fi network
  WiFi.begin(ssid, password);
  //check wi-fi is connected to wi-fi network
  while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected..!");
  Serial.print("Got IP: ");  Serial.println(WiFi.localIP());
  server.on("/", handle_OnConnect);
  server.on("/led1on", handle_led1on);
  server.on("/led1off", handle_led1off);
  server.on("/led2on", handle_led2on);
  server.on("/led2off", handle_led2off);
  server.onNotFound(handle_NotFound);
  server.begin();
  Serial.println("HTTP server started");
}
void loop() {
  server.handleClient();
  if(LED1status)
  {digitalWrite(LED1pin, HIGH);}
  else
  {digitalWrite(LED1pin, LOW);}
  if(LED2status)
  {digitalWrite(LED2pin, HIGH);}
  else
  {digitalWrite(LED2pin, LOW);}
}
void handle_OnConnect() {
  LED1status = LOW;
  LED2status = LOW;
  Serial.println("GPIO4 Status: OFF | GPIO5 Status: OFF");
  server.send(200, "text/html", SendHTML(LED1status,LED2status)); 
}
void handle_led1on() {
  LED1status = HIGH;
  Serial.println("GPIO4 Status: ON");
  server.send(200, "text/html", SendHTML(true,LED2status)); 
}
void handle_led1off() {
  LED1status = LOW;
  Serial.println("GPIO4 Status: OFF");
  server.send(200, "text/html", SendHTML(false,LED2status)); 
}
void handle_led2on() {
  LED2status = HIGH;
  Serial.println("GPIO5 Status: ON");
  server.send(200, "text/html", SendHTML(LED1status,true)); 
}
void handle_led2off() {
  LED2status = LOW;
  Serial.println("GPIO5 Status: OFF");
  server.send(200, "text/html", SendHTML(LED1status,false)); 
}
void handle_NotFound(){
  server.send(404, "text/plain", "Not found");
}
String SendHTML(uint8_t led1stat,uint8_t led2stat){
  String ptr = "<!DOCTYPE html> <html>\n";
  ptr +="<meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
  ptr +="<title>Управление светодиодом</title>\n";
  ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
  ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
  ptr +=".button {display: block;width: 80px;background-color: #3498db;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";
  ptr +=".button-on {background-color: #3498db;}\n";
  ptr +=".button-on:active {background-color: #2980b9;}\n";
  ptr +=".button-off {background-color: #34495e;}\n";
  ptr +=".button-off:active {background-color: #2c3e50;}\n";
  ptr +="p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";
  ptr +="</style>\n";
  ptr +="</head>\n";
  ptr +="<body>\n"; 
  #ifdef ESP8266
  ptr +="<h1>ESP8266 Веб сервер</h1>\n";
  #else
  ptr +="<h1>ESP32 Веб сервер</h1>\n";
  #endif
    ptr +="<h3>Режим станции (STA)</h3>\n";
   if(led1stat)
  {ptr +="<p>Состояние LED1: ВКЛ.</p><a class=\"button button-off\" href=\"/led1off\">ВЫКЛ.</a>\n";}
  else
  {ptr +="<p>Состояние LED1: ВЫКЛ.</p><a class=\"button button-on\" href=\"/led1on\">ВКЛ.</a>\n";}
  if(led2stat)
  {ptr +="<p>Состояние LED2: ВКЛ.</p><a class=\"button button-off\" href=\"/led2off\">ВЫКЛ.</a>\n";}
  else
  {ptr +="<p>Состояние LED2: ВЫКЛ.</p><a class=\"button button-on\" href=\"/led2on\">ВКЛ.</a>\n";}
  ptr +="</body>\n";
  ptr +="</html>\n";
  return ptr;
}

Вод такой код получается. Пояснять его не буду. Тут добавились 3 условия, а остальной код мы рассмотрели выше.

На этом урок заканчиваю. Если вам понравился Урок, не забудьте нажать на сердечко в правой колонке сверху страницы, или ниже статьи, если вы читаете с телефона.

Понравился Урок 1. Веб-сервер ESP32 (ESP8266) в среде Arduino IDE? Не забудь поделиться с друзьями в соц. сетях.

А также подписаться на наш канал на YouTube, вступить в группу Вконтакте, в группу на Facebook.

Спасибо за внимание!

Технологии начинаются с простого!

Фотографии к статье

Файлы для скачивания

ESP32_AP ESP32_AP.ino4 Kb 314 Скачать
ESP32_STA ESP32_STA.ino4 Kb 263 Скачать
esp8266_STA esp8266_STA.ino4 Kb 254 Скачать
ESP32_and_esp8266_STA ESP32_and_esp8266_STA.ino4 Kb 244 Скачать
ESP8266_AP ESP8266_AP.ino4 Kb 252 Скачать

Комментарии

Ваше Имя*

Гость

Гость: Гость (14 ноября, 2020 в 05:45)

Как сделать веб- сервер с использованием файловой системы esp?

Admin

Admin (5 ноября, 2020 в 16:44)
А на 10 диодов 10 страниц делать? Так себе ваш урок.
Вы о чем? в уроке используется всего 2 светодиода!

Ноь

Гость: Ноь (25 октября, 2020 в 22:26)

А на 10 диодов 10 страниц делать? Так себе ваш урок.