ESP8266 NodeMCU веб-сервер с использованием Server-Sent Events (автоматическое обновление показаний датчиков)

В этом руководстве показано, как использовать Server-Sent Events (SSE) в веб-сервере ESP8266 NodeMCU, программируемом с помощью Arduino IDE. SSE позволяет браузеру получать автоматические обновления от сервера через HTTP-соединение. Это полезно для отправки обновлённых показаний датчиков в браузер, например. Каждый раз, когда доступно новое показание, ESP8266 отправляет его клиенту, и веб-страница может обновляться автоматически без необходимости выполнения дополнительных запросов.

ESP8266 NodeMCU веб-сервер Server-Sent Events SSE автоматическое обновление показаний датчиков Arduino

В качестве примера мы создадим веб-сервер, который отображает показания датчика температуры, влажности и давления BME280. Чтобы узнать больше о BME280, прочитайте наше руководство:

У нас также есть аналогичное руководство по Server-Sent Events для ESP32.

Введение в Server-Sent Events (SSE)

Server-Sent Events (SSE) позволяют клиенту получать автоматические обновления от сервера через HTTP-соединение.

ESP8266 NodeMCU Server-Sent Events веб-сервер как это работает

Клиент инициирует SSE-соединение, и сервер использует протокол источника событий для отправки обновлений клиенту. Клиент будет получать обновления от сервера, но не может отправлять какие-либо данные серверу после начального рукопожатия.

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

Важно: Server-Sent Events (SSE) не поддерживаются в Internet Explorer.

Обзор проекта

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

Веб-сервер BME280 с использованием Server-Sent Events SSE ESP8266 NodeMCU Arduino
  • Веб-сервер ESP8266 отображает три карточки с показаниями температуры, влажности и давления BME280;

  • ESP8266 получает новые показания с датчика каждые 30 секунд;

  • Каждый раз, когда доступно новое показание, плата (сервер) отправляет его клиенту с помощью server-sent events;

  • Клиент получает данные и обновляет веб-страницу соответственно;

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

Как это работает?

Следующая диаграмма обобщает, как Server-Sent Events работают для обновления веб-страницы.

ESP8266 NodeMCU BME280 веб-сервер с Server Sent Events SSE как это работает
  1. Клиент инициирует SSE-соединение, и сервер использует протокол источника событий по URL /events для отправки обновлений клиенту;

  2. ESP8266 получает новые показания датчика;

  3. Он отправляет показания в виде событий с именами: „temperature“, „humidity“ и „pressure“ клиенту;

  4. Клиент имеет слушатели событий для событий, отправленных сервером, и получает обновлённые показания датчика по этим событиям;

  5. Он обновляет веб-страницу самыми новыми показаниями.

Подготовка Arduino IDE

Мы будем программировать плату ESP8266 с использованием Arduino IDE, поэтому убедитесь, что она установлена в вашей Arduino IDE.

Установка библиотек – Async Web Server

Мы создадим веб-сервер, используя следующие библиотеки:

Вы можете установить эти библиотеки с помощью менеджера библиотек Arduino. Перейдите в Sketch > Include Library > Manage Libraries и найдите названия библиотек.

Установка библиотек – датчик BME280

Для получения показаний с модуля датчика BME280 мы будем использовать библиотеку Adafruit_BME280. Вам также нужно установить библиотеку Adafruit_Sensor.

Вы можете установить эти библиотеки с помощью менеджера библиотек Arduino. Перейдите в Sketch > Include Library > Manage Libraries и найдите названия библиотек.

Чтобы узнать больше о датчике BME280, прочитайте наше руководство: ESP8266 с датчиком BME280 с использованием Arduino IDE (давление, температура, влажность).

Сборка схемы

Чтобы продемонстрировать, как использовать server-sent events с ESP8266, мы будем отправлять показания датчика BME280 в браузер. Поэтому вам нужно подключить датчик BME280 к вашему ESP8266.

Необходимые компоненты

Для выполнения этого руководства вам понадобятся следующие компоненты:

Вы можете использовать приведённые выше ссылки или перейти непосредственно на MakerAdvisor.com/tools, чтобы найти все компоненты для ваших проектов по лучшей цене!

