Custom flight controller firmware for an auto-stabilizing quadcopter, running on a Raspberry Pi Pico 2 W (RP2350) in no_std Rust with the Embassy async framework. The drone is controlled entirely over a custom Wi-Fi UDP protocol from a PC — no RC radio.
Author: Carlos Artacho Leyva
- Reads a MPU-6050 IMU over I2C (calibration + complementary filter for pitch/roll).
- Receives throttle/pitch/roll/yaw commands and live PID gains from a PC over Wi-Fi UDP at 50 Hz.
- Runs a per-axis PID control loop and mixes the output into four motor commands.
- Drives four ESCs with a manually-timed DShot300 signal, generated inside an interrupt-free block for jitter-free timing.
- Software failsafe: motors are cut to zero throttle if UDP packets stop arriving.
- USB CDC serial output for telemetry and debugging.
- Raspberry Pi Pico 2 W (RP2350)
- MPU-6050 GY-521 IMU
- Mamba F45 4-in-1 ESC (DShot300)
- 4x ECOII 2306 brushless motors
- MINI560 buck converter (11.1V → 5V, since the ESC has no BEC)
- 3S 11.1V LiPo battery
- Mark4 quadcopter frame, FR4 perfboard, XT60 connectors
- Rust nightly toolchain with target
thumbv8m.main-none-eabihf picotoolfor flashing over USB- Python 3 (standard library only) for the ground-station client
| Function | Pico GPIO | Notes |
|---|---|---|
| Motor 1 (DShot) | GP0 | front-left |
| Motor 2 (DShot) | GP1 | front-right |
| Motor 3 (DShot) | GP2 | rear-right |
| Motor 4 (DShot) | GP3 | rear-left |
| ESC telemetry RX | GP5 | optional UART |
| I2C SDA | GP6 | MPU-6050 |
| I2C SCL | GP7 | MPU-6050 |
The MPU-6050 is powered from 3V3 OUT (pin 36). The Pico is powered from the MINI560 5V output into VSYS (pin 39).
The Python client (udp_client.py) controls the drone from the keyboard:
| Key | Action |
|---|---|
W / S |
Throttle up / down |
| Arrows | Pitch / roll |
A / D |
Yaw |
U / J |
Kp up / down |
I / K |
Ki up / down |
O / L |
Kd up / down |
SPACE |
Emergency stop |
Q |
Quit |
20-byte little-endian UDP packet, sent at 50 Hz:
struct.pack('<Hhhhfff', throttle, pitch, roll, yaw_rate, kp, ki, kd)
throttle: u16, 0–2000 (DShot)pitch,roll: i16, centidegreesyaw_rate: i16, centidegrees/skp,ki,kd: f32 PID gains
Five Embassy async tasks share state through Mutex<CriticalSectionRawMutex, _> globals:
usb_task— USB CDC telemetry (non-blocking sends)udp_task— parses control packets, updates setpoint + failsafe timestampimu_task— MPU-6050 read, calibration, complementary filterpid_task—Ticker-driven PID loop + motor mixer + failsafemotor_task— DShot300 frame transmission inside acritical_sectionblock
Reached tethered hover at ~50% throttle with working IMU, Wi-Fi control, PID stabilization and live tuning. Development paused after a hardware failure of the Pico during high-thrust bench testing (suspected BEC/Back-EMF event). See the full documentation for the troubleshooting log and failure analysis.