Умный дом на Arduino и TL-MR3020 без Lighttpd и PHP

Это новая версия шаблона для построения простого «умного дома», сделаного на базе ардуино и роутера mr3020.
Визуально и функционально (управление пинами, считывание датчиков) он ничем не отличается от того, что было написано в предыдущих частях, за исключением диммера, который был убран из-за не корректной работы (позже будет переделан).
В прежней реализации использовался сервер Lighttpd, скриптовый язык PHP, утилита stty для настройки порта, а так же небольшой прокси-сервер ser2net, служивший посредником между сервером и ардуиной.
Если у Вас это установленно, то ничего удалять не нужно, просто пропустите установку uHTTPd.
Весь этот «программный комплекс» требовал перенесения файловой системы на флешку, что в свою очередь вынуждало использовать хаб.
Теперь для работы системы, достаточно легкого сервера uHTTPd (используется в OpenWrt для работы Web-интерфейса) и двух маленьких программ. Благодаря этому нет необходимости использовать флешку.
Как это работает?
Ардуина два раза в секунду отправляет данные роутеру (по USB или UART), на роутере программа arduread получает эти данные и записывает их в файл. Клиент подключённый к роутеру, раз в секунду считывает данные из этого файла.
Когда клиент отправляет команду (например включить свет), то её принимает другая программа — arduserver, которая слушает TCP-порт и отправляет ардуине принятые от клиента команды. Ардуина выполняет команду и продолжает отправлять данные, которые опять таки считывает arduread и записывает в файл. Клиент, при следующем запросе, читает из файла уже новые данные и они отображаются на страничке.
Функционально, система может включать/отключать устройства подключённые к ардуине и считавать информацию с датчиков.
Терминология:
Сервер — роутер или любой другой компьютер.
Клиент — устройство (телефон, планшет или компьютер), с которого Вы подключаетесь к серверу.
Soft-сервера — uHTTPd и arduserver.
Ардуина
Всё что делает ардуина — это принимает команды и два раза в секунду отсылает серверу информацию.
Заливаем скетч:
#include <DHT.h>
#include "CyberLib.h"
#define MAXMILLIS 4294967295
#define DHTPIN 9 // пин для подключения датчика
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
int hum = 110; // влажность
int thum = 110; // температура
byte d2 = 0; // флаг состояния
byte d3 = 0;
byte d4 = 0;
byte d5 = 0;
byte d6 = 0;
byte d7 = 0;
byte d8 = 0;
byte d9 = 0;
byte d10 = 0;
byte d11 = 0;
byte d12 = 0;
byte d13 = 0;
byte pir1 = 0;
byte knop1 = 0;
byte knop2 = 0;
byte knop3 = 0;
byte descript[5]; // массив
unsigned long time; // время
long timelapsed = 0;
int count=0; // счетчик минут
void setup()
{
Serial.begin(57600);
D2_Out;
D3_Out;
D4_Out;
D5_Out;
D6_Out;
D7_Out;
D8_Out;
//D9_Out; // DHT
D10_Out;
D11_Out;
D12_Out;
D13_Out;
thum = dht.readTemperature(); // считываем температуру, сначала делаем это в сетапе, так как следующее чтение будет только через минуту
hum = dht.readHumidity(); // считываем влажность
}
void loop()
{
if (Serial.available()>4) // ждём дескриптор и нужный символ
{
if (Serial.read()=='Y') // проверяем первый символ, если это 'Y', то продолжаем принимать, если нет, то выходим из цикла чтения
{
for (byte i=0; i < 5; i++)
{
descript[i] = Serial.read(); // добавляем символы в массив
}
if((descript[0] =='+') && (descript[1] =='=') && (descript[2] =='Z')) // проверяем дескриптор
{
switch (descript[3])
{
case 'o': // обновление
glavnaia(); // отправка ответа
break;
////////////// Кнопки ///////////////////
case 'A': // d2 вкл
D2_High;
d2 = 1; // флаг вкл
break;
case 'a':
D2_Low;
d2 = 0; // флаг откл
break;
case 'B': // d3
D3_High;
d3 = 1;
break;
case 'b': // d3
D3_Low;
d3 = 0;
break;
case 'C': // d4
D4_High;
d4 = 1;
break;
case 'c': // d4
D4_Low;
d4 = 0;
break;
case 'D': // d5
D5_High;
d5 = 1;
break;
case 'd': // d5
D5_Low;
d5 = 0;
break;
case 'E': // d6
D6_High;
d6 = 1;
break;
case 'e': // d6
D6_Low;
d6 = 0;
break;
case 'F': // d7
D7_High;
d7 = 1;
break;
case 'f': // d7
D7_Low;
d7 = 0;
break;
case 'G': // d8
D8_High;
d8 = 1;
break;
case 'g': // d8
D8_Low;
d8 = 0;
break;
case 'H': // d9
//D9_High; // DHT
//d9 = 1;
break;
case 'h': // d9
//D9_Low; // DHT
//d9 = 0;
break;
case 'I': // d10
D10_High;
d10 = 1;
break;
case 'i': // d10
D10_Low;
d10 = 0;
break;
case 'J': // d11
D11_High;
d11 = 1;
break;
case 'j': // d11
D11_Low;
d11 = 0;
break;
case 'K': // d12
D12_High;
d12 = 1;
break;
case 'k': // d12
D12_Low;
d12 = 0;
break;
case 'M': // d13
D13_High;
d13 = 1;
break;
case 'm': // d13
D13_Low;
d13 = 0;
break;
default:
glavnaia();
}
}
else // если дескриптор ложный, то очищаем буфер
{
for(byte i=0; i < 255; i++)
{
Serial.read();
}
}
} // конец if (Serial.read()=='Y')
} // конец чтение порта
unsigned long currtime = millis();
if(currtime > time) timelapsed = (currtime - time);
else timelapsed = (MAXMILLIS - time + currtime);
if(timelapsed >= 440) // функция будет выполняться раз в 2 минуты
{
time = currtime;
count++;
if(count == 120) thum = dht.readTemperature();
if(count == 240) hum = dht.readHumidity();
if(count > 239) count=0;
glavnaia(); // отправка данных серверу
} // конец включения по температуре
} // конец loop
void glavnaia() // отправка данных серверу
{
// сумма проверяется на сервере, на предмет целостности пакета
int summa = d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 + d10 + d11 + d12 + d13 + thum + hum + pir1 + knop1 + knop2;
Serial.print('A');
Serial.print(" ");
Serial.print(summa);
Serial.print(" ");
Serial.print(d2);
Serial.print(" ");
Serial.print(d3);
Serial.print(" ");
Serial.print(d4);
Serial.print(" ");
Serial.print(d5);
Serial.print(" ");
Serial.print(d6);
Serial.print(" ");
Serial.print(d7);
Serial.print(" ");
Serial.print(d8);
Serial.print(" ");
Serial.print(d9);
Serial.print(" ");
Serial.print(d10);
Serial.print(" ");
Serial.print(d11);
Serial.print(" ");
Serial.print(d12);
Serial.print(" ");
Serial.print(d13);
Serial.print(" ");
Serial.print(thum);
Serial.print(" ");
Serial.print(hum);
Serial.print(" ");
Serial.print(pir1); // не используется
Serial.print(" ");
Serial.print(knop1); // не используется
Serial.print(" ");
Serial.print(knop2); // не используется
Serial.print(" ");
Serial.print(knop3); // не используется
Serial.print(" ");
Serial.print(0); // не используется
Serial.print(" ");
Serial.print(0); // не используется
Serial.print(" ");
Serial.println('Z');
}
В скетче используются две библиотеки: (устанавливаются стандартным способом.)
CyberLib — для ускорения ардуины.
DHT — для датчика влажности и температуры (подключение).
Ардуина получает команду включить/выключить пин, выполняет действие и устанавливает соответствующий флаг (d2 = 1; — вкл, d2 = 0; — откл), по этим флагам клиент определяет что включено или выключено.
Сервер
Если у Вас ещё не установлена OpenWrt, то здесь, Вы найдёте исчерпывающую информацию. (переносить систему на флешку не обязательно)
На роутере будут установлены следующие программы:
uHTTPd — легковесный сервер, написанный разработчиками OpenWrt для работы на встраиваемых устройствах, он будет отдавать клиенту основной контент.
Если установлен Lighttpd, то устанавливать uHTTPd не нужно.
arduread — получает данные присланные ардуиной и записывает их в файл file.db. Клиент подключенный к роутеру, раз в секунду считывает данные из этого файла, тем самым получая актуальную информацию.
arduserver — это маленький TCP-сервер, который ожидает соединение на порту 3490 (порт можно указать любой). Когда от клиента прилетает какая-либо команда, arduserver ловит её и отправляет в ардуину.
То есть, на роутере работает arduread — «слушающая» ардуину, и два HTTP-сервера — uHTTPd для отдачи контента и arduserver — принимающий команды для ардуины.
Обновляем репозиторий:
opkg update
Устанавливаем uHTTPd:
uHTTPd используется в OpenWrt для работы с Web-интерфейсом Luci, поэтому если интерфейс у Вас есть, то значит и сервер уже установлен.
opkg install uhttpd
Засовываем его в автозагрузку и стартуем:
/etc/init.d/uhttpd enable
/etc/init.d/uhttpd start
Если без веб-интерфейса Вам неудобно пользоваться роутером, то ставим Luci:
opkg install luci
Устанавливаем драйвера для ардуины (если знаете какой из них используется в Вашей, то другие ставить не нужно, если нет, то ставьте все).
Если подключаете ардуину к UARTу роутера, то пропустите этот пункт.
opkg install kmod-usb-serial-ftdi kmod-usb-acm kmod-usb-serial-pl2303 kmod-usb-serial-cp210x kmod-usb-serial-ch341 libftdi
После этих операций, на роутере без флешки, останется 372Кб.

