Тут применены пару модулей из китая, это модуль сети и блок с реле для ардуино на 5 вольт. На монтажке смонтирован микроконтроллер mega328 и опторазвязки на входы, для того чтоб не попалить ничего. В итоге получилось устройство на 7 входов и 7 выходов. По себестоимости рублей 600-700.
Очень много времени заняло написание и отладка веб-интерфейса. Давно этим не занимался, пришлось очень многое вспоминать. Сначала хотел приспособить базу на sqlite для того чтоб можно было использовать двойные и тройные нажатия на выключатели, однако возникли сложности, все это плохо работало, и в итоге все сократилось до одного скрипта, пары файлов на html и javascript. Вот как это выглядит:
Здесь картинка с видеонаблюдения перед входом, для ее встраивания на компе поток rtsp с регистратора преобразуется с помощью ffmpeg по статье с хабра [2]. Под ней расположены кнопки управления светом, пока только в проходной комнате, где я делаю ремонт, и на улице. Кнопки на css я взял на просторах интернета [3]. Далее я расположил погодный информер с гисметео [4].
Выключатели применил Этюд от Шнайдер Электрик [5], можно применять и обычные переключатели Этюд, только надо вставлять возвратную пружинку под рычажок. Мне выключателей не хватило, пришлось один переделать - подошла половинка пружинки от старой зажигалки.
В планах прикрутить графики температуры, датчики на ESP-01 заказаны и ползут на оленях из китая. Так что возможно будет продолжение...
Список того что использовано:
[1] Умный дом своими руками. Там схема и прошивка устройства.
[2] Видео с камеры наблюдения на сайте бесплатно и без смс
[4] Конструктор погодных информеров
Если кому интересно, куски кода:<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width"> <link rel="stylesheet" href="/res/slider.css"> <link rel="stylesheet" type="text/css" href="https://ost1.gismeteo.ru/assets/flat-ui/legacy/css/informer.min.css"> <script type="text/javascript" src="/res/a.js"></script> <script type="text/javascript" src="/res/hls.min.js"></script> <script langauge="javascript"> window.setInterval("refreshDiv()", 15000); function refreshDiv(){ ajaxLoad('/atm.tcl?now=0','perekl'); } </script> <title>Home, sweet home</title> </head> <body> <video id="video"></video> <script> if(Hls.isSupported()) { var video = document.getElementById('video'); var hls = new Hls(); hls.loadSource('/index.m3u8'); hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED,function() { video.play(); }); } </script> <div id="perekl"></div> <input type="button" onclick="ajaxLoad('/atm.tcl?now=1','perekl');" value="Выключить все" style="margin-bottom: 15px; font-size: large;" > <script langauge="javascript"> ajaxLoad('/atm.tcl?now=0','perekl'); </script> <!-- Gismeteo informer START --> ... здесь информер от гисметео ... <!-- Gismeteo informer END --> </body></html>a.js кусок для ajax запросов:
// here we define global variable var ajaxdestination=""; function ajaxLoad(what,where) { // get data from source (what) try { xmlhttp = window.XMLHttpRequest?new XMLHttpRequest(): new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { /* do nothing */ } // document.getElementById(where).innerHTML =""; // we are defining the destination DIV id, must be stored in global variable (ajaxdestination) ajaxdestination=where; xmlhttp.onreadystatechange = triggered; // when request finished, call the function to put result to destination DIV xmlhttp.open("GET", what); xmlhttp.send(null); return false; } function triggered() { // put data returned by requested URL to selected DIV if (xmlhttp.readyState == 4) if (xmlhttp.status == 200) document.getElementById(ajaxdestination).innerHTML =xmlhttp.responseText; else document.getElementById(ajaxdestination).innerHTML = xmlhttp.status+' '+xmlhttp.statusText; }Скрипт atm.tcl на TCL:
#! /usr/bin/tclsh #source res/config.inc set ip 192.168.0.14 set pass sec # названия входных портов set portin {"Вход" "Комната" "" "" "" "" "Выключить все" } # команды при нажатии кнопок set cmd {"8:2" "9:2" "8:2" "" "" "" "a:0"} # названия выходных портов, вначале пусто для входов, чтоб не править нумерацию портов set portout {"" "" "" "" "" "" "" "" "Улица свет" "Комната свет" "" "" "" ""} lappend auto_path [pwd]/lib/ package require html2 package require ncgi namespace import ::html2::* set qer [ncgi::parse] ncgi::importAll close stderr set stderr [open /home/tcl_error.log a] # Послать команду атмеге proc sendcmd c { #puts stderr "http://$::ip/$::pass/?cmd=$c" set res [catch {set a [exec wget -q -O - http://$::ip/$::pass/?cmd=$c]}] if {$res != 0} { puts stderr "Нет связи" exit } return $a } # пингаем атмегу proc ping {} { set res [catch {exec ping -c1 $::ip}] if {$res != 0} { ncgi::header puts "No ping $::ip" puts stderr "Нет пинга" exit } } #ping #puts stderr $qer ncgi::header switch -glob $qer { "pt cnt*" { # обработка нажатий кнопок puts [lindex $cmd $pt] } "all" { # команда на все выходы 0 1 или 2 sendcmd "a:$all" } "toggle" { # переключение выхода sendcmd "$toggle:2" } "now" { # ajax вывод переключателей if {$now == 1} {sendcmd "a:0"} set i -1 foreach po $portout { incr i if {$po ne ""} { p { label -class "b-mail-switch" { set st "" if {[sendcmd get&pt=$i] eq "OFF"} {set st "checked"} puts "<input name='port$i' type='checkbox' onchange=\"ajaxLoad('/atm.tcl?toggle=$i','pust');\" class='b-mail-switch__checkbox' $st>" span -class "b-mail-switch__input" { span -class "b-mail-switch__inner" { span -class "b-mail-switch__state b-mail-switch__state_left" span -class "b-mail-switch__state b-mail-switch__state_right" } } } span -style "margin-left: 15px; font-size: large;" {puts [lindex $portout $i]} } } } } } close stderr