Создаем простую метеостанцию на ESP8266 с BME280

Добавлено 21 сентября 2020 в 07:31

Не позволяйте погодным приложениям для смартфонов или коммерческим метеостанциям (которые снабжают вас данными со станций, расположенных за много километров) разрушить ваши планы на открытом воздухе. С помощью этого IoT-проекта вы можете сами стать метеорологом!

Создаем простую метеостанцию на ESP8266 с BME280
Создаем простую метеостанцию на ESP8266 с BME280

В данном проекте ESP8266 NodeMCU используется в качестве устройства управления, которое легко подключается к существующей сети Wi-Fi и создает веб-сервер. Когда любое подключенное устройство обращается к этому веб-серверу, ESP8266 считывает температуру, влажность, атмосферное давление и высоту из BME280 и отправляет их в веб-браузер этого устройства, выводя их в приятном интерфейсе. Интересно? Давайте начнем!

BME280 – датчик температуры, влажности и давления

Сначала давайте кратко рассмотрим модуль BME280.

BME280 – это цифровой датчик температуры, влажности и давления нового поколения, производимый Bosch. Это преемник таких датчиков, как BMP180, BMP085 или BMP183.

Рисунок 1 Чип BME280 на модуле
Рисунок 1 – Чип BME280 на модуле

Рабочее напряжение модуля BME280 составляет от 3,3 В до 5 В – идеально подходит для взаимодействия с микроконтроллерами 3,3 В, такими как ESP8266. Подробности о модуле можно прочитать в этой статье.

Рисунок 2 Диапазоны измерений датчика BME280
Рисунок 2 – Диапазоны измерений датчика BME280

Модуль имеет простой двухпроводной интерфейс I2C для связи. По умолчанию I2C-адрес модуля BME280 – 0x76 и может быть легко изменен на 0x77 с помощью следующей процедуры.

Подключение датчика BME280 к плате ESP8266 NodeMCU

Подключение довольно простое. Начните с подключения вывода VIN к выводу 3.3V на ESP8266 NodeMCU и соедините выводы GND на данных платах.

Затем подключите вывод SCL к тактовому выводу I2C D1 на вашей плате ESP8266 и подключите вывод SDA к выводу данных I2C D2 на ESP8266.

На следующей схеме показано, как всё подключить.

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

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

Существует дополнение для Arduino IDE, которое позволяет вам программировать ESP8266 NodeMCU с помощью Arduino IDE. Чтобы подготовить вашу Arduino IDE к работе с ESP8266, если вы еще этого не сделали, следуйте инструкциям из следующей статьи:

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

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

Чтобы установить библиотеку, перейдите в раздел Скетч → Подключить библиотеку → Управлять библиотеками…. Подождите, пока менеджер библиотек загрузит индекс библиотек и обновит список установленных библиотек.

Отфильтруйте результаты поиска, набрав «bme280». Там должно быть пара записей. Ищите библиотеку Adafruit BME280 by Adafruit. Нажмите на эту запись, а затем выберите «Установка».

Рисунок 4 Установка библиотеки BME280 в Arduino IDE
Рисунок 4 – Установка библиотеки BME280 в Arduino IDE

Библиотека датчиков BME280 использует Adafruit Sensor support backend. Поэтому найдите в менеджере библиотек Adafruit Unified Sensor и установите ее тоже (возможно, вам придется немного прокрутить)

Рисунок 5 Установка  библиотеки Adafruit Unified Sensor
Рисунок 5 – Установка библиотеки Adafruit Unified Sensor

Отображение температуры, влажности, давления и высоты на веб-сервере ESP8266

Теперь мы собираемся настроить ESP8266 в режим станции (STA) и создать веб-сервер для выдачи веб-страниц любому подключенному клиенту в существующей сети.

Если вы хотите узнать о создании веб-сервера с ESP8266 в режимах AP/STA, ознакомьтесь с этим руководством:

Прежде чем вы отправитесь загружать скетч, вам нужно внести одно изменение, чтобы он у вас заработал. Чтобы ESP8266 мог установить соединение с существующей сетью, вам необходимо изменить следующие две переменные в соответствии с учетными данными вашей сети.

const char* ssid = "YourNetworkName";   // SSID
const char* password = "YourPassword";  // пароль

