Здравствуйте. Целью написания данной статьи является желание поделиться с общественностью накопленной информацией и, конечно же, узнать что-то новое. Я опишу, как подключить ардуину к роутеру и как сделать шаблон веб-интерфейса для управления ардуиной, а точнее, для дискретного «дёрганья ножками».
Как это выглядит «вживую», можно посмотреть здесь.
Повествование будет вестись на основе популярного нынче роутера TL-MR3020 и Arduino Nano.
Подключаться ардуина будет к USB, вариант с UARTом хоть и проще, но всё же трудней. Такая вот суперпозиция )
Я предполагаю что у заинтересованных лиц уже есть роутер прошитый OpenWrt и установлен сервер (Lighttpd), поддерживающий PHP. Ну, или просто компьютер
Если то — истина, тогда follow my way
Подключение ардуино
Будем отправлять в ардуину запрос состоящий из дескриптора (Y+=Z) и управляющего символа (например, 'А'- вкл d13). Дескриптор позволит отфильтровать возможный мусор и исключит случайные срабатывания.
Ардуина обрабатывает управляющий символ внутри функции switch (например, включает светодиод) и рапортует о выполнении.
Подключаем ардуину к роутеру ⇨ включаем в розетку ⇨ устанавливаем необходимые пакеты.
Поскольку придётся использовать хаб, то необходимо помнить, что некоторые из них работают некорректно.
Драйвера для всех существующих ардуин, утилиту для настройки порта stty и ser2net (о нём ниже):
Тот, кто разбирается, сам выберет необходимый драйвер.
Если действия производятся на «большом компьютере», то достаточно:
Проверим, как подцепилось устройство: Может быть, придётся перегрузить.
Как-то так...
Настроим порт утилитой stty:
Открываем вторую консоль и пишем в неё:
Тут будет ответ.
В первой консоли испытываем:
D13 загорелась
D13 погасла.
Если при посылке пакета ардуина перегружается (диоды моргают, но D13 не горит), тогда нужно поставить электролитический конденсатор 5-10мкФ между Reset и GND.
Не забудьте отключать его, когда заливаете скетч.
Существуют трудности у ардуин с мостом ch341
Если всё работает, то идём дальше.
Редактируем файл конфигурации ser2net:
Закомментируйте всё строчки в конце и сохраните.
Добавьте stty и ser2net в автозагрузку:
Вот так нужно сделать:
Обратите внимание. Строки инициализации должны быть записаны одной строкой (без переноса).
Перегрузите и проверьте.
Роутер отправляет в ардуину запрос от клиента состоящий из дескриптора (Y+=Z) и управляющего символа (например 'А'- вкл d2).Ардуина обрабатывает управляющий символ и отправляет ответ роутеру, который в свою очередь отдаст его клиенту. Работа с клиентом описана ниже.
Скачайте архив и распакуйте его в рабочую папку сервера (по умолчанию это /var/www), вот так – /var/www/knopki (у вас может быть своя папка).
В браузере зайдите по адресу ваш_роутер/knopki/. Если связь установлена, то вы увидите это:
Нажмите на D13 — загорится светодиод на ардуине и кнопка подсветится.
Обновление страницы:
index.html раз в три секунды (интервал можно изменить) запрашивает данные у ардуины (отправляя ей символ о) с помощью функции ajax (ajax позволяет не перегружать страницу).
Запрос передаётся php-файлу (box2.php) находящемуся на сервере, который в свою очередь обращается к ардуине через сокет ser2net.
Ардуина получает команду, обрабатывает её и отправляет ответ, который по той же цепочке возвращается html-страничке (index.html).
Html-страничка разбирает ответ и выводит на экран нужную кнопку.
Если открыть ещё одну страничку (или зайти с другого устройства) и нажать какую-то кнопку, то на первой страничке (в течении 3 сек.) эта кнопка тоже станет включённой. Для этого обновление и нужно.
Нажатие на кнопку:
Нажатие на кнопку работает так же, как и «обновление», в ардуину отсылается символ включения или отключения (в зависимости от состояния), ардуина выполняет действие и посылает в ответ строку с флагами состояния (единица или ноль).
Ответ разбирается в html-странице и в зависимости от флагов выводит на экран нужную кнопку.
Для лучшего понимания откройте файл index.html из архива и посмотрите комментарии.
Если связи между ардуиной и роутером нет, то будет только красная надпись stD — это индикатор работы, когда связь установлена, надпись становится серой.
Подождите минуту и пообновляйте страницу. Если связь не устанавливается, то проверьте настройки роутера.
Проверьте права на файлы, правильность путей и устройств.
Названия кнопок меняются в конце файла index.html
Позиция и размер кнопок задаются в файле knopki.css
Цвет фона меняется в файле style.css
На этом, пожалуй, стоит закончить. Спасибо тем, кто потратил своё драгоценное. Если где-то допустил неточность, то прошу поправить меня.
«Дизаин» несколько мрачноват, но это пока всего лишь шаблон для демонстрации функций.
Статья писалась под великолепную музыку «Nirvana».
Как это выглядит «вживую», можно посмотреть здесь.
Повествование будет вестись на основе популярного нынче роутера TL-MR3020 и Arduino Nano.
Подключаться ардуина будет к USB, вариант с UARTом хоть и проще, но всё же трудней. Такая вот суперпозиция )
Я предполагаю что у заинтересованных лиц уже есть роутер прошитый OpenWrt и установлен сервер (Lighttpd), поддерживающий PHP. Ну, или просто компьютер
Если то — истина, тогда follow my way
Подключение ардуино
Прошиваем пробный скетч
int led = 13;
byte descript[5];
void setup()
{
Serial.begin(57600);
pinMode(led, 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 'A':
digitalWrite(led, HIGH);
Serial.println("OK vkl"); // ответ
break;
case 'a':
digitalWrite(led, LOW);
Serial.println("OK otkl"); // ответ
break;
}
}
else // если символ был не 'Y', то очищаем буфер
{
for(byte i=0; i < 255; i++)
{
Serial.read();
}
}
} // конец if (Serial.read()=='Y')
} // конец чтение порта
}
Будем отправлять в ардуину запрос состоящий из дескриптора (Y+=Z) и управляющего символа (например, 'А'- вкл d13). Дескриптор позволит отфильтровать возможный мусор и исключит случайные срабатывания.
Ардуина обрабатывает управляющий символ внутри функции switch (например, включает светодиод) и рапортует о выполнении.
Подключаем ардуину к роутеру ⇨ включаем в розетку ⇨ устанавливаем необходимые пакеты.
Поскольку придётся использовать хаб, то необходимо помнить, что некоторые из них работают некорректно.
Драйвера для всех существующих ардуин, утилиту для настройки порта stty и ser2net (о нём ниже):
Тот, кто разбирается, сам выберет необходимый драйвер.
opkg update
opkg install kmod-usb-serial-ftdi kmod-usb-acm kmod-usb-serial-pl2303 kmod-usb-serial-cp210x kmod-usb-serial-ch341 libftdi coreutils-stty ser2net
Если действия производятся на «большом компьютере», то достаточно:
apt-get install ser2net
Проверим, как подцепилось устройство: Может быть, придётся перегрузить.
ls /dev/tty*
Как-то так...
Настроим порт утилитой stty:
stty -F /dev/ttyUSB0 cs8 57600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts -hupcl
Открываем вторую консоль и пишем в неё:
cat /dev/ttyUSB0
Тут будет ответ.
В первой консоли испытываем:
echo 'Y+=ZA' > /dev/ttyUSB0
D13 загорелась
echo 'Y+=Za' > /dev/ttyUSB0
D13 погасла.
Если при посылке пакета ардуина перегружается (диоды моргают, но D13 не горит), тогда нужно поставить электролитический конденсатор 5-10мкФ между Reset и GND.
Не забудьте отключать его, когда заливаете скетч.
Существуют трудности у ардуин с мостом ch341
Если всё работает, то идём дальше.
Редактируем файл конфигурации ser2net:
nano /etc/ser2net.conf
Закомментируйте всё строчки в конце и сохраните.
Добавьте stty и ser2net в автозагрузку:
nano /etc/rc.local
Вот так нужно сделать:
stty -F /dev/ttyUSB0 cs8 57600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts -hupcl
ser2net -C "3002:raw:0:/dev/ttyUSB0:57600 NONE 1STOPBIT 8DATABITS -XONXOFF -LOCAL -RTSCTS"
exit 0
Обратите внимание. Строки инициализации должны быть записаны одной строкой (без переноса).
Перегрузите и проверьте.
echo 'Y+=ZA' > /dev/ttyUSB0
echo 'Y+=Za' > /dev/ttyUSB0
Теперь залейте в ардуину новый скетч:
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).Ардуина обрабатывает управляющий символ и отправляет ответ роутеру, который в свою очередь отдаст его клиенту. Работа с клиентом описана ниже.
Скачайте архив и распакуйте его в рабочую папку сервера (по умолчанию это /var/www), вот так – /var/www/knopki (у вас может быть своя папка).
В браузере зайдите по адресу ваш_роутер/knopki/. Если связь установлена, то вы увидите это:
Нажмите на D13 — загорится светодиод на ардуине и кнопка подсветится.
Суть работы
Обновление страницы:
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) находящемуся на сервере, который в свою очередь обращается к ардуине через сокет ser2net.
ser2net
ser2net — это крохотный прокси-серверок создающий соединение между сокетом и устройством (/dev/tty*).
Как показала практика, через сокет php-файл работает лучше, нежели обращаясь к устройству напрямую.
То есть, если пхп-файл открывает устройство вот так ( fopen('/dev/tty*', ...) ), то через некоторое время (несколько часов) всё зависает, а если так ( fsockopen(«localhost», 3002, $errno, $errstr, 1); ), то никаких проблем.
Мне кажется, что это как-то связано с переполнением буфера. И исходя из вышесказанного, хотелось бы услышать на этот счёт мнение Умных людей.
Как показала практика, через сокет php-файл работает лучше, нежели обращаясь к устройству напрямую.
То есть, если пхп-файл открывает устройство вот так ( fopen('/dev/tty*', ...) ), то через некоторое время (несколько часов) всё зависает, а если так ( fsockopen(«localhost», 3002, $errno, $errstr, 1); ), то никаких проблем.
Мне кажется, что это как-то связано с переполнением буфера. И исходя из вышесказанного, хотелось бы услышать на этот счёт мнение Умных людей.
<?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(); }
...
Если открыть ещё одну страничку (или зайти с другого устройства) и нажать какую-то кнопку, то на первой страничке (в течении 3 сек.) эта кнопка тоже станет включённой. Для этого обновление и нужно.
Нажатие на кнопку:
Нажатие на кнопку работает так же, как и «обновление», в ардуину отсылается символ включения или отключения (в зависимости от состояния), ардуина выполняет действие и посылает в ответ строку с флагами состояния (единица или ноль).
Ответ разбирается в html-странице и в зависимости от флагов выводит на экран нужную кнопку.
Для лучшего понимания откройте файл index.html из архива и посмотрите комментарии.
Если связи между ардуиной и роутером нет, то будет только красная надпись stD — это индикатор работы, когда связь установлена, надпись становится серой.
Подождите минуту и пообновляйте страницу. Если связь не устанавливается, то проверьте настройки роутера.
Проверьте права на файлы, правильность путей и устройств.
Внешний вид
Названия кнопок меняются в конце файла 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; /* цвет фона */
}
...
На этом, пожалуй, стоит закончить. Спасибо тем, кто потратил своё драгоценное. Если где-то допустил неточность, то прошу поправить меня.
«Дизаин» несколько мрачноват, но это пока всего лишь шаблон для демонстрации функций.
Статья писалась под великолепную музыку «Nirvana».