"Умный дом" на основе Arduino
Эта статья является началом «Умного дома» на базе Arduino.
В этой части описано как сделать простой веб-интерфейс для управления (вкл/откл) пинами ардуино.
Посмотреть и понажимать можно здесь...
Понадобится роутер с установленой OpenWrt и возможностью подключения ардуино (USB, UART) или любой компьютер с
И любая Ардуина.
Здесь я подробно описал процесс установки OpenWrt на роутер TL-MR3020.
Тут про установку сервера Lighttpd.
А здесь о том как подключить к роутеру Ардуино.
Итак, Вы установили OpenWrt и наладили связь с ардуиной как описано тут.
Теперь сделаем интерфейс с помощью которого можно будет включать и отключать «что-то» подключённое к пинам ардуино (D2 — D13).
Скачайте архив и распакуйте его в рабочую папку сервера (по умолчанию это /var/www), чтоб получилось так /var/www/knopki.
У Вас может быть своя папка.
Прошейте в ардуину этот скетч:
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 descript[5]; // массив
void setup()
{
Serial.begin(57600);
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
pinMode(12, OUTPUT);
pinMode(13, OUTPUT);
}
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 вкл
digitalWrite(2, HIGH); // вкл d2
d2 = 1; // ставим флаг в единицу (вкл)
glavnaia(); // отправка ответа
break;
case 'a': // d2 откл
digitalWrite(2, LOW); // откл d2
d2 = 0; // ставим флаг в ноль (откл)
glavnaia(); // отправка ответа
break;
case 'B': // d3
digitalWrite(3, HIGH);
d3 = 1;
glavnaia();
break;
case 'b': // d3
digitalWrite(3, LOW);
d3 = 0;
glavnaia();
break;
case 'C': // d4
digitalWrite(4, HIGH);
d4 = 1;
glavnaia();
break;
case 'c': // d4
digitalWrite(4, LOW);
d4 = 0;
glavnaia();
break;
case 'D': // d5
digitalWrite(5, HIGH);
d5 = 1;
glavnaia();
break;
case 'd': // d5
digitalWrite(5, LOW);
d5 = 0;
glavnaia();
break;
case 'E': // d6
digitalWrite(6, HIGH);
d6 = 1;
glavnaia();
break;
case 'e': // d6
digitalWrite(6, LOW);
d6 = 0;
glavnaia();
break;
case 'F': // d7
digitalWrite(7, HIGH);
d7 = 1;
glavnaia();
break;
case 'f': // d7
digitalWrite(7, LOW);
d7 = 0;
glavnaia();
break;
case 'G': // d8
digitalWrite(8, HIGH);
d8 = 1;
glavnaia();
break;
case 'g': // d8
digitalWrite(8, LOW);
d8 = 0;
glavnaia();
break;
case 'H': // d9
digitalWrite(9, HIGH);
d9 = 1;
glavnaia();
break;
case 'h': // d9
digitalWrite(9, LOW);
d9 = 0;
glavnaia();
break;
case 'I': // d10
digitalWrite(10, HIGH);
d10 = 1;
glavnaia();
break;
case 'i': // d10
digitalWrite(10, LOW);
d10 = 0;
glavnaia();
break;
case 'J': // d11
digitalWrite(11, HIGH);
d11 = 1;
glavnaia();
break;
case 'j': // d11
digitalWrite(11, LOW);
d11 = 0;
glavnaia();
break;
case 'K': // d12
digitalWrite(12, HIGH);
d12 = 1;
glavnaia();
break;
case 'k': // d12
digitalWrite(12, LOW);
d12 = 0;
glavnaia();
break;
case 'M': // d13
digitalWrite(13, HIGH);
d13 = 1;
glavnaia();
break;
case 'm': // d13
digitalWrite(13, LOW);
d13 = 0;
glavnaia();
break;
default:
glavnaia();
}
}
else // если дескриптор ложный, то очищаем буфер
{
for(byte i=0; i < 255; i++)
{
Serial.read();
}
}
} // конец if (Serial.read()=='Y')
} // конец чтение порта
} // конец loop
void glavnaia() // отправка данных
{
Serial.print(d2);//0
Serial.print(",");
Serial.print(d3);//1
Serial.print(",");
Serial.print(d4);//2
Serial.print(",");
Serial.print(d5);//3
Serial.print(",");
Serial.print(d6);//4
Serial.print(",");
Serial.print(d7);//5
Serial.print(",");
Serial.print(d8);//6
Serial.print(",");
Serial.print(d9);//7
Serial.print(",");
Serial.print(d10);//8
Serial.print(",");
Serial.print(d11);//9
Serial.print(",");
Serial.print(d12);//10
Serial.print(",");
Serial.println(d13);//11 отсылается 12 значений
}
Алгоритм обмена данными между ардуиной и роутером
Роутер отправляет в ардуину запрос от клиента состоящий из дескриптора (Y+=Z) и управляющего символа (например 'А'- вкл d2). Дескриптор позволит отфильтровать возможный мусор и исключит случайные срабатывания.
Ардуина обрабатывает управляющий символ внутри функции switch (например включает светодиод) и отправляет ответ роутеру, который в свою очередь отдаст его клиенту.
Работа с клиентом описана ниже.
Ардуина обрабатывает управляющий символ внутри функции switch (например включает светодиод) и отправляет ответ роутеру, который в свою очередь отдаст его клиенту.
Работа с клиентом описана ниже.
Теперь в браузере зайдите по адресу ваш_роутер/knopki/
Если связь установлена, то Вы увидите это:
Нажмите на D13 — загорится светодиод на ардуине и кнопка подсветится.
Теперь осталось приспособить реле и включать везде и всё.
Если связи между ардуиной и роутером нет, то будет только красная надпись stD — это индикатор работы, когда связь установлена, надпись становится серой.
Подождите минуту и пообновляйте страницу. Если связь не устанавливается, то проверьте настройки роутера.
Проверьте права на файлы, правильность путей и устройств.
Как работает: Обновление страницы.
index.html раз в три секунды (интервал можно изменить) запрашивает данные у ардуины (отправляя ей символ о) с помощью функции ajax (ajax позволяет не перегружать страницу).
/*обновление*/
show();
setInterval(show,3000); /* частота обновления в милисекундах */
function show(){ /* функция обновления */
$.ajax({
type: "GET",
url: "box2.php?df=o", /* отправка символа о */
timeout:300,
cache: false,
success: function(data){
...
Запрос передаётся php-файлу (box2.php) находящемуся на сервере, который в свою очередь отправляет его в ардуину.
<?php
if($fp = fsockopen("localhost", 3002, $errno, $errstr, 1)) // открываем порт, в качестве посредника между роутером и ардуиной выступает ser2net
{
fwrite($fp, 'Y+=Z'); // отправляем в порт дескриптор Y+=Z
fwrite($fp, $_GET['df']); // отправляем в порт символ полученый от html странички
stream_set_timeout($fp, 0, 150000); // полезный таймаут, если ответа нет, то поток закроется через 150 мс
$bufft = fgets($fp); // получаем ответ от ардуины
fclose($fp); // закрываем порт
echo $bufft; // отправляем ответ клиенту
}
else
{
usleep(70000);
$fp = fsockopen("localhost", 3002, $errno, $errstr, 1);
fwrite($fp, 'Y+=Z');
fwrite($fp, $_GET['df']);
stream_set_timeout($fp, 0, 150000);
$bufft = fgets($fp);
fclose($fp);
echo $bufft;
}
?>
Ардуина получает команду, обрабатывает её и отправляет ответ, который по той же цепочке возвращается html-страничке (index.html).
...
switch (descript[3])
{
case 'o': // обновление
glavnaia(); // отправка ответа
break;
...
void glavnaia() // отправка данных
{
Serial.print(d2);//0
Serial.print(",");
Serial.print(d3);//1
...
Html-страничка разбирает ответ и выводит на экран нужную кнопку.
...
success: function(data){
var vars = data.split(","); /* разбор строки принятой от ардуино */
if(vars.length == dlina){ /* проверка длины данных (количество блоков разделённых запятой) */
/*d2*/
if(vars[0] == 1) { $(".d2otkl").show(); $(".d2vkl").hide(); } /* в зависимости от принятого флага скрывает/показавыет кнопку вкл или откл */
else if(vars[0] == 0) { $(".d2otkl").hide(); $(".d2vkl").show(); }
...
/* эта часть отвечает за цвет индикатора работы (stD) */
$('#std').html("<v2>stD</v2>"); /* если данные приняты правильно, то надпись stD будет серой */
}
else
{
$('#std').html("<v>stD</v>"); /* если данные приняты НЕправильно, то надпись stD будет красной */
}
Если открыть ещё одну страничку (или зайти с другого устройства) и нажать какую-то кнопку, то на первой страничке (в течении 3 сек.) эта кнопка тоже станет включённой.
Для этого и нужно обновление.
Как работает: Нажатие на кнопку.
Нажатие на кнопку работает так же как и «обновление», в ардуину отсылается символ включения или отключения (в зависимости от состояния), ардуина выполняет действие и посылает в ответ строку с флагами состояния (единица или ноль).
Ответ разбирается в html-странице и в зависимости от флагов выводит на экран нужную кнопку.
Для лучшего понимания откройте файл index.html из архива, и посмотрите комментарии.
Изменение дизайна
Названия кнопок меняются в конце файла index.html
...
<div class="knop d2vkl">D2</div> <!-- здесь менять названия кнопок -->
<div class="knop d2otkl">D2</div> <!-- чтобы убрать кнопку удалите оба блока -->
...
Расположение и размер кнопок задаются в файле knopki.css
...
.d2vkl{
top: 20px; /*координаты кнопок*/
left: 20px; /*координаты кнопок*/
box-shadow: 0 0 10px 3px rgba(0,0,0,0.3); /*цвет и размер тени кнопки*/
-webkit-transition-duration: 0.6s; /*плавность появления*/
-o-transition-duration: 0.6s;
-moz-transition-duration: 0.6s;
transition-duration: 0.6s;
}
.d2vkl:hover{ /*наведение мыши на кнопку*/
box-shadow: 0 0 2px 1px rgba(0,0,0,0.3);
}
...
...
.knop {
position: absolute;
width: 200px; /*ширина для всех кнопок*/
height: 100px; /*высота для всех кнопок*/
display: none;
cursor: pointer;
font-size: 30px; /*размер текста на кнопках*/
font-weight: 600; /*ширина текста на кнопках*/
font-family: Arial, Helvetica, sans-serif; /*шрифт*/
color: #161616; /*цвет текста на кнопках*/
text-shadow: 0px 1px 2px #7c7c7c; /*цвет и размер тени кнопок*/
text-align: center;
line-height: 3.2;
}
Цвет фона меняется в файле style.css
body {
background:#202020; /* цвет фона */
}
...
На этом первая часть заканчивается, в следующей части добавлен диммер и запись значений в EEPROM...
«Дизаин» конечно «страшненький», но, это пока всего лишь шаблон для демонстрации функций.
Вот тут можно скачать библиотеку для разгона Arduino.
Обсудить на форуме...
- +348
- stD
34913
Поддержать автора
Комментарии (13)
Попробуйте в файле index.html увеличить таймауты с 300 на 500: