• Система автоматизации с открытым исходным кодом на базе 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 это тоже неверно. Клиент должен произвести отключение
Начнем с того что не я указал, а Руи Сантоси и его примеры цитируют очень многие. А вы могли бы не умничать и более глубже объяснить эти моменты.
 
Сверху Снизу