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

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

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

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

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

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

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

ESP32 ESP8266 Server-Sent Events как это работает

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

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

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

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

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

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

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

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

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

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

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

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

ESP32 BME280 веб-сервер с Server Sent Events (SSE) как это работает
  1. Клиент инициирует SSE-соединение, и сервер использует протокол event source по URL /events для отправки обновлений клиенту;

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

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

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

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

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

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

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

Для создания веб-сервера мы будем использовать библиотеку ESPAsyncWebServer (от ESP32Async). Эта библиотека требует AsyncTCP (от ESP32Async).

Вы можете установить эти библиотеки в менеджере библиотек Arduino. Откройте менеджер библиотек, нажав на значок библиотеки на левой боковой панели.

Найдите ESPAsyncWebServer и установите ESPAsyncWebServer от ESP32Async.

Установка ESPAsyncWebServer ESP32 Arduino IDE

Затем установите библиотеку AsyncTCP. Найдите AsyncTCP и установите AsyncTCP от ESP32Async.

Установка AsyncTCP ESP32 Arduino IDE

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

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

Вы также можете установить эти библиотеки через менеджер библиотек Arduino IDE.

Чтобы узнать больше о датчике BME280, прочитайте наше руководство: ESP32 with BME280 Sensor using Arduino IDE (Pressure, Temperature, Humidity).

Сборка схемы

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

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

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

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

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

ESP32 подключение к BME280 схема

Рекомендуемое чтение: ESP32 Pinout Reference: Which GPIO pins should you use?

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

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

/*********
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-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 <WiFi.h>
#include <AsyncTCP.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").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>)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 Client 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();
  }
}

Исходный код на GitHub

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

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"

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

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

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

AsyncWebServer и AsyncEventSource

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

AsyncWebServer server(80);

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

AsyncEventSource events("/events");

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

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

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

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

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

Функция getSensorReadings() получает показания температуры, влажности и давления от датчика 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

Следующая функция устанавливает ESP32 в режим 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)

Функция processor() заменяет все заполнители (placeholders) в 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, которые затем загружаются в файловую систему ESP32 и на них ссылаются в коде.

Вот содержимое переменной 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>

Есть заголовок первого уровня с содержимым «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").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>

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

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

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

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

После создания экземпляра event source вы можете начать прослушивание сообщений от сервера с помощью 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) {

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

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

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

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

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);

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

setup()

В функции setup() инициализируйте монитор последовательного порта, Wi-Fi и датчик BME280.

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

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

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

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();

loop()

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

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());

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

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

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

ESP32 веб-сервер с Server Sent Events монитор последовательного порта демонстрация

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

BME280 веб-сервер с Server-Sent Events ESP32

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

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

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

Заключение

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

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

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

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


Источник: ESP32 Web Server using Server-Sent Events (Update Sensor Readings Automatically)