A PyPortal based flight tracker powered by Adafruit, Geoapify, ADSB.lol, and The OpenSky Network.
Heavily inspired by Bob Hammell's PyPortal Flight Tracker (GH, Tutorial).
Thank you to markleoart for creating the aircraft icon sprite sheets!
Users are assumed have read through Adafruit's PyPortal learning guide. CircuitPython v9.0 is currently in use for this repository, no other versions are evaluated & reverse compatibility is not guaranteed.
The CircuitPython libraries in lib
are sourced from the Official and Community bundles, which can be found on the CircuitPython libraries page. Compatibility for a given SkyPortal release is only ensured with library files vendored by this repository.
WARNING: In order to generate the background map tile, this project's boot.py
modifies the boot process to allow the filesystem to be used as a writeable cache. In the unlikely event that things go horribly awry you may lose the existing contents of your device, so be sure to back them up before working with this project.
To get up and running, copy the following files from the repository to your PyPortal:
assets/
lib/
skyportal/
boot.py
code.py
pyportal_startup.bmp
pyportal_startup.wav
secrets.py
skyportal_config.py
The Skyportal Releases page contains bundled *.tar.gz
archives, built in CI, that can be downloaded and extracted directly onto the device. Bundles come in two flavors: one pure-python implementation and a compiled version, where the skyportal
library has been compiled to *.mpy
and added to lib/
.
The following secrets are required for functionality:
secrets = {
# Your local timezone, see: http://worldtimeapi.org/timezones
"timezone": "America/New_York",
# WIFI information
"ssid": "YOUR_SSID",
"password": "YOUR_WIFI_PASSWORD",
# Geoapify, used to generate static mapping
"geoapify_key": "YOUR_GEOAPIFY_API_KEY",
# Adafruit IO, used for transient image hosting & local time lookup
"aio_username" : "YOUR_AIO_USERNAME",
"aio_key" : "YOUR_AIO_KEY",
# Open Sky Network credentials, for getting flight information
# Can be omitted if not using OpenSky
"opensky_username": "YOUR_OPENSKY_USERNAME",
"opensky_password": "YOUR_OPENSKY_PASSWORD",
# Proxy API Gateway credentials
# Can be omitted if not using a proxy server
"proxy_api_url": "YOUR_PROXY_API_URL",
"proxy_api_key": "YOUR_PROXY_API_KEY",
}
A collection of functionality-related constants is specified in skyportal_config.py
, which can be adjusted to suit your needs:
Variable Name | Description | Default |
---|---|---|
USE_DEFAULT_MAP |
Use the default map image rather than query Geoapify | False |
MAP_CENTER_LAT |
Map center latitude, decimal degrees | 42.41 |
MAP_CENTER_LON |
Map center longitude, deimal degrees | -71.17 |
GRID_WIDTH_MI |
Map grid width, miles | 15 |
AIRCRAFT_DATA_SOURCE |
Aircraft State API to utilize1 | opensky |
SKIP_GROUND |
Skip drawing aircraft on the ground | True |
GEO_ALTITUDE_THRESHOLD_M |
Skip drawing aircraft below this GPS altitude, meters | 20 |
Notes:
"opensky"
Query the OpenSky Network API. This requires a user account to be created & credentials added to secrets.py
.
Information on their REST API can be found here.
"adsblol"
Query the ADSB.lol. This currently does not require user authentication.
Information on their REST API can be found here.
NOTE: This API provides a lot of interesting information in the state vector provided for each aircraft. Depending on the level of congestion in your query area, may be more data than can fit into RAM (See: Known Limitations).
"proxy"
Query a user-specified proxy server using the URL and API key provided in secrets.py
.
For authentication, the API is assumed to expect an API key provided in the "x-api-key"
header.
The proxy API is assumed to expect three parameters:
lat
, center latitude, decimal degreeslon
, denter longitude, decimal degreesradius
, search radius, milesThe proxy API is expected to return two parameters:
"ac"
- A list of state vectors, as dictionaries, whose kv pairs map directly to skyportal.aircraftlib.AircraftState
"api_time"
- UTC epoch time, in seconds, may be a floatAn example using ADSB.lol and AWS Lambda is provided by this repository in ./adsblol-proxy
NOTE: Touchscreen input is mostly limited to one touch event per screen tap, rather than continuously firing while the screen is being touched.
NOTE: Due to the lack of an available asynchronous requests library for CircuitPython, web calls are blocking and will block touchscreen functionality until a response is obtained. A UI element in the lower left corner notifies the user whether or not the screen is currently able to process touch inputs.
Tapping on an aircraft icon will display state information for the aircraft closest to the registered touch point; this lookup is thresholded to a maximum distance of 30 pixels from the touch point. There is currently no method to disambiguate between aircraft that are in very close proximity.
The PyPortal is a highly memory constrained environment, which presents challenges when aiming to create a highly expressive UI. While every attempt is being made to minimize memory usage to keep the Skyportal functioning, the device may occasionally run out of memory. The most likely point for this to happen is when receiving the web request with aircraft state information from your API of choice. Depending on how congested your selected airspace is at query time, there may simply be too much information provided by the API for the device to handle & I've intentionally left the exception unhandled so it will crash the device. Should you find this ocurring often, you may be interested in setting up a proxy server to return only the information needed for the device to function, which can significantly alleviate the amount of RAM needed. See Proxy API for more information.