Desk Pal plane radar

A tiny radar scope for your desk that shows the real planes flying near your home, with their callsigns. Print three parts, click in an ESP32-S3 touch screen, add your Wi-Fi, and learn how map maths turns positions into dots on a screen.

DifficultyIntermediate
Build time1h 30m
Est. cost$32
Parts4
Files9
Steps19

Things you'll need

QtyPartType
1
Waveshare ESP32-S3-Touch-LCD-1.28 board
Waveshare ESP32-S3-Touch-LCD-1.28 board The brain, a round 1.28-inch touch screen and a motion sensor in one board. No soldering.
electronics
3
Printed parts
Printed parts Scope body, front ring and foot. All three are in the 3MF below on one plate, no supports.
printed
1
USB-C data cable Powers the board and loads the program. It must be a data cable, not a charge-only one.
electronics
14 g
PLA filament Black looks like a real radar scope. Any PLA works.
consumable

Tools & software

1
Home Wi-Fi (2.4 GHz) The radar downloads plane positions over Wi-Fi. Almost every home router has 2.4 GHz. Ask a grown-up for the name and password.
1
Chrome or Edge browser Loads the program onto the board straight from this page. Nothing to install.
1
VS Code + PlatformIO Free. Needed once, to put your Wi-Fi name, password and home location into the program.

Files & downloads

Download all

Flash writes a precompiled image over USB-C in Chrome or Edge. The .ino below is the source — you do not need Arduino IDE unless you want to change the sketch.

What you're building

A little radar screen for your desk. You are the dot in the middle. Every plane flying within 25 km of your home shows up as a small green arrow pointing the way it's flying, with its callsign next to it, while a sweep line circles the scope like an airport radar. Tap the glass to zoom in.

No soldering. You print three parts, drop in one board, type your Wi-Fi details into the program, and upload it.

Note

Where do the planes come from? Most planes send out a radio message called ADS-B about twice a second: who they are, where they are and how fast they're going. Volunteers all over the world catch those messages with small antennas and share them on the internet. Your radar asks one of those free sites, adsb.fi, "what is flying near me?" every 20 seconds.

What you'll learn

  • How latitude and longitude pin your home to the map
  • How maths turns two map positions into a distance and a direction
  • How sin and cos turn a direction into an (x, y) dot on a round screen
  • How the radar guesses where planes are between downloads
  • How a chip with two cores can download and draw at the same time

Meet the parts

The black scope body, the small foot and the front ring
Three printed parts: the scope body (top), the foot (left) and the front ring (bottom).
  1. Scope body: the big part with the flat sides. The screen looks out of its round front. It has a window in the side for the USB-C plug.
  2. Front ring: holds the board. It has a notch where the USB-C port goes.
  3. Foot: a small block with a tab. It clips onto the back so the scope leans back and looks up at you.
  4. The board: a Waveshare ESP32-S3 with a round 1.28-inch touch screen and Wi-Fi built in.
Note

Before you start. The printer works on its own for about 30 minutes. You do about 5 minutes of clicking parts together. Getting the program on with your Wi-Fi takes about 20 minutes the first time, mostly waiting for downloads. You'll need a grown-up who knows the Wi-Fi password.


Part 1: Print

One plate, three parts, one colour. Start the printer, then read Part 2 while it works.

1

Download the 3MF

Grab Print-ready 3MF from Files & downloads above and open it in Bambu Studio. Using another slicer? Open the STL instead; all three parts are in it, already lying flat.

Download the 3MF
2

Pick a colour and print

Settings are already in the file: 0.2 mm layers, 2 walls, 15% infill, no supports. About 30 minutes on a Bambu P1S.

Pick a colour and print
3

Check the parts

Pick off any strings or blobs, especially around the USB-C window in the side of the body, the notch in the ring and the tab on the foot.


Part 2: Put it together

No glue, no screws. Three moves: board into the ring, ring into the body, foot onto the back. If a part feels tight, wiggle it; never force it.

1

Drop the board into the ring

Lay the front ring on the table. Put the board in screen-down, so you are looking at the back of the board with all the little chips. Turn it until the USB-C port sits in the notch of the ring.

Drop the board into the ring
2

Push the ring into the body

Hold the scope body with its open front facing you. Slide the ring in USB-C end first, so the port lines up with the window in the side wall. Then press the rest of the ring in until it sits flush with the front.

