Skocz do zawartości

WhenArrivalPL – własna tablica najbliższych odjazdów z M5Stack i GTFS


Pomocna odpowiedź

Napisano (edytowany)

Pewnego razu przyszło mi czekać około czterech godzin na przyjazd pociągu na peron. Po pewnym czasie człowiek zaczyna się zastanawiać nie tylko nad tym, kiedy wreszcie coś przyjedzie, ale również nad tym, czy nie dałoby się zrobić własnego urządzenia, które pokazywałoby najbliższe odjazdy bez ciągłego wyciągania telefonu i przeglądania kilku stron lub aplikacji. Tak powstał pomysł na WhenArrivalPL (MojaStacja) – niewielką, sieciową tablicę wyświetlającą najbliższe odjazdy dla wybranego przystanku lub stacji.

Projekt powstał we współpracy ze sklepem Botland, a od początku zależało mi na tym, aby nie ograniczać go wyłącznie do kolei. W praktyce urządzenie korzysta z danych GTFS, więc może współpracować również z komunikacją miejską w wielu polskich miastach. Całość została podzielona na dwa elementy: urządzenie z ESP32 i ekranem dotykowym, które wyświetla gotowe dane oraz lokalny serwer napisany w Pythonie, który pobiera, analizuje i upraszcza dane GTFS.

1.jpg2.jpg

Takie rozdzielenie okazało się bardzo istotne. Firmware urządzenia nie łączy się bezpośrednio z publicznymi serwisami komunikacji – komunikuje się wyłącznie z lokalnym serwerem. Dzięki temu mikrokontroler nie musi rozpakowywać dużych archiwów, analizować ogromnych plików tekstowych ani przejmować się różnicami pomiędzy poszczególnymi dostawcami danych.

Wybór komponentów

W tym projekcie zamiast klasycznego Arduino, osobnego ekranu, czytnika kart SD i plątaniny przewodów zdecydowałem się na urządzenia M5Stack. Firmware obsługuje obecnie M5Stack CoreS3 oraz M5Stack Tab5.

Głównym powodem takiego wyboru była prostota wykonania urządzenia. M5Stack ma już na jednej płytce mikrokontroler ESP32, ekran dotykowy, komunikację Wi-Fi, zasilanie oraz pozostałe elementy potrzebne do zbudowania samodzielnego terminala. W przypadku obsługiwanych urządzeń dostępna jest również karta microSD. Nie trzeba więc projektować PCB ani lutować kilkunastu przewodów pomiędzy ESP32 i ekranem.

W praktyce wystarczy urządzenie M5Stack, przewód USB i ewentualnie karta microSD. Dla mnie było to szczególnie istotne, ponieważ projekt ma być stosunkowo łatwy do powtórzenia. Jeżeli ktoś chce zbudować własną tablicę odjazdów, znacznie przyjemniej jest wgrać firmware do gotowego modułu niż zaczynać od szukania pinoutu wyświetlacza i lutowania przewodów SPI.

1a.thumb.JPG.0539bbba3bcfe3790db1a3cd5971a384.JPG1b.thumb.JPG.74bff959b74df96034c7cee2821bc55b.JPG

Program można zbudować w PlatformIO odpowiednio poleceniem: pio run -e m5stack-cores3 lub pio run -e m5stack-tab5 lub pobrać z sekcji Releases na GitHubie. Po uruchomieniu urządzenie pobiera z serwera listę dostępnych miast, pozwala wyszukać przystanek, a następnie wyświetla jego najbliższe odjazdy.

Po co osobny serwer w Pythonie?

Pierwszym pomysłem mogłoby być oczywiście bezpośrednie pobieranie danych przez ESP32. W końcu mikrokontroler ma Wi-Fi, HTTPS nie jest dla niego niczym niezwykłym, a informacji o kilku najbliższych pociągach nie powinno być dużo. Problem zaczyna się wtedy, gdy zobaczymy, w jaki sposób rozpowszechniany jest GTFS Schedule. GTFS opisuje między innymi przystanki, linie, kursy oraz godziny odjazdów. Dane statyczne są jednak zazwyczaj publikowane jako zestaw plików tekstowych umieszczonych w archiwum ZIP.

Osoba, która wymyśliła, żeby protokół przeznaczony między innymi do wymiany danych rozkładowych przesyłał wszystko jako ZIP pełen plików tekstowych, powinna dostać dożywotni zakaz projektowania protokołów.

Dla komputera nie stanowi to większego problemu. Dla niewielkiego urządzenia IoT oznacza natomiast konieczność pobrania dużego pliku, rozpakowania go i przeanalizowania setek tysięcy lub nawet milionów rekordów tylko po to, aby odpowiedzieć na proste pytanie: "Co odjeżdża z tego przystanku w ciągu najbliższych kilkudziesięciu minut?". Dlatego powstał drugi element projektu – WhenArrivalPL_API.

Jest to niewielki serwer oparty o Python, FastAPI i SQLite. Pobiera właściwe źródła GTFS, przetwarza je i udostępnia urządzeniom znacznie prostsze API zwracające dane w JSON. Serwer obsługuje również GTFS-Realtime TripUpdates tam, gdzie odpowiedni operator taki strumień udostępnia.

ESP32 zamiast analizować cały GTFS może więc wykonać na przykład zapytanie:

GET /transit/gdansk/schedule/Wrzeszcz%20PKP/10

i otrzymać gotową listę najbliższych dziesięciu odjazdów. Serwer sam uwzględnia opóźnienia z GTFS-RT i wylicza przewidywany czas odjazdu. Anulowane kursy oraz pominięte przystanki mogą zostać usunięte z wyników.

Dlaczego Python?

Python został wybrany celowo. Nie zależało mi na tworzeniu serwera wymagającego mocnego komputera ani konkretnej architektury procesora. Program ma działać na praktycznie dowolnym urządzeniu, na którym można uruchomić współczesnego Pythona.

Może to być więc przykładowo:

  • Raspberry Pi 3B, 4B lub 5B,
  • Raspberry Pi Zero,
  • Arduino UNO Q,
  • Arduino Ventuno Q,
  • inny komputer SBC z Linuxem,
  • stary laptop,
  • mini-PC,
  • tani terminal poleasingowy.

W większości przypadków taki serwer może pracować bez monitora gdzieś obok routera. Serwer wykorzystuje SQLite, więc nie wymaga również instalowania i konfigurowania osobnego PostgreSQL, MySQL czy innej usługi bazodanowej. Statyczny GTFS jest domyślnie pobierany podczas uruchomienia, a następnie aktualizowany co 24 godziny. Dane GTFS-Realtime oraz informacje dodatkowe mogą być odświeżane co 60 sekund. Częstotliwości te można zmienić parametrami programu.

3a.thumb.JPG.0575faa0c761218a4f2311d584057fe2.JPG3b.thumb.JPG.9df216660b94b5a3317aefb110c72ca5.JPG

W aktualnej wersji skonfigurowanych jest wiele źródeł danych – między innymi dla Gdańska, Warszawy, Krakowa, Wrocławia, Poznania, Szczecina oraz polskich kolei. Nie wszystkie źródła udostępniają GTFS-RT, dlatego dla części z nich dostępny jest wyłącznie rozkład statyczny.

Uruchamianie serwera

Serwer można uruchomić na Windowsie oraz Linuxie korzystając z Pythona. Główny plik znajduje się w /app/main.py. W obu przypadkach potrzebujemy przede wszystkim Pythona 3 oraz pip i zainstalowanych wymaganych bibliotek dostępnych w requirements.txt.

Po uruchomieniu serwer nasłuchuje domyślnie na wszystkich interfejsach i porcie 8000 tj. http://0.0.0.0:8000. Jeżeli nie potrzebujemy całej Polski, warto uruchamiać tylko wybrane źródła. Parametr --provider można podać kilka razy, np.:

--provider gdansk --provider polish-trains

Możemy również zmienić miejsce przechowywania bazy:

--database /home/user/mojastacja/transit.sqlite3

oraz częstotliwość pobierania danych:

--refresh-seconds 60 --static-refresh-seconds 86400

Warto też ustawić automatyczne uruchamianie serwera razem z komputerem.

Konfiguracja urządzenia

Po uruchomieniu serwera możemy skonfigurować właściwą tablicę. Firmware obsługuje dwa sposoby konfiguracji.

Pierwszym jest plik: include/secrets.h. Do projektu dołączony jest przykład, który należy skopiować: include/secrets.h.example do: include/secrets.h. Następnie ustawiamy:

#define SECRETS_WIFI_SSID "moja-siec"
#define SECRETS_WIFI_PASSWORD "moje-haslo"
#define SECRETS_PROVIDER_URL "http://192.168.1.50:8000"
#define SECRETS_NTP_SERVER "pool.ntp.org"

SECRETS_PROVIDER_URL powinien wskazywać komputer, na którym działa nasz serwer. Nie dopisujemy tutaj /transit ani żadnej innej końcówki. Poprawny adres wygląda więc przykładowo tak: http://192.168.1.50:8000 a nie: http://192.168.1.50:8000/transit

Konfiguracja z karty microSD

Alternatywnie można przygotować kartę microSD sformatowaną jako FAT32 i utworzyć na niej: /config.json o przykładowej zawartości:

{
  "wifi": {
    "ssid": "moja-siec",
    "password": "moje-haslo"
  },
  "provider_url": "http://192.168.1.50:8000",
  "ntp_server": "pool.ntp.org"
}

Jeżeli dostępne są oba źródła konfiguracji, niepuste wartości z config.json mają pierwszeństwo przed wpisami zapisanymi w secrets.h. Takie rozwiązanie jest wygodne, ponieważ po zmianie sieci Wi-Fi lub adresu serwera nie trzeba ponownie kompilować firmware.

Synchronizacja czasu przez NTP

Poprawny czas jest dla tablicy odjazdów oczywiście dość istotny. Urządzenie synchronizuje zegar przez NTP. Domyślnym serwerem jest: pool.ntp.org a zapasowym: time.cloudflare.com. Sam NTP dostarcza czas UTC. Firmware przelicza go następnie na czas obowiązujący w Polsce i sam uwzględnia zmianę pomiędzy CET i CEST. Nie trzeba więc dwa razy w roku ręcznie zmieniać zegara tablicy.

Ma to również znaczenie podczas analizowania odjazdów. API zwraca daty jako pełne znaczniki ISO 8601, a urządzenie normalizuje je do UTC przed porównaniem z aktualnym zegarem.

Wybór miasta i przystanku

Po poprawnym połączeniu z Wi-Fi i serwerem nie musimy wpisywać identyfikatorów GTFS ręcznie. Przytrzymanie ekranu przez około 3 sekundy otwiera konfigurację. Najpierw wybieramy miasto, a następnie wpisujemy fragment nazwy przystanku i naciskamy: SZUKAJ

Urządzenie pobiera z API maksymalnie 255 pasujących przystanków i pozwala wybrać właściwy z listy. Sposób wprowadzania tekstu zależy od urządzenia. Tab5 wyświetla pełną ekranową klawiaturę QWERTY. Na mniejszym CoreS3 zastosowałem natomiast klawiaturę podobną do tej ze starszych telefonów. Jeden przycisk odpowiada kilku literom: 2 ABC 3 DEF 4 GHI ... Kolejne dotknięcia w odstępie do 0,9 sekundy zmieniają aktualnie wybraną literę.

2a.thumb.JPG.6faf5612a695a68e4e780bffac9b8edb.JPG2b.thumb.JPG.0be56e2a9ed6e67c6543492a9a48e42c.JPG

Dodatkowo: WROC wraca do wyboru miasta, USUN usuwa ostatni znak, a: SZUKAJ wysyła zapytanie do serwera.

Wybrany przystanek jest zapisywany w pamięci urządzenia. Jeżeli dostępna jest karta SD, ustawienie może zostać zapisane na niej, natomiast przy jej braku firmware wykorzystuje pamięć nieulotną ESP32. Dzięki temu po każdym restarcie nie trzeba ponownie przechodzić całego procesu konfiguracji. Po wybraniu przystanku zaczyna działać właściwy ekran tablicy.

CoreS3 pobiera do 10 pozycji i pokazuje maksymalnie 6, natomiast większy Tab5 pobiera 24 i może wyświetlić do 14. Dane są odświeżane co 30 sekund, a odjazdy, których czas już minął, automatycznie znikają z listy.

Dłuższe nazwy stacji, kierunków i miast są automatycznie przewijane, dzięki czemu interfejs nie wymaga skracania nazw pochodzących z GTFS.

Podsumowanie

WhenArrivalPL (MojaStacja) zaczęła się od bardzo prostego problemu – czterech godzin spędzonych na czekaniu na pociąg – a skończyła jako nieco większy projekt składający się z firmware dla ESP32 oraz własnego serwera przetwarzającego GTFS.

Zastosowanie gotowego urządzenia M5Stack znacznie uprościło część sprzętową. Nie trzeba projektować elektroniki ani lutować ekranu, czytnika microSD i dodatkowych modułów.

Najważniejszą decyzją okazało się jednak przeniesienie obsługi GTFS z mikrokontrolera na osobny serwer. ESP32 dostaje dzięki temu dokładnie te dane, których potrzebuje, w postaci prostego JSON-a, natomiast Python może spokojnie zajmować się pobieraniem archiwów, rozpakowywaniem GTFS, zapisem danych do SQLite i obsługą GTFS-Realtime. Serwer nie wymaga przy tym specjalnego komputera. Raspberry Pi, niewielki SBC, stary laptop czy poleasingowy terminal w zupełności wystarczą.

Dzięki temu po pierwszym skonfigurowaniu system może działać praktycznie samodzielnie, a następnym razem, gdy przyjdzie czekać kilka godzin na pociąg, przynajmniej będzie wiadomo, jak bardzo jest spóźniony.

Linki do repozytoriów:

Pewnie wciąż zawiera masę błędów, jak to kod pisany z AI, ale z testów wynika, że wszystko działa względnie poprawnie.

Projekt korzysta z zasobów Mikołaja Kuranowskiego do pozyskiwania czasów odjazdów.

Edytowano przez H1M4W4R1
  • Lubię! 1

Podoba Ci się ten projekt? Zostaw pozytywny komentarz i daj znać autorowi, że zbudował coś fajnego!

Masz uwagi? Napisz kulturalnie co warto zmienić. Doceń pracę autora nad konstrukcją oraz opisem.

@H1M4W4R1 dzięki za opis kolejnego projektu! Mam nadzieję, że takie DIY będą inspiracją dla osób, której do tej pory unikały elektroniki, bo nie czuły się na siłach, aby projektować samodzielnie elektronikę. Tutaj widać, że są aktualnie dostępne fajne, gotowe rozwiązania, aby można było skupić się wyłącznie na programowaniu 🙂 

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...