obrazek-posta

Po teorii i koncepcji opisanej we wcześniejszym wpisie nadszedł czas na praktykę. Tym razem opiszę elementy składowe, schematy i program jednostki lokalizacyjnej.

ℹ️ UWAGA AUTORA
System był projektowany wiele lat temu, około 2017 roku. Wiele się od tego czasu zmieniło :) System robiłem od podstaw samemu (AI nie było, a szkoda - bardzo by wtedy pomogło), konieczne było robienie wielu rzeczy wręcz na oślep. Być może część z prezentowanych w tym cyklu artykułach technik jest obecnie przestarzała. Był to jednak projekt edukacyjny, więc proszę o wyrozumiałość, gdy zauważycie, że gdzieś jest luka, coś robi się inaczej, a aplikacja nie jest przystosowana na smartfony 😉

Mikrokontroler

Na element stanowiący „mózg” układu, czyli mikrokontroler początkowo miało być wybrane Arduino Nano lub Micro. Problemem okazało się jednoczesne używanie dwóch portów szeregowych. Arduino Nano ma tylko jeden port szeregowy. Niby są jakieś biblioteki, które pozwalają programowo obsłużyć drugi port szeregowy, ale mimo wielu testów nie udało mi się tego poprawnie uruchomić. Uruchomienie jednego portu szeregowego blokowało drugi. Być może się to da zrobić – mi to nie działało, więc postanowiłem poszukać innych rozwiązań. Strzałem w dziesiątkę okazała się płytka Teensy LC. Oparta jest o mikroprocesor Cortex M0+, który jest znacznie wydajniejszy niż Atmegi w Arduino, choć to akurat dla mnie nie miało szczególnego znaczenia. To, co istotne, to 3 oddzielne porty szeregowe, pełna zgodność z Arduino IDE, wysoka energooszczędność i malutkie rozmiary płytki (nawet minimalnie mniejsze niż Arduino Nano). Poniżej możecie zobaczyć schematy wyprowadzeń. Jak widać dosyć istotną różnicą są poziomy logiczne na 3,3V a nie 5V jak w Arduino, ale akurat w naszym przypadku to nie ma większego znaczenia.

fot. pjrc.com fot. pjrc.com

Ogólnie można powiedzieć, że Teensy LC ma dużo większe możliwości niż potrzebujemy (a trzeba dodać, że to najuboższa z płytek Teensy). Nie jest też szczególnie drogie, w Polsce można je kupić za około 70-80 zł, choć producent wycenił je na mniej niż 12$. Oczywiście takiego klona Arudino Nano można u naszych skośnookich przyjaciół zza Wielkiego Muru kupić za 4 razy mniej, ale jak widać warto dać trochę więcej za znacznie większe możliwości.

Moduł GSM

Musicie pamiętać, że początki projektu sięgają 2017 roku, stąd część komponentów prawdopodobnie dałoby się obecnie zastąpić czymś nowszym i lepszym. Nie orientuję się jak to wygląda w 2026 roku, ale wtedy najlepszym wyborem do projektu był moduł SIM800L. Na pewno nadal spełnia on wszystkie wymagania. Decydowały o tym możliwości układu, wymiary i bardzo (bardzo) niska cena – spokojnie do kupienia na Ali za 12 zł. Modułem steruje się przez UART komendami AT, ma niski pobór (do 7 mA w IDLE, a można go nawet uśpić, wtedy do 2 mA). Działa na nim GPRS z „szaloną” na dzisiejsze czasy prędkością do 85,6 kbps – na szczęście na ilość danych, jaką będziemy przesyłać to w zupełności wystarcza 🙂 . Format karty to microSIM. Poniżej schemat wyprowadzeń.

sim800l

Należy pamiętać, żeby przypadkiem nie zasilać modułu z mikrokontrolera. Chwilowy pobór prądu podczas komunikacji może teoretycznie sięgać nawet 2A, co w najlepszym przypadku sprawi, że moduł będzie świrować, a w najgorszym uszkodzi nam mikrokontroler. Wprawdzie ja go tak podłączałem i nic nie wybuchło, ale nie radzę 😉 .

Moduł GPS

W kwestii modułu lokalizacji satelitarnej (bo tak powinno brzmieć poprawne określenie) przyświecały mi podobne założenia, jak w innych elementach, czyli: jakość, cena, wymiary. Najlepszym wyborem okazał się moduł NEO-7M. Do wyboru mamy również bliźniacze moduły 6M i 8M – obydwa dobrze się spiszą, ale ten pierwszy ma nieco wyższy pobór prądu i obsługuje wyłącznie GPS, a 8M jest trochę droższy (choć ma większą funkcjonalność). Poniżej zdjęcie modułu.

neo7m

Podobnie jak w przypadku modułu GSM, proponuję nie przepłacać i kupić moduł na Aliexpress. Jest prawie dwa razy taniej (moduł kosztuje około 30 zł).

Zasilanie

Jednostka będzie zasilania z akumulatora pojazdu. Aby zasilić jednostkę z napięcia samochodu, które wynosi około 12-14V, wykorzystałem przetwornicę impulsową LM2596. Zasila ona mikrokontroler oraz moduł GSM.

stepdown

Napięcie wyjściowe reguluje się śrubką widoczną na zdjęciu powyżej. Są również dostępne modele z wyświetlaczem, ale nam zależy na maksymalnej miniaturyzacji.

Pozostałe elementy

Potrzebne nam będą również:

  • transoptor PC817 – do wykrywania zapłonu pojazdu,
  • tranzystor BC547B – do włączania/wyłączania modułu GPS,
  • dioda prostownicza 1N4148 – jako zabezpieczenie diody transoptora przed odwrotną polaryzacją,
  • kondensator 220µF – do filtracji zasilania,
  • rezystory 2 kΩ, 6 kΩ i 10 kΩ (najlepiej kupić sobie zestaw rezystorów, kosztuje grosze, a niektóre wartości mogą być do poprawy),
  • przewody połączeniowe, płytka prototypowa, złącza raster 2,54mm, lutownica i wszystkie inne duperele przydatne przy elektronice – szczypce, kombinerki, narzędzie do ściągania izolacji itd.

Jak to wszystko ma działać?

Najpierw proponuję rzucić okiem na poglądowy schemat zasilania układu:

schema1

Przedstawia on jedynie ideę zasilania jednostki. Zostaje ona podłączona do zasilania z akumulatora pojazdu, ale konieczne jest również podłączenie jej do sygnału „zapłonu”, po to, aby móc sterować przerwaniem w programie mikrokontrolera, które będzie zmieniać tryb pracy jednostki. Każdy samochód ma zapłon w wielu miejscach, najłatwiej może być wyciągnąć go z kostki radia. Do celów testowych możemy również sprawdzić miernikiem w pudle bezpieczników, gdzie pojawia się prąd po przekręceniu kluczyka, gdzie stałe 12V i tam się podłączyć. Napięcie zasilania zostaje podane na przetwornicę step-down, która obniża napięcie w okolice 4,1V. Takie napięcie mieści się w dopuszczalnych zarówno dla mikrokontrolera jak i modułu GSM. Przed modułem GSM został szeregowo włączony dodatkowo kondensator 200 µF, którego zadaniem jest filtracja zasilania.

Jeśli chodzi o napięcie ze stacyjki, jest ono sygnałem sterującym dla programu mikrokontrolera zmieniającym tryb pracy urządzenia. Aby skorzystać z tego napięcia jako sygnału wejściowego, zostało ono podłączone do układu z transoptorem, który pozwala na przepływ prądu i sygnał wysoki na mikrokontrolerze, jeśli przekręcimy „zapłon” w samochodzie. Układ ten składa sie z transoptora PC817, z którym po stronie fotoemitera szeregowo połączony jest rezystor 2,2 kΩ oraz dioda prostownicza 1N4148, która służy jako zabezpieczenie przed spaleniem diody transoptora w przypadku pojawienia się odwrotnej polaryzacji. Kolektor fototranzystora połączony jest z wyprowadzeniem 3,3 V z Teensy, a emiter z wyprowadzeniem cyfrowym nr 15 oraz przez rezystor 10 kΩ z masą mikrokontrolera. Tak zbudowany układ po przekręceniu kluczyka da nam stan wysoki na pinie 15, który obsługuje przerwanie zmieniające tryb pracy urządzenia. Brzmi trochę skomplikowanie, ale po przeczytaniu całości wpisu powinno się poukładać w głowie :).

Moduł GPS zasilany jest z wyprowadzenia 3,3V mikrokontrolera poprzez układ z tranzystorem. Zadaniem tranzystora jest uruchamianie bądź wyłączanie modułu w zależności od stanu pinu cyfrowego nr 16, którzy poprzez rezystor 6,3 kΩ połączony jest z bazą tranzystora. Kolektor połączony jest z masą modułu GPS, a emiter z masą mikrokontrolera. Umożliwia to sterowanie zasilaniem modułu z poziomu programu – stan wysoki na pinie 16 powoduje przepływ prądu w obwodzie zasilając tym samym moduł GPS.

schema2

Komunikacja pomiędzy modułami GSM i lokalizacyjnym a mikrokontrolerem odbywa się za pomocą interfejsu szeregowego, a więc złącza TX do wysyłania danych oraz RX do odbierania danych. Mikrokontroler Teensy LC posiada 3 niezależne porty szeregowe i każdy z nich ma przypisane konkretne numery pinów na płytce. Moduł GPS podłączony jest do portu Serial 1, czyli pinów o numerach 0 i 1, natomiast moduł GSM do portu Serial 2, czyli pinów 9 i 10.

Żeby to wszystko poskładać w kupę, zlutowałem ścieżki i elementy elektroniczne na płytce uniwersalnej, do której następnie dołączane były przetwornica, moduł GPS i mikrokontroler.

prototyp

Po skleceniu w całość wyglądało to tak:

prototyp

prototyp

Może nie jest to szczyt elegancji ale w końcu to tylko prototyp. Jak już wiedziałem, że wszystko działa dobrze, zrobiłem to na specjalnie zaprojektowanej płytce, o czym później.

Program mikrokontrolera

Jak wiadomo, w Arduino mamy dwie główne funkcje setup oraz loop. Pierwsza z nich odpowiada za konfigurację urządzenia zaraz po jego uruchomieniu i instrukcje w niej zawarte wykonywane są jednokrotnie. Inicjalizowane są w niej interfejsy szeregowe oraz definiowane funkcje pinów oraz przerwania. Funkcja loop zawiera instrukcje, które wykonywane są w pętli po wykonaniu instrukcji w funkcji setup. Przed obiema pętlami definiowane są jeszcze zmienne używane później w programie oraz ich domyślne wartości.

Na początku funkcji setup inicjalizowana jest transmisja na portach szeregowych:

  • Serial – odpowiadający za komunikację między urządzeniem, a komputerem,
  • sim800 – komunikacja między mikrokontrolerem a modułem GSM,
  • gpsx – komunikacja między mikrokontrolerem a modułem GPS.
Serial.begin(9600);
sim800.begin(9600);
gpsx.begin(9600);

Następnie definiowany jest tryb pinu A2 jako wyjścia, A1 jako wejścia oraz jako przerwania. Przerwanie jest to sygnał z urządzenia peryferyjnego, który powoduje zatrzymanie programu głównego i wywołanie funkcji obsługującej przerwanie. W przypadku omawianego programu, jest to zmiana sygnału na pinie A1, która spowodowana jest włączeniem lub wyłączeniem zapłonu. Przerwanie reaguje na zmianę, a więc na zbocze rosnące lub opadające sygnału sterującego.

pinMode(A1, INPUT); //ustawienie trybu pinu A1 jako wejścia
pinMode(A2, OUTPUT); //ustawienie trybu pinu A2 jako wyjścia
attachInterrupt(A1, switchMode, CHANGE); //inicjalizacja przerwania
digitalWrite(A2, HIGH); //ustawienie domyślne stanu wysokiego na pinie A2

Wywołuje ono funkcję switchMode, której zadaniem jest ustawienie zmiennej dev_mode na wartość HIGH w przypadku, kiedy zapłon jest włączony oraz na wartość LOW w przypadku braku sygnału z zapłonu. Zmienia ona również stan pinu A2 powodując tym samym uruchomienie bądź wyłączenie modułu GPS.

void switchMode()
{
  if (digitalRead(A1) == LOW)
  {
    dev_mode = LOW;
    digitalWrite(A2, LOW);
  }
  else
  {
    dev_mode = HIGH;
    digitalWrite(A2, HIGH); 
  }
  mode0 = 0;
  
}

W zależności od wartości zmiennej dev_mode, program główny wykonuje różne instrukcje, czyli przechodzi w tryb wysyłania lokalizacji albo uśpienia. W trybie uśpienia sprawdzana jest najpierw wartość zmiennej mode0, której domyślna wartość wynosi 0 i ustalana jest podczas każdego przerwania. Służy ona do sprawdzenia, czy program wchodzi do pętli trybu uśpienia po raz pierwszy od przerwania. Jeśli tak, to moduł SIM800L ustawiany jest w tryb odbioru SMS-ów, a następnie zmiennej mode0 przypisywana jest wartość 1. Dzięki temu, kiedy program wykona pętlę kolejny raz, nie będą powielane ustawienia modułu GSM do odbierania SMS-ów.

if (mode0 == 0)
  {
    Serial.println("konfigurowanie odbierania sms");
    delay(500);
    sim800.println("at+cmgf=1\r\n"); //tryb tekstowy sms-ow
    runsl();
    delay(500);
    sim800.println("at+cnmi=1,2,0,0,0\r\n"); //tryb odbierania sms-ow
    runsl();
    delay(500);
    mode0 = 1; 
    Serial.println("Pomyslnie skonfigurowanie odbieranie");
    delay(500);
    gps_done = 0;

  }

Tryb odbierania SMS-ów polega na tym, że przy nadejściu wiadomości, zostanie ona natychmiast wyświetlona na porcie szeregowym. Instrukcja warunkowa IF sprawdza, czy dane są dostępne na porcie, a jeśli tak, zapisuje je do zmiennej text_msg. Kolejne instrukcje warunkowe przy pomocy funkcji indexOf wychwytują, czy w wiadomości znajdują się wyrazy „ON” lub „PULL”. Dodatkowo zastosowano funkcję toUpperCase, której zadaniem jest zamiana ewentualnych małych liter na wielkie. Dzięki temu użytkownik wysyłający wiadomość nie musi pisać jej wielkimi literami. Jeśli wiadomość brzmi „ON”, zmienna dev_mode przybiera wartość 1 oraz na pinie A2 pojawia się stan wysoki, co w konsekwencji powoduje wybudzenie modułu GPS i przejście do trybu wysyłania danych lokalizacyjnych. Wiadomość o treści „PULL” powoduje uruchomienie modułu GPS, a następnie wykonywanie funkcji gps_read, dopóki zmienna gps_done nie będzie różna od zera. Zmienna gps_done przybiera wartość 1 wyłącznie po pomyślnym wysłaniu lokalizacji. Oznacza to, że program zawsze „zaczeka” na uzyskanie poprawnej lokalizacji, a dopiero potem moduł GPS zostaje wyłączony.

else
  {
    if(sim800.available()>0)
    {
      text_msg = sim800.readString();
      Serial.println(text_msg);
      delay(10);
    }
    
    if(text_msg.toUpperCase().indexOf("ON")>=0)
    {
      digitalWrite(A2, HIGH);
      dev_mode = 1;
      text_msg = "";
    }
    
    if(text_msg.toUpperCase().indexOf("PULL")>=0)
    {
        digitalWrite(A2, HIGH);
      
        while (gps_done == 0)
        {
          gps_read();
        }
        digitalWrite(A2, LOW);
        gps_done = 0;
        text_msg = "";
    }
  }

Tryb przesyłania lokalizacji aktywny jest, jeśli zmienna dev_mode ma wartość 1. Wywoływana jest wtedy funkcja gps_read. W pętli WHILE sprawdzane jest, czy są dostępne dane pochodzące z portu szeregowego modułu lokalizacyjnego. Jeżeli nie, wyświetlany jest komunikat o sprawdzeniu poprawności podłączenia modułu GPS. Jeśli dane są dostępne, wywoływana jest funkcja displayInfo. Na jej początku sprawdzane jest, czy dostępne dane są poprawne, a więc czy moduł GPS nawiązał już łączność z satelitami i jest w stanie podać poprawną pozycję. Jeśli tak, przy pomocy szeregu komend AT wysyłanych za pomocą portu szeregowego na moduł GSM, nawiązywana jest łączność z serwerem oraz tworzona jest ramka POST, w której znajdują się następujące informacje:

  • szerokość i długość geograficzna,
  • prędkość w km/h,
  • czas uzyskania pozycji,
  • ilość satelit,
  • kierunek poruszania,
  • status urządzenia,
  • token urządzenia.
sim800.println("at+httppara=\"URL\",\"http://IP_SERWERA/nittrack/dodaj.php\"\r\n"); //wpisz IP swojego serwera
    runsl();
    delay(1000);
    sim800.println("at+httppara=\"CONTENT\",\"application/x-www-form-urlencoded\"\r\n");
    runsl();
    delay(500);
    
      String urlData = "lng=";
      urlData += String(gps.location.lng(), 6);
      urlData += "&lat=";
      urlData += String(gps.location.lat(), 6);   
      urlData += "&speed=";
      urlData += String(gps.speed.kmph());
      urlData += "&gps_time=";
      urlData += String(gps.time.value());
      urlData += "&satellites=";
      urlData += String(gps.satellites.value());    
      urlData += "&course=";
      urlData += String(gps.course.deg());    
      urlData += "&device_status=";
      urlData += String("drive");
      urlData += "&token=";
      urlData += String(token);

Ramka zapisywana jest do zmiennej urlData. Następnie przy pomocy funkcji length określana jest jej długość – jest to parametr konieczny przy zapytaniu POST.

    //wyswietl ramke danych i oblicz ich dlugosc  
    Serial.println(urlData);
    lengthx = String(urlData.length());
    
    sim800.println("at+httpdata=" + lengthx + ",10000\r\n");
    delay(300);       
    sim800.println(urlData);
    delay(6700);
    sim800.println("at+httpaction=1\r\n");
    runsl();
    delay(3000);

Na końcu pętli połączenie z serwerem jest przerywane do momentu uzyskania kolejnych danych lokalizacyjnych.

Brzmi trochę skomplikowanie, ale łatwiej będzie zrozumieć działanie programu na podstawie algorytmu:

algorytm

Pełen kod programu możesz pobrać z githuba.

Pobór prądu

Jak wspominałem w koncepcji, mały pobór prądu jest dosyć istotną kwestią w przypadku jednostki lokalizacyjnej. Nikt nie chce, żeby po tygodniu stania padł akumulator w samochodzie, bo mamy jakiś kawałek elektroniki w środku. Przeprowadziłem więc badania poboru prądu obejmujące wszystkie cykle pracy urządzenia, czyli:

  • uruchomienie jednostki,
  • wyszukiwanie satelit,
  • wysyłanie danych lokalizacyjnych na serwer,
  • przejście z trybu pracy do uśpienia i na odwrót,
  • wykonanie akcji „PULL”, czyli pojedynczego pobrania lokalizacji.

Pomiary zostały wykonane multimetrem Owon B35T+ z możliwością przesyłania danych przy pomocy interfejsu Bluetooth. Częstotliwość pomiarów wynosiła 2 Hz. Dane przesyłane były na smartfon z dedykowaną aplikacją umożliwiającą zapis wskazań do pliku CSV.

