Community project

Dual-Controller Mini Drone (copy)

ESP32
Photo of Dual-Controller Mini Drone (copy)
Generated with AI

Võ North

Published October 21, 2026

This guide builds a quadcopter flight controller using an ESP32 microcontroller, four brushless motors, and ESC modules for independent motor control. The system reads orientation data from an MPU-6050 inertial measurement unit and accepts wireless gamepad commands via the Dabble app, enabling stable flight with roll, pitch, yaw, and throttle control.

The guide provides a complete wiring diagram, parts list, and step-by-step assembly instructions for integrating the 7.4V LiPo battery, regulated 5V supply, motor controllers, and sensor breakout. Firmware is included with conservative throttle limits for safe first-flight testing and a motor-mixing algorithm optimized for X-frame quadcopter geometry.

Wiring diagram

Wiring diagram for Dual-Controller Mini Drone (copy)

Gather all the parts

QtyComponent
1

Lithium Ion Battery - 7.4V 1000mAh (2S LiPo)

7.4V (2S) 1000mAh lithium polymer battery pack with a 25C continuous discharge rate, JST-XH charge plug, and Deans discharge connector; sized for R/C vehicles, robotics, and other portable projects needing a compact 2-cell high-current supply.

1

LM2579 5 V Buck Converter Module

A ready-made step-down regulator module that turns the 2S battery voltage into a regulated 5 V supply for both controller boards.

1

470 µF Electrolytic Capacitor, 16 V or higher

470 µF, ≥16 V

A polarized capacitor that smooths battery voltage spikes at the buck-converter input.

1

0.1 µF Ceramic Capacitor, 50 V

0.1 µF

A non-polarized capacitor that removes fast electrical noise from the regulated 5 V supply.

1

DFRobot SEN0142 Fermion MPU-6050 6 DOF Sensor Breakout

DFRobot SEN0142 MPU-6050 breakout with 3-5 V board input, I2C interface, onboard I2C pull-ups, and i2cdevlib Arduino example coverage.

1

2S Brushless ESC 1

An electronic speed controller that powers and controls brushless motor 1 from the 2S battery.

1

2S Brushless ESC 2

An electronic speed controller that powers and controls brushless motor 2 from the 2S battery.

1

2S Brushless ESC 3

An electronic speed controller that powers and controls brushless motor 3 from the 2S battery.

1

2S Brushless ESC 4

An electronic speed controller that powers and controls brushless motor 4 from the 2S battery.

1

7.4 V 2750 KV Brushless Motor 1

A three-wire brushless motor driven by ESC 1.

1

7.4 V 2750 KV Brushless Motor 2

A three-wire brushless motor driven by ESC 2.

1

7.4 V 2750 KV Brushless Motor 3

A three-wire brushless motor driven by ESC 3.

1

7.4 V 2750 KV Brushless Motor 4

A three-wire brushless motor driven by ESC 4.

Assemble it in 6 steps

1. Make the battery power junction

With the battery unplugged, join lipo_2s BAT+ to buck_5v IN+, cap_input_470uf +, and the red V+ lead from each ESC using short, heavy red wire. Join lipo_2s BAT- to buck_5v IN-, cap_input_470uf -, and the black V- lead from each ESC using matching heavy black wire. This is the high-current motor-power junction.

  • Keep the 470 µF capacitor close to the battery/buck junction.
  • Use wire, connectors, and solder joints rated for the combined motor current.
  • Make sure the capacitor’s striped negative lead goes to battery negative — reversing it can cause it to overheat or burst.
  • Never connect the LiPo while soldering or while propellers are fitted.

2. Set the regulated 5 V supply

Before attaching the ESP32, adjust buck_5v OUT+ and OUT- to exactly 5.0 V with a meter. Connect buck_5v OUT+ to the ESP32 VIN/5V pin and cap_5v_ceramic lead 1 (power). Connect buck_5v OUT- to ESP32 GND and cap_5v_ceramic lead 2 (ground).

  • Put the 0.1 µF ceramic capacitor physically close to the ESP32 power pins.
  • Run the controller ground back to the same battery-negative junction as the ESCs.
  • Do not connect 5 V to the ESP32 3V3 pin — that can damage the board.
  • Do not connect the LiPo until the buck output has been checked at 5.0 V.

3. Mount the ESP32 and motion sensor

Mount the ESP32 and mpu6050_1 at the frame’s center of mass, away from the battery and motors. Connect MPU6050 VIN → ESP32 3V3 (power), MPU6050 GND → ESP32 GND (ground), MPU6050 SDA → GPIO21 (data), and MPU6050 SCL → GPIO22 (clock).

  • Align the printed MPU6050 axes with the frame axes.
  • Soft mounting can reduce motor vibration reaching the sensor.
  • Keep these short sensor wires away from the thick battery, ESC, and three-phase motor wires — electrical noise can make readings unstable.

4. Wire the four ESC control leads

Connect esc_1 SIGNAL → ESP32 GPIO12 (motor-1 command), esc_2 SIGNAL → GPIO13 (motor-2 command), esc_3 SIGNAL → GPIO14 (motor-3 command), and esc_4 SIGNAL → GPIO27 (motor-4 command). Connect each ESC SIGNAL_GND → the shared ESP32/battery-negative ground point (ground). Leave every ESC BEC_5V red lead disconnected and cover its exposed metal with heat-shrink tubing.

  • Label every ESC and command wire before closing the frame.
  • Route thin command leads separately from the battery and motor-phase wires.
  • GPIO12 is a startup-sensitive ESP32 pin. If the ESP32 will not start reliably, move only esc_1 SIGNAL to GPIO25 and update the firmware pin constant.
  • Do not connect the ESC red BEC leads to the buck output — power sources connected together can damage equipment.

5. Connect every motor

Connect esc_1 M1/M2/M3 → motor_1 A/B/C, esc_2 M1/M2/M3 → motor_2 A/B/C, esc_3 M1/M2/M3 → motor_3 A/B/C, and esc_4 M1/M2/M3 → motor_4 A/B/C. These three wires are motor phases, so they do not have positive and negative polarity.

  • Keep each motor’s three phase wires together and away from the MPU6050 leads.
  • If a motor spins the wrong way later, unplug the battery and swap any two phase wires for that motor.
  • Remove every propeller before testing because a motor can start unexpectedly.

6. Inspect before applying battery power

With no propellers fitted, confirm that every low-power ground reaches the same battery-negative/common-ground junction. Check capacitor polarity, confirm the buck is exactly 5.0 V, and confirm all four ESC BEC red wires are insulated. Keep the heavy battery/ESC/motor wiring on the outside of the frame and the MPU6050 wiring toward the center.

  • For the first battery connection, use a current-limited supply or smoke stopper if available.
  • The Bluetooth name used by the Dabble app is MiniDrone.
  • A 2S LiPo and four ESCs can supply enough current to melt wires or cause serious injury. Disconnect immediately if anything becomes hot, swells, or smells burnt.

Review all connections

1. Connections between "lipo_2s" and "ESP32"

Functionlipo_2sESP32
powerBAT+ → LM2579 5 V Buck Converter Module IN+EXT
powerBAT+ → 470 µF Electrolytic Capacitor, 16 V or higher +EXT
powerBAT+ → 2S Brushless ESC 1 V+EXT
powerBAT+ → 2S Brushless ESC 2 V+EXT
powerBAT+ → 2S Brushless ESC 3 V+EXT
powerBAT+ → 2S Brushless ESC 4 V+EXT
groundBAT- → LM2579 5 V Buck Converter Module IN-EXT
groundBAT- → 470 µF Electrolytic Capacitor, 16 V or higher -EXT
groundBAT-GND

2. Connections between "buck_5v" and "ESP32"

Functionbuck_5vESP32
powerOUT+VIN
groundOUT-GND

3. Connections between "cap_5v_ceramic" and "ESP32"

Functioncap_5v_ceramicESP32
power1VIN
ground2GND

4. Connections between "mpu6050_1" and "ESP32"

Functionmpu6050_1ESP32
powerVIN3V3
groundGNDGND
i2cSDAGPIO 21
i2cSCLGPIO 22

5. Connections between "esc_1" and "ESP32"

Functionesc_1ESP32
pwmSIGNALGPIO 12
groundSIGNAL_GNDGND
powerM1 → 7.4 V 2750 KV Brushless Motor 1 AEXT
powerM2 → 7.4 V 2750 KV Brushless Motor 1 BEXT
powerM3 → 7.4 V 2750 KV Brushless Motor 1 CEXT
groundV-GND

6. Connections between "esc_2" and "ESP32"

Functionesc_2ESP32
pwmSIGNALGPIO 13
groundSIGNAL_GNDGND
powerM1 → 7.4 V 2750 KV Brushless Motor 2 AEXT
powerM2 → 7.4 V 2750 KV Brushless Motor 2 BEXT
powerM3 → 7.4 V 2750 KV Brushless Motor 2 CEXT
groundV-GND

7. Connections between "esc_3" and "ESP32"

Functionesc_3ESP32
pwmSIGNALGPIO 14
groundSIGNAL_GNDGND
powerM1 → 7.4 V 2750 KV Brushless Motor 3 AEXT
powerM2 → 7.4 V 2750 KV Brushless Motor 3 BEXT
powerM3 → 7.4 V 2750 KV Brushless Motor 3 CEXT
groundV-GND

8. Connections between "esc_4" and "ESP32"

Functionesc_4ESP32
pwmSIGNALGPIO 27
groundSIGNAL_GNDGND
powerM1 → 7.4 V 2750 KV Brushless Motor 4 AEXT
powerM2 → 7.4 V 2750 KV Brushless Motor 4 BEXT
powerM3 → 7.4 V 2750 KV Brushless Motor 4 CEXT
groundV-GND

Deploy the firmware

#define CUSTOM_SETTINGS
#define INCLUDE_GAMEPAD_MODULE
#include <Arduino.h>
#include <Wire.h>
#include <DabbleESP32.h>

