Интернет вещей (IoT, Internet of Things) является многообещающим направлением, как уверяют аналитики. Одним из главных трендов IoT является автоматизация жилья или, как любят выражаться маркетологи, создание «умного дома».
Оставим в покое словесные упражнения и рассмотрим конкретный проект.
Постановка задачи
Я живу в собственном доме недалеко от Москвы. Помимо очевидных плюсов подобного варианта проживания, имеются свои нюансы. Если в многоквартирном доме большинство коммунальных задач берет на себя управляющая компания, то в собственном доме их приходится решать самостоятельно.
Одной из таких задач для меня стала необходимость дистанционного мониторинга и управления системой отопления. Справедливо утверждение, что в средней полосе России отопление зимой это не вопрос комфорта, но выживания. Согласно многократно подтвержденному эмпирическому закону, все неприятности случаются в самое неподходящее время. Более чем за десятилетие опыта жизни в собственном доме я тоже убедился в справедливости этого законе.
Но если, например, отказ насоса водоснабжения в 30-ти градусный мороз еще как-то можно пережить, то выход из строя отопительного котла превращается в катастрофу. В такой мороз нормально утепленный дом выстужается менее чем за сутки.
Мне приходится часто отлучаться из дома на длительное время, в том числе и зимой. Поэтому возможность дистанционного мониторинга состояния системы отопления и ее управления стала для меня актуальной задачей.
В моем доме система отопления имеет два котла, солярный (увы, газа нет и не предвидится) и электрический. Данный выбор обусловлен не только вопросами резервирования, но и оптимизации расходов на отопление. По ночам, за исключением суровых морозов, работает электрокотел, так как в доме установлен двухтарифный электросчетчик. Мощности этого котла вполне хватает для комфортной ночной температуры (18-19 градусов). Днем же в работу вступает солярный котел, поднимающий температуру до 22-23 градусов. В таком режиме система отопления работает уже несколько лет и позволяет сделать вывод об экономичности данного варианта.
Понятное дело, что ежедневные ручные переключения режимов работы системы отопления не самое разумный выбор, поэтому принято решение автоматизировать этот процесс и, заодно, предусмотреть возможность дистанционного управления.
Техническое задание
Следуя привычке разработчика, первым делом я систематизировал требования к создаваемой системе управления и накидал для себя нечто похожее на техническое задание.
Вот краткий перечень основных требований к проектируемому решению:
- контролировать температуру в доме и на улице
- обеспечивать три режима выбора отопительных котлов (подробнее чуть ниже)
- обеспечивать дистанционный мониторинг состояния системы и ее управление
Первоначально в списке было еще несколько пунктов, но потом они оказались исключенными в силу разных причин. Например, я планировал оснастить систему экраном с индикацией текущих параметров и возможностью управления через тачскрин. Но это мне показалось не нужным дублированием дистанционного управления через Интернет. Конечно, можно придумать вполне жизненные ситуации, когда локальная индикация и управление необходимы. Не спорю, но не стоит забывать, что эта возможность потребовала бы дополнительного усложнения и удорожания системы.
В алгоритм управления системой отопления заложен сценарий апокалипсиса, связанный с полным отключением электроснабжения. Понятное дело, в этом случае не приходится рассуждать о дистанционном управлении. Но находящиеся в доме могут несколькими простыми манипуляциями перейти в аварийный режим отопления. Достаточно переключить один внешний четырехполюсный тумблер и запустить резервный бензиновый электрогенератор. Это обеспечит работу солярного котла в автономном режиме. На практике такое случалось уже пару раз, когда ледяные дожди приводили к массовому обрыву проводов ЛЭП.
Современные котлы отопления, как правило, имеют выносные блоки управления, подключаемые обычным двужильным проводом. Чтобы не влезать в заводские схемы управления, было решено коммутировать собственно эти провода. Разрыв провода, осуществляемый обычным электромеханическим реле, приводит к остановке работы котла.
Метод обеспечения безопасности IoT
Начитавшись страшилок про последствия взлома умных домов, я решил подстраховаться и минимизировать возможность внешнего взлома. Кто-то скажет, дескать, кому нужно взламывать именно твой умный дом. Соглашусь, вероятность минимальна, но наблюдая регулярные попытки хакинга своих вебсерверов, я решил действовать по принципу: лучше переспать, чем недоесть. Шутка.
Для этого я отказался от распространенной парадигмы, когда центральный сервер является инициатором управления распределенными умными датчиками (устройствами). Было решено использовать классическую схему клиент-сервер, где клиентом выступает умный датчик.
Выбор такой архитектуры не всегда возможен в IoT, но в данном случае вполне допустим, так как системы отопления обладают достаточно большой инерционность. Даже наличие возможности мгновенного и произвольного изменения установок в системе, например, значения температуры в помещении, не приводит к мгновенному достижению заданных параметров.
Передача инициативы в обмене данными на сторону умного датчика позволяет практически полностью исключить его взлом посторонними лицами. Ведь датчик воспринимает только ответ от сервера на свой запрос. Теоретически можно перехватить такой запрос и подменить ответ, но эта угроза минимизируется, например, протоколом https. Если нет желания поднимать в датчике этот протокол, то есть вариант с вычислением контрольных сумм с учетом параметров, априори неизвестных злоумышленнику. Но данный криптографический вопрос выходит за рамки рассматриваемой темы.
Если на запрос не был получен ответ сервера, умный датчик, выждав определенный тайм-аут, продолжает работать в ранее установленном режиме.
В качестве сервера было решено создать небольшой веб-сайт с базой MySQL, который развертывался на домене третьего уровня одного из моих сайтов. Сайт был написан с использованием адаптивной верстки, что позволяет комфортно работать со смартфона.
Для обмена информацией с сервером был выбран пятиминутный период.
Отчасти этот выбор обусловлен одним нюансом работы электрокотла. Для исключения закипания воды в колбе нагревателя от остаточного тепла ТЭНов, используется так называемый выбег котла. Другими словами, после выключения ТЭНов циркулярный насос продолжает работать некоторое время. В моем котле по умолчанию стоит выбег в течение 4 минут, хотя его можно увеличить и на более продолжительное время. Поэтому пятиминутный интервал обмена вполне укладывался в логику работы отопительной системы. Да и более частый обмен данными не давал никакой пользы, лишь приводил к увеличению числа записей в базе сервера.
Алгоритм работы
Работа умного датчика, получившего название метеомодуль, не содержит ничего необычного. В цикле опрашиваются датчики температуры и влажности. Это продолжается примерно 4,5 минуты. Затем происходит формирование GET-запроса к серверу и обрабатывается полученный ответ. В итоге период (главный цикл) получается длительностью примерно 5 минут. Здесь не требуется идеальная точность, на практике период оказался меньше на несколько секунд, что приводит к постепенному сдвигу. При идеальном пятиминутном периоде в сутки передавалось бы 288 отсчетов, реально их оказывается 289-290. Это совсем не сказывается на работе системы.
Основной скетч программы с подробными комментариями приведен в листинге. В силу обширного объема кода я не стал публиковать реализации используемых подпрограмм. В листинге оставлены диагностические сообщения для вывода в терминал.
/*
* Sketch Meteo Control Mega2560
* ver. 13.0
* упрощенный алгоритм автоматики день - солярка, ночь - электрика. Начальный порог 21 градус, шаг - 0,5 градуса
* обмен с сервером по http 1.0
*/
// libs
#include <Wire.h>
#include "DHT.h"
// wired connections
// подключение таймера через шину I2C, адрес на шине 104
#define DS3231_I2C_ADDRESS 104
// define
#define HYSTERESIS 0.5 // гистерезис порога температуры, градусы
#define LONG_CYCLE 9 // продолжительность цикла измерений, 9 - около 5 мин с учетом времени обмена с сервером
#define SHORT_CYCLE 13 // продолжительность малого цикла измерений, 13 сек. с учетом времени сбора данных с датчиков малый цикл получается около 30 сек
#define DAY_BEGIN 6 // начало дневного тарифного периода
#define DAY_END 22 // конец дневного тарифного периода
#define MIN_INTERVAL 3000 // интервал чтения датчиков температуры 3 сек
#define PIN_DHT_IN 23 // вход датчика температуры и влажности внутри AM2301
#define PIN_DHT_OUT 22 // вход датчика температуры и влажности снаружи AM2301
#define DHTTYPE DHT21
DHT dhtin(PIN_DHT_IN, DHTTYPE);
DHT dhtout(PIN_DHT_OUT, DHTTYPE);
#define RELAY_E 25 // выход управления реле электрокотла
#define RELAY_D 24 // выход управления реле солярного котла
#define LED_R 27 // LED RGB
#define LED_G 29 // LED RGB
#define LED_B 31 // LED RGB
#define LED 13 // внутренний светодиод
#define LEAP_YEAR(_year) ((_year%4)==0) // для вычисления високосного года
// vars
uint32_t workTime; // время работы котла с момента включения реле
float hIn; // влажность внутри
float tIn; // температура внутри
float hOut; // влажность снаружи
float tOut; // температура снаружи
float tModule; // температура внутри метеомодуля
float tInSet; // установленное значение температуры внутри
float tOutSet; // установленное значение температуры снаружи. В текущей версии не используется. Параметр оставлен для развития
byte seconds, minutes, hours, day, date, month, year;
byte del; // счетчик большого цикла, считает декрементом малые циклы
char weekDay[4];
byte tMSB, tLSB;
float temp3231;
static byte monthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
uint32_t unixSeconds; // метка времени UNIX
uint16_t timeWorkElectro; // время работы (сек) электрокотла между сеансами обмена с сервером
uint16_t timeWorkDiesel; // время работы (сек) солярного котла между сеансами обмена с сервером
uint32_t unixSecondsStartCycle; // метка времени UNIX начала цикла между сеансами обмена с сервером
int modeWork; // режим работы метеомодуля, 0 - auto, 1 - ручное-выключено, 2 - ручное-электро, 3 - ручное-солярка, 4 - полуавтомат-электро, 5 - полуавтомат-солярка
byte typeBoiler; // тип рабочего котла, 0 - котлы не работают, 1 - электро, 2 - солярный
char statusBoiler; // статус работающего котла для сервера
char unit = '1'; // id модуля 1 в ASCII-коде
char mode; // метка режима работы метеомодуля для сервера
String message; // строка для отправки на сервер
char ans; // символ из буфера
String answerServer; // исходная строка ответа сервера
String tInSer; // строка от сервера = порог температуры внутри
String tOutSer; // строка от сервера = порог температуры снаружи
String timeSer; // строка от сервера = установка времени
char datetime[15]; // массив для установки времени модуля
void setup()
{
Serial.begin(115200); // выставляем скорость COM порта для терминала
Serial.println("Start setup()");
Serial.println("Meteo Module. Ver.13.0 Unit Number: " + String(unit));
pinMode(LED, OUTPUT); //LED flash
pinMode(LED_R, OUTPUT); //LED_R
pinMode(LED_G, OUTPUT); //LED_G
pinMode(LED_B, OUTPUT); //LED_B
// инициализация внешнего таймера
Wire.begin();
//set control register to output square wave on pin 3 at 1Hz
Wire.beginTransmission(DS3231_I2C_ADDRESS); // 104 is DS3231 device address
Wire.write(0x0E);
Wire.write(B00000000);
Wire.write(B10001000);
Wire.endTransmission();
// устанавливаем порог температуры по умолчанию
tInSet = 21;
tOutSet = -15;
// включаем наружний термометр
pinMode(PIN_DHT_OUT, INPUT_PULLUP);
dhtout.begin();
// включаем внутренний термометр
pinMode(PIN_DHT_IN, INPUT_PULLUP);
dhtin.begin();
// задаем пины управления котлами на выход
pinMode(RELAY_E, OUTPUT);
pinMode(RELAY_D, OUTPUT);
modeWork = 0; // автоматический режим
// котлы в состоянии выключено
relayElectroSwitchOff();
relayDieselSwitchOff();
timeWorkElectro = 0; // сбрасываем время работы котлов
timeWorkDiesel = 0;
unixSecondsStartCycle = 0; // сбрасываем начальное время работы котлов
typeBoiler = 0;
Serial.println("All Boilers Off");
digitalWrite(LED_G, HIGH); // включаем зеленый цвет RGB-светодиода. Исходное состояние, котлы выключены
//инициализация serial 1 is to esp8266
Serial1.begin(115200); //скорость передачи в модуль ESP8266
Serial1.setTimeout(1000);
while (!Serial1);
String startcommand = "AT+CWMODE=1"; // модуль ESP8266 в режиме клиента
Serial1.println(startcommand);
Serial.println(startcommand);
delay(2000);
del = 0; // сброс счетчика большого цикла
}
void loop()
{
Serial.print("Start loop(). ");
// диагностический вывод текущего времени
get3231Date(); // получаем текущее время
unixSeconds = timeUnix(seconds, minutes, hours, date, month, year); // UNIX-метка в секундах
Serial.print("Current datetime: ");
Serial.print(weekDay); Serial.print(", ");
if (date < 10)
Serial.print("0");
Serial.print(date, DEC); Serial.print(".");
if (month < 10)
Serial.print("0");
Serial.print(month, DEC); Serial.print(".");
Serial.print(year, DEC); Serial.print(" - ");
if (hours < 10)
Serial.print("0");
Serial.print(hours, DEC); Serial.print(":");
if (minutes < 10)
Serial.print("0");
Serial.print(minutes, DEC); Serial.print(":");
if (seconds < 10)
Serial.print("0");
Serial.println(seconds, DEC);
// сбор данных с датчиков
Serial.println("Getting temperature and himidity");
getSensors();
// подготовка сообщения для отправки на сервер
collectServerData();
// БЛОК ОБМЕНА С СЕРВЕРОМ И ИНИЦИАЛИЗАЦИИ
// отправка данных на сервер и прием управляющей строки
Serial.println("Send data to server");
connectServer();
// анализ управляющей строки и установка новых режимов
controlServer();
// БЛОК УПРАВЛЕНИЯ КОТЛАМИ В ЗАВИСИМОСТИ ОТ УСТАНОВЛЕННОГО РЕЖИМА
switch(modeWork){
case 0: // автоматический режим
Serial.println("Current Mode: Auto");
autoMode();
break;
case 1: // ручной режим
Serial.println("Manual Mode");
manualMode1();
break;
case 2: // ручной режим
Serial.println("Manual Mode");
manualMode2();
break;
case 3: // ручной режим
Serial.println("Manual Mode");
manualMode3();
break;
case 4: // полуавтоматический режим
Serial.println("Semi Auto Mode Electro");
semiAutoMode4();
break;
case 5: // полуавтоматический режим
Serial.println("Semi Auto Mode Diesel");
semiAutoMode5();
break;
}
del = LONG_CYCLE; // устанавливаем счетчик большого цикла
while (del > 0)
{
Serial.print("Start short cycle #");
Serial.println(del); // отображение номера малого цикла
mDelay(SHORT_CYCLE);
// сбор данных с датчиков
Serial.println("Getting temperature and himidity");
getSensors();
del--; // декремент счетчика в большом цикле
}
}
Как я упоминал выше, в метеомодуле предусмотрено три режима работы:
- автоматический
- полуавтоматический
- ручной
В автоматическом режиме метеомодуль по встроенным часам реального времени выбирает какой котел включить в то или иное время. В часы льготного тарифа на электроэнергию запускается электрокотел.
В первоначальном варианте системы предусматривалась возможность работы электрокотла так же в дневной период, чтобы сэкономить солярку. В этом варианте метеомодуль отслеживал продолжительность работы электрокотла днем. Если в течение часа не удавалось достичь заданной температуры в доме, то электрокотел отключался и после паузы на выбег, в работу включался солярный котел.
По опыту первой зимы такой вариант был убран. Причина заключалась в недостаточной мощности электрокотла, который не мог в относительно сильные морозы (ниже -10 градусов) обеспечить достижение заданной комфортной температуры. Поэтому было решено днем в автоматическом режиме однозначно запускать солярный котел.
Полуавтоматический режим подразумевает жесткий выбор того или иного котла с поддержанием автоматической регулировки его работы по датчикам температуры метеомодуля. Этот режим оказался полезным в нескольких случаях. Во-первых, при выходе одного котла из строя принудительно задается работа другого котла вне зависимости от времени суток. Во-вторых, в слабые морозы и оттепели можно круглосуточно включать в работу электрокотел, или, наоборот, в очень сильные морозы запускать только солярный котел.
Ручной режим я практически не использую. Он подразумевает не только выбор конкретного котла для работы, но и передачу управления им штатному выносному блоку. Другими словами, котел будет управляться заданными температурными параметрами на этом блоке. Метеомодуль в таком режиме продолжает работать только как станция мониторинга температуры и влажности.
В своем запросе к серверу метеомодуль передает пакет данных, который включает информацию о текущем состоянии котлов (какой котел выбран, работает или нет), текущее локальное время метеомодуля, продолжительность работы котлов в предшествующий пятиминутный период, текущую температуру и влажность внутри и снаружи дома. Так же в запрос включен идентификатор метеомодуля. В моем случае это излишне, но привычка проектировать под масштабирование дала о себе знать.
После отправки запроса метеомодуль ожидает ответ сервера в течение 20 секунд. Полученный ответ парсится с помощью регулярных выражений. В ответе сервера присутствует четыре параметра:
- пороговое значение температуры внутри дома
- пороговое значение температуры снаружи дома
- заданный режим работы
- время первоначальной установки для часов реального времени модуля
В текущей версии пороговое значение наружной температуры не используется. Эта возможность была предусмотрена для реализации выбора шаблонов отопления, в зависимости от температуры «за бортом». Возможно, эту функцию когда-нибудь реализую.
Последний параметр требуется довольно редко. Я его задавал лишь дважды. При первоначальном запуске модуля и после замены батарейки в модуле часов реального времени. Если временные установки не требуют изменения, то этот параметр равен нулю.
После разбора ответа от сервера, обнуляются текущие счетчики времени работы котлов. Ведь предыдущее значение уже было отправлено на сервер. При сбросе учитывается время паузы на ожидание ответа от сервера.
Надо заметить, что передаваемое время работы котла имеет оценочное значение. По этому параметру нельзя судит, скажем, о потребленной электроэнергии. Это связано с особенностями работы котлов отопления. Например, при достижении температуры в котле 80 градусов происходит его выключение, но продолжает работать циркулярный насос. При снижении температуры теплоносителя до 60 градусов, котел снова включается в работу. Метеомодуль лишь измеряет суммарное время, которое потребовалось котлу для достижения температурного порога внутри дома.
После достижения заданной температуры котел отключается, а метеомодуль продолжает с периодичностью 30 секунд считывать температурные показатели. При снижении температуры более чем на 0,5 градуса, котел отопления вновь включается в работу. Такая величина гистерезиса была подобрана опытным путем, с учетом инерционности работы системы отопления.
Для визуальной индикации работоспособности метеомодуля в подпрограмму задержки между циклами измерения температуры, добавлено мигание встроенным светодиодом.
Хочу отметить, что выбор режима работы котла происходит в конце пятиминутного периода. При первоначальном включении модуля или при его перезагрузке по умолчанию устанавливается автоматический режим.
Реализация
Для воплощения идеи я использовал то, что оказалось под рукой. Было решено построить метеомодуль с применением модулей Arduino. В качестве процессорной платы была взята Mega 2560, оставшаяся от предыдущих экспериментов. Эта плата заведомо избыточна для данной задачи, но она была в наличии. К тому же к ней был шилд макетирования, на котором разместились почти все остальные модули. Это часы реального времени DS3231 и WiFi-модуль ESP8266(01). Был куплен блок коммутации с двумя реле для раздельного управления электрическим и солярным котлами.
В качестве источника питания использован имевшийся компьютерный блок питания. Как известно, в таком блоке достаточно широкий выбор вторичного питающего напряжения. Там есть +5В и, что особенно важно при работе с WiFi-модулем ESP8266, +3,3В. К тому же эти блоки очень надежны, принимая во внимание непрерывный характер работы метеомодуля.
На рисунке представлена схема коммутации плат. Принципиальная схема не рисовалась в виду ее очевидности. На рисунке есть RGB-светодиод для визуальной индикации режимов работы метеомодуля. Зеленый цвет показывает, что котлы выключены, красный означает работу солярного котла, голубой – электрического. У меня под рукой не оказалось резисторов на 220 Ом, поэтому RGB-светодиод был подключен напрямую к выходам платы, без токоограничивающих резисторов. Каюсь, был не прав, но шел на риск осознанно. Ток потребления каждого вывода светодиода составляет всего 20 мА, выход платы позволяет подключать до 40 мА. За три года эксплуатации пока проблем не было.
В качестве датчиков температуры были использованы DHT21 (AM2301). Первоначально для измерения температуры внутри дома использовал датчик DHT11, но у него очень плохая точность измерения и, по невыясненной причине, библиотека DTH.h некорректно работала при использовании в схеме двух разных типов датчиков. Но так как замена DHT11 в силу его чрезмерной погрешности была очевидна, то я не стал разбираться с проблемой библиотеки.
Цифры в квадратиках означают номера проводов, подключающие внешние устройства к основной плате.
Вся схема была собрана в навесном металлическом щитке, используемом для монтажа электропроводки. Выбор такого корпуса так же был связан с тем, что имелось под рукой.
Но тут меня ожидал вполне предсказуемый сюрприз. При полностью закрытой дверце корпус щитка экранировал WiFi сигнал. Пришлось дверцу оставлять приоткрытой, так как не было желания искать другой подходящий корпус и все заново перемонтировать. Вот и живу уже три года с приоткрытой дверцей.
Сервер управления
Вебсервер, используемый для мониторинга и управления написан на чистом PHP и имеет адаптивную верстку. Первоначально была задумка написать приложение для Андроид, но от этой идеи отказался, так как все равно сервер был бы необходим.
После авторизации становятся доступны несколько страниц с информацией. Это текущее состояние системы по последнему полученному запросу от метеомодуля, таблица значений в текущем часе и графическое представление сводной информации за произвольный период времени. Так же есть страница с выбором настроек для управления метеомодулем.
На момент написания статьи метеомодуль был уже отключен, ведь отопительный сезон завершился. Поэтому все параметры на главной странице сайта актуальны на момент выключения. Внимательный читатель заметит, что это было 2 мая.
В качестве примера графиков приведены значения на 25 января 2018 года. Гистограммы показывают время работы котлов.
Страница установки параметров
Как я уже упоминал, это решение для мониторинга и управления системой отопления частного дома уже отработало три отопительных сезона. За это время было всего два зависания, вызванных долговременным пропаданием канала к Интернет. Причем зависал не весь метеомодуль, а только WiFi-модуль ESP8266.
В целом, функционал системы меня полностью устраивает, но учитывая явную избыточность примененной платформы, подумываю о его расширении.
Автор: AlexeyVPolunin