Подключение нескольких DS18B20 к ESP32 и отображение значений через веб-сервер

Подключение нескольких DS18B20 к ESP32 и отображение значений через веб-сервер

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

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

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

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

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

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

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

Распиновка DS18B20

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

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

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

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

Существует дополнение для Arduino IDE, которое позволяет программировать ESP32 с помощью Arduino IDE. Следуйте руководству ниже, чтобы подготовить Arduino IDE для работы с ESP32, если вы ещё этого не сделали.

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

Микроконтроллер ESP32 быстро стал одной из самых популярных плат среди любителей, инженеров и людей, интересующихся Интернетом вещей (IoT)…

/lastminuteengineers/esp32-arduino-ide-tutorial/index

Установка библиотеки для 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 15 on the ESP32
#define ONE_WIRE_BUS 15

// 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 на шине

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

Создание веб-сервера ESP32 в режиме WiFi Station (STA)

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

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

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

ESP32 стал одним из самых популярных микроконтроллеров в мире IoT — и не без причины. Он мощный, доступный и имеет встроенные…

/lastminuteengineers/creating-esp32-web-server-arduino-ide/index

Перед загрузкой скетча вам необходимо внести некоторые изменения, чтобы он работал у вас.

  • Вам нужно изменить следующие две переменные на учётные данные вашей сети, чтобы ESP32 мог установить соединение с существующей сетью.

const char* ssid = "YourNetworkName";  // Enter SSID here
const char* password = "YourPassword";  //Enter Password here
  • Перед выдачей веб-страницы ESP32 считывает температуру с каждого 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 <WiFi.h>
#include <WebServer.h>
#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 15 on the ESP32
#define ONE_WIRE_BUS 15

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

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

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

WebServer server(80);

float tempSensor1, tempSensor2, tempSensor3;

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>ESP32 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>ESP32 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 бод. И нажмите кнопку EN на ESP32. Если всё в порядке, он выведет динамический IP-адрес, полученный от вашего маршрутизатора, и покажет сообщение HTTP server started.

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

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

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

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

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

  • WiFi.h — библиотека предоставляет специфичные для ESP32 методы WiFi, которые мы вызываем для подключения к сети.

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

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

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

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

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

// Data wire is plugged into port 15 on the ESP32
#define ONE_WIRE_BUS 15

// 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  };

Поскольку мы настраиваем ESP32 в режиме Station (STA), он будет подключаться к существующей WiFi-сети. Следовательно, нам нужно предоставить ему SSID и пароль вашей сети. Далее мы запускаем веб-сервер на порту 80.

/*Put your SSID & Password*/
const char* ssid = "YourNetworkName";  // Enter SSID here
const char* password = "YourPassword";  //Enter Password here

WebServer 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);

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

//check wi-fi is connected to wi-fi network
  while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.print(".");
  }

После подключения ESP32 к сети скетч выводит IP-адрес, назначенный ESP32, отображая значение 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() отвечает за генерацию веб-страницы всякий раз, когда веб-сервер ESP32 получает запрос от веб-клиента. Она просто объединяет 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>ESP32 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>ESP32 Temperature Monitor</h1>\n";

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

Для динамического отображения показаний температуры мы помещаем эти значения в тег paragraph. Для отображения символа градуса мы используем 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 на веб-сервере ESP32 — с CSS

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

String SendHTML(float tempSensor1,float tempSensor2,float tempSensor3){
  String ptr = "<!DOCTYPE html>";
  ptr +="<html>";
  ptr +="<head>";
  ptr +="<title>ESP32 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>ESP32 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'>°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'>°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'>°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'>";
  • Иконки, используемые для отображения показаний температуры, на самом деле являются масштабируемой векторной графикой (Scalable Vector Graphics), определённой в теге <svg>. Создание SVG не требует специальных навыков программирования. Вы можете использовать Google SVG Editor для создания графики для вашей страницы. Мы использовали следующие 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";