Community project

ESP32 Quadcopter Drone Controller

paul andré

Published July 15, 2026

ESP3220 components10 assembly steps
Remix this project

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

Interactive · read-only
Wiring diagram for ESP32 Quadcopter Drone Controller

Pan and zoom to explore the wiring. Remix the project to edit it in your own workspace.

Parts list

Bill of materials
ComponentQtyNotes
LiPo 3.7V 1000mAh Battery1Single-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.
Boost Converter1Small 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.
Brushed DC Motor — Rear Right (M1, CW, Prop A)1Motor 1, rear-right, clockwise, prop type A. Blue & Red wires.
Brushed DC Motor — Front Right (M2, CCW, Prop B)1Motor 2, front-right, counter-clockwise, prop type B. Black & White wires.
Brushed DC Motor — Front Left (M3, CW, Prop A)1Motor 3, front-left, clockwise, prop type A. Blue & Red wires.
Brushed DC Motor — Rear Left (M0, CCW, Prop B)1Motor 0, rear-left, counter-clockwise, prop type B. Black & White wires.
MPU-6500 IMU (SPI)16-axis IMU (3-axis gyro + 3-axis accel) used by Flix. Wired in SPI mode: SCL=SCK, SDA=MOSI, SAO=MISO, NCS=CS.
RC Receiver (SBUS/UART)1RC receiver for drone control. Signal TX connected to ESP32 GPIO4. VIN powered from 3.3V or VCC depending on receiver model.
Gate Pull-down Resistor 100Ω100 Ω1100Ω pull-down resistor from MOSFET gate to GND — keeps gate LOW when GPIO is floating, preventing accidental motor turn-on at boot
UMW 100N03A N-Channel MOSFET1Logic-level N-ch MOSFET for Motor 0 low-side switching
1N5822 Schottky Flyback Diode1Flyback diode for Motor 0
UMW 100N03A N-Channel MOSFET1Logic-level N-ch MOSFET for Motor 1 low-side switching
UMW 100N03A N-Channel MOSFET1Logic-level N-ch MOSFET for Motor 2 low-side switching
UMW 100N03A N-Channel MOSFET1Logic-level N-ch MOSFET for Motor 3 low-side switching
Gate Pull-down Resistor 100Ω100 Ω1100Ω pull-down resistor from MOSFET gate to GND — keeps gate LOW when GPIO is floating, preventing accidental motor turn-on at boot
Gate Pull-down Resistor 100Ω100 Ω1100Ω pull-down resistor from MOSFET gate to GND — keeps gate LOW when GPIO is floating, preventing accidental motor turn-on at boot
Gate Pull-down Resistor 100Ω100 Ω1100Ω pull-down resistor from MOSFET gate to GND — keeps gate LOW when GPIO is floating, preventing accidental motor turn-on at boot
1N5822 Schottky Flyback Diode1Flyback diode for Motor 1
1N5822 Schottky Flyback Diode1Flyback diode for Motor 2
1N5822 Schottky Flyback Diode1Flyback diode for Motor 3

Assembly

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.

    • Tip: 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.

    • Tip: 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).

    • Tip: 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.

    • Tip: 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.

Firmware

ESP32
main.cppDeploy to device
/*
 * 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
}

“Deploy to device” opens this project in Schematik, where you can flash it to your board over USB.

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