Developer Guide

Introduction

The A.R.Drone SDK provides the tools and resources needed to control an A.R.Drone from native applications running on all kinds of hardware platforms (PC, iPhone, iPad, Android and any host device supporting Wifi networks in ad-hoc mode).

It provides reference functionalities that you can customize and extend, plus code examples with their associated projects which allow the users to create a fully functional application in a matter of minutes.

What you can do with the SDK

The functions provided in the SDK allow an user application to very easily control the drone movements, retrieve navigation data like sensors measurements and video streams, and configure the drone behaviour.

Getting started

If you have not done so yet, you can download the SDK package here .

You can then directly compile the provided examples or try to customize them.

The core application

Every ARDrone application built using the ARDrone SDK shares the same core architecture. The ARDrone SDK provides the key objects needed to run the application and to coordinate the handling of user input, display the camera video and handling the tags detection. Where applications deviate from one another is in how they configure these default objects and also where they incorporate custom objects to enhance their application’s user interface and behavior.

When you create your own application you should re-use the high level APIs of the SDK. As each application depends on the platform and device used, it is important to understand when it needs customization and when the high level APIs are sufficient. This chapter provides an overview of the core application architecture and the high-level APIs customization points.

Core Application Architecture

Once connected to the AR.Drone, you can re-use the ARDrone SDK to initiate communication with the AR.Drone. Your application should manage the various devices (wifi connection, HMI). The AR.Drone SDK provides tools and methods to send commands, receive information from the AR.Drone and manage the video stream. To manage devices you need to understand the cycles of an AR.Drone application. The following sections describe these cycles and also provide a summary of some of the key design patterns used throughout the development of an ARDrone application.

The Application Life Cycle

The application life cycle is the minimum sequence of events that take place between the launch and termination of your AR.Drone application. You can compile the SDK with or without the main entry point provided with the SDK but your application must comply with the reference AR.Drone application life cycle to work correctly. The application is launched by calling it's main function. At this point, the application life cycle can be started. It initializes different user interfaces and calls custom SDK initialization objects from your application. During the event loop, the AR.Drone SDK calls the registered delegate objects. When the user performs an action that would cause your application to quit, the AR.Drone SDK notifies your application and begins the termination process.

Figure 1 Application life cycle

The Main Function

The main function of an ARDrone application
#include <ardrone_api.h>
#include <ardrone_tool/ardrone_tool.h>

C_RESULT res;

res = ardrone_tool_setup_com( NULL );

if( FAILED(res) )
{
   PRINT("Wifi initialization failed. It means either:\n");
   PRINT("\t* you're not root (you can set up wifi connection only as root)\n");
   PRINT("\t* wifi device is not present (on your pc or on your card)\n");
   PRINT("\t* you set the wrong name for wifi interface (for example rausb0 instead of wlan0) \n");
   PRINT("\t* ap is not up (reboot card or remove wifi usb dongle)\n");
   PRINT("\t* wifi device has no antenna\n");
}
else
{
   res = ardrone_tool_init(argc, argv);

   while( SUCCEED(res) && ardrone_tool_exit() == FALSE )
   {
      res = ardrone_tool_update();
   }

   res = ardrone_tool_shutdown();
}

Minimum requirement to compile your AR.Drone application

To compile your application with the SDK you must integrate some MACRO.

Listing 1.1 Threads table
#include <VP_Api/vp_api_thread_helper.h>

BEGIN_THREAD_TABLE
END_THREAD_TABLE
Listing 1.2 Navdata table
#include <ardrone_tool/Navdata/ardrone_navdata_client.h>

BEGIN_NAVDATA_HANDLER_TABLE
END_NAVDATA_HANDLER_TABLE

Implementing Custom TOOL Schemes

You can register TOOL types for your application that include custom TOOL schemes. A custom TOOL scheme is a mechanism through which you customize specific code for your application and handle registering objects for the SDK.

Registering Custom TOOL Schemes

To register a TOOL type for your application you specify the custom tool methods declared in ardrone_tool.h file which is introduced in "The application life cycle". The following list identifies some methods that you might want to implement in your application.
  • ardrone_tool_init_custom
  • ardrone_tool_update_custom
  • ardrone_tool_display_custom
  • ardrone_tool_shutdown_custom
  • ardrone_tool_exit
Methods for parsing command line
  • ardrone_tool_check_argc_custom
  • ardrone_tool_display_cmd_line_custom
  • ardrone_tool_parse_cmd_line_custom

