Trwająca od kilku lat rewolucja Internet of Things nabiera tempa, jednak brak jednolitych standardów powoduje, że rynek ten jest wciąż mocno zdefragmentowany, a każdy z producentów sprzętu i oprogramowania próbuje „zagarnąć” jak największy kawałek tortu dla siebie. Po swoich nie do końca udanych początkach z systemami Android@Home oraz Brillo, do działania ponownie wkracza jeden z największych graczy na rynku – Google. Czy połączenie zmodyfikowanego na potrzeby rynku IoT systemu Android wraz z najbardziej popularnymi platformami sprzętowymi może się nie udać? Sprawdźmy to!
Android Things – nowy/stary system operacyjny dla urządzeń IoT
Obserwując od kilku lat rosnący rynek urządzeń IoT można było odnieść mylne wrażenie, że firma Google pozostaje w cieniu rozpoczynającej się rywalizacji, a wszystkie dotychczasowe działania firmy skupione są wyłącznie na segmencie urządzeń mobilnych, gdzie system Android już od kilku lat wiedzie niepodważalny prym. W tym czasie organizacje/projekty takie jak IoTivity [1] czy AllJoyn [2] zaczynają zrzeszać pod banderą Linux Foundation największych graczy na rynku taki jak Intel, Samsung czy LG. Czy Google naprawdę „przespało” ten okres?
Nic bardziej mylącego! O pierwszych działaniach firmy Google na rynku urządzeń IoT (a uściślając – w obszarze systemów automatyki domowej) można było usłyszeć już w 2011 roku podczas konferencji Google I/O [3]. Zaprezentowany wówczas projekt Android@Home zakładał stworzenie kompletnego systemu automatyki domowej, której poszczególne urządzenia pracują pod kontrolą systemu operacyjnego Android (nazwanego wówczas dumnie „systemem operacyjnym dla Twojego domu”). Jednym z pierwszych oficjalnych produktów spod logo projektu Android@Home miała być inteligentna żarówka stworzona przy współpracy z firmą LightingScience. Niestety mimo wielu obiecujących zapowiedzi, idea projektu Android@Home upadła dość szybko – zapowiedziane produkty nie pojawiły się na rynku, firma LightingScience usunęła ze swojej strony materiały zapowiadające nowy projekt, a Google nie podjęło już w swych wystąpieniach tematu Android@Home.
Od czasu Android@Home mija kilka lat, gdy podczas kolejnej konferencji Google I/O w 2015 roku firma zapowiada projekt Brillo – bazujący na Androidzie system operacyjny dla niewielkich urządzeń IoT wyposażonych w 32–64 MB RAM. Projekt ten w początkowej fazie zostaje entuzjastycznie przyjęty, jednak brak w nazwie projektu – mogącego działać jak magnes – kluczowego słowa „Android” nie był przypadkowy. Główne środowisko deweloperskie projektu Brillo bazowało na języku C++ i nie przypominało ono deweloperom urządzeń mobilnych dobrze znanych „schematów” z pracy w środowisku Android. Być może właśnie to było przyczyną, że niewielu programistów skupionych wówczas wkoło projektu Android zdecydowało się na podjęcie eksperymentów z systemem Brillo. Z biegiem kolejnych miesięcy, projekt Brillo zaczął pokrywać się kurzem.
Sytuacja zmieniła się pod koniec roku 2016, kiedy to firma Google ogłosiła swój najnowszy projekt dla urządzeń IoT – system Android Things. „Nowy” projekt giganta z Mountain View jest właściwie kontynuacją systemu Brillo, w którym zdecydowano się uwzględnić większość krytycznych uwag pochodzących od developerów Brillo. Do najważniejszych zmian należy zaliczyć możliwość tworzenia aplikacji w języku Java z wykorzystaniem Android SDK, Google API oraz dobrze znanego programistom urządzeń mobilnych – pakietu Android Studio. Tym samym – jak zostanie to przedstawione w dalszej części niniejszego artykułu – przygotowanie aplikacji dla systemu Android Things niewiele różni się od tworzenia typowej aplikacji dla urządzeń mobilnych. Co więcej, firma Google przy współpracy z producentami najbardziej popularnych sprzętowych zestawów deweloperskich przygotowała gotowe obrazy dla takich platform jak: Intel Edison oraz Joule, NXP Pico i.MX7D, NXP i.MX6UL, NXP Argon i.MX6UL oraz Raspberry Pi 3. Tak więc wszystkie znaki na niebie i ziemi wskazują, że tym razem musi się udać.
Zanim przystąpimy do realizacji pierwszego projektu z wykorzystaniem Android Things, warto krótko scharakteryzować sam system i odpowiedzieć na pytanie, ile właściwie jest Androida w projekcie Android Things.
System Android Things jest zmodyfikowaną wersją standardowego projektu Android, dostosowaną do potrzeb typowych systemów embedded (a więc w odróżnieniu od współczesnych telefonów – systemów pełniących jedną, ściśle określoną funkcję, jak np. sterowanie ogrzewaniem w systemie inteligentnego budynku). Ponieważ standardowe API systemu Android nie umożliwia programiście bezpośredniej obsługi najbardziej typowych dla systemów embedded komponentów sprzętowych (takich jak np. porty GPIO, PWM czy magistrale UART, SPI, I2C i I2S), interfejs programowy (API) systemu Android Things został rozbudowany o bibliotekę Things Support Library, umożliwiającą prostą obsługę „sprzętowego otoczenia” naszego projektu (rysunek 1).
Zgodnie z „charakterem pracy” urządzeń embedded, system operacyjny Android Things został zoptymalizowany pod kątem pracy z jedną aplikacją użytkownika, pełniącą funkcję głównej aktywności i uruchamianą tuż po starcie systemu. W urządzeniach z systemem Android Things wyświetlacz pełni funkcję opcjonalną i nie jest wymagany do pracy urządzenia. Dla wygody programistów chcących zbudować aplikację z graficznym interfejsem użytkownika, w systemie Android Things zdecydowano się pozostawić standardowy UI toolkit, umożliwiający budowanie funkcjonalnych interfejsów za pomocą graficznego edytora lub plików opisu XML.
Zmiana charakteru pracy systemu operacyjnego Android (z oprogramowania zarządzającego pracą wielofunkcyjnego telefonu na ściśle ukierunkowane urządzenie wbudowane) pozwoliła na „odchudzenie” systemu poprzez usunięcie aplikacji systemowych i wielu innych komponentów programowych. Tak więc, przygotowując aplikację dla systemu Android Things, warto pamiętać, że nie skorzystamy z takich standardowych dostawców treści (Content Providers) [4] jak: CalendarContract, ContactsContract, DocumentsContract, DownloadManager, MediaStore, Settings, Telephony, UserDictionary oraz VoicemailContract. System Android Things wspiera również skróconą listę komponentów z Google API – zbiorcze zestawienie umieszczono w tabeli 1.
Mimo znacznego odchudzenia Android Things, podczas pracy z systemem należy pamiętać, że wciąż mamy do czynienia ze standardowym ART (Android Runtime), które nie zawsze jest demonem wydajności. Niskopoziomowi programiści systemów embedded pracujący z językami C/C++ w dystrybucjach Linuksowych mogą odczuwać „niedosyt” z szybkości działania systemu (analizując go np. poprzez porównanie maksymalnej szybkości zmian stanów na wyprowadzeniach GPIO). Do kogo zatem jest adresowany Android Things?
W niniejszym artykule – do testów systemu Android Things – wybrano jedną z najbardziej popularnych płytek deweloperskich – Raspberry Pi 3. Przy współpracy z Raspberry Pi Foundation, firma Google przygotowała gotowe do pobrania obrazy systemu dostępne pod adresem https://goo.gl/eQLXKT.
Procedura przygotowania karty pamięci SD z systemem Android Things jest analogiczna do instalacji innych systemów operacyjnych (Raspbian, Ubuntu, itp.) dla zestawu Raspberry Pi. W pierwszym kroku, w czytniku kart SD naszego komputera umieszczamy kartę pamięci o pojemności minimum 8 GB. Następnie po pobraniu i rozpakowaniu archiwum androidthings_rpi3_devpreview_4_1.zip, zawierającego obraz systemu Android Things, rozpoczynamy proces wgrywania obrazu na kartę SD. W systemie operacyjnym Linux, operację tę wykonamy za pomocą narzędzia dd [6] dd bs=4M if=iot_rpi3.img of=/dev/sd<X>, natomiast w środowisku Windows, wgranie obrazu systemu na kartę pamięci może zostać zrealizowane za pomocą narzędzia Win32DiskImager [7].
Po zakończonej sukcesem operacji instalacji obrazu przygotowaną kartę SD umieszczamy w slocie karty pamięci zestawu Raspberry Pi, natomiast do złącza HDMI podłączamy zewnętrzny monitor. Taki minimalny zestaw połączeń pozwoli nam upewnić się, że przygotowanie karty SD z systemem zostało zrealizowane w prawidłowy sposób. Po włączeniu zasilania na ekranie zostanie wyświetlono animacja startowa, jak na rysunku 2.
Należy podkreślić, że pierwsze uruchomienie Android Things jest dość czasochłonne (proces ten może trwać około 3 minut), a pomiędzy kolejnymi etapami startu systemu mogą występować momenty, kiedy ekran nie wyświetla żadnego obrazu. Ponieważ dioda aktywności systemu umieszczona na płytce Raspberry Pi nie jest obsługiwana, urządzenie może stwarzać wrażenie „zawieszonego” – mając jednak na uwadze, że Android Things to wciąż wersja „developer preview” – uzbrójmy się w chwilę cierpliwości.
Istnieje również możliwość podłączenia zestawu Raspberry Pi do sieci Wi-Fi, jednak w przypadku systemu Android Things, zestawienie takiego połączenia jest bardziej pracochłonne. Ponieważ graficzny ekran startowy systemu uniemożliwia nam przeprowadzenie jakichkolwiek interakcji z systemem, do ustanowienia połączenie Wi-Fi niezbędne jest uprzednie zalogowanie się do konsoli systemu poprzez port UART, z wykorzystaniem sprzętowego konwertera UART TTL<–>USB oraz wybranego oprogramowania umożliwiającego obsługę portu szeregowego w konfiguracji 115200 8N1. Schemat wymaganych połączeń dla zestawu Raspberry Pi 3 został przedstawiony na rysunku 3.
$ am startservice
-n com.google.wifisetup/.WifiSetupService
-a WifiSetupService.Connect
-e ssid <NAZWA SIECI>
-e passphrase <HASŁO>
Poprawność nawiązania połączenia może zostać zweryfikowana poleceniem:
$ logcat -d | grep Wifi
...
V WifiWatcher: Network state changed to CONNECTED
V WifiWatcher: SSID changed: ...
I WifiConfigurator: Successfully connected to ...
Jeżeli konfiguracja sieci została przeprowadzona prawidłowo, na ekranie startowym zostanie wyświetlony przydzielony adres IP, a ustawienia sieci zostaną zapisane – urządzenie spróbuje zestawić to samo połączenie przy kolejnym starcie systemu. Aby skasować listę zapisanych sieci Wi-Fi, w terminalu systemu należy wydać polecenie:
$ am startservice
-n com.google.wifisetup/.WifiSetupService
-a WifiSetupService.Reset
Przygotowanie środowiska programistycznego
Jak wspomniano we wstępnie, jedną z niewątpliwych zalet systemu Android Things jest możliwość pracy w wygodnym i funkcjonalnym środowisku Android Studio, będącym oficjalnym IDE dla systemu Android. Ostatnią stabilną wersję pakiet Android Studio (w chwili tworzenia artykułu jest to wersja oznaczona numerem 2.3.3) można pobrać pod adresem https://goo.gl/6qBMkm.
Przy pierwszym uruchomieniu środowiska Android Studio, niezbędne jest uruchomienie menedżera oprogramowania SDK Manager (rysunek 4) oraz pobranie najnowszych narzędzi SDK Tools w wersji 25.0.3 lub wyżej, oraz SDK Platforms dla Androida 7.0 (wersja API 24) – jak przedstawiono to na rysunku 5 oraz rysunku 6.
Pierwszy projekt – sterowanie GPIO
Zarówno platforma sprzętowa, jak i środowisko programistyczne jest już gotowe na przygotowanie pierwszego prostego projektu. Zgodnie z niepisaną zasadą, będzie to Hello World w wersji na systemy embedded, czyli prosty projekt obsługujący diody LED oraz jeden przycisk.
Na obecnym etapie konfiguracji projektu przygotowanie aplikacji jest tożsame z tworzeniem aplikacji dla urządzeń mobilnych. Pierwszym z wyróżników jest konieczność włączenia wsparcia dla biblioteki Things Support Library, niedostępnej w standardowym API. W tym celu, edytujemy plik app/build.gradle w sekcji dependencies:
...
provided ‚com.google.android.things:androidthings:0.4.1-devpreview’
}
oraz plik AndroidManifest.xml:
<application ...>
<uses-library android:name=”com.google.android.things”/>
...
</application>
Na obecnym etapie edycji projektu uzyskano gotowy szkielet aplikacji dla systemu Android Things. Aby sprawdzić poprawność wprowadzonych zmian, zbudujmy projekt poprzez wybranie opcji Build Make Project (lub wybierając skrót klawiszowy Ctrl+F9).
Zanim przystąpimy do wykonania połączeń sprzętowych (podłączenia diod LED i przycisku) oraz implementacji obsługi programowej, spróbujmy uruchomić szkielet naszej aplikacji na platformie docelowej. Uruchomienie zbudowanej uprzednio aplikacji odbywa się poprzez wybranie opcji „Run Run ‘app’” (lub skrótu klawiszowego Shift+F10). Użytkownik zostanie poproszony o wybranie platformy docelowej, jak przedstawiono to na rysunku 11. Ponieważ nie zestawiliśmy dotychczas połączenia z platformą Raspberry Pi, lista dostępnych urządzeń jest pusta. Aby nawiązać połączenie z docelową platformą deweloperską, za pomocą terminalu systemu operacyjnego wydajemy polecenie:
* daemon not running. starting it now on port 5037 *
* daemon started successfully *
connected to 192.168.0.129:5555
Aby upewnić się, że połączenie zostało zestawione prawidłowo, wykorzystując ponownie program adb [10], możemy wyświetlić listę aktualnie podłączonych urządzeń:
[user@archlinux ~]$ adb devices
List of devices attached
192.168.0.129:5555 device
Tym samym lista urządzeń w programie Android Studio, na których możemy uruchomić szkielet naszej aplikacji, została poszerzona o nowy wpis (rysunek 12).
Wybierając docelową platformę, rozpoczynamy proces instalacji aplikacji. Ponieważ jest to proces dość czasochłonny, w oczekiwaniu na wyświetlenie napisu Hello World, zrealizujmy na płytce prototypowej połączenia diod LED i przycisku. Zadaniem przykładowej aplikacji będzie wyświetlenie prostych efektów świetlnych na dwóch diodach LED, przełączanych za pomocą pojedynczego przycisku. Schemat połączeń przedstawiono na rysunku 13.
Każda aplikacja przygotowana na platformę Android ma własny „cykl życia”, podczas którego może znajdować się ona w jednym z określonych stanów [11] (m.in. aplikacja jest uruchamiana, zatrzymana, przerwana lub zakończona). Programista aplikacji nie ma wpływu na stan, w jakim obecnie znajduje się aplikacja, jednak może zapewnić prawidłową programową obsługę każdego z tych stanów. Obsługa ta jest realizowana poprzez przedefiniowanie kilku kluczowych metod (takich jak m.in. onCreate(), onDestroy(), onResume(), onPause()) w klasie dziedziczącej po klasie android.app.Activity. Cykl życia aplikacji Android wymaga, aby została wywołana metoda onCreate(), którą każda klasa pochodna od Activity (w tym MainActivity z omawianego przykładu) nadpisuje celem wykonania własnych operacji inicjalizujących. Ponieważ w systemie Android Things mamy do czynienia wyłącznie z jedną aplikacją użytkownika, która nie może być przeniesiona do „tła” celem uruchomienia innej aplikacji, nasz program znajduje się w jednym z dwóch stanów: uruchomienia lub zatrzymania. Tym samym rolą programisty jest nadpisanie metody onCreate() – odpowiadającej za inicjalizację aplikacji oraz metody onDestroy(), która umożliwi poprawne jej zamknięcie. Zaktualizowany kod klasy MainActivity został przedstawiony na listingu 3.
W systemie Android Things, dostęp do określonych zasobów sprzętowych (takich jak: GPIO, I2C, I2S, SPI, UART czy PWM) realizowany jest poprzez obiekt PeripheralManagerService [12] z biblioteki Things Support Library oraz jedną z szeregu metod przedstawionych w tabeli 2.
Konfiguracja portów GPIO jest natomiast realizowana poprzez zbiór metody klasy Gpio [13] – tabela 3. Inicjalizacja wyprowadzeń BCM4 oraz BCM17 jako portów wyjściowych (do których zostały podłączone diody LED) została przedstawiona na listingu 4.
W analogiczny sposób konfigurujemy przycisk podłączony do wyprowadzenia BCM23, pełniącego funkcję wejścia – listing 5.
Konfiguracja wejścia wymaga również określenia zbocza sygnału, które wyzwoli funkcję obsługi przycisku, wskazaną poprzez registerGpioCallback() – listing 6.
Do uzyskania finalnego efektu pozostało jedynie poprawne zamknięcie aplikacji poprzez zwolnienie zasobów w metodzie onDestroy() oraz implementacja prostej funkcji (z wykorzystaniem interfejsu Runnable [14]), która z zadanym interwałem czasowym zmieni stan logiczny na wyprowadzeniach BCM4 oraz BCM17. Kompletny kod klasy MainActivity został przedstawiony na listingu 8.
Łukasz Skalski
Linki zewnętrzne:
1. https://goo.gl/WzJu5p
2. https://goo.gl/jbSDuS
3. https://goo.gl/sDYK7V
4. https://goo.gl/Es3zWk
5. https://goo.gl/QUGeCX
6. https://goo.gl/mK9Dgu
7. https://goo.gl/yLZ8nh
8. https://goo.gl/eUYxqd
9. https://goo.gl/UcL8GN
10. https://goo.gl/EkPR28
11. https://goo.gl/Q7d3YT
12. https://goo.gl/HgLNyA
13. https://goo.gl/mX8GPR
14. https://goo.gl/6XmXVq