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
Interactive · read-only
Pan and zoom to explore the wiring. Remix the project to edit it in your own workspace.
Parts list
Bill of materials| Component | Qty | Notes |
|---|---|---|
| LiPo 3.7V 1000mAh Battery | 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. |
| Boost Converter | 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. |
| Brushed DC Motor — Rear Right (M1, CW, Prop A) | 1 | Motor 1, rear-right, clockwise, prop type A. Blue & Red wires. |
| Brushed DC Motor — Front Right (M2, CCW, Prop B) | 1 | Motor 2, front-right, counter-clockwise, prop type B. Black & White wires. |
| Brushed DC Motor — Front Left (M3, CW, Prop A) | 1 | Motor 3, front-left, clockwise, prop type A. Blue & Red wires. |
| Brushed DC Motor — Rear Left (M0, CCW, Prop B) | 1 | Motor 0, rear-left, counter-clockwise, prop type B. Black & White wires. |
| MPU-6500 IMU (SPI) | 1 | 6-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) | 1 | RC 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 Ω | 1 | 100Ω 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 MOSFET | 1 | Logic-level N-ch MOSFET for Motor 0 low-side switching |
| 1N5822 Schottky Flyback Diode | 1 | Flyback diode for Motor 0 |
| UMW 100N03A N-Channel MOSFET | 1 | Logic-level N-ch MOSFET for Motor 1 low-side switching |
| UMW 100N03A N-Channel MOSFET | 1 | Logic-level N-ch MOSFET for Motor 2 low-side switching |
| UMW 100N03A N-Channel MOSFET | 1 | Logic-level N-ch MOSFET for Motor 3 low-side switching |
| Gate Pull-down Resistor 100Ω100 Ω | 1 | 100Ω 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 Ω | 1 | 100Ω 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 Ω | 1 | 100Ω 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 Diode | 1 | Flyback diode for Motor 1 |
| 1N5822 Schottky Flyback Diode | 1 | Flyback diode for Motor 2 |
| 1N5822 Schottky Flyback Diode | 1 | Flyback diode for Motor 3 |
Assembly
10 stepsPrepare 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.
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.
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.
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.
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.
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.
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.
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.
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.
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/*
* 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.