ESP8266 и Arduino IDE. Часть 8: Авто-обновление веб-страницы

Примечание

Оригинал статьи: martyncurrey.com

Этот урок демонстрирует, как создавать самообновляющиеся веб-страницы на ESP8266 с использованием трёх различных подходов: HTML meta refresh, JavaScript page reload и AJAX с частичным обновлением страницы.

Метод 1: HTML Meta Refresh

Старые веб-сайты требовали взаимодействия пользователя для обновления контента. HTML-тег refresh обеспечивает автоматическую перезагрузку страницы:

<meta http-equiv="refresh" content="30">

Этот подход перезагружает всю страницу каждые 30 секунд, но считается «неуклюжим» и имеет минимальный интервал в 1 секунду.

Скетч: ESP8266_Part8_01_AutoUpdate_HTML

/*
 * Sketch: ESP8266_Part8_01_AutoUpdate_HTML
 * Intended to be run on an ESP8266
 */

String header = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";

String html_1 = R"=====(
<!DOCTYPE html>
<html>
 <head>
 <meta name='viewport' content='width=device-width, initial-scale=1.0'/>
 <meta charset='utf-8'>
 <meta http-equiv='refresh' content='5'>
 <style>
   body {font-size:100%;}
   #main {display: table; margin: auto;  padding: 0 10px 0 10px; }
   h2 {text-align:center; }
   p { text-align:center; }
 </style>
   <title>Auto Update Example Using HTML</title>
 </head>

 <body>
   <div id='main'>
     <h2>Auto Update Example Using HTML</h2>
     <div id='count'>
       <p>Count = %count%</p>
     </div>
   </div>
 </body>
</html>
)=====";

#include <ESP8266WiFi.h>

char ssid[] = "NetworkName";
char pass[] = "password";

WiFiServer server(80);

String tmpString = "";
unsigned int count = 0;

void setup() {
    Serial.begin(115200);
    Serial.println();
    Serial.println("Serial started at 115200");
    Serial.println();

    Serial.print(F("Connecting to "));  Serial.println(ssid);
    WiFi.begin(ssid, pass);

    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(".");
        delay(500);
    }

    Serial.println("");
    Serial.println(F("[CONNECTED]"));
    Serial.print("[IP ");
    Serial.print(WiFi.localIP());
    Serial.println("]");

    server.begin();
    Serial.println("Server started");
}

void loop() {
    WiFiClient client = server.available();
    if (!client)  {  return;  }

    count ++;

    tmpString = html_1;
    tmpString.replace("%count%", String(count) );

    client.flush();
    client.print( header );
    client.print( tmpString );

    Serial.print("count = "); Serial.println(count);

    delay(5);
}
Авто-обновление через HTML

Метод 2: JavaScript setTimeout

JavaScript предоставляет больше контроля с setTimeout() и setInterval():

function refresh(refreshPeriod) {
    setTimeout('location.reload(true)', refreshPeriod);
}
window.onload = refresh(5000);

Этот метод даёт больше гибкости, но по-прежнему перезагружает всю страницу, что может вызвать проблемы с производительностью.

Авто-обновление через JavaScript

Метод 3: AJAX с setInterval

Наиболее эффективный подход использует AJAX с setInterval() для обновления только определённых элементов страницы:

setInterval(updateCount, 5000);

Вместо перезагрузки всей страницы AJAX-запросы получают только необходимые данные, обеспечивая более плавные визуальные обновления.

Скетч: ESP8266_Part8_03_AutoUpdate_Javascript_AJAX

/*
 * Sketch: ESP8266_Part8_03_AutoUpdate_Javascript_AJAX
 * Intended to be run on an ESP8266
 */

String header = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";

String html_1 = R"=====(
<!DOCTYPE html>
<html>
 <head>
  <meta name='viewport' content='width=device-width, initial-scale=1.0'/>
  <meta charset='utf-8'>
  <style>
    body {font-size:100%;}
    #main {display: table; margin: auto;  padding: 0 10px 0 10px; }
    h2 {text-align:center; }
    p { text-align:center; }
  </style>

  <script>
    function updateCount() {
       ajaxLoad('getCount');
    }

    var ajaxRequest = null;
    if (window.XMLHttpRequest)  { ajaxRequest =new XMLHttpRequest(); }
    else                        { ajaxRequest =new ActiveXObject("Microsoft.XMLHTTP"); }

    function ajaxLoad(ajaxURL) {
      if(!ajaxRequest){ alert('AJAX is not supported.'); return; }

      ajaxRequest.open('GET',ajaxURL,true);
      ajaxRequest.onreadystatechange = function() {
        if(ajaxRequest.readyState == 4 && ajaxRequest.status==200) {
          var ajaxResult = ajaxRequest.responseText;
          document.getElementById('count_P').textContent = ajaxResult;
        }
      }
      ajaxRequest.send();
    }

    setInterval(updateCount, 5000);

  </script>
  <title>Auto Update Example Using Javascript 2</title>
 </head>

 <body>
   <div id='main'>
     <h2>Auto Update Example Using Javascript 2</h2>
     <div id='count_DIV'>
       <p id='count_P'>Count = 0</p>
     </div>
   </div>
 </body>
</html>
)=====";

#include <ESP8266WiFi.h>

char ssid[] = "NetworkName";
char pass[] = "password";

WiFiServer server(80);

String request = "";
unsigned int count = 0;

void setup() {
    Serial.begin(115200);
    Serial.println();
    Serial.println("Serial started at 115200");
    Serial.println();

    Serial.print(F("Connecting to "));  Serial.println(ssid);
    WiFi.begin(ssid, pass);

    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(".");
        delay(500);
    }

    Serial.println("");
    Serial.println(F("[CONNECTED]"));
    Serial.print("[IP ");
    Serial.print(WiFi.localIP());
    Serial.println("]");

    server.begin();
    Serial.println("Server started");
}

void loop() {
    WiFiClient client = server.available();
    if (!client)  {  return;  }

    request = client.readStringUntil('\r');

    if ( request.indexOf("getCount") > 0 ) {
        count ++;
        client.print( header );
        client.print( "Count = " );
        client.print( count );

        Serial.print("Count=");
        Serial.println(count);
    }
    else {
        client.flush();
        client.print( header );
        client.print( html_1 );
        count = 0;
    }

    delay(5);
}
AJAX авто-обновление

Практический пример: монитор температуры и влажности

Урок завершается практическим примером с датчиком DHT11. Веб-страница отображает:

  • Текущее время (обновляется каждую секунду локально)

  • Температуру в Цельсиях и Фаренгейтах (обновляется каждые 5 секунд через AJAX)

  • Влажность в процентах (обновляется каждые 5 секунд через AJAX)

Схема подключения

Схема подключения DHT11 Макетная плата с DHT11 Макетная плата - вид сверху

Скетч: ESP8266_Part8_04_AutoUpdate_DHT11

/*
 * Sketch: ESP8266_Part8_04_AutoUpdate_DHT11
 * Intended to be run on an ESP8266
 */

String header = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";

String html_1 = R"=====(
<!DOCTYPE html>
<html>
 <head>
  <meta name='viewport' content='width=device-width, initial-scale=1.0'/>
  <meta charset='utf-8'>
  <style>
    body {font-size:100%;}
    #main {display: table; margin: auto;  padding: 10px 10px 10px 10px; }
    #content { border: 5px solid blue; border-radius: 15px; padding: 10px 0px 10px 0px;}
    h2 {text-align:center; margin: 10px 0px 10px 0px;}
    p { text-align:center; margin: 5px 0px 10px 0px; font-size: 120%;}
    #time_P { margin: 10px 0px 15px 0px;}
  </style>

  <script>
    function updateTime() {
       var d = new Date();
       var t = "";
       t = d.toLocaleTimeString();
       document.getElementById('P_time').textContent = t;
    }

    function updateTemp() {
       ajaxLoad('getTemp');
    }

    var ajaxRequest = null;
    if (window.XMLHttpRequest)  { ajaxRequest =new XMLHttpRequest(); }
    else                        { ajaxRequest =new ActiveXObject("Microsoft.XMLHTTP"); }

    function ajaxLoad(ajaxURL) {
      if(!ajaxRequest){ alert('AJAX is not supported.'); return; }

      ajaxRequest.open('GET',ajaxURL,true);
      ajaxRequest.onreadystatechange = function() {
        if(ajaxRequest.readyState == 4 && ajaxRequest.status==200) {
          var ajaxResult = ajaxRequest.responseText;
          var tmpArray = ajaxResult.split("|");
          document.getElementById('temp_C').textContent = tmpArray[0];
          document.getElementById('temp_F').textContent = tmpArray[1];
          document.getElementById('hmd').textContent = tmpArray[2];
        }
      }
      ajaxRequest.send();
    }

    var myVar1 = setInterval(updateTemp, 5000);
    var myVar2 = setInterval(updateTime, 1000);

  </script>

  <title>Temperature & Humidity Monitor</title>
 </head>

 <body>
   <div id='main'>
     <h2>Temperature & Humidity Monitor</h2>
     <div id='content'>
       <p id='P_time'>Time</p>
       <h2>Temperature</h2>
       <p> <span id='temp_C'>--.-</span> &deg;C &nbsp;-&nbsp; <span id='temp_F'>--.-</span> &deg;F </p>
       <h2>Humidity</h2>
       <p> <span id='hmd'>--</span> % </p>
     </div>
   </div>
 </body>
</html>
)=====";

#include <ESP8266WiFi.h>

char ssid[] = "myNetworkName";
char pass[] = "password";

#include "DHT.h"
#define DHTPIN 2
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);

float tempF = 0;
float tempC = 0;
float humid = 0;

WiFiServer server(80);
String request = "";

void setup() {
    Serial.begin(115200);
    Serial.println();
    Serial.println("Serial started at 115200");
    Serial.println();

    dht.begin();

    Serial.print(F("Connecting to "));  Serial.println(ssid);
    WiFi.begin(ssid, pass);

    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(".");
        delay(500);
    }

    Serial.println("");
    Serial.println(F("[CONNECTED]"));
    Serial.print("[IP ");
    Serial.print(WiFi.localIP());
    Serial.println("]");

    server.begin();
    Serial.println("Server started");
}

void loop() {
    WiFiClient client = server.available();
    if (!client)  {  return;  }

    request = client.readStringUntil('\r');

    Serial.println(request);
    Serial.println("");

    if ( request.indexOf("getTemp") > 0 ) {
        Serial.println("getTemp received");

        humid = dht.readHumidity();
        tempC = dht.readTemperature();
        tempF = dht.readTemperature(true);

        if ( !isnan(humid) && !isnan(tempC) && !isnan(tempC) ) {
            client.print( header );
            client.print( tempC );
            client.print( "|" );
            client.print( tempF );
            client.print( "|" );
            client.print( humid );
            Serial.println("data sent");
        }
        else {
            Serial.println("Error reading the sensor");
        }
    }
    else {
        client.flush();
        client.print( header );
        client.print( html_1 );
        Serial.println("New page served");
    }

    delay(5);
}

Результат

Монитор температуры и влажности

Сервер различает начальные запросы страницы и последующие AJAX-запросы данных, проверяя наличие строки «getTemp» в запросе. В зависимости от этого возвращается либо полный HTML, либо данные датчика.