Подключите ардуину и убедитесь что она определилась.
ls /dev/tty*

Arduino Nano — ttyUSB0
Arduino Mega — ttyACM0
UART — ttyATH0
Переходим в каталог /tmp
cd /tmp
Скачиваем пакеты arduread и arduserver:
wget http://stdforum.ru/file/arduread_1-1_ar71xx.ipk
wget http://stdforum.ru/file/arduserver_1-1_ar71xx.ipk
Устанавливаем их:
opkg install arduread_1-1_ar71xx.ipk
opkg install arduserver_1-1_ar71xx.ipk
Linux
Broadcom
Ralink
Скачайте архив с бинарниками (устанавливать не нужно), распакуйте и скопируйте файлы в корень (/), всё остальное как в статье.
Открываем файл /etc/rc.local и вставляем нижеследующие строчки (до exit 0), чтоб было как в примере:
vi /etc/rc.local
Обратите внимание на устройство ttyUSB0, у Вас может быть другое. Так же, если Вы измените название папки с сайтом, то в аргументах arduread нужно указать именно её.
(sleep 20 && /arduread /dev/ttyUSB0 /www/ymdom/file.db )&
(sleep 1 && /arduserver 3490 /dev/ttyUSB0 )&
exit 0
подсказка vi
Нажимаем символ "i".
Вносим изменения.
Нажимаем Esc.
Вводим последовательно символы :wq (: w-сохранить q-выйти)
Нажимаем Enter.
Файл отредактирован.
Вносим изменения.
Нажимаем Esc.
Вводим последовательно символы :wq (: w-сохранить q-выйти)
Нажимаем Enter.
Файл отредактирован.
Создаём папку ymdom в корневой папке сервера /www
mkdir /www/ymdom
Проверим:
ls /www

