Model Configuration

The model configuration system allows code to be reused between different aircraft models. For each model, a unique configuration file is written. ZeroPilot can then be compiled for a specific model selected by a command line option.

Writing a configuration file

To make ZeroPilot compatible with a new aircraft model, a new configuration file must be written to define hardware-specific details.

In the Models folder, create a new folder and name it after the new aircraft model. This model name should be all lowercase with no spaces.

In the newly-created folder, create a file config.hpp. This will be the model-specific configuration file. The template for the configuration file is as follows:

#ifndef ZPSW3_CONFIG_HPP #define ZPSW3_CONFIG_HPP #include "config_foundation.hpp" #include "tim.h" namespace config { /* Motor config */ constexpr Motor_t motors[] = { }; constexpr uint8_t NUM_MOTORS = sizeof(motors)/sizeof(Motor_t); } #endif // ZPSW3_CONFIG_HPP

Start by defining the model’s motors in the motors array. For each motor, create an initializer list that initializes the data fields of the Motor_t struct. An example of this for two motors is provided below.

constexpr Motor_t motors[] = { { //Yaw servo motor .axis = yaw, .isInverted = false, .driverConstructor = constructObject<MotorChannel, PWMChannel, /*timer*/ &htim1, /*timer_channel*/ 0> }, { //Roll BLDC motor .axis = roll, .isInverted = true, .driverConstructor = constructObject<MotorChannel, DSHOTChannel> } };

The first motor data field is axis, which can be set to pitch, yaw, roll, or thrust, and specifies how the motor will be used for control the aircraft.

The second field is isInverted. This specifies whether the “positive” direction of a motor should match or oppose the convention. If a motor spins in the wrong direction when responding to input, this can be set to true to reverse it.

The third field is driverConstructor. This is a pointer to a factory function responsible for initializing the motor driver. It must be assigned using the template constructObject<class BaseClass, class DerivedClass, auto... args>.

The first template argument is the general type of object to be created, which should be a virtual base class that the driver inherits. For motor drivers, the virtual base class is MotorChannel.

The second template argument is the specific subtype of the object. This should be a derived class that inherits the base class. In this example, it refers to the specific type of motor driver that should be initialized. This will depend on the kind of motor, it could be PWMChannel or DSHOTChannel, both of which inherit MotorChannel.

args is an optional template parameter. After the first two arguments, any number of additional values can be supplied. These will be used as arguments for the constructor of the driver (which was specified in the second template argument).

In the example, four template arguments are supplied for the first motor as highlighted below. The last two are timer and timer_channel, which are arguments required by the PWMChannel constructor.

.driverConstructor = constructObject<MotorChannel, PWMChannel, /*timer*/ &htim1, /*timer_channel*/ 0>

Note: This template allows hardware-specific details to be hidden from the rest of the application. The configuration file specifies exact drivers like PWMChannel. The application, however, treats all motors the same way using MotorChannel. It does not need to be concerned with the specific type of the motor.