Details about these methods are explained in more detail later in this document.

Managing threads

You must declare a threads table in vp_api_thread_helper.h file. You can add threads to your application and default threads implemented in the SDK. The listing of the default threads will be described later in this document.

Listing 1.3 Implementing threads table
#include <VP_Api/vp_api_thread_helper.h>
PROTO_THREAD_ROUTINE(your_thread, private_data);

DEFINE_THREAD_ROUTINE(your_thread, private_data)
{
   //your code
}

BEGIN_THREAD_TABLE
  THREAD_TABLE_ENTRY( your_thread, 20 )
  THREAD_TABLE_ENTRY( navdata_update, 20 )
END_THREAD_TABLE

To run and stop the threads, you declare MACRO in vp_api_thread_helper.h file. Use START_THREAD macro to run the thread and JOIN_THREAD macro to stop the thread. START_THREAD must be called in custom implemented method named ardrone_tool_init_custom which was introduced in "Implementing custom tool schemes". JOIN_THREAD is called in custom implemented method named ardrone_tool_shutdown_custom which was introduced in "Implementing custom tool schemes".

Listing 1.4 Managing threads
C_RESULT ardrone_tool_init_custom(int argc, char **argv)
{
  START_THREAD(your_thread, &private_data);

  return C_OK;
}

C_RESULT ardrone_tool_shutdown_custom()
{
  JOIN_THREAD( your_thread );
}                  

Activating default threads by adding in the threads table. The delegate object handles the default threads.

Handling input events

A delegate object handles your input device by implementing init, update and close methods in your application. The delegate object calls the init method when you register a new device in the SDK. During the event loop, the delegate object calls the update method. The delegate object calls shutdown method when the application exit or your application can directly call the ardrone_tool_input_remove method (declared in ardrone_tool_input.h file) to remove your device from the SDK.

This methods are embedded into input_device_t structure declared in ardrone_input.h file :
typedef struct _input_device_t {
  char name[MAX_NAME_LENGTH];

  C_RESULT (*init)(void);
  C_RESULT (*update)(void);
  C_RESULT (*shutdown)(void);
} input_device_t;

A method named ardrone_tool_input_add gives you a means to registering methods by passing the input_device object. Your application must add The method ardrone_tool_input_add in a custom method named ardrone_tool_init_custom which was introduced in "Implementing custom tool schemes". Adding the method ardrone_tool_input_add for each input device.

Listing 1.3 Implementing init, update and close methods

The Following code showing implementing methods for game pad controller.
Implementing update methods by using input methods defined in ardrone_input.h file. Methods are described in section "Methods for controlling the AR.Drone"

#include <ardrone_api.h>
#include <VP_Os/vp_os_types.h>
#include <ardrone_tool/UI/ardrone_input.h>

input_device_t gamepad = {
  "Gamepad",
  open_gamepad,
  update_gamepad,
  close_gamepad
};

C_RESULT open_gamepad(void)
{
  C_RESULT res = C_FAIL;

  FILE* f = fopen("/proc/bus/input/devices", "r");

  if( f != NULL )
  {
    res = parse_proc_input_devices( f, GAMEPAD_LOGICTECH_ID);

    fclose( f );

    if( SUCCEED( res ) && strcmp(gamepad.name, "Gamepad")!=0)
    {
         char dev_path[20]="/dev/input/";
         strcat(dev_path, gamepad.name);
            joy_dev = open(dev_path, O_NONBLOCK | O_RDONLY);
    }
      else
      {
         return C_FAIL;
      }
  }

  return res;
}