Переходим в неё:
cd /www/ymdom
И загружаем необходимые файлы:
wget http://stdforum.ru/file/ymdom/index.html
wget http://stdforum.ru/file/ymdom/knopki.css
wget http://stdforum.ru/file/ymdom/style.css
wget http://stdforum.ru/file/ymdom/jquery.min.js
Проверим:
ls /www/ymdom

Всё на месте :)
Проверим свободное место:

Умный дом занял 24Кб.
Теперь нужно подкорректировать файл index.html, вписав в него ip-адрес роутера (если у Вас выделенный ip и планируете заходить извне, то впишите соответствующие имя или айпишку).
Узнать адрес роутера

ifconfig

vi /www/ymdom/index.html
Строка №31

Менять нужно только адрес 192.168.5.222, порт (3490) трогать не надо.
Если всё же решите поменять порт, то не забудте поменять его в аргументах для запуска программы arduserver, в файле /etc/rc.local (sleep 1 && /arduserver 3490 /dev/ttyUSB0).
Опять же, если будете заходить извне, и «умный дом» подключён к интернету не напрямую, а через другой роутер, то на нём нужно пробросить порт 3490, и 80-ый конечно тоже.
Если подключаете ардуину к UARTу, то нужно в файле /etc/inittab закомментировать последнюю строчку:
vi /etc/inittab
#ttyATH0::askfirst:/bin/ash --login
Перегружаем:
reboot
Заходим в браузере по адрес/ymdom/ и видим…