Схема – ESP8266 с BME280 через I2C

Мы будем использовать I2C-связь с модулем датчика BME280. Для этого подключите датчик к выводам SDA и SCL ESP8266, как показано на следующей схеме.

Схема подключения ESP8266 NodeMCU с BME280 через I2C

Рекомендуемое чтение: Справочник по распиновке ESP8266

Код для веб-сервера ESP8266 с использованием Server-Sent Events (SSE)

Скопируйте следующий код в вашу Arduino IDE.

/*********
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp8266-nodemcu-web-server-sent-events-sse/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/

#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Adafruit_BME280.h>
#include <Adafruit_Sensor.h>

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

// Create an Event Source on /events
AsyncEventSource events("/events");

// Timer variables
unsigned long lastTime = 0;
unsigned long timerDelay = 30000;

// Create a sensor object
Adafruit_BME280 bme; // BME280 connect to ESP32 I2C (GPIO 21 = SDA, GPIO 22 = SCL)

float temperature;
float humidity;
float pressure;

// Init BME280
void initBME(){
    if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
}

void getSensorReadings(){
  temperature = bme.readTemperature();
  // Convert temperature to Fahrenheit
  //temperature = 1.8 * bme.readTemperature() + 32;
  humidity = bme.readHumidity();
  pressure = bme.readPressure()/ 100.0F;
}

// Initialize WiFi
void initWiFi() {
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);
    Serial.print("Connecting to WiFi ..");
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print('.');
        delay(1000);
    }
    Serial.println(WiFi.localIP());
}

String processor(const String& var){
  getSensorReadings();
  //Serial.println(var);
  if(var == "TEMPERATURE"){
    return String(temperature);
  }
  else if(var == "HUMIDITY"){
    return String(humidity);
  }
  else if(var == "PRESSURE"){
    return String(pressure);
  }
  return String();
}

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <title>ESP Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
  <link rel="icon" href="data:,">
  <style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    p { font-size: 1.2rem;}
    body {  margin: 0;}
    .topnav { overflow: hidden; background-color: #50B8B4; color: white; font-size: 1rem; }
    .content { padding: 20px; }
    .card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); }
    .cards { max-width: 800px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
    .reading { font-size: 1.4rem; }
  </style>
</head>
<body>
  <div class="topnav">
    <h1>BME280 WEB SERVER (SSE)</h1>
  </div>
  <div class="content">
    <div class="cards">
      <div class="card">
        <p><i class="fas fa-thermometer-half" style="color:#059e8a;"></i> TEMPERATURE</p><p><span class="reading"><span id="temp">%TEMPERATURE%</span> &deg;C</span></p>
      </div>
      <div class="card">
        <p><i class="fas fa-tint" style="color:#00add6;"></i> HUMIDITY</p><p><span class="reading"><span id="hum">%HUMIDITY%</span> &percnt;</span></p>
      </div>
      <div class="card">
        <p><i class="fas fa-angle-double-down" style="color:#e1e437;"></i> PRESSURE</p><p><span class="reading"><span id="pres">%PRESSURE%</span> hPa</span></p>
      </div>
    </div>
  </div>
<script>
if (!!window.EventSource) {
 var source = new EventSource('/events');

 source.addEventListener('open', function(e) {
  console.log("Events Connected");
 }, false);
 source.addEventListener('error', function(e) {
  if (e.target.readyState != EventSource.OPEN) {
    console.log("Events Disconnected");
  }
 }, false);

 source.addEventListener('message', function(e) {
  console.log("message", e.data);
 }, false);

 source.addEventListener('temperature', function(e) {
  console.log("temperature", e.data);
  document.getElementById("temp").innerHTML = e.data;
 }, false);

 source.addEventListener('humidity', function(e) {
  console.log("humidity", e.data);
  document.getElementById("hum").innerHTML = e.data;
 }, false);

 source.addEventListener('pressure', function(e) {
  console.log("pressure", e.data);
  document.getElementById("pres").innerHTML = e.data;
 }, false);
}
</script>
</body>
</html>)rawliteral";

void setup() {
  Serial.begin(115200);
  initWiFi();
  initBME();

  // Handle Web Server
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/html", index_html, processor);
  });

  // Handle Web Server Events
  events.onConnect([](AsyncEventSourceClient *client){
    if(client->lastId()){
      Serial.printf("Client reconnected! Last message ID that it got is: %u\n", client->lastId());
    }
    // send event with message "hello!", id current millis
    // and set reconnect delay to 1 second
    client->send("hello!", NULL, millis(), 10000);
  });
  server.addHandler(&events);
  server.begin();
}

void loop() {
  if ((millis() - lastTime) > timerDelay) {
    getSensorReadings();
    Serial.printf("Temperature = %.2f ºC \n", temperature);
    Serial.printf("Humidity = %.2f \n", humidity);
    Serial.printf("Pressure = %.2f hPa \n", pressure);
    Serial.println();

    // Send Events to the Web Server with the Sensor Readings
    events.send("ping",NULL,millis());
    events.send(String(temperature).c_str(),"temperature",millis());
    events.send(String(humidity).c_str(),"humidity",millis());
    events.send(String(pressure).c_str(),"pressure",millis());

    lastTime = millis();
  }
}

Просмотреть исходный код

Вставьте ваши сетевые учётные данные в следующие переменные, и код заработает сразу.

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Как работает код

Продолжайте чтение, чтобы узнать, как работает код, или перейдите к разделу Демонстрация.

Подключение библиотек

Библиотеки Adafruit_Sensor и Adafruit_BME280 необходимы для работы с датчиком BME280.

#include <Adafruit_BME280.h>
#include <Adafruit_Sensor.h>

Библиотеки WiFi, ESPAsyncWebServer и AsyncTCP используются для создания веб-сервера.

#include <WiFi.h>
#include "ESPAsyncWebServer.h"

Сетевые учётные данные

Вставьте ваши сетевые учётные данные в следующие переменные, чтобы ESP8266 мог подключиться к вашей локальной сети через Wi-Fi.

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

AsyncWebServer и AsyncEventSource

Создайте объект AsyncWebServer на порту 80.

AsyncWebServer server(80);

Следующая строка создаёт новый источник событий по адресу /events.

AsyncEventSource events("/events");

Объявление переменных

Переменные lastTime и timerDelay будут использоваться для обновления показаний датчика каждые X секунд. В качестве примера мы будем получать новые показания датчика каждые 30 секунд (30000 миллисекунд). Вы можете изменить это время задержки в переменной timerDelay.

unsigned long lastTime = 0;
unsigned long timerDelay = 30000;

Создайте объект Adafruit_BME280 с именем bme на стандартных I2C-выводах ESP8266.

Adafruit_BME280 bme;

Переменные типа float temperature, humidity и pressure будут использоваться для хранения показаний датчика BME280.

float temperature;
float humidity;
float pressure;

Инициализация BME280

Следующая функция может быть вызвана для инициализации датчика BME280.

void initBME(){
  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
}

Получение показаний BME280

Функция getSensorReading() получает показания температуры, влажности и давления от датчика BME280 и сохраняет их в переменных temperature, humidity и pressure.

void getSensorReadings(){
  temperature = bme.readTemperature();
  // Convert temperature to Fahrenheit
  //temperature = 1.8 * bme.readTemperature() + 32;
  humidity = bme.readHumidity();
  pressure = bme.readPressure()/ 100.0F;
}

Инициализация Wi-Fi

Следующая функция устанавливает ESP8266 в режим Wi-Fi станции и подключается к вашему роутеру, используя ssid и password, определённые ранее.

void initWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
}

Процессор

Функция processor() заменяет все заполнители в HTML-тексте, используемом для создания веб-страницы, текущими показаниями датчика перед отправкой его в браузер.

String processor(const String& var){
  getSensorReadings();
  //Serial.println(var);
  if(var == "TEMPERATURE"){
    return String(temperature);
  }
  else if(var == "HUMIDITY"){
    return String(humidity);
  }
  else if(var == "PRESSURE"){
    return String(pressure);
  }
}

Это позволяет нам отображать текущие показания датчика на веб-странице при первом обращении к ней. В противном случае вы увидите пустое место, пока не будут доступны новые показания (что может занять некоторое время в зависимости от времени задержки, установленного в коде).

Создание веб-страницы

Переменная index_html содержит весь HTML, CSS и JavaScript для создания веб-страницы.

Примечание: для простоты этого руководства мы размещаем всё необходимое для создания веб-страницы в переменной index_html, которую используем в скетче Arduino. Обратите внимание, что может быть более практичным иметь отдельные файлы HTML, CSS и JavaScript, которые затем вы загружаете в файловую систему ESP8266 и ссылаетесь на них в коде.

Вот содержимое переменной index_html:

<!DOCTYPE HTML>
<html>
<head>
  <title>ESP Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
  <link rel="icon" href="data:,">
  <style>
    html {
      font-family: Arial;
      display: inline-block;
      text-align: center;
    }
    p {
      font-size: 1.2rem;
    }
    body {
      margin: 0;
    }
    .topnav {
      overflow: hidden;
      background-color: #50B8B4;
      color: white;
      font-size: 1rem;
    }
    .content {
      padding: 20px;
    }
    .card {
      background-color: white;
      box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
    }
    .cards {
      max-width: 800px;
      margin: 0 auto;
      display: grid;
      grid-gap: 2rem;
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
    .reading {
      font-size: 1.4rem;
    }
  </style>
</head>
<body>
  <div class="topnav">
    <h1>BME280 WEB SERVER (SSE)</h1>
  </div>
  <div class="content">
    <div class="cards">
      <div class="card">
        <p><i class="fas fa-thermometer-half" style="color:#059e8a;"></i> TEMPERATURE</p>
        <p><span class="reading"><span id="temp">%TEMPERATURE%</span> &deg;C</span></p>
      </div>
      <div class="card">
        <p><i class="fas fa-tint" style="color:#00add6;"></i> HUMIDITY</p>
        <p><span class="reading"><span id="hum">%HUMIDITY%</span> &percnt;</span></p>
      </div>
      <div class="card">
        <p><i class="fas fa-angle-double-down" style="color:#e1e437;"></i> PRESSURE</p><p><span class="reading"><span id="pres">%PRESSURE%</span> hPa</span></p>
      </div>
    </div>
  </div>
<script>
if (!!window.EventSource) {
 var source = new EventSource('/events');

 source.addEventListener('open', function(e) {
  console.log("Events Connected");
 }, false);
 source.addEventListener('error', function(e) {
  if (e.target.readyState != EventSource.OPEN) {
    console.log("Events Disconnected");
  }
 }, false);

 source.addEventListener('message', function(e) {
  console.log("message", e.data);
 }, false);

 source.addEventListener('temperature', function(e) {
  console.log("temperature", e.data);
  document.getElementById("temp").textContent = e.data;
 }, false);

 source.addEventListener('humidity', function(e) {
  console.log("humidity", e.data);
  document.getElementById("hum").textContent = e.data;
 }, false);

 source.addEventListener('pressure', function(e) {
  console.log("pressure", e.data);
  document.getElementById("pres").textContent = e.data;
 }, false);
}
</script>
</body>
</html>

Мы не будем подробно рассматривать, как работают HTML и CSS. Мы только рассмотрим, как обрабатывать события, отправленные сервером.

CSS

Между тегами <style></style> мы включаем стили для оформления веб-страницы с помощью CSS. Не стесняйтесь изменять их, чтобы веб-страница выглядела так, как вы хотите. Мы не будем объяснять, как работает CSS для этой веб-страницы, потому что это не имеет отношения к данному руководству.

<style>
  html {
    font-family: Arial;
    display: inline-block;
    text-align: center;
  }
  p {
    font-size: 1.2rem;
  }
  body {
    margin: 0;
  }
  .topnav {
    overflow: hidden;
    background-color: #50B8B4;
    color: white;
    font-size: 1rem;
  }
  .content {
    padding: 20px;
  }
  .card {
    background-color: white;
    box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
  }
  .cards {
    max-width: 800px;
    margin: 0 auto;
    display: grid;
    grid-gap: 2rem;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
  .reading {
    font-size: 1.4rem;
  }
</style>

HTML

Между тегами <body></body> мы добавляем содержимое веб-страницы, которое видно пользователю.

<body>
  <div class="topnav">
    <h1>BME280 WEB SERVER (SSE)</h1>
  </div>
  <div class="content">
    <div class="cards">
      <div class="card">
        <p><i class="fas fa-thermometer-half" style="color:#059e8a;"></i> TEMPERATURE</p>
        <p><span class="reading"><span id="temp">%TEMPERATURE%</span> &deg;C</span></p>
      </div>
      <div class="card">
        <p><i class="fas fa-tint" style="color:#00add6;"></i> HUMIDITY</p>
        <p><span class="reading"><span id="hum">%HUMIDITY%</span> &percnt;</span></p>
      </div>
      <div class="card">
        <p><i class="fas fa-angle-double-down" style="color:#e1e437;"></i> PRESSURE</p><p><span class="reading"><span id="pres">%PRESSURE%</span> hPa</span></p>
      </div>
    </div>
  </div>

Есть заголовок 1 с содержимым «BME280 WEB SERVER (SSE)». Это текст, который отображается на верхней панели. Не стесняйтесь изменять этот текст.

<h1>BME280 WEB SERVER (SSE)</h1>

Затем мы отображаем показания датчиков в отдельных div-тегах. Давайте кратко рассмотрим параграфы, которые отображают температуру:

<p><i class="fas fa-thermometer-half" style="color:#059e8a;"></i> TEMPERATURE</p>
<p><span class="reading"><span id="temp">%TEMPERATURE%</span> &deg;C</span></p>

Первый параграф просто отображает текст «TEMPERATURE». Мы определяем цвет, а также иконку.

Во втором параграфе вы можете видеть, что заполнитель %TEMPERATURE% окружён тегами <span id=»temp»></span>. HTML-атрибут id используется для указания уникального идентификатора HTML-элемента.

Он используется для указания на определённый стиль или может быть использован JavaScript для доступа к элементу с этим конкретным id и управления им. Именно это мы и собираемся сделать. Например, когда клиент получает новое событие с последним показанием температуры, он обновляет HTML-элемент с id «temp» новым показанием.

Аналогичный процесс выполняется для обновления остальных показаний.

JavaScript – EventSource

JavaScript находится между тегами <script></script>. Он отвечает за инициализацию соединения EventSource с сервером и обработку событий, полученных от сервера.

<script>
if (!!window.EventSource) {
  var source = new EventSource('/events');
  source.addEventListener('open', function(e) {
    console.log("Events Connected");
  }, false);
  source.addEventListener('error', function(e) {
    if (e.target.readyState != EventSource.OPEN) {
      console.log("Events Disconnected");
    }
  }, false);

  source.addEventListener('message', function(e) {
    console.log("message", e.data);
  }, false);

  source.addEventListener('temperature', function(e) {
    console.log("temperature", e.data);
    document.getElementById("temp").innerHTML = e.data;
  }, false);

  source.addEventListener('humidity', function(e) {
    console.log("humidity", e.data);
    document.getElementById("hum").innerHTML = e.data;
  }, false);

  source.addEventListener('pressure', function(e) {
    console.log("pressure", e.data);
    document.getElementById("pres").innerHTML = e.data;
  }, false);
}
</script>

Давайте рассмотрим, как это работает.

Обработка событий

Создайте новый объект EventSource и укажите URL страницы, отправляющей обновления. В нашем случае это /events.

if (!!window.EventSource) {
  var source = new EventSource('/events');

После создания экземпляра источника событий вы можете начать прослушивать сообщения от сервера с помощью addEventListener().

Это стандартные слушатели событий, как показано в документации AsyncWebServer.

source.addEventListener('open', function(e) {
  console.log("Events Connected");
}, false);

source.addEventListener('error', function(e) {
  if (e.target.readyState != EventSource.OPEN) {
    console.log("Events Disconnected");
  }
}, false);

source.addEventListener('message', function(e) {
  console.log("message", e.data);
}, false);

Затем добавьте слушатель событий для «temperature».

source.addEventListener('temperature', function(e) {

Когда доступно новое показание температуры, ESP8266 отправляет событие («temperature») клиенту. Следующие строки обрабатывают то, что происходит, когда браузер получает это событие.

console.log("temperature", e.data);
document.getElementById("temp").innerHTML = e.data;

По сути, выводим новые показания в консоль браузера и помещаем полученные данные в элемент с соответствующим id («temp») на веб-странице.

Аналогичный процесс выполняется для влажности и давления.

source.addEventListener('humidity', function(e) {
  console.log("humidity", e.data);
  document.getElementById("hum").innerHTML = e.data;
}, false);

source.addEventListener('pressure', function(e) {
  console.log("pressure", e.data);
  document.getElementById("pres").innerHTML = e.data;
}, false);

source.addEventListener('gas', function(e) {
  console.log("gas", e.data);
  document.getElementById("gas").innerHTML = e.data;
}, false);

setup()

В setup() инициализируйте Serial Monitor, Wi-Fi и датчик BME280.

Serial.begin(115200);
initWiFi();
initBME();

Обработка запросов

Когда вы обращаетесь к IP-адресу ESP8266 по корневому URL /, отправляется текст, хранящийся в переменной index_html, для создания веб-страницы, и передаётся processor в качестве аргумента, чтобы все заполнители были заменены последними показаниями датчика.

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/html", index_html, processor);
});

Источник событий сервера

Настройте источник событий на сервере.

// Handle Web Server Events
events.onConnect([](AsyncEventSourceClient *client){
  if(client->lastId()){
    Serial.printf("Client reconnected! Last message ID that it got is: %u\n", client->lastId());
  }
  // send event with message "hello!", id current millis
  // and set reconnect delay to 1 second
  client->send("hello!", NULL, millis(), 10000);
});
server.addHandler(&events);

Наконец, запустите сервер.

server.begin();

loop()

В loop() получаем новые показания датчика:

getSensorReadings();

Выводим новые показания в Serial Monitor.

Serial.printf("Temperature = %.2f ºC \n", temperature);
Serial.printf("Humidity = %.2f % \n", humidity);
Serial.printf("Pressure = %.2f hPa \n", pressure);
Serial.println();

Наконец, отправляем события в браузер с новейшими показаниями датчика для обновления веб-страницы.

// Send Events to the Web Server with the Sensor Readings
events.send("ping",NULL,millis());
events.send(String(temperature).c_str(),"temperature",millis());
events.send(String(humidity).c_str(),"humidity",millis());
events.send(String(pressure).c_str(),"pressure",millis());

Демонстрация

После вставки ваших сетевых учётных данных в переменные ssid и password вы можете загрузить код на свою плату. Не забудьте проверить, правильно ли выбрана плата и COM-порт.

После загрузки кода откройте Serial Monitor на скорости 115200 бод и нажмите встроенную кнопку EN/RST. Должен быть выведен IP-адрес ESP.

ESP8266 NodeMCU веб-сервер Server Sent Events Serial Monitor демонстрация

Откройте браузер в вашей локальной сети и введите IP-адрес ESP8266. Вы должны получить доступ к веб-странице для мониторинга показаний датчика.

Веб-сервер BME280 с Server-Sent Events SSE ESP8266 NodeMCU

Показания обновляются автоматически каждые 30 секунд.

В то же время вы должны получать новые показания датчика в Serial Monitor, как показано на предыдущем снимке экрана. Кроме того, вы можете проверить, получает ли клиент события. В вашем браузере откройте консоль, нажав Ctrl + Shift + J.

Server-Sent Events консоль браузера ESP8266 NodeMCU веб-сервер демонстрация

Заключение

В этом руководстве вы узнали, как использовать Server-Sent Events с ESP8266. Server-Sent Events позволяют веб-странице (клиенту) получать обновления от сервера. Это может использоваться для автоматического отображения новых показаний датчика на странице веб-сервера, когда они доступны.

У нас есть похожее руководство, но с использованием датчика окружающей среды BME680. Вы можете ознакомиться с руководством по следующей ссылке:

Вместо Server-Sent Events вы также можете использовать протокол WebSocket для поддержания веб-страницы в актуальном состоянии. Ознакомьтесь со следующим руководством, чтобы узнать, как настроить WebSocket-сервер с ESP8266:

Узнайте больше об ESP8266 с помощью наших ресурсов: