ESP8266 и Arduino IDE. Часть 10b: IoT-сайт. Улучшение веб-страницы
Теперь пришло время сделать веб-страницу привлекательнее. Сложная часть – это HTML и JavaScript для виджетов. Код скетча практически такой же, как в предыдущем примере.
В предыдущей статье мы закончили с работающим, но очень простым сайтом. Теперь пора сделать веб-страницу красивее.
Когда я начал думать о части 10 руководства по ESP8266, я также заинтересовался виджетами для веб-страниц и подумал, что было бы хорошей идеей объединить эти два направления.
Я начал с поиска готовых виджетов в интернете, и их много. Почти все они очень тяжёлые (большой объём кода) и слишком сложные для моих задач. Мне нужно было что-то простое и лёгкое, поэтому я разработал свои собственные. Это заняло больше времени, чем ожидалось – пришлось заново изучать базовую математику (особенно градусы и радианы), и свободного времени было мало. Графика базовая, но виджеты работают довольно хорошо.
Как и с базовым сайтом, я разрабатывал новую страницу как отдельный проект, используя фиктивные данные для значений датчиков. Когда страница заработала, я скопировал её в скетч ESP8266.
О графике
Вся графика, включая солнце, создаётся динамически. Циферблаты и график используют HTML canvas и JavaScript, солнце – SVG.
Время
Время, отображаемое на сайте, берётся с системы, используемой для просмотра веб-страницы – компьютера или мобильного устройства.
function updateTime()
{
var d = new Date();
var t = d.toLocaleTimeString();
document.getElementById('time').textContent = t;
}
Когда в следующей части будет добавлен LCD, не будет системы, отображающей веб-страницу, поэтому не будет простого способа получить время. Возможно, время будет браться с NTP-сервера.
Индикатор солнца/яркости
Я создал солнце в графическом редакторе и экспортировал как SVG. Оригинальное изображение имело все 12 лучей, и поскольку SVG – это текстовые файлы, я смог редактировать его для отображения разного количества лучей, удаляя части.
Солнце имеет 13 значений: от 0 лучей до 12 лучей. Значение, возвращаемое analogRead() от LDR – число от 0 до 1023, которое преобразуется в число от 0 до 12 с помощью функции Arduino map(). Новое значение определяет количество лучей в изображении. Чем ярче – тем больше лучей.
brightness = map(analogRead(pinLDR), 0, 1023, 0, 12);
Помещая данные SVG-графики в массив, можно легко создавать финальную графику на лету, используя значение яркости. Элемент массива 0 – основная часть графики, необходимая всегда. Каждый элемент от 1 до 12 – один из лучей. Это означает, что если значение яркости 4, нужно просто объединить все элементы массива от 0 до 4. Если значение яркости 9 – объединить элементы от 0 до 9. Идеально для цикла for.
function drawSVG(brightnessVal)
{
// Создание иконки солнца
// Количество лучей соответствует значению яркости от 0 до 12
var SVGicon = svgArray[0].replace('[bright]', brightnessVal);
for (var i=1; i <= brightnessVal; i++) { SVGicon = SVGicon + svgArray[i]; }
document.getElementById('brightnessIcon').textContent = SVGicon;
}
Значение яркости может отображаться числом внутри центра солнца – по умолчанию это отключено и может быть включено раскомментированием соответствующей строки.
Почему SVG? Основная причина – сохранение кода сайта единым блоком и упрощение управления файлами. Можно было бы использовать отдельные изображения, но это потребовало бы работы с 13 разными иконками солнца (возможно, через SPIFFS) и значительно больше памяти. С SVG тот же результат достигается гораздо проще.
Циферблаты
Циферблаты перерисовываются каждый раз при обновлении значения. Это происходит достаточно быстро, чтобы пользователь этого не заметил.
Циферблаты обладают определённой гибкостью – принимают различные начальные и конечные точки дуги, а также можно задать минимальные и максимальные значения шкалы и цвет дуги.
function drawDial(canvasID, dialColour, startAngle, stopAngle, minVal, maxVal, dialValue)
Циферблат масштабируется по ширине canvas, который должен быть создан заранее. Высота canvas не используется – если canvas выше, чем шире, циферблат будет нарисован в верхней части.
Размер canvas должен соответствовать начальной и конечной точкам дуги. Чем ближе к полному кругу, тем ближе canvas должен быть к квадрату. Чем ближе дуга к полукругу, тем шире должен быть canvas. Нахождение точного размера – метод проб и ошибок.
График
График предназначен для отображения температуры и влажности. Он автоматически масштабируется под размер canvas, а минимальные и максимальные значения шкалы устанавливаются через gMin и gMax.
График жёстко закодирован на 20 делений – 20 значений по оси X.
function drawGraph(canvasID, gMin, gMax, drawLines, t, h)
Размер графика по умолчанию 440x150. Размеры можно менять (в разумных пределах), изменяя размер canvas.
Аргумент drawLines – булево значение (true или false), указывающее функции рисовать линии или нет. При false график рисуется, но данные не отображаются. Это используется при первоначальном отрисовке графика до получения данных.
t и h – значения для построения: t = температура, h = влажность.
Разделение HTML в отдельный файл
С увеличением объёма HTML и JavaScript скетч становится неудобным для навигации. Пора переместить HTML в отдельный файл – не SPIFFS, а файл внутри скетча, который остаётся частью проекта. Файл будет включён и загружен при каждой компиляции программы. Это делается с помощью функции вкладок Arduino IDE.
Почему webpage.h, а не webpage.html? Arduino IDE не включает файлы с расширением .html – необходимо использовать .h.
Почему не SPIFFS? Главная причина – удобство. HTML-файл можно было бы загрузить в SPIFFS и читать из основного скетча, но это означает необходимость загрузки HTML через функцию загрузки ESP8266 И компиляции/загрузки скетча. При использовании второй вкладки HTML остаётся частью скетча и загружается при компиляции кода.
Скетч ESP8266-10_sketch04_IOT_Website_02
В новом скетче HTML вынесен в файл webpage.h, а основной код выглядит следующим образом:
//
// ESP8266 and the Arduino IDE Part 10: Environment monitor station
// ESP8266-10_sketch04_IOT_Website_02
//
#include "webpage.h"
#include <ESP8266WiFi.h>
#include <WebSocketsServer.h>
WiFiServer server(80);
WebSocketsServer webSocket = WebSocketsServer(81);
char ssid[] = "mySSID"; // измените на свои данные сети
char pass[]= "myPassword";
#include "DHT.h"
#define DHTPIN D6
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
byte const pinLED = D8;
byte const pinLDR = A0;
int brightness = 0;
float humidity = 0;
float tempC = 0;
float tempF = 0;
boolean DHTreadingsOK = false;
boolean updateWebpage = false;
Ядро скетча (не-HTML часть) практически такое же, как и раньше: запуск веб-сервера, отдача веб-страницы, использование таймера для чтения значений датчиков и отправка новых значений на веб-страницу.
HTML-кода стало значительно больше, особенно JavaScript. Большая часть создаёт циферблаты и график. Механизм работы веб-страницы остаётся тем же – страница получает новые данные через WebSocket, и новые данные копируются в соответствующие части страницы.
Используется немного другой строковый литерал для хранения HTML:
String html_1 = R"=="==(
Это позволяет использовать смешанные кавычки в HTML/JavaScript и необходимо для SVG-массивов, которые содержат одинарные кавычки внутри двойных.
Полный скетч доступен для скачивания на сайте автора.