Where Is The Main Function in ArduPilot?

Detailing where the main function is in ArduPilot, taking ArduCopter + Linux as an example.

When studying ArduPilot’s source code, the first question most people would ask is: where is ArduPilot’s program entry point?
The short answer is not surprising at all: there is a main function as every user space program does.
However, it’s not straightforward to tell where it is. So let’s find out.


Main as a Macro

In the file libraries/AP_HAL/AP_HAL_Main.h it defines a macro:

    int AP_MAIN(int argc, char* const argv[]); \
    int AP_MAIN(int argc, char* const argv[]) { \
        hal.run(argc, argv, CALLBACKS); \
        return 0; \
    } \

and in the same file you can also find #define AP_MAIN main.

So it turns out that AP_HAL_MAIN_CALLBACKS() is actually the main function we are looking for.
It takes CALLBACKS as an argument and passes it in hal.run(argc, argv, CALLBACKS);.
Then, there are two things we want to understand in the rest of the post:

  1. Where the macro AP_HAL_MAIN_CALLBACKS() is used to create the main function
  2. Where hal.run(argc, argv, CALLBACKS); is implemented

Where The Main Macro is Used

Taking ArduCopter as an example, we can find that it is used in the file ArduCopter/ArduCopter.cpp#L576 to create the main function and also assign the callback as an instance of the class CopterArduCopter/Copter.h#L183:


where the variable copter is declared in

Copter copter;

The Real Main Function

Now let’s focus on hal.run(argc, argv, CALLBACKS); inside the main function.

Here, hal is an instance of AP_HAL declared as follows:

const AP_HAL::HAL& hal = AP_HAL::get_HAL();

This class AP_HAL is hardware dependent — meaning the implementation is up to the platform specific drivers.

In the case of Linux, AP_HAL::get_HAL() is implemented in the Linux’s driver:

const AP_HAL::HAL &AP_HAL::get_HAL()
    return halInstance;

where halInstance is declared nearby:

static HAL_Linux halInstance;

So now we know hal is actually an instance of class HAL_Linux, we can then track where the hal.run(argc, argv, CALLBACKS); call implements:

void HAL_Linux::run(int argc, char* const argv[], Callbacks* callbacks) const
    // ...
    // Parse startup arguments ...
    // ...
    // ...
    // Initialize GPIO, RCIN/OUT, UART, AIN ...
    // ...
    // ...
    while (!_should_exit) {
    // ...

As you can see, this function does all the initialization. Most importantly, it contains the loop that consistently invokes the loop function from the callbacks which corresponds to copter that is passed from the macro AP_HAL_MAIN_CALLBACKS(&copter) as a parameter.

In case you are curious what the loop does in class Copter, it invokes the scheduler’s function:

void Copter::loop()
    G_Dt = scheduler.get_last_loop_time_s();

Was this post helpful?

Leave a Reply

Your email address will not be published.