ESP32 Веб-сервер: отображение показаний датчиков на индикаторах (Gauges)
Узнайте, как создать веб-сервер на ESP32 для отображения показаний датчиков на индикаторах (gauges). В качестве примера мы отобразим температуру и влажность с датчика BME280 на двух различных индикаторах: линейном и радиальном. Вы можете легко модифицировать проект для отображения любых других данных. Для создания индикаторов мы будем использовать JavaScript-библиотеку canvas-gauges.
У нас есть аналогичное руководство для платы ESP8266: Веб-сервер — отображение показаний датчиков на индикаторах
Обзор проекта
В этом проекте мы создадим веб-сервер на ESP32, который отображает показания температуры и влажности с датчика BME280. Мы создадим линейный индикатор, похожий на термометр, для отображения температуры, и радиальный индикатор для отображения влажности.
Server-Sent Events
Показания автоматически обновляются на веб-странице с помощью Server-Sent Events (SSE).
Чтобы узнать больше о SSE, вы можете прочитать:
Файлы, сохранённые в файловой системе
Для лучшей организации проекта и удобства понимания мы сохраним HTML, CSS и JavaScript файлы для построения веб-страницы в файловой системе платы (LittleFS).
Необходимые условия
Убедитесь, что вы выполнили все предварительные условия из этого раздела, прежде чем продолжить проект.
1. Установка платы ESP32 в Arduino IDE
Мы будем программировать ESP32 с помощью Arduino IDE. Поэтому у вас должно быть установлено дополнение ESP32. Следуйте следующему руководству, если вы ещё этого не сделали:
Если вы хотите использовать VS Code с расширением PlatformIO, следуйте следующему руководству, чтобы узнать, как программировать ESP32:
2. Плагин загрузки файловой системы
Для загрузки HTML, CSS и JavaScript файлов во флеш-память ESP32 (LittleFS) мы будем использовать плагин для Arduino IDE: LittleFS Filesystem uploader. Следуйте следующему руководству для установки плагина загрузки файловой системы:
Если вы используете VS Code с расширением PlatformIO, прочитайте следующее руководство, чтобы узнать, как загружать файлы в файловую систему:
3. Установка библиотек
Для создания этого проекта вам необходимо установить следующие библиотеки:
ESPAsyncWebServer by ESP32Async
AsyncTCP by ESP32Async
Вы можете установить первые три библиотеки с помощью Arduino Library Manager. Перейдите в Sketch > Include Library > Manage Libraries и найдите библиотеки по имени.
Необходимые компоненты
Для выполнения этого руководства вам понадобятся следующие компоненты:
Вы можете использовать любой другой датчик, который подходит для вашего проекта. Если у вас нет датчика, вы также можете экспериментировать со случайными значениями, чтобы узнать, как работает проект.
Схема подключения
Мы будем отправлять показания температуры и влажности с датчика BME280. Мы будем использовать связь I2C с модулем датчика BME280. Для этого подключите датчик к стандартным контактам ESP32 SCL (GPIO 22) и SDA (GPIO 21), как показано на следующей схеме.
Рекомендуемое чтение: ESP32 Pinout Reference: Which GPIO pins should you use?
Организация файлов
Для поддержания порядка в проекте и упрощения понимания мы создадим четыре файла для построения веб-сервера:
Скетч Arduino, который обрабатывает веб-сервер;
index.html: для определения содержимого веб-страницы;
style.css: для стилизации веб-страницы;
script.js: для программирования поведения веб-страницы — обработка ответов веб-сервера, событий, создание индикаторов и т.д.
Вы должны сохранить HTML, CSS и JavaScript файлы внутри папки с именем data внутри папки скетча Arduino, как показано на предыдущей схеме. Мы загрузим эти файлы в файловую систему ESP32 (LittleFS).
Вы можете скачать все файлы проекта:
HTML файл
Скопируйте следующий код в файл index.html.
<!DOCTYPE html>
<html>
<head>
<title>ESP IOT DASHBOARD</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/png" href="favicon.png">
<link rel="stylesheet" type="text/css" href="style.css">
<script src="http://cdn.rawgit.com/Mikhus/canvas-gauges/gh-pages/download/2.1.7/all/gauge.min.js"></script>
</head>
<body>
<div class="topnav">
<h1>ESP WEB SERVER GAUGES</h1>
</div>
<div class="content">
<div class="card-grid">
<div class="card">
<p class="card-title">Temperature</p>
<canvas id="gauge-temperature"></canvas>
</div>
<div class="card">
<p class="card-title">Humidity</p>
<canvas id="gauge-humidity"></canvas>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
HTML файл для этого проекта очень простой. Он включает JavaScript-библиотеку canvas-gauges в заголовок HTML файла:
<script src="https://cdn.rawgit.com/Mikhus/canvas-gauges/gh-pages/download/2.1.7/all/gauge.min.js"></script>
Здесь есть тег <canvas> с идентификатором gauge-temperature, где мы позже отрисуем индикатор температуры.
<canvas id="gauge-temperature"></canvas>
Также есть ещё один тег <canvas> с идентификатором gauge-humidity, где мы позже отрисуем индикатор влажности.
<canvas id="gauge-humidity"></canvas>
CSS файл
Скопируйте следующие стили в ваш файл style.css. Они стилизуют веб-страницу простыми цветами и стилями.
html {
font-family: Arial, Helvetica, sans-serif;
display: inline-block;
text-align: center;
}
h1 {
font-size: 1.8rem;
color: white;
}
p {
font-size: 1.4rem;
}
.topnav {
overflow: hidden;
background-color: #0A1128;
}
body {
margin: 0;
}
.content {
padding: 5%;
}
.card-grid {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-gap: 2rem;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.card {
background-color: white;
box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
}
.card-title {
font-size: 1.2rem;
font-weight: bold;
color: #034078
}
JavaScript файл (создание индикаторов)
Скопируйте следующий код в файл script.js.
// Get current sensor readings when the page loads
window.addEventListener('load', getReadings);
// Create Temperature Gauge
var gaugeTemp = new LinearGauge({
renderTo: 'gauge-temperature',
width: 120,
height: 400,
units: "Temperature C",
minValue: 0,
startAngle: 90,
ticksAngle: 180,
maxValue: 40,
colorValueBoxRect: "#049faa",
colorValueBoxRectEnd: "#049faa",
colorValueBoxBackground: "#f1fbfc",
valueDec: 2,
valueInt: 2,
majorTicks: [
"0",
"5",
"10",
"15",
"20",
"25",
"30",
"35",
"40"
],
minorTicks: 4,
strokeTicks: true,
highlights: [
{
"from": 30,
"to": 40,
"color": "rgba(200, 50, 50, .75)"
}
],
colorPlate: "#fff",
colorBarProgress: "#CC2936",
colorBarProgressEnd: "#049faa",
borderShadowWidth: 0,
borders: false,
needleType: "arrow",
needleWidth: 2,
needleCircleSize: 7,
needleCircleOuter: true,
needleCircleInner: false,
animationDuration: 1500,
animationRule: "linear",
barWidth: 10,
}).draw();
// Create Humidity Gauge
var gaugeHum = new RadialGauge({
renderTo: 'gauge-humidity',
width: 300,
height: 300,
units: "Humidity (%)",
minValue: 0,
maxValue: 100,
colorValueBoxRect: "#049faa",
colorValueBoxRectEnd: "#049faa",
colorValueBoxBackground: "#f1fbfc",
valueInt: 2,
majorTicks: [
"0",
"20",
"40",
"60",
"80",
"100"
],
minorTicks: 4,
strokeTicks: true,
highlights: [
{
"from": 80,
"to": 100,
"color": "#03C0C1"
}
],
colorPlate: "#fff",
borderShadowWidth: 0,
borders: false,
needleType: "line",
colorNeedle: "#007F80",
colorNeedleEnd: "#007F80",
needleWidth: 2,
needleCircleSize: 3,
colorNeedleCircleOuter: "#007F80",
needleCircleOuter: true,
needleCircleInner: false,
animationDuration: 1500,
animationRule: "linear"
}).draw();
// Function to get current readings on the webpage when it loads for the first time
function getReadings(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var myObj = JSON.parse(this.responseText);
console.log(myObj);
var temp = myObj.temperature;
var hum = myObj.humidity;
gaugeTemp.value = temp;
gaugeHum.value = hum;
}
};
xhr.open("GET", "/readings", true);
xhr.send();
}
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('new_readings', function(e) {
console.log("new_readings", e.data);
var myObj = JSON.parse(e.data);
console.log(myObj);
gaugeTemp.value = myObj.temperature;
gaugeHum.value = myObj.humidity;
}, false);
}
Вот краткое описание того, что делает этот код:
инициализация протокола источника событий;
добавление слушателя событий для события new_readings;
создание индикаторов;
получение последних показаний датчиков из события new_readings и отображение их на соответствующих индикаторах;
выполнение HTTP GET-запроса для получения текущих показаний датчиков при первом доступе к веб-странице.
Получение показаний
Когда вы впервые открываете веб-страницу, мы запрашиваем у сервера текущие показания датчиков. В противном случае нам пришлось бы ждать поступления новых показаний датчиков (через Server-Sent Events), что может занять некоторое время в зависимости от интервала, который вы установили на сервере.
Добавьте слушатель событий, который вызывает функцию getReadings при загрузке веб-страницы.
// Get current sensor readings when the page loads
window.addEventListener('load', getReadings);
Объект window представляет открытое окно в браузере. Метод addEventListener() настраивает функцию, которая будет вызываться при наступлении определённого события. В данном случае мы вызовем функцию getReadings при загрузке страницы („load“), чтобы получить текущие показания датчиков.
Теперь давайте посмотрим на функцию getReadings. Создайте новый объект XMLHttpRequest. Затем отправьте GET-запрос на сервер по URL /readings, используя методы open() и send().
function getReadings() {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/readings", true);
xhr.send();
}
Когда мы отправляем этот запрос, ESP отправит ответ с необходимой информацией. Поэтому нам нужно обработать то, что происходит, когда мы получаем ответ. Мы будем использовать свойство onreadystatechange, которое определяет функцию, выполняемую при изменении свойства readyState. Свойство readyState содержит статус XMLHttpRequest. Ответ на запрос готов, когда readyState равно 4, а статус равен 200.
readyState = 4 означает, что запрос завершён и ответ готов;
status = 200 означает «OK»
Таким образом, запрос должен выглядеть примерно так:
function getStates(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
… DO WHATEVER YOU WANT WITH THE RESPONSE …
}
};
xhr.open("GET", "/states", true);
xhr.send();
}
Ответ, отправленный ESP, представляет собой следующий текст в формате JSON (это просто произвольные значения).
{
"temperature" : "25.02",
"humidity" : "64.01",
}
Нам нужно преобразовать строку JSON в объект JSON с помощью метода parse(). Результат сохраняется в переменной myObj.
var myObj = JSON.parse(this.responseText);
Переменная myObj — это JSON-объект, содержащий показания температуры и влажности. Мы хотим обновить значения индикаторов соответствующими показаниями.
Обновление значения индикатора выполняется просто. Например, наш индикатор температуры называется gaugeTemp (как мы увидим позже), чтобы обновить значение, мы можем просто вызвать: gaugeTemp.value = NEW_VALUE. В нашем случае новое значение — это показание температуры, сохранённое в JSON-объекте myObj.
gaugeTemp.value = myObj.temperature;
Аналогично для влажности (наш индикатор влажности называется gaugeHum).
gaugeHum.value = myObj.humidity;
Вот полная функция getReadings().
function getReadings(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var myObj = JSON.parse(this.responseText);
console.log(myObj);
var temp = myObj.temperature;
var hum = myObj.humidity;
gaugeTemp.value = temp;
gaugeHum.value = hum;
}
};
xhr.open("GET", "/readings", true);
xhr.send();
}
Создание индикаторов
Библиотека canvas-charts позволяет создавать линейные и радиальные индикаторы для отображения показаний. Она предоставляет несколько примеров и очень проста в использовании. Мы рекомендуем ознакомиться с документацией и изучить все возможности индикаторов:
Индикатор температуры
Следующие строки создают индикатор для отображения температуры.
// Create Temperature Gauge
var gaugeTemp = new LinearGauge({
renderTo: 'gauge-temperature',
width: 120,
height: 400,
units: "Temperature C",
minValue: 0,
startAngle: 90,
ticksAngle: 180,
maxValue: 40,
colorValueBoxRect: "#049faa",
colorValueBoxRectEnd: "#049faa",
colorValueBoxBackground: "#f1fbfc",
valueDec: 2,
valueInt: 2,
majorTicks: [
"0",
"5",
"10",
"15",
"20",
"25",
"30",
"35",
"40"
],
minorTicks: 4,
strokeTicks: true,
highlights: [
{
"from": 30,
"to": 40,
"color": "rgba(200, 50, 50, .75)"
}
],
colorPlate: "#fff",
colorBarProgress: "#CC2936",
colorBarProgressEnd: "#049faa",
borderShadowWidth: 0,
borders: false,
needleType: "arrow",
needleWidth: 2,
needleCircleSize: 7,
needleCircleOuter: true,
needleCircleInner: false,
animationDuration: 1500,
animationRule: "linear",
barWidth: 10,
}).draw();
Для создания нового линейного индикатора используйте метод new LinearGauge() и передайте в качестве аргумента свойства индикатора.
var gaugeTemp = new LinearGauge({
В следующей строке определите, куда вы хотите поместить диаграмму (это должен быть элемент <canvas>). В нашем примере мы хотим разместить его в HTML-элементе <canvas> с идентификатором gauge-temperature — см. раздел HTML файла.
renderTo: 'gauge-temperature',
Затем мы определяем другие свойства для настройки нашего индикатора. Названия говорят сами за себя, но мы рекомендуем ознакомиться со всеми возможными конфигурациями и изменить индикатор в соответствии с вашими потребностями.
В конце вам нужно применить метод draw(), чтобы фактически отобразить индикатор на канве.
}).draw();
Обратите внимание, что если вам нужно изменить диапазон индикатора, необходимо изменить свойства minValue и maxValue:
minValue: 0,
maxValue: 40,
Вам также необходимо настроить значения majorTicks для значений, отображаемых на оси.
majorTicks: [
"0",
"5",
"10",
"15",
"20",
"25",
"30",
"35",
"40"
],
Индикатор влажности
Создание индикатора влажности аналогично, но мы используем функцию new RadialGauge() вместо неё, и он отрисовывается на <canvas> с идентификатором gauge-humidity. Обратите внимание, что мы применяем метод draw() к индикатору, чтобы он был нарисован на канве.
// Create Humidity Gauge
var gaugeHum = new RadialGauge({
renderTo: 'gauge-humidity',
width: 300,
height: 300,
units: "Humidity (%)",
minValue: 0,
maxValue: 100,
colorValueBoxRect: "#049faa",
colorValueBoxRectEnd: "#049faa",
colorValueBoxBackground: "#f1fbfc",
valueInt: 2,
majorTicks: [
"0",
"20",
"40",
"60",
"80",
"100"
],
minorTicks: 4,
strokeTicks: true,
highlights: [
{
"from": 80,
"to": 100,
"color": "#03C0C1"
}
],
colorPlate: "#fff",
borderShadowWidth: 0,
borders: false,
needleType: "line",
colorNeedle: "#007F80",
colorNeedleEnd: "#007F80",
needleWidth: 2,
needleCircleSize: 3,
colorNeedleCircleOuter: "#007F80",
needleCircleOuter: true,
needleCircleInner: false,
animationDuration: 1500,
animationRule: "linear"
}).draw();
Обработка событий
Обновление показаний на индикаторе при получении клиентом показаний в событии new_readings.
Создайте новый объект 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);
Затем добавьте слушатель событий для new_readings.
source.addEventListener('new_readings', function(e) {
Когда доступны новые показания, ESP32 отправляет событие (new_readings) клиенту. Следующие строки обрабатывают то, что происходит, когда браузер получает это событие.
source.addEventListener('new_readings', function(e) {
console.log("new_readings", e.data);
var myObj = JSON.parse(e.data);
console.log(myObj);
gaugeTemp.value = myObj.temperature;
gaugeHum.value = myObj.humidity;
}, false);
По сути, выводим новые показания в консоль браузера, преобразуем данные в JSON-объект и отображаем показания на соответствующих индикаторах.
Скетч Arduino
Скопируйте следующий код в Arduino IDE или в файл main.cpp, если вы используете PlatformIO.
Вы также можете скачать все файлы здесь.
/*********
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete instructions at https://RandomNerdTutorials.com/esp32-web-server-gauges/
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 <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "LittleFS.h"
#include <Arduino_JSON.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");
// Json Variable to Hold Sensor Readings
JSONVar readings;
// Timer variables
unsigned long lastTime = 0;
unsigned long timerDelay = 10000;
// Create a sensor object
Adafruit_BME280 bme; // BME280 connect to ESP32 I2C (GPIO 21 = SDA, GPIO 22 = SCL)
// Init BME280
void initBME(){
if (!bme.begin(0x76)) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
}
// Get Sensor Readings and return JSON object
String getSensorReadings(){
readings["temperature"] = String(bme.readTemperature());
readings["humidity"] = String(bme.readHumidity());
String jsonString = JSON.stringify(readings);
return jsonString;
}
// Initialize LittleFS
void initLittleFS() {
if (!LittleFS.begin()) {
Serial.println("An error has occurred while mounting LittleFS");
}
Serial.println("LittleFS mounted successfully");
}
// 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());
}
void setup() {
// Serial port for debugging purposes
Serial.begin(115200);
initBME();
initWiFi();
initLittleFS();
// Web Server Root URL
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(LittleFS, "/index.html", "text/html");
});
server.serveStatic("/", LittleFS, "/");
// Request for the latest sensor readings
server.on("/readings", HTTP_GET, [](AsyncWebServerRequest *request){
String json = getSensorReadings();
request->send(200, "application/json", json);
json = String();
});
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);
// Start server
server.begin();
}
void loop() {
if ((millis() - lastTime) > timerDelay) {
// Send Events to the client with the Sensor Readings Every 10 seconds
events.send("ping",NULL,millis());
events.send(getSensorReadings().c_str(),"new_readings" ,millis());
lastTime = millis();
}
}
Как работает код
Давайте рассмотрим код и посмотрим, как он работает для отправки показаний клиенту с помощью server-sent events.
Подключение библиотек
Библиотеки Adafruit_Sensor и Adafruit_BME280 необходимы для работы с датчиком BME280.
#include <Adafruit_BME280.h>
#include <Adafruit_Sensor.h>
Библиотеки WiFi, ESPAsyncWebServer и AsyncTCP используются для создания веб-сервера.
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
Мы будем использовать LittleFS для сохранения файлов, необходимых для построения веб-сервера.
#include "LittleFS.h"
Вам также необходимо подключить библиотеку Arduino_JSON, чтобы упростить работу с JSON-строками.
#include <Arduino_JSON.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);
Следующая строка создаёт новый источник событий на /events.
AsyncEventSource events("/events");
Объявление переменных
Переменная readings — это JSON-переменная для хранения показаний датчиков в формате JSON.
JSONVar readings;
Переменные lastTime и timerDelay будут использоваться для обновления показаний датчиков каждые X секунд. В качестве примера мы будем получать новые показания датчиков каждые 30 секунд (30000 миллисекунд). Вы можете изменить время задержки в переменной timerDelay.
unsigned long lastTime = 0;
unsigned long timerDelay = 30000;
Создайте объект Adafruit_BME280 с именем bme на стандартных контактах I2C ESP.
Adafruit_BME280 bme;
Инициализация датчика BME280
Следующая функция может быть вызвана для инициализации датчика BME280.
// Init BME280
void initBME(){
if (!bme.begin(0x76)) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
}
Получение показаний BME280
Для получения температуры и влажности с датчика BME280 используйте следующие методы объекта bme:
bme.readTemperature()
bme.readHumidity()
Функция getSensorReadings() получает показания датчиков и сохраняет их в JSON-массиве readings.
// Get Sensor Readings and return JSON object
String getSensorReadings(){
readings["temperature"] = String(bme.readTemperature());
readings["humidity"] = String(bme.readHumidity());
String jsonString = JSON.stringify(readings);
return jsonString;
}
Массив readings затем преобразуется в строковую переменную JSON с помощью метода stringify() и сохраняется в переменной jsonString.
Функция возвращает переменную jsonString с текущими показаниями датчиков. JSON-строка имеет следующий формат (значения приведены для примера).
{
"temperature" : "25",
"humidity" : "50"
}
setup()
В setup() инициализируйте Serial Monitor, Wi-Fi, файловую систему и датчик BME280.
void setup() {
// Serial port for debugging purposes
Serial.begin(115200);
initBME();
initWiFi();
initLittleFS ();
Обработка запросов
Когда вы обращаетесь к IP-адресу ESP32 по корневому URL /, отправляется текст, который хранится в файле index.html, для построения веб-страницы.
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(LittleFS, "/index.html", "text/html");
});
Обслуживание других статических файлов, запрошенных клиентом (style.css и script.js).
server.serveStatic("/", LittleFS, "/");
Отправка JSON-строки с текущими показаниями датчиков при получении запроса на URL /readings.
// Request for the latest sensor readings
server.on("/readings", HTTP_GET, [](AsyncWebServerRequest *request){
String json = getSensorReadings();
request->send(200, "application/json", json);
json = String();
});
Переменная json содержит результат функции getSensorReadings(). Для отправки JSON-строки в качестве ответа метод send() принимает первым аргументом код ответа (200), вторым — тип содержимого («application/json») и, наконец, само содержимое (переменная json).
Источник событий сервера
Настройте источник событий на сервере.
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() отправляйте события в браузер с новейшими показаниями датчиков для обновления веб-страницы каждые 30 секунд.
events.send("ping",NULL,millis());
events.send(getSensorReadings().c_str(),"new_readings" ,millis());
Используйте метод send() объекта events и передайте в качестве аргумента содержимое, которое вы хотите отправить, и имя события. В данном случае мы хотим отправить JSON-строку, возвращаемую функцией getSensorReadings(). Метод send() принимает переменную типа char, поэтому нам нужно использовать метод c_str() для преобразования переменной. Имя события — new_readings.
Обычно мы также отправляем ping-сообщение каждые X секунд. Эта строка не обязательна. Она используется для проверки на стороне клиента, что сервер активен.
events.send("ping",NULL,millis());
Загрузка кода и файлов
После ввода ваших сетевых учётных данных сохраните код. Перейдите в Sketch > Show Sketch Folder и создайте папку с именем data.
Внутри этой папки вы должны сохранить HTML, CSS и JavaScript файлы.
Затем загрузите код на плату ESP32. Убедитесь, что выбрана правильная плата и COM-порт. Также убедитесь, что вы добавили ваши сетевые учётные данные.
После загрузки кода вам нужно загрузить файлы в файловую систему.
Нажмите [Ctrl] + [Shift] + [P] в Windows или [Cmd] + [Shift] + [P] в MacOS, чтобы открыть палитру команд. Найдите команду Upload LittleFS to Pico/ESP8266/ESP32 и нажмите на неё.
Если у вас нет этой опции, это потому, что вы не установили плагин загрузки файловой системы. Ознакомьтесь с этим руководством.
Важно: убедитесь, что Serial Monitor закрыт перед загрузкой в файловую систему. В противном случае загрузка не удастся.
Когда всё успешно загружено, откройте Serial Monitor на скорости 115200 бод. Нажмите кнопку EN/RST на ESP32, и он должен напечатать IP-адрес ESP32.
Демонстрация
Откройте браузер и введите IP-адрес ESP32. Вы должны получить доступ к веб-странице, которая показывает индикаторы с последними показаниями датчиков.
Вы также можете проверить индикаторы с помощью смартфона (веб-страница адаптивна для мобильных устройств).
Заключение
В этом руководстве вы узнали, как создать веб-сервер для отображения показаний датчиков на линейных и радиальных индикаторах. В качестве примера мы отобразили температуру и влажность с датчика BME280. Вы можете использовать эти индикаторы для отображения любых других значений, которые могут быть полезны для вашего проекта.
Вам также может быть интересно:
ESP32/ESP8266 Plot Sensor Readings in Real Time Charts – Web Server
ESP32 Web Server using Server-Sent Events (Update Sensor Readings Automatically)
Узнайте больше об ESP32 с нашими ресурсами:
Примечание
Данная статья является переводом материала с сайта Random Nerd Tutorials. Все права на оригинальный контент принадлежат авторам Rui Santos и Sara Santos.