Как только вы это сделаете, попробуйте скетч в работе.

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

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME280 bme;

float temperature, humidity, pressure, altitude;

/* Введите SSID и пароль от вашей сети */
const char* ssid = "YourNetworkName";   // SSID
const char* password = "YourPassword";  // пароль

ESP8266WebServer server(80);              
 
void setup() 
{
  Serial.begin(115200);
  delay(100);
  
  bme.begin(0x76);   

  Serial.println("Connecting to ");
  Serial.println(ssid);

  // подключиться к вашей локальной wi-fi сети
  WiFi.begin(ssid, password);

  // проверить, выполнено ли подключение wi-fi сети
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected..!");
  Serial.print("Got IP: ");  Serial.println(WiFi.localIP());

  server.on("/", handle_OnConnect);
  server.onNotFound(handle_NotFound);

  server.begin();
  Serial.println("HTTP server started");

}
void loop() 
{
  server.handleClient();
}

void handle_OnConnect() 
{
  temperature = bme.readTemperature();
  humidity = bme.readHumidity();
  pressure = bme.readPressure() / 100.0F;
  altitude = bme.readAltitude(SEALEVELPRESSURE_HPA);
  server.send(200, "text/html", SendHTML(temperature,humidity,pressure,altitude)); 
}

void handle_NotFound()
{
  server.send(404, "text/plain", "Not found");
}

String SendHTML(float temperature,float humidity,float pressure,float altitude)
{
  String ptr = "<!DOCTYPE html> <html>\n";
  ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
  ptr +="<title>ESP8266 Weather Station</title>\n";
  ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
  ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n";
  ptr +="p {font-size: 24px;color: #444444;margin-bottom: 10px;}\n";
  ptr +="</style>\n";
  ptr +="</head>\n";
  ptr +="<body>\n";
  ptr +="<div id=\"webpage\">\n";
  ptr +="<h1>ESP8266 Weather Station</h1>\n";
  ptr +="<p>Temperature: ";
  ptr +=temperature;
  ptr +="&deg;C</p>";
  ptr +="<p>Humidity: ";
  ptr +=humidity;
  ptr +="%</p>";
  ptr +="<p>Pressure: ";
  ptr +=pressure;
  ptr +="hPa</p>";
  ptr +="<p>Altitude: ";
  ptr +=altitude;
  ptr +="m</p>";
  ptr +="</div>\n";
  ptr +="</body>\n";
  ptr +="</html>\n";
  return ptr;
}

Доступ к веб-серверу

После загрузки скетча откройте монитор последовательного порта со скоростью 115200 бит/с и нажмите кнопку RST на NodeMCU. Если всё в порядке, он выведет динамический IP адрес, полученный от вашего маршрутизатора, и покажет сообщение о том, что HTTP сервер запущен.

Рисунок 6 Монитор последовательного порта сервер запущен
Рисунок 6 – Монитор последовательного порта – сервер запущен

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

Рисунок 7 Показания BME280 на веб-сервере ESP8266
Рисунок 7 – Показания BME280 на веб-сервере ESP8266

Подробное объяснение кода

Скетч начинается с включения следующих библиотек:

  • библиотека ESP8266WebServer.h предоставляет специальные методы ESP8266 NodeMCU для работы с Wi-Fi, которые мы вызываем для подключения к сети. Она также имеет несколько методов, которые помогут нам настроить сервер и обрабатывать входящие HTTP-запросы, не беспокоясь о деталях низкоуровневой реализации;
  • библиотека Wire.h взаимодействует с любым устройством I2C, а не только с BME280;
  • библиотеки Adafruit_BME280.h и Adafruit_Sensor.h – это аппаратно-зависимые библиотеки, которые обрабатывают низкоуровневые функции.
#include <ESP8266WebServer.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

Затем мы создаем объект датчика и переменные для хранения температуры, влажности, давления и высоты.

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME280 bme;

float temperature, humidity, pressure, altitude;

Поскольку мы конфигурируем ESP8266 в режим станции (STA), он присоединится к существующей сети Wi-Fi. Следовательно, нам необходимо предоставить ему SSID и пароль вашей сети. Затем мы запускаем веб-сервер на порту 80.

