Porting to a new flight controller board

ArduPilot supports a wide variety of flight controllers with new controllers being added all the time. This page spells out the steps to port ArduPilot to a new board with an emphasis on porting to STM32 based boards (the most common type) using ChibiOS.


Any firmware customization of ArduPilot code must abide by the terms of the GPL3.0+ open source code license. ArduPilot also reminds developers and manufacturers to adhere to the appropriate trademark and copyright laws when developing new autopilots


Consider joining the ArduPilot Discord Chat to speak with other developers about this topic.

Step 1 - getting started

  • determine which microcontroller the new flight controllers uses. If it is a MCU we already support, for example STM32F405, STM32F427, STM32F745, STM32F765, STM32F777, STM32H743, or STM32H757, then the port should be relatively straight forward. If it is another MCU, ping us on the ArduPilot Discord Chat for advice on how to proceed.

  • determine the crystal frequency (normally 8Mhz or 24Mhz). Refer to the schematic or read the writing on the crystal which is normally a small silver square.


The MCU must have at least 1 MB of flash to run the flight controller code. However, processors with lower flash memory can be used to develop DroneCAN peripherals which integrate many of ArduPilot’s peripheral drivers for airspeed sensors, gps, compass, baro, etc. See the ap-peripheral-landing-page section for more information.


Choose your board name carefully! Use 13 characters or less for your board name, otherwise it may be truncated when the board name is sent from the flight controller to a ground station such as Mission Planner.

Step 2 - create a hwdef.dat file for the board

  • make a subdir in libraries/AP_HAL_ChibiOS/hwdef for your board (i.e. “new-board”). This directory name will eventually be used during the build process (i.e. “waf configure –board new-board”) so keep the name relatively short.

  • copy/rename an existing template hwdef.dat that is similar to the CPU for your board into the directory created above. For example, if the board has a STMF40x chip, the MatekF405, SpeedyBeeF405Mini, or MambaF405v2 hwdefs (among others) provide examples from which to start.


The FMUV3 board is commented heavily and contains most of the HAL directives used in hardware definition files. Also, the scripts directory contains pin function assignments for the supported microprocessors for reference.

Step 3 - configure and build a minimal firmware for the board

Follow the Building the code instructions or take a shortcut and read the BUILD.md file which includes doing the following:

  • cd ardupilot (or wherever you have cloned ArduPilot to)

  • ./waf configure --board new-board

  • ./waf copter

If successful the build should produce an .apj file in build/new-board/bin/arducopter.apj

Step 4 - upload an ArduPilot compatible bootloader to the board

Some boards come with a bootloader pre-installed while others rely on the board manufacturer to use dfu to install the firmware to the board. In either case, in order to conveniently load ArduPilot to the board over USB, an ArduPilot compatible bootloader must be uploaded to the board using dfu. “dfu” can be downloaded from here.

The source code for the bootloaders can be found in AP_Bootloader but pre-compiled binaries are available for many boards in the Tools/Bootloaders directory on our firmware server. Please refer to the README.txt to see if one of the existing bootloaders is compatible for the new board.


Please see the section at the end of this document on how to create a bootloader for your board.


Your board must be plugged into USB and in DFU mode. DFU mode is usually entered by shorting two pins together on the board. Please see your board’s documentation for details on how to accomplish this.

Upload the bootloader to the board dfu-util -a 0 --dfuse-address 0x08000000 -D new-board-bootloader.bin -R

Step 5 - upload the minimal firmware onto the board

If using Mission Planner to load the firmware to the board:

  • connect the board to the windows PC with a USB cable

  • go to MP’s Initial Setup >> Install Firmware screen and click on the Load custom firmware and select the .apj file and press OK. If the “Load custom firmware” link it not available go to the Config/Tuning >> Planner page and set the “Layout” to “Advanced”

  • if the MP fails to load the firmware to the board it is possible the “APJ_BOARD_ID” from your hwdef.dat file does not match the .apj firmware file. The board-id in the bootloader is listed in the bootloader’s README.txt file. A temporary work around is to change the APJ_BOARD_ID in the hwdef.dat file to match the bootloader’s. Longer term a bootloader specific to the new board needs to be created so that ground stations can differentiate this board from others and automatically load the correct firmware.


    Any time you make a change to the board definition file, you must clean up the build, and reconfigure WAF before re-compiling:

  • ./waf distclean

  • ./waf configure --board new-board


Windows7/8 users may need to create a .ini file to allow the USB device to be recognised. On Windows10 the board should be recognised automatically.

If using waf to upload (Linux, MacOSX only):

  • connect the board to the PC with a USB cable

  • commands are in BUILD.md but in short, ./waf copter --upload

After uploading, most likely no LEDs on the board will light up but it should be possible to connect to the board from your favourite ground station. An error message should appear on the ground station HUD complaining, “failed to init barometer”.

Step 6 - fill in the hwdef.dat to specify pins used for each peripheral function

  • read the fmuv3 hwdef.dat file (used for The Cube) to understand the full list of hardware configurations that must be specified.

  • start filling in the new board’s hwdef.dat file for each bus (SPI, I2C, UART, CAN, etc). Ideally you can refer to the board’s schematic to determine how pins should be configured but if the schematic is not available a trial-and-error approach may work because on each CPU, there are a limited number of pins that can be used for each peripheral function. See the STM*.py scripts in the AP_HAL_ChibiOS/hwdef/scripts directory as a guide as to what pins can be used for each peripheral function

  • as you enter new values into the hwdef.dat file you can re-compile and upload the firmware to test whether each peripheral function has begun working.


to quickly check if the hwdef.dat file has any errors, run the libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py script on the new hwdef.dat file and look for errors and warnings in the output

Step 7 - bring up the sensors

similar to step 6, add the sensor related configuration to the hwdef.dat file start with the baro first, then IMU, then compass and finally any other sensors the default sensor orientation should also be filled in along with other things

upload and the firmware and test the sensors are working.

Step 8 - enable parameter storage

For boards with storage, the storage method used (either FRAM or Flash) should be specified in the hwdef.dat file.

For an example of how FRAM is enabled, search for “ramtron” in the fmuv3 hwdef.dat file. In short you add a couple of lines like this:

  • # enable RAMTROM parameter storage

  • define HAL_WITH_RAMTRON 1

For boards using Flash, the bootloader load address needs to be selected so that loading the code does not erase the parameters. See the FLASH_RESERVE_START_KB value in skyviper-f412 and skyviper-v2450 as a reference.

It is also possible to use ardupilot on a board with no storage. In this case configuration parameters will have their default values at startup.

The paramter defaults can be defined by creating a defaults.parm file in the same directory as the hwdef.dat file. In the case that you are including another hwdef.dat, you may also consider using @include PATH_TO_OTHER_DEFAULTS_PARM in your new defaults.parm file.

Here is how it was done for the skyviper

Step 9 - Creating a Bootloader

When doing an initial port you may be happy to use a bootloader that was built for another board. That gets you going quickly, but also means the bootloader will not have the right board ID for your board, and may not have the right LED displays.

To create a bootloader that is just right for your board you need to build the a hwdef-bl.dat for your board. That goes in the same directory as your hwdef.dat, and has the same format, but should not include things like I2C, SPI or CAN peripherals. There are lots of examples of hwdef-bl.dat files already in the hwdef directory you can use as examples.

The key things you must have in your hwdef-bl.dat are:

  • You must set FLASH_BOOTLOADER_LOAD_KB to the location in kilobytes where the main code will start. This should be the same as FLASH_RESERVE_START_KB from your main hwdef.dat.

  • you must set FLASH_RESERVE_START_KB to zero (so the bootloader is placed at the start of flash)

  • Your SERIAL_ORDER will control what ports the bootloader will be active on. Just having OTG1 for USB is fine, or you can list some serial UARTs.

To build the bootloader you do the following:

  • ./waf configure --board YourBoard --bootloader

  • ./waf clean

  • ./waf bootloader

Step 10 - Board Testing

A board port submitted to ArduPilot should be tested before submitting to ArduPilot for inclusion in the project.

The test setup should have a receiver and GPS/Compass attached to the board. Load the firmware and test the following:

  • the board boots and connects to a GCS via USB (Mission Planner, QGC, MAVProxy,etc.)

  • the GPS and Compass is recognized.

  • the RC is recognized and RC input follows the TX

  • the pitch and roll reports (ie HUD horizon) follows autopilot movement correctly (ie IMU is oriented properly and working). test each IMU individually using the INSx_USE parameters.

  • the board arms with default arming checks(may need to force arm if GPS is indoors)

  • attach a battery either directly if onboard power sensors or via external power module and make sure voltage is correctly displayed and current indication present

  • attach a test servo to each output after setting the output to a normal function like elevator, and exercise each one with TX while in MANUAL mode to check output functionality. Bdshot capable outputs should be tested with a BLHeli32 esc for passthrough mode communication (non-IOMCU outputs).

  • move the GPS to each UART output after setting all other UART protocols to NONE and the tested UART to GPS to assure the UART is functioning.

  • UARTs with CTS/RTS lines should use a telemetry radio with those connected to be sure they function with BRD_SERx__CTSRTS=1

Next Steps

If you have gotten this far, congratulations you have ported ArduPilot to a new board! Please reach out to the other developers on the ArduPilot Discord Chat to announce your success.

For widely available boards it is very likely we will help you get the board on the official list of supported boards including automatic firmware builds, easy uploading through the ground stations and onto our wiki! In any case, we welcome new ports so please contact us.

In order to add the board to the official build list, get a board ID number reserved by submitting a change PR to this list ,for a new board ID next in the list above 1000.

Then submit a pull request, adding the following to the board’s subfolder in the AP_HAL_ChibiOS/hwdef library folder, and containing:

  • hwdef.dat with correct board id

  • hwdef-bl.dat with correct board id

  • README.md with board pinout, images, and configuration data needed for a wiki page

  • defaults.parm if board specific defaults are needed