В данной статье мы рассмотрим создание веб-сервера на модуле ESP32 для управления светодиодом: настройку ESP32, подключение светодиодов и создание веб-интерфейса для управления ими. Независимо от того, являетесь ли вы опытным электронщиком или только начинаете заниматься проектами Интернета вещей, я думаю, эта статья поможет вам поднять свои навыки в этой тематике на новый уровень.
Ранее на нашем сайте мы уже рассматривали создание веб-сервера на ESP32 для измерения температуры и влажности, но рассматриваемый в этой статье подход немного другой - на основе этих двух статей статей вы сами сможете выбрать какой из этих подходов вам подойдет лучше.
Также на нашем сайте вы можете посмотреть проекты веб-серверов на основе других микроконтроллеров/плат:
- веб-сервер AJAX на ESP8266;
- веб-сервер на основе ESP8266 и платы STM32F103C8;
- веб-сервер на Node.js и Raspberry Pi: управление светодиодом;
- веб-сервер на Raspberry Pi с хостингом сайта на WordPress.
Давайте кратко представим, что нам нужно сделать, чтобы наш веб-сервер заработал:
- подключение светодиодов к контактам GPIO12 и GPIO14 модуля ESP32, чтобы мы могли ими управлять;
- после завершения загрузки программы в модуль попробовать получить доступ к веб-странице управления, введя IP-адрес ESP32 в браузере;
- нажимать кнопки на веб-странице, чтобы включить или выключить светодиод.
Необходимые компоненты
- Плата разработки ESP32 (купить на AliExpress) — это основной компонент, который будет использоваться для управления светодиодами через веб-сервер.
- Светодиоды (купить на AliExpress) — вы можете использовать любой тип светодиодов в зависимости от требований вашего проекта. Для этого проекта мы использовали 2 светодиода диаметром 5 мм.
- Макетная плата — она будет использоваться для подключения светодиодов к микроконтроллеру ESP32.
- Соединительные провода — они будут использоваться для подключения макетной платы к микроконтроллеру ESP32 и светодиодным индикаторам.
- USB-кабель — для подключения платы разработки ESP32 к компьютеру для программирования.
- Компьютер — для программирования и загрузки кода на плату разработки ESP32.
- Резисторы (купить на AliExpress) — для ограничения тока, проходящего через светодиоды, и предотвращения их перегорания.
Часто задаваемые вопросы о ESP32
Может ли ESP32 быть одновременно сервером и клиентом?
Да, ESP32 может работать как сервер, так и клиент в сети. Он может прослушивать входящие запросы в качестве сервера и отвечать на них, а также инициировать запросы в качестве клиента для доступа к данным или ресурсам на других устройствах. Эта возможность делает ESP32 универсальной платформой для различных приложений, таких как Интернет вещей, где он может выступать в качестве концентратора для нескольких устройств. Встроенные возможности подключения Wi-Fi и Bluetooth ESP32 еще больше расширяют его функциональность в качестве сетевого устройства.
Какое имя хоста у веб-сервера ESP32?
Имя хоста веб-сервера ESP32 может быть либо IP-адресом устройства ESP32, либо назначенным ему доменным именем. По умолчанию имя хоста устройства ESP32 — «Espressif». Однако это можно изменить, изменив сетевые настройки устройства или запрограммировав имя хоста с помощью Arduino IDE. Имя хоста используется другими устройствами в сети для идентификации веб-сервера ESP32 и подключения к нему. При доступе к веб-серверу с помощью веб-браузера имя хоста можно ввести в поле URL-адреса вместе с соответствующим номером порта.
Что такое WebSocket в ESP32?
WebSocket — это протокол связи, который обеспечивает двустороннюю связь между клиентом и сервером через одно TCP-соединение. В ESP32 протокол WebSocket можно использовать для обеспечения связи в реальном времени между веб-сервером, работающим на ESP32, и веб-браузером. Это позволяет веб-серверу отправлять данные клиенту, как только они становятся доступными, тем самым устраняя необходимость постоянного запроса новых данных клиенту.
Работа светодиодного веб-сервера ESP32
Наш проект работает путем создания веб-сервера на микроконтроллере ESP32, который передает веб-страницу на клиентское устройство, например компьютер или мобильный телефон, через Wi-Fi. Веб-страница содержит элементы управления, которые позволяют включать и выключать светодиоды.
Вот пошаговое описание того, как будет работать наш проект веб-сервера на ESP32:
- Микроконтроллер ESP32 подключен к светодиодам и запрограммирован в качестве веб-сервера.
- Код веб-сервера загружается в ESP32 с помощью Arduino IDE или других инструментов программирования.
- ESP32 подключен к сети Wi-Fi, что позволяет ему взаимодействовать с клиентскими устройствами по сети.
- Когда клиентское устройство подключается к IP-адресу ESP32, код веб-сервера передает веб-страницу веб-браузеру клиентского устройства.
- Веб-страница содержит элементы управления светодиодами, позволяющие пользователю включать или выключать их.
- Когда пользователь взаимодействует с элементами управления на веб-странице, микроконтроллер ESP32 получает команду и соответствующим образом регулирует свечение светодиодов.
- ESP32 отправляет ответ обратно на клиентское устройство, обновляя веб-страницу текущим состоянием светодиодов.
Схема проекта
Схема веб-сервера на модуле ESP32 для управления светодиодом представлена на следующем рисунке:
Микроконтроллер ESP32 имеет множество контактов GPIO, которые можно использовать для взаимодействия с внешними устройствами, такими как светодиоды. В этом примере мы подключим ESP32 к двум светодиодам, подключенным к контактам GPIO 12 и 14.
Вот как подключить ESP32 к двум светодиодам:
- Подключите один конец резистора сопротивлением 220 Ом к контакту 12 GPIO на ESP32, а другой конец резистора подключите к положительному (анодному) выводу первого светодиода.
- Подключите отрицательный (катодный) контакт светодиода к земле (GND) на ESP32.
- Повторите шаги 1 и 2 для контакта GPIO 14 и другого светодиода.
Загрузка кода в плату ESP32 из Arduino IDE
Arduino IDE предоставляет удобный интерфейс как для начинающих, так и для опытных пользователей, позволяя писать и загружать код в ESP32, не требуя глубоких знаний языков программирования.
Вот подробное руководство, которому вы можете следовать - начало работы с ESP32 с использованием Arduino IDE.
Редактирование кода
Перед загрузкой кода обязательно измените сетевые учетные данные в коде на свой SSID и пароль в этом фрагменте кода.
1 2 |
const char* ssid = " REPLACE_WITH_YOUR_SSID"; const char* password = " REPLACE_WITH_YOUR_PASSWORD"; |
Полный код веб-сервера на ESP32 для управления светодиодами
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
// подключаем библиотеку Wi-Fi #include <WiFi.h> // замените своими сетевыми учетными данными const char* ssid = " REPLACE_WITH_YOUR_SSID"; const char* password = " REPLACE_WITH_YOUR_PASSWORD"; // установите номер порта веб-сервера на 80 WiFiServer server(80); // переменная для хранения HTTP запроса String header; // вспомогательные переменные для хранения текущего состояния вывода String output12State = "off"; String output14State = "off"; // назначаем выходные переменные контактам GPIO const int output12 = 12; const int output14 = 14; // текущее время unsigned long currentTime = millis(); // предыдущее время unsigned long previousTime = 0; // Define timeout time in milliseconds (example: 2000ms = 2s) const long timeoutTime = 2000; void setup() { Serial.begin(115200); // Initialize the output variables as outputs pinMode(output12, OUTPUT); pinMode(output14, OUTPUT); // устанавливаем контакты в состояние LOW digitalWrite(output12, LOW); digitalWrite(output14, LOW); // соединяемся с Wi-Fi сетью с нашим SSID и паролем Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } // печатаем локальный IP адрес и запускаем веб-сервер Serial.println(""); Serial.println("WiFi connected."); Serial.println("IP address: "); Serial.println(WiFi.localIP()); server.begin(); } void loop(){ WiFiClient client = server.available(); // прослушиваем входящих клиентов if (client) { // если подключается новый клиент, currentTime = millis(); previousTime = currentTime; Serial.println("New Client."); // выводим сообщение в последовательный порт String currentLine = ""; // make a String to hold incoming data from the client while (client.connected() && currentTime - previousTime <= timeoutTime) { // loop while the client's connected currentTime = millis(); if (client.available()) { // if there's bytes to read from the client, char c = client.read(); // read a byte, then Serial.write(c); // print it out the serial monitor header += c; if (c == '\n') { // if the byte is a newline character // if the current line is blank, you got two newline characters in a row. // that's the end of the client HTTP request, so send a response: if (currentLine.length() == 0) { // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) // and a content-type so the client knows what's coming, then a blank line: client.println("HTTP/1.1 200 OK"); client.println("Content-type:text/html"); client.println("Connection: close"); client.println(); // turns the GPIOs on and off if (header.indexOf("GET /12/on") >= 0) { Serial.println("GPIO 12 on"); output12State = "on"; digitalWrite(output12, HIGH); } else if (header.indexOf("GET /12/off") >= 0) { Serial.println("GPIO 12 off"); output12State = "off"; digitalWrite(output12, LOW); } else if (header.indexOf("GET /14/on") >= 0) { Serial.println("GPIO 14 on"); output14State = "on"; digitalWrite(output14, HIGH); } else if (header.indexOf("GET /14/off") >= 0) { Serial.println("GPIO 14 off"); output14State = "off"; digitalWrite(output14, LOW); } // Display the HTML web page client.println("<!DOCTYPE html><html>"); client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"); client.println("<link rel=\"icon\" href=\"data:,\">"); // CSS to style the on/off buttons // Feel free to change the background-color and font-size attributes to fit your preferences client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}"); client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;"); client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}"); client.println(".button2 {background-color: #555555;}</style></head>"); // Web Page Heading client.println("<body><h1>ESP32 Web Server</h1>"); // Display current state, and ON/OFF buttons for GPIO 12 client.println("<p>GPIO 12 - State " + output12State + "</p>"); // If the output12State is off, it displays the ON button if (output12State=="off") { client.println("<p><a href=\"/12/on\"><button class=\"button\">ON</button></a></p>"); } else { client.println("<p><a href=\"/12/off\"><button class=\"button button2\">OFF</button></a></p>"); } // Display current state, and ON/OFF buttons for GPIO 14 client.println("<p>GPIO 14 - State " + output14State + "</p>"); // If the output14State is off, it displays the ON button if (output14State=="off") { client.println("<p><a href=\"/14/on\"><button class=\"button\">ON</button></a></p>"); } else { client.println("<p><a href=\"/14/off\"><button class=\"button button2\">OFF</button></a></p>"); } client.println("</body></html>"); // ответ HTTP заканчивается еще одной пустой строкой client.println(); // выходим из цикла while break; } else { // если появилась новая строка, очистим текущую строку currentLine = ""; } } else if (c != '\r') { // если у вас еще есть что то кроме символа возврата каретки, currentLine += c; // добавляем его в конец текущей строки } } } // очищаем переменную заголовка header = ""; // закрываем соединение client.stop(); Serial.println("Client disconnected."); Serial.println(""); } } |
Загрузка кода в плату ESP32
Чтобы загрузить код в плату ESP32, вам необходимо выполнить следующие шаги:
- Подключите плату ESP32 к компьютеру с помощью USB-кабеля.
- Откройте Arduino IDE, перейдите в "Tools" > "Board" и выберите из списка свою плату ESP32.
- Обязательно также выберите правильный порт в разделе "Tools" > "Port".
- Напишите или скопируйте свой код в Arduino IDE.
- Убедитесь, что ваш код компилируется, нажав кнопку "Verify" (значок галочки) в верхнем левом углу IDE. Если есть какие-либо ошибки, исправьте их, прежде чем продолжить.
- Загрузите свой код в плату ESP32, нажав кнопку "Upload" (Загрузить) (значок со стрелкой вправо) в верхнем левом углу IDE. IDE скомпилирует ваш код, а затем загрузит его в плату ESP32. Ход загрузки будет отображаться в нижней части IDE.
После завершения загрузки плата ESP32 автоматически перезагрузится и начнет выполнять ваш код.
Примечание. Перед загрузкой кода вам может потребоваться нажать кнопку "Boot" (Загрузка) или "Reset" (Сброс) на плате ESP32, чтобы войти в режим загрузчика. Конкретные шаги для входа в режим загрузчика могут различаться в зависимости от вашей платы ESP32. Чтобы узнать, как это сделать, обратитесь к документации вашей платы.
Поиск IP-адреса платы ESP32
- Откройте последовательный монитор в Arduino IDE, щелкнув значок лупы в правом верхнем углу окна IDE.
- Выберите скорость передачи данных 115200 бод из раскрывающегося списка в правом нижнем углу окна Serial Monitor (последовательного монитора).
- Сбросьте настройки платы ESP32, нажав кнопку EN(RST) на плате.
- Подождите, пока плата ESP32 подключится к сети Wi-Fi. Последовательный монитор будет отображать сообщения, указывающие ход соединения.
- Как только плата ESP32 будет подключена к сети Wi-Fi, она получит IP-адрес из сети. Последовательный монитор отобразит IP-адрес платы ESP32.
Альтернативно вы можете использовать инструмент сетевого сканирования, такой как «Расширенный IP-сканер», для сканирования вашей сети на наличие подключенных устройств и определения IP-адреса вашей платы ESP32.
Подключение к веб-серверу ESP32 и его тестирование
- Откройте ту же сеть через устройство или используйте то же устройство, к точке доступа которого подключен ваш ESP32.
- Откройте веб-браузер на своем компьютере или мобильном устройстве и введите IP-адрес платы ESP32 в адресную строку.
Если вы присмотритесь, вы найдете дополнительные данные на последовательном мониторе и получить более подробную информацию о веб-сервере, к которому вы обращаетесь, например HTTP-запросы, адрес хоста и т. д.
После того, как вы получили доступ к странице управления в своем браузере, вы можете протестировать ее, нажав «ВКЛ» и «ВЫКЛ» и одновременно проверив состояние светодиода и последовательный монитор.
Теперь, если вы нажмете кнопку GPIO 12, вы увидите, что последовательный монитор получает запрос на URL-адрес 12/on, а затем загорается ваш светодиод.
После этого Esp32 обновляет свое состояние на веб-странице как «ВКЛ» или «ВЫКЛ».
Это будет работать так же и для GPIO 14.
Объяснение кода программы веб-сервера
Вначале подключим библиотеку для работы с WiFi.
1 |
#include <WiFi.h> |
В следующих строках вам необходимо ввести свои SSID и пароль для вашей Wi-Fi сети.
1 2 3 |
// Replace with your network credentials const char* ssid = "Replace with your SSID"; const char* password = "Replace with your password"; |
Следующая строка создает объект сервера, который будет прослушивать входящие HTTP-запросы через порт 80. Порт 80 является портом по умолчанию для HTTP-трафика.
1 |
WiFiServer server(80); |
Далее объявим строковую переменную под названием «header», которая будет использоваться для хранения HTTP-запроса, полученного от клиента.
1 |
String header; |
Затем объявим две строковые переменные с названиями «output12State» и «output14State», которые будут использоваться для хранения текущего состояния (включено или выключено) двух выходных контактов на плате ESP32.
1 2 3 |
// Auxiliar variables to store the current output state String output12State = "off"; String output14State = "off"; |
Следующие две строки задают две константы, называемые «output12» и «output14», которые представляют контакты GPIO на плате ESP32, которые будут использоваться для управления состоянием внешних устройств.
1 2 3 |
// Assign output variables to GPIO pins const int output12 = 12; const int output14 = 14; |
Эти три строки создают переменные для измерения времени, прошедшего с момента запуска сервера, и для установки тайм-аута для клиентских запросов.
1 2 3 4 5 6 |
// Current time unsigned long currentTime = millis(); // Previous time unsigned long previousTime = 0; // Define timeout time in milliseconds (example: 2000ms = 2s) const long timeoutTime = 2000; |
В функции setup(), которая вызывается один раз при запуске ESP32, мы устанавливаем скорость последовательной связи, инициализируем два контакта GPIO в качестве выходов и устанавливаем на них уровень LOW. Затем мы подключаемся к сети Wi-Fi, используя ранее определенные SSID и пароль, и ждем подключения. После подключения мы печатаем IP-адрес ESP32 в окне монитора последовательной связи и запускаем веб-сервер.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
void setup() { Serial.begin(115200); // Initialize the output variables as outputs pinMode(output12, OUTPUT); pinMode(output14, OUTPUT); // Set outputs to LOW digitalWrite(output12, LOW); digitalWrite(output14, LOW); // Connect to Wi-Fi network with SSID and password Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } // Print local IP address and start web server Serial.println(""); Serial.println("WiFi connected."); Serial.println("IP address: "); Serial.println(WiFi.localIP()); server.begin(); } |
Это основная функция цикла, которая выполняется неоднократно, пока ESP32 включен.
1 |
void loop(){ |
Эта строка прослушивает входящие клиентские подключения к веб-серверу ESP32. Если доступен клиент, он создает новый экземпляр класса WiFiClient и присваивает его переменной клиента.
1 |
WiFiClient client = server.available(); // Listen for incoming client |
Эта строка проверяет, подключился ли новый клиент к веб-серверу. Если клиент подключился, код внутри оператора if будет выполнен.
1 |
if (client) { |
Эти две строки получают текущее время в миллисекундах и присваивают его переменным currentTime и previousTime.
1 2 |
currentTime = millis(); previousTime = currentTime; |
Эта строка выводит сообщение на последовательный монитор, указывающее, что подключился новый клиент.
1 |
Serial.println("New Client."); |
Эта строка создает пустую строковую переменную с именем currentLine, которая будет хранить входящие данные от клиента.
1 |
String currentLine = ""; |
Этот цикл while выполняется до тех пор, пока клиент подключен и время, прошедшее с момента предыдущей итерации цикла, меньше или равно переменной timeoutTime.
1 2 |
while (client.connected() && currentTime - previousTime <= timeoutTime) { // loop while the client's connected currentTime = millis(); |
Этот блок кода проверяет, доступны ли данные для чтения от клиента. Если доступны данные, он считывает байт от клиента, выводит его на последовательный монитор и добавляет к переменной заголовка.
1 2 3 4 |
if (client.available()) { // if there's bytes to read from the client, char c = client.read(); // read a byte, then Serial.write(c); // print it out the serial monitor header += c; |
Этот код проверяет, является ли текущий символ символом новой строки (\n). Если да, он проверяет, пуста ли текущая строка. Если это так, это означает, что достигнут конец HTTP-запроса, и ESP32 должен отправить ответ клиенту.
1 2 3 4 |
if (c == '\n') { // if the byte is a newline character // if the current line is blank, you got two newline characters in a row. // that's the end of the client HTTP request, so send a response: if (currentLine.length() == 0) { |
Этот код отправляет клиенту HTTP-заголовки, указывающие, что ответом является HTML-страница.
1 2 3 4 5 6 |
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) // and a content-type so the client knows what's coming, then a blank line: client.println("HTTP/1.1 200 OK"); client.println("Content-type:text/html"); client.println("Connection: close"); client.println(); |
Этот код обрабатывает запросы HTTP GET, полученные сервером, и на основе запроса включает или выключает контакты GPIO.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// turns the GPIOs on and off if (header.indexOf("GET /12/on") >= 0) { Serial.println("GPIO 12 on"); output12State = "on"; digitalWrite(output12, HIGH); } else if (header.indexOf("GET /12/off") >= 0) { Serial.println("GPIO 12 off"); output12State = "off"; digitalWrite(output12, LOW); } else if (header.indexOf("GET /14/on") >= 0) { Serial.println("GPIO 14 on"); output14State = "on"; digitalWrite(output14, HIGH); } else if (header.indexOf("GET /14/off") >= 0) { Serial.println("GPIO 14 off"); output14State = "off"; digitalWrite(output14, LOW); } |
Объяснение HTML кода веб-сервера ESP32
Эти строки отображают текущее состояние GPIO 12 и GPIO 14 и отображают кнопку «ВКЛ» или «ВЫКЛ» в зависимости от ее текущего состояния.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
client.println("<!DOCTYPE html><html>"); client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"); client.println("<link rel=\"icon\" href=\"data:,\">"); These lines create the HTML header of the web page, including the viewport meta tag and an empty favicon link. client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}"); client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;"); client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}"); client.println(".button2 {background-color: #555555;}</style></head>"); This block of code defines the CSS styles that will be applied to the buttons in the web page. client.println("<body><h1>ESP32 Web Server</h1>"); This line sets the main heading of the web page. client.println("<p>GPIO 12 - State " + output12State + "</p>"); if (output12State=="off") { client.println("<p><a href=\"/12/on\"><button class=\"button\">ON</button></a></p>"); } else { client.println("<p><a href=\"/12/off\"><button class=\"button button2\">OFF</button></a></p>"); } client.println("<p>GPIO 14 - State " + output14State + "</p>"); if (output14State=="off") { client.println("<p><a href=\"/14/on\"><button class=\"button\">ON</button></a></p>"); } else { client.println("<p><a href=\"/14/off\"><button class=\"button button2\">OFF</button></a></p>"); } |
Эта строка закрывает HTML-документ.
1 |
client.println("</body></html>"); |
Эта строка завершает заголовки ответа HTTP.
1 |
client.println(); |
Эти строки закрывают клиентское соединение и выводят сообщения на последовательный монитор.
1 2 3 |
client.stop(); Serial.println("Client disconnected."); Serial.println(""); |
Урок рабочий, благодарю за пояснения.
Как вывести русский текст в этом примере на html странице?
Честно говоря, не знаю, не силен в веб-разработке
Спасибо! Все толково изложено. Интересный пример. В ESP32 еще много чего есть (внешние прерывания, ШИМ (не только для сервопривода), АЦП, ЦАП? ...) весь инструмент хотелось бы освоить.., а вот понятных примеров, особенно на русскоязычных ресурсах, "кот наплакал".
К сожалению да. Я перевожу понемногу эти материалы с англоязычных источников, но в последнее время что то все меньше времени удается выделять на это
Спасибо за урок. Как раз не хватает простых стартовых проектов, чтобы попробовать базовые возможности.
Спасибо и вам что оценили мой труд. Лично мне очень нравятся модули ESP32 и ESP8266 по соотношению цена/возможности, мне кажется их даже удобнее использовать начинающим чем платы Ардуино, умеют все то же самое, команды и среда программирования одна и та же, а вдобавок есть еще WiFi и Bluetooth, что значительно упрощает многие проекты.