Kurs Qt – #4 – aplikacja mobilna, łączność Bluetooth z Arduino

Kurs Qt – #4 – aplikacja mobilna, łączność Bluetooth z Arduino

Za nami pierwsza, prosta aplikacja mobilna. Pora, aby pójść krok dalej i nawiązać bezprzewodowe połączenie między telefonem, a Arduino.

Do wykonania tego ćwiczenia potrzebny będzie telefon z Androidem w wersji 4.1 lub wyższej oraz moduł w standardzie Bluetooth Classic (wersje od 1.0 do 3.0), czyli np. popularny HC-05.


Podstawowa aplikacja w Qt

Tworzymy projekt - dokładnie tak jak w trzeciej części. Następnie przechodzimy do budowy interfejsu. Można wykorzystać np. ten, który był pokazany w drugim artykule. Można zbudować go od nowa (będzie to idealny trening) lub można zrobić to znacznie szybciej przez zamianę pliku mainwindow.ui nowo utworzonego projektu z plikiem mainwindow.ui, który był używany w drugiej części tej serii.

W dalej opisywanym przykładzie dokonałem tylko jednej zmiany – dodałem nowy przycisk Wyczyść do czyszczenia okna konsoli. Sygnał kliknięcia połączyłem ze slotem czyszczenia okna w trybie Design.

Przykładowy interfejs aplikacji

Przykładowy interfejs aplikacji

Gdy interfejs będzie już gotowy to tworzymy sloty dla każdego z przycisków. W każdym z nich warto użyć qDebug() – coś na wzór poniższego kodu:

Uruchom teraz aplikację w trybie Desktop. Jeśli wszystko zadziała poprawnie, to uruchom ten sam kod na swoim telefonie (opis w poprzedniej części kursu). Poklikaj testowo w przyciski, a następnie sprawdź zakładkę Komunikaty aplikacji - powinno być tam widoczne wyjście z qDebug().

Informacja zwrotna widoczna w sekcji z komunikatami aplikacji

Informacja zwrotna widoczna w sekcji z komunikatami aplikacji

To właśnie duża zaleta Qt – napisaliśmy kod, który możemy uruchomić na PC lub dowolnym urządzeniu z Androidem. Zajmijmy się teraz sprzętem!

Przykładowe podłączenie modułu BT

Teraz pora na nasz moduł Bluetooth. W przypadku HC-05 sprawa jest dość prosta, musimy go zasilić i podłączyć do mikrokontrolera. W najprostszej postaci może to wyglądać następująco:

Schemat połączeń Arduino UNO z modułem bluetooth HC-05

Schemat połączeń Arduino UNO z modułem bluetooth HC-05

Program dla mikrokontrolera

Jeśli moduł jest odpowiednio skonfigurowany to bez problemu powinien zadziałać program, który był przez nas używany poprzednio. Dla przypomnienia:

Wgrajmy teraz program do mikrokontrolera odpinając wcześniej od pinów TX i RX płytki Arduino Uno przewody od modułu Bluetooth - przewody komunikacyjne idące do modułu Bluetooth skutecznie przeszkodzą we wgrywaniu kodu. Gdy program zostanie wgrany to możemy podłączyć moduł i wrócić do Qt.

Wyszukiwanie modułów Bluetooth w Qt

Tym razem skorzystamy z klasy QBluetoothDeviceDiscoveryAgent. Jest to klasa podobna do używanej wcześniej klasy QSerialPortInfo, z tą różnicą, że jest ona przeznaczona do wyszukiwania urządzeń typu Bluetooth Classic.

Najpierw w pliku konfiguracyjnym *.pro dodajmy moduł Bluetooth:

Następnie w pliku mainwindow.h dodajemy:

Pod private w deklaracji klasy MainWindow dodajemy:

Przejdźmy teraz do pliku mainwindow.cpp, a konkretnie do konstruktora klasy MainWindow i dodajmy następujący kod:

Teraz w slocie dla przycisku Szukaj dodajemy następującą linijkę, dzięki której za pomocą stworzonego przed chwilą wskaźnika rozpoczynamy wyszukiwanie urządzeń Bluetooth.

Teraz musimy przechwycić właściwości znalezionych urządzeń (jeśli na takie trafimy). Idealnie nadaje się do tego mechanizm sygnałów i slotów. Dodajmy kolejną linię kodu:

Za jej pomocą połączyliśmy sygnał deviceDiscovered(QBluetoothDeviceInfo) informujący o znalezieniu urządzenia Bluetooth z jeszcze niestworzonym slotem captureDeviceProperties(QBluetoothDeviceInfo).

Sygnał deviceDiscovered() jest sygnałem zdefiniowanym w klasie QBluetoothDeviceDiscoveryAgent co możemy zobaczyć przeglądając dokumentację lub pliki źródłowe:

Zaimplementujmy teraz nasz slot captureDeviceProperties(QBluetoothDeviceInfo). W tym celu, w pliku mainwindow.h, pod słowem kluczowym private slots: stwórzmy deklarację slotu:

Następnie w pliku mainwindow.cpp tworzymy jego implementację:

Jeśli dane urządzenie zostało wykryte to do ComboBoxa (podobnie jak w przypadku portów COM) wpisujemy parametry urządzenia. Tutaj będzie to jego nazwa i adres zamieniony na typ QString.

Sprawdźmy nasz kod w akcji. Upewnij się, że Twój moduł HC-05 jest zasilony. Następnie wgraj aplikację na telefon i rozpocznij wyszukiwanie. Efekt powinien być następujący:

Przykład wykrytego urządzenia

Przykład wykrytego urządzenia

Uwaga: wyszukiwanie urządzeń jest czynnością, która działa w tle przez określony czas. Sygnał deviceDiscovered() jest emitowany, kiedy zebrane są najważniejsze informacje o urządzeniu (co możemy przeczytać w dokumentacji), stąd może się zdarzyć taka sytuacja, że w ComboBoxie pojawi się tylko adres urządzenia (bez jego nazwy HC-05).

Dodajmy nową funkcjonalność wykorzystując sygnał finished() klasy QBluetoothDeviceDiscoveryAgent. Sygnał ten emitowany jest po zakończeniu wyszukiwania. Połączymy go ze slotem searchingFinished(), który zaraz stworzymy. W konstruktorze klasy MainWindow dodajmy:

W pliku mainwindow.h dodajmy:

W pliku mainwindow.cpp dodajmy:

Musimy też dokonać małej zmiany w slocie on_pushButtonSearch_clicked(). Dodajemy tam kilka linii:

Warto zapamiętać, że elementy interfejsu możemy aktywować bądź dezaktywować, np.:

Tym samym w dodanym przez nas kodzie, połączyliśmy sygnał zakończenia wyszukiwania z naszym slotem searchingFinished().

Kolejno w slocie, w którym rozpoczynamy wyszukiwanie, czyścimy zawartość ComboBoxa (w przeciwnym wypadku dodawalibyśmy te same urządzenia), dezaktywujemy ten przycisk Szukaj i uruchamiamy wyszukiwanie. Natomiast w chwili, gdy wyemitowany zostanie sygnał finished() informujący o zakończeniu wyszukiwania, aktywujemy ponownie przycisk Szukaj.

Sygnał finished() moglibyśmy wykorzystać w innym celu np. do ciągłego wyszukiwania urządzeń (gdyby było to potrzebne), wtedy w slocie searchingFinished(), umieścilibyśmy fragment:

Natomiast w captureDeviceProperties(), przed dodaniem nowego urządzenia, musielibyśmy sprawdzić, czy nie dodaliśmy takiego urządzenia już wcześniej lub sprawdzalibyśmy, które urządzenia znalezione wcześniej, nie zostały aktualne wyszukane. Mogłoby to świadczyć o tym, że przestały już one być aktywne lub zniknęły z naszego pola zasięgu.


Zajmijmy się przez chwilę naszym interfejsem, a właściwie naszą konsolą. Skopiujmy napisaną w części drugiej metodę addToLogs(QString message) i dodajmy odpowiednią deklarację w pliku mainwindow.h oraz definicję w pliku mainwindow.cpp. Następnie zamieńmy wszystkie wystąpienia qDebug() na metodę addToLogs(). Dodajmy też informację o znalezionych urządzeniach.

Efekt powinien wyglądać mniej więcej tak:

Działanie aplikacji

Działanie aplikacji

Aplikacja mobilna w Qt – ustanawianie połączenia Bluetooth

Teraz zajmiemy się połączeniem. W tym celu wykorzystamy klasę QBluetoothSocket. Jest to klasa, która w użyciu przypomina używaną przez nas wcześniej klasę QSerialPort. W pliku mainwindow.h dodajemy odpowiedni nagłówek:

Następnie tworzymy wskaźnik na obiekt typu QBluetoothSocket:

W pliku mainwindow.cpp tworzymy obiekt QBluetoothSocket i przypisujemy jego adres do wskaźnika.

W argumencie konstruktora obiektu QBluetoothSocket podajemy typ protokołu. W naszym przypadku jest to RFCOMM socket protocol. Podajemy to przez zdefiniowanego w używanej bibliotece enuma: QbluetoothServiceInfo::RfcommProtocol.

Od razu połączmy sygnały dotyczące połączenia i możliwości odczytu danych (zdefiniowane w klasie QBluetoothSocket - szczegóły w dokumentacji) z naszymi slotami, które za chwilę stworzymy:

Stwórzmy teraz odpowiednie sloty, mainwindow.h:

W pliku mainwindow.cpp:

Na tym etapie warto zbudować projekt, aby sprawdzić czy nie wkradły się tutaj żadne błędy. Jeśli jest dobrze to możemy przejść do ustanawiania połączenia. Wykorzystamy metodę connectToService() klasy QBluetoothSocket - szczegóły w dokumentacji.

Metoda connectToService() w naszym przypadku wymaga trzech argumentów:

  1. adresu (w odpowiednim formacie) urządzenia, z którym chcemy się połączyć,
  2. ID usługi, a konkretnie UUID,
  3. typ otwarcia połączenia (Read, Write, Read/Write).

Adres urządzenia uzyskaliśmy już podczas wyszukiwania, weźmiemy go z naszego ComboBoxa (za jego pomocą, będziemy definiować z którym urządzeniem chcemy się połączyć, np. w sytuacji, gdybyśmy mieli dwa moduły HC-05). Interesuje nas możliwość czytania i pisania do modułu, więc wybieramy opcję Read/Write.

Moduł HC-05, to co odczyta z innego urządzenia BT, wyprowadzi dalej poprzez pin TX i odwrotnie to co wyślemy z naszego mikrokontrolera na pin RX HC-05, zostanie wysłane dalej do innego urządzenia BT - praca w profilu portu szeregowego Serial Port Profile (SPP). Musimy znać odpowiednie UUID opisujące takie działanie. Niestety jest to informacja, której nie dowiemy się z dokumentacji Qt, ale możemy ją znaleźć tutaj, gdzie dowiemy się że UUID ma postać:

00001101-0000-1000-8000-00805F9B34FB.

W programie będzie to wyglądać następująco:

Adres urządzenia uzyskaliśmy przez pocięcie wybranej w ComboBoxie zawartości (czyli wybranego przez nas urządzenia) w formie obiektu QString za pomocą separatora, którym była spacja.


UUID w formie akceptowalnej przez metodę connectToService() uzyskujemy w nieco skompilowany sposób. Najpierw przez utworzenie QStringa (00001101-0000-1000-8000-00805F9B34FB) opisującego usługę Serial Port Profile, a następnie przez utworzenie obiektu QBluetoothUuid ze QStringa.

Działanie to najlepiej opisuje poniższy fragment z dokumentacji:

Creates a QBluetoothUuid object from the string uuid, which must be formatted as five hex fields separated by "-", e.g., "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" where "x" is a hex digit. The curly braces shown here are optional, but it is normal to include them.


Teoretycznie mamy już cały kod, który jest niezbędny do ustanowienia połączenia. Czas sprawdzić to na telefonie. Uruchamiamy program i testujemy. Podczas etapu łączenia, powinniśmy zostać poproszeni o sparowanie urządzeń. Jeśli nie zostaniemy o to poproszeni, to powinniśmy sami dokonać parowania.

Przykładowy ekran parowania urządzeń

Przykładowy ekran parowania urządzeń

Wpisujemy 1234 i zatwierdzamy klikając OK.

Podanie domyślnego kodu

Podanie domyślnego kodu

Jeśli wszystko przebiegło poprawnie i połączenie zostanie nawiązane to moduł Bluetooh HC-05 zacznie migać diodami znacznie wolniej. Co ważniejsze, w naszej konsoli powinny pojawić się logi:

Przykładowe logi

Przykładowe logi

Przykładowy widok w przypadku błędnej konfiguracji

Przykładowy widok w przypadku błędnej konfiguracji

Aplikacja mobilna w Qt – wysyłanie/odbieranie danych

Odbieranie danych mamy częściowo zaimplementowane – dostajemy sygnał, że nowe dane znajdują się w buforze. Musimy je odczytać w stworzonym przez nas wcześniej slocie socketReadyToRead(). Zrobimy to identycznie jak w części drugiej - będzie tylko mała różnica:

Efekt powinien być następujący:

Odbieranie danych

Odbieranie danych

Teraz czas na kolejny, ważny fragment, czyli zdalne sterowanie i wydawanie poleceń. Dokonajmy kilku zmian w slotach naszych przycisków odpowiedzialnych za sterowanie diodą:

Efekt:

Sterowanie diodą

Sterowanie diodą

Zróbmy to nieco ładniej dodając kilka zabezpieczeń oraz stwórzmy metodę do łatwego wysyłania danych (podobnie jak zrobiliśmy to w drugiej części tej serii). W pliku mainwindow.h dodajemy:

W pliku mainwindow.cpp dodajemy:

Następnie robimy porządek w slotach:

O czym zapomnieliśmy? O zamykaniu połączenia. Zatem korzystamy z metody disconnectFromService() klasy QBluetoothSocket.

I to w zasadzie koniec!

Wygląd aplikacji mobilnej

Na koniec chciałbym jeszcze dodać kilka słów o wyglądzie naszej aplikacji uruchomionej na Androidzie, który nie jest zachwycający. Nie podoba Ci się wygląd aplikacji? Z pomocą przychodzą inne moduły Qt są to między innymi QML i Qt Quick, które pozwalają na tworzenie znacznie ładniejszych interfejsów graficznych (to tylko jedna z wielu ich funkcjonalności), gdzie np.: możemy wykorzystać kontrolki zbudowane w oparciu o wytyczne Google Material Design. Przykład:

Przykład innego interfejsu

Przykład innego interfejsu

Podsumowanie

W tej części zbudowaliśmy aplikację na urządzenie z Androidem, która pozwoliła nam na uzyskanie bezprzewodowego połączenia poprzez Bluetooth z mikrokontrolerem. Mamy więc gotową bazę do budowy własnych systemów zdalnego sterowania!

Czy artykuł był pomocny? Oceń go:

Średnia ocena 4.9 / 5. Głosów łącznie: 22

Doszliśmy więc do końca krótkiej serii, która miała za zadanie wprowadzić Was w świat programowania z wykorzystaniem Qt. Mam nadzieję, że artykuły te będą dla wielu osób inspiracją do dalszej nauki tego popularnego zestawu narzędzi. Dajcie znać w komentarzach, jeśli chcielibyście, aby w ramach tej serii pojawiło się więcej artykuł! Co interesuje Was najbardziej?

Autor: Mateusz Patyk

Nawigacja kursu


O Autorze

Autorem tej serii wpisów jest Mateusz Patyk, który zawodowo zajmuje się programowaniem systemów wbudowanych oraz rozwijaniem aplikacji na desktopy i urządzenia mobilne. Jego głównymi obszarami zainteresowań są systemy sterowania, egzoszkielety i urządzenia do wspomagania chodu człowieka. Prywatnie miłośnik dobrego kina i gier strategicznych.

android, arduino, bluetooth, programowanie, qt

Trwa ładowanie komentarzy...