Łączenie Arduino z OpenWrt

Od jakiegoś czasu “bawię się” Arduino, a swoje doświadczenia staram się tu opisywać. Pisałem też kiedyś o mojej koncepcji inteligentnego domu (lub jak kto woli domowej automatyki) w oparciu o OpenWRT. Do rozpoczęcia przygody z Arduino skłoniła mnie właśnie chęć rozbudowy tego systemu.

OpenWRT zainstalowany w routerze jest świetny, elastyczny, linuksowy, jednak trochę brakuje mu możliwości z realnym światem. Problem w dużej części rozwiązał zakup karty przekaźnikowej wyposażonej dodatkowo w wejścia, jednak cały czas mi czegoś brakowało.

Oczywiście mogłem też pójść w kierunku wykorzystania portów GPIO w routerach, ale dla mnie to mało eleganckie rozwiązanie. Trzeba rozbierać router, lutować, itp. Zaletą jest niewątpliwie, że wszystko jest obsługiwane w jednym miejscu - w routerze. Ale co jeśli router się popsuje - trzeba kupić nowy i znowu kombinować z rozkręcaniem, lutowaniem. A co jeśli taki model nie będzie już dostępny? Poza tym linii GPIO zdatnych do użytku nie ma zwykle wiele i nie nadają się do niektórych zastosowań.

Dlatego zainteresowałem się Arduino. Jest standardowe - jak się uszkodzi, to kupię nowe (albo nawet wezmę z szuflady zapasowe), wgram program i podmienię to, które się zepsuło. Do tego daje mi sporą ilość wejść/wyjść cyfrowych i wejścia analogowe. Ponieważ jest programowalne i może działać autonomicznie - gdy restartuję router może nadal sterować automatyką, nie otrzymując tylko dodatkowych danych z routera. Jeżeli się okaże, że muszę zainstalować je trochę dalej od routera, to dołożę port ethernet i podłączę je w innym miejscu domu. Do tego nie będę musiał obciążać routera analizą danych wejściowych - przykładowo Arduino samo zliczy ilość zużywanego węgla i routerowi udostępni odpowiedni licznik. Router pobierze te dane, kiedy będzie potrzebował.

Trzeba jednak jakoś połączyć te dwa elementy. Najprostszym (i najtańszym) rozwiązaniem jest podłączenie Arduino do routera kablem USB. Dodatkowym plusem takiego rozwiązania jest fakt, że zasilanie będzie pobierane z routera (w moim przypadku aktywnego huba usb).

Po podłączeniu mojego Arduino Uno do routera TP-Link TL-WR1043ND z automatu w systemie nie pojawił się nowy port. Konieczne było doinstalowanie modułu kmod-usb-acm (przez okpg update, a następnie opkg install kmod-usb-acm). Gdy instalacja się zakończyła dmesg pokazało informację o nowym urządzeniu (/dev/ttyACM0):

usbcore: registered new interface driver cdc_acm
cdc_acm: v0.26:USB Abstract Control Model driver for USB modems and ISDN adapters
usb 1-1.4.4: new full speed USB device using ar71xx-ehci and address 7
usb 1-1.4.4: configuration #1 chosen from 1 choice
cdc_acm 1-1.4.4:1.0: ttyACM0: USB ACM device

Warto w tym miejscu zaznaczyć, że to działanie jest właściwe dla Arduino Uno. Wcześniejsze wersje płytki, z tego co wiem, posiadały na pokładzie chip FT232RL od FTDI, więc spodziewam się, że do ich obsługi właściwy będzie moduł kmod-usb-serial-ftdi, jednak tego nie miałem okazji przetestować. W Uno za obsługę USB odpowiada atmega8u2, dzięki czemu port stał się bardziej uniwersalny (umożliwia pracę w różnych trybach).

Gdy port jest już widoczny w Arduino, można się do niego łączyć. Nie miałoby to jednak żadnego sensu, gdyby w Arduino nie było programu, który podjąłby komunikację. Wgywamy więc wcześniej prosty program:

// progam zaczyna liczyć od zera w górę
// wynik wysyła przez port szeregowy

long counter=0;

void setup() {
  // inicjalizacja portu na starcie
  Serial.begin(9600);
}

void loop() {
  Serial.println(counter); // wypisujemy licznik i przechodzimy do nowej linii
  counter++; // zwiększamy licznik
  delay(500); // czekamy pół sekundy
}

Teraz już można się połączyć. Jak? Można to zrobić na różne sposoby, ja preferuję php z wykorzystaniem doświadczeń (i bibliotek) zdobytych przy okazji obsługi karty przekaźnikowej . Mój program w php łączący się z Arduino wygląda tak:

<?php
include "php_serial.class.php";

$port="/dev/ttyACM0";

$serial = new phpSerial;
$serial->deviceClose($port);
$serial->deviceSet($port);
$serial->confBaudRate(9600);
$serial->confParity(none); //Set the Parity
$serial->confCharacterLength(8);//Set the word length
$serial->confStopBits(1); //Set Stop Bit
$serial->confFlowControl(none);

if( !$serial->deviceOpen("a+b") ) { echo "Nie mozna otworzyc portu"; exit; }

while(1)
{
$read = $serial->readPort();
echo $read;
sleep(1);
}

$serial->deviceClose();
?>

Po jego uruchomieniu (przez php-cli -f nazwapliku.php) na ekranie co sekundę pokazują się 2 liczby. Dlaczego co sekundę? Bo odczyty robimy w pętli co sekundę, a Arduino zwiększa licznik co pół sekundy. Co charakterystyczne po uruchomieniu programu licznik zawsze zaczyna od zera. Dlaczego? Bo Arduino Uno przy każdym przychodzącym połączeniu szeregowym resetuje się i oczekuje na program. Funkcjonalność ma ułatwić obsługę, żeby za każdym programowaniem nie było potrzeby wciskania przycisku “reset”. W moim przypadku jednak to przeszkadzało.

Jak wyłączyć auto-reset w Arduino Uno? Poza barbarzyńską metodą przecięcia jednej ze ścieżek na płytce, można podłączyć 10µF kondensator między złącza reset i masę. Jeżeli kondensator jest elektrolityczny (a jest), trzeba przy tym pamiętać, że ważna jest polaryzacja, więc do GND ma trafić nóżka oznaczona jako minus (zwykle białym paskiem na obudowie).

Po wykonaniu tej czynności, kolejne połączenia do Arduino nie powodują restartu urządzenia, dzięki czemu licznik zwiększa się nieprzerwanie. Testowo zostawiłem uruchomione Arduino na nieco ponad trzy tygodnie, łącząc się z nim od czasu do czasu w celu sprawdzenia stanu licznika. Nic nieprzewidzianego się nie wydarzyło, wszystko działało jak należy.

Na zakończenie dziękuję firmie Nettigo za bezpłatne udostępnienie urządzeń do testów.