constexpr uint8_t ESC1_PIN = 12;
constexpr uint8_t ESC2_PIN = 13;
constexpr uint8_t ESC3_PIN = 14;
constexpr uint8_t ESC4_PIN = 27;
constexpr uint8_t MPU_SDA_PIN = 21;
constexpr uint8_t MPU_SCL_PIN = 22;

constexpr uint32_t ESC_FREQUENCY_HZ = 50;
constexpr uint8_t ESC_RESOLUTION_BITS = 16;
constexpr uint16_t ESC_MIN_US = 1000;
constexpr uint16_t ESC_MAX_US = 1600;  // Conservative first-test limit.
constexpr uint32_t PWM_PERIOD_US = 20000;
constexpr uint8_t MPU6050_ADDRESS = 0x68;
constexpr uint32_t CONTROL_PERIOD_MS = 20;
constexpr int16_t JOYSTICK_DEADBAND = 8;

bool armed = false;
uint16_t throttleUs = ESC_MIN_US;
uint32_t lastControlMs = 0;

uint32_t pulseToDuty(uint16_t pulseUs) {
  const uint32_t maxDuty = (1UL << ESC_RESOLUTION_BITS) - 1UL;
  return (static_cast<uint32_t>(pulseUs) * maxDuty) / PWM_PERIOD_US;
}

void writeEsc(uint8_t pin, uint16_t pulseUs) {
  ledcWrite(pin, pulseToDuty(constrain(pulseUs, ESC_MIN_US, ESC_MAX_US)));
}

void stopMotors() {
  writeEsc(ESC1_PIN, ESC_MIN_US);
  writeEsc(ESC2_PIN, ESC_MIN_US);
  writeEsc(ESC3_PIN, ESC_MIN_US);
  writeEsc(ESC4_PIN, ESC_MIN_US);
}

void setMotorMix(uint16_t collectiveUs, int16_t roll, int16_t pitch, int16_t yaw) {
  // X-frame: ESC1 front-left, ESC2 front-right, ESC3 rear-right, ESC4 rear-left.
  writeEsc(ESC1_PIN, collectiveUs + pitch - roll - yaw);
  writeEsc(ESC2_PIN, collectiveUs + pitch + roll + yaw);
  writeEsc(ESC3_PIN, collectiveUs - pitch + roll - yaw);
  writeEsc(ESC4_PIN, collectiveUs - pitch - roll + yaw);
}

bool wakeMpu6050() {
  Wire.beginTransmission(MPU6050_ADDRESS);
  Wire.write(0x6B);
  Wire.write(0x00);
  return Wire.endTransmission() == 0;
}

void setup() {
  Serial.begin(115200);

  ledcAttach(ESC1_PIN, ESC_FREQUENCY_HZ, ESC_RESOLUTION_BITS);
  ledcAttach(ESC2_PIN, ESC_FREQUENCY_HZ, ESC_RESOLUTION_BITS);
  ledcAttach(ESC3_PIN, ESC_FREQUENCY_HZ, ESC_RESOLUTION_BITS);
  ledcAttach(ESC4_PIN, ESC_FREQUENCY_HZ, ESC_RESOLUTION_BITS);
  stopMotors();

  Wire.begin(MPU_SDA_PIN, MPU_SCL_PIN);
  const bool imuFound = wakeMpu6050();
  Dabble.begin("MiniDrone");
  Serial.println(imuFound ? "MPU6050 ready. Connect Dabble, then press Start to arm." : "MPU6050 not found. Connect Dabble, then press Start to arm.");
}

void loop() {
  Dabble.processInput();
  const uint32_t now = millis();

  // Select is an immediate motor stop. Start only arms when throttle is at minimum.
  if (GamePad.isSelectPressed()) {
    armed = false;
    throttleUs = ESC_MIN_US;
    stopMotors();
  }

  if (GamePad.isStartPressed() && throttleUs <= ESC_MIN_US + 10) {
    armed = true;
  }

  if (now - lastControlMs >= CONTROL_PERIOD_MS) {
    lastControlMs = now;

    // Dabble GamePad: the centre joystick controls roll and pitch.
    const int16_t rollInput = GamePad.getXaxisData();
    const int16_t pitchInput = GamePad.getYaxisData();
    const int16_t yawInput = (GamePad.isLeftPressed() ? -1 : 0) +
                             (GamePad.isRightPressed() ? 1 : 0);

    // Up/Down adjust collective throttle in small steps.
    if (GamePad.isUpPressed()) {
      throttleUs = min<uint16_t>(ESC_MAX_US, throttleUs + 4);
    } else if (GamePad.isDownPressed()) {
      throttleUs = max<uint16_t>(ESC_MIN_US, throttleUs - 4);
    }

    if (armed) {
      const int16_t roll = abs(rollInput) > JOYSTICK_DEADBAND
                               ? map(rollInput, -100, 100, -80, 80)
                               : 0;
      const int16_t pitch = abs(pitchInput) > JOYSTICK_DEADBAND
                                ? map(pitchInput, -100, 100, -80, 80)
                                : 0;
      const int16_t yaw = yawInput * 50;
      setMotorMix(throttleUs, roll, pitch, yaw);
    } else {
      stopMotors();
    }
  }
}

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