ESP8266:Прошивки/Arduino/Библиотеки/Библиотека ESP8266WiFi/Класс сервера/Сервер, обслуживающий веб-страницу: различия между версиями
Myagkij (обсуждение | вклад) (Новая страница: «{{ESP8266 панель перехода}} {{Перевод от Сubewriter}} {{Myagkij-редактор}} {{Черновик}} =Сервер, обслужив…») |
Нет описания правки |
||
Строка 15: | Строка 15: | ||
Начнем с создания экземпляра класса WiFiServer: | Начнем с создания экземпляра класса WiFiServer: | ||
<syntaxhighlight lang="c | <syntaxhighlight lang="c"> | ||
WiFiServer server(80); | WiFiServer server(80); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Строка 25: | Строка 25: | ||
Теперь давайте напишем короткую функцию prepareHtmlPage(), которая будет возвращать переменную типа String, содержащую содержимое веб-страницы. Мы передадим эту переменную серверу, чтобы тот передал ее клиенту. | Теперь давайте напишем короткую функцию prepareHtmlPage(), которая будет возвращать переменную типа String, содержащую содержимое веб-страницы. Мы передадим эту переменную серверу, чтобы тот передал ее клиенту. | ||
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS | <syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS"> | ||
String prepareHtmlPage() | String prepareHtmlPage() | ||
{ | { | ||
Строка 51: | Строка 51: | ||
Заголовок информирует клиента, какой тип контента последует и как его будут выдавать: | Заголовок информирует клиента, какой тип контента последует и как его будут выдавать: | ||
<syntaxhighlight lang="html5 | <syntaxhighlight lang="html5"> | ||
Content-Type: text/html | Content-Type: text/html | ||
Connection: close | Connection: close | ||
Строка 59: | Строка 59: | ||
В нашем примере типом контента является '''text/html''', после ответа соединение будет закрыто, а контент будет запрашиваться клиентом '''каждые 5 секунд'''. В конце заголовка будут стоять символы пустой строки ('''«\r\n»'''). Они нужны для того, чтобы отделить заголовок от контента, который последует дальше. | В нашем примере типом контента является '''text/html''', после ответа соединение будет закрыто, а контент будет запрашиваться клиентом '''каждые 5 секунд'''. В конце заголовка будут стоять символы пустой строки ('''«\r\n»'''). Они нужны для того, чтобы отделить заголовок от контента, который последует дальше. | ||
<syntaxhighlight lang="html5" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS | <syntaxhighlight lang="html5" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS"> | ||
<!DOCTYPE HTML> | <!DOCTYPE HTML> | ||
<html> | <html> | ||
Строка 68: | Строка 68: | ||
Этот контент содержит два базовых [[HTML-тег]]а. Один – для описания типа [[HTML-документ]]а ('''<!DOCTYPE HTML>'''), а второй – чтобы отметить начало ('''<html>''') и конец ('''</html>''') документа. Внутри – необработанное значение входного аналогового контакта [[ESP8266]], считанное функцией analogRead(A0) и преобразованное в тип данных String. | Этот контент содержит два базовых [[HTML-тег]]а. Один – для описания типа [[HTML-документ]]а ('''<!DOCTYPE HTML>'''), а второй – чтобы отметить начало ('''<html>''') и конец ('''</html>''') документа. Внутри – необработанное значение входного аналогового контакта [[ESP8266]], считанное функцией analogRead(A0) и преобразованное в тип данных String. | ||
<syntaxhighlight lang="c | <syntaxhighlight lang="c"> | ||
String(analogRead(A0)) | String(analogRead(A0)) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Строка 76: | Строка 76: | ||
Выдача веб-страницы выполняется в блоке loop(), где сервер ждет подключения нового клиента и получения данных с запросом. | Выдача веб-страницы выполняется в блоке loop(), где сервер ждет подключения нового клиента и получения данных с запросом. | ||
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS | <syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS"> | ||
void loop() | void loop() | ||
{ | { | ||
Строка 89: | Строка 89: | ||
Когда подключится новый клиент, сервер прочтет его запрос, а затем напечатает его в мониторе порта. | Когда подключится новый клиент, сервер прочтет его запрос, а затем напечатает его в мониторе порта. | ||
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS | <syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS"> | ||
while (client.connected()) | while (client.connected()) | ||
{ | { | ||
Строка 102: | Строка 102: | ||
Запрос от клиента помечен символом новой строки. Дойдя до этого символа, сервер должен отправить обратно веб-страницу, а затем выйти из цикла while() при помощи оператора break. | Запрос от клиента помечен символом новой строки. Дойдя до этого символа, сервер должен отправить обратно веб-страницу, а затем выйти из цикла while() при помощи оператора break. | ||
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS | <syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS"> | ||
if (line.length() == 1 && line[0] == '\n') | if (line.length() == 1 && line[0] == '\n') | ||
{ | { | ||
Строка 112: | Строка 112: | ||
Весь этот процесс завершается закрытием соединения с клиентом. | Весь этот процесс завершается закрытием соединения с клиентом. | ||
<syntaxhighlight lang="c | <syntaxhighlight lang="c"> | ||
client.stop(); | client.stop(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Строка 120: | Строка 120: | ||
Ниже весь этот [[скетч]] представлен в полном виде: | Ниже весь этот [[скетч]] представлен в полном виде: | ||
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS | <syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS"> | ||
#include <ESP8266WiFi.h> | #include <ESP8266WiFi.h> | ||
Строка 208: | Строка 208: | ||
Меняем в скетче строчки с '''ssid''' и '''password''' на [[SSID]] и пароль к своей точке доступа. Загружаем скетч на [[ESP8266]] и открываем монитор порта. Сначала в мониторе порта должно появиться подтверждение того, что модуль подключен к точке доступа, а [[веб-сервер]] – запущен. | Меняем в скетче строчки с '''ssid''' и '''password''' на [[SSID]] и пароль к своей точке доступа. Загружаем скетч на [[ESP8266]] и открываем монитор порта. Сначала в мониторе порта должно появиться подтверждение того, что модуль подключен к точке доступа, а [[веб-сервер]] – запущен. | ||
<syntaxhighlight lang="bash | <syntaxhighlight lang="bash"> | ||
Connecting to sensor-net ........ connected | Connecting to sensor-net ........ connected | ||
Web server started, open 192.168.1.104 in a web browser | Web server started, open 192.168.1.104 in a web browser | ||
Строка 219: | Строка 219: | ||
Эта страница будет обновляться '''каждые 5 секунд'''. С каждым новым обновлением в мониторе порта должен появляться новый запрос от клиента: | Эта страница будет обновляться '''каждые 5 секунд'''. С каждым новым обновлением в мониторе порта должен появляться новый запрос от клиента: | ||
<syntaxhighlight lang="html5 | <syntaxhighlight lang="html5"> | ||
[Client connected] | [Client connected] | ||
GET / HTTP/1.1 | GET / HTTP/1.1 |
Версия от 19:56, 23 мая 2023
Черновик |
Сервер, обслуживающий веб-страницу[1]
Настройка веб-сервера на ESP8266 на удивление проста, т.к. требует очень небольшого скетча. Это простота обеспечивается за счет универсальности библиотеки ESP8266WiFi.
Цель этого скетча-примера – подготовить веб-страницу, чтобы ее можно было открыть веб-браузером. Страница будет показывать текущее значение аналогового входного контакта ESP8266.
Объект
Начнем с создания экземпляра класса WiFiServer:
WiFiServer server(80);
Сервер отвечает клиентам (в данном случае – браузерам) на порте 80, который является стандартным портом, через который браузеры «общаются» с веб-серверами.
Страница
Теперь давайте напишем короткую функцию prepareHtmlPage(), которая будет возвращать переменную типа String, содержащую содержимое веб-страницы. Мы передадим эту переменную серверу, чтобы тот передал ее клиенту.
String prepareHtmlPage()
{
String htmlPage =
String("HTTP/1.1 200 OK\r\n") +
"Content-Type: text/html\r\n" +
"Connection: close\r\n" + // после отправки ответа
// соединение будет закрыто
"Refresh: 5\r\n" + // автоматически обновляем
// страницу каждые 5 секунд
"\r\n" +
"<!DOCTYPE HTML>" +
"<html>" +
"Analog input: " + String(analogRead(A0)) +
"</html>" +
"\r\n";
return htmlPage;
}
Эта функция не делает ничего выдающегося. Просто собирает вместе текстовый заголовок и HTML-содержимое страницы.
Сначала заголовок
Заголовок информирует клиента, какой тип контента последует и как его будут выдавать:
Content-Type: text/html
Connection: close
Refresh: 5
В нашем примере типом контента является text/html, после ответа соединение будет закрыто, а контент будет запрашиваться клиентом каждые 5 секунд. В конце заголовка будут стоять символы пустой строки («\r\n»). Они нужны для того, чтобы отделить заголовок от контента, который последует дальше.
<!DOCTYPE HTML>
<html>
Analog input: [Value]
</html>
Этот контент содержит два базовых HTML-тега. Один – для описания типа HTML-документа (<!DOCTYPE HTML>), а второй – чтобы отметить начало (''') и конец (''') документа. Внутри – необработанное значение входного аналогового контакта ESP8266, считанное функцией analogRead(A0) и преобразованное в тип данных String.
String(analogRead(A0))
Выдача страницы
Выдача веб-страницы выполняется в блоке loop(), где сервер ждет подключения нового клиента и получения данных с запросом.
void loop()
{
WiFiClient client = server.available();
if (client)
{
// у нас новый клиент, отправивший запрос
}
}
Когда подключится новый клиент, сервер прочтет его запрос, а затем напечатает его в мониторе порта.
while (client.connected())
{
if (client.available())
{
String line = client.readStringUntil('\r');
Serial.print(line);
}
}
Запрос от клиента помечен символом новой строки. Дойдя до этого символа, сервер должен отправить обратно веб-страницу, а затем выйти из цикла while() при помощи оператора break.
if (line.length() == 1 && line[0] == '\n')
{
client.println(prepareHtmlPage());
break;
}
Весь этот процесс завершается закрытием соединения с клиентом.
client.stop();
Скетч целиком
Ниже весь этот скетч представлен в полном виде:
#include <ESP8266WiFi.h>
const char* ssid = "********";
const char* password = "********";
WiFiServer server(80);
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.printf("Connecting to %s ", ssid);
// "Подключение к "
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println(" connected");
// " подключено"
server.begin();
Serial.printf("Web server started, open %s in a web browser\n", WiFi.localIP().toString().c_str());
// "Веб-сервер запущен, открываем %s в веб-браузере"
}
// подготавливаем веб-страницу для отправки клиенту (браузеру):
String prepareHtmlPage()
{
String htmlPage =
String("HTTP/1.1 200 OK\r\n") +
"Content-Type: text/html\r\n" +
"Connection: close\r\n" + // после отправки ответа
// соединение будет закрыто
"Refresh: 5\r\n" + // автоматически обновляем
// страницу каждые 5 секунд
"\r\n" +
"<!DOCTYPE HTML>" +
"<html>" +
"Analog input: " + String(analogRead(A0)) +
"</html>" +
"\r\n";
return htmlPage;
}
void loop()
{
WiFiClient client = server.available();
// ждем подключения клиента (браузера):
if (client)
{
Serial.println("\n[Client connected]");
// "Клиент подключен"
while (client.connected())
{
// считываем запрос клиента (браузера) строчка за строчкой:
if (client.available())
{
String line = client.readStringUntil('\r');
Serial.print(line);
// ждем конца запроса, помеченного пустой строкой:
if (line.length() == 1 && line[0] == '\n')
{
client.println(prepareHtmlPage());
break;
}
}
}
delay(1); // даем браузеру время получить данные
// закрываем соединение:
client.stop();
Serial.println("[Client disonnected]");
// "Клиент отключен"
}
}
Запускаем скетч
Меняем в скетче строчки с ssid и password на SSID и пароль к своей точке доступа. Загружаем скетч на ESP8266 и открываем монитор порта. Сначала в мониторе порта должно появиться подтверждение того, что модуль подключен к точке доступа, а веб-сервер – запущен.
Connecting to sensor-net ........ connected
Web server started, open 192.168.1.104 in a web browser
Вводим в адресную строку браузера показанный IP-адрес. В результате должна появиться страница, обслуживаемая ESP8266.
Эта страница будет обновляться каждые 5 секунд. С каждым новым обновлением в мониторе порта должен появляться новый запрос от клиента:
[Client connected]
GET / HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: en-US
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: 192.168.1.104
DNT: 1
Connection: Keep-Alive
[client disonnected]
Что дальше?
Этот скетч отчасти похож на «обратный» клиентский скетч – в том, из чего состоит протокол. Во-первых, он начинается с заголовка, содержащего информацию о коммуникации. Во-вторых, содержит информацию об одобренном типе контента (вроде text/html). В-третьих, сообщает, останется ли соединение открытым, когда монитор порта закончит показывать заголовок, или будет закрыто. В-четвертых, содержит идентификатор отправителя вроде User-Agent: Mozilla/5.0 (Windows NT 6.1). Также включает информацию о языке, IP-адресе и т.д.
Итого
Этот пример демонстрирует, что в настройке веб-сервера на базе ESP8266 нет ничего сложного. Такой сервер может принимать запросы от мощных устройств и программ вроде ПК и веб-браузера. По этой ссылке можно почитать о других классах вроде ESP8266WebServer, благодаря которым свой веб-серверный скетч можно сделать еще более функциональным.
Если вы хотите попробовать другой скетч, использующий класс сервера, обратите внимание на WiFiWebServer.ino, позволяющий включать/выключать GPIO-контакт прямо из браузера.
Полный список функций для работы с классом сервера смотрите по [ссылка этой ссылке].