Community project
ESP32 Quadcopter Drone Controller
Build a fully functional quadcopter flight controller powered by the ESP32 microcontroller. This project combines a 3-axis IMU for attitude sensing, RC receiver input for pilot commands, and four brushed DC motors with MOSFET gate drivers to create a stable flying platform. The guide provides a complete wiring diagram, detailed parts list, and step-by-step assembly instructions to get the drone airborne.
The controller reads gyroscope and accelerometer data from the MPU-6500 IMU via SPI, processes incoming RC commands through the SBUS/UART receiver, and outputs PWM signals to four motor driver circuits. Each motor is protected with a Schottky flyback diode and gate pull-down resistor to ensure safe switching. The LiPo battery is boosted to supply stable power throughout the system, and the provided firmware handles motor mapping, pin configuration, and real-time flight stabilization.
Wiring diagram

Gather all the parts
| Qty | Component |
|---|---|
| 1 | Single-cell LiPo pack, nominal 3.7 V, 1000 mAh. Default rechargeable choice for portable ESP32 / Pico projects. Pair with a TP4056 charger for safe USB recharging. |
| 1 | Small adjustable MT3608-style DC-DC boost converter module for stepping a lower DC input up to a higher rail such as 5V, 9V, or 12V. It is a power-path module with VIN/VOUT terminals, not a GPIO peripheral. |
| 1 | Brushed DC Motor — Rear Right (M1, CW, Prop A) Motor 1, rear-right, clockwise, prop type A. Blue & Red wires. |
| 1 | Brushed DC Motor — Front Right (M2, CCW, Prop B) Motor 2, front-right, counter-clockwise, prop type B. Black & White wires. |
| 1 | Brushed DC Motor — Front Left (M3, CW, Prop A) Motor 3, front-left, clockwise, prop type A. Blue & Red wires. |
| 1 | Brushed DC Motor — Rear Left (M0, CCW, Prop B) Motor 0, rear-left, counter-clockwise, prop type B. Black & White wires. |
| 1 | MPU-6500 IMU (SPI) 6-axis IMU (3-axis gyro + 3-axis accel) used by Flix. Wired in SPI mode: SCL=SCK, SDA=MOSI, SAO=MISO, NCS=CS. |
| 1 | RC Receiver (SBUS/UART) RC receiver for drone control. Signal TX connected to ESP32 GPIO4. VIN powered from 3.3V or VCC depending on receiver model. |
| 1 | Gate Pull-down Resistor 100Ω 100 Ω 100Ω pull-down resistor from MOSFET gate to GND — keeps gate LOW when GPIO is floating, preventing accidental motor turn-on at boot |
| 1 | UMW 100N03A N-Channel MOSFET Logic-level N-ch MOSFET for Motor 0 low-side switching |
| 1 | 1N5822 Schottky Flyback Diode Flyback diode for Motor 0 |
| 1 | UMW 100N03A N-Channel MOSFET Logic-level N-ch MOSFET for Motor 1 low-side switching |
| 1 | UMW 100N03A N-Channel MOSFET Logic-level N-ch MOSFET for Motor 2 low-side switching |
| 1 | UMW 100N03A N-Channel MOSFET Logic-level N-ch MOSFET for Motor 3 low-side switching |
| 1 | Gate Pull-down Resistor 100Ω 100 Ω 100Ω pull-down resistor from MOSFET gate to GND — keeps gate LOW when GPIO is floating, preventing accidental motor turn-on at boot |
| 1 | Gate Pull-down Resistor 100Ω 100 Ω 100Ω pull-down resistor from MOSFET gate to GND — keeps gate LOW when GPIO is floating, preventing accidental motor turn-on at boot |
| 1 | Gate Pull-down Resistor 100Ω 100 Ω 100Ω pull-down resistor from MOSFET gate to GND — keeps gate LOW when GPIO is floating, preventing accidental motor turn-on at boot |
| 1 | 1N5822 Schottky Flyback Diode Flyback diode for Motor 1 |
| 1 | 1N5822 Schottky Flyback Diode Flyback diode for Motor 2 |
| 1 | 1N5822 Schottky Flyback Diode Flyback diode for Motor 3 |
Assemble it in 10 steps
1. Prepare the LiPo battery & boost converter
Connect the LiPo battery positive (+) to the boost converter VIN+ and negative (−) to VIN−. Adjust the MT3608 trim pot with a multimeter to set VOUT to exactly 5.0V BEFORE connecting anything else. An over-voltage output will damage the ESP32.
- Always pre-set the boost converter output to 5V before connecting the ESP32.
- Never reverse polarity on the LiPo terminals.
2. Power the ESP32
Connect boost converter VOUT+ to ESP32 VIN pin and VOUT− to ESP32 GND. The ESP32's onboard 3.3V LDO will supply the IMU and RC receiver from its 3V3 pin.
- Add a 100µF electrolytic capacitor across VBAT (LiPo+/GND) close to the motor pads to absorb motor startup spikes.
3. Wire the IMU (MPU-6500, SPI mode)
Connect the MPU-6500 breakout to the ESP32: • VCC → 3.3V • GND → GND • SCL (SCK) → GPIO18 • SDA (MOSI) → GPIO23 • SAO (MISO) → GPIO19 • NCS → GPIO5 This uses the ESP32 default VSPI bus.
- Keep SPI wires short (<10 cm) to avoid signal integrity issues at high speeds.
4. Wire the RC receiver
Connect the RC receiver: • VIN → 3.3V (or 5V if your receiver requires it — check its datasheet) • GND → GND • Signal TX → GPIO4 If your receiver outputs 5V signal levels, add a 10kΩ/20kΩ voltage divider to GPIO4.
- Check your specific receiver's logic level. A 5V signal into GPIO4 (3.3V tolerant only) will damage the ESP32.
5. Build Motor 0 protection chain (Rear Left, GPIO12)
For Motor 0 (black & white wires, rear-left position): 1. Connect ESP32 GPIO12 → 100Ω resistor IN 2. Resistor OUT → MOSFET GATE 3. MOSFET SOURCE → GND 4. MOSFET DRAIN → Motor 0 M− wire 5. Motor 0 M+ wire → LiPo VBAT rail 6. 1N5822 diode: ANODE to DRAIN node, CATHODE to VBAT rail (stripe = cathode).
- The 100N03A is a DPAK SMD part — solder it to a small PCB pad or use a breakout. D=Drain (top tab), G=Gate, S=Source.
- Verify diode orientation: stripe (cathode) must face VBAT, not GND.
6. Build Motor 1 protection chain (Rear Right, GPIO13)
Repeat the same 6-step chain as Motor 0 for Motor 1 (blue & red wires, rear-right): GPIO13 → 100Ω → GATE | SOURCE → GND | DRAIN → M− | M+ → VBAT | diode across motor.
7. Build Motor 2 protection chain (Front Right, GPIO14)
Repeat for Motor 2 (black & white wires, front-right): GPIO14 → 100Ω → GATE | SOURCE → GND | DRAIN → M− | M+ → VBAT | diode across motor.
8. Build Motor 3 protection chain (Front Left, GPIO15)
Repeat for Motor 3 (blue & red wires, front-left): GPIO15 → 100Ω → GATE | SOURCE → GND | DRAIN → M− | M+ → VBAT | diode across motor.
9. Double-check motor wire polarity
Flix uses specific wire colour conventions: • Prop B motors (CCW — M0 rear-left, M2 front-right): Black & White wires • Prop A motors (CW — M1 rear-right, M3 front-left): Blue & Red wires Connect M+ to VBAT and M− to the MOSFET DRAIN as wired. Swapping polarity reverses spin direction.
- If a motor spins the wrong way, swap its two wires — brushed motors reverse direction by swapping polarity.
10. Power-on test
With propellers OFF: 1. Power up via LiPo + boost converter. 2. Open the Schematik serial monitor (115200 baud). 3. Confirm '[FLIX] MPU-6500 IMU ready' is printed. 4. Confirm no motor twitching at boot. 5. Replace the stub loop with the real Flix firmware once wiring is confirmed.
- Never attach propellers during bench testing.
- Ensure all motor GND connections share a common GND with the ESP32.
Review all connections
1. Connections between "battery" and "ESP32"
| Function | battery | ESP32 |
|---|---|---|
| power | +V → Boost Converter VIN+ | EXT |
| ground | GND → Boost Converter VIN- | EXT |
2. Connections between "boost" and "ESP32"
| Function | boost | ESP32 |
|---|---|---|
| power | VOUT+ | 5V |
| ground | VOUT- | GND |
3. Connections between "imu" and "ESP32"
| Function | imu | ESP32 |
|---|---|---|
| power | VCC | 3V3 |
| ground | GND | GND |
| spi | SCL | GPIO 18 |
| spi | SDA | GPIO 23 |
| spi | SAO | GPIO 19 |
| spi | NCS | GPIO 5 |
4. Connections between "rc_receiver" and "ESP32"
| Function | rc_receiver | ESP32 |
|---|---|---|
| power | VIN | 3V3 |
| ground | GND | GND |
| uart | SIGNAL | GPIO 4 |
5. Connections between "mosfet_m0" and "ESP32"
| Function | mosfet_m0 | ESP32 |
|---|---|---|
| digital | GATE | GPIO 12 |
| ground | SOURCE | GND |
| digital | DRAIN → Brushed DC Motor — Rear Left (M0, CCW, Prop B) M- | EXT |
6. Connections between "rgate_m0" and "ESP32"
| Function | rgate_m0 | ESP32 |
|---|---|---|
| data | A → UMW 100N03A N-Channel MOSFET GATE | EXT |
| ground | B | GND |
7. Connections between "motor0" and "ESP32"
| Function | motor0 | ESP32 |
|---|---|---|
| power | M+ | VIN |
8. Connections between "diode_m0" and "ESP32"
| Function | diode_m0 | ESP32 |
|---|---|---|
| digital | ANODE → UMW 100N03A N-Channel MOSFET DRAIN | EXT |
| power | CATHODE | VIN |
9. Connections between "mosfet_m1" and "ESP32"
| Function | mosfet_m1 | ESP32 |
|---|---|---|
| digital | GATE | GPIO 13 |
| ground | SOURCE | GND |
| digital | DRAIN → Brushed DC Motor — Rear Right (M1, CW, Prop A) M- | EXT |
10. Connections between "rgate_m1" and "ESP32"
| Function | rgate_m1 | ESP32 |
|---|---|---|
| data | A → UMW 100N03A N-Channel MOSFET GATE | EXT |
| ground | B | GND |
11. Connections between "motor1" and "ESP32"
| Function | motor1 | ESP32 |
|---|---|---|
| power | M+ | VIN |
12. Connections between "diode_m1" and "ESP32"
| Function | diode_m1 | ESP32 |
|---|---|---|
| digital | ANODE → UMW 100N03A N-Channel MOSFET DRAIN | EXT |
| power | CATHODE | VIN |
13. Connections between "mosfet_m2" and "ESP32"
| Function | mosfet_m2 | ESP32 |
|---|---|---|
| digital | GATE | GPIO 14 |
| ground | SOURCE | GND |
| digital | DRAIN → Brushed DC Motor — Front Right (M2, CCW, Prop B) M- | EXT |
14. Connections between "rgate_m2" and "ESP32"
| Function | rgate_m2 | ESP32 |
|---|---|---|
| data | A → UMW 100N03A N-Channel MOSFET GATE | EXT |
| ground | B | GND |
15. Connections between "motor2" and "ESP32"
| Function | motor2 | ESP32 |
|---|---|---|
| power | M+ | VIN |
16. Connections between "diode_m2" and "ESP32"
| Function | diode_m2 | ESP32 |
|---|---|---|
| digital | ANODE → UMW 100N03A N-Channel MOSFET DRAIN | EXT |
| power | CATHODE | VIN |
17. Connections between "mosfet_m3" and "ESP32"
| Function | mosfet_m3 | ESP32 |
|---|---|---|
| digital | GATE | GPIO 15 |
| ground | SOURCE | GND |
| digital | DRAIN → Brushed DC Motor — Front Left (M3, CW, Prop A) M- | EXT |
18. Connections between "rgate_m3" and "ESP32"
| Function | rgate_m3 | ESP32 |
|---|---|---|
| data | A → UMW 100N03A N-Channel MOSFET GATE | EXT |
| ground | B | GND |
19. Connections between "motor3" and "ESP32"
| Function | motor3 | ESP32 |
|---|---|---|
| power | M+ | VIN |
20. Connections between "diode_m3" and "ESP32"
| Function | diode_m3 | ESP32 |
|---|---|---|
| digital | ANODE → UMW 100N03A N-Channel MOSFET DRAIN | EXT |
| power | CATHODE | VIN |
Deploy the firmware
/*
* Flix-compatible Quadcopter — ESP32 Pin Definitions
*
* Motor mapping (Flix convention):
* Motor 0 — Rear Left, CCW, Prop B → GPIO12
* Motor 1 — Rear Right, CW, Prop A → GPIO13
* Motor 2 — Front Right, CCW, Prop B → GPIO14
* Motor 3 — Front Left, CW, Prop A → GPIO15
*
* IMU (MPU-6500, SPI mode):
* SCK → GPIO18
* MOSI → GPIO23
* MISO → GPIO19
* CS → GPIO5
*
* RC Receiver:
* SIGNAL (TX) → GPIO4
*
* NOTE: GPIO12, 15 are boot-strapping pins on ESP32.
* They are used as outputs here (motor PWM). All motor
* PWM outputs are LOW at boot, so no strapping conflict occurs
* in normal use. Verified consistent with Flix hardware design.
*
* NOTE: GPIO5 is also a boot-strapping pin (SPI CS).
* It is driven HIGH at boot by the internal pull-up, which is
* correct SPI CS idle state (deasserted). Safe to use.
*/
#include <Arduino.h>
#include <SPI.h>
#include <MPU6500_WE.h>
// ── Pin definitions ──────────────────────────────────────────────
#define MOTOR0_PIN 12 // Rear Left, CCW, Prop B
#define MOTOR1_PIN 13 // Rear Right, CW, Prop A
#define MOTOR2_PIN 14 // Front Right, CCW, Prop B
#define MOTOR3_PIN 15 // Front Left, CW, Prop A
#define IMU_SCK 18
#define IMU_MOSI 23
#define IMU_MISO 19
#define IMU_CS 5
#define RC_SIGNAL_PIN 4
// ── LEDC (PWM) config ─────────────────────────────────────────────
#define PWM_FREQ 20000 // 20 kHz — above audible range
#define PWM_RES 8 // 8-bit: 0–255
#define CH_M0 0
#define CH_M1 1
#define CH_M2 2
#define CH_M3 3
// ── IMU ───────────────────────────────────────────────────────────
// Forward declarations
void setMotor(uint8_t channel, uint8_t throttle);
void setAllMotors(uint8_t throttle);
MPU6500_WE imu(IMU_CS); // SPI chip-select pin
// ── Helpers ──────────────────────────────────────────────────────
void setMotor(uint8_t channel, uint8_t throttle) {
ledcWrite(channel, throttle);
}
void setAllMotors(uint8_t throttle) {
setMotor(CH_M0, throttle);
setMotor(CH_M1, throttle);
setMotor(CH_M2, throttle);
setMotor(CH_M3, throttle);
}
// ── Setup ─────────────────────────────────────────────────────────
void setup() {
Serial.begin(115200);
Serial.println("[FLIX] Booting...");
// Motors — initialise to zero throttle immediately
ledcSetup(CH_M0, PWM_FREQ, PWM_RES);
ledcSetup(CH_M1, PWM_FREQ, PWM_RES);
ledcSetup(CH_M2, PWM_FREQ, PWM_RES);
ledcSetup(CH_M3, PWM_FREQ, PWM_RES);
ledcAttachPin(MOTOR0_PIN, CH_M0);
ledcAttachPin(MOTOR1_PIN, CH_M1);
ledcAttachPin(MOTOR2_PIN, CH_M2);
ledcAttachPin(MOTOR3_PIN, CH_M3);
setAllMotors(0);
Serial.println("[FLIX] Motors initialised (all OFF)");
// SPI bus for IMU
SPI.begin(IMU_SCK, IMU_MISO, IMU_MOSI, IMU_CS);
// IMU init
if (!imu.init()) {
Serial.println("[FLIX] ERROR: MPU-6500 not found — check SPI wiring!");
while (1) { delay(500); }
}
imu.enableGyrDLPF();
imu.setGyrDLPF(MPU6500_DLPF_6);
imu.setSampleRateDivider(4); // ~200 Hz
imu.setGyrRange(MPU6500_GYRO_RANGE_500);
imu.setAccRange(MPU6500_ACC_RANGE_4G);
imu.enableAccDLPF(true);
imu.setAccDLPF(MPU6500_DLPF_6);
Serial.println("[FLIX] MPU-6500 IMU ready");
// RC receiver pin
pinMode(RC_SIGNAL_PIN, INPUT);
Serial.println("[FLIX] RC signal pin ready (GPIO4)");
Serial.println("[FLIX] Init complete. Replace this stub with Flix firmware.");
}
// ── Loop — stub (replace with Flix PID + RC loop) ─────────────────
void loop() {
// Read IMU
xyzFloat gyr = imu.getGyrValues();
xyzFloat acc = imu.getGValues();
Serial.printf("[IMU] Gyr: %.2f %.2f %.2f | Acc: %.2f %.2f %.2f\n",
gyr.x, gyr.y, gyr.z, acc.x, acc.y, acc.z);
// ── Replace below with your Flix PID + RC input logic ──
// Motor 0 (rear-left, CCW): setMotor(CH_M0, throttle0);
// Motor 1 (rear-right, CW) : setMotor(CH_M1, throttle1);
// Motor 2 (front-right, CCW): setMotor(CH_M2, throttle2);
// Motor 3 (front-left, CW) : setMotor(CH_M3, throttle3);
setAllMotors(0); // safe default: motors off
delay(5); // ~200 Hz loop rate placeholder
}Remix this project
Make it yours in one click
Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.



