ESP8266 NodeMCU Веб-сервер: отображение показаний датчиков на шкалах (Gauges)

Узнайте, как создать веб-сервер на ESP8266 NodeMCU для отображения показаний датчиков на шкалах (gauges). В качестве примера мы отобразим температуру и влажность с датчика BME280 на двух различных шкалах: линейной и радиальной. Вы можете легко модифицировать проект для отображения любых других данных. Для создания шкал мы будем использовать JavaScript-библиотеку canvas-gauges.

ESP8266 NodeMCU Веб-сервер: отображение показаний датчиков на шкалах

У нас есть аналогичное руководство для платы ESP32: Веб-сервер – Отображение показаний датчиков на шкалах

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

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

Обзор веб-сервера ESP8266 NodeMCU с шкалами Gauges

Server-Sent Events

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

Обзор Server-Sent Events веб-сервера ESP8266 NodeMCU с шкалами Gauges

Чтобы узнать больше о SSE, вы можете прочитать:

Файлы, сохранённые в файловой системе

Для лучшей организации проекта и простоты понимания мы сохраним файлы HTML, CSS и JavaScript для построения веб-страницы в файловой системе платы (LittleFS).

Предварительные требования

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

1. Установка платы ESP8266 в Arduino IDE

Мы будем программировать ESP8266 с помощью Arduino IDE. Поэтому у вас должно быть установлено дополнение ESP8266. Следуйте следующему руководству, если вы ещё этого не сделали:

Если вы хотите использовать VS Code с расширением PlatformIO, следуйте следующему руководству, чтобы узнать, как программировать ESP8266:

2. Плагин загрузки файловой системы

Для загрузки файлов HTML, CSS и JavaScript в файловую систему ESP8266 (LittleFS) мы будем использовать плагин для Arduino IDE: LittleFS Filesystem Uploader. Следуйте следующему руководству для установки плагина загрузки файловой системы:

Если вы используете VS Code с расширением PlatformIO, прочитайте следующее руководство, чтобы узнать, как загрузить файлы в файловую систему:

3. Установка библиотек

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

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

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

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

Вы можете использовать любой другой датчик или отображать любые другие значения, которые полезны для вашего проекта. Если у вас нет датчика, вы также можете экспериментировать со случайными значениями, чтобы узнать, как работает проект.

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

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

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

Схема подключения ESP8266 NodeMCU и датчика BME280 — температура, влажность, давление

Рекомендуемое чтение: Справочник по распиновке ESP8266: какие выводы GPIO следует использовать?

Организация файлов

Для поддержания организованности проекта и облегчения его понимания мы создадим четыре файла для построения веб-сервера:

  • Скетч Arduino, который обрабатывает веб-сервер;

  • index.html: для определения содержимого веб-страницы;

  • style.css: для стилизации веб-страницы;

  • script.js: для программирования поведения веб-страницы — обработки ответов веб-сервера, событий, создания шкал и т.д.

Организация файлов проекта ESP8266 — Arduino скетч, index.html, style.css, script.js

Вы должны сохранить файлы HTML, CSS и JavaScript внутри папки с именем data внутри папки скетча Arduino, как показано на предыдущей диаграмме. Мы загрузим эти файлы в файловую систему ESP8266 (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" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
    <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);
}

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

Вот краткое описание того, что делает этот код:

  • инициализация протокола event source;

  • добавление слушателя событий для события 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, а status равен 200.

  • readyState = 4 означает, что запрос завершён и ответ готов;

  • status = 200 означает «OK»

Таким образом, запрос должен выглядеть примерно так:

function getStates(){
  var xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      ... ДЕЛАЙТЕ ВСЁ, ЧТО ХОТИТЕ, С ОТВЕТОМ ...
    }
  };
  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');

После того как вы создали экземпляр 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);

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

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

Когда новые показания доступны, ESP8266 отправляет событие (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
  Complete instructions at https://RandomNerdTutorials.com/esp8266-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 <ESP8266WiFi.h>
#include <ESPAsyncTCP.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 = 30000;

// 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 initFS() {
  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.begin(115200);
  initBME();
  initWiFi();
  initFS();

  // 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 30 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>

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

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

Мы будем использовать LittleFS для сохранения файлов для построения веб-сервера.

#include "LittleFS.h"

Вам также необходимо подключить библиотеку Arduino_JSON, чтобы упростить работу с JSON-строками.

#include <Arduino_JSON.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");

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

Переменная 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();
  initFS();

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

Когда вы обращаетесь к IP-адресу ESP8266 по корневому 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.

Arduino IDE — открыть папку скетча для создания папки data

Внутри этой папки вы должны сохранить файлы HTML, CSS и JavaScript.

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

Кнопка загрузки Arduino IDE

После загрузки кода вам необходимо загрузить файлы в файловую систему.

Нажмите [ Ctrl] + [ Shift] + [ P] на Windows или [ Cmd] + [ Shift] + [ P] на MacOS, чтобы открыть палитру команд. Найдите команду Upload LittleFS to Pico/ESP8266/ESP32 и нажмите на неё.

Если у вас нет этой опции, значит вы не установили плагин загрузки файловой системы. Ознакомьтесь с этим руководством.

ESP8266 — загрузка образа файловой системы

Когда всё будет успешно загружено, откройте Serial Monitor с скоростью передачи данных 115200. Нажмите кнопку EN/RST на ESP8266, и он должен вывести IP-адрес ESP8266.

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

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

Демонстрация веб-сервера с шкалами Gauges на ESP32/ESP8266

Вы также можете проверить свои шкалы с помощью смартфона (веб-страница адаптивна для мобильных устройств).

Демонстрация веб-сервера с шкалами Gauges на смартфоне

Заключение

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

Вам также может быть интересно прочитать:

Узнайте больше о ESP8266 с нашими ресурсами:

Спасибо за чтение.