Строка сверху, это «служебная» информация, пояснения содержатся в комментариях в файле:
index.html
<!doctype html>
<!--[if lt IE 7]> <html class="no-js ie6 oldie" lang="ru"> <![endif]-->
<!--[if IE 7]> <html class="no-js ie7 oldie" lang="ru"> <![endif]-->
<!--[if IE 8]> <html class="no-js ie8 oldie" lang="ru"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="ru"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>stD_mr3020</title>
<link rel="author" href="https://plus.google.com/104333559503937496126" />
<link rel="stylesheet" href="style.css" type="text/css" />
<link rel="stylesheet" href="shim.css" type="text/css" />
<link rel="stylesheet" href="knopki.css" type="text/css" />
<link rel="stylesheet" href="slai.css" type="text/css"/>
<script src="jquery.min.js"></script>
<script>
var dlina = 23; /* количество блоков (разделённых пробелами) в ответе от ардуино (нужно менять если добавляете функции в ардуине) */
var flagobnov = 1;
var slmode = 0;
var simvol = ' ';
var err_con = 0;
var err_tout = 0;
var count_obnov = 0;
var adres = 'http://192.168.5.222:3490/';
$(document).ready(function(){
function sendtoardu(){ $.ajax({ type: "POST", url: adres + simvol }); } /* отправка команды в ардуину */
/* d2 */
$(".d2vkl").click(function(){simvol = 'A'; sendtoardu();}); /* нажатие на кнопку, вызывает функцию sendtoardu() */
$(".d2otkl").click(function(){simvol = 'a'; sendtoardu();});
/* d3 */
$(".d3vkl").click(function(){simvol = 'B'; sendtoardu();});
$(".d3otkl").click(function(){simvol = 'b'; sendtoardu();});
/* d4 */
$(".d4vkl").click(function(){simvol = 'C'; sendtoardu();});
$(".d4otkl").click(function(){simvol = 'c'; sendtoardu();});
/* d5 */
$(".d5vkl").click(function(){simvol = 'D'; sendtoardu();});
$(".d5otkl").click(function(){simvol = 'd'; sendtoardu();});
/* d6 */
$(".d6vkl").click(function(){simvol = 'E'; sendtoardu();});
$(".d6otkl").click(function(){simvol = 'e'; sendtoardu();});
/* d7 */
$(".d7vkl").click(function(){simvol = 'F'; sendtoardu();});
$(".d7otkl").click(function(){simvol = 'f'; sendtoardu();});
/* d8 */
$(".d8vkl").click(function(){simvol = 'G'; sendtoardu();});
$(".d8otkl").click(function(){simvol = 'g'; sendtoardu();});
/* d9 */
$(".d9vkl").click(function(){simvol = 'H'; sendtoardu();});
$(".d9otkl").click(function(){simvol = 'h'; sendtoardu();});
/* d10 */
$(".d10vkl").click(function(){simvol = 'I'; sendtoardu();});
$(".d10otkl").click(function(){simvol = 'i'; sendtoardu();});
/* d11 */
$(".d11vkl").click(function(){simvol = 'J'; sendtoardu();});
$(".d11otkl").click(function(){simvol = 'j'; sendtoardu();});
/* d12 */
$(".d12vkl").click(function(){simvol = 'K'; sendtoardu();});
$(".d12otkl").click(function(){simvol = 'k'; sendtoardu();});
/* d13 */
$(".d13vkl").click(function(){simvol = 'M'; sendtoardu();});
$(".d13otkl").click(function(){simvol = 'm'; sendtoardu();});
$(".slip").click(function(){ /* кнопка пуск */
$(".pansl").hide();
flagobnov = 1;
show();
});
$(".knp1").click(function(){ /* показать панель "кнопки" */
$(".panel1").toggle();
});
$(".knp3").click(function(){ /* показать панель "темп" */
$(".panel3").toggle();
});
}); /* КОНЕЦ document.ready */
/*обновление*/
show();
setInterval(show,980); /* частота обновления в милисекундах */
function show(){ /* функция обновления */
if(flagobnov == 1) { /* это флаг нужен для временного отключения обновления */
$.ajax({
type: "GET",
url: "file.db", /* файл из которого забираются данные */
timeout:560, /* время (мс), в течении которого функция будет ждать ответа от сервера */
cache: false,
success: function(data){
var vars = data.split(" "); /* разбор строки прочитаной из файла */
if(vars.length == dlina) /* проверка длины данных (количество блоков разделённых пробелом) */
{
count_obnov++; /* это просто счётчик обновлений */
/* err_con - это счётчик неудачно полученых данных, err_tout - счётчик запросов закончившихся таймаутом */
$('#std').html("Connect! Errcon: " + err_con + " Errtout: " + err_tout + " Obnov: " + count_obnov);
/*d2*/
if(vars[2] == 1) { $(".d2otkl").show(); $(".d2vkl").hide(); } /* в зависимости от принятого флага скрывает/показавыет кнопку вкл или откл */
else if(vars[2] == 0) { $(".d2otkl").hide(); $(".d2vkl").show(); }
/*d3*/
if(vars[3] == 1) { $(".d3otkl").show(); $(".d3vkl").hide(); }
else if(vars[3] == 0) { $(".d3otkl").hide(); $(".d3vkl").show(); }
/*d4*/
if(vars[4] == 1) { $(".d4otkl").show(); $(".d4vkl").hide(); }
else if(vars[4] == 0) { $(".d4otkl").hide(); $(".d4vkl").show(); }
/*d5*/
if(vars[5] == 1) { $(".d5otkl").show(); $(".d5vkl").hide(); }
else if(vars[5] == 0) { $(".d5otkl").hide(); $(".d5vkl").show(); }
/*d6*/
if(vars[6] == 1) { $(".d6otkl").show(); $(".d6vkl").hide(); }
else if(vars[6] == 0) { $(".d6otkl").hide(); $(".d6vkl").show(); }
/*d7*/
if(vars[7] == 1) { $(".d7otkl").show(); $(".d7vkl").hide(); }
else if(vars[7] == 0) { $(".d7otkl").hide(); $(".d7vkl").show(); }
/*d8*/
if(vars[8] == 1) { $(".d8otkl").show(); $(".d8vkl").hide(); }
else if(vars[8] == 0) { $(".d8otkl").hide(); $(".d8vkl").show(); }
/*d9*/
if(vars[9] == 1) { $(".d9otkl").show(); $(".d9vkl").hide(); }
else if(vars[9] == 0) { $(".d9otkl").hide(); $(".d9vkl").show(); }
/*d10*/
if(vars[10] == 1) { $(".d10otkl").show(); $(".d10vkl").hide(); }
else if(vars[10] == 0) { $(".d10otkl").hide(); $(".d10vkl").show(); }
/*d11*/
if(vars[11] == 1) { $(".d11otkl").show(); $(".d11vkl").hide(); }
else if(vars[11] == 0) { $(".d11otkl").hide(); $(".d11vkl").show(); }
/*d12*/
if(vars[12] == 1) { $(".d12otkl").show(); $(".d12vkl").hide(); }
else if(vars[12] == 0) { $(".d12otkl").hide(); $(".d12vkl").show(); }
/*d13*/
if(vars[13] == 1) { $(".d13otkl").show(); $(".d13vkl").hide(); }
else if(vars[13] == 0) { $(".d13otkl").hide(); $(".d13vkl").show(); }
/* температура */
$("#thum").html(vars[14] + "`C");
/* влажность */
$("#hum").html(vars[15] + " %");
}
else
{
err_con++; /* если дынные приняты криво, то пишем ошибку */
$('#std').html("NOT dlina! Errcon: " + err_con + " Errtout: " + err_tout + " Obnov: " + count_obnov);
/* если МК отвалился, то в файл file.db будет записан код "8888" */
if(data == 8888) alert("MK NOT CONNECT");
}
},
error: function(XMLHttpRequest, textStatus, errorThrown)
{
if(textStatus == "timeout")
{
err_tout++; /* функция ajax не дождалась ответа */
$('#std').html("Timeout! Errcon: " + err_con + " Errtout: " + err_tout + " Obnov: " + count_obnov);
}
}
});
slmode++; /* спящий режим */
if(slmode > 10)
{
$(".pansl").show();
flagobnov = 0;
slmode = 0;
}
}
}
</script>
</head>
<body>
<v><div id="std"></div></v>
<div class="knp1" title="Кнопки" >Кнопки</div>
<div class="knp3" title="Темп" >Темп</div>
<!-- кнопки -->
<div class="panel1">
<div class="knop d2vkl" title="Вкл D2" >D2</div> <!-- здесь менять названия кнопок -->
<div class="knop d2otkl" title="Откл D2">D2</div> <!-- чтобы убрать кнопку удалите оба блока -->
<div class="knop d3vkl" title="Вкл D3">D3</div>
<div class="knop d3otkl" title="Откл D3">D3</div>
<div class="knop d4vkl" title="Вкл D4">D4</div>
<div class="knop d4otkl" title="Откл D4">D4</div>
<div class="knop d5vkl" title="Вкл D5">D5</div>
<div class="knop d5otkl" title="Откл D5">D5</div>
<div class="knop d6vkl" title="Вкл D6">D6</div>
<div class="knop d6otkl" title="Откл D6">D6</div>
<div class="knop d7vkl" title="Вкл D7">D7</div>
<div class="knop d7otkl" title="Откл D7">D7</div>
<div class="knop d8vkl" title="Вкл D8">D8</div>
<div class="knop d8otkl" title="Откл D8">D8</div>
<div class="knop d9vkl"title="Вкл D9">D9</div>
<div class="knop d9otkl" title="Откл D9">D9</div>
<div class="knop d10vkl" title="Вкл D10">D10</div>
<div class="knop d10otkl" title="Откл D10">D10</div>
<div class="knop d11vkl" title="Вкл D11">D11</div>
<div class="knop d11otkl" title="Откл D11">D11</div>
<div class="knop d12vkl" title="Вкл D12">D12</div>
<div class="knop d12otkl" title="Откл D12">D12</div>
<div class="knop d13vkl" title="Вкл D13">D13</div>
<div class="knop d13otkl" title="Откл D13">D13</div>
</div>
<div class="panel3">
<vl1><div id="thum" title="температура"></div></vl1>
<vl2><div id="hum" title="влажность"></div></vl2>
</div>
<div class="pansl">
<div class="slip" title="спящий режим">ПУСК</div>
</div>
<a href="http://istarik.ru/" > <st>istarik.ru</st>
</body>
</html>
Исходники
Если что-то пошло не так, то в корневой папке появятся файлы Error.log и/или ErrorServer.log:
ls /
vi /Error.log
vi /ErrorServer.log
Расшифровку логов можно подсмотреть в исходниках arduread и arduserver
arduread
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#define BUFSIZE 128
char bRead[BUFSIZE] = {0,};
char er_log_str[50]={0,};
char device[20]={0,};
char papka[128]={0,};
char str_iz_file[100] = {0,};
void error_log()
{
time_t t;
time(&t);
FILE *f;
f = fopen("Error.log", "a");
fprintf(f, "%s. ", er_log_str);
fprintf(f, "%s", ctime( &t));
printf("Write to Error.log\n");
fclose(f);
exit(0);
}
void error_to_filebd()
{
FILE *f;
f = fopen(papka, "w");
fprintf(f, "%s", "8888");
fclose(f);
printf("Zapisal Oshibky 8888:\n");
strncpy(str_iz_file, "Error", 99);
}
int open_port(void)
{
int fd;
fd = open(device, O_RDWR | O_NOCTTY |O_NONBLOCK);
if(fd == -1)
{
strncpy(er_log_str, "Error - NOT open port", 49);
error_log();
}
else
{
struct termios options;
tcgetattr(fd, &options);
cfsetispeed(&options, B57600);
cfsetospeed(&options, B57600);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cc[VMIN] = 1;
options.c_cc[VTIME] = 1;
options.c_lflag = ICANON;
options.c_oflag = 0;
options.c_oflag &= ~OPOST;
tcflush(fd, TCIFLUSH);
tcsetattr(fd, TCSANOW, &options);
}
return fd;
}
void main(int argc, char *argv[])
{
if(argc!=3)
{
printf("Primer - ./port /dev/ttyACM1 /www/tim/file.db\n");
strncpy(er_log_str, "Not argumets", 49);
error_log();
}
strncpy(device, argv[1], 19);
strncpy(papka, argv[2], 127);
int d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, thum, hum, pir1, knop1, knop2, ar_summ;
int err_count1 = 0;
int err_count2 = 0;
unsigned int i;
int fd = open_port();
sleep(2);
if (fd < 0) return;
tcflush(fd, TCIFLUSH);
while(!VINTR)
{
usleep(470000);
read(fd, bRead, BUFSIZE);
for(i = 0; i<=strlen(bRead); i++)
{
if(bRead[i] == '\r' || bRead[i] == '\n')
{
bRead[i] = '\0';
tcflush(fd, TCIFLUSH);
break;
}
}
//////////////////////////// Проверка A-Z /////////////////////////////////////
char first_sim = '0';
if(bRead[0] == 'A' && bRead[strlen(bRead)-1] == 'Z')
{
sscanf(bRead,"%c %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", &first_sim, &ar_summ, &d2, &d3, &d4, &d5, &d6, &d7, &d8, &d9, &d10, &d11, &d12, &d13, &thum, &hum, &pir1, &knop1, &knop2);
err_count1 = 0;
//printf("A-Z\n");
}
else
{
tcflush(fd, TCIFLUSH);
memset(bRead, 0, sizeof(bRead));
err_count1++;
if(err_count1 > 10)
{
err_count1 = 0;
error_to_filebd();
}
//printf("Not A-Z\n");
continue;
}
///////////////////////////// Проверка суммы /////////////////////////////////////
int summa = d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 + d10 + d11 + d12 + d13 + thum + hum + pir1 + knop1 + knop2;
if(summa != ar_summ)
{
tcflush(fd, TCIFLUSH);
memset(bRead, 0, sizeof(bRead));
summa=0;
ar_summ=999; d2=0; d3=0; d4=0; d5=0; d6=0; d7=0; d8=0; d9=0; d10=0; d11=0; d12=0; d13=0; thum=0; hum=0; pir1=0; knop1=0; knop2=0;
err_count2++;
if(err_count2 > 10)
{
err_count2 = 0;
error_to_filebd();
}
//printf("Not summa\n");
continue;
}
else
{
err_count2 = 0;
//printf("Summa\n");
}
////////////////////////////// Сравнение строк /////////////////////////////////////
if(strcmp(bRead, str_iz_file)==0)
{
memset(bRead, 0, sizeof(bRead));
summa=0;
ar_summ=999; d2=0; d3=0; d4=0; d5=0; d6=0; d7=0; d8=0; d9=0; d10=0; d11=0; d12=0; d13=0; thum=0; hum=0; pir1=0; knop1=0; knop2=0;
//printf("Stroki odinakovie\n");
continue;
}
else
{
//printf("NOT Sravnil\n");
FILE *f;
f = fopen(papka, "w");
fprintf(f, "%s", bRead);
fclose(f);
//printf("Write to filebd\n");
memcpy(str_iz_file, bRead, sizeof(str_iz_file));
//printf("str_iz_file %s\n", str_iz_file);
memset(bRead, 0, sizeof(bRead));
summa=0;
ar_summ=999; d2=0; d3=0; d4=0; d5=0; d6=0; d7=0; d8=0; d9=0; d10=0; d11=0; d12=0; d13=0;
}
} // Конец основного цикла (while)
} // end of main()
// gcc -W -Wall arduread.c -o arduread
// ./arduread /dev/ttyACM0 /www/ymdom/file.db
// make package/arduread/compile V=s
arduserver
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string.h>
#include <time.h>
char device[17]={0,};
char er_log_str[51]={0,};
void error_log()
{
time_t t;
time(&t);
FILE *f;
f = fopen("ErrorServer.log", "a");
fprintf(f, "%s. ", er_log_str);
fprintf(f, "%s", ctime( &t));
printf("Write to ErrorServer.log\n");
fclose(f);
exit(0);
}
int main(int argc, char *argv[])
{
if(argc!=3)
{
printf("Primer - ./arduserver 3490 /dev/ttyACM1\n");
strncpy(er_log_str, "Not argumets", 50);
error_log();
}
strncpy(device, argv[2], 15);
int one = 1, client_fd;
struct sockaddr_in svr_addr, cli_addr;
socklen_t sin_len = sizeof(cli_addr);
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0)
{
strncpy(er_log_str, "Error - socket", 50);
error_log();
}
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
svr_addr.sin_family = AF_INET;
svr_addr.sin_addr.s_addr = INADDR_ANY;
svr_addr.sin_port = htons(atoi(argv[1]));
if(bind(sock, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) == -1)
{
close(sock);
strncpy(er_log_str, "Error - bind", 50);
error_log();
}
listen(sock, 5);
while (1)
{
client_fd = accept(sock, (struct sockaddr *) &cli_addr, &sin_len);
if(client_fd == -1)
{
continue;
}
char buffer[8] = {0,};
read(client_fd, buffer, 7);
char to_Ardu[41] = {0,};
snprintf(to_Ardu, 40, "echo 'Y+=Z%c' > %s", buffer[6], device);
system(to_Ardu);
close(client_fd);
}
}
// gcc -W -Wall arduserver.c -o arduserver
// ./arduserver 3490 /dev/ttyACM1
// make package/arduserver/compile V=s
На этом всё.
Телеграм-чат istarik

