«Умный дом» на Arduino для бытовки
Ничего необычного, просто очередной контроллер на Arduino. С датчиками, релюшками и веб-страничкой. Но и со своими особенностями и вполне конкретным практическим применением, хоть и довольно банальным — удалённое включение электроотопления на даче.
Ключевые особенности:
- Датчик параметров электросети — счётчик электроэнергии «Нева» по RS-485;
- Удалённая модификация web-страницы управления, лежащей на SD-карте;
- Надёжное включение группы датчиков температуры Dallas на длинных линиях;
- Графики изменения параметров без привлечения облачных сервисов;
- Отказоустойчивые решения (внешний вотчдог, ИБП, авторестарт роутера);
- Защита изернет-шилда от зависаний при помехах от силовых реле;
- Законченная конструкция в корпусе на DIN-рейку.
Делал я эту систему не спеша, в качестве развлечения, время от времени забрасывая проект на месяцы… От идеи до реализации прошло каких-то полтора года :)
Так как до этого вообще не имел дела с микроконтроллерами, то начал со стартового наборчика с алиэкспресса, поигрался со светодиодиками, кнопочками и датчиками, понял примерно что к чему, и начал сооружать контроллер для удалённого управления и мониторинга. Ну не прям «умный дом» конечно, но что-то около того.
Практическая цель была поставлена сначала одна — удалённо включать отопление на даче. Дом я пока построить не успел (руки не дошли, ага), но зато у меня есть замечательная комфортабельная дачная бытовка в полном фарше, отапливаемая электроконвекторами. Заранее прогреть к вечеру пятницы остывшую за неделю бытовку — весьма заманчивая возможность в холодное время года.
Однако задача по удалённому управлению нагрузкой оказалась совсем тривиальной. Ардуино плюс изернет-шилд, готовый скетч из интернета, и релюшки уже щёлкают от галочек на веб-страничке. Практическая цель была достигнута как-то очень быстро и просто. И это было скучно. Хотелось чего-то более интересного.
Поставил себе вторую практическую цель — мониторить текущее потребление в дачной электросети. Приобрёл простенькие датчики тока для тестов, поигрался. Работало всё хорошо, но для боевой работы этот вариант не годился. Датчиков мощнее, чем на 5А, мне найти не удалось, а мне надо было на 25А минимум.
И появилась у меня мысль использовать в качестве датчика электросети счётчик электроэнергии. И это была прекрасная мысль! И создал я устройство такое, и увидел, что это хорошо! Не без трудностей, но задачу эту я выполнил в итоге превосходно, о чём с чувством глубокого удовлетворения и поведаю ниже:).
Функционал
Первая (и пока последняя) боевая версия контроллера «умной бытовки» обладает таким функционалом:
- Удалённое ручное управление включением силовых реле через браузер и контроль их текущего состояния;
- Удалённый мониторинг параметров трёхфазной электросети (напряжение, ток, частота и ещё много всякой ненужной фигни, которую я потом отключил);
- Удалённый мониторинг показаний двухтарифного счётчика электроэнергии;
- Удалённый мониторинг группы датчиков температуры;
- Логирование данных и регистрация событий на карту памяти;
- Отображение в браузере данных со всех датчиков за выбранные сутки в виде графиков.
Идеология
Принцип построения всей системы — без использования сторонних облачных сервисов и серверов сбора и отображения данных. Это не хорошо и не плохо. На начальном этапе «умнодомостроения» такая схема и достаточна, и проста. Хотя и накладывает определённые ограничения использования.
Ардуино с изернет-шилдом является единственным веб-сервером в системе. Веб-страница с интерфейсом управления хранится на карте памяти изернет-шилда и передаётся в браузер при обращении его к заданному IP-адресу. Дальнейшее взаимодействие интерфейса пользователя с Ардуино осуществляется посредством java-скриптов, тела которых не встроены в веб-страницу, а хранятся на моём домашнем файловом хранилище с доступом в интернет. Такой подход и уменьшает вес веб-страницы, что ускоряет её чтение с SD-карты, и позволяет более быстро и просто модифицировать код скриптов.
Чтение лога данных для отображения графиков производится с карты памяти по запросу (нажатие кнопки в браузере). Данные отображаются только в текущей сессии страницы браузера, никуда не сохраняются, и при перезагрузке страницы их необходимо вычитывать вновь. Это минус идеологии, так как чтение с карты памяти довольно медленное, и получение суточного объёма данных может занимать минуту-две (была мысль переработать формат хранения данных чтобы уменьшить их объём и ускорить чтение).
Текущие значения датчиков отображаются на странице и обновляются в реальном времени с частотой цикла loop, которая у меня равна примерно 1 Гц.
Никакой «умности» в алгоритме сейчас не заложено. Буду ли вразумлять алгоритм в будущем — не знаю, меня пока нынешний вариант полностью устраивает.
Топология сети
На даче мобильный интернет Yota (usb-модем + wifi-роутер). Фиксированного IP-адреса нет, и возможности его получить тоже нет, даже за деньги. Да и даже динамического белого IP-адреса нет, поэтому DynDNS не применить. Только серый IP-адрес внутренней сети Yota. IPv6 Yota не поддерживает (по крайней мере на 2017 год это так). Поэтому способ достучаться до дачного роутера извне я нашёл только один — VPN.
Дома (в городе) проводной интернет с белым фиксированным IP-адресом. Роутер, за ним сетевое хранилище. На этом домашнем роутере поднят VPN-сервер. Дачный же роутер настроен на поднятие VPN-туннеля по PPTP к домашнему роутеру.
Контроллер на Ардуино подключен к LAN-порту дачного роутера и сидит за NAT, к нему проброшен 80-ый порт. Таким образом, получить доступ к Ардуино я могу только из своей VPN. Соответственно, домашняя локальная сеть и VPN у меня это два сегмента одной подсети, доступ там прямой. На своём рабочем офисном компе и на смартфоне настроил VPN-подключение и также получил доступ к Ардуино. Не очень удобно пользоваться, но работает. Да и безопасность относительная обеспечена — без авторизации в моей VPN никто чужой попасть на страницу управления контроллером не может.
Узкое место — VPN-туннель до дачного роутера. Периодически падает. Причем рвётся именно связь по PPTP, доступ в интернет при этом остаётся. И самое противное, что при обрыве PPTP-соединения оно больше само не поднимается. Не спасает даже перезагрузка роутера. Только полный перезапуск его по питанию вместе с USB-модемом, и то не сразу. Нужно выключить, подождать минут 10, включить снова. Повезёт — хорошо, нет — следующая итерация. Причина, если верить техподдержке Zyxel, в блокировке пакетов PPTP сотовым оператором, так как одна сторона правильно шлёт, вторая правильно слушает, но данные не доходят (на обоих концах VPN-туннеля у меня роутеры Zyxel). Виновата вроде бы Yota, но добиться от них чего-то вразумительного невозможно. А может и не Yota.
WatchDog
Для обеспечения относительно бесперебойной работы VPN использую костыль программный вотчдог — дачный роутер питается через силовое реле, управляемое Ардуиной, и как только VPN-сервер перестаёт пинговаться (пингует тоже Ардуина), то через 10 мин питание с роутера снимается на 10 мин, затем роутер вновь включается и ожидается установка PPTP-соединения в течение 5-ти минут. Если связи нет — следующая итерация. Иногда это соединение устойчиво работает неделями и даже месяцами, а иногда начинает рваться чуть ли ни ежедневно. Иного пути решения проблемы пока не видится совсем. Как я уже говорил, IPv6 Yota не поддерживает, белый IP физикам не даёт и не продаёт, другого провайдера с нормальными тарифами и безусловно безлимитным трификом нет. Впрочем, это решение хоть и костыльное, но задачу свою выполняет очень хорошо. Теперь у меня связь есть всегда, за очень редкими исключениями, когда я попадаю на момент перезагрузки.
Кроме программного я также реализовал и аппаратный вотчдог. На всякий случай. Контроллер должен работать неделями и даже иногда месяцами в отсутствии меня, и не зависать. Как себя поведёт всё это хозяйство в большие минуса мне было неизвестно, поэтому подстраховался. Встроенный в Атмегу вотчдог меня не устроил тем, что совсем не работал на Ардуино Мега «из коробки», и чтобы заставить его работать нужно было основательно потрахаться. Эта проблема хорошо описана тут. Кроме того, максимальный интервал у него 8 секунд, что мне показалось недостаточным. Поэтому я применил специализированную микросхему сторожевого таймера TPL5000DGST, интервал которого задаётся комбинацией из трёх пинов и может достигать 64 секунд, что меня вполне устроило. Для этой микросхемы приобрел маленькую макетку, спаял и закрепил на разъёме с IO-портами Ардуины (фотка будет ниже в разделе про сборку). Тесты показали надёжную работу этого вотчдога, и мне было интересно, как часто он будет срабатывать в реальности. Для этого я добавил в код программы переменную, где хранится время и дата запуска программы, и эта информация выводится на веб-страничку управления. Практика показала, что в отличие от VPN, контроллер работает бесперебойно долго, и сторожевой таймер пока так ни разу и не пригодился (пока писал статью, вотчдог таки сработал впервые за всё время, всё же не зря сделал). Время непрерывной работы достигало 3-х с лишним месяцев и моглы бы быть и больше, если бы мне не нужно было время от времени что-то подкорячить в коде и перепрошить контроллер.
Датчик параметров электросети — электросчётчик «Нева»
Как я уже упоминал выше, в качестве датчика параметров электросети, на мой взгляд, нельзя найти ничего лучше и точнее, чем современный цифровой счётчик электроэнергии с интерфейсом внешнего доступа к данным. Я выбрал счётчик Нева питерской компании Тайпит с интерфейсом RS-485.
Подчеркну, что электросчётчик с постоянно подключенными к нему проводами 485-го интерфейса официально использовать в качестве прибора учёта не разрешено. Поэтому в дачной электросети я ставлю два счётчика последовательно. Первый — официальный прибор учёта электроэнергии, опломбированный и зарегистрированный, расположен в уличном вводном щитке. Второй — мой датчик параметров электросети, неопломбированный и неучтённый, на который энергосбытовой компании уже наплевать, так как её не волнует всё то, что установлено после прибора учёта. Этот электросчётчик расположен уже в щитке внутри бытовки, и в этом же щитке находится и контроллер на Ардуино, но, об этом чуть позже.
На даче у меня трёхфазная электросеть. В городе я имею возможность отлаживаться только на однофазной. Поэтому приобрёл два счётчика, трёхфазный Нева МТ-324 и однофазный Нева МТ-124. Первоначальную отладку контроллера делал на столе на трёхфазном счётчике с подключенной одной фазой, затем установил его штатно в дачный щиток в боевой режим работы. Отладку последующих модификаций софта делал уже на более дешёвом однофазном счётчике на столе:
Для подключения счётчика к Ардуино требуется преобразователь уровней RS-485 в TTL:
Также для работы со счётчиком нужен свободный последовательный порт на Ардуино. К сожалению, Arduino Uno имеет только один последовательный порт, заняв который под счётчик пришлось бы лишиться отладочного вывода текстовой информации (монитора порта), а без этого писать скетч не реально. Поэтому использую Arduino Mega, у которой сериальных портов несколько. Можно было бы реализовать второй последовательный порт софтово через цифровые порты Ардуино, но подходящей библиотеки с возможностью изменения других настроек порта, кроме скорости, я не нашёл. А настройки порта счётчика отличны от дефолтных: битрейт 9600, 7 бит данных, 1 стоповый бит, контроль чётности — event. Стандартный объект Serial в Ардуино, к счастью, позволяет выполнить эти настройки.
Протокол обмена со счётчиком получить было относительно несложно — производитель счётчика в открытом доступе имеет программу чтения параметров под Windows, в которой тоже есть монитор порта. Для подключения счётчика к компьютеру использовал преобразователь интерфейсов MOXA 232/432/485USB. Некоторое время на визуальный анализ посылок — и основные команды я вычленил.
Однако мне этого показалось недостаточно, и я обратился по e-mail к производителю. После месячной переписки с компанией Тайпит мне наконец удалось заполучить полный перечень команд с интерпретацией:
Кодировка параметров MT3ХX E4S
Кодировка параметров НЕВА MT124 AS OP (E4P)
Дальше дело техники — написать под Ардуино цикл опроса параметров и вывести их для начала в монитор порта. Время одного цикла получилось около секунды. Однако в конечном варианте проекта с логированием данных на флэшку и опросом температурных датчиков это время выросло до 4-x секунд. Это меня уже совершенно не устраивало и пришлось погрузиться в оптимизацию. В итоге я вновь добился секундного интервала без потери функциональности. К слову, скетч я переписывал с нуля два или три раза, пока не нашёл правильную архитектуру и экономные алгоритмы.
Программная реализация обмена со счётчиком
Код выдран из контекста моего большого рабочего скетча. Скомпилирован, но в таком виде я его никогда не запускал. Привожу только для примера, а не как готовую рабочую программу. Хотя в теории всё должно работать именно в таком виде.
Код написан для двух типов счётчиков одновременно, однофазного МТ-124 и трёхфазного МТ-324. Выбор типа счётчика происходит в программе автоматически по ответному слову инициализирующей команды.
Код привожу как есть, без прекрас и дополнительных комментариев кроме тех, что писал сам для себя. И да, я не программист, и даже не учусь на него, поэтому пинать меня за качество кода не следует, но поучить как надо кодить можно: EnergyMeterNeva.ino
Огромный дополнительный плюс счётчика электроэнергии — это надёжные и точные часы реального времени. Мне не пришлось обеспечивать систему дополнительным модулем, который ещё нужно найти не просто абы какой, а качественный. Текущее время с точностью до секунды я получаю со счётчика среди прочих данных. Да, относительно атомного времени время счётчика немного сдвинуто (несколько секунд), уж не знаю с чем это связано, с некачественной заводской установкой или ещё чем, но точность хода при этом отличная, просто с небольшим смещением.
В редкие моменты, когда электропитание на даче отключается и счётчик становится недоступен, текущее время я получаю от внутреннего таймера Ардуины. Когда электросчётчик работает и его данные доступны, внутренний таймер Ардуины я перепрописываю значением со счётчика на каждом витке loop. Когда счётчик отваливается — текущее время продолжает тикать на таймере Ардуины.
Помимо чтения параметров счётчик, естественно, можно и программировать. То есть интерфейс работает и на чтение, и на запись. Однако я с такими сложностями добивался протокола команд чтения, что о просьбе протокола записи я даже не заикнулся производителю. Во-первых, мне это было ни к чему, разве что только время чуть сдвинуть. Во-вторых, подозреваю, что эти данные уже не являются открытыми, так как могут быть использованы в мошеннических целях.
Температурные датчики
Тест температурных датчиков с помощью скетча-примера я уже проводил отдельно раньше. Теперь оставалось только встроить их опрос в основной проект. Это не составило никакого труда. Все девять имеющихся у меня датчиков работали без проблем при параллельном включении по 1-Ware. Разброс показаний между ними составил около 0.5 градуса, что показывает бессмысленность использования их на максимальной точности в 0.0625 градуса. Датчики для теста собрал в пачку и завернул в несколько слоёв пупырчатого полиэтилена. Для большей точности пачку расположил вертикально и ждал сутки для полного выравнивания температуры. Показания всех датчиков так и не оказались одинаковыми.
Однако загрублять точность конвертации температуры самих датчиков я тоже не стал. Проще округлить показания программно, а выгоды по времени опроса я бы не получил, так как придумал такой алгоритм, при котором время ожидания конвертации не является пустым бесполезным delay (750). Обычная логика работы с датчиками такая — сначала подача команды на запуск конвертации температуры, потом ожидание окончания конвертации (те самые 750 мс минимум), и уже затем вычитывание данных. Я сделал всё наоборот, что позволило мне исключить пустой интервал ожидания — сначала вычитываю данные из датчиков, а потом сразу запускаю конвертацию. И пока весь остальной код в цикле LOOP отработает, данные как раз успевают подготовиться для вычитывания на следующем витке. По времени данные с датчиков я получаю в этом случае чуть позже — цикл LOOP занимает примерно 1–1.5 секунды, но это совершенно не критично.
Иногда со всех датчиков я получал данные »85» или »0». Что это за косяк, я так и не понял, поэтому сделал в коде проверку и исключил попадание таких данных в результат. Ещё обнаружился косяк у одного из датчиков — он не держал настройки при отключении питания. То ли флэшка его внутренняя дохлая, то ли ещё что. Поэтому в сетапе прописал настройку датчиков, и теперь по включению питания (если оно таки пропадает) все датчики гаранитрованно настроены.
Адреса конкретных датчиков я получил с помощью скетча-примера, где-то нарытого и немного мной модифицированного: DS18×20_Temperature.ino
После чего адреса я забил константами в массив и в основной программе обращался к датчикам уже сразу по их адресам: TempSensors_DS18B20.ino
Для правильной работы датчиков на шине 1-Wire требуется установить подтяжечный резистор 4.7 кОм между линией данных и питанием. Мне было удобно припаять между пинами клеммной колодки SMD-резистор, но нашёл я у себя в подходящем корпусе только 5.1 кОм, его и поставил (он виден на фотке в разделе про сборку на нижней стороне платы). Работает всё хорошо.
Датчики температуры у меня подключены электрически параллельно на одной длинной линии (+5, gnd и data), все 9 штук, но хитро. Физически кабели витой пары подключены звездой для удобства разводки датчиков по объекту. В каждом плече кабеля я использую две пары. Одна пара — это питание датчика. Вторая пара — это линия данных, которая идёт по одному проводу к датчику и возвращается от него же обратно по второму проводу. Таким образом получается возможным развести кабели звездой от щитка, но электрически это звезда только по питанию, а по данным это одна линия. Такой вариант подключения оказался более надёжен в работе на длинных линиях, при простом параллельном подключении было много сбоев при чтении данных. Вот эскиз такой схемы:
Полуметровые хвосты трёхпроводных кабелей самих датчиков я не укорачивал, подключил их как были, оказалось не критично.
Всего по бытовке разведено три кабеля, два — для внешних датчиков, на каждом по одному, и один для всех оставшихся семи внутренних. Эти семь внутренних датчиков подключены по той же схеме, но в пределах одного длинного кабеля и с короткими ответвлениями от него (см. нижнюю Т-образную конфигурацию на эскизе). Где-то хватило штатного полуметрового хвоста датчика для ответвления, где-то ответвлял с помощью такой же витой пары.
Двухпроводную схему включения датчиков, с т.н. паразитным питанием (когда датчики получают питание по линии данных) я использовать не стал потому что…, а зачем? Кабели я в любом случае брал бы четырёхпарные, просто потому что они у меня были. Проблем с прокладкой кабеля по бытовке никаких. Да и схема с паразитным питанием критична к величине потребляемого тока в некоторых режимах работы, могли возникнуть ненужные проблемы.
Общая длина витой пары по бытовке составила примерно 25 метров. Куски для внешних датчиков — 5 и 10 метров, и десятиметровый внутренний кусок с ответвлениями на семь датчиков. Всё работает почти идеально. Лишь изредка проскакивают прочерки вместо значений температуры. Это значит, что данные с конкретного датчика были прочитаны некорректно. Но случается это настолько редко (замечаю раз в месяц может), что не доставляет никаких проблем.
Удалённый доступ
Для удалённого доступа к Ардуино был куплен Ethernet shield. При наличии встроенной библиотеки работа с ним, как и со всем остальным в Ардуино, оказалась довольно проста.
Функционально схема работы у меня такая. На Ардуино поднят веб-сервер, который при обращении к нему клиента (браузера) генерирует веб-страничку с различной информацией. Автообновление данных на страничке реализуется посредством яваскрипта, опрашивающего по таймеру сервер.
Также страничка имеет набор контролов для управления исполнительными механизмами, подключенными к Ардуино — силовыми реле, которые коммутируют нагрузку — электрообогреватели и освещение.
С дизайном веб-странички я не парился, тем более что был необходим минимальный объём текстовых данных для её более быстрой загрузки, поэтому самый примитивный html и всё:
В html-код я вместо данных встроил теги, которые на лету подменяются реальными данными при генерации странички сервером. При автообновлении данных по запросу яваскрипта они передаются в браузер уже непосредственно из микроконтроллера в формате JSON.
Код страничики лежит в файле на карте памяти и загружается с неё при обращении к серверу. Для более быстрой и удобной модификации кода странички я встроил механизм её обновления в неё саму. Внизу, под блоком основных контролов есть текстовое поле и кнопка Отправить. В текстовое поле копирую новый html-код, жму кнопку, после чего java-скрипт производит отправку данных на веб-сервер контроллера, который сохраняет его сначала в буферный файл. Если передача произошла успешно, то основной файл подменяется буферным, страничка автоматически обновляется. Всё. Изменения приняты.
Привожу фрагменты кода моей реализации этого механизма.
В html-страничке встраиваем форму:
По кнопке «Отправить» запускается следующий ява-скрипт: send_HTM.js
В скетче в функции обработки запросов веб-сервера по префиксам в запросе 'CONTROL.HTM' (старт отправки файла), 'htmlineN' (отправка строки №) и 'END_CONTROL.HTM' (конец отправки файла) определяем дальнейшие действия:
File acceptHtmFile; ................ if (fl_accept_htm) // префикс 'CONTROL.HTM' { SD.remove(CTRL_HTM); acceptHtmFile = SD.open(CTRL_HTM, FILE_WRITE); // открываем файл на запись if (!acceptHtmFile) // если файл открыть не удалось - ничего не пишем { #ifdef DEBUG_SD Serial.println("SD-card not found"); #endif client.print("FAIL"); client.stop(); } else client.print("OK_OPEN_FILE"); acceptHtmMode = true; break; } if (fl_htmline) // префикс 'htmlineN' { int b = acceptHtmFile.println(tag); if (b == 0) { client.print("FAIL"); acceptHtmMode = false; cntHtmModeIteration = 0; } else { client.print("OK"); } cntHtmModeIteration = 0; break; } if (fl_endhtm) // префикс 'END_CONTROL.HTM' { SD.remove(CONTROL_HTM); acceptHtmFile.close(); File htmlFile = SD.open(CONTROL_HTM, FILE_WRITE); // открываем на запись acceptHtmFile = SD.open(CTRL_HTM); // открываем на чтение for (int i = 0; i < acceptHtmFile.size(); i++) { digitalWrite(PIN_WATCHDOG_DONE, 1); htmlFile.write(acceptHtmFile.read()); digitalWrite(PIN_WATCHDOG_DONE, 0); } acceptHtmFile.close(); htmlFile.close(); client.print("OK_CLOSE_FILE"); acceptHtmMode = false; cntHtmModeIteration = 0; break; }
Дефайны CONTROL_HTM и CTRL_HTM здесь это имена html-файлов. Первый — основной файл, второй — буферный. В массиве чаров tag лежит текст принятой строки, выделенный из запроса. Логика такова: при приёме данных они пишутся в буферный файл, по окончании приёма буферный файл переписывается в основной. Как просто переименовать файлы я так и не смог понять, в стандартной библиотеке SD такой функции нет, поэтому тупое посимвольное копирование, отнимающее кучу времени.
Было бы удобным код веб-странички управления хранить не на карте памяти контроллера, а на клиентской машине, или загружать с какого-нибудь внешнего ресурса. Но запрет на кроссдоменные запросы не позволяет этого сделать. Яваскрипты могут отправлять свои запросы только тому северу, с которого были загружены сами. Тела яваскриптов при этом могут подгружаться откуда угодно, важно лишь только откуда была загружена страница с их вызовом.
Логирование данных
Ethernet shield имеет на борту слот карты памяти micro-SD. Именно из-за его наличия я и решил писать данные в лог-файлы. Для работы с картой памяти также имеется встроенная библиотека, и управлять записью-чтением файлов с ней вообще элементарно.
Для экономии объёма данных алгоритм логирования я построил так, что запись происходит только тогда, когда данные изменяются более чем на заданный порог. Для температуры это 0.1°, для напряжения это 0.2В. В один файл пишутся данные за одни сутки. В ноль часов создаётся новый файл. Формат хранения я выбрал обычный текстовый, с разделителями, чтобы можно было быстро контролировать содержимое файлов при отладке, и была бы простая возможность загрузки в Excel.
Конструктивные ограничения не позволяют удобно вставлять-вынимать карту памяти, поэтому я использовал карту большого объема. По моим расчётам, она будет заполняться в течение нескольких лет, после чего нужно будет подразобрать корпус, вынуть карту памяти и очистить её.
Приводить код логирования смысла не вижу, там всё совершенно тривиально — банальная запись текста в файл. Да и размазан этот код по всему скетчу (логируются не только параметры датчиков, но ещё и разнообразные разовые события), вычленить затруднительно.
Графики
В качестве движка для построения графиков я использую очень гибко настраиваемую javascript-библиотеку визуализации данных amchart. Библиотека бесплатная и доступна для скачивания и автономного использования. Эту библиотеку я также расположил на своём сетевом хранилище с постоянным доступом в интернет. Подключить и использовать её с дефолтными настройками не сложно, однако чтобы получить в итоге тот вид, который мне был нужен, пришлось немало повозиться. Помогло огромное количество примеров на сайте и наличие подробной документации.
Для примера приведу свой яваскрипт отрисовки графиков. Сам по себе он бесполезен, так как работает только в совокупности и с веб-сервером, и с html-страницей, и, возможно, завязан на другие скрипты (дело было давно, всех деталей уже не помню). Но настройки внешнего вида моих графиков содержаться именно в нём и почерпнуть их оттуда можно: get_log.js
Большим преимуществом библиотеки amchart является то, что она умеет отрисовывать правильные графики по «рваным» данным. Как я уже упоминал выше, в лог я сохраняю данные только при их изменении. То есть это происходит асинхронно и хаотично. Новых данных может не быть несколько минут, а потом за несколько секунд они поменяются несколько раз. Соответственно записи в логе идут с произвольными интервалами времени. Amchart при отрисовке учитывает это самостоятельно, у меня нет необходимости интерполировать данные перед отрисовкой. Я просто отправляю массив данных как есть, и вижу красивый равномерный во времени график.
Недостаток этой библиотеки я обнаружил только один — она не умеет (ну или я так и не понял как) по-человечески обновлять графики в реальном времени. Можно добавить новые данные к уже имеющимся, но перерисовка производится каждый раз полностью всего массива данных, и это сильно подтормаживает работу браузера. Впрочем, сама идеология чтения из Ардуины данных для отрисовки по запросу из браузера ущербна своей неоптимальностью, поэтому бороться за быстрое обновление в реальном времени смысла не было никакого.
Правильным решением было бы организовать отдельный сервер хранения и визуализации данных, куда с Ардуины в реальном времени данные капали бы по чуть-чуть и складировались в БД, и откуда бы они могли быстро быть отданы пользователю в браузер для визуализации.
Сейчас графики выглядят так (на примере дня, когда в бытовке никого нет и, соответственно, нет никакого энергопотребления). Когда возникают данные тока, масштаб автоматически устанавливается так, чтобы всё красиво влезало, и на вертикальной оси возникают и значения уровней тока:
Графики отображаются на той же самой страничке, где происходит управление, прямо ниже основного блока контролов.
Полный комплект исходников проекта не привожу намеренно по нескольким причинам:
- Его нельзя запустить как есть в любой другой сети, кроме моей, так как я не пытался сделать проект портабельным, и он жёстко завязан на мои адреса и мою топологию сети.
- Я уверен, что общая идеология проекта страдает массой разнообразных проблем, так как это моя первая попытка в той области, в которой я разбираюсь плохо. Поэтому не предлагаю никому весь проект к повторению именно в таком виде. Я поделился лишь теми моментами, в которых более менее уверен.
- Проект делался давно и долго, и я уже никогда не вспомню всех деталей и не смогу объяснить ряд решений. Объём скетча очень большой (по моим меркам, около 2 тыс строк), разнообразных обслуживающих ява-скриптов более десятка, принципиальную схему железа я не делал. То есть не смогу помочь консультативно по большинству вопросов.
Сборка
С самого начала я поставил себе цель — сделать законченное устройство, а не просто макет с ворохом проводочков на столе:
И сразу же решил, что устройство это я хочу разместить внутри электрощитка. Там и питание, и счётчик, и вообще, это удобно.
Для этого требовался динреечный корпус. Вначале думал разработать его и напечатать на 3D-принтере. Но, своего 3D-принтера у меня нет, а то, что печатают мои коллеги по работе на своих самосборных принтерах, меня совершенно не устраивало по качеству внешнего вида. Нашел в продаже готовые корпуса на DIN-рейку (разных размеров), выглядят хорошо, пользоваться удобно (разборные), да ещё и плата-слепыш под них специально имеется готовая.
Купил самый большой корпус, чтобы в него вместилась не только Ардуино с изернет-шилдом, но еще и реле для коммутации нагрузки:
Дальше был длительный и увлекательный процесс монтажа всей требухи в корпус. Под это дело я даже приобрёл себе замечательный паяльничек с функцией сна (имеющиеся у меня паяльники все были ещё советских времен):
Для монтажа накупил кучу всевозможных стоечек, винтиков, шайбочек и гаечек. Первая прикидочная сборка:
Для подключения проводов к верхним контактам пришлось использовать загнутые штырьки, иначе не влезало в корпус:
Изолирующие шайбы местами приходилось подрезать:
А местами изгаляться более извратно, поднимать винт на втулке, и вычурно подрезать втулку:
Для одиночной релюшки не хватило точек опоры, поэтому повисла только на двух точках:
Прикидочная сборка вместе с клеммными колодками:
Потом поделка стала постепенно обрастать проводами. Плата была использована только для разводки питания и для подключений к клеммным колодкам. Для сигнальных связей использовал провод МС-16 (мне он больше нравится), для силовых он не проходил по напряжению (до 100 В), поэтому МГТФ:
На лицевой панели закрепил светодиодики, токоограничивающие резисторы припаял прямо к ножкам светодиодов и закрыл термоусадкой:
В итоге получилась вот такая бородатая начинка:
А вот и платка с микросхемкой сторожевого таймера, приютилась в недрах моего творения, прямо над преобразователем уровней RS-485 — TTL: