reid.wiki

Projects / Embedded & Control

Drone Ground Station

A portable ground control station in a Pelican 1550: mini PC, custom ESP32-S3 control-panel PCB, dual MFD displays, and a fully engineered 12V LiFePO4 power system.

Status
Active
Timeline
2025 to present
Last updated
October 2026
ESP32-S3KiCadLVGLTinyUSBPlatformIOQGroundControlWFB-NGLiFePO4build123d

Overview

The autonomous drone needed something better than a laptop balanced on a truck tailgate. This is a self-contained ground station built into a Pelican 1550: a Beelink mini PC running Ubuntu, QGroundControl, and the WFB-NG video downlink, plus a custom control panel that makes the whole thing feel like equipment instead of my desk at home.

The control panel PCB

The panel is driven by a custom ESP32-S3 board that bridges physical controls (joystick, throttle, toggle switches, four 6-position rotary switches, a key switch) to the PC as a USB composite device: an HID gamepad that QGroundControl reads natively, plus a CDC serial channel for telemetry.

The board was routed entirely headless, verified at every step with kicad-cli: schematic ERC driven from 33 violations to 0, then autorouted to 0 DRC violations. The routing survived one rework. A pad-by-pad check against Espressif’s pinout showed the generic 2×20 header footprint put the DevKitC-1’s two pin rows 2.54 mm apart instead of 22.86 mm, so the module could not have seated. A custom footprint fixed the geometry, an SPI remap moved the display bus off the octal-PSRAM pins, and the board grew to 160 × 110 mm to fit the real module. The final route is 514 trace segments and 12 vias, with all 29 MCU pads checked against the datasheet.

Ground Station Controller PCB, 3D render
Controller PCB, 160 × 110 mm: DevKitC-1 socket, dual MCP23017 expanders, 514 segments and 12 vias routed headless.

Two ILI9341 displays run LVGL-based MFD/PFD screens, prototyped in an SDL2 desktop simulator that switches to real hardware with one line.

The firmware (PlatformIO, Arduino-ESP32, LVGL 9.5) compiles to 678 KB with 42% of RAM in use. Its first compile caught a bug that reading the source would not have: the DevKitC-1 board definition hard-sets ARDUINO_USB_MODE=1, the hardware CDC peripheral, which cannot present a HID device. Defining it as 0 only collided with the default; it had to be removed with build_unflags, or the gamepad would never have enumerated for QGroundControl.

GROUND STATION · MFD / PFDLINKPFDBATT14.6 VALT82 mRANGE2.14 miMODEAUTO ▸ WPT 3MFDJOYSTICKTHRTOGGLES ×4ROTARY ×4ARM KEY
LVGL PFD/MFD over CDC serial; the ESP32-S3 presents the controls to the mini-PC as a USB HID device.

The panel inserts

Both halves of the case get a flat insert: the lid carries the displays, the base carries the keyboard and flight controls. Each one is roughly 480 × 365 mm, far larger than my 256 mm print bed, so both come out of a build123d script that reads Pelican’s STEP model of the case, sections the interior at the panel plane, and splits the result into four quarters joined by half-lap seams with 0.2 mm of clearance.

Working from the STEP model fixed the worst problem with the first base panel, a single fabricated board with four identical 24.8 mm corner radii. The case corners are splines, not arcs: a best-fit circle misses them by 6.9 mm. Sectioning the real geometry gives an outline that follows the wall with 0.3 mm of clearance all the way around.

Exploded render of the lid insert: four printed panel quarters, standoffs, a birch frame, and five lid bosses
Lid insert, exploded: four printed quarters, 22.3 mm standoffs, a 9 mm Baltic birch frame, and the five molded lid bosses (amber) it screws into.

The model also turned up mounting points I did not have to add. The lid has five molded screw bosses (four corners, one on the centerline) with 3.2 mm pilot holes, and the base has 15 small ledges around its walls, 24.5 mm below the rim. Neither insert needs the case drilled or anything glued to it.

The lid stack is a 4 mm printed panel, 22.3 mm standoffs, and a 9 mm Baltic birch frame that screws to the bosses. The standoff length is not a free choice: it is whatever puts the back of the frame on the boss faces. The main display has no mounting holes of its own, so it is clamped instead. The panel window is smaller than the display body, and the frame presses it forward from behind.

MAIN DISPLAY345 × 195 mm window · clamped, no fastenersMFDMFDROTARY ×4SWITCHFANprint seamM3 to frame ×17lid boss ×5
Lid insert as seen with the lid open, generated from the same script that writes the print files. Four quarters meet at the dashed seams; 17 M3 screws tie them to the birch frame, which lands on the lid's 5 molded bosses (amber).

The base insert gets leaned on, so it is designed for stiffness. Deflection scales with span cubed: a 100 mm strip of 5 mm PLA across a 240 mm span sags about 5.5 mm under a 5 kg hand, and going to 7 mm only buys a factor of 2.7. Shortening the span is the better lever. The 7 mm panel sits flat on a 9 mm birch sub-frame that rests on all 15 ledges, held by 38 screws on a grid, so the largest gap between fasteners is 91 mm. Nothing is fixed to the case; the whole assembly lifts straight out.

Exploded render of the base insert: four printed panel quarters above a birch sub-frame
Base insert, exploded: four 7 mm printed quarters over the birch sub-frame. The quarters fit a 256 mm bed; the frames are CNC-cut as single pieces.

The keyboard and trackpad seat from below into pockets behind a 2 mm lip, so their surfaces sit just under the panel face and their cables never cross the top side. The trackpad is shallow enough that the sub-frame itself holds it up; the keyboard passes through the frame and is held by straps underneath.

KEYBOARD40% · 250 × 82 mm · recessed 2 mmTRACKPADBATTERY HATCHJOYSTICKTHROTTLESWITCHES ×4print seamM3 to sub-frame ×38case ledge ×15
Base insert, looking down into the case. The keyboard and trackpad seat from below behind a 2 mm lip; 38 M3 screws pull the quarters flat onto a birch sub-frame resting on the case's 15 molded ledges (amber).

The power system

A 12 V, 40 Ah LiFePO4 bus with a per-branch fuse schedule, pass-through AC charging, and thermostatic cooling. Budgeted at ~38 W typical draw for 13+ hours of runtime, with a labeled 23-wire harness cut list.

Status

PCB v1 is fabricated and hand-assembled: 25 parts, 187 pads, all through-hole. Next is staged bring-up (supply rails, then an I2C scan for the two MCP23017 expanders, then the displays) before the full firmware goes on. Remaining after that: porting the LVGL screens from the simulator, the PC-side telemetry bridge, touch input, and the battery fit-check (the 170 mm pack vs. 149 mm of case depth is an open argument with physics).

Panel inserts: the lid quarters have been test-printed and fit the lid. The base insert redesign and both birch frames are designed, with cut files ready to order.