Flight Code

In this section

This section aims to outline the architecture of the flight code (the code that runs on flight controllers).

On this particular page, we will first take a look at the directory structure so you'll know where to find what you're looking for. Then we'll look at how LibrePilot starts when the flight controller is powered on and which files are involved.

In this page

Directory Structure

The flight code lives inside the flight/ directory. Here is the structure of this directory:

Directory structure of flight/

To get started with development and understand the basic structure there are only a few directories you'll likely need to know about (don't worry, details will follow):

modules/Contains modules that comprise all the features of the flight code
pios/Contains PiOS, a hardware abstraction layer (HAL)
targets/Contains firmware and bootloader code for supported target boards.
Also, contains configuration options for each target (more details later).

Boot Process


The following section is designed to give an overview of the files needed to boot LibrePilot. Unless you absolutely know what you're doing, you should not modify those files. If you do and flash the modified code onto your flight controller, you risk rendering it unusable, especially when modifying the bootloader. The files are simply mentioned to help you understand the boot process.

Sometimes the best point for starting to learn about how a program works is the start of the program.

So, one of the first questions someone with any C/C++ experience is likely to have when they look at all those files and directories is: "Where is main()?"

The answer lies in the targets/ directory. Each processor (or, more precisely, each target board) has its own firmware and bootloader directories. So actually, there are two main functions, one for the bootloader and one for the firmware.

The Bootloader

When a flight controller with LibrePilot loaded is plugged in, the first code that will run is the bootloader. That is because the linker is configured to place the bootloader code right at the start of the processor's address space for code.

In the most basic terms, it is the bootloader's job to:

  1. Check if the user requested a device firmware update (DFU) over USB (e.g. via the "Rescue"-button in the Firmware tab inside GCS)
  2. Download the firmware update via USB (if requested)
  3. Start the firmware

If you want to take a look under the hood, choose any board inside targets/ and open bootloader/main.c (for example flight/targets/revolution/bootloader/main.c).

Again, notice that the bootloader is the first code that runs when power is applied to the controller. If the processor would get stuck inside the bootloader, there would be no way to apply firmware and even bootloader updates over USB. That is why modifying this code is a bad idea unless you absolutely know what you're doing.

The Firmware

As soon as the bootloader is done, it will make the processor jump to the firmware code. At the beginning of the firmware code is the main function, which resides in flight/targets/<board name>/firmware/<board name>.cpp.

The firmware main function calls some low level initialization functions using PiOS (the hardware abstraction layer). Then, it will start the system module, which in turn starts all the other modules.