Developer Guide

This section provides guidance for developers working with ArduPilot.

Getting Started

Building ArduPilot

Prerequisites:

  • GCC/Clang compiler for ARM

  • CMake or Waf build system

  • MAVLink headers

Basic Build:

# Configure for a specific vehicle
./waf configure --board CUAV_NANO
./waf copter

# Or for Plane
./waf plane

# Or for Rover
./waf rover

Common Build Targets:

  • copter - Multicopter firmware

  • plane - Fixed-wing firmware

  • rover - Rover firmware

  • sub - Submarine firmware

  • AntennaTracker - Antenna tracker

  • Tools - Tools and utilities

Code Structure

Directory Layout

ardupilot/
├── libraries/
│   ├── AP_HAL/          # Hardware Abstraction Layer
│   ├── AC_AttitudeControl/  # Attitude control
│   ├── AC_PID/         # PID controllers
│   ├── AC_WPNav/       # Waypoint navigation
│   ├── AP_AHRS/        # AHRS
│   ├── AP_GPS/         # GPS driver
│   ├── AP_Baro/        # Barometer
│   ├── AP_Compass/     # Compass
│   ├── AP_BattMonitor/ # Battery monitor
│   └── ...
├── ArduCopter/   # Copter firmware
├── ArduPlane/    # Plane firmware
└── ArduRover/    # Rover firmware

Key Concepts

Singleton Pattern

Most ArduPilot classes use the singleton pattern:

// Get singleton
AP_GPS *gps = AP_GPS::get_singleton();

// Check availability
if (gps == nullptr) {
    // Not available on this platform
}

// Use
gps->update();
Location loc = gps->location();

Update Loop

Sensors and controllers must be updated in the main loop:

void loop() {
    // 100Hz tasks
    ahrs.update();
    motors->output();

    // 50Hz tasks
    gps->update();
    baro->update();

    // 10Hz tasks
    gcs()->send_message(HEARTBEAT);

    // Wait for next loop
    scheduler->delay(1);
}

HAL (Hardware Abstraction Layer)

Access hardware through HAL:

// UART/Serial
AP_HAL::UARTDriver *uart = hal.uartD[0];  // Serial0
uart->printf("Hello\n");

// GPIO
hal.gpio->pinMode(13, HAL_GPIO_OUTPUT);
hal.gpio->digitalWrite(13, HAL_GPIO_HIGH);

// ADC
uint16_t adc_value = hal.adc->ch(0);

// Timer
hal.scheduler->register_timer_process(callback);

Parameter System

Define parameters in your class:

// In header file
class MyClass {
public:
    static const struct AP_Param::GroupInfo var_info[];

private:
    AP_Float _my_param {"my_param", 0.5f};
    AP_Int8 _my_mode {"my_mode", 0};
};

// In source file
const struct AP_Param::GroupInfo MyClass::var_info[] = {
    {"my_param", 0, var_info::PARAM_FLOAT, _my_param, 0, 0, 0, 1.0},
    {"my_mode", 0, var_info::PARAM_INT8, _my_mode, 0, 0, 0, 1},
    var_info::END
};

Logging

Log data using DataFlash:

#include <DataFlash.h>

// Define log structure
struct PACKED log_MyStruct {
    LOG_PACKET_HEADER;
    uint64_t timestamp;
    float data;
};

// Write log
DataFlash::instance()->Log_Write_MyStruct(timestamp, data);

// Log format (in GetMessageInfo)
{ "MYS", "QF", "TimeUS,Data", "TimeUS,Data" }

Debugging

Serial Debug

// Simple debug
Serial.printf("Value: %f\n", value);

// Conditional debug
#if DEBUG_LEVEL > 0
    debug("Function: value=%f", value);
#endif

Common Patterns

Flight Mode Implementation

class ModeMyMode : public Mode {
public:
    ModeMyMode() : Mode("MYMODE") {}

    void init() override {
        // Called when mode starts
    }

    void run() override {
        // Called every loop in this mode
        // Control motors here
    }

    bool requires_arming() override { return true; }
};

Sensor Driver

class MySensor_Backend : public AP_Sensor_Backend {
public:
    MySensor_Backend(AP_Sensor &frontend, uint8_t instance);

    void update() override {
        // Read sensor
        // Publish data
        frontend.set_status(OK);
        frontend.set_data(distance);
    }
};

Testing

SITL (Software In The Loop)

# Run Copter SITL
cd ArduCopter
../Tools/autotest/sim_vehicle.py --map --console

# Run Plane SITL
cd ArduPlane
../Tools/autotest/sim_vehicle.py --map --console

SITL Commands

# Arm/Disarm
mode guided
arm throttle
disarm

# Takeoff
takeoff 10

# Fly to location
wp set 123456789 987654321 20

# Land
mode land

Useful Resources