niedziela, 19 sierpnia 2018

Dzisiaj śledzimy niebo i to co nad nami lata... A do tego nawet to wyświetlimy!

Wczoraj w nocy bawiłem się w detektywa ;) Śledziłem samoloty... Czyli nic nielegalnego, można powiedzieć - niestety. 

Łukasz Jokiel z zaprzyjaźnionego bloga opisywał sposób budowy swojego odbiornika ADS-B. Pomysł świetny, czemu nie. Ale kolega (ponownie pozdrowienia dla Macieja), zasugerował mi inny projekt - zdecydowanie prostszy w realizacji.

Postanowiłem dodać kolejny bardzo długi post. Love it or hate it, ale będzie dużo czytania. Wydaje mi się jednak, że czasem nawet warto przejść po raz kolejny pewne zebrane w innych miejscach tematy dla przypomnienia.

Nad moją miejscowością lata dużo samolotów, co często podziwiamy z dzieciakami. Kolejnym gadżetem z serii 'Nie jest mi to do niczego potrzebne, ale warto to zrobić' jest informacja o przelatujących nad nami samolotach z wykorzystaniem Wemos D1 Mini oraz wyświetlacza OLED SSD1306. Przy okazji nauczymy się wiele rzeczy! Wstyd się przyznać, ale sam kilka tematów robiłem po raz pierwszy w życiu.

W tym wpisie przewiną się między innymi:
- Wemos D1 Mini
- OLED SSD1306
- pakiet Arduino IDE do obsługi Arduino i innych platform
- GitHUB
- ładowanie sketch'y (i co to w ogóle jest)
- kompilacja
- wgrywanie skompilowanego kodu na docelową platformę

Czyli dużo tematów. Gdzie nie sięgniesz tam sprawa zaczyna mieć zawsze jakieś głębokie korzenie :)

Efekt finalny będzie wyglądał jak na zdjęciu poniżej:

Oczywiście wiem, że jest FlightRadar24. Trzeba jednak siedzieć z komórką, albo otwartym komputerem, a my będziemy mieli informację na bieżąco wyświetloną na ekranie. No i zrobimy coś sami, dużo się przy okazji ucząc.

Wemos D1 Mini (i jego klony) jest już bardzo popularnym urządzeniem, do tego tanim. 
2-3 dolary za platformę:
- opartą o ESP-8266, 
- kompatybilną z Arduino, NodeMCU, MicroPython, 
- posiadającą WiFi i 
- posiadającą dużą bazę 'shieldów' (czyli 'nakładek' rozszerzających funkcjonalność) 
jest ceną naprawdę niską, nawet dla samej zabawy i poznania nowych możliwości.

Wyświetlacz to już kwestia 20 PLN, to również jednak moim zdaniem nie jest wielką barierą.

Zaczniemy od czegoś prostszego - wyświetlimy tekst na ekranie.

Podłączamy jak poniżej:

 


Jak widać, jest to banalnie proste:
GND -> GND
VCC -> 3.3V
SCL -> D1
SDA -> D2 

I to tyle jeżeli chodzi o Hardware. Super!

Przechodzimy do oprogramowania. Zaczynamy od Arduino IDE: https://www.arduino.cc/en/Main/Software. Alternatywą jest PlatformIO, nawet w przypadku tego projektu jest to ciut prostsze, ale skupmy się na ten moment na Arduino IDE.

Po pierwsze podłączamy D1 Mini to komputera. I teraz UWAGA - już kilka razy przekonałem, się, że dużo zależy od kabla USB -> Micro USB. Ileż razy kląłem, gdy sprzęt nie chciał się pojawić na liście, a wystarczyło wymienić kabel na porządny. Gdy już pojawi się nam nowy port COM, wypełniamy w sekcji Tools w Arduino IDE następujące parametry:


Chcemy połączyć się z ekranem OLED, a do tego będą nam potrzebne dodatkowe biblioteki. Przechodzimy do Sketch -> Include Library -> Manage Libraries.


Wyszukujemy dwie z pakietu Adafruit:
- Adafruit GFX
- Adafruit SSD1306
wybieramy je i instalujemy. Opcja będzie dostępna po ich zaznaczeniu.

Trzecia jaka jest konieczna to:
- ESP8266 OLED driver for SSD1306 autorstwa Daniela Eichhorna



Świetnie, brawo! Jesteśmy gotowi.

Czym jest sketch? Niczym więcej jak programem, który musimy napisać, później skompilować na język maszynowy i wgrać do naszego modułu.

Otwórzcie nowy plik i wklejcie poniższe. Nie wgłębiamy się w każdą z bibliotek, bo to mogłoby nam zająć miesiące ;), na ten moment wykorzystamy tylko to, czego potrzebujemy.


#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 0  // GPIO0
Adafruit_SSD1306 OLED(OLED_RESET);

void setup()   {
  OLED.begin();
  OLED.clearDisplay();

  //Dodajemy elementy do 'display listy'
  OLED.setTextWrap(false);
  OLED.setTextSize(1);
  OLED.setTextColor(WHITE);
  OLED.setCursor(0,0);
  OLED.println("Cezarowy blogspot");

  OLED.display(); //wyświetl zawartość pamięci ekranu na ekran 
  OLED.startscrollleft(0x00, 0x0F); //make display scroll 


void loop() {
}

Można wybrać Verify/Compile, co wyłącznie skompiluje nasz program, można od razu Upload, co wymusi kompilację i załaduje skompilowany sketch do D1 Mini.


Cierpliwie czekamy na zakończenie kompilacji...

I załadowanie programu do D1 Mini...


I od razu efekt!


Skoro proste tematy mamy za sobą, ruszamy od razu z czymś trudniejszym. A co! Do odważnych świat należy!

Po pierwsze, kolejne dwie biblioteki do pobrania. WiFiManager i biblioteka do parsowania (czyli czytania i przetwarzania) plików JSON.



Od razu piszę, że pomysł i kod nie jest mój, dostosowałem znaleziony przez Macieja kod do swoich potrzeb.

Zacznijmy od tego, że skorzystaliśmy z biblioteki Weather Station opracowanej przez zespół ThingPulse. W ramach dostępnych w niej przykładów znajduje się kod PlaneSpotterDemo, który przerobimy na nasze potrzeby.

Ze strony https://github.com/ThingPulse/esp8266-weather-station pobieramy ZIP z biblioteką, którą trzeba rozpakować do folderu jak poniżej.



Restart Arduino IDE i już mamy w Examples dostępny kod PlaneSpotterDemo.


U mnie potrzebny był 'delikatny' tuning.

W parametrach wywołania strony, która pokazuje JSON trzeba podać promień okręgu jaki mamy sprawdzać, oraz jeżeli chcecie - wysokość graniczną samolotów Od/Do.

Przy okazji dowiecie się jak wyglądają pliki JSON, które można później obrabiać, czytać i zapisywać na przykład w Domoticz.

Na przykład:
https://public-api.adsbexchange.com/VirtualRadar/AircraftList.json?lat=LATZDomoticz&lng=LongZDomoticz&fDstL=0&fDstU=12, gdzie fDstL i fDstU oznacza odległość Od/Do jaka nas interesuje.

Kolejny: fDstL=0&fDstU=12&fAltL=5000&fAltU=45000 interesują nas samoloty, które są w odległości od zera do 12 kilometrów, na wysokości od 5 do 45 tysięcy stóp.

Aby zobaczyć co lata obok lotniska na Balicach, wystarczy sprawdzić adres: https://public-api.adsbexchange.com/VirtualRadar/AircraftList.json?lat=50.0769780&lng=19.7881190&fDstL=0&fDstU=100

W ogóle - nazwy parametrów to jak widać fDst, L oraz U oznacza limit dolny oraz górny, S - zaczynający się od - jeżeli szukacie konkretnych modeli samolotów. Itd., itp. Dokładna dokumentacja wszystkich parametrów znajduje się pod adresem: http://www.virtualradarserver.co.uk/Documentation/Formats/AircraftList.aspx

Aplikacja o której tutaj mówimy pobiera właśnie dane z tej strony, przetwarza je, zapisuje w odpowiednich zmiennych i wyświetla na ekranie OLED.

Aby być pewnym, że moje parametry są poprawne, uruchamiam na jednym ekranie https://global.adsbexchange.com/VirtualRadar/desktop.html z moją okolicą, na drugim odświeżam wyniki https://public-api.adsbexchange.com/VirtualRadar/AircraftList.json?lat=50.0769780&lng=19.7881190&fDstL=0&fDstU=100 (podajecie oczywiście swoje współrzędne). Wtedy wiem, że to co mam na mapie pokrywa się z tym, co zawierają dane w formacie JSON.




Aby program zadziałał musimy prowadzić kilka zmian.

Po pierwsze, mam ekran SSD1306, także usuwam komentarz (czyli //) z jednej linii i komentuję tą z SH1106.

#include "SSD1306Wire.h"
//#include "SH1106Wire.h"

const String QUERY_STRING = "lat=LatZDomoticz&lng=19.562571&fDstL=0&fDstU=12";

Autor podłącza piny trochę inaczej niż ja, również musiałem to zmienić.

//const int SDA_PIN = D3;
//const int SDC_PIN = D4;
const int SDA_PIN = D2;
const int SDC_PIN = D1;

SSD1306Wire     display(I2C_DISPLAY_ADDRESS, SDA_PIN, SDC_PIN);
//SH1106Wire      display(I2C_DISPLAY_ADDRESS, SDA_PIN, SDC_PIN);

Autor korzysta z biblioteki WiFiManager i stara się automatycznie podłączyć do dostępnej, otwartej, sieci. Ja musiałem się podłączyć do swojej, opatrzyłem więc komentarzem następujące linie i podałem w funkcji WiFi.begin swoje dane sieci - nazwę i hasło.

//  WiFiManager wifiManager;
// Uncomment for testing wifi manager
//wifiManager.resetSettings();
//  wifiManager.setAPCallback(configModeCallback);

//or use this for auto generated name ESP + ChipID
//  wifiManager.autoConnect();

//Manual Wifi
  WiFi.begin("WiFiID", "Password");
//  String hostname(HOSTNAME);
//  hostname += String(ESP.getChipId(), HEX);
//  WiFi.hostname(hostname);

  // Setup OTA
//  Serial.println("Hostname: " + hostname);
//  ArduinoOTA.setHostname((const char *)hostname.c_str());
//  ArduinoOTA.onProgress(drawOtaProgress);
//  ArduinoOTA.begin();

Kompilacja powinna przejść bez problemu, ładowanie identycznie jak w przypadku pierwszego przykładu również.

TADAM! Mamy, działa, pokazuje!

Tak w sumie posiadając ekran otwierają się przed nami nowe możliwości eksperymentowania - z samych przykładów można się mnóstwo nauczyć i robić swoje małe (albo i duże) projekty.

Postaram się rozszerzyć ten wpis o kolejny, w którym dane wylądują w Domoticz, ale tak prawdę mówiąc, to muszę się tego najpierw sam nauczyć :D




Powodzenia! Pozdrawiam serdecznie!

1 komentarz:

  1. -- 'Aircrafts' - nazwa dummy text
    -- 'xxxx' w apiUrl - tu wstaw współrzędne, które Cię interesują
    -- 'fDstU' 'fDstU' minimalna i maksymalna odległość od podanych współrzędnch

    -- skrypt odpala się co minutę, mnie interesowała trochę większa częstotliwość stąd:
    -- domoticz.openURL({ url = apiUrl, method = apiMethod, callback = apiCallback }).afterSec(15) -- i kolejne
    -- if(gnd == false) to warunek na samoloty, które są w powietrzu

    return {
    on = {
    timer = { 'every minute' },
    httpResponses = { 'aircraftsRetrieved' }
    },
    execute = function(domoticz, item)
    if (item.isTimer) then
    local apiUrl = 'https://public-api.adsbexchange.com/VirtualRadar/AircraftList.json?lat=xxxx&lng=xxxx&fDstL=0&fDstU=1'
    local apiMethod = 'GET'
    local apiCallback = 'aircraftsRetrieved'
    domoticz.openURL({ url = apiUrl, method = apiMethod, callback = apiCallback })
    domoticz.openURL({ url = apiUrl, method = apiMethod, callback = apiCallback }).afterSec(15)
    domoticz.openURL({ url = apiUrl, method = apiMethod, callback = apiCallback }).afterSec(30)
    domoticz.openURL({ url = apiUrl, method = apiMethod, callback = apiCallback }).afterSec(45)
    elseif (item.isHTTPResponse) then
    local textArea = domoticz.devices('Aircrafts')
    local cnt = 0
    local caption = ''
    if (item.ok) then
    local json = domoticz.utils.fromJSON(item.data)
    for i, ac in pairs(json.acList) do
    local gnd = ac.Gnd and ac.Gnd or false
    if(gnd == false) then
    cnt = cnt + 1
    local mdl = ac.Mdl and ac.Mdl or '-' --model
    local from = ac.From and ac.From or '-'
    local to = ac.To and ac.To or '-'
    local op = ac.Op and ac.Op or '-' --operator
    local call = ac.Call and ac.Call or '-' --call name
    local dst = ac.Dst and ac.Dst or '-' --distance
    local alt = ac.Alt and domoticz.utils.round(ac.Alt * 0.3048) or '-' --altitude
    local spd = ac.Spd and domoticz.utils.round(ac.Spd * 1.852,0) or '-' --speed
    caption = caption..'Aircraft: '..mdl..'\nFrom: '..from..'\nTo: '..to..'\nOper: '..op..'\nCall: '..call..'\nDist: '..dst..' km'..'\nAlt: '..alt..' km'..'\nSpeed: '..spd..' km/h'..'\n\n'
    end
    end
    if(cnt == 0) then
    caption = '-'
    end
    else
    caption = '?'
    end
    if(not(textArea.text == caption)) then
    textArea.updateText(caption)
    end
    end
    end
    }

    OdpowiedzUsuń