lcaloi/adsb2mqtt-node

By lcaloi

Updated about 1 hour ago

ADSB2MQTT shares local ADS-B receiver data over MQTT to build a collaborative aircraft network.

Image
Message queues
Internet of things
Data science
0

500

lcaloi/adsb2mqtt-node repository overview

ADSB2MQTT

Português do Brasil

ADSB2MQTT is an open source platform for sharing ADS-B readings from local receivers over MQTT and building a collaborative real-time aircraft network.

The project exists to make locally captured ADS-B data easier to share and to reward contributors with simple access to aggregated data from the rest of the network. Every node that contributes data should be able to consume data from other contributing nodes.

Who It Is For

  • ADS-B enthusiasts already running dump1090, readsb, tar1090, or similar stacks.
  • Operators who want to share local coverage without giving up control of their receiver.
  • Users who want a local aircraft.json-compatible API.
  • Users who want MQTT topics compatible with the dump1090 ecosystem.
  • Developers interested in an open ADS-B data network.

How It Works

Publishing And Consumption Diagram
graph TD;
    antenna["Antenna / SDR"] --> dump1090["dump1090 / readsb"];
    dump1090 --> node["adsb2mqtt-node"];

    node --> publicLocalhost;

    subgraph publicLocalhost[" "]
        direction LR
        public["Public"] <--> localhost["Localhost"];
    end

    localhost --> localApi["Local API /v1/aircraft"];
    localhost --> localMqtt["Local MQTT"];
Publishing And Consumption Flow
sequenceDiagram
    participant Antenna as Antenna / SDR
    participant Dump1090 as dump1090 / readsb
    participant Node as adsb2mqtt-node
    participant Local as Localhost
    participant Server as adsb2mqtt-server
    participant Public as Public MQTT Seed

    Node->>Server: registers or validates ADSBDB_NODE_ID
    Server-->>Node: returns ADSBDB_NODE_ID, ADSBDB_TOKEN, and MQTT credentials
    Server->>Public: syncs node ACL and password

    Antenna->>Dump1090: receives ADS-B / Mode-S signals
    Dump1090-->>Node: sends Beast or aircraft.json

    par Public publishing
        Node->>Public: publishes adsbdb/raw/<node_id>
        Node->>Public: publishes adsbdb/aircraft/<node_id>/<hex>
        Public-->>Server: delivers messages from nodes
        Server-->>Public: makes aggregated network data available
    and Local consumption
        Node->>Local: exposes local API /v1/aircraft
        Node->>Local: exposes optional local MQTT
    end

    alt Public seed unavailable
        Node-->>Public: keeps reconnecting
        Node->>Local: keeps local API and local MQTT available
    end

The node publishes ADS-B/Mode-S messages to the public MQTT network while, in parallel, it can expose local consumption through a dump1090 aircraft.json compatible API and optional local MQTT. If the public seed is unavailable, the node keeps trying to reconnect automatically while the local API and local MQTT keep working for consumers on the same machine or local network.

Quick Start

If you already have Beast output available, run the node:

docker run -d \
  --name adsb2mqtt-node \
  --restart unless-stopped \
  -e BEASTHOST="YOUR_DUMP1090_IP" \
  -e BEASTPORT="YOUR_DUMP1090_PORT" \
  lcaloi/adsb2mqtt-node:latest

On first boot, if ADSBDB_NODE_ID and ADSBDB_TOKEN are not declared, the node registers a network identity and prints setup instructions in the container logs:

ADS-B DB NODE REGISTERED

ADSBDB_NODE_ID
uuid-issued-by-the-network

ADSBDB_TOKEN
token-issued-by-the-network

Persist both values in your .env or docker-compose.yml to keep the same identity when the container is recreated.

Example Output

curl http://localhost/v1/aircraft
{
  "now": 1785973864,
  "messages": 547355,
  "aircraft": [
    {
      "hex": "e49e6c",
      "type": "adsb_icao",
      "flight": "PTLLG   ",
      "alt_baro": 12000,
      "gs": 232.8,
      "track": 319.88,
      "lat": -21.157562,
      "lon": -49.430886,
      "seen": 0.9,
      "rssi": -26.8
    }
  ]
}

Local endpoints:

curl http://localhost:8080/healthz
curl http://localhost:8080/v1/aircraft
curl http://localhost:8080/v1/nodes/ranking
curl http://localhost:8081/v1/aircraft

Requirements

  • Docker.
  • An ADS-B receiver with Beast output or aircraft.json.
  • Network access from the container to the receiver.

Common dump1090/readsb ports:

PortCommon use
30002Raw output
30003SBS/BaseStation
30005Beast output
30008Beast/receiver output in some setups

Running With Docker

docker run -d \
  --name adsb2mqtt-node \
  --restart unless-stopped \
  -e BEASTHOST="192.168.68.103" \
  -e BEASTPORT="30005" \
  lcaloi/adsb2mqtt-node:latest

Built-in defaults:

ADSBDB_POLL_INTERVAL=5s
ADSBDB_MAX_BATCH=200

Docker Compose

services:
  node:
    image: lcaloi/adsb2mqtt-node:latest
    restart: unless-stopped
    environment:
      BEASTHOST: "127.0.0.1"
      BEASTPORT: "30005"
      ADSBDB_NODE_ID: ""
      ADSBDB_TOKEN: ""

After the first registration, fill ADSBDB_NODE_ID and ADSBDB_TOKEN with the values printed in the logs.

Identity Onboarding

When ADSBDB_NODE_ID is not declared, the node automatically registers a new identity:

adsb2mqtt-node -> POST /v1/nodes/register -> adsb2mqtt-server

The server generates:

  • node_id: UUID v4 issued by the network.
  • token: secret used to authenticate the node.

On first registration, the log shows:

============================================
 ADS-B DB NODE REGISTERED
--------------------------------------------
 Persist this network identity before
 recreating the container.

 ADSBDB_NODE_ID
 5fd61d1c-37aa-4c77-8bd0-f27550cfe06f

 ADSBDB_TOKEN
 secret

 Add both values to docker-compose.yml
 or to your .env file.
============================================

If ADSBDB_NODE_ID is already declared, the node reuses that identity and the server validates whether the ADSBDB_NODE_ID + ADSBDB_TOKEN pair was issued by the network. A random ID receives 401 Unauthorized.

Beast And Dump1090

Beast mode:

BEASTHOST=127.0.0.1
BEASTPORT=30005

Enriched mode with aircraft.json:

DUMP1090_URL=http://127.0.0.1/data/aircraft.json

When BEASTHOST and DUMP1090_URL are configured together, the node uses Beast for real-time contribution and queries the dump1090 JSON to enrich /v1/aircraft with fields already calculated by the receiver.

If only Beast is configured, the node/server attempts to decode fields such as type, flight, alt_baro, gs, track, baro_rate, lat, and lon. Position decoding depends on recent even/odd CPR pairs from the same aircraft.

MQTT

The node publishes to the central broker:

adsbdb/raw/<node_id>
adsbdb/aircraft/<node_id>/<hex>
adsbdb/status/<node_id>

When MQTT connects, the expected log is:

connected to mqtt url=mqtt://seed.adsbdb.com.br:4222
published mqtt raw_messages=200 ...

If the public MQTT seed goes down, the node retries every 60 seconds. It does not use HTTP fallback for ingest while MQTT is configured; it keeps the local store and waits for reconnection:

mqtt unavailable raw_messages=200 ... waiting_reconnect=true
retrying mqtt connection url=mqtt://seed.adsbdb.com.br:4222

Embedded Local MQTT Broker

The adsb2mqtt-node image can start a local Mosquitto broker inside the same container for local consumers.

Enable it with:

ADSBDB_EMBEDDED_MQTT=true
ADSBDB_NODE_MQTT_PORT=1883

By default, the embedded local broker starts without login when these variables are empty:

ADSBDB_MQTT_ADMIN_USERNAME=
ADSBDB_MQTT_ADMIN_PASSWORD=

To require authentication on the local broker, set both:

ADSBDB_MQTT_ADMIN_USERNAME=admin
ADSBDB_MQTT_ADMIN_PASSWORD=abc123

If only one of them is set, the container fails on startup to avoid ambiguous configuration.

Bridge To The Official Seed

When embedded MQTT is enabled, the local Mosquitto bridges to the official public seed:

ADSBDB_EMBEDDED_MQTT=true

In this mode, the node publishes to the local broker at mqtt://127.0.0.1:1883, and local Mosquitto replicates to mqtt://seed.adsbdb.com.br:4222.

The bridge is bidirectional by topic:

local -> official seed:
  adsbdb/raw/<node_id>
  adsbdb/aircraft/<node_id>/+
  adsbdb/status/<node_id>

official seed -> local:
  adsbdb/raw/+
  adsbdb/aircraft/+/+

The bridge uses the node MQTT credentials for the official seed. On first boot, adsb2mqtt-node registers the identity first, receives ADSBDB_NODE_ID and ADSBDB_TOKEN, and only then starts local Mosquitto with the authenticated bridge. There is no need to restart the container just to activate the bridge after first onboarding.

Local Node API

The node exposes a local API on :8081:

curl http://localhost:8081/healthz
curl http://localhost:8081/v1/aircraft
curl http://localhost:8081/v1/stats

Node Variables

VariableDefaultDescription
ADSBDB_NODE_IDemptyPublic node identifier. If empty, the node requests a UUID from the network.
ADSBDB_TOKENemptyToken used to authenticate when ADSBDB_NODE_ID is already declared.
ADSBDB_MQTT_USERNAMEnode_<ADSBDB_NODE_ID>Node MQTT username.
ADSBDB_MQTT_PASSWORDADSBDB_TOKENNode MQTT password.
ADSBDB_NODE_ADDR:8081Local HTTP address for the node.
BEASTHOSTemptyBeast output host/IP.
BEASTPORT30008Beast TCP port.
DUMP1090_URLemptyURL for dump1090/readsb aircraft.json.
ADSBDB_POLL_INTERVAL5sBatch send interval.
ADSBDB_MAX_BATCH200Maximum messages per batch.
ADSBDB_EMBEDDED_MQTTfalseStarts local Mosquitto inside the container.
ADSBDB_MQTT_ADMIN_USERNAMEemptyLocal broker admin user. If username and password are empty, local MQTT starts without login.
ADSBDB_MQTT_ADMIN_PASSWORDemptyLocal broker admin password. Must be set together with the username to require authentication.

Troubleshooting

Check whether the receiver is reachable from the machine running Docker:

ping 192.168.68.103
nc -vz 192.168.68.103 30005

Test the central MQTT broker:

nc -vz seed.adsbdb.com.br 4222

Subscribe to topics:

mosquitto_sub -h seed.adsbdb.com.br -p 4222 \
  -u ADSBDB_NODE_ID -P ADSBDB_TOKEN \
  -t 'adsbdb/#' -v

Important logs:

published mqtt raw_messages=...     # MQTT active
sent raw_messages=...               # HTTP fallback, only when MQTT is empty
mqtt unavailable ...                # MQTT configured, waiting for reconnection
mqtt connect timeout ...            # broker unreachable on startup

Roadmap

Phase 1: Operator Experience
  • ✅ Local and public API with dump1090 aircraft.json compatible output.
  • 🚧 Web dashboard with real-time map.
  • 🚧 Node health visualization.
Phase 2: Network Quality
  • 🚧 Node ranking by availability, volume, and data quality.
  • ⏳ Score based on geographic coverage, valid positions, and stability.
  • ⏳ Public contribution metrics.
  • ⏳ Alerts for offline or low-quality nodes.
Phase 3: Governance And Access
  • ✅ Initial node registration with persistent identity.
  • ✅ Deprecated image blocking with ADSBDB_DEPRECATED_JOIN_TOKENS.
  • ⏳ Contribution-based consumption quotas.
  • ⏳ Rate limits by node, token, and client type.
  • ⏳ Node registration with public keys.
Phase 4: Scale And Federation
  • ⏳ Central MQTT broker with automated ACL.
  • ⏳ Geospatial history with a dedicated database.
  • ⏳ Federation between seeds and servers.
  • ⏳ Cross-region replication.

Documentation

License

MIT. See LICENSE.

Tag summary

Content type

Image

Digest

sha256:4dfcf505d

Size

8.9 MB

Last updated

about 1 hour ago

docker pull lcaloi/adsb2mqtt-node