3

Clip on the foot

Push the foot's tab into the slot near the bottom of the back. Stand the scope up: it should lean back a little and stay put.

Clip on the foot

Part 3: Test the screen

Optional, but it only takes a minute and proves your board and screen work before you start typing.

Note

Word to know: flash. Flashing means copying a program onto a chip. The chip keeps it even when unplugged, like saving a game.

1

Plug in

Connect the USB-C cable from the board to a computer (Windows, Mac, Chromebook or Linux). It must be a data cable, not a charge-only one.

2

Click Flash to board

Scroll up to Files & downloads and click Flash to board. Use Chrome or Edge. Pick the port that appears, click Install, and wait for the green tick.

3

See the setup screen

The screen says Wi-Fi failed and lists four names: WIFI_SSID, WIFI_PASSWORD, HOME_LAT and HOME_LON. That's correct! The program on this page can't know your Wi-Fi. Part 4 fixes that.


Part 4: Add your Wi-Fi and your home

The radar needs three things only you know: your Wi-Fi name, its password, and where your home is. You'll type them into the program and upload it yourself with PlatformIO, a free add-on for VS Code that downloads everything this board needs.

1

Install VS Code and PlatformIO

Install VS Code. Open it, click Extensions (the four squares on the left), search for PlatformIO IDE and click Install. Wait until it says it's ready.

2

Open the project

Download platformio.zip from Files & downloads and unzip it. In VS Code choose File → Open Folder and pick the plane-radar-platformio folder. Then open plane-radar/plane-radar.ino.

3

Type in your Wi-Fi

Near the top you'll find the four lines shown below. Replace the words inside the quotes with your Wi-Fi name and password, exactly as they are written: capital letters and spaces matter. Leave the #ifndef and #endif lines around them alone.

4

Find your home on the map

Open Google Maps on a computer and right-click near your home. The first line of the menu shows two numbers, like 41.87810, -87.62980. The first is your latitude (HOME_LAT), the second your longitude (HOME_LON). Keep the minus sign if there is one, and keep the f at the end. Two numbers after the dot is plenty: that's accurate to about 1 km, so you don't need your exact house.

5

Upload

Save the file. Plug in the radar and click the → (Upload) arrow in the blue bar at the bottom of VS Code. The first build takes a few minutes while it downloads tools. Wait for SUCCESS.

Upload
6

Watch it find planes

The screen says joining Wi-Fi with your network's name, then the radar appears. Within a few seconds the bottom line should say LIVE and how many planes it can see.

Watch it find planes
plane-radar.ino
#define WIFI_SSID "YOUR_WIFI_SSID_HERE"          // your Wi-Fi name, inside the quotes
#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD_HERE"  // your Wi-Fi password, inside the quotes
#define HOME_LAT 41.8781f                        // your latitude (this one is Chicago)
#define HOME_LON -87.6298f                       // your longitude (keep the minus and the f)

The four lines to change, near the top of plane-radar.ino. In the real file each one sits between an #ifndef and an #endif line.

Warning

Keep your password private. After this step your Wi-Fi password is inside your copy of the program. Don't share your edited plane-radar.ino online, and don't post a screenshot of those lines.

Tip

Only 2.4 GHz works. The chip can't use 5 GHz Wi-Fi. Most home routers send both under the same name, so usually you don't have to do anything. If it won't join, ask a grown-up whether there is a separate 2.4 GHz network name.


Play it

Four things to try.

1

You're the dot in the middle

North is at the top, where the N is. Each green arrow is a real plane, pointing the way it's flying, and the word next to it is its callsign: the flight's name on air-traffic radio, like UAL123. Planes that don't send a callsign show their 6-character radio ID instead. The sweep line goes round once every 12 seconds, just for looks.

You're the dot in the middle
2

Tap = zoom

Tap the glass to change the range: 5, 10, 15, 25 km, then back to 5. The number at the bottom shows the range, and the rings split it into quarters. On 25 km, each ring is about 6 km further out.

3

Read the bottom line

LIVE 3 ac means 3 aircraft, downloaded live. SYNC means it's downloading right now. HOLD means the last download failed, so it's showing where the planes probably are. DEMO means pretend planes, because it couldn't get real ones.

4

Point it north

