Build a receiver

← Back

A receiver listens for the Remote ID broadcasts drones are required to transmit and reports them to your account. It receives only — it transmits nothing, and needs no permission to run.

What you need

Raspberry Pi
A 4 or 5; anything running 64-bit Raspberry Pi OS.
Bluetooth adapter
Must support Bluetooth 5 extended advertising. A Bluetooth 4 dongle will pair happily and silently miss most drones.
Wi-Fi adapter
Must support monitor mode, and must be a second adapter — monitor mode takes it off your network.
A USB 2.0 port
Not optional. See below.

Install

sudo apt update && sudo apt install -y python3-pip bluez aircrack-ng git
sudo git clone https://github.com/thetopnach/cielotrack-receiver.git /opt/cielotrack-receiver
cd /opt/cielotrack-receiver
sudo pip3 install -r requirements.txt --break-system-packages
sudo cp .env.example .env        # set BASE_LAT, BASE_LON and WIFI_INTERFACE
sudo cp cielotrack-receiver.service /etc/systemd/system/
sudo systemctl enable --now cielotrack-receiver

Full instructions are in the repository README.

Register it

On first start the receiver prints a six-digit claim code:

🔑 Central server: unclaimed. Claim code: 481-207 — enter this at https://cielotrack.com/receivers

Sign in, then enter that code on the Receivers page. Your receiver picks up its API key within a minute and starts reporting. The key is handed over exactly once and then erased server-side, so it never sits in a database waiting to be read.

Things that cost us time

USB 3.0 ports jam 2.4 GHz. A SuperSpeed port radiates broadband noise across the band both radios use. Ours raised the noise floor to −74 dBm and caught nothing for 46 hours while drones passed overhead at −90 dBm. Use USB 2.0 ports or a USB 2.0 extension cable for both radios.
Bluetooth 4 dongles miss most drones. Remote ID uses extended advertising. Check the log says BLE: extended scanning active rather than legacy.
Don't rotate Wi-Fi channels. Every beacon we have decoded arrived on channel 6, and a rotating scan is elsewhere most of the time.
Antenna placement beats everything. Moving a receiver across the house changed reception more than any software change we made.

Or send data from your own hardware

You don't have to run our code. Anything that can make HTTPS requests can report to CieloTrack.

1. Register the device

curl -X POST https://cielotrack.com/v1/devices/claim   -H 'Content-Type: application/json'   -d '{"device_id":"<uuid>","bootstrap_secret":"<random>"}'

2. Claim the returned code at /receivers, then collect the key

curl https://cielotrack.com/v1/devices/<uuid>/status   -H 'X-Bootstrap-Secret: <random>'

3. Send detections

curl -X POST https://cielotrack.com/v1/detections   -H 'Authorization: Bearer <api-key>'   -H 'Content-Type: application/json'   -d '{"detected_at":"2026-01-01T00:00:00Z","uas_id":"…","lat":32.9,"lon":-96.7}'

Batch up to 500 rows at /v1/detections/batch as {"detections":[…]}. Invalid rows come back with their index and the reason; valid rows in the same batch are still stored.

Detection fields

Only detected_at is required — send whatever else your hardware can decode, and omit or null the rest. Values outside these ranges are rejected rather than stored, so a bad reading never lands on the map.

FieldTypeAccepts
detected_at
When the aircraft was seen. The only required field.
timestamp required, ISO-8601
uas_id
Serial or registration broadcast by the aircraft.
string up to 128 characters
ua_type
Airframe class, e.g. Helicopter, Hybrid Lift, Fixed Wing.
string up to 64 characters
protocol
How it was heard: BLE or Wi-Fi.
string up to 64 characters
mac
Radio address the broadcast came from.
string up to 64 characters
lat
Aircraft latitude, degrees.
number -90 to 90
lon
Aircraft longitude, degrees.
number -180 to 180
altitude_m
Aircraft altitude, metres.
number -1,000 to 100,000
altitude_ref
What altitude_m is measured against: agl (height above ground, comparable to the 400 ft limit) or absolute.
string up to 32 characters
speed_mps
Ground speed, metres per second.
number -1,000 to 1,000
operator_lat
Controller latitude, degrees.
number -90 to 90
operator_lon
Controller longitude, degrees.
number -180 to 180
operator_altitude_m
Controller altitude, metres.
number -1,000 to 100,000
operator_location_type
How the operator position was derived: takeoff, live-gnss or fixed.
string up to 32 characters
identity_source
decoded when read from this broadcast; inferred-from-mac when recovered from an earlier sighting of the same radio.
string up to 64 characters
message_count
How many Remote ID messages went into this record.
integer 0 to 10,000,000
rssi_dbm
Received signal strength, dBm (negative). Send null when the radio reports it as unavailable rather than sending the sentinel.
integer -200 to 20