Отображение значений нескольких DS18B20 на веб-сервере ESP8266 NodeMCU

Отображение значений нескольких DS18B20 на веб-сервере ESP8266 NodeMCU

Вы когда-нибудь хотели разместить датчики по всему дому и саду, чтобы они регулярно отправляли данные о температуре на центральный сервер? Тогда этот IoT-проект может стать для вас отличной отправной точкой!

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

Несколько DS18B20 на одной шине

Одна из главных особенностей DS18B20 заключается в том, что несколько DS18B20 могут сосуществовать на одной шине 1-Wire. Поскольку каждый DS18B20 имеет уникальный 64-битный серийный код, записанный на заводе, их легко отличить друг от друга.

Эта функция может быть огромным преимуществом, когда вы хотите управлять множеством DS18B20, распределённых по большой площади. В этом руководстве мы будем делать именно это.

Подключение нескольких датчиков DS18B20 к ESP8266 NodeMCU

Подключение датчиков DS18B20 к ESP8266 NodeMCU довольно простое.

Распиновка DS18B20

Начните с параллельного подключения всех DS18B20, т.е. объедините все выводы VDD, GND и сигнальные выводы. Затем подключите VDD к выходу 3.3V, GND к земле, а сигнальный вывод подключите к цифровому выводу D2 на ESP8266 NodeMCU.

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

Подключение нескольких датчиков температуры DS18B20 к ESP8266

Подготовка 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/

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

Протокол Dallas 1-Wire достаточно сложен и требует значительного количества кода для разбора коммуникации. Чтобы скрыть эту ненужную сложность, мы установим библиотеку DallasTemperature.h, чтобы можно было использовать простые команды для получения показаний температуры с датчика.

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

Отфильтруйте поиск, введя «ds18b20». Должно появиться несколько записей. Найдите DallasTemperature от Miles Burton. Нажмите на эту запись, а затем выберите Install.

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

Библиотека Dallas Temperature является аппаратно-зависимой библиотекой, которая обрабатывает функции нижнего уровня. Для работы её необходимо сочетать с библиотекой One Wire для связи с любым устройством на шине 1-Wire, а не только с DS18B20. Установите и эту библиотеку тоже.

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

Определение адресов DS18B20 на шине

Мы знаем, что каждый DS18B20 имеет уникальный 64-битный адрес, назначенный для отличия одного датчика от другого. Сначала мы найдём этот адрес, чтобы соответствующим образом пометить каждый датчик. Затем адрес можно использовать для считывания данных с каждого датчика по отдельности.

Следующий скетч обнаруживает все DS18B20, присутствующие на шине, и выводит их адреса 1-Wire в монитор последовательного порта.

Вы можете подключать по одному датчику за раз, чтобы найти его адрес (или последовательно добавлять новые датчики), чтобы можно было идентифицировать каждый по его адресу. Затем вы можете пометить каждый датчик.

#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port D2 on the ESP8266
#define ONE_WIRE_BUS D2

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

// variable to hold device addresses
DeviceAddress Thermometer;

int deviceCount = 0;

void setup(void)
{
  // start serial port
  Serial.begin(115200);

  // Start up the library
  sensors.begin();

  // locate devices on the bus
  Serial.println("Locating devices...");
  Serial.print("Found ");
  deviceCount = sensors.getDeviceCount();
  Serial.print(deviceCount, DEC);
  Serial.println(" devices.");
  Serial.println("");

  Serial.println("Printing addresses...");
  for (int i = 0;  i < deviceCount;  i++)
  {
    Serial.print("Sensor ");
    Serial.print(i+1);
    Serial.print(" : ");
    sensors.getAddress(Thermometer, i);
    printAddress(Thermometer);
  }
}

void loop(void)
{ }

void printAddress(DeviceAddress deviceAddress)
{
  for (uint8_t i = 0; i < 8; i++)
  {
    Serial.print("0x");
    if (deviceAddress[i] < 0x10) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
    if (i < 7) Serial.print(", ");
  }
  Serial.println("");
}

Теперь откройте монитор последовательного порта. Вы должны получить что-то вроде следующего.

Определение адреса 1-Wire всех DS18B20 на шине

Скопируйте все адреса, так как они понадобятся нам в следующем скетче.

Создание веб-сервера ESP8266 в режиме станции (STA)

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

Если вы хотите узнать о создании веб-сервера с ESP8266 NodeMCU в режиме 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
  • Перед отправкой веб-страницы ESP8266 считывает температуру с каждого DS18B20 по его адресу, поэтому вам нужно заменить адреса DS18B20 на те, которые вы нашли в предыдущем скетче.

uint8_t sensor1[8] = { 0x28, 0xEE, 0xD5, 0x64, 0x1A, 0x16, 0x02, 0xEC };
uint8_t sensor2[8] = { 0x28, 0x61, 0x64, 0x12, 0x3C, 0x7C, 0x2F, 0x27 };
uint8_t sensor3[8] = { 0x28, 0x61, 0x64, 0x12, 0x3F, 0xFD, 0x80, 0xC6 };

Когда вы закончите, попробуйте загрузить скетч.

#include <ESP8266WebServer.h>
#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port D2 on the ESP8266
#define ONE_WIRE_BUS D2

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

float tempSensor1, tempSensor2, tempSensor3;

uint8_t sensor1[8] = { 0x28, 0xEE, 0xD5, 0x64, 0x1A, 0x16, 0x02, 0xEC  };
uint8_t sensor2[8] = { 0x28, 0x61, 0x64, 0x12, 0x3C, 0x7C, 0x2F, 0x27  };
uint8_t sensor3[8] = { 0x28, 0x61, 0x64, 0x12, 0x3F, 0xFD, 0x80, 0xC6  };

/*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);

  sensors.begin();

  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() {
  sensors.requestTemperatures();
  tempSensor1 = sensors.getTempC(sensor1); // Gets the values of the temperature
  tempSensor2 = sensors.getTempC(sensor2); // Gets the values of the temperature
  tempSensor3 = sensors.getTempC(sensor3); // Gets the values of the temperature
  server.send(200, "text/html", SendHTML(tempSensor1,tempSensor2,tempSensor3));
}

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

String SendHTML(float tempSensor1,float tempSensor2,float tempSensor3){
  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 Temperature Monitor</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 Temperature Monitor</h1>\n";
  ptr +="<p>Living Room: ";
  ptr +=tempSensor1;
  ptr +="&deg;C</p>";
  ptr +="<p>Bedroom: ";
  ptr +=tempSensor2;
  ptr +="&deg;C</p>";
  ptr +="<p>Kitchen: ";
  ptr +=tempSensor3;
  ptr +="&deg;C</p>";
  ptr +="</div>\n";
  ptr +="</body>\n";
  ptr +="</html>\n";
  return ptr;
}

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

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

IP-адрес веб-сервера ESP8266 в режиме станции в мониторе последовательного порта

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

Показания нескольких DS18B20 на веб-сервере ESP8266 — без CSS

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

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

  • ESP8266WebServer.h — библиотека, предоставляющая специфичные для ESP8266 методы WiFi, которые мы вызываем для подключения к сети. Она также имеет некоторые доступные методы, которые помогут нам настроить сервер и обрабатывать входящие HTTP-запросы без необходимости беспокоиться о деталях реализации нижнего уровня.

  • DallasTemperature.h — аппаратно-зависимая библиотека, которая обрабатывает функции нижнего уровня. Для работы её необходимо сочетать с библиотекой One Wire.

  • OneWire.h — библиотека для связи с любым устройством на шине 1-Wire, а не только с DS18B20.

#include <ESP8266WebServer.h>
#include <OneWire.h>
#include <DallasTemperature.h>

Далее мы создаём экземпляры, необходимые для датчика температуры, и переменные для хранения показаний температуры. Датчик температуры подключён к GPIO D2.

// Data wire is plugged into port D2 on the ESP8266
#define ONE_WIRE_BUS D2

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

float tempSensor1, tempSensor2, tempSensor3;

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

uint8_t sensor1[8] = { 0x28, 0xEE, 0xD5, 0x64, 0x1A, 0x16, 0x02, 0xEC  };
uint8_t sensor2[8] = { 0x28, 0x61, 0x64, 0x12, 0x3C, 0x7C, 0x2F, 0x27  };
uint8_t sensor3[8] = { 0x28, 0x61, 0x64, 0x12, 0x3F, 0xFD, 0x80, 0xC6  };

Поскольку мы настраиваем ESP8266 в режиме станции (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-сервер перед его фактическим запуском. Прежде всего, мы инициализируем последовательную связь с ПК и инициализируем объект DallasTemperature с помощью функции begin(). Она инициализирует шину и обнаруживает все DS18B20, присутствующие на ней. Каждому датчику присваивается индекс и устанавливается разрешение 12 бит.

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

sensors.begin();

Теперь нам нужно подключиться к 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, отображая значение 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.on.

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() {
  sensors.requestTemperatures();
  tempSensor1 = sensors.getTempC(sensor1);
  tempSensor2 = sensors.getTempC(sensor2);
  tempSensor3 = sensors.getTempC(sensor3);
  server.send(200, "text/html", SendHTML(tempSensor1,tempSensor2,tempSensor3));
}

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

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

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

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

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

String SendHTML(float tempSensor1,float tempSensor2,float tempSensor3){
  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 Temperature Monitor</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 Temperature Monitor</h1>\n";

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

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

ptr +="<p>Living Room: ";
ptr +=tempSensor1;
ptr +="&deg;C</p>";
ptr +="<p>Bedroom: ";
ptr +=tempSensor2;
ptr +="&deg;C</p>";
ptr +="<p>Kitchen: ";
ptr +=tempSensor3;
ptr +="&deg;C</p>";
ptr +="</div>\n";
ptr +="</body>\n";
ptr +="</html>\n";
return ptr;
}

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

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

Показания нескольких DS18B20 на веб-сервере ESP8266 — с CSS

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

String SendHTML(float tempSensor1,float tempSensor2,float tempSensor3){
  String ptr = "<!DOCTYPE html>";
  ptr +="<html>";
  ptr +="<head>";
  ptr +="<title>ESP8266 Temperature Monitor</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-top: 50px;} ";
  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 +=".temperature{font-weight: 300;font-size: 50px;padding-right: 15px;}";
  ptr +=".living-room .temperature{color: #3B97D3;}";
  ptr +=".bedroom .temperature{color: #F29C1F;}";
  ptr +=".kitchen .temperature{color: #26B99A;}";
  ptr +=".superscript{font-size: 17px;font-weight: 600;position: absolute;right: -5px;top: 15px;}";
  ptr +=".data{padding: 10px;}";
  ptr +=".container{display: table;margin: 0 auto;}";
  ptr +=".icon{width:82px}";
  ptr +="</style>";
  ptr +="</head>";
  ptr +="<body>";
  ptr +="<h1>ESP8266 Temperature Monitor</h1>";
  ptr +="<div class='container'>";
  ptr +="<div class='data living-room'>";
  ptr +="<div class='side-by-side icon'>";
  ptr +="<svg enable-background='new 0 0 65.178 45.699'height=45.699px id=Layer_1 version=1.1 viewBox='0 0 65.178 45.699'width=65.178px x=0px xml:space=preserve xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink y=0px><polygon fill=#3B97D3 points='8.969,44.261 8.969,16.469 7.469,16.469 7.469,44.261 1.469,44.261 1.469,45.699 14.906,45.699 ";
  ptr +="14.906,44.261 '/><polygon fill=#3B97D3 points='13.438,0 3,0 0,14.938 16.438,14.938 '/><polygon fill=#3B97D3 points='29.927,45.699 26.261,45.699 26.261,41.156 32.927,41.156 '/><polygon fill=#3B97D3 points='58.572,45.699 62.239,45.699 62.239,41.156 55.572,41.156 '/><path d='M61.521,17.344c-2.021,0-3.656,1.637-3.656,3.656v14.199H30.594V21c0-2.02-1.638-3.656-3.656-3.656";
  ptr +="c-2.02,0-3.657,1.636-3.657,3.656v14.938c0,2.021,1.637,3.655,3.656,3.655H61.52c2.02,0,3.655-1.637,3.655-3.655V21";
  ptr +="C65.177,18.98,63.54,17.344,61.521,17.344z'fill=#3B97D3 /><g><path d='M32.052,30.042c0,2.02,1.637,3.656,3.656,3.656h16.688c2.019,0,3.656-1.638,3.656-3.656v-3.844h-24";
  ptr +="L32.052,30.042L32.052,30.042z'fill=#3B97D3 /><path d='M52.396,6.781H35.709c-2.02,0-3.656,1.637-3.656,3.656v14.344h24V10.438";
  ptr +="C56.053,8.418,54.415,6.781,52.396,6.781z'fill=#3B97D3 /></g></svg>";
  ptr +="</div>";
  ptr +="<div class='side-by-side text'>Living Room</div>";
  ptr +="<div class='side-by-side temperature'>";
  ptr +=(int)tempSensor1;
  ptr +="<span class='superscript'>&deg;C</span></div>";
  ptr +="</div>";
  ptr +="<div class='data bedroom'>";
  ptr +="<div class='side-by-side icon'>";
  ptr +="<svg enable-background='new 0 0 43.438 35.75'height=35.75px id=Layer_1 version=1.1 viewBox='0 0 43.438 35.75'width=43.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='M25.489,14.909H17.95C13.007,14.908,0,15.245,0,20.188v3.688h43.438v-3.688";
  ptr +="C43.438,15.245,30.431,14.909,25.489,14.909z'fill=#F29C1F /><polygon fill=#F29C1F points='0,31.25 0,35.75 2.5,35.75 4.5,31.25 38.938,31.25 40.938,35.75 43.438,35.75 43.438,31.25 ";
  ptr +="43.438,25.375 0,25.375      '/><path d='M13.584,11.694c-3.332,0-6.033,0.973-6.033,2.175c0,0.134,0.041,0.264,0.105,0.391";
  ptr +="c3.745-0.631,7.974-0.709,10.341-0.709h1.538C19.105,12.501,16.613,11.694,13.584,11.694z'fill=#F29C1F /><path d='M30.009,11.694c-3.03,0-5.522,0.807-5.951,1.856h1.425V13.55c2.389,0,6.674,0.081,10.444,0.728";
  ptr +="c0.069-0.132,0.114-0.268,0.114-0.408C36.041,12.668,33.34,11.694,30.009,11.694z'fill=#F29C1F /><path d='M6.042,14.088c0-2.224,3.376-4.025,7.542-4.025c3.825,0,6.976,1.519,7.468,3.488h1.488";
  ptr +="c0.49-1.97,3.644-3.489,7.469-3.489c4.166,0,7.542,1.801,7.542,4.025c0,0.17-0.029,0.337-0.067,0.502";
  ptr +="c1.08,0.247,2.088,0.549,2.945,0.926V3.481C40.429,1.559,38.871,0,36.948,0H6.49C4.568,0,3.009,1.559,3.009,3.481v12.054";
  ptr +="c0.895-0.398,1.956-0.713,3.095-0.968C6.069,14.41,6.042,14.251,6.042,14.088z'fill=#F29C1F /></g></svg>";
  ptr +="</div>";
  ptr +="<div class='side-by-side text'>Bedroom</div>";
  ptr +="<div class='side-by-side temperature'>";
  ptr +=(int)tempSensor2;
  ptr +="<span class='superscript'>&deg;C</span></div>";
  ptr +="</div>";
  ptr +="<div class='data kitchen'>";
  ptr +="<div class='side-by-side icon'>";
  ptr +="<svg enable-background='new 0 0 48 31.5'height=31.5px id=Layer_1 version=1.1 viewBox='0 0 48 31.5'width=48px x=0px xml:space=preserve xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink y=0px><circle cx=24.916 cy=15.75 fill=#26B99A r=15.75 /><path d='M14.917,15.75c0-5.522,4.478-10,10-10c2.92,0,5.541,1.26,7.369,3.257l1.088-1.031";
  ptr +="c-2.103-2.285-5.106-3.726-8.457-3.726c-6.351,0-11.5,5.149-11.5,11.5c0,3.127,1.252,5.958,3.277,8.031l1.088-1.031";
  ptr +="C16.011,20.945,14.917,18.477,14.917,15.75z'fill=#FFFFFF /><path d='M45.766,2.906c-1.232,0-2.232,1-2.232,2.234v11.203c0,0,2.76,0,3,0v12H48v-12V2.906";
  ptr +="C48,2.906,46.035,2.906,45.766,2.906z'fill=#26B99A /><path d='M6.005,2.917v5.184c0,0.975-0.638,1.792-1.516,2.083V2.917H3.021v7.267c-0.878-0.29-1.516-1.107-1.516-2.083";
  ptr +="V2.917H0v5.458c0,1.802,1.306,3.291,3.021,3.592v16.376H4.49v-16.38c1.695-0.318,2.979-1.8,2.979-3.588V2.917H6.005z'fill=#26B99A /></svg>";
  ptr +="</div>";
  ptr +="<div class='side-by-side text'>Kitchen</div>";
  ptr +="<div class='side-by-side temperature'>";
  ptr +=(int)tempSensor3;
  ptr +="<span class='superscript'>&deg;C</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 Editor для создания графики для вашей страницы. Мы использовали следующие SVG-иконки.

SVG-иконки

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

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

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

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

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

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

Обновление веб-страницы не слишком практично, если у вас тяжёлая веб-страница. Лучший метод — использовать асинхронный Javascript и 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";