Создание простой метеостанции на ESP8266 с датчиком BME280
Не полагайтесь на погодные приложения для смартфона или коммерческие метеостанции (которые предоставляют данные со станций, расположенных в километрах от вас), чтобы они не испортили ваши планы на открытом воздухе. С помощью этого IoT-проекта вы можете стать своим собственным синоптиком!
В этом проекте ESP8266 NodeMCU используется в качестве управляющего устройства, которое легко подключается к любой существующей WiFi-сети и создаёт веб-сервер. Когда любое подключённое устройство обращается к этому веб-серверу, 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.
На следующей схеме показано, как всё подключить.
Подготовка Arduino IDE
Для Arduino IDE доступно дополнение, которое позволяет программировать ESP8266 NodeMCU в Arduino IDE. Если вы ещё не настроили его, следуйте приведённому ниже руководству, чтобы подготовить Arduino IDE для работы с ESP8266.
Существует несколько платформ разработки для программирования ESP8266. Вы можете использовать Arduino IDE, предназначенную для тех, кто знаком с Arduino, Espruino…
Установка библиотеки для BME280
Работа с модулем BME280 может быть довольно сложной. К счастью, была создана библиотека Adafruit BME280, которая скрывает все сложности, позволяя нам выполнять простые команды для чтения данных о температуре, относительной влажности и атмосферном давлении.
Для установки библиотеки перейдите в Arduino IDE > Sketch > Include Library > Manage Libraries… Дождитесь, пока менеджер библиотек загрузит индекс библиотек и обновит список установленных библиотек.
Отфильтруйте поиск, набрав «bme280». Вы должны увидеть несколько записей. Найдите Adafruit BME280 Library от Adafruit. Нажмите на эту запись, затем выберите Install.
Библиотека датчика BME280 использует бэкенд поддержки датчиков Adafruit. Поэтому в менеджере библиотек найдите Adafruit Unified Sensor и установите его тоже (возможно, придётся немного прокрутить список).
Отображение температуры, влажности, давления и высоты на веб-сервере ESP8266
Теперь мы настроим наш ESP8266 в режиме Station (STA) и создадим веб-сервер для отправки веб-страниц любому подключённому клиенту в существующей сети.
Если вы хотите узнать, как создать веб-сервер на ESP8266 в режиме AP/STA, ознакомьтесь с этим руководством.
В последние годы ESP8266 стал одним из самых популярных модулей в мире IoT и проектов, связанных с WiFi. Это недорогой WiFi-модуль, который при небольших усилиях…
Перед загрузкой скетча необходимо внести одно изменение, чтобы он заработал у вас. Обновите следующие две переменные, указав учётные данные вашей сети, чтобы ESP8266 мог подключиться к существующей сети.
const char* ssid = "YourNetworkName"; // Enter SSID here
const char* password = "YourPassword"; //Enter Password here
После внесения изменений попробуйте загрузить скетч.
#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;
/*Put your SSID & Password*/
const char* ssid = "YourNetworkName"; // Enter SSID here
const char* password = "YourPassword"; //Enter Password here
ESP8266WebServer server(80);
void setup() {
Serial.begin(115200);
delay(100);
bme.begin(0x76);
Serial.println("Connecting to ");
Serial.println(ssid);
//connect to your local wi-fi network
WiFi.begin(ssid, password);
//check wi-fi is connected to wi-fi network
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;
}
Доступ к веб-серверу
После загрузки скетча откройте Serial Monitor и установите скорость передачи данных на 115200. Затем нажмите кнопку EN на NodeMCU. Если всё в порядке, на экране отобразится динамический IP-адрес, полученный от вашего маршрутизатора, и сообщение HTTP server started.
Затем откройте браузер и перейдите по IP-адресу, отображённому в Serial Monitor. ESP8266 должен отправить веб-страницу с данными о температуре, влажности, давлении и высоте, полученными с датчика BME280.
Подробное объяснение кода
Скетч начинается с подключения следующих библиотек.
ESP8266WebServer.h — библиотека, предоставляющая специфичные для ESP8266 методы WiFi, которые мы используем для подключения к сети. Она также содержит методы, которые помогают настроить сервер и обрабатывать входящие 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 в режиме Station (STA), он будет подключаться к существующей WiFi-сети. Поэтому нам необходимо указать SSID и пароль вашей сети. Затем мы запускаем веб-сервер на порту 80.
/*Put your SSID & Password*/
const char* ssid = "YourNetworkName"; // Enter SSID here
const char* password = "YourPassword"; //Enter Password here
ESP8266WebServer server(80);
Внутри функции Setup()
В функции Setup() мы настраиваем наш HTTP-сервер перед его фактическим запуском.
Прежде всего, мы инициализируем последовательное соединение с ПК и инициализируем объект BME с помощью функции begin(). Эта функция инициализирует интерфейс I2C с заданным I2C-адресом (0x76) и проверяет идентификатор чипа. Затем она сбрасывает чип с помощью программного сброса и ожидает калибровки датчика после пробуждения.
Serial.begin(115200);
delay(100);
bme.begin(0x76);
Теперь нам нужно подключиться к WiFi-сети с помощью функции WiFi.begin(). Функция принимает SSID (имя сети) и пароль в качестве параметров.
Serial.println("Connecting to ");
Serial.println(ssid);
//connect to your local wi-fi network
WiFi.begin(ssid, password);
Пока ESP8266 пытается подключиться к сети, мы можем проверять статус подключения с помощью функции WiFi.status().
//check wi-fi is connected to wi-fi network
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
После успешного подключения ESP8266 к сети скетч выводит IP-адрес, присвоенный ESP8266, в Serial Monitor с помощью значения 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, чтобы указать серверу выполнять её при получении запроса на неуказанный URL.
server.onNotFound(handle_NotFound);
Теперь, чтобы запустить наш сервер, мы вызываем метод begin() на объекте сервера.
server.begin();
Serial.println("HTTP server started");
Внутри функции Loop()
Для обработки фактических входящих HTTP-запросов нам необходимо вызывать метод handleClient() на объекте сервера.
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 не требует специальных навыков программирования. Вы можете использовать редактор Google SVG для создания графики для вашей страницы. Мы использовали следующие SVG-иконки.
Улучшение кода — автоматическое обновление страницы
Одно из улучшений, которое вы можете внести в наш код, — это автоматическое обновление страницы для обновления значений датчика.
Добавив один мета-тег в ваш HTML-документ, вы можете указать браузеру автоматически перезагружать страницу через заданный интервал.
<meta http-equiv="refresh" content="2" >
Поместите этот код в тег <head> вашего документа, и этот мета-тег укажет браузеру обновлять страницу каждые две секунды. Весьма удобно!
Динамическая загрузка данных датчика с помощью AJAX
Обновление веб-страницы не слишком практично, если у вас тяжёлая веб-страница. Лучший метод — использование Asynchronous Javascript And Xml (AJAX), что позволяет запрашивать данные с сервера асинхронно (в фоновом режиме) без обновления страницы.
Объект XMLHttpRequest в JavaScript обычно используется для выполнения AJAX на веб-страницах. Он выполняет скрытый 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: «Forbidden»
404: «Page not found»
Когда 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";