The top of the screen is always north. Put the radar on your desk so that when you look at it, you're facing north (a phone compass helps). Now a plane on the left of the screen really is to your west. Hear a plane? Look for it on the scope!


How it works: five tiny lessons

You don't need to read the whole program (it's in Files & downloads if you're curious). Each lesson below shows the one small piece of code that does the job, and gives you something to try.

Lesson 1: Your home is two numbers

Every place on Earth has an address made of two angles. Latitude says how far north or south of the equator you are (−90 to 90). Longitude says how far east or west of Greenwich in London you are (−180 to 180). Every plane sends its own latitude and longitude too.

The download is plain text in a format called JSON. For each plane the radar keeps six things: its radio ID (hex), callsign (flight), position (lat, lon), direction (track, in degrees from north) and ground speed (gs, in knots).

adsb.fi reply
{"ac": [
  {"hex": "a1b2c3", "flight": "UAL123  ", "lat": 41.95, "lon": -87.71, "track": 270, "gs": 250},
  {"hex": "c0ffee", "flight": "SWA42   ", "lat": 41.80, "lon": -87.60, "track": 95,  "gs": 180}
]}

A shortened, made-up example of what the radar downloads. The real reply has more fields; the program filters out everything except these six.

Tip

Try it: one degree of latitude is always about 111 km. A plane's latitude is 0.1 bigger than yours. How far north of you is it? (About 11 km: on the 10 km scale it's just off the top!)

Lesson 2: Two positions → how far and which way

The Earth is round, so the program can't just measure on a flat map. It uses the haversine formula, the same maths pilots and phones use. You don't need to understand every part: it takes your home and a plane's position and gives back how far (in km) and which way (the bearing: 0° is north, 90° east, 180° south, 270° west).

plane-radar.ino
// How far? Measured along the curve of the Earth.
const float a = sinf(dlat / 2) * sinf(dlat / 2) +
                cosf(lat1) * cosf(lat2) * sinf(dlon / 2) * sinf(dlon / 2);
distKm = 6371.0f * 2 * atan2f(sqrtf(a), sqrtf(1 - a));   // 6371 km = the Earth's radius

// Which way? 0 = north, 90 = east, 180 = south, 270 = west.
brgDeg = atan2f(sinf(dlon) * cosf(lat2),
                cosf(lat1) * sinf(lat2) - sinf(lat1) * cosf(lat2) * cosf(dlon)) * RAD_TO_DEG;

Simplified from geoToPolar(). dlat and dlon are the differences between the plane and your home.

Tip

Try it: type status in the Serial Monitor (see Secret commands) to check the home position the radar is using. Is it really you?

Lesson 3: Sin and cos put the dot on the screen

Now the program knows each plane's distance and bearing. To draw it, it needs an x and a y. Picture a clock hand pointing at the plane: sin says how far the hand's tip is to the right, cos how far it is up. Multiply by the distance and you have the dot.

One twist: on a screen, y counts down from the top, so the program subtracts the up part.

plane-radar.ino
static void polarToXY(float distKm, float brgDeg, int *px, int *py) {
  const float r = distKm / rangeKm() * 109;   // at the zoom distance, the dot is on the edge (109 px)
  const float rad = brgDeg * DEG_TO_RAD;      // degrees → radians, the unit sin and cos want
  *px = CX + sinf(rad) * r;                   // right = east
  *py = CY - cosf(rad) * r;                   // minus, because screen y counts down
}

Simplified from polarToXY(). CX and CY are the middle of the screen, (120, 120).

Tip

Try it: a plane due east (90°), 10 km away, on the 10 km scale. sin 90° = 1 and cos 90° = 0, so it's drawn at (120 + 109, 120) = (229, 120), on the right-hand edge. Where is a plane due south (180°), 5 km away, on the 10 km scale? (Answer: sin 180° = 0, cos 180° = −1, so (120, 174): halfway down.)

Lesson 4: Guessing between downloads

New positions only arrive every 20 seconds. If the radar just drew those, every plane would sit still and then jump. Instead, every frame it moves each plane a tiny bit along its direction at its speed. Sailors called this dead reckoning. When a real position arrives, the plane slides gently to it instead of jumping.

