Создание простой метеостанции на ESP8266 с датчиком BME280

Руководство по созданию метеостанции на ESP8266 с датчиком BME280

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

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

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

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

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

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

Рабочее напряжение модуля 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.

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

Схема подключения ESP8266 к датчику температуры, влажности и давления BME280 (Fritzing)

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

Для Arduino IDE доступно дополнение, которое позволяет программировать ESP8266 NodeMCU в Arduino IDE. Если вы ещё не настроили его, следуйте приведённому ниже руководству, чтобы подготовить Arduino IDE для работы с ESP8266.

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

Существует несколько платформ разработки для программирования ESP8266. Вы можете использовать Arduino IDE, предназначенную для тех, кто знаком с Arduino, Espruino…

https://lastminuteengineers.com/esp8266-nodemcu-arduino-tutorial/

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

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

Для установки библиотеки перейдите в Arduino IDE > Sketch > Include Library > Manage Libraries… Дождитесь, пока менеджер библиотек загрузит индекс библиотек и обновит список установленных библиотек.

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

Установка библиотеки BME280 в Arduino IDE

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

Установка библиотеки Adafruit Unified Sensor

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

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

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

Создание простого веб-сервера на ESP8266 NodeMCU в Arduino IDE

В последние годы ESP8266 стал одним из самых популярных модулей в мире IoT и проектов, связанных с WiFi. Это недорогой WiFi-модуль, который при небольших усилиях…

https://lastminuteengineers.com/creating-esp8266-web-server-arduino-ide/

Перед загрузкой скетча необходимо внести одно изменение, чтобы он заработал у вас. Обновите следующие две переменные, указав учётные данные вашей сети, чтобы 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 +="&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;
}

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

После загрузки скетча откройте Serial Monitor и установите скорость передачи данных на 115200. Затем нажмите кнопку EN на NodeMCU. Если всё в порядке, на экране отобразится динамический IP-адрес, полученный от вашего маршрутизатора, и сообщение HTTP server started.

IP-адрес веб-сервера ESP8266 в режиме Station на Serial Monitor

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

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

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

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

  • 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-сущность &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;
}

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

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

Показания 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 не требует специальных навыков программирования. Вы можете использовать редактор Google SVG для создания графики для вашей страницы. Мы использовали следующие 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";