/* Введите SSID и пароль от вашей сети */
const char* ssid = "YourNetworkName";   // SSID
const char* password = "YourPassword";  // пароль

ESP8266WebServer server(80);

Внутренности функции setup()

Внутри функции setup() мы настраиваем наш HTTP-сервер перед его запуском.

Прежде всего, мы инициализируем последовательную связь с ПК и инициализируем объект BME с помощью функции begin(). Она инициализирует интерфейс I2C с заданным адресом I2C (0x76) и проверяет правильность идентификатора чипа. Затем она программно перезапускает микросхему и ждет окончания калибровки датчика после запуска.

Serial.begin(115200);
delay(100);

bme.begin(0x76);

Теперь нам нужно подключиться к сети Wi-Fi с помощью функции WiFi.begin(). Данная функция принимает в качестве параметров SSID (имя сети) и пароль.

Serial.println("Connecting to ");
Serial.println(ssid);

// подключиться к вашей локальной wi-fi сети
WiFi.begin(ssid, password);

Пока ESP8266 пытается подключиться к сети, мы проверяем статус подключения с помощью функции WiFi.status().

// проверить, выполнено ли подключение wi-fi сети
while (WiFi.status() != WL_CONNECTED) 
{
  delay(1000);
  Serial.print(".");
}

После подключения ESP8266 к сети скетч выводит IP-адрес, назначенный ESP8266, напечатав значение результата метода WiFi.localIP() в монитор последовательного порта.

Serial.println("");
Serial.println("WiFi connected..!");
Serial.print("Got IP: ");  Serial.println(WiFi.localIP());

Чтобы обрабатывать входящие HTTP запросы, нам нужно указать, какой код выполнять при введении определенного URL. Для этого мы используем метод on(). Этот метод принимает два параметра. Первый – это URL путь, а второй – имя функции, которую мы хотим выполнить при входе на этот URL.

Приведенный ниже код указывает, что когда сервер получает HTTP запрос по корневому пути (/), он запускает функцию handle_OnConnect(). Обратите внимание, что указанный URL адрес представляет собой относительный путь.

server.on("/", handle_OnConnect);

Мы не указали, что должен делать сервер, если клиент запрашивает какой-либо URL, отличающийся от указанных в server.on(). Он должен выдать ответ с HTTP статусом 404 (Not Found, «страница не найдена») и сообщением для пользователя. Помещаем эти действия в отдельную функцию и используем метод server.onNotFound(), чтобы указать серверу, что он должен выполнить, когда получит запрос на URI, который не был задан с помощью server.on().

server.onNotFound(handle_NotFound);

Теперь, чтобы запустить сервер, вызываем метод begin() для объекта server.

server.begin();
Serial.println("HTTP server started");

Внутренности функции loop()

Чтобы обработать реальные входящие HTTP-запросы, нам нужно вызвать метод handleClient() объекта server.

server.handleClient();

Далее нам нужно создать функцию, которую мы прикрепили к корневому (/) URL с помощью server.on(). Помните?

В начале этой функции мы получаем показания температуры от каждого датчика. Чтобы ответить на HTTP-запрос, используем метод send(). Хотя этот метод можно вызывать с другим набором аргументов, его самая простая форма состоит из HTTP кода ответа, типа контента и самого контента.

В нашем случае мы отправляем код 200 (один из кодов состояния HTTP), который соответствует ответу OK. Затем мы указываем тип контента как "text/html", и, наконец, мы вызываем пользовательскую функцию SendHTML(), которая создает динамическую HTML-страницу, содержащую показания температуры, влажности, давления и высоты.

void handle_OnConnect() 
{
  temperature = bme.readTemperature();
  humidity = bme.readHumidity();
  pressure = bme.readPressure() / 100.0F;
  altitude = bme.readAltitude(SEALEVELPRESSURE_HPA);
  server.send(200, "text/html", SendHTML(temperature,humidity,pressure,altitude)); 
}

Точно так же нам нужно создать функцию для обработки страницы ошибки 404.

void handle_NotFound()
{
  server.send(404, "text/plain", "Not found");
}

Отображение веб-страницы HTML

Функция SendHTML() отвечает за создание веб-страницы всякий раз, когда веб-сервер ESP8266 получает запрос от веб-клиента. Она просто объединяет HTML-код в большую строку и возвращает ее в функцию server.send(), которую мы обсуждали ранее. Данная функция в качестве параметров для динамического генерирования HTML контента принимает показания температуры, влажности, давления и высоты.

Первый текст, который вы всегда должны отправлять, – это объявление <!DOCTYPE>, которое указывает, что мы отправляем HTML-код.

String SendHTML(float temperature,float humidity,float pressure,float altitude)
{
  String ptr = "<!DOCTYPE html> <html>\n";

Затем <meta> элемент viewport делает веб-страницу адаптивной в любом веб-браузере. Далее тег title устанавливает заголовок страницы.

ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
ptr +="<title>ESP8266 Weather Station</title>\n";

Стилизация веб-страницы

Далее идет немного CSS кода для стилизации внешнего вида веб-страницы. Мы выбираем шрифт Helvetica, определяем контент, который будет отображаться в виде inline-block и выровнен по центру.

ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";

Следующий код устанавливает цвет, шрифт и поля вокруг элементов body, H1 и p.

ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n";
ptr +="p {font-size: 24px;color: #444444;margin-bottom: 10px;}\n";
ptr +="</style>\n";
ptr +="</head>\n";
ptr +="<body>\n";

Установка заголовка веб-страницы

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

ptr +="<div id=\"webpage\">\n";
ptr +="<h1>ESP8266 Weather Station</h1>\n";

Отображение показаний на веб-странице

Чтобы динамически отображать показания температуры, влажности, давления и высоты, мы помещаем эти значения в теги параграфов. Для отображения символа градуса мы используем сущность HTML &deg;.

  ptr +="<p>Temperature: ";
  ptr +=temperature;
  ptr +="&deg;C</p>";
  ptr +="<p>Humidity: ";
  ptr +=humidity;
  ptr +="%</p>";
  ptr +="<p>Pressure: ";
  ptr +=pressure;
  ptr +="hPa</p>";
  ptr +="<p>Altitude: ";
  ptr +=altitude;
  ptr +="m</p>";
  ptr +="</div>\n";
  ptr +="</body>\n";
  ptr +="</html>\n";
  return ptr;
}

Стилизация веб-страницы, чтобы она выглядела более профессионально

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

Рисунок 8 Показания BME280 на странице веб-сервера ESP8266 (более профессиональный вид)
Рисунок 8 – Показания BME280 на странице веб-сервера ESP8266 (более профессиональный вид)

Довольно красиво, правда? Давайте без лишних слов, применим стиль к нашей предыдущей HTML-странице. Для начала скопируйте и вставьте приведенный ниже код, чтобы заменить в приведенном выше скетче функцию SendHTML().