Poniższy wykres przedstawia zależność natężenia prądu od czasu począwszy od uruchomienia jednostki, przez zalogowanie się do sieci i wysyłanie danych lokalizacyjnych na serwer. Napięcie zasilania wynosiło ok. 14,4 V.

wykres

Po uruchomieniu urządzenia następuje zalogowanie się do sieci GSM oraz podstawowa konfiguracja urządzenia umożliwiająca połączenie GPRS. Proces ten trwa około 20 sekund. W tym czasie następują wyraźne fluktuacje natężenia prądu, którego wartość waha się od około 40 mA do 65 mA. Następnie urządzenie oczekuje na poprawne dane lokalizacyjne z modułu GPS, pobierając ok. 39 – 41 mA. Po odebraniu poprawnych danych z satelit, jednostka wysyła je na serwer. Na tym etapie widoczny jest znaczny skok natężenia prądu, który może sięgać 80 mA, jednak stan ten trwa maksymalnie 3 sekundy, ponieważ po tym czasie program kończy połączenie z serwerem.

Na kolejnym wykresie początkowo jednostka pracuje w trybie wysyłania pozycji, po czym zapłon zostaje wyłączony i urządzenie przechodzi w tryb uśpienia. Widoczne jest pojedyncze wysłanie pozycji już po przejściu w stan uśpienia. Dzieje się tak dlatego, że jeśli wyłączenie zapłonu będzie miało miejsce w czasie wykonywania się pętli wysyłania, konstrukcja programu pozwala na dokończenie tego procesu i dopiero wtedy moduł GSM zostaje ustawiony na tryb oczekiwania na SMS. Przejście w tryb uśpienia powoduje obniżenie poboru prądu do zakresu 30 – 32 mA. Na wykresie przedstawione jest również odpytanie uśpionej jednostki o aktualną pozycję, czyli odebranie SMS-a o treści „PULL”. Zauważalne jest zwiększenie poboru o ok. 10 mA, a następnie skok do blisko 80 mA, czyli wysłanie pozycji oraz ponowne przejście w stan uśpienia. Napięcie zasilania wynosiło ok. 14,4 V podczas włączonego zapłonu i ok. 12,9 V przy wyłączonym zapłonie.

wykres

Na ostatnim wykresie początkowo urządzenie było w trybie czuwania, a następnie zapłon został włączony. Przez kolejne 30 sekund moduł GPS próbował uzyskać poprawny sygnał z satelit. Można zauważyć, że w tym czasie pobór prądu nie zmienił się. Oznacza to, że podczas wyszukiwania sygnału satelit pobór prądu jest podobny, jak podczas odbierania ich sygnału.

wykres

Płytka docelowa

Po testach i badaniach tak skleconego urządzenia, postanowiłem wykonać dedykowaną płytkę PCB, która wymaga tylko dolutowania elementów składowych. Płytkę zaprojektowałem w programie EasyEDA, który polecam z całego serca. Jest bardzo intuicyjny, prosty w obsłudze, ma pełno gotowych elementów i jest darmowy. W dodatku integruje się ze sklepem JLC PCB, w którym możemy bezpośrednio zamówić zaprojektowane płytki. Tak wyglądał gotowy projekt:

pcb

pcb

Płytki kosztowały chyba 8$ za 10 sztuk, co uważam za świetną cenę, zważywszy, że nie można nic zarzucić jakości wykonania, szybkości wysyłki i obsłudze klienta. Zlutowane, gotowe urządzenie wygląda całkiem fajnie:

wykres

Co ciekawe, na gotowej płytce musiałem zmieniać wartość rezystora w układzie z tranzystorem, bo nie chciał się uruchamiać. Zrobiłem to eksperymentalnie, bo obliczenia nie bardzo mi się tutaj zgadzały.

To wszystko na temat jednostki lokalizacyjnej. Trzeba pamiętać, by zmienić w programie wartości APN w zależności, jakiego operatora kartę SIM mamy.

W kolejnym wpisie przedstawiona zostanie aplikacja internetowa obsługująca jednostkę.