Подключение Ардуино к роутеру TL-MR3020
1

Удалённое управление Arduino через Web-интерфейс...
Предполагается, что на роутере установлена OpenWrt и система перенесена на флешку. Как это сделать я подробно описал здесь.
Если OpenWrt установлена, тогда переходим к основной задаче.
Существуют два способа подключения, первый — к UART роутера, второй — через USB.
Оба варианта работают одинаково, однако первый требует разбора роутера и подпаивания контактов:

Второй вариант проще, но придётся ипользовать usb-хаб.

Я опишу оба способа подключения и покажу как сделать простой веб-интерфейс для управления.
Кто будет подключать по usb, может сразу перейти сюда.
Вариант с UART
Вскрываем роутер. Крышка у него приклеена, поэтому берём что-то типа ножа и ковыряем по всему периметру. Пластик достаточно прочный, так что можно не боятся повредить.
Достаём плату и припаиваем три контакта RX, TX и GND, четвёртый контакт — это плюс (3,3V), он нам не нужен.

Теперь зальём в ардуину простенький скетч для проверки.
int led = 13;
void setup()
{
Serial.begin(57600);
pinMode(led, OUTPUT);
}
void loop()
{
if (Serial.available())
{
byte insim = Serial.read();
switch (insim)
{
case 'a':
digitalWrite(led, HIGH);
break;
case 'b':
digitalWrite(led, LOW);
break;
}
}
}
Будем посылать в ардуину символы a и b, в ответ на которые будет зажигаться и гаснуть D13.
Подключаем ардуину как на рисунке:

Ардуина TX ⇨ RX Роутера (синий)
Роутер TX ⇨ RX Ардуина (зелёный)
CND ⇨ CND
Не смотря на то, что чип роутера питается от 3,3V, а ардуина от 5V, никаких проблем не возникает, посему нет необходимости согласовывать уровни.
Подключаем сетевой кабель (или не подключаем если Вы соединяетесь по WIFI) и подаём питание на роутер и ардуину.
Заходим на роутер по ssh (на всякий случай)
Ради интереса смотрим существующие устройства:
ls /dev/tty*
В списке будет присутствовать ttyATH0, это и есть UART.
Обновляем репозиторий…
opkg update
Установим утилиту для настройки порта:
opkg install coreutils-stty

Настроим порт командой…
stty -F /dev/ttyATH0 cs8 57600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts -hupcl
Проверяем…
echo 'a' > /dev/ttyATH0
D13 загорелась.
echo 'b' > /dev/ttyATH0
D13 погасла.
Должно работать, если нет, то возвращаемся и проверяем что не так.
Доведём до ума:
Если у Вас не установлен редактор nano, то исправим ситуацию...
opkg install nano
Добавим в автозагрузку настройку порта:
nano /etc/rc.local
В конец файла (перед exit 0) добавим строчку:
stty -F /dev/ttyATH0 cs8 57600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts -hupcl
подсказка nano
Сохранить______Выйти

Сохранить______Выйти
Перегружаем роутер и проверяем…
reboot
echo 'a' > /dev/ttyATH0
echo 'b' > /dev/ttyATH0
Поскольку при загрузке (да и в процессе работы) в консоль прилетают различные символы, нам надо их отфильтровывать, а также сделать обратную связь, чтоб в ответ на команду ардуина сообщала о выполнении.
Залейте в ардуину этот скетч: Не забывайте отсоединять провода RX,TX во время прошивки.
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 'b':
digitalWrite(led, LOW);
Serial.println("OK otkl"); // ответ
break;
}
}
else
{
for(byte i=0; i < 255; i++)
{
Serial.read();
}
}
}// конец if (Serial.read()=='Y')
} // конец чтение порта
}
Перед управляющим символом (a,b) будем отправлять четыре символа служащие дескриптором Y+=Z, благодоря этому всё что не нужно отфильтруется и не будет случайных срабатываний.
Открываем параллельно ещё одну ssh-сессию и вводим там команду:
cat /dev/ttyATH0
Здесь будет ответ ардуины.
Проверяем...
echo 'Y+=Za' > /dev/ttyATH0
echo 'Y+=Zb' > /dev/ttyATH0
И последнее, надо отвязать UART от отладочной консоли. Для этого надо в файле /etc/inittab закомментировать последнюю строчку:
nano /etc/inittab
Вот так:
::sysinit:/etc/init.d/rcS S boot
::shutdown:/etc/init.d/rcS K shutdown
#ttyATH0::askfirst:/bin/ash --login
Сохраняем, перегружаем.
Если что-то не работает, то возвращаемся к началу и проверяем что сделали не так.
С первым вариантом покончено, если подключение по usb не интересно, то переходите ниже...
Вариант с USB
Зальём в ардуину проверочный скетч:
int led = 13;
void setup()
{
Serial.begin(57600);
pinMode(led, OUTPUT);
}
void loop()
{
if (Serial.available())
{
byte insim = Serial.read();
switch (insim)
{
case 'a':
digitalWrite(led, HIGH);
break;
case 'b':
digitalWrite(led, LOW);
break;
}
}
}
Будем посылать в ардуину символы a и b в ответ на которые будет зажигаться и гаснуть D13.
Подключаем к роутеру хаб и втыкаем в него флешку и ардуину. Включаем.
Желательно чтобы хаб был с отдельным питанием. Некоторые хабы работают некорректно.
Заходим на роутер по ssh (на всякий случай)
Обновляем репозиторий…
opkg update
Установим драйвера для всех существующих ардуин и утилиту для настройки порта stty:
opkg install kmod-usb-serial-ftdi kmod-usb-acm kmod-usb-serial-pl2303 kmod-usb-serial-cp210x libftdi coreutils-stty
reboot
Можно не перегружать, по идее устройство должно появиться сразу.
Проверим… если нет, тогда перегрузите.
ls /dev/tty*

У Вас может быть /dev/ttyACM0, тогда его и используйте в дальнейших командах и настройках.
Настроим порт командой…
stty -F /dev/ttyUSB0 cs8 57600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts -hupcl
Проверяем…
echo 'a' > /dev/ttyUSB0
D13 загорелась.
echo 'b' > /dev/ttyUSB0
D13 погасла.
Если при посылке пакета ардуина перегружается (диоды моргают, но D13 не горит), тогда нужно поставить электролитический косденсатор

Не забудьте отключать его когда заливаете скетч.
Далее сделаем защиту от случайных срабатываний и обратную связь, чтоб в ответ на команду ардуина сообщала о выполнении.
Если редактор nano отсутствует, то установим...
opkg install nano
Добавим в автозагрузку настройку порта:
nano /etc/rc.local
В конец файла (перед exit 0) добавим строчку:
stty -F /dev/ttyUSB0 cs8 57600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts -hupcl
подсказка nano
Сохранить______Выйти

Сохранить______Выйти
Перегружаем…
reboot
… и проверяем…
echo 'a' > /dev/ttyUSB0
echo 'b' > /dev/ttyUSB0
Теперь выключаем роутер и прошиваем в ардуину этот скетч:
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 'b':
digitalWrite(led, LOW);
Serial.println("OK otkl"); // ответ
break;
}
}
else
{
for(byte i=0; i < 255; i++)
{
Serial.read();
}
}
}// конец if (Serial.read()=='Y')
} // конец чтение порта
}
Перед управляющим символом (a,b) будем отправлять четыре символа служащие дескриптором Y+=Z, таким образом отфильтруется случайный мусор и не будет случайных срабатываний.
После обработки команды, ардуина будет отправлять ответ.
Возвращаем ардуину в хаб и включаем роутер.
Открываем две параллельные ssh-сессии, в первой водим команду:
cat /dev/ttyUSB0
Здесь будет ответ ардуины.
Во второй пробуем…
echo 'Y+=Za' > /dev/ttyUSB0
echo 'Y+=Zb' > /dev/ttyUSB0
Всё должно работать, если нет, то возвращаемся и внимательно проверяем.
Если всё получилось, то можно переходить к следующей части.
Интерфейс
Сделаем простой веб-интерфейс для управления двумя лампочками.
Выглядеть будет вот так… Можно понажимать.
Скачайте архив и распакуйте его в рабочую папку сервера, чтоб было так сервер/primer/.
Здесь подробная инструкция по установке сервера Lighttpd на OpenWrt.
Проверьте, чтоб в файле /etc/php.ini всё было так, как написано здесь!
Если Вы пользуете Win, то отключите всякие файрволы/антивирусы!
Установим и настроим небольшой прокси-сервер ser2net, он создаёт соединение между сокетом и устройством (/dev/ttyUSB0).
Как показала практика, через ser2net, php-файл работает лучше, нежели обращаясь к устройству напрямую.
opkg update
opkg install ser2net
Редактируем файл конфигурации:
nano /etc/ser2net.conf
Закомментируйте всё строчки в конце и сохраните.
Добавим ser2net в автозагрузку:
nano /etc/rc.local
Добавьте в конец файла вот эту строку:
ser2net -C "3002:raw:0:/dev/ttyUSB0:57600 NONE 1STOPBIT 8DATABITS -XONXOFF -LOCAL -RTSCTS"
Должно получится так:
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
В примере используется устройство /dev/ttyUSB0, у Вас может быть другое! (ttyATH0 — консоль, ttyACM0 — мега)
Внимание! Строки инициализации должны быть записаны одной строкой (без переноса).
Прошейте в ардуину этот скетч:
int descript[5];
int R1 = 0; // флаг первой лампы D12
int R2 = 0; // флаг второй лампы D13
void setup()
{
Serial.begin(57600);
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':
digitalWrite(12, HIGH);
R1 = 1; // ставим флаг в единицу (вкл)
glavnaia();
break;
case 'a':
digitalWrite(12, LOW);
R1 = 0; // ставим флаг в ноль (откл)
glavnaia();
break;
case 'B':
digitalWrite(13, HIGH);
R2 = 1;
glavnaia();
break;
case 'b':
digitalWrite(13, LOW);
R2 = 0;
glavnaia();
break;
}
}
else
{
for(byte i=0; i < 255; i++)
{
Serial.read();
}
}
}// конец if (Serial.read()=='Y')
} // конец чтение порта
}
void glavnaia() // отправка ответа
{
Serial.print(R1); // отсылаем флаг
Serial.print(","); // запятая для парсинга строки
Serial.println(R2); // отсылаем флаг !!!последняя строчка должна быть println!!!
}
Добавлены функции для второй лампочки и обновления. (к D12 подключите светодиод через резистор 200-1000 Ом)
Включаем/перегружаем роутер и в браузере заходим по аресу ваш_роутер/primer/
Должно получится так:

Если надпись stD серая, это значит что связь с ардуиной установлена, если красная, то связи нет.
Работа заключается в следующем:
index.html раз в три секунды (интервал можно изменить) запрашивает данные у ардуины (отправляя ей символ о) с помощью функции ajax (ajax позволяет не перегружать страницу).
...
function show() /* функция обновления */
{
$.ajax({
type: "GET",
url: "box2.php?df=o",
timeout:400, /* если до роутера "длиные пинги" то этот параметр нужно увеличивать */
cache: false,
success: function(data)
{
var vars = data.split(","); /* разбор строки принятой от ардуино */
if(vars.length == dlina) /* проверка длины данных (количество блоков разделённых запятой) */
{
$('#std').html("<v2>stD</v2>"); /* если данные приняты правильно, то надпись stD будет серой */
/* lampR1 */
/* если принята единица, то рисуем жёлтую лампочку. Переменной присваевается символ, который будет отослан при следующем нажатии */
if(vars[0]==1) $('#lampR1').html('<img src="images/onlamp.png">'),(r1 = 'a');
/* если принят ноль, то рисуем серую лампочку. Переменной присваевается символ, который будет отослан при следующем нажатии */
else if(vars[0]==0) $('#lampR1').html('<img src="images/offlamp.png">'),(r1 = 'A');
/* lampR2 */
if(vars[1]==1) $('#lampR2').html('<img src="images/onlamp.png">'),(r2 = 'b');
else if(vars[1]==0) $('#lampR2').html('<img src="images/offlamp.png">'),(r2 = 'B');
}
else
{
$('#std').html("<v>stD</v>"); /* если данные приняты НЕправильно, то надпись stD будет красной */
}
}
});
}
$(document).ready(function()
{
show();
setInterval('show()',3000); /* частота обновления */
});
...
Запрос передаётся php-файлу (box2.php) находящемуся на сервере, который в свою очередь обращается к ардуине через сокет ser2net.
<?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 (server[3]) // simvol
{
case 'o': // obnovlenie
glavnaia();
break;
...
void glavnaia() // отправка ответа
{
Serial.print(R1); // отсылаем флаг
Serial.print(","); // запятая для парсинга строки
Serial.println(R2); // отсылаем флаг !!!последняя строчка должна быть println!!!
}
Html-страничка разбирает ответ и выводит значения на экран.
...
success: function(data)
{
var vars = data.split(","); /* разбор строки принятой от ардуино */
if(vars.length == dlina) /* проверка длины данных (количество блоков разделённых запятой) */
{
$('#std').html("<v2>stD</v2>"); /* если данные приняты правильно, то надпись stD будет серой */
/* lampR1 */
/* если принята единица, то рисуем жёлтую лампочку. Переменной присваевается символ, который будет отослан при следующем нажатии */
if(vars[0]==1) $('#lampR1').html('<img src="images/onlamp.png">'),(r1 = 'a');
/* если принят ноль, то рисуем серую лампочку. Переменной присваевается символ, который будет отослан при следующем нажатии */
else if(vars[0]==0) $('#lampR1').html('<img src="images/offlamp.png">'),(r1 = 'A');
...
Если открыть ещё одну страничку (или зайти с другого устройства) и включить лампочку, то на первой страничке (в течении 3 сек.) тоже включится лампочка.
Для этого и нужно обновление.
Нажатие на лампочку работает так же как и «обновление», в ардуину отсылается символ включения или отключения (в зависимости от состояния лампочки), ардуина выполняет действие и посылает в ответ строку с флагами состояния (единица или ноль). Ответ разбирается в html-странице и в зависимости от флагов выводит картинку включённой или отключённой лампочки.
Для лучшего понимания откройте файл index.html из архива, и посмотрите комментарии.
Внимание! Если Вы редактируете файл на роутере, то удалите все комментарии, в противном случае могут возникнуть проблемы с русской кодировкой.
Если редактируете файлы на виндовс-машине, то пользуйтесь редактором Notepad++.
Если что-то не так, то возвращаемся и проверяем всё с удвоенным вниманием. Проверяем права на файлы, правильность путей и устройств.
В следующей части — «умный дом» на основе ардуино.
Вот тут можно скачать библиотеку для разгона Arduino.
Телеграм-чат istarik

- +888
- stD
53166
Поддержать автора
Комментарии (45)
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
PS. Роутер у меня TP-Link TL-WR842ND с OpenWrt AA 12.09 на борту.
Дайте вот такую команду
И выложете сюда вывод.
Но на ttyS0 тишина…
istarik.ru/uploads/images/00/00/01/2015/04/24/93ab06.png
Замкните на ардуине RST и GND, включите её в компьютер, откройте Serial Monitor (115200) и перегрузите роутер. Вы должны увидеть лог загрузки. Если его нет, поменяйте местами rx-tx.
Не могу понять, для чего в PHP скрипте используется условный оператор, который в обоих случаях делает то же самое, но только во втором с задержкой. Для чего?
Задержка сделана на тот случай, если в данный момент сокет занят другим клиентом.
…
Я Вам рекомендую попробовать вот это, без пхп.
После того как светодиод начал быстро моргать, выполнил команду mount_rootЮ а затем mtd -r erase rootfs_data. Что при этом восстанавливается? по крайней мере доступ по 192.168.0.254 не восстановился.
root@OpenWrt:~# opkg update
Downloading istarik.ru/packages/mr3020/packages//Packages.gz.
wget: bad address 'istarik.ru'
Collected errors:
* opkg_download: Failed to download istarik.ru/packages/mr3020/packages//Packages.gz, wget returned 1.
в чем дело?
внутри написано
src/gz attitude_adjustment istarik.ru/packages/mr3020/packages/
dest root /
dest ram /tmp
lists_dir ext /var/opkg-lists
option overlay_root /overlay
дошел до управления через веб. при этом на нажатия ws ad робот не реагирует… я думаю проблема в связи- юарт роутера-ардуино…
вот хотел проверить вашим простым примером… а тут оказывается столько всего надо сделать…
поэтому вопрос непонятно почему автор ничего такого не делал, а у него все работает…
… и то, что написано у меня.
…
Впрочем это не важно, у Вас другая прошивка и часть программ не установятся, тем более ядерные модули.
если все сделаю по вашей инструкции и с вашей прошивкой может будет работать?
Делаю всё по инструкции для usb.
После загрузки ардуины первой тестовой программой подключаю её к роутеру через активный usb-хаб
ввожу в консоль:
echo 'a' > /dev/ttyUSB0
или
echo 'b' > /dev/ttyUSB0
И на любую из этих команд ардуина реагирует сиюсекундным разовым морганием светодиода, после чего светодиод погасает.
Что можно попробовать сделать? Очень хочется разобраться. Спасибо заранее =)
Происходит это, потому-что давая команду echo 'a' > /dev/ttyUSB0 открывается устройство и ардуина перегружается (как при подключении к компьютеру). Эта проблема решалась командой stty -F /dev/ttyUSB0 cs8 57600 ... с соответствующими параметрами, но она не работает с ардуинами, у которых в качестве usb-моста используется микросхема ch34x (которая у вас скорее всего стоит).
Спасибо.