String SendHTML(float temperature,float humidity,float pressure,float altitude)
{
  String ptr = "<!DOCTYPE html>";
  ptr +="<html>";
  ptr +="<head>";
  ptr +="<title>ESP8266 Weather Station</title>";
  ptr +="<meta name='viewport' content='width=device-width, initial-scale=1.0'>";
  ptr +="<link href='https://fonts.googleapis.com/css?family=Open+Sans:300,400,600' rel='stylesheet'>";
  ptr +="<style>";
  ptr +="html { font-family: 'Open Sans', sans-serif; display: block; margin: 0px auto; text-align: center;color: #444444;}";
  ptr +="body{margin: 0px;} ";
  ptr +="h1 {margin: 50px auto 30px;} ";
  ptr +=".side-by-side{display: table-cell;vertical-align: middle;position: relative;}";
  ptr +=".text{font-weight: 600;font-size: 19px;width: 200px;}";
  ptr +=".reading{font-weight: 300;font-size: 50px;padding-right: 25px;}";
  ptr +=".temperature .reading{color: #F29C1F;}";
  ptr +=".humidity .reading{color: #3B97D3;}";
  ptr +=".pressure .reading{color: #26B99A;}";
  ptr +=".altitude .reading{color: #955BA5;}";
  ptr +=".superscript{font-size: 17px;font-weight: 600;position: absolute;top: 10px;}";
  ptr +=".data{padding: 10px;}";
  ptr +=".container{display: table;margin: 0 auto;}";
  ptr +=".icon{width:65px}";
  ptr +="</style>";
  ptr +="</head>";
  ptr +="<body>";
  ptr +="<h1>ESP8266 Weather Station</h1>";
  ptr +="<div class='container'>";
  ptr +="<div class='data temperature'>";
  ptr +="<div class='side-by-side icon'>";
  ptr +="<svg enable-background='new 0 0 19.438 54.003'height=54.003px id=Layer_1 version=1.1 viewBox='0 0 19.438 54.003'width=19.438px x=0px xml:space=preserve xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink y=0px><g><path d='M11.976,8.82v-2h4.084V6.063C16.06,2.715,13.345,0,9.996,0H9.313C5.965,0,3.252,2.715,3.252,6.063v30.982";
  ptr +="C1.261,38.825,0,41.403,0,44.286c0,5.367,4.351,9.718,9.719,9.718c5.368,0,9.719-4.351,9.719-9.718";
  ptr +="c0-2.943-1.312-5.574-3.378-7.355V18.436h-3.914v-2h3.914v-2.808h-4.084v-2h4.084V8.82H11.976z M15.302,44.833";
  ptr +="c0,3.083-2.5,5.583-5.583,5.583s-5.583-2.5-5.583-5.583c0-2.279,1.368-4.236,3.326-5.104V24.257C7.462,23.01,8.472,22,9.719,22";
  ptr +="s2.257,1.01,2.257,2.257V39.73C13.934,40.597,15.302,42.554,15.302,44.833z'fill=#F29C21 /></g></svg>";
  ptr +="</div>";
  ptr +="<div class='side-by-side text'>Temperature</div>";
  ptr +="<div class='side-by-side reading'>";
  ptr +=(int)temperature;
  ptr +="<span class='superscript'>&deg;C</span></div>";
  ptr +="</div>";
  ptr +="<div class='data humidity'>";
  ptr +="<div class='side-by-side icon'>";
  ptr +="<svg enable-background='new 0 0 29.235 40.64'height=40.64px id=Layer_1 version=1.1 viewBox='0 0 29.235 40.64'width=29.235px x=0px xml:space=preserve xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink y=0px><path d='M14.618,0C14.618,0,0,17.95,0,26.022C0,34.096,6.544,40.64,14.618,40.64s14.617-6.544,14.617-14.617";
  ptr +="C29.235,17.95,14.618,0,14.618,0z M13.667,37.135c-5.604,0-10.162-4.56-10.162-10.162c0-0.787,0.638-1.426,1.426-1.426";
  ptr +="c0.787,0,1.425,0.639,1.425,1.426c0,4.031,3.28,7.312,7.311,7.312c0.787,0,1.425,0.638,1.425,1.425";
  ptr +="C15.093,36.497,14.455,37.135,13.667,37.135z'fill=#3C97D3 /></svg>";
  ptr +="</div>";
  ptr +="<div class='side-by-side text'>Humidity</div>";
  ptr +="<div class='side-by-side reading'>";
  ptr +=(int)humidity;
  ptr +="<span class='superscript'>%</span></div>";
  ptr +="</div>";
  ptr +="<div class='data pressure'>";
  ptr +="<div class='side-by-side icon'>";
  ptr +="<svg enable-background='new 0 0 40.542 40.541'height=40.541px id=Layer_1 version=1.1 viewBox='0 0 40.542 40.541'width=40.542px x=0px xml:space=preserve xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink y=0px><g><path d='M34.313,20.271c0-0.552,0.447-1,1-1h5.178c-0.236-4.841-2.163-9.228-5.214-12.593l-3.425,3.424";
  ptr +="c-0.195,0.195-0.451,0.293-0.707,0.293s-0.512-0.098-0.707-0.293c-0.391-0.391-0.391-1.023,0-1.414l3.425-3.424";
  ptr +="c-3.375-3.059-7.776-4.987-12.634-5.215c0.015,0.067,0.041,0.13,0.041,0.202v4.687c0,0.552-0.447,1-1,1s-1-0.448-1-1V0.25";
  ptr +="c0-0.071,0.026-0.134,0.041-0.202C14.39,0.279,9.936,2.256,6.544,5.385l3.576,3.577c0.391,0.391,0.391,1.024,0,1.414";
  ptr +="c-0.195,0.195-0.451,0.293-0.707,0.293s-0.512-0.098-0.707-0.293L5.142,6.812c-2.98,3.348-4.858,7.682-5.092,12.459h4.804";
  ptr +="c0.552,0,1,0.448,1,1s-0.448,1-1,1H0.05c0.525,10.728,9.362,19.271,20.22,19.271c10.857,0,19.696-8.543,20.22-19.271h-5.178";
  ptr +="C34.76,21.271,34.313,20.823,34.313,20.271z M23.084,22.037c-0.559,1.561-2.274,2.372-3.833,1.814";
  ptr +="c-1.561-0.557-2.373-2.272-1.815-3.833c0.372-1.041,1.263-1.737,2.277-1.928L25.2,7.202L22.497,19.05";
  ptr +="C23.196,19.843,23.464,20.973,23.084,22.037z'fill=#26B999 /></g></svg>";
  ptr +="</div>";
  ptr +="<div class='side-by-side text'>Pressure</div>";
  ptr +="<div class='side-by-side reading'>";
  ptr +=(int)pressure;
  ptr +="<span class='superscript'>hPa</span></div>";
  ptr +="</div>";
  ptr +="<div class='data altitude'>";
  ptr +="<div class='side-by-side icon'>";
  ptr +="<svg enable-background='new 0 0 58.422 40.639'height=40.639px id=Layer_1 version=1.1 viewBox='0 0 58.422 40.639'width=58.422px x=0px xml:space=preserve xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink y=0px><g><path d='M58.203,37.754l0.007-0.004L42.09,9.935l-0.001,0.001c-0.356-0.543-0.969-0.902-1.667-0.902";
  ptr +="c-0.655,0-1.231,0.32-1.595,0.808l-0.011-0.007l-0.039,0.067c-0.021,0.03-0.035,0.063-0.054,0.094L22.78,37.692l0.008,0.004";
  ptr +="c-0.149,0.28-0.242,0.594-0.242,0.934c0,1.102,0.894,1.995,1.994,1.995v0.015h31.888c1.101,0,1.994-0.893,1.994-1.994";
  ptr +="C58.422,38.323,58.339,38.024,58.203,37.754z'fill=#955BA5 /><path d='M19.704,38.674l-0.013-0.004l13.544-23.522L25.13,1.156l-0.002,0.001C24.671,0.459,23.885,0,22.985,0";
  ptr +="c-0.84,0-1.582,0.41-2.051,1.038l-0.016-0.01L20.87,1.114c-0.025,0.039-0.046,0.082-0.068,0.124L0.299,36.851l0.013,0.004";
  ptr +="C0.117,37.215,0,37.62,0,38.059c0,1.412,1.147,2.565,2.565,2.565v0.015h16.989c-0.091-0.256-0.149-0.526-0.149-0.813";
  ptr +="C19.405,39.407,19.518,39.019,19.704,38.674z'fill=#955BA5 /></g></svg>";
  ptr +="</div>";
  ptr +="<div class='side-by-side text'>Altitude</div>";
  ptr +="<div class='side-by-side reading'>";
  ptr +=(int)altitude;
  ptr +="<span class='superscript'>m</span></div>";
  ptr +="</div>";
  ptr +="</div>";
  ptr +="</body>";
  ptr +="</html>";
  return ptr;
}

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