- +457
- stD
100989
Поддержать автора
Комментарии (193)
При входе на 192.168.1.1/ymdom/
Появляются только две иконки
КНОПКИ
ТЕМП
они не активны и больше ничего нет :(
/dev/tty /dev/ttyS10 /dev/ttyS13 /dev/ttyS2 /dev/ttyS5 /dev/ttyS8
/dev/ttyS0 /dev/ttyS11 /dev/ttyS14 /dev/ttyS3 /dev/ttyS6 /dev/ttyS9
/dev/ttyS1 /dev/ttyS12 /dev/ttyS15 /dev/ttyS4 /dev/ttyS7 /dev/ttyUSB0
root@OpenWrt:~# ls /
Error.log bin lib proc sbin usr
arduread dev mnt rom sys var
arduserver etc overlay root tmp www
root@OpenWrt:~# vi /Error.log
Error — NOT open port. Wed Sep 23 00:22:53 2015
Error — NOT open port. Wed Sep 23 00:22:50 2015
Error — NOT open port. Wed Sep 23 00:22:50 2015
У меня Ардуино нано
на CH340G
установил пакет kmod-usb-serial-ch341 и работает…
Библиотека CyberLib.rar должна быть zip
Какой порт пробрасывать для доступа из вне
3490?
Возможно ли организовать функцию охраны?
echo «Subject: Домашний Сервер > Включено питание 220 Вольт! `date -R`» | ssmtp 55ohrana@gmail.com
Раньше через crontab управлял питанием USB и включал-выключал нагрузку по расписанию
0 21 * * * echo 1 >/sys/class/gpio/gpio6/value
0 7 * * * echo 0 >/sys/class/gpio/gpio6/value
Но как же тогда у вас устройство виделось?
root@OpenWrt:~# ls /dev/tty*
/dev/tty /dev/ttyS10 /dev/ttyS13 /dev/ttyS2 /dev/ttyS5 /dev/ttyS8
/dev/ttyS0 /dev/ttyS11 /dev/ttyS14 /dev/ttyS3 /dev/ttyS6 /dev/ttyS9
/dev/ttyS1 /dev/ttyS12 /dev/ttyS15 /dev/ttyS4 /dev/ttyS7 /dev/ttyUSB0
…
Не понял.
…
Пробрасывать надо порт 3490, ну и 80 тоже.
…
Можно.
Удалите файл index.html и сделайте так:
# HTTP listen addresses, multiple allowed
list listen_http 0.0.0.0:80
# list listen_http [::]:80
# HTTPS listen addresses, multiple allowed
list listen_https 0.0.0.0:443
# list listen_https [::]:443
# Server document root
option home /www
# Reject requests from RFC1918 IP addresses
# directed to the servers public IP(s).
# This is a DNS rebinding countermeasure.
option rfc1918_filter 1
Попробовал браузер IE- эффект тот же((
Что покажет команда:
Когда кнопка не нажимается должен мигать только TX, а когда нажимаете должен моргать RX, у Вас так?
То есть до этого в файле index.html было так:
Стало так:
…
В общем поздравляю Вас с успехом, будут вопросы — спрашивайте.
По поводу кода ардуино, в начале кода, объявляете переменные int thum=110 & hum=110. Мне кажется нет смысла их приравнивать к 110, т.к. в void setup код считывает показания. Да, в предыдущей статье эти цифры играли какую-то роль, т.к. там считывание происходило только через 2 минуты. Но в этой статье считывание производится во время запуска.
Смысл есть. Вобще неплохо было бы "обстодесивать" их перед каждым считыванием.
В index.html — ip правильный. С датчиков в броузере показывает температуру и влажность. Куда копать?
И очень интересует версия в которой данные читаются не из файла, а из ОЗУ
…
Вот это сделано?
::shutdown:/etc/init.d/rcS K shutdown
#ttyATH0::askfirst:/bin/ash --login
А это содержимое файла.
в этой строке столо использование USB (sleep 1 && /arduserver 3490 /dev/ttyUSB0), хотя я работаю с UART. Т.е читал я через UART, а писать пытался через USB!
Добавьте в /etc/rc.local (до запуска arduread и arduserver) команду:
И измените (в /etc/rc.local) путь к файлу file.db, должно получится вот так:
Так же в файле /etc/config/uhttpd необходимо изменить рабочую папку сервера /www на /tmp
Всё, теперь перегрузитесь и проверяйте. У меня сейчас нет роутера, так что жду от вас результата.
как понимать эти фразы «Менять нужно только адрес 192.168.5.222, порт (3490) трогать не надо.
Если всё же решите поменять порт, то не забудте поменять его в аргументах для запуска программы arduserver, в файле /etc/rc.local (sleep 1 && /arduserver 3490 /dev/ttyUSB0).
Опять же, если будете заходить извне, и «умный дом» подключён к интернету не напрямую, а через другой роутер, то на нём нужно пробросить порт 3490, и 80-ый конечно тоже. „
Поменяйте порт в файле index.html и в (sleep 1 && /arduserver 3490 /dev/ttyUSB0)
Само сабой, файлы luci лежат в папке /www, а сервер настроен на работу в папке /tmp.
Ser2net тоже использует определённый порт. Чем Вам мешает дополнительный порт? Ведь в работе он абсолютно прозрачен.
И еще вопрос — как перенести в ОЗУ, но что-бы luci осталась работоспособной?
Перегрузитесь, luci будет на 81-ом порту. адрес:81
На всякий случай полный листинг uhttpd:
…
Мы добавили виртуальный хост для luci.
Температура обновляется, но нажатия кнопок не обрабатываются. Через web нажимаю — не включаются. Через консоль включил кнопки, они подсветились. Через web пытаюсь выключить — не выключаются. Таймоут ставил 500, 700, 200 — безрезультатно. Может arduserver как-то не правильно отрабатывает. Вот что выдает htop, а по ходу arduserver не запускается. В процессах висит два процесса arduread /dev/ttyS0 /www/ymdom/file.db. Ведь ардусервер тоже должен висеть в процессах?
Два процесса — это мой косяк, эксперменты ставил… Вобщем сейчас исправлю.
А что у Вас за устройство /dev/ttyS0?
…
Запустите arduserver из консоли, что скажет?
Write to ErrorServer.log
Кстати в корне нет файла ErrorServer.log.
ttyS0 com порт, ардуина подключена через UART.
Перед публикацией статьи я всё проверяю на работоспособность.
…
До какого количества хотите увеличить?
Задался целью (да простит меня sTD) поставить знак градуса, чтобы не "'C" было, а "°С" стало.
В index.html, где прописано "'C", стираем символ перед С и, нажав alt, набираем 0176 на цифровой клавиатуре(num lock). В итоге получаем знак "°".
Также методом тыка на ардуино нашёл этот же символ под кодом 223, т.е. чтобы его вывести на дисплей нужно набрать lcd.print(char(223)).
Жутко напрягает то, что при нажатии на «темп» не исчезают кнопки, и наоборот. Но… эммм, да простит меня sTD, я поборол сие явление, теперь у меня кнопки не накладываются на температуру. Для этого надо в index.html дописать код. Было:
стало:
Если хотите могу сказать, а если хотите самостоятельно дойти, то промолчу.
Соответственно в ЛСД идёт локальная, а в сериал глобальная, которая не инициализирована == 0.
Поменял в начале на float и float tempA0=term1(A0_Read): 0.
что-то ещё видимо обнуляет ее. может быть в начале массив, там где byte = 0.
:
Да, я знаю, пробовал несколько, нужна точность во всём диапазоне, но проклятая нелинейность бесит.
После некоторых танцев с бубном, завелось и отлично работает.
Появился такой вопрос, возможно ли изменение управляющего дескриптера (Y+=Z) или же добавления новых типа Y+=A и т.д.
Я подключил библиотеку SoftwareSerial и управляю с дуины другими дуинами. Немного не хватает количества символов. Хотя может быть добавить просто длинны типа — Y+=Zxx?
типа —
При возможности менять дескрипторы, можно было бы забиндить их на допустим на Y+=P
Y+=F —
и т.д.
Просто слишком много конечных устройств)
Ну и соответсвенно кнопки уже в вебе вязать по группам на дескриптор.
Как то так.
Сначала по кнопке уходит символ 1, потом а. итого на одно значение можно поставить 50 событий. Единственный прикол остался с тем, что дуина иногда ловит символы в обратном порядке, почему-то. То есть сначала -а, потом 1. Решил это просто продублировав действия дескрипта но уже в обратном порядке.
Получается 899 команд — от 100 до 999 (трёхзначное число).
Использую роутер Netgear WNCE2001 со старой прошивкой openwrt openwrt-14.07-ramips-rt305x-wnce2001-squashfs-factory-worldwide
Так как новая прошивка не сохраняет настройки
Можно ли сделать для него файлы arduread и arduserver
Спасибо.
В скетче ардуины если yt меняешь DHT22 на DHT11, тогда показания отображаются. А если меняешь значение на DHT11, то в показателях нули.
Кроме этого ничего не менял.
ЧЯДНТ?
А если попробовать этот датчик с примером из библиотеки?
Да датчик то нормальный. Я его проверял уже.
Подскажите нубу для чего Вы задаёте значение 110 переменным thum, hum? =)
Сделайте так:
Скажите, а какой из двух представленных на Вашем сайте вариантов УД лучше?
При работе на странице постоянно появляется кнопка «Пуск» Что это?
В файле index.html есть блок отвечающий за это:
Можете отключить или увеличить интервал: