Community project

ESP32 Quadcopter Drone Controller

ESP32

paul andré

Published July 15, 2026

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

Wiring diagram for ESP32 Quadcopter Drone Controller

Gather all the parts

QtyComponent
1

LiPo 3.7V 1000mAh Battery

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

Boost Converter

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"

FunctionbatteryESP32
power+VBoost Converter VIN+EXT
groundGNDBoost Converter VIN-EXT

2. Connections between "boost" and "ESP32"

FunctionboostESP32
powerVOUT+5V
groundVOUT-GND

3. Connections between "imu" and "ESP32"

FunctionimuESP32
powerVCC3V3
groundGNDGND
spiSCLGPIO 18
spiSDAGPIO 23
spiSAOGPIO 19
spiNCSGPIO 5

4. Connections between "rc_receiver" and "ESP32"

Functionrc_receiverESP32
powerVIN3V3
groundGNDGND
uartSIGNALGPIO 4

5. Connections between "mosfet_m0" and "ESP32"

Functionmosfet_m0ESP32
digitalGATEGPIO 12
groundSOURCEGND
digitalDRAINBrushed DC Motor — Rear Left (M0, CCW, Prop B) M-EXT

6. Connections between "rgate_m0" and "ESP32"

Functionrgate_m0ESP32
dataAUMW 100N03A N-Channel MOSFET GATEEXT
groundBGND

7. Connections between "motor0" and "ESP32"

Functionmotor0ESP32
powerM+VIN

8. Connections between "diode_m0" and "ESP32"

Functiondiode_m0ESP32
digitalANODEUMW 100N03A N-Channel MOSFET DRAINEXT
powerCATHODEVIN

9. Connections between "mosfet_m1" and "ESP32"

Functionmosfet_m1ESP32
digitalGATEGPIO 13
groundSOURCEGND
digitalDRAINBrushed DC Motor — Rear Right (M1, CW, Prop A) M-EXT

10. Connections between "rgate_m1" and "ESP32"

Functionrgate_m1ESP32
dataAUMW 100N03A N-Channel MOSFET GATEEXT
groundBGND

11. Connections between "motor1" and "ESP32"

Functionmotor1ESP32
powerM+VIN

12. Connections between "diode_m1" and "ESP32"

Functiondiode_m1ESP32
digitalANODEUMW 100N03A N-Channel MOSFET DRAINEXT
powerCATHODEVIN

13. Connections between "mosfet_m2" and "ESP32"

Functionmosfet_m2ESP32
digitalGATEGPIO 14
groundSOURCEGND
digitalDRAINBrushed DC Motor — Front Right (M2, CCW, Prop B) M-EXT

14. Connections between "rgate_m2" and "ESP32"

Functionrgate_m2ESP32
dataAUMW 100N03A N-Channel MOSFET GATEEXT
groundBGND

15. Connections between "motor2" and "ESP32"

Functionmotor2ESP32
powerM+VIN

16. Connections between "diode_m2" and "ESP32"

Functiondiode_m2ESP32
digitalANODEUMW 100N03A N-Channel MOSFET DRAINEXT
powerCATHODEVIN

17. Connections between "mosfet_m3" and "ESP32"

Functionmosfet_m3ESP32
digitalGATEGPIO 15
groundSOURCEGND
digitalDRAINBrushed DC Motor — Front Left (M3, CW, Prop A) M-EXT

18. Connections between "rgate_m3" and "ESP32"

Functionrgate_m3ESP32
dataAUMW 100N03A N-Channel MOSFET GATEEXT
groundBGND

19. Connections between "motor3" and "ESP32"

Functionmotor3ESP32
powerM+VIN

20. Connections between "diode_m3" and "ESP32"

Functiondiode_m3ESP32
digitalANODEUMW 100N03A N-Channel MOSFET DRAINEXT
powerCATHODEVIN

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.

Open in Schematik