На нашей веб-странице мы будем использовать шрифт Open Sans от Google. Обратите внимание, что вы не можете видеть шрифт Google, без активного подключения к интернету на устройстве, с которого вы собираетесь получить доступ к странице. Шрифты Google загружаются на лету.

ptr +="<link href='https://fonts.googleapis.com/css?family=Open+Sans:300,400,600' rel='stylesheet'>";

Иконки, используемые для отображения показаний температуры, на самом деле является масштабируемой векторной графикой (SVG), определенной в теге <svg>. Создание SVG не требует каких-либо специальных навыков программирования. Для создания графики для вашей страницы вы можете использовать любой редактор SVG. Мы использовали эти иконки SVG.

Рисунок 9 SVG иконки
Рисунок 9 – SVG иконки

Улучшение кода – автоматическое обновление страницы

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

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

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

Поместите этот код в тег <head> вашего документа, и этот мета-тег укажет браузеру обновлять страницу каждые две секунды. Довольно изящно!

Динамическая загрузка данных датчиков с помощью AJAX

Если ваша веб-страница много весит, то полное ее обновление не слишком практично. Лучше использовать асинхронный Javascript и Xml (AJAX), чтобы мы могли запрашивать данные с сервера асинхронно (в фоновом режиме), не обновляя страницу.

