ESP8266:Прошивки/Arduino/Библиотеки/Библиотека ESP8266WiFi/Класс сервера/Сервер, обслуживающий веб-страницу: различия между версиями

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску
(Новая страница: «{{ESP8266 панель перехода}} {{Перевод от Сubewriter}} {{Myagkij-редактор}} {{Черновик}} =Сервер, обслужив…»)
 
Нет описания правки
Строка 15: Строка 15:
Начнем с создания экземпляра класса WiFiServer:
Начнем с создания экземпляра класса WiFiServer:


<syntaxhighlight lang="c" enclose="div">
<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" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
String prepareHtmlPage()
String prepareHtmlPage()
{
{
Строка 51: Строка 51:
Заголовок информирует клиента, какой тип контента последует и как его будут выдавать:
Заголовок информирует клиента, какой тип контента последует и как его будут выдавать:


<syntaxhighlight lang="html5" enclose="div">
<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" enclose="div">
<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" enclose="div">
<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" enclose="div">
<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" enclose="div">
<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" enclose="div">
<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" enclose="div">
<syntaxhighlight lang="c">
client.stop();
client.stop();
</syntaxhighlight>
</syntaxhighlight>
Строка 120: Строка 120:
Ниже весь этот [[скетч]] представлен в полном виде:
Ниже весь этот [[скетч]] представлен в полном виде:


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<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" enclose="div">
<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" enclose="div">
<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-контакт прямо из браузера.

Полный список функций для работы с классом сервера смотрите по [ссылка этой ссылке].

См.также

Внешние ссылки