ESP8266 и Arduino IDE. Часть 10b: IoT-сайт. Улучшение веб-страницы

Теперь пришло время сделать веб-страницу привлекательнее. Сложная часть – это HTML и JavaScript для виджетов. Код скетча практически такой же, как в предыдущем примере.

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

Улучшенная веб-страница IoT

Базовая веб-страница (до улучшения)

Когда я начал думать о части 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

График 400x100

График 200x200

Размер графика по умолчанию 440x150. Размеры можно менять (в разумных пределах), изменяя размер canvas.

Аргумент drawLines – булево значение (true или false), указывающее функции рисовать линии или нет. При false график рисуется, но данные не отображаются. Это используется при первоначальном отрисовке графика до получения данных.

t и h – значения для построения: t = температура, h = влажность.


Разделение HTML в отдельный файл

С увеличением объёма HTML и JavaScript скетч становится неудобным для навигации. Пора переместить HTML в отдельный файл – не SPIFFS, а файл внутри скетча, который остаётся частью проекта. Файл будет включён и загружен при каждой компиляции программы. Это делается с помощью функции вкладок Arduino IDE.

Arduino IDE -- новая вкладка

Создание вкладки

Ввод имени файла

Вторая вкладка в IDE

Копирование кода

Перемещение HTML

Почему 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-массивов, которые содержат одинарные кавычки внутри двойных.

Полный скетч доступен для скачивания на сайте автора.