Для выполнения AJAX на веб-страницах в JavaScript обычно используется объект XMLHttpRequest. Он тихо выполняет GET-запрос на сервер и обновляет элемент на странице. AJAX – это не новая технология или другой язык, а просто существующие технологии, используемые по-новому. Кроме того, AJAX также позволяет:

  • запрашивать данные с сервера после загрузки страницы;
  • получать данные с сервера после загрузки страницы;
  • отправлять данные на сервер в фоновом режиме.

Ниже приведен скрипт AJAX, который мы будем использовать. Поместите этот скрипт непосредственно перед закрывающим тегом </head>.

ptr +="<script>\n";
ptr +="setInterval(loadDoc,1000);\n";
ptr +="function loadDoc() {\n";
ptr +="var xhttp = new XMLHttpRequest();\n";
ptr +="xhttp.onreadystatechange = function() {\n";
ptr +="if (this.readyState == 4 && this.status == 200) {\n";
ptr +="document.body.innerHTML =this.responseText}\n";
ptr +="};\n";
ptr +="xhttp.open(\"GET\", \"/\", true);\n";
ptr +="xhttp.send();\n";
ptr +="}\n";
ptr +="</script>\n";

Скрипт начинается с тега <script>, так как AJAX-скрипт – это не что иное, как javascript, и поэтому нужно писать его в теге <script>. Чтобы данная функция вызывалась повторно, мы будем использовать функцию javascript setInterval(). Она принимает два параметра: функцию для выполнения и интервал времени (в миллисекундах), через который выполнять указанную функцию.

ptr +="<script>\n";
ptr +="setInterval(loadDoc,1000);\n";

Сердцем этого скрипта является функция loadDoc(). Внутри этой функции создается объект XMLHttpRequest(). Данный объект используется для запроса данных с веб-сервера.

ptr +="function loadDoc() {\n";
ptr +="var xhttp = new XMLHttpRequest();\n";

Функция xhttp.onreadystatechange() вызывается каждый раз, когда изменяется readyState. Свойство readyState хранит состояние XMLHttpRequest. Возможны следующие значения:

  • 0: запрос не инициализирован;
  • 1: установлено подключение к серверу;
  • 2: запрос получен;
  • 3: обработка запроса;
  • 4: запрос завершен, и ответ готов.

Свойство status содержит статус объекта XMLHttpRequest. Возможны следующие значения:

  • 200: OK;
  • 403: запрещено;
  • 404: страница не найдена.

Когда readyState равен 4, а status – 200, ответ готов. Теперь обновляется содержимое элемента body (содержащего значения температуры, влажности, давления и высоты).

ptr +="xhttp.onreadystatechange = function() {\n";
ptr +="if (this.readyState == 4 && this.status == 200) {\n";
ptr +="document.body.innerHTML =this.responseText}\n";
ptr +="};\n";

Затем HTTP-запрос инициируется с помощью функций open() и send().

ptr +="xhttp.open(\"GET\", \"/\", true);\n";
ptr +="xhttp.send();\n";
ptr +="}\n";

Теги

ESP8266EspressifIoT (интернет вещей)NodeMCUАтмосферное давлениеВлажностьДатчикДатчик барометрического давленияДатчик температурыИзмерение температурыОтносительная влажность воздухаТемпература

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.


  • 2022-11-02Владимир

    Каким образом измеряется высота если мы не задали давление на уровне моря ?

  • 2022-04-25Сергей

    Спасибо, всё доходчиво и просто. Подскажите как правильно сменить иконки на другие, например готовый скачанные с интернета?

  • 2022-01-07Сергей

    К каким пинам можно подсоединить BME280 к ESP-01?

  • 2020-10-30Eugene Pirogov

    Здравствуйте.
    Отличная статья. всё работает. Подскажите, как объединить показания с датчика давления BMP280 и DS18B20
    https://radioprog.ru/post/896

    пытался объединить оба файла, но к сожалению. не получилось