plane-radar.ino
static void coast(float *lat, float *lon, float track, float gs, float dt) {
  const float km = gs * 1.852f / 3600.0f * dt;     // knots → km travelled in dt seconds
  const float rad = track * DEG_TO_RAD;            // which way it's flying
  *lat += km * cosf(rad) / 111.32f;                // the northward part (111 km per degree)
  *lon += km * sinf(rad) / (111.32f * cosf(*lat * DEG_TO_RAD));  // the eastward part
}

Simplified from coast(). Degrees of longitude get narrower towards the poles, which is what the last cos is for.

Tip

Try it: an airliner cruising at 450 knots covers about 833 km per hour. How far does it fly in the 20 seconds between downloads? (About 4.6 km: almost the whole 5 km scale! That's why the guessing matters.)

Lesson 5: Two cores, two jobs

Downloading takes a second or two. If the drawing had to wait, the sweep would freeze every 20 seconds. The ESP32-S3 has two cores, like two small brains, so the program starts a helper task on one core that only downloads, while loop() keeps drawing on the other.

Both share the list of planes. To stop the drawer reading a list that's only half updated, they take turns with a lock (a mutex): like a talking stick, only the one holding it may touch the list.

plane-radar.ino
// In setup(): start a helper task on core 0 that only downloads.
xTaskCreatePinnedToCore(adsbFetchTask, "adsb", 16384, nullptr, 1, &fetchTaskHandle, 0);

static void adsbFetchTask(void *) {
  for (;;) {                                              // forever:
    const bool ok = fetchAircraft();                      //   ask adsb.fi what's flying near home
    vTaskDelay(pdMS_TO_TICKS(ok ? 20000 : 45000));        //   wait 20 s (45 s if it failed)
  }
}

Simplified from setup() and adsbFetchTask().

Note

Why doesn't it flicker? Every frame is drawn into a sprite: a 240 × 240 picture kept in memory. Only when it's finished does the program copy it to the screen in one go.


Secret commands

Your computer can chat with the board through the same USB-C cable. That chat is called serial. In VS Code, click the plug icon in the blue PlatformIO bar to open the Serial Monitor (115200), type a word, press Enter.

Type this What happens
help lists the commands
status Wi-Fi up or down, your home position, the range, and how many planes it has
range tells you the current range
range next same as tapping the glass
range 10 jumps straight to 5, 10, 15 or 25 km
Tip

Detective trick: if the radar isn't working, status tells you why. Look at the err= part: assoc timeout means a wrong Wi-Fi name or password, edit WIFI_SSID means you uploaded without your details.


Level up: change the code

You already know how to upload, so changing things is easy: edit, save, click →.

Ideas, easiest first:

  • Faster sweep: change SWEEP_MS = 12000.0f to 6000.0f.
  • Start zoomed in: rangeIdx = 3 means 25 km. Try 1 for 10 km.
  • Amber planes instead of green: change the colour below.
  • Bigger arrows: in drawPlane, change nose = 7.0f to 10.0f and wing = 4.5f to 6.5f.
plane-radar.ino
static const uint16_t COL_AC = RGB565(92, 255, 154);   // planes: {red, green, blue}
// try amber:
static const uint16_t COL_AC = RGB565(255, 190, 60);

Keep only one of the two lines. Every number goes from 0 to 255.


If something goes wrong

  • The screen says Wi-Fi failed and edit WIFI_SSID. The program still has the example text. Did you save the file before clicking Upload?
  • Wi-Fi failed and assoc timeout. Wrong name or password (capitals matter), or a 5 GHz-only network. Tap the screen to try again; it also retries by itself every 20 seconds.
  • It says DEMO and never LIVE. The radar is online but can't reach adsb.fi. Some school or office networks block it; try home Wi-Fi.
  • LIVE 0 ac. It works; there just aren't any planes near you right now. Zoom out to 25 km, or try again at a busier time of day.
  • Planes are in the wrong direction. Check the signs in your home position. Anywhere in the Americas has a minus longitude; south of the equator has a minus latitude. Then check you're facing north.
  • Upload says no port found. Try another USB-C cable. Still nothing? Take the ring out of the body, hold the board's BOOT button while you plug it in, let go, and upload again.

Build photos

Licensed under CC BY-SA 4.0. Check with the author before commercial use.

Community makes

Built this? Share a photo of your print so others can see how it turned out.

No makes yet. Be the first.