4. Теперь мы можем использовать наше изображение с помощью тега img следующим образом:
<img src="data:image/jpeg;base64...5n/2Q==">
Для этого добавим следующий код в следующую строку после “Turn LED off”:
client.print("<img src=\""); client.print(image01); client.print("\">");
Наш веб-сервер вряд ли получит премию года за лучший дизайн, но по крайней мере, пользователь теперь видит, что он управляет именно светодиодами:

Кстати, в реальном проекте все изображения целесообразно вынести в отдельный файл, который можно назвать например, “images.h”. Тогда в основной программе можно будет написать #include "images.h". Это позволит отделить ресурсы от кода, и сделать текст программы более читаемым.
Самостоятельная работа: вывести разные типы изображений, например кнопки, светодиоды.
3.11 Удаленный мониторинг
С помощью web-сервера можно не только включать и выключать светодиод, но и получать информацию с удаленного объекта. Например, можно узнать температуру в квартире, находясь на каникулах или в отпуске. Как нетрудно догадаться, для этого достаточно вывести нужные нам параметры в HTML, который и отобразится на веб-странице.
К примеру, достаточно добавить такую строку в код формирования HTML:
if (digitalRead(buttonPin) == HIGH) {
client.print("Button state: ON<br>");
} else {
client.print("Button state: OFF<br>");
}
При обновлении страницы в браузере мы увидим соответствующий текст. Разумеется, это может быть не только кнопка, но и например, датчик закрывания двери или датчик освещенности (желающие могут перечитать главу 2.6 “Ввод аналоговых величин”).
Рассмотрим пример подробнее. Допустим, у нас есть данные с разных сенсоров (подробнее можно прочитать в главе 2.7-2.10). Чтобы не загромождать тестовый код, представим данные в виде уже готовых переменных, реальный код чтения желающие могут добавить самостоятельно.
float temperature = 22.5;
int humidity = 60;
int pressure = 1010;
bool doorClosed = true;
bool windowClosed = false;
Наша задача - отобразить эти данные на веб-сервере, для чего проще всего воспользоваться HTML-тегом “Table” (таблица).
Пример таблицы в HTML:
<table border = "1">
<tr>
<td>Row 1, Column 1</td>
<td>Row 1, Column 2</td>
</tr>
<tr>
<td>Row 2, Column 1</td>
<td>Row 2, Column 2</td>
</tr>
</table>
В браузере это будет выглядеть вот так:
Здесь tr - это table row, строка таблицы, а td - это элемент ячейки. Таким образом, мы можем группировать данные так, как нам удобно. Добавим таблицу в наш сервер. Для экономии места будет приведена только функция loop, остальное не изменилось.
void loop() {
// Возобновление соединения при необходимости
while (WiFi.status() != WL_CONNECTED) {
delay(500);
WiFi.begin(ssid, password);
Serial.print(".");
}
// Данные с датчиков
float temperature = 22.5;
int humidity = 60;
int pressure = 1010;
bool doorClosed = true;
bool windowClosed = false;
// Здесь можно добавить код чтения
// ...
WiFiClient client = server.available();
if (client) {
Serial.println("New Client.");
String currentLine = "";
while (client.connected()) {
if (client.available()) {
char c = client.read();
if (c == '\n') {
if (currentLine.length() == 0) {
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
client.print("<html>");
client.print("<head><title>ESP32 Server</title></head>");
client.print("<body>");
client.print("<h3>ESP32 sensors data</h3>");
client.print("<table border = \"1\">");
client.print(" <tr><td>Temperature, C</td><td>");
client.print(String(temperature)); client.print("</td></tr>");
client.print(" <tr><td>Humidity, percent</td><td>");
client.print(String(humidity)); client.print("</td></tr>");
client.print(" <tr><td>Pressure, hPa</td><td>");
client.print(String(pressure)); client.print("</td></tr>");
client.print(" <tr><td>Door closed:</td><td>");
client.print(doorClosed ? "yes" : "no"); client.print("</td></tr>");
client.print(" <tr><td>Window closed:</td><td>");
client.print(windowClosed ? "yes" : "no"); client.print("</td></tr>");
client.print("</table>");
client.print("");
client.print("</body></html>");
client.println();
break;
} else {
currentLine = "";
}
} else if (c != '\r') {
currentLine += c;
}
}
}
client.stop();
Serial.println("Client Disconnected.");
}
}
Как можно видеть, код довольно прост. Мы сформировали таблицу с нужными нам полями. Для вывода строк yes/no мы использовали конструкцию языка С doorClosed ? "yes" : "no", она позволяет выбрать одно из двух значений в зависимости от условия.
Запускаем браузер, и видим данные с нашего сервера:

Теперь мы можем оставить устройство подключенным, и наблюдать состояние дома или квартиры удаленно, с любой точки земного шара.
Самостоятельная работа #1: подключить реальные датчики, например DS1820.
Самостоятельная работа #2: Поэкспериментировать с раскраской и стилями таблицы. Например, так можно добавить красный цвет к ячейке:
<td bgcolor="#FF0000">Row 1, Column 1</td>
3.12 Делаем односторонний мессенджер
Плата ESP32 может быть включена круглосуточно, что легко позволяет использовать ее для приема входящих сообщений: достаточно лишь добавить в веб-сервер форму для отправки текстовых данных. Это позволит любому кто знает адрес сервера, зайти на страницу и оставить сообщение его владельцу.
На языке HTML код для отправки выглядит просто, достаточно добавить следующие строки:
<form action="/send">
Enter your message: <input type="text" name="msg"><br>
<input type="submit" value="Submit">
</form>
При этом страница в браузере будет иметь следующий вид:
При этом, если пользователь введет текст, например “123 456”, и нажмет кнопку “Submit”, браузером будет отправлен запрос “GET /send?msg=123+456 HTTP/1.1”, который мы можем обработать на сервере. Дальше, как говорится, дело техники - надо извлечить из строки подстроку, убрав начальные и конечные части.
Добавим код там, где проверяется начало строки:
if (currentLine.startsWith("GET /send?msg=") && currentLine.endsWith(" HTTP/1.1")) {
// String looks like GET /send?msg=123+456 HTTP/1.1
String msg = currentLine.substring(14, currentLine.length()-8);
msg.replace("+", " ");
Serial.println(msg);
}
Как можно видеть, с помощью функции substring мы взяли подстроку, пропустив 14 символов сначала (длина “GET /send?msg=”) и 8 символов с конца (длина “ HTTP/1.1”). Функция replace заменяет символы “+” на пробелы, чтобы из строки “123+456” получить “123 456”.
Добавим вместо Serial.println вывод на дисплей, который мы рассмотрели в главе 3.4.
Готовый код “мессенджера” целиком приведен ниже.
#include <WiFi.h>
#include "SSD1306.h"
const char* ssid = "TP-LINK_AB11";
const char* password = "12345678";
int ledPin = 2;
WiFiServer server(8000);
SSD1306 display(0x3c, 21, 22);
void setup()
{
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
// Инициализация дисплея
display.init();
display.flipScreenVertically();
display.clear();
display.setFont(ArialMT_Plain_10);
display.setTextAlignment(TEXT_ALIGN_LEFT);
display.drawString(0, 0, "App started");
display.display();
delay(10);
// Запуск WiFi
Serial.print("Connecting to ");
Serial.println(ssid);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
WiFi.begin(ssid, password);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
}
void loop(){
while (WiFi.status() != WL_CONNECTED) {
delay(500);
WiFi.begin(ssid, password);
Serial.print(".");
}
WiFiClient client = server.available();
if (client) {
Serial.println("New Client.");
String currentLine = "";
while (client.connected()) {
if (client.available()) {
char c = client.read();
if (c == '\n') { // if the byte is a newline character
if (currentLine.length() == 0) {
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
client.print("<html>");
client.print("<head><title>ESP32 Server</title></head>");
client.print("<body>");
client.print("<h3>ESP32 sensors data</h3>");
client.print("<form action=\"/send\">");
client.print("Enter the message: <input type=\"text\" name=\"msg\"><br>");
client.print("<input type=\"submit\" value=\"Submit\">");
client.print("</form>");
client.print("");
client.print("</body></html>");
client.println();
break;
} else {
if (currentLine.startsWith("GET /send?msg=") &&
currentLine.endsWith(" HTTP/1.1")) {
// Строка выглядит как “GET /send?msg=123+456 HTTP/1.1”
String msg = currentLine.substring(14, currentLine.length()-8);
msg.replace("+", " ");
// Вывод строки на дисплей
display.clear();
display.setFont(ArialMT_Plain_10);
display.setTextAlignment(TEXT_ALIGN_LEFT);
display.drawString(0, 0, msg);
display.display();
}
currentLine = "";
}
} else if (c != '\r') {
currentLine += c;
}
}
}
client.stop();
Serial.println("Client Disconnected.");
}
}
Теперь можно запустить программу, открывать сервер в браузере и ввести текст - он появится на экране. Это можно использовать например, для отправки сообщений своим близким, находясь в другом городе, в каникулах, отпуске или командировке.
Разумеется, данный код может быть улучшен. Например, данная библиотека не поддерживает переносы строк, так что длинная веденная строка не поместится на экран. Читатели могут сделать это самостоятельно, для этого можно воспользоваться функциями length и substring для разбивки длинной строки на строки.
Самостоятельная работа #1: добавить к плате “пищалку” или светодиод, которая будет сигнализировать о наличии нового сообщения. При желании также можно добавить на плату кнопку “Сообщение прочитано”, которой получатель подтвердит получение. Для этого достаточно добавить переменную messageIsRead, и при получении нового сообщения устанавливать ее в False, а при нажатии кнопки менять значение на True. В общем, простор для творчества тут весьма велик.
Самостоятельная работа #2: Добавить вывод нескольких сообщений. Для этого можно добавить несколько переменных, например “String msg1, msg2, msg3”, а при получении нового сообщения добавлять его в “конец очереди”: