Od razu GPS logger zbudowano, czyli Arduino i GPS
W poprzedniej części napisaliśmy jak podłączyć kartę microSD do Arduino i coś na niej zapisywać. Jak każdy mógł zauważyć w plątaninie kabli krył się też odbiornik GPS. Ten post miał powstać wcześniej, ale wyglądało na to, że mamy jakiś problem z dostawą z Chin, o czym kilka słów na firmowym blogu. Na szczęście wszystko się wyjaśniło, więc moduły GPS są dostępne i jeżeli planujesz podłączyć z Arduino zrobić odbiornik GPS to wiesz gdzie szukać :)
Moduł GPS VK2828U7G5FL podłączony do Arduino
Teraz o tym jak podłączymy moduł VK2828U7G5LF do Arduino. Posłuży nam ten schemat:
Dla ułatwienia kolory kabli są takie same jak na złączu, które jest dodawane do każdego modułu. W podstawowym układzie wystarczą te trzy przewody: czarny – masa, czerwony – zasilanie (tak, może być 5V), niebieski – UART TTL z danymi.
Podłączyliśmy niebieski przewód do cyfrowego pinu 2 na Arduino. Dlaczego nie do 0, gdzie jest sprzętowy UART? Przez piny 0 i 1 odbywa się programowanie Arduino. Mając tam podłączony moduł GPS, który ciągle nadaje dane utrudniałoby programowanie (trzeba odłączać moduł GPS przy każdym wgraniu szkicu). Ponieważ pamięci nam na Arduino wystarczy, a GPS nadaje z niezbyt wygórowaną prędkością (9600), to SoftwareSerial
, czyli programowa realizacja portu szeregowego na dowolnych pinach cyfrowych w zupełności nam wystarczy.
A czemu podłączyliśmy tylko linę TX modułu GPS? Nie będziemy nic nadawać do modułu, ustawienia fabryczne są dla nas OK. Jeśli jednak chcielibyśmy coś zmienić, to można to zrobić, wysyłając do modułu sekwencję kodów. Można je znaleźć w karcie katalogowej modułu. Co można zmienić? Np częstotliwość z jaką moduł wysyła pozycję na interfejs UART. Domyślnie jest to 1 raz na sekundę (czyli 1 Hz), maksymalnie może to być 10 razy na sekundę (czyli 10 Hz). Ryzyko uszkodzenia interfejsu UART modułu! Moduł posiada interfejs UART TTL, ale wszystko wskazuje na to, że górny poziom napięcia na wejściu to 3.3V a nie Vcc, więc podłączając TX z Arduino działające na poziomie 5V ryzykujesz uszkodzenie UART w module GPS.
Jeżeli chcesz zmienić ustawienia modułu GPS to zrób dzielnik napięcia między TX Arduino a RX modułu lub skorzystaj z konwertera poziomów logicznych. Jeżeli korzystasz z Arduino działającego w standardzie 3.3V to możesz moduł podłączyć bezpośrednio.
Program na Arduino do odbierania danych z GPS
Pierwszy krok, sprawdźmy czy w ogóle odbieramy coś z modułu. Wgrajmy minimalistyczny program:
#include <SoftwareSerial.h> SoftwareSerial gpsBoard (2, 3, false); void setup() { Serial.begin(38400); gpsBoard.begin(9600); } void loop() { while (gpsBoard.available()) { byte c = gpsBoard.read(); // Read the GPS data Serial.print((char)c); } }
Co w szkicu? Najpierw importujemy bibliotekę SoftwareSerial oraz definiujemy obiekt gpsBoard
, który posiada interfejs (funkcje) jak Serial. Standardowo biblioteka SoftwareSerial obsługuje zarówno nadawanie i odbieranie, dlatego definicja obiektu ma format:
SoftwareSerial gpsBoard (2, 3, false);
Pin 2 to odbieranie danych, 3 nadawanie. Trzeci parametr to czy sygnał jest odwrócony. Zdarzają się urządzenia, które mają odwrócony poziom sygnałów, i bit 0 jest reprezentowany przez stan wysoki napięcia a bit 1 przez zero. Domyślnie wartość tego argumentu to false, ale używam go tutaj, byście byli świadomi jego istnienia – przydaje się gdy spotkasz takie urządzenie (np sensory Maxbotix używają odwróconego seriala). Niestety, pin 3 nie może być użyty do innych celów. Jeśli zależy Ci na wolnych pinach i/lub na zmniejszeniu rozmiaru szkicu możesz skorzystać z okrojonej biblioteki SoftwareSerial, którą możesz znaleźć w tym wątku na forum Arduino (ale sam z nie nie korzystałem). Jest tam link do wersji TX-only oraz RX-only.
W setup
ustawiamy port UART Arduino, oraz programowy serial na odpowiednie prędkości. W loop
każdy odebrany znak z modułu GPS wysyłamy do komputera.
Po wgraniu na Arduino na monitorze portu szeregowego powinny zacząć się pokazywać takie wpisy:
$GPRMC,103512.00,A,5217.08229,N,02053.34368,E,0.067,,240316,,,A*79 $GPVTG,,T,,M,0.067,N,0.124,K,A*25 $GPGGA,103512.00,5217.08229,N,02053.34368,E,1,06,1.69,84.5,M,34.2,M,,*62 $GPGSA,A,3,05,13,30,20,09,28,,,,,,,2.53,1.69,1.89*0D $GPGSV,3,1,11,05,52,246,16,07,48,068,,08,16,062,26,09,14,118,23*71 $GPGSV,3,2,11,13,42,292,22,15,06,297,18,20,28,307,23,21,06,337,*7D $GPGSV,3,3,11,27,12,031,08,28,40,165,27,30,80,078,13*4A $GPGLL,5217.08229,N,02053.34368,E,103512.00,A,A*63
Tak wygląda to, co podaje moduł GPS. Powyższy zrzut jest dla sytuacji, w której moduł już uzyskał pozycję (sygnalizują to migające zielone diody z obu stron płytki modułu). Gdy moduł startuje podaje informacje o sobie:
$GPTXT,01,01,02,u-blox ag - www.u-blox.com*50 $GPTXT,01,01,02,HW UBX-G70xx 00070000 FF7FFFFFo*69 $GPTXT,01,01,02,ROM CORE 1.00 (59842) Jun 27 2012 17:43:52*59 $GPTXT,01,01,02,PROTVER 14.00*1E $GPTXT,01,01,02,ANTSUPERV=AC SD PDoS SR*20 $GPTXT,01,01,02,ANTSTATUS=DONTKNOW*33
A potem w formacie jak wyżej pozycję co jedną sekundę, tylko będzie mniej liczb. Dane te są nieczytelne dla człowieka (format ten nazywa się NMEA, składa się komunikatów aka sentencji), trzeba je przekształcić, nie będziemy jednak tutaj robić ręcznie konwersji formatu. Skorzystamy z gotowej biblioteki TinyGPS. Ściągnij i rozpakuj w folderze z bibliotekami. Po restarcie Arduino będzie dostępny nowy przykład (Examples/TinyGPS/test_with_gps_device). Otwórz go, popraw piny w definicji obiektuy SoftwareSerial oraz w setup zmień jego prędkość na 9600 z przykładowego 4800. Po wgraniu, powinieneś zobaczyć mniej więcej taki obraz:
Testing TinyGPS library v. 13 by Mikal Hart Sats HDOP Latitude Longitude Fix Date Time Date Alt Course Speed Card Distance Course Card Chars Sentences Checksum (deg) (deg) Age Age (m) --- from GPS ---- ---- to London ---- RX RX Fail ------------------------------------------------------------------------------------------------------------------------------------- **** **** ********* ********** **** ********** ******** **** ****** ****** ***** *** ******* ****** *** 0 0 0 8 129 52.284843 20.889328 602 03/24/2016 11:03:40 619 113.30 0.00 0.41 N 1440 274.92 W 481 2 0 8 129 52.284843 20.889335 627 03/24/2016 11:03:41 644 113.40 0.00 1.11 N 1440 274.92 W 962 4 0 8 129 52.284851 20.889335 639 03/24/2016 11:03:42 658 113.60 0.00 0.26 N 1440 274.92 W 1443 6 0 8 129 52.284851 20.889333 659 03/24/2016 11:03:43 676 113.90 0.00 0.26 N 1440 274.92 W 1926 8 0 8 129 52.284854 20.889329 675 03/24/2016 11:03:44 694 114.10 0.00 0.15 N 1440 274.92 W 2409 10 0 8 129 52.284858 20.889328 702 03/24/2016 11:03:45 719 114.60 0.00 0.33 N 1440 274.92 W 2890 12 0
Możesz zajrzeć do przykładu by sprawdzić jak uzyskać niektóre z informacji, jak np liczbę widocznych satelitów.
Jak odczytać pozycję?
Jeśli chodzi o odczyt najbardziej nas interesującego parametru, czyli położenia, to najpierw, zamiast wysyłać znak do komputera, przykażemy go do obiektu gps
. Metoda encode
z kolejnych znaków składa całość komunikatu i na podstawie całości dokonuje obliczeń:
while (gpsBoard.available()) { byte c = gpsBoard.read(); // Read the GPS data //Serial.print((char)c); gps.encode(c)); }
gps
jest obiektem typu TinyGPS. Teraz pozostaje uzyskać dane w łatwiejszym do użycia formacie:
long lat, lon; unsigned long fix_age; gps.get_position(&lat, &lon, &fix_age);
Zwróć uwagę, na znaki &
przed nazwami zmiennych. Dzięki temu zapisując jakieś wartości do tych zmiennych wewnątrz funkcji, wartość ta widoczna jest poza funkcją. Po wywołaniu get_positio
n
zmienne lat
, lon
i fix_age
będą nadal miały taką wartość jaka została ustawiona.
W lat
i lon
dostaniesz odpowiednio szerokość i długość geograficzną. Podane są jako liczba całkowita, w milionowych częściach stopnia. Czyli 9 stopni to będzie wartość 9 milionów, -15 stopni – minus 15 milionów. Trzeba pamiętać też, że wartość ta podana jest w systemie dziesiętnym, czyli lat
równe 9 i pół miliona oznacza 9 stopni i 30 minut szerokości geograficznej.
Trzeci argument fix_age
zawiera wiek (w milisekundach) od ostatniego odczytu z modułu GPS. Przed pierwszym odczytem będzie zawierał wartość równą stałej TinyGPS::GPS_INVALID_AGE
. Jeśli porównasz fix_age
z tą wartością i będą równe, to znaczy, że jeszcze nie odczytał pozycji.
Sprawę zgubienia sygnału z satelity można wykryć inaczej.
unsigned long chars = 0; unsigned short sentences = 0, failed = 0; gps.stats(&chars, &sentences, &failed);
Po wywołaniu tej funkcji mamy trzy wartości: chars
– ile znaków odebrał obiekt, failed
– ile komunikatów z moduł GPS nie zostało poprawnie odczytane i to co nas interesuje sentences
– czyli liczba poprawnych komunikatów. Z każdym poprawnym odczytem pozycji ten licznik będzie się zwiększał (bodajże o 2 na każdy poprawny odczyt). Jeżeli przez kilka sekund pozostaje bez zmian – na pewno nie ma sygnału. Pamiętaj, TinyGPS będzie zwracał zawsze ostatnią odczytaną pozycję i inne dane, więc to jedyna metoda by programowo to sprawdzić.
Więcej o bibliotece TinyGPS można poczytać na stronie autora biblioteki.
Co jeszcze warto wiedzieć?
Moduł ma dwie zielone diody, które rytmicznie pulsują co 1 sekundę gdy moduł uzyskał sygnał na tyle dobry, by wyznaczyć pozycję. Impuls ten jest dostępny również w postaci sygnału PPS na złączu (przewód biały). Podłączyć go można do Arduino – impulsy co 1 sekundę na danym pinie oznaczają że moduł podaje poprawną pozycję. Ale nie tylko. GPS jest uznawany za referencyjny wzorzec czasu. Odbiornik jest wzorcem czasu klasy STRATUM 0 (można poczytać więcej o tym we wpisie o NTP na WIkipedii), więc impuls jest dokładnie co 1 sekundę i może stanowić odniesienie do dokładnych pomiarów czasu.