Salta al contenuto

Display e dispositivi

Display da scrivania, cornici e-paper, pannelli LED, ESP32 e Raspberry Pi: come mostrare gli aerei vicini senza sprecare richieste, da un pezzo a qualche migliaio.

Due schemi, a seconda di quanti pezzi

Ogni dispositivo chiede da sé

Per pochi pezzi. È il più semplice: il dispositivo chiede gli aerei nel suo raggio. Ma la chiave sta nel firmware, dove si può estrarre, e ogni dispositivo in più conta sulla stessa chiave.

Un tuo server per tutti

Da qualche decina di pezzi in su. Il tuo server scarica una volta (la zona dei tuoi clienti, o il mondo intero con Pro) e ogni dispositivo chiede al server solo gli aerei vicini a sé. Il consumo non cresce con i pezzi venduti e la chiave non esce mai dal server.

I tuoi dispositivi
chiedono al tuo server gli aerei vicini
Il tuo server
una richiesta ogni 30 s, tiene la copia in memoria
API FlyItalyADSB
una chiave per tutto il prodotto

Quanto consuma un dispositivo

Il dato si aggiorna ogni 2 secondi; per un display di solito bastano 15–30 secondi. Richieste al mese per un dispositivo che chiede da sé:

OgniAl mese, per dispositivoDispositivi nel piano Free
5 s518.400nessuno
15 s172.8001
30 s86.4003
60 s43.2006

Con lo schema del server il conto è uno solo: una richiesta ogni 30 secondi fa 86.400 richieste al mese, qualunque sia il numero di dispositivi.

Quanto pesa una risposta

Quanti aerei e quanti byte restituisce una richiesta, in alcuni posti:

ZonaRaggioAereiJSONCon gzip
Milano Malpensa15 km54 KB1 KB
Londra Heathrow50 km3733 KB7 KB
New York JFK50 km10563 KB10 KB
Francoforte500 km662670 KB111 KB
Mondo intero—~11.0007,2 MB1,1 MB

Misura del 26 ottobre 2026, 21:24 ora italiana. Di sera in Europa: di giorno gli aerei sono di più.

Il codice

Prova con curl (15 km intorno a Heathrow)
curl --compressed -H "X-Api-Key: LA_TUA_CHIAVE" \
  https://api.flyitalyadsb.com/v2/lat/51.47/lon/-0.45/dist/15
ESP32 che chiede da sé
// ESP32 (Arduino core 3.x) + ArduinoJson 7
// Gli aerei entro 15 km dal dispositivo, ogni 30 secondi.
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

const char* WIFI_SSID = "...";
const char* WIFI_PASS = "...";
const char* API_KEY = "LA_TUA_CHIAVE";  // meglio: chiedi al tuo server, che tiene la chiave
const char* URL = "https://api.flyitalyadsb.com/v2/lat/45.63/lon/8.72/dist/15";

void setup() {
  Serial.begin(115200);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(500);
}

void loop() {
  WiFiClientSecure tls;
  tls.setInsecure();  // solo per la prova: in un prodotto fissa il certificato radice
  HTTPClient http;
  http.useHTTP10(true);  // niente chunked: ArduinoJson legge lo stream
  http.begin(tls, URL);
  http.addHeader("X-Api-Key", API_KEY);
  http.setUserAgent("my-desk-display/1.0");
  int status = http.GET();
  if (status == 200) {
    JsonDocument filter;  // tieni solo i campi che il display mostra
    filter["ac"][0]["flight"] = true;
    filter["ac"][0]["t"] = true;
    filter["ac"][0]["alt_baro"] = true;
    JsonDocument doc;
    if (!deserializeJson(doc, http.getStream(), DeserializationOption::Filter(filter))) {
      for (JsonObject a : doc["ac"].as<JsonArray>()) {
        Serial.print(a["flight"] | "?");
        Serial.print("  ");
        Serial.print(a["t"] | "?");
        Serial.print("  ");
        if (a["alt_baro"].is<int>()) Serial.println(a["alt_baro"].as<int>());
        else Serial.println("ground");
      }
    }
  } else {
    Serial.printf("HTTP %d\n", status);  // 429: oltre il limite, riprova più tardi
  }
  http.end();
  delay(30000);  // ogni 30 s: 86.400 richieste al mese
}
Il tuo server per tutti i dispositivi (Python)
# server.py: una sola richiesta per tutti i tuoi dispositivi (Python 3.9+, nessuna dipendenza)
import gzip, json, math, os, threading, time, urllib.request
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import parse_qs, urlparse

KEY = os.environ["FIA_API_KEY"]
# Pro e Business: il mondo intero. Con Free usa la tua zona (fino a 500 km).
SOURCE = "https://api.flyitalyadsb.com/v2/lat/0/lon/0/dist/20000"
EVERY_S = 30
snapshot = {"now": 0, "ac": []}

def refresh():
    while True:
        try:
            req = urllib.request.Request(SOURCE, headers={
                "X-Api-Key": KEY, "Accept-Encoding": "gzip", "User-Agent": "my-product/1.0"})
            with urllib.request.urlopen(req, timeout=30) as r:
                body = r.read()
                if r.headers.get("Content-Encoding") == "gzip":
                    body = gzip.decompress(body)
            snapshot.update(json.loads(body))
        except Exception as e:  # continua a servire l’ultima copia
            print("refresh failed:", e)
        time.sleep(EVERY_S)

def km(la1, lo1, la2, lo2):
    p = math.pi / 180
    h = (math.sin((la2 - la1) * p / 2) ** 2
         + math.cos(la1 * p) * math.cos(la2 * p) * math.sin((lo2 - lo1) * p / 2) ** 2)
    return 12742 * math.asin(math.sqrt(h))

class Near(BaseHTTPRequestHandler):
    def do_GET(self):  # /near?lat=51.47&lon=-0.45&km=15
        q = {k: float(v[0]) for k, v in parse_qs(urlparse(self.path).query).items()}
        ac = [a for a in snapshot["ac"]
              if "lat" in a and km(q["lat"], q["lon"], a["lat"], a["lon"]) <= q.get("km", 15)]
        body = json.dumps({"now": snapshot["now"], "ac": ac}).encode()
        self.send_response(200)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

threading.Thread(target=refresh, daemon=True).start()
ThreadingHTTPServer(("0.0.0.0", 8080), Near).serve_forever()

Al posto di LA_TUA_CHIAVE la tua chiave: è gratuita, si chiede qui. Specifica completa in openapi.json.

Attenzione a

  • Una chiave in un firmware si può estrarre. Se vendi molti pezzi, tienila sul tuo server.
  • Raggio piccolo su un ESP32. Una risposta di 15 km pesa pochi KB; 50 km sopra New York già 63 KB. Filtra i campi come nell’esempio, o fai filtrare al tuo server.
  • Un User-Agent tuo in ogni richiesta, e l’header X-Api-Key.
  • Su un 429 aspetta i secondi di Retry-After invece di riprovare subito: le richieste respinte non contano, ma ripeterle non serve.
  • L’attribuzione sul dispositivo o nel manuale e sulla pagina del prodotto: la riga da usare.
  • Il vantaggio feeder (limiti Pro gratis) è per uso non commerciale: non copre i pezzi che vendi.

Per approfondire

← Per chi costruisce prodotti