STM32F4 Quadcopter flight controller

Keywords: C, FreeRTOS, STM32F4, flight controller, quadcopter, sensor fushion, PID

As an on and off project I’ve been working on a custom flight controller firmware. It’s based on the STM32F4 ARM Cortex-M4 using FreeRTOS, 9-DOF IMU for stability around the pitch, roll and yaw axis aswell as three different barometers and a downwards-pointing laser for reliable altitude hold functionality. It can be controlled either from an RC receiver/transmitter system, or from a custom Android joystick app via an ESP8266 over WiFi. A very simple PCB was designed in KiCad.

I used STM32CubeMX to set up the peripherals and HAL libraries, and IAR Embedded Workbench for development and debugging of the code. In the below clip the basic functionality is demonstrated, and a new video of the full project is to be expected shortly!

The app was designed using Blynk and the circuit board was ordered from OSHPark.


Used in the project is:

  • STM32F4 ARM Cortex-M4 running at 168 MHz
  • EM7180 sensor fushion module with a 9-DOF MPU-9050 (gyrometer, accelerometer, magnetometer) and a BMP280 (barometer)
  • BMP280 barometer
  • BMP180 barometer
  • VL53L0X laser for ground distance estimation
  • drone_flowchart

    The EM7180 outputs raw sensor data aswell as quaternions at 200 Hz. A task converts the quaternions into euler angles to feed a PID loop roll, pitch and yaw angles at 200 Hz. Another task calculats the altitude (cm) using a lowpass filtered average from the three barometers aswell as data from the accelerometer and laser. Depending on the altitude, either laser- (low altitude), or barometer readings (high altitude) is being fused together with the accelerometer data using a complementary filter. The PID loop corresponding to altitude is running at 50 Hz due to the slower barometer and laser update rates. The PID-loop is given the filtered estimate of the current location from the sensors, the velocity, and the desired current location read from the RC receiver or from the Android app. It outputs a float number between -1.0f and 1.0f which will contribute to the final PWM signal consisted of:

  • hover – a hard coded value between 0.0f and 1.0f which corresponds to when the drone has enough thrust to hover.
  • alt – a PID controlled value between -1.0f and 1.0f for altitude hold functionality.
  • pitch – a PID controlled value between -1.0f and 1.0f.
  • roll – a PID controlled value between -1.0f and 1.0f.
  • yaw – a PID controlled value between -1.0f and 1.0f.
  • // Calculate PID values
    float pitch     = PID_Calc(&pitchPid);
    float roll      = PID_Calc(&rollPid);
    float yaw       = PID_Calc(&yawPid);
    float alt       = PID_Calc(&altitudePid);
    // Calculate the motor values
    // 1  front  4
    // left  right
    // 2  back   3
    esc1 = hover + alt + roll + pitch + yaw;
    esc2 = hover + alt + roll - pitch - yaw;
    esc3 = hover + alt - roll - pitch + yaw;
    esc4 = hover + alt - roll + pitch - yaw;
    // Feed motors only if armed
        ESC_SetSpeed(TIM_CHANNEL_1, esc1);
        ESC_SetSpeed(TIM_CHANNEL_2, esc2);
        ESC_SetSpeed(TIM_CHANNEL_3, esc3);
        ESC_SetSpeed(TIM_CHANNEL_4, esc4);

    The sum of the PID outputs together with the hard coded hover variable will add up to a float between -1.0f to 1.0f which is converted into a PWM signal of 1-2 ms pulses.

    The full firmware can be found on GitHub

    Write a Comment