Bluetooth Relay z Androidem

Bluetooth Relay z Androidem
Pobierz PDF Download icon
AVT-5295 to zestaw przygotowany przez Piotra Rosenbauma i opisany w numerze Elektroniki Praktycznej 06/2011. Choć zestaw nie jest najnowszy, cieszy się niemałym powodzeniem, szczególnie w dobie rosnącej popularności komunikacji bezprzewodowej. Pozwala w niedrogi sposób zrealizować zdalne sterowanie sześciu niezależnie włączanymi urządzeniami, a dodatkowo umożliwia monitorowanie temperatury. Niestety, od czasu powstania tego projektu, sytuacja w świecie elektroniki zmieniła się na tyle, że kontrola takiego urządzenia z komputera PC wydaje się mało atrakcyjnym rozwiązaniem. W dobie urządzeń mobilnych, sterowanie przekaźnikami przez Bluetooth z telefonu byłoby znacznie bardziej użyteczne. Dlatego prezentujemy nowe oprogramowanie do zestawu AVT-5295, przygotowane pod system Android.

Oryginalny projekt Piotra Rosenbauma zawierał skompilowane oprogramowanie pod system MS Windows. Opis dostarczony przez autora koncentrował się na części sprzętowej, a szczegóły dotyczące sposobu wymiany danych poprzez interfejs Bluetooth były niekompletne.

Jednakże zastosowanie oprogramowanie do monitorowania komunikacji przez interfejs szeregowy umożliwiło pełny reverse-engineering zastosowanego, prostego protokołu komunikacyjnego i odtworzenie przesyłanych komunikatów w nowym programie.

Aplikacja pod system Android nie została jedna stworzona w Javie, czyli w natywnym języku, z którego korzysta system operacyjny, ale z użyciem platformy Apache Cordova. Platforma ta stanowi bezpłatny zbiór bibliotek, tłumaczących polecenia javascriptowe na polecenia języków stosowanych natywnie w mobilnych systemach operacyjnych - w naszym przypadku - w Javy.

Cała platforma Cordova obejmuje też kompilatory i szereg pluginów. Korzystania z Cordovy można nauczyć się dzięki publikowanemu w Elektronice Praktycznej od lutego 2015 roku kursowi programowania aplikacji mobilnych. Niemal cała wiedza potrzebna do realizacji niniejszej aplikacji zawarta jest w pierwszych pięciu częściach kursu, przy czym w opisywanym programie, na potrzeby uzyskania odpowiedniego wyglądu aplikacji, skorzystano z języków HTML i CSS w stopniu wykraczającym poza zakres informacji ze wspomnianych części kursu.

Warto dodać, że udostępniamy czytelnikom nie tylko gotową, skompilowaną aplikację androidową, ale też jej pełny kod źródłowy, dzięki czemu każdy będzie mógł samodzielnie zmodyfikować wygląd i funkcje aplikacji, skompilować ją, a być może nawet przenieść na inny system operacyjny.

Opis protokołu

Tabela 1. Zestaw komend (znaków) przesyłanych przez aplikację do modułu z przekaźnikami

Protokół komunikacyjny, zastosowany w zestawie AVT5295 jest bardzo prosty i polega na przesyłaniu pojedynczych znaków lub ciągów znaków przez interfejs Bluetooth. Na początek urządzenie mobilne powinno samo nawiązać połączenie z wykrytym urządzeniem Bluetooth o nazwie "Bluetooth Adaptor".

Następnie zestaw z przekaźnikami zaczyna cyklicznie, co 1 sekundę, przesyłać ciąg znaków, zawierający aktualną temperaturę, odczytywaną z wbudowanego sensora. Każdy taki komunikat zakończony jest znakiem końca linii, takim jaki stosuje się w systemie Windows, czyli bajtami 0x0D i 0x0A, określanymi też mianem znaków CR i LF.

W systemie Windows te dwa następujące po sobie bajty są traktowane jako jeden znak końca linii, choć w praktyce są to następujące po sobie znaki "r" i "n". Gdyby chcieć przenosić aplikację na inny system, należy wziąć pod uwagę fakt, że w Linuksie jako znak końca linii używany jest tylko 0x0A, a w systemach MacOS - tylko 0x0D. Ale mniejsza z tym.

W naszym przypadku informacja o temperaturze zajmuje najczęściej 6 bajtów, na które składają się następujące znaki: dwie cyfry przed przecinkiem, znak przecinka (autor użył w tym celu kropki), jedna cyfra po przecinku i wspomniane dwa znaki końca linii (CR i LF). To jedyne dane, jakie moduł Bluetooth Relay przesyła do aplikacji.

Aplikacja natomiast steruje pracą modułu poprzez wysyłanie pojedynczych liter alfabetu. Moduł obsługuje 14 różnych poleceń, z których każde reprezentowane jest przez inny znak. Małe litery od "a" do "l" służą do włączania i wyłączania kolejnych przekaźników, a znaki "y" i "z" do włączania i wyłączania (odpowiednio) wszystkich przekaźników na raz. Pełna lista komend przesyłanych do modułu została zawarta w tabeli 1.

Wszystkie operacje związane z opóźnionym włączaniem lub wyłączaniem przekaźników oraz z kontrolą ich stanu są wykonywane w aplikacji. Aplikacja nie ma podglądu na rzeczywisty stan przekaźników ani nie może wysłać polecenia zmiany stanu przekaźnika za jakiś czas, np. po jej wyłączeniu. Prezentowany w aplikacji stan przekaźników jest kalkulowany w oparciu o polecenia wydawane przez użytkownika.

Konstrukcja aplikacji

Przygotowana aplikacja wykorzystuje oprogramowanie Apache Cordova i zawartą w niej platformę androidową. Ponadto użyto dwóch pluginów:

  • com.megster.cordova.bluetoothserial, potrzebnego do realizacji komunikacji przez interfejs Bluetooth,
  • org.apache.cordova.dialogs, ułatwiającego prezentowanie okien dialogowych.

Program opiera się o standardową, czystą aplikację Cordovy, w której zmieniono jedynie treść plików index. js, index.html i index.css. Ponadto skorzystano z biblioteki jQuery, którą dodano do zasobów w katalogu z plikami Javascripta oraz wprowadzono ikonkę, by zastąpić domyślną ikonę aplikacji Cordovy.

Działanie aplikacji rozpoczyna się od przypisania poleceń do wszystkich przycisków oraz inicjalizacji interfejsu Bluetooth. Po nawiązaniu połączenia z modułem z przekaźnikami, uruchamiane jest nasłuchiwanie na ciągi znaków, przesyłane interfejsem Bluetooth i zakończone znakami końca linii (CR i LF). Pozyskiwane ciągi znaków są prezentowane jako wskazanie temperatury, w górnej części aplikacji.

Rysunek 1. Główne okno aplikacji przed dokonaniem połączenia z modułem z przekaźnikami

Rysunek 2. Okno dialogowe z pytaniem o czas zwłoki

W przypadku naciśnięcia któregokolwiek z przycisków, aplikacja wysyła adekwatną komendę do modułu z przekaźnikami lub wyświetla okno dialogowe z prośbą o wskazanie opóźnienia, z jakim ma być wysłana dana komenda. Ponieważ logika programu jest napisana z użyciem języka Javascript, bardzo łatwo było zrealizować asynchroniczną pracę aplikacji.

W efekcie, nic nie stoi na przeszkodzie, by zażądać wykonania kilku opóźnionych operacji dla tego samego przekaźnika, wywoływanych w dowolnym czasie. Każda kolejna zlecona, opóźniona operacja nie wstrzymuje wykonywania poprzedniej, choć wyświetlany timer prezentuje odliczanie w dosyć specyficzny sposób: z upływem każdej jednostki czasu (godziny, minuty, sekundy), w jakiej podane było opóźnienie wykonania danej komendy, zmieniane jest wskazanie timera dla danego przekaźnika.

Jeśli przykładowo, dla przekaźnika pierwszego, prowadzone są jednocześnie trzy odliczania, liczby prezentujące pozostały czas dla poszczególnych timerów będą prezentowane naprzemiennie.

Oprócz tej różnicy z harmonogramowaniem opóźnionych komend dla przekaźników, działanie aplikacji mobilnej jest identyczne z działaniem oryginalnego programu dla systemu operacyjnego MS Windows. Naturalnie istotnie różny jest graficzny interfejs użytkownika.

Szczegóły implementacji HTML

Listing 1. Fragmenty kodu HTML aplikacji

Na samym początku sekcji BODY znajduje się warstwa "connection", która informuje o aktualnym stanie połączenia Bluetooth. Poniżej znalazło się pole w które wpisywana jest wartość temperatury podana przez moduł z przekaźnikami (pole "temp"). Następnie umieszczono dwa duże przyciski "Włącz" i "Wyłącz", które sterują wszystkimi przekaźnikami jednocześnie.

W dalszej części umieszczono powtarzające się tabele klasy "relay" - po jednej dla każdego przekaźnika. Ich pola (przyciski i spany) różnią się tylko ostatnią cyfrą, która pozwala rozpoznać, którego przekaźnika dotyczą. Każdy z przekaźników obsługiwany jest przez cztery przyciski: dwa do natychmiastowego włączenia i wyłączenia ("now-on-X" i "now-off-X", gdzie X to numer przekaźnika) oraz dwa do opóźnionego włączenia i wyłączenia ("later-on-X" i "later-off-X").

Pole "state-X" informuje o aktualnym stanie danego przekaźnika, a pole "time-X" wskazuje czas pozostały w timerach powiązanych z danym przekaźnikiem. Dzięki zastosowaniu wtyczki org.apache.cordova.dialogs, nie było konieczne samodzielne tworzenie okien dialogowych, potrzebnych do wpisywania czasu opóźnienia wysłania komend. Fragment pliku index.html został pokazany na listingu 1.

Szczegóły implementacji CSS

Listing 2. Fragment kodu CSS aplikacji

Plik CSS bazuje na standardowym pliku stylu do świeżej aplikacji Cordovy, pozbawionym kilku niepotrzebnych elementów. Zmieniono m.in. parametry nagłówka H1 oraz wykorzystano klasę .blink wraz ze zmodyfikowanymi parametrami aplikacji, by sygnalizować że trwa nawiązywanie połączenia Bluetooth.

Elementom tabeli i przyciskom (TABLE, TD, BUTTON) przypisano odpowiednie rozmiary i wielkości czcionek, tak by nie pozostawiały pustego miejsca na ekranie. Dodano też klasy:

  • .relay-on - by kolorem oznaczyć przekaźniki włączone,
  • .timed-on - by kolorem zielonym oznaczać odliczanie timera powodującego włączenie przekaźnika,
  • .timed-on - by kolorem czerwonym oznaczać odliczanie timera powodującego wyłączenie przekaźnika. Fragment pliku index.css znalazł się na listingu 2. Szczegóły implementacji Javascript

Naturalnie najbardziej złożony jest kod Javascriptu. W funkcji app.onDeviceReady() umieszczono wywołania dwóch funkcji:

  • app.bt1(), która inicjalizuje połączenie Bluetooth z modułem z przekaźnikami oraz prowadzi nasłuch danych dotyczących temperatury,
  • app.assign_buttons(), która powoduje przypisanie akcji do poszczególnych przycisków.

Oprócz tego stworzono funkcję app.countdown(), która służy do prezentacji czasu pozostałego do zakończenia odliczania czasu timerami.

Funkcja app.bt1() została pokazana na listingu 3. Rozpoczyna się od zmiany wyglądu paska informującego o stanie interfejsu Bluetooth. Następnie aplikacja wykrywa urządzenia Bluetooth w otoczeniu i gdy skończy, zmienia pasek informujący o stanie interfejsu oraz zaczyna, dla każdego ze znalezionych urządzeń, sprawdzać czy nazwa urządzenia to "Serial Adaptor".

Listing 3. Funkcja app.bt1()

Jeśli tak się stanie, aplikacja nawiązuje z danym urządzeniem połączenie, zmienia stan pasku informującego o interfejsie BT i zaczyna nasłuchiwać, korzystając z polecenia bluetoothSerial.subscribe() na ciągi znaków zakończone znakiem końca linii. Po każdym odebranym pakiecie takich danych, treść pola "temp" w aplikacji podmieniana jest na nowe wskazanie temperatury.

Funkcja app.assign_buttons() składa się w większości z powtarzających się elementów. Dla każdego z przycisków definiowane jest odpowiednie przypisanie akcji następującej po jego naciśnięciu. Jest to przesłanie odpowiedniej komendy przez interfejs Bluetooth oraz zmiana etykietki ze stanem adekwatnych przekaźników.

Listing 4. Fragment funkcji app.assign_buttons() obejmujący przypisanie akcji do przycisków natychmiastowo wywołujących komendy

Fragment tego kodu przedstawiono na listingu 4. Warto zwrócić uwagę na to, że w przypadku przycisków włączających i wyłączających wszystkie przekaźniki jednocześnie, zmiana sygnalizacji stanu przekaźników jest realizowana w oparciu o klasę .state, a nie o unikalne identyfikatory.

Znacznie ciekawszy jest kod służący do przypisania akcji przyciskom zwłocznym (listing 5). Został on tak zoptymalizowany, by zajął jak najmniej miejsca - wszystkim tym przyciskom (klasy .later) przypisywana jest jedna i ta sama funkcja. Wstępnie jednak pobierany jest identyfikator danego przycisku, dla którego funkcja została wywołana. Identyfikator ten pozwala później rozpoznać, którego przekaźnika dotyczy dana operacja.

Listing 5. Fragment kodu funkcji app.assign_buttons() obejmujący przypisanie akcji do przycisków wywołujących komendy ze zwłoką

Dla ułatwienia, wykorzystano polecenie navigator. notification.prompt(), które wywołuje okno do wprowadzania czasu opóźnienia wysłania komendy. Na dole okna umieszczono trzy przyciski, odpowiadające trzem jednostkom podawanej wartości opóźnienia: godzinom, minutom i sekundom.

Naciśnięcie jednego z tych przycisków powoduje wywołanie kolejnej funkcji, w której obliczany jest odpowiedni mnożnik czasu dla wybranej jednostki: 3600, 60 lub 1. W przypadku gdy użytkownik nie naciśnie żadnego z tych przycisków, tylko zamknie okienko dialogowe, korzystając z systemowego przycisku "cofnij", timer nie zostanie ustawiony.

Timer nie zostanie też ustawiony, jeśli wartość wpisana w oknie dialogowym nie jest liczbą całkowitą. Obliczona liczba sekund (a następnie milisekund), jaka musi upłynąć przed wysłaniem komendy jest podawana w funkcji setTimeout().

Listing 6. Funkcja app.countdown()

Aby skrócić ilość kodu potrzebnego do obsługi przycisków zwłocznych, z zapamiętanego wcześniej identyfikatora wycinane są fragmenty określające numer przekaźnika, którego dotyczy przycisk oraz rodzaj wywoływanej operacji (włączenie lub wyłączenie).

By uniknąć problemu ponownego definiowania komend Bluetooth, odpowiadających poszczególnym operacjom, w funkcji uruchamianej po żądanym czasie zwłoki wyzwalane jest kliknięcie w przycisk odpowiadający danej operacji i danemu przekaźnikowi; przyciskom tym przypisano bowiem już odpowiednie komendy we wcześniejszym fragmencie kodu funkcji app.assign_buttons().

Rysunek 3. Okno aplikacji w trakcie jej pracy w orientacji poziomej

Na koniec, już poza funkcją setTimeout() wywoływana jest funkcja app.countdown() z parametrami określającymi numer przekaźnika, rodzaj operacji oraz wybrane jednostkę czasu i czas zwłoki. Funkcja app.countdown() jest praktycznie niezależna i służy tylko temu, by w odpowiednich miejscach interfejsu prezentować odliczanie czasu timerów. Funkcja ta została zaprezentowana na listingu 6.

Działanie funkcji app.countdown() polega na sprawdzaniu, czy pozostały czas nie osiągnął zera i w razie potrzeby na zmniejszeniu licznika czasu o 1, wpisaniu nowej wartości w odpowiednim polu interfejsu użytkownika oraz na ponownym zaplanowaniu wywołania siebie samej, z wykorzystaniem funkcji setTimeout(), z opóźnieniem odpowiadającym jednej jednostce czasu, jaką użytkownik wybrał dla danego timera.

Podsumowanie

Gotowa aplikacja zajmuje niecałe 2 MB i uruchamia się praktycznie błyskawicznie. Pozwala zastąpić program w wersji na Windows. Dodatkowo, przenośny kod sprawia, że bardzo łatwo dokonać kompilacji kodu dla innych systemów operacyjnych, takich jak Windows Phone 8 i iOS, z tym że trzeba mieć na uwadze, że niektóre urządzenia mogą mieć problemy z komunikacją z użyciem interfejsu Bluetooth.

Marcin Karbowniczek, EP

Artykuł ukazał się w
Wrzesień 2015
DO POBRANIA
Pobierz PDF Download icon
Zobacz też
Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik wrzesień 2020

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio wrzesień 2020

Świat Radio

Magazyn użytkowników eteru

Automatyka Podzespoły Aplikacje wrzesień 2020

Automatyka Podzespoły Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna wrzesień 2020

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Praktyczny Kurs Elektroniki 2018

Praktyczny Kurs Elektroniki

24 pasjonujące projekty elektroniczne

Elektronika dla Wszystkich sierpień 2020

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów