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_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.
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.
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!
-- 'Aircrafts' - nazwa dummy text
OdpowiedzUsuń-- '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
}