• Уважаемые посетители сайта esp8266.ru!
    Мы отказались от размещения рекламы на страницах форума для большего комфорта пользователей.
    Вы можете оказать посильную поддержку администрации форума. Данные средства пойдут на оплату услуг облачных провайдеров для сайта esp8266.ru
  • Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Проблема с WiFiClient client = server.available(); и браузером Гугл хром

PichBah

New member
Делаю дома WiFi сервер на esp8266, в качестве отправной точки использую код
  1. /*********
  2. Руи Сантос
  3. Более подробно об этом проекте на: Random Nerd Tutorials | Learn ESP32, ESP8266, Arduino, and Raspberry Pi
  4. *********/

  5. // загружаем библиотеку для WiFi и другие библиотеки:
  6. //#include <WiFi.h> //Это выключаем
  7. #include <ESP8266WiFi.h> //Это добавляем
  8. #include <Wire.h>
  9. #include <Adafruit_BME280.h>
  10. #include <Adafruit_Sensor.h>

  11. // при использовании SPI убираем комментарии у строчек ниже:
  12. /*#include <SPI.h>
  13. #define BME_SCK 18
  14. #define BME_MISO 19
  15. #define BME_MOSI 23
  16. #define BME_CS 5*/

  17. #define SEALEVELPRESSURE_HPA (1013.25)

  18. Adafruit_BME280 bme; // I2C
  19. // аппаратный SPI:
  20. //Adafruit_BME280 bme(BME_CS);
  21. // программный SPI:
  22. //Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);

  23. // ставим здесь учетные данные своей сети:
  24. const char* ssid = "";
  25. const char* password = "";

  26. // выставляем номер порта на «80»:
  27. WiFiServer server(80);

  28. // создаем переменную для хранения HTTP-запроса:
  29. String header;

  30. void setup() {
  31. Wire.begin(2, 0); //Включаем I2C на пинах 2 и 0
  32. Serial.begin(115200);
  33. bool status;

  34. // настройки по умолчанию
  35. // (вы также можете использовать объект библиотеки Wire):
  36. //status = bme.begin();
  37. if (!bme.begin(0x76)) {
  38. Serial.println("Could not find a valid BME280 sensor, check wiring!");
  39. // "Невозможно найти корректный датчик BME280,проверьте подключение!"
  40. while (1);
  41. }

  42. // подключаемся к WiFi-сети при помощи SSID и пароля:
  43. Serial.print("Connecting to "); // "Подключение к"
  44. Serial.println(ssid);
  45. WiFi.begin(ssid, password);
  46. while (WiFi.status() != WL_CONNECTED) {
  47. delay(500);
  48. Serial.print(".");
  49. }
  50. // печатаем локальный IP-адрес и запускаем веб-сервер:
  51. Serial.println("");
  52. Serial.println("WiFi connected."); // "WiFi подключен."
  53. Serial.println("IP address: "); // "IP-адрес: "
  54. Serial.println(WiFi.localIP());
  55. server.begin();
  56. }

  57. void loop(){
  58. // начинаем прослушивать входящих клиентов:
  59. WiFiClient client = server.available();

  60. if (client) { // если подключился новый клиент,
  61. Serial.println("New Client."); // печатаем сообщение
  62. // «Новый клиент.»
  63. // в мониторе порта;
  64. String currentLine = ""; // создаем строку для хранения
  65. // входящих данных от клиента;
  66. while (client.connected()) { // цикл while() будет работать
  67. // все то время, пока клиент
  68. // будет подключен к серверу;
  69. if (client.available()) { // если у клиента есть данные,
  70. // которые можно прочесть,
  71. char c = client.read(); // считываем байт, а затем
  72. Serial.write(c); // печатаем его в мониторе порта
  73. header += c;
  74. if (c == '\n') { // если этим байтом является
  75. // символ новой строки
  76. // если мы получим два символа новой строки подряд,
  77. // то это значит, что текущая строчка пуста;
  78. // это конец HTTP-запроса клиента,
  79. // а значит – пора отправлять ответ:
  80. if (currentLine.length() == 0) {
  81. // HTTP-заголовки всегда начинаются
  82. // с кода ответа (например, «HTTP/1.1 200 OK»)
  83. // и информации о типе контента
  84. // (чтобы клиент понимал, что получает);
  85. // в конце пишем пустую строчку:
  86. client.println("HTTP/1.1 200 OK");
  87. client.println("Content-type:text/html");
  88. client.println("Connection: close");
  89. // "Соединение: отключено"
  90. client.println();
  91. // показываем веб-страницу с помощью этого HTML-кода:
  92. client.println("<!DOCTYPE html><html>");
  93. client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
  94. client.println("<link rel=\"icon\" href=\"data:,\">");
  95. // задаем CSS-стили для таблицы:
  96. client.println("<style>body { text-align: center; font-family: \"Trebuchet MS\", Arial;}");
  97. client.println("table { border-collapse: collapse; width:35%; margin-left:auto; margin-right:auto; }");
  98. client.println("th { padding: 12px; background-color: #0043af; color: white; }");
  99. client.println("tr { border: 1px solid #ddd; padding: 12px; }");
  100. client.println("tr:hover { background-color: #bcbcbc; }");
  101. client.println("td { border: none; padding: 12px; }");
  102. client.println(".sensor { color:white; font-weight: bold; background-color: #bcbcbc; padding: 1px; }");
  103. // заголовок веб-страницы:
  104. client.println("</style></head><body><h1>ESP32 with BME280</h1>");
  105. client.println("<table><tr><th>MEASUREMENT</th><th>VALUE</th></tr>");
  106. client.println("<tr><td>Temp. Celsius</td><td><span class=\"sensor\">");
  107. client.println(bme.readTemperature());
  108. client.println(" *C</span></td></tr>");
  109. client.println("<tr><td>Temp. Fahrenheit</td><td><span class=\"sensor\">");
  110. client.println(1.8 * bme.readTemperature() + 32);
  111. client.println(" *F</span></td></tr>");
  112. client.println("<tr><td>Pressure</td><td><span class=\"sensor\">");
  113. client.println(bme.readPressure() / 100.0F);
  114. client.println(" hPa</span></td></tr>");
  115. client.println("<tr><td>Approx. Altitude</td><td><span class=\"sensor\">");
  116. client.println(bme.readAltitude(SEALEVELPRESSURE_HPA));
  117. client.println(" m</span></td></tr>");
  118. client.println("<tr><td>Humidity</td><td><span class=\"sensor\">");
  119. client.println(bme.readHumidity());
  120. client.println(" %</span></td></tr>");
  121. client.println("</body></html>");
  122. // конец HTTP-ответа задается
  123. // с помощью дополнительной пустой строки:
  124. client.println();
  125. // выходим из цикла while():
  126. break;
  127. } else { // если получили символ новой строки,
  128. // очищаем переменную «currentLine»
  129. currentLine = "";
  130. }
  131. } else if (c != '\r') { // если получили любые данные,
  132. // кроме символа возврата каретки,
  133. currentLine += c; // добавляем эти данные
  134. // в конец строки «currentLine»
  135. }
  136. }
  137. }
  138. // очищаем переменную «header»:
  139. header = "";
  140. // отключаем соединение:
  141. client.stop();
  142. Serial.println("Client disconnected.");
  143. // "Клиент отключился.")
  144. Serial.println("");
  145. }
  146. }
Код написан для ESP32, поэтому меняю #include <WiFi.h> на #include <ESP8266WiFi.h>, включаю
I2C. Прописываю свои ssid и password. WiFi роутер по MAC адресу выдает статический IP. Подключаюсь по нему и все работает, но! Если в Хроме открыть IPадрес в режиме инкогнито все хорошо, открывается страница и клиент отключается, тоже самое если открыть страницу с телефона. Но если открыть страничку в хроме не инкогнито, страница открывается, клиент отключается и сразу подключается новый клиент. И при этом он молчит, контроллер зависает на строке 78. Чего такого отправляет Хром, что клиент создается вновь и как это исправить?
 

Алексей.

Active member
Чего такого отправляет Хром, что клиент создается вновь и как это исправить?
Вы в отладчике в хроме смотрели?
Ничего он не отправляет, только ещё одно соединение устанавливает.
Может ещё favicon запрашивать.
 

PichBah

New member
Вы в отладчике в хроме смотрели?
Ничего он не отправляет, только ещё одно соединение устанавливает.
Может ещё favicon запрашивать.
В отладчике Хрома и сообщениях в SerialPort была замечена разница, но после добавления строки
client.println("<meta http-equiv=\"cache-control\" content=\"max-age=0\">"); сообщения в порт стали одинаковыми.
Судя по тому что выдал гугл на запрос "favicon ESP8266" очень похоже на то что у меня.
 

PichBah

New member
Точно не уверен, есть мысли что данная проблема возникает из за
GET /favicon.ico HTTP/1.1 запроса (Спасибо @Алексей.). Который идет следом за GET / HTTP/1.1 запросом. favicon.ico это иконка странички на вкладке в браузере. Этот запрос видно только если сменить порт сервера со стандартного 80го на любой другой. Тогда адрес страницы в локальной сети имеет вид http//ip:port. В любом случае от подхода описанного в примере вынужден отказаться в пользу библиотек #include <ESPAsyncWebServer.h> или #include <ESP8266WebServer.h>. На первый взгляд библиотеки очень похожи, хотя первая и поддерживает одновременное подключение нескольких клиентов, автор настаивает на использовании platformio для работы с ней. Оставлю это здесь, мб кому нибудь пригодится.
 

pvvx

Активный участник сообщества
@PichBah вы указали HTTP1.1 и [inline]client.println("Connection: close");[/inline] без указания длины context, что уже неверно. А далее закрываете соединение [inline]client.stop();[/inline]. Для TCP это тоже неверно. Клиент должен произвести отключение, иначе у вас метрика TCP (структура в памяти) перейдет в состояние TIME_WAIT на 120 сек и порт соединения будет занят всё это время. Так вы ограничиваете память приложения набором структур с таймерами у Lwip на каждое неправильно закрытое соединение.
Короче Ардуино для детсада...
 

PichBah

New member
Короче Ардуино для детсада...
Спасибо за пояснения что я все делаю не верно (а то я и без вас не знал, иначе бы не стал спрашивать на форуме, л - логика?) да я еще учусь...
вы указали HTTP1.1 и client.println("Connection: close"); без указания длины context, что уже неверно. А далее закрываете соединение client.stop();. Для TCP это тоже неверно. Клиент должен произвести отключение
Начнем с того что не я указал, а Руи Сантоси и его примеры цитируют очень многие. А вы могли бы не умничать и более глубже объяснить эти моменты.
 
Сверху Снизу