← Home · Parts list · Contact
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.
| 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. |
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 ./provision.sh # lists your Wi-Fi adapters sudo ./provision.sh wlan1 # monitor mode, udev, NetworkManager, bluetoothd sudo cp cielotrack-receiver.service /etc/systemd/system/ sudo systemctl enable --now cielotrack-receiver
Check it afterwards with
cat /opt/cielotrack-receiver/status.json — written every minute,
needing neither the server nor a claimed device, which are usually the things
you are trying to diagnose. radios.problems is empty when both
radios are as intended.
Full instructions are in the repository README.
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.
BLE: extended scanning active rather than legacy.You don't have to run our code. Anything that can make HTTPS requests can report to CieloTrack.
curl -X POST https://cielotrack.com/v1/devices/claim -H 'Content-Type: application/json' -d '{"device_id":"<uuid>","bootstrap_secret":"<random>"}'
curl https://cielotrack.com/v1/devices/<uuid>/status -H 'X-Bootstrap-Secret: <random>'
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.
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.
| Field | Type | Accepts |
|---|---|---|
detected_atWhen the aircraft was seen. The only required field. |
timestamp | required, ISO-8601 |
uas_idSerial or registration broadcast by the aircraft. |
string | up to 128 characters |
ua_typeAirframe class, e.g. Helicopter, Hybrid Lift, Fixed Wing. |
string | up to 64 characters |
protocolHow it was heard: BLE or Wi-Fi. |
string | up to 64 characters |
macRadio address the broadcast came from. |
string | up to 64 characters |
latAircraft latitude, degrees. |
number | -90 to 90 |
lonAircraft longitude, degrees. |
number | -180 to 180 |
altitude_mAircraft altitude, metres. |
number | -1,000 to 100,000 |
altitude_refWhat altitude_m is measured against: absolute (WGS84 or barometric) or agl. Prefer sending absolute here and putting height above takeoff or ground in height_m — an aircraft broadcasts both, and they answer different questions. agl remains accepted so existing senders keep working. |
string | up to 32 characters |
height_mHeight above the takeoff point or the ground, metres. This is the figure comparable to the 400 ft limit. Send it alongside altitude_m rather than instead of it. |
number | -1,000 to 100,000 |
height_refWhat height_m is measured from: takeoff or ground. |
string | up to 32 characters |
speed_mpsGround speed, metres per second. |
number | -1,000 to 1,000 |
operator_latController latitude, degrees. |
number | -90 to 90 |
operator_lonController longitude, degrees. |
number | -180 to 180 |
operator_altitude_mController altitude, metres. |
number | -1,000 to 100,000 |
operator_location_typeHow the operator position was derived: takeoff, live-gnss or fixed. |
string | up to 32 characters |
identity_sourcedecoded when read from this broadcast; inferred-from-mac when recovered from an earlier sighting of the same radio; undecoded when the contact carried no identifying message and none could be recovered. |
string | up to 64 characters |
message_countHow many Remote ID messages went into this record. |
integer | 0 to 10,000,000 |
rssi_dbmReceived signal strength, dBm (negative). Send null when the radio reports it as unavailable rather than sending the sentinel. |
integer | -200 to 20 |