Создаем простую метеостанцию на ESP8266 с BME280
Не позволяйте погодным приложениям для смартфонов или коммерческим метеостанциям (которые снабжают вас данными со станций, расположенных за много километров) разрушить ваши планы на открытом воздухе. С помощью этого IoT-проекта вы можете сами стать метеорологом!
В данном проекте ESP8266 NodeMCU используется в качестве устройства управления, которое легко подключается к существующей сети Wi-Fi и создает веб-сервер. Когда любое подключенное устройство обращается к этому веб-серверу, ESP8266 считывает температуру, влажность, атмосферное давление и высоту из BME280 и отправляет их в веб-браузер этого устройства, выводя их в приятном интерфейсе. Интересно? Давайте начнем!
BME280 – датчик температуры, влажности и давления
Сначала давайте кратко рассмотрим модуль BME280.
BME280 – это цифровой датчик температуры, влажности и давления нового поколения, производимый Bosch. Это преемник таких датчиков, как BMP180, BMP085 или BMP183.
Рабочее напряжение модуля BME280 составляет от 3,3 В до 5 В – идеально подходит для взаимодействия с микроконтроллерами 3,3 В, такими как ESP8266. Подробности о модуле можно прочитать в этой статье.
Модуль имеет простой двухпроводной интерфейс I2C для связи. По умолчанию I2C-адрес модуля BME280 – 0x76 и может быть легко изменен на 0x77 с помощью следующей процедуры.
Подключение датчика BME280 к плате ESP8266 NodeMCU
Подключение довольно простое. Начните с подключения вывода VIN к выводу 3.3V на ESP8266 NodeMCU и соедините выводы GND на данных платах.
Затем подключите вывод SCL к тактовому выводу I2C D1 на вашей плате ESP8266 и подключите вывод SDA к выводу данных I2C D2 на ESP8266.
На следующей схеме показано, как всё подключить.
Подготовка IDE Arduino
Существует дополнение для Arduino IDE, которое позволяет вам программировать ESP8266 NodeMCU с помощью Arduino IDE. Чтобы подготовить вашу Arduino IDE к работе с ESP8266, если вы еще этого не сделали, следуйте инструкциям из следующей статьи:
Установка библиотеки для BME280
Для связи с модулем BME280 необходимо выполнить кучу работы. К счастью, чтобы скрыть все сложности, была написана библиотека Adafruit BME280, чтобы мы могли выполнять простые команды для считывания данных о температуре, относительной влажности и атмосферном давлении.
Чтобы установить библиотеку, перейдите в раздел Скетч → Подключить библиотеку → Управлять библиотеками…. Подождите, пока менеджер библиотек загрузит индекс библиотек и обновит список установленных библиотек.
Отфильтруйте результаты поиска, набрав «bme280». Там должно быть пара записей. Ищите библиотеку Adafruit BME280 by Adafruit. Нажмите на эту запись, а затем выберите «Установка».
Библиотека датчиков BME280 использует Adafruit Sensor support backend. Поэтому найдите в менеджере библиотек 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 +="°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 сервер запущен.
Затем откройте браузер и укажите ему IP-адрес, выведенный в мониторе последовательного порта. ESP8266 должен выдать веб-страницу с показаниями температуры, влажности, давления и высоты от BME280.
Подробное объяснение кода
Скетч начинается с включения следующих библиотек:
- библиотека
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 °
.
ptr +="<p>Temperature: ";
ptr +=temperature;
ptr +="°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;
}
Стилизация веб-страницы, чтобы она выглядела более профессионально
Программисты, подобные нам, часто боятся дизайна – но небольшое усилие может сделать вашу веб-страницу более привлекательной и профессиональной. Скриншот, приведенный ниже, даст вам базовое представление о том, что мы собираемся делать.
Довольно красиво, правда? Давайте без лишних слов, применим стиль к нашей предыдущей 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'>°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.
Улучшение кода – автоматическое обновление страницы
Одно из улучшений, которое вы можете сделать с нашим кодом, – это автоматическое обновление страницы для обновления показаний датчиков.
Добавив один мета-тег в ваш 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";