C_RESULT update_gamepad(void)
{
  static int32_t x = 0, y = 0;
  static bool_t refresh_values = FALSE;
  ssize_t res;
  static struct js_event js_e_buffer[64];
  static int32_t start = 0;
  input_state_t* input_state;

  static int32_t theta_trim = 0;
  static int32_t phi_trim   = 0;
  static int32_t yaw_trim   = 0;

  res = read(joy_dev, js_e_buffer, sizeof(struct js_event) * 64);

  if( !res || (res < 0 && errno == EAGAIN) )
    return C_OK;

  if( res < 0 )
    return C_FAIL;

  if (res < (int) sizeof(struct js_event))// If non-complete bloc: ignored
    return C_OK;

  // Buffer décomposition in blocs (if the last is incomplete, it's ignored)
  int32_t idx = 0;
  refresh_values = FALSE;
  input_state = ardrone_tool_get_input_state();
  for (idx = 0; idx < res / sizeof(struct js_event); idx++)
  {
    if(js_e_buffer[idx].type & JS_EVENT_INIT )// If Init, the first values are ignored
    {
      break;
    }
    else if(js_e_buffer[idx].type & JS_EVENT_BUTTON )// Event Button detected
    {
      switch( js_e_buffer[idx].number )
      {
        case PAD_AG :
        ardrone_tool_set_ui_pad_ag(js_e_buffer[idx].value);
        break;
        case PAD_AB :
        ardrone_tool_set_ui_pad_ab(js_e_buffer[idx].value);
        break;
        case PAD_AD :
        ardrone_tool_set_ui_pad_ad(js_e_buffer[idx].value);
        break;
        case PAD_AH :
        ardrone_tool_set_ui_pad_ah(js_e_buffer[idx].value);
        break;
        case PAD_L1 :
        if( js_e_buffer[idx].value )
        {
                   ardrone_tool_set_ui_pad_l1(1);
        }
                else
                {
                   ardrone_tool_set_ui_pad_l1(0);
                }
        break;
        case PAD_R1 :
        if( js_e_buffer[idx].value )
        {
                   ardrone_tool_set_ui_pad_r1(1);
        }
                else
                {
                   ardrone_tool_set_ui_pad_r1(0);
                }
        break;
        case PAD_L2 :
        break;
        case PAD_R2 :
        break;
        case PAD_SELECT :
        ardrone_tool_set_ui_pad_select(js_e_buffer[idx].value);
        break;
        case PAD_START :
      if( js_e_buffer[idx].value )
      {
         start ^= 1;
     ardrone_tool_set_ui_pad_start( start );
      }
      break;
        default:
       break;
      }
    }
    else if(js_e_buffer[idx].type & JS_EVENT_AXIS )// Event Axis detected
    {
      refresh_values = TRUE;
      switch( js_e_buffer[idx].number )
      {
        case PAD_X:
          x = ( js_e_buffer[idx].value + 1 ) >> 15;
          break;
        case PAD_Y:
          y = ( js_e_buffer[idx].value + 1 ) >> 15;
          break;
        default:
          break;
      }
    }
    else
    {// TODO: default: ERROR (non-supported)
    }
  }

  if(refresh_values)// Axis values to refresh
    {
       ardrone_tool_set_ui_pad_xy( x, y );
    }
  return C_OK;
}

C_RESULT close_gamepad(void)
{
  close( joy_dev );

  return C_OK;
}

Registering a game controller device

To register a new device for your application, you must call ardrone_tool_input_add method. Call ardrone_tool_input_remove method to unregister device. To add a new device call ardrone_tool_input_add method in your custom implemented method named ardrone_tool_init_custom (see "Implementing custom tool schemes"). To remove your device call ardrone_tool_input_remove method in your custom implemented method named ardrone_tool_shutdown_custom (see "Implementing custom tool schemes").

Listing 1.4 Registering a game controller device
C_RESULT ardrone_tool_init_custom(int argc, char **argv)
{
  // Add inputs
  ardrone_tool_input_add( &gamepad );

  return C_OK;
}

C_RESULT ardrone_tool_shutdown_custom()
{
  ardrone_tool_input_remove( &gamepad );

  return C_OK;
}

Methods for controlling the AR.Drone

The following list identifies some methods for basic controlling commands of the AR.Drone.

Using progressive commands

The ardrone_at_set_progress_cmd method enables you to pilot the AR.Drone with better control. It replaces pitch,roll,gaz and yaw commands which was introduced in the precedent section game-pad emulation. For example, you can use progressive commands to send the accelerometer values or to send radiogp commands.

Handling control commands

A mechanism is provided to handle control commands. That implements, updating software and retrieving settings of the AR.Drone. You can register to the mechanism by implementing "start" and "end" methods. The delegate object calls the "start" method before running the event process and "end" method after that the event process was terminated, in which you know if the process is success or not. The methods are passed into ardrone_control_soft_update_event_t structure. Calling the method ardrone_control_send_event sends the event command.

Objects are declared in <ardrone_tool/Control/ardrone_control.h> file.

The code sends a command to update software.
#include <ardrone_tool/Control/ardrone_control.h>

static void on_update_event_end( ardrone_control_event_t* event )
{
  if( event->status == ARDRONE_CONTROL_EVENT_FINISH_SUCCESS )
  {
    PRINT("Update completed with success\n");
  }
  else
  {
    PRINT("Update failure\n");
  }

  updater_exit = TRUE;
}

{
   static ardrone_control_soft_update_event_t update_event;

   update_event.event        = ARDRONE_UPDATE_CONTROL_MODE;   

   //This value must be large enough to update the software. 
   update_event.num_retries  = 50;     

   update_event.status       = ARDRONE_CONTROL_EVENT_WAITING;
   update_event.ardrone_control_event_start  = NULL; //Not implemented
   update_event.ardrone_control_event_end    = on_update_event_end;
   update_event.ack_received = 0;      
   update_event.sendsize     = 0;
   ardrone_control_send_event( (ardrone_control_event_t*) &update_event );
}

Displaying the video stream

The video stream data are handled using a pipeline. This SDK provides some stages that you can sequentially connect.
The life cycle of a pipeline must realize a minimum sequence.

Listing 1.5 Processing of a pipeline
    #include <VP_Api/vp_api.h>

    res = vp_api_open(&pipeline, &pipeline_handle);

    if( SUCCEED(res) )
    {
      int loop = SUCCESS;
      out.status = VP_API_STATUS_PROCESSING;

      while( !ardrone_tool_exit() && (loop == SUCCESS) )
      {
        if( image_vision_window_view == WINDOW_VISIBLE ) {
          if( SUCCEED(vp_api_run(&pipeline, &out)) ) {
            if( (out.status == VP_API_STATUS_PROCESSING || out.status == VP_API_STATUS_STILL_RUNNING) ) {
              loop = SUCCESS;
            }
          }
          else loop = -1; // Finish this thread
        }
      }

      vp_api_close(&pipeline, &pipeline_handle);

Your application needs to call this in a thread introduced in "Managing threads".

Listing 1.6 Handling the video stream data for an iPhone application

Header files in VP_SDK
#include <VP_Api/vp_api.h> 
#include <VP_Api/vp_api_error.h>
#include <VP_Api/vp_api_stage.h>
#include <VP_Api/vp_api_picture.h>
#include <VP_Stages/vp_stages_yuv2rgb.h>

Header files in VLIB
#include <VLIB/Stages/vlib_stage_decode.h>

Header files in ARDroneLib
#include <ardrone_tool/ardrone_tool.h>
#include <ardrone_tool/Com/config_com.h>
#include <ardrone_tool/Video/video_com_stage.h>

include in header file
PROTO_THREAD_ROUTINE(video_stage, data);

Implementing in source code file 
DEFINE_THREAD_ROUTINE(video_stage, data)
{
C_RESULT res;

vp_api_io_pipeline_t pipeline;
vp_api_io_data_t out;
vp_api_io_stage_t stages[NB_STAGES];

vp_api_picture_t picture;

vlib_stage_decoding_config_t vec;
video_com_config_t           icc;

video_stage_started = TRUE;

vp_os_memset(&icc, 0, sizeof( icc ));
vp_os_memset(&vec, 0, sizeof( vec ));

/// Picture configuration
picture.format = PIX_FMT_RGB565;

picture.width = 512;
picture.height = 512;
picture.framerate = 15;

picture.y_buf = vp_os_malloc( picture.width * picture.height );
picture.cr_buf = vp_os_malloc( picture.width * picture.height / 2 );
picture.cb_buf = vp_os_malloc( picture.width * picture.height / 2 );

picture.y_line_size = picture.width * 2;
picture.cb_line_size = 0;
picture.cr_line_size = 0;

icc.com = COM_VIDEO();
icc.buffer_size = 100000;
icc.protocol = VP_COM_UDP;
COM_CONFIG_SOCKET_VIDEO(&icc.socket, VP_COM_CLIENT, VIDEO_PORT, wifi_ardrone_ip);

vec.width = 512;
vec.height = 512;
vec.picture = &picture;
vec.luma_only = FALSE;
vec.block_mode_enable = TRUE;

pipeline.nb_stages = 0;

stages[pipeline.nb_stages].type = VP_API_INPUT_SOCKET;
stages[pipeline.nb_stages].cfg = (void *)&icc;
stages[pipeline.nb_stages].funcs = video_com_funcs;
pipeline.nb_stages++;

stages[pipeline.nb_stages].type = VP_API_FILTER_DECODER;
stages[pipeline.nb_stages].cfg = (void*)&vec;
stages[pipeline.nb_stages].funcs = vlib_decoding_funcs;
pipeline.nb_stages++;

stages[pipeline.nb_stages].type = VP_API_OUTPUT_LCD;
stages[pipeline.nb_stages].cfg = (void*)&vec;
stages[pipeline.nb_stages].funcs = opengl_video_stage_funcs;
pipeline.nb_stages++;

pipeline.stages = &stages[0];

if( !ardrone_tool_exit() )
{
res = vp_api_open(&pipeline, &pipeline_handle);

if( SUCCEED(res) )
{
int loop = SUCCESS;
out.status = VP_API_STATUS_PROCESSING;

while( !ardrone_tool_exit() && (loop == SUCCESS) )
{
if(!video_stage_started)
{
vp_os_mutex_lock(&video_stage_mutex);
icc.num_retries = VIDEO_MAX_RETRIES;
vp_os_cond_wait(&video_stage_condition);
video_stage_started = TRUE;
vp_os_mutex_unlock(&video_stage_mutex);
}

if( SUCCEED(vp_api_run(&pipeline, &out)) ) {
if( (out.status == VP_API_STATUS_PROCESSING || out.status == VP_API_STATUS_STILL_RUNNING) ) {
loop = SUCCESS;
}
}
else loop = -1; // Finish this thread
}

vp_api_close(&pipeline, &pipeline_handle);
}
}

return (THREAD_RET)0;
}

Toggling the video camera

The ardrone_at_zap method will enable you to select the video from either the horizontal or the vertical camera.

The following figure displays the video channels


Displaying of horizontal video camera


Displaying of vertical video camera


Both, "large horizontal" and "small vertical" video camera


Both, "large vertical" and "small horizontal" video camera

Playing a led animation

The ardrone_at_set_led_animation method enables you to play a led animation.

Playing a flight animation

The ardrone_at_set_anim method will enable you to play flight animations with the AR.Drone.

Receiving Navdata Information

To control the AR.Drone you need to retrieve informations containing alert messages. The following Navdata are important to the user.

  • Communication Lost
  • Emergency landing
  • VBat low
  • Motors status
  • Cutout system detection

The following picture displays a control panel showing alert messages.

Navdata gives you informations about settings of the AR.Drone. The following list identifies some parameters.
  • Yaw speed (°/s)
  • Vert speed (°/s)
  • Trim Pitch (°)
  • Trim Yaw (°/s)
  • Tilt (°)

With these settings, you can adjust the control of the AR.Drone to your driving style. The following picture displays a settings panel.

To change the setting parameters of the AR. Drone, you must use the corresponding AT commands located in the file ardrone_api.h file. The following list identifies some methods to configure the AR.Drone.
  • ardrone_at_set_tilt, indicates to the enemy AR.Drone that it was hit.
  • ardrone_at_set_vert_speed
  • ardrone_at_set_manual_trim, to change in manual mode to configure trim parameters.
  • ardrone_at_set_max_angle
  • ardrone_at_set_yaw
    ...

The Navdata are sent periodically (15 times per second). A mechanism is provided to retrieve this data. To handle Navdata you should implement methods (init, process and release) that are embedded in the ardrone_navdata_handler_t structure declared in ardrone_navdata_client.h. A delegate object calls the init method when launching the Navdata client thread. During the event loop, the delegate object calls the process method. The delegate object calls release method when the thread exits. In order to register this method in the client, your application should define a Navdata table which was introduced in "Minimum requirement to compile your AR.Drone application". The following listing code shows an example to handle Navdata.

#include <ardrone_tool/Navdata/ardrone_navdata_client.h>

C_RESULT navdata_init( void* private_data )
{
   //Your code to initialize your local variables.
}

C_RESULT navdata_process( const navdata_unpacked_t* const pnd )
{
  //Retrieves current Navdata unpacked.
}

C_RESULT navdata_release( void )
{
  //Free local variables.
}

//Defines Navdata table in your application
BEGIN_NAVDATA_HANDLER_TABLE
  NAVDATA_HANDLER_TABLE_ENTRY(navdata_init, navdata_process, navdata_release, NULL) //Register your methods to the client
END_NAVDATA_HANDLER_TABLE
The client calls process method with a reference to a structure navdata_unpacked_t declared in ardrone_api.h file. This structure contains other structures. The following list identifies some structures.
  • navdata_demo_t navdata_demo; This structure contains alert messages, settings parameters and informations about single player mode.
  • navdata_vision_detect_t navdata_vision_detect; This structure contains informations about multi-player mode.

Also available in: HTML TXT