NAV
Objective C Java C

General information

The SDK will help you connect, pilot, receive stream, save and download medias (photo and video), send and play autopilot flight plans and update your drone. You can use it on the Rolling Spider, Cargos, Mambo, Swing, Jumping Sumo, Jumping Sumo Evos, Bebop Drone, Bebop 2, Disco, SkyController and SkyController 2.

FreeFlight3 is using this SDK.

This SDK is mainly written is C, it provides libraries for Unix system, Android and iOS.

It also comes with a drone simulator called Sphinx, which is intended to help you test your application before flying with your actual drone. All information about Sphinx (installation, user manual, application notes) are available HERE.

How to use

Creating a project

To use the sdk, you will need to add the libraries to your project. To get the libraries, you can either download the released ones, or build your own with the sources (see how to build the SDK).

iOS

  1. First, download the binaries: SDK version 3.12.6 iOS libs
  2. Unzip it.
  3. In the Project Navigator in XCode, click on your project. Then click on your Target and finally click on Build Settings.
  4. In Header Search Paths, add a Any iOS Simulator SDK and a Any iOS SDK architecture (both for Debug and Release).
    Then fill these architectures with:

    For any simulator SDK: PATH_TO_THE_UNZIPPED_FOLDER/arsdk-ios_sim/staging/usr/include
    For any iOS SDK: PATH_TO_THE_UNZIPPED_FOLDER/arsdk-ios/staging/usr/include
    alt text

  5. In Library Search Paths, add a Any iOS Simulator SDK and a Any iOS SDK architecture (both for Debug and Release).
    Then fill these architectures with:

    For any simulator SDK: PATH_TO_THE_UNZIPPED_FOLDER/arsdk-ios_sim/staging/usr/lib
    For any iOS SDK: PATH_TO_THE_UNZIPPED_FOLDER/arsdk-ios/staging/usr/lib
    alt text

  6. In Other Linker Flags add
    -larcommands -larcontroller -lardiscoverywithouteacc -larnetwork -larnetworkal -larsal -larstream -larstream2 -larmavlink -ljson -larmedia -larutils -lcurl -lardatatransfer -lmux -lpomp -lcrypto -lssl -lz
    alt text

    Please note that if you want your app to be compatible with the SkyController2, you will need to replace -lardiscoverywithouteacc by -lardiscovery and also include the framework ExternalAccessory. Using this framework will have an impact during your app submission (see here).

  7. You’re all set !

Let’s start coding !

Android

  1. Open your app build.gradle file
  2. Add to the dependencies the following line
    compile 'com.parrot:arsdk:3.12.6' alt text
  3. Load the native libraries (see code on the right)

Load native libraries

// Not needed in C
// Not needed in Objective C
    ARSDK.loadSDKLibs();

You’re all set, let’s start coding !

Use samples

To have a good overview of what you can do with the SDK, you can start by using and browsing the code of the samples we provide.

For that, simply clone the Sample repository:
git clone https://github.com/Parrot-Developers/Samples.git

There is one example for iOS, one for Android and a few for Unix. The mobile samples use the following architecture:
alt mobile_uml

They are standalone, this means that you can clone the sample repo and use them without compiling the SDK. To enable this, they will use the precompiled SDK libraries.

The mobile samples show you how to connect, pilot, take pictures, display video stream if available, and download medias from the drone.

They support the following drones:

And the following remote controllers:

What if you want to only build an app for the Bebop? Simply delete other files than:

As said before, each mobile sample can be used without having to build the SDK: it will use the precompiled libraries. But you can also use the sample with your own compiled SDK.

iOS

Use the precompiled SDK (hosted on Github):
Use the buildWithPrecompiledSDK configuration to use the precompiled libraries (Product->Scheme->Edit Scheme). Please note that the first time you’ll build with the precompiled SDK, it will download the precompiled libraries from Github, this might take a while.

Use your own compiled SDK:
You can build this sample with Alchemy. In your <SDK> execute this command:

./build.sh -p arsdk-ios -t build-sample -j for iOS ./build.sh -p arsdk-ios_sim -t build-sample -j for iOS simulator

If you prefer to build directly from XCode, use the buildWithLocalSDK configuration to use your localy compiled libraries (see go deeper to first compile your own SDK).

Then, in XCode, use the buildWithLocalSDK configuration to use your freshly compiled sdk libraries. (Product->Scheme->Edit Scheme).

Please note that there are two targets in the iOS sample: SDKSample and SDKSampleForSkyController2. The first one is using -lardiscoverywithouteacc in its Other Linker Flags list and does not include the ExternalAccessory framework. However SDKSampleForSkyController2 uses -lardiscovery and includes ExternalAccessory framework.

Android

Use the precompiled SDK (hosted on JCenter):
With Android Studio, open the settings.gradle located in SDKSample/buildWithPrecompiledSDK.

Use your own compiled SDK:
You can build this sample with Alchemy. In your <SDK> execute this command:

./build.sh -p arsdk-android -t build-sample

Otherwise, if you want to use Android Studio build, first execute this command in <SDK>: ./build.sh -p arsdk-android -t build-jni

Then, in Android Studio, open the settings.gradle located in SDKSample/buildWithLocalSDK.

Start coding

Here are the instruction about how to use the SDK to control the Bebop drone.

Discover the drones

First of all, you will need to discover the drones around you. To do that, we will use the libARDiscovery.

Start discovery:

// Not yet available in pure C
#import <libARDiscovery/ARDISCOVERY_BonjourDiscovery.h>

- (void)startDiscovery
{
    [[ARDiscovery sharedInstance] start];
}
private ARDiscoveryService mArdiscoveryService;
private ServiceConnection mArdiscoveryServiceConnection;

private void initDiscoveryService()
{
   // create the service connection
   if (mArdiscoveryServiceConnection == null)
   {
       mArdiscoveryServiceConnection = new ServiceConnection()
       {
           @Override
           public void onServiceConnected(ComponentName name, IBinder service)
           {
               mArdiscoveryService = ((ARDiscoveryService.LocalBinder) service).getService();

               startDiscovery();
           }

           @Override
           public void onServiceDisconnected(ComponentName name)
           {
               mArdiscoveryService = null;
           }
       };
   }

   if (mArdiscoveryService == null)
   {
       // if the discovery service doesn't exists, bind to it
       Intent i = new Intent(getApplicationContext(), ARDiscoveryService.class);
       getApplicationContext().bindService(i, mArdiscoveryServiceConnection, Context.BIND_AUTO_CREATE);
   }
   else
   {
       // if the discovery service already exists, start discovery
       startDiscovery();
   }
}

private void startDiscovery()
{
   if (mArdiscoveryService != null)
   {
       mArdiscoveryService.start();
   }
}

The libARDiscovery will let you know when BLE and Wifi devices have been found on network:

// Not yet available in pure C
- (void)registerReceivers
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(discoveryDidUpdateServices:) name:kARDiscoveryNotificationServicesDevicesListUpdated object:nil];
}

- (void)discoveryDidUpdateServices:(NSNotification *)notification
{
    NSArray *deviceList = [[notification userInfo] objectForKey:kARDiscoveryServicesList];

    // Do what you want with the device list (deviceList is an array of ARService*)
}
private void registerReceivers()
{
    ARDiscoveryServicesDevicesListUpdatedReceiver receiver =
        new ARDiscoveryServicesDevicesListUpdatedReceiver(mDiscoveryDelegate);
    LocalBroadcastManager localBroadcastMgr = LocalBroadcastManager.getInstance(getApplicationContext());
    localBroadcastMgr.registerReceiver(receiver,
        new IntentFilter(ARDiscoveryService.kARDiscoveryServiceNotificationServicesDevicesListUpdated));
}

private final ARDiscoveryServicesDevicesListUpdatedReceiverDelegate mDiscoveryDelegate =
    new ARDiscoveryServicesDevicesListUpdatedReceiverDelegate() {

    @Override
    public void onServicesDevicesListUpdated() {
        if (mArdiscoveryService != null) {
            List<ARDiscoveryDeviceService> deviceList = mArdiscoveryService.getDeviceServicesArray();

            // Do what you want with the device list
        }
    }
};

Once you have the ARService you want to use, transform it into an ARDiscoveryDevice (you will need it at the next step)

// No BLE support in C, so we use the device IP/Port
// product should only be a wifi product (no Rolling Spider)
ARDISCOVERY_Device_t* createDiscoveryDevice(eARDISCOVERY_PRODUCT product, const char *name, const char *ip, int port)
{
    eARDISCOVERY_ERROR errorDiscovery = ARDISCOVERY_OK;
    ARDISCOVERY_Device_t *device = NULL;

    if (ip == NULL || port == 0)
    {
        fprintf(stderr, "Bad parameters");
        return device;
    }
    if (product < ARDISCOVERY_PRODUCT_NSNETSERVICE || product >= ARDISCOVERY_PRODUCT_BLESERVICE)
    {
        fprintf(stderr, "Bad product (not a wifi product)");
        return device;
    }

    device = ARDISCOVERY_Device_New(&errorDiscovery);

    if (errorDiscovery == ARDISCOVERY_OK)
    {
        errorDiscovery = ARDISCOVERY_Device_InitWifi (device, product, name, port);
    }

    if (errorDiscovery != ARDISCOVERY_OK)
    {
        ARDISCOVERY_Device_Delete(&device);
    }

    return device;
}
// this should be called in background
- (ARDISCOVERY_Device_t *)createDiscoveryDeviceWithService:(ARService*)service
{
   ARDISCOVERY_Device_t *device = NULL;
    eARDISCOVERY_ERROR errorDiscovery = ARDISCOVERY_OK;

    device = [service createDevice:&errorDiscovery];

    if (errorDiscovery != ARDISCOVERY_OK)
        NSLog(@"Discovery error :%s", ARDISCOVERY_Error_ToString(errorDiscovery));

    return device;
}
private ARDiscoveryDevice createDiscoveryDevice(@NonNull ARDiscoveryDeviceService service) {
    ARDiscoveryDevice device = null;
    try {
        device = new ARDiscoveryDevice(mContext, service);
    } catch (ARDiscoveryException e) {
        Log.e(TAG, "Exception", e);
    }

    return device;
}

Clean everything:

// Not needed in pure C as we currently don't use ARDiscovery
- (void)unregisterReceivers
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kARDiscoveryNotificationServicesDevicesListUpdated object:nil];
}

- (void)stopDiscovery
{
    [[ARDiscovery sharedInstance] stop];
}
private void unregisterReceivers()
{
    LocalBroadcastManager localBroadcastMgr = LocalBroadcastManager.getInstance(getApplicationContext());

    localBroadcastMgr.unregisterReceiver(mArdiscoveryServicesDevicesListUpdatedReceiver);
}

private void closeServices()
{
    Log.d(TAG, "closeServices ...");

    if (mArdiscoveryService != null)
    {
        new Thread(new Runnable() {
            @Override
            public void run()
            {
                mArdiscoveryService.stop();

                getApplicationContext().unbindService(mArdiscoveryServiceConnection);
                mArdiscoveryService = null;
            }
        }).start();
    }
}

Setup a device controller

The device controller is an object that will make the interface between the drone and you.

After having started the device controller, you should receive all its states and settings through the command received callback.

Create the device controller:

eARCONTROLLER_ERROR error = ARCONTROLLER_OK;
ARCONTROLLER_Device_t *deviceController = ARCONTROLLER_Device_New (discoveryDevice, &error);
ARDISCOVERY_Device_t *discoveryDevice = [self createDiscoveryDeviceWithService:service];
eARCONTROLLER_ERROR error = ARCONTROLLER_OK;
ARCONTROLLER_Device_t *deviceController = ARCONTROLLER_Device_New (discoveryDevice, &error);
ARDiscoveryDevice discoveryDevice = createDiscoveryDevice(mDeviceService);
if (discoveryDevice != null) {
    try
    {
        deviceController = new ARDeviceController (device);
        discoveryDevice.dispose();
    }
    catch (ARControllerException e)
    {
        e.printStackTrace();
    }
}

Listen to the states changes:

error = ARCONTROLLER_Device_AddStateChangedCallback(deviceController, stateChanged, NULL);

// called when the state of the device controller has changed
void stateChanged (eARCONTROLLER_DEVICE_STATE newState, eARCONTROLLER_ERROR error, void *customData)
{
    switch (newState)
    {
        case ARCONTROLLER_DEVICE_STATE_RUNNING:
            break;
        case ARCONTROLLER_DEVICE_STATE_STOPPED:
            break;
        case ARCONTROLLER_DEVICE_STATE_STARTING:
            break;
        case ARCONTROLLER_DEVICE_STATE_STOPPING:
            break;
        default:
            break;
    }
}
error = ARCONTROLLER_Device_AddStateChangedCallback(deviceController, stateChanged, (__bridge void *)(self));

// called when the state of the device controller has changed
void stateChanged (eARCONTROLLER_DEVICE_STATE newState, eARCONTROLLER_ERROR error, void *customData)
{
    // SELF_TYPE is the class name of self
    SELF_TYPE *selfCpy = (__bridge SELF_TYPE *)customData;

    switch (newState)
    {
        case ARCONTROLLER_DEVICE_STATE_RUNNING:
            break;
        case ARCONTROLLER_DEVICE_STATE_STOPPED:
            break;
        case ARCONTROLLER_DEVICE_STATE_STARTING:
            break;
        case ARCONTROLLER_DEVICE_STATE_STOPPING:
            break;
        default:
            break;
    }
}
// your class should implement ARDeviceControllerListener
deviceController.addListener (this);

@Override
// called when the state of the device controller has changed
public void onStateChanged (ARDeviceController deviceController, ARCONTROLLER_DEVICE_STATE_ENUM newState, ARCONTROLLER_ERROR_ENUM error)
{
    switch (newState)
    {
        case ARCONTROLLER_DEVICE_STATE_RUNNING:
            break;
        case ARCONTROLLER_DEVICE_STATE_STOPPED:
            break;
        case ARCONTROLLER_DEVICE_STATE_STARTING:
            break;
        case ARCONTROLLER_DEVICE_STATE_STOPPING:
            break;

        default:
            break;
    }
}

Listen to the commands received from the drone (example of the battery level received)

error = ARCONTROLLER_Device_AddCommandReceivedCallback(deviceController, onCommandReceived, NULL);

// called when a command has been received from the drone
void onCommandReceived (eARCONTROLLER_DICTIONARY_KEY commandKey, ARCONTROLLER_DICTIONARY_ELEMENT_t *elementDictionary, void *customData)
{
    if (elementDictionary != NULL)
    {
        // if the command received is a battery state changed
        if (commandKey == ARCONTROLLER_DICTIONARY_KEY_COMMON_COMMONSTATE_BATTERYSTATECHANGED)
        {
            ARCONTROLLER_DICTIONARY_ARG_t *arg = NULL;
            ARCONTROLLER_DICTIONARY_ELEMENT_t *element = NULL;

            // get the command received in the device controller
            HASH_FIND_STR (elementDictionary, ARCONTROLLER_DICTIONARY_SINGLE_KEY, element);
            if (element != NULL)
            {
                // get the value
                HASH_FIND_STR (element->arguments, ARCONTROLLER_DICTIONARY_KEY_COMMON_COMMONSTATE_BATTERYSTATECHANGED_PERCENT, arg);

                if (arg != NULL)
                {
                    uint8_t batteryLevel = arg->value.U8;
                    // do what you want with the battery level
                }
            }
        }
        // else if (commandKey == THE COMMAND YOU ARE INTERESTED IN)
    }
}
error = ARCONTROLLER_Device_AddCommandReceivedCallback(deviceController, onCommandReceived, (__bridge void *)(self));

// called when a command has been received from the drone
void onCommandReceived (eARCONTROLLER_DICTIONARY_KEY commandKey, ARCONTROLLER_DICTIONARY_ELEMENT_t *elementDictionary, void *customData)
{
    SELF_TYPE *selfCpy = (__bridge SELF_TYPE *)customData;

    if (elementDictionary != NULL)
    {
        // if the command received is a battery state changed
        if (commandKey == ARCONTROLLER_DICTIONARY_KEY_COMMON_COMMONSTATE_BATTERYSTATECHANGED)
        {
            ARCONTROLLER_DICTIONARY_ARG_t *arg = NULL;
            ARCONTROLLER_DICTIONARY_ELEMENT_t *element = NULL;

            // get the command received in the device controller
            HASH_FIND_STR (elementDictionary, ARCONTROLLER_DICTIONARY_SINGLE_KEY, element);
            if (element != NULL)
            {
                // get the value
                HASH_FIND_STR (element->arguments, ARCONTROLLER_DICTIONARY_KEY_COMMON_COMMONSTATE_BATTERYSTATECHANGED_PERCENT, arg);

                if (arg != NULL)
                {
                    uint8_t batteryLevel = arg->value.U8;
                    // do what you want with the battery level
                }
            }
        }
        // else if (commandKey == THE COMMAND YOU ARE INTERESTED IN)
    }
}
// your class should implement ARDeviceControllerListener
deviceController.addListener (this);

@Override
// called when a command has been received from the drone
public void onCommandReceived(ARDeviceController deviceController, ARCONTROLLER_DICTIONARY_KEY_ENUM commandKey, ARControllerDictionary elementDictionary)
{
    if (elementDictionary != null)
    {
        // if the command received is a battery state changed
        if (commandKey == ARCONTROLLER_DICTIONARY_KEY_ENUM.ARCONTROLLER_DICTIONARY_KEY_COMMON_COMMONSTATE_BATTERYSTATECHANGED)
        {
            ARControllerArgumentDictionary<Object> args = elementDictionary.get(ARControllerDictionary.ARCONTROLLER_DICTIONARY_SINGLE_KEY);

            if (args != null)
            {
                Integer batValue = (Integer) args.get(ARFeatureCommon.ARCONTROLLER_DICTIONARY_KEY_COMMON_COMMONSTATE_BATTERYSTATECHANGED_PERCENT);

                // do what you want with the battery level
            }
        }
    }
    else
    {
        Log.e(TAG, "elementDictionary is null");
    }
}

Listen to the video stream received from the drone

error = ARCONTROLLER_Device_SetVideoStreamCallbacks(deviceController, configDecoderCallback, didReceiveFrameCallback, NULL , NULL);

static eARCONTROLLER_ERROR configDecoderCallback (ARCONTROLLER_Stream_Codec_t codec, void *customData)
{
    // configure your decoder
    // return ARCONTROLLER_OK if configuration went well
    // otherwise, return ARCONTROLLER_ERROR. In that case,
    // configDecoderCallback will be called again
}

static eARCONTROLLER_ERROR didReceiveFrameCallback (ARCONTROLLER_Frame_t *frame, void *customData)
{
    // display the frame
    // return ARCONTROLLER_OK if display went well
    // otherwise, return ARCONTROLLER_ERROR. In that case,
    // configDecoderCallback will be called again
}
// if you want the stream to be MP4 compilant (needed if you decode with iOS hardware decoder)
error = ARCONTROLLER_Device_SetVideoStreamMP4Compliant(_deviceController, 1);

error = ARCONTROLLER_Device_SetVideoStreamCallbacks(_deviceController, configDecoderCallback, didReceiveFrameCallback, NULL , (__bridge void *)(self));

static eARCONTROLLER_ERROR configDecoderCallback (ARCONTROLLER_Stream_Codec_t codec, void *customData)
{
    SELF_TYPE *selfCpy = (__bridge SELF_TYPE *)customData;
    // configure your decoder
    // return ARCONTROLLER_OK if configuration went well
    // otherwise, return ARCONTROLLER_ERROR. In that case,
    // configDecoderCallback will be called again
}

static eARCONTROLLER_ERROR didReceiveFrameCallback (ARCONTROLLER_Frame_t *frame, void *customData)
{
    SELF_TYPE *selfCpy = (__bridge SELF_TYPE *)customData;
    // display the frame
    // return ARCONTROLLER_OK if display went well
    // otherwise, return ARCONTROLLER_ERROR. In that case,
    // configDecoderCallback will be called again
}
// your class should implement ARDeviceControllerStreamListener
deviceController.addStreamListener(this);

@Override
public ARCONTROLLER_ERROR_ENUM configureDecoder(ARDeviceController deviceController, final ARControllerCodec codec) {
    // configure your decoder
    // return ARCONTROLLER_ERROR_ENUM.ARCONTROLLER_OK if display went well
    // otherwise, return ARCONTROLLER_ERROR_ENUM.ARCONTROLLER_ERROR. In that case,
    // configDecoderCallback will be called again
}

@Override
public ARCONTROLLER_ERROR_ENUM onFrameReceived(ARDeviceController deviceController, final ARFrame frame) {
    // display the frame
    // return ARCONTROLLER_ERROR_ENUM.ARCONTROLLER_OK if display went well
    // otherwise, return ARCONTROLLER_ERROR_ENUM.ARCONTROLLER_ERROR. In that case,
    // configDecoderCallback will be called again
}

@Override
public void onFrameTimeout(ARDeviceController deviceController) {}

Finally, starts the device controller (after that call, the callback you set in ARCONTROLLER_Device_AddStateChangedCallback should be called).

error = ARCONTROLLER_Device_Start (deviceController);
error = ARCONTROLLER_Device_Start (deviceController);
if (error == ARCONTROLLER_OK)
{
    _deviceController = deviceController;
}
ARCONTROLLER_ERROR_ENUM error = deviceController.start();

Cleanup when done:

// This function will wait until the device controller is stopped
void deleteDeviceController(ARCONTROLLER_Device_t *deviceController)
{
    if (deviceController == NULL)
    {
        return;
    }

    eARCONTROLLER_ERROR error = ARCONTROLLER_OK;

    eARCONTROLLER_DEVICE_STATE state = ARCONTROLLER_Device_GetState(deviceController, &error);
    if ((error == ARCONTROLLER_OK) && (state != ARCONTROLLER_DEVICE_STATE_STOPPED))
    {
        // after that, stateChanged should be called soon
        error = ARCONTROLLER_Device_Stop (deviceController);

        if (error == ARCONTROLLER_OK)
        {
            sem_wait(&someSemaphore);
        }
        else
        {
            fprintf(stderr, "- error:%s", ARCONTROLLER_Error_ToString(error));
        }
    }

    // once the device controller is stopped, we can delete it
    ARCONTROLLER_Device_Delete(&deviceController);
}

// dont forget to create the semaphore and to sem_post it in the case ARCONTROLLER_DEVICE_STATE_STOPPED of the stateChanged function
// DO NOT CALL ARCONTROLLER_Device_Delete FROM THE stateChanged FUNCTION !
- (void)deleteDeviceController
{
    // in background
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        eARCONTROLLER_ERROR error = ARCONTROLLER_OK;

        // if the device controller is not stopped, stop it
        eARCONTROLLER_DEVICE_STATE state = ARCONTROLLER_Device_GetState(_deviceController, &error);
        if ((error == ARCONTROLLER_OK) && (state != ARCONTROLLER_DEVICE_STATE_STOPPED))
        {
            // after that, stateChanged should be called soon
            error = ARCONTROLLER_Device_Stop (_deviceController);

            if (error == ARCONTROLLER_OK)
            {
                dispatch_semaphore_wait(_stateSem, DISPATCH_TIME_FOREVER);
            }
            else
            {
                NSLog(@"- error:%s", ARCONTROLLER_Error_ToString(error));
            }
        }

        // once the device controller is stopped, we can delete it
        if (_deviceController != NULL)
        {
            ARCONTROLLER_Device_Delete(&_deviceController);
        }
    });
}

// dont forget to add dispatch_semaphore_signal(pilotingViewController.stateSem); in the case ARCONTROLLER_DEVICE_STATE_STOPPED of the stateChanged function
ARCONTROLLER_ERROR_ENUM error = deviceController.stop();

// only when the deviceController is stopped
deviceController.dispose();

Taking off

In order to make your drone take off you will need to ensure that its flying status is landed (even if you can send take off commands anyway, it just won’t take of if it not in landed state).
Then, you can send the take off command.
In response, your drone will send you a state change (if it has taken off): State Landed -> State TakingOff -> State Hovering (or Flying).

Take off

eARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE getFlyingState(ARCONTROLLER_Device_t *deviceController)
{
    eARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE flyingState = ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_MAX;
    eARCONTROLLER_ERROR error;
    ARCONTROLLER_DICTIONARY_ELEMENT_t *elementDictionary = ARCONTROLLER_ARDrone3_GetCommandElements(deviceController->aRDrone3, ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED, &error);
    if (error == ARCONTROLLER_OK && elementDictionary != NULL)
    {
        ARCONTROLLER_DICTIONARY_ARG_t *arg = NULL;
        ARCONTROLLER_DICTIONARY_ELEMENT_t *element = NULL;
        HASH_FIND_STR (elementDictionary, ARCONTROLLER_DICTIONARY_SINGLE_KEY, element);
        if (element != NULL)
        {
            // Get the value
            HASH_FIND_STR(element->arguments, ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE, arg);
            if (arg != NULL)
            {
                // Enums are stored as I32
                flyingState = arg->value.I32;
            }
        }
    }
    return flyingState;
}

void takeOff(ARCONTROLLER_Device_t *deviceController)
{
    if (deviceController == NULL)
    {
        return;
    }
    if (getFlyingState(deviceController) == ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_LANDED)
    {
        deviceController->aRDrone3->sendPilotingTakeOff(deviceController->aRDrone3);
    }
}
- (eARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE)getFlyingState {

    eARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE flyingState = ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_MAX;

    eARCONTROLLER_ERROR error;
    // get the current flying state description
    ARCONTROLLER_DICTIONARY_ELEMENT_t *elementDictionary = ARCONTROLLER_ARDrone3_GetCommandElements(_deviceController->aRDrone3, ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED, &error);
    if (error == ARCONTROLLER_OK && elementDictionary != NULL)
    {
        ARCONTROLLER_DICTIONARY_ARG_t *arg = NULL;
        ARCONTROLLER_DICTIONARY_ELEMENT_t *element = NULL;
        HASH_FIND_STR (elementDictionary, ARCONTROLLER_DICTIONARY_SINGLE_KEY, element);
        if (element != NULL)
        {
            // get the value
            HASH_FIND_STR (element->arguments, ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE, arg);

            if (arg != NULL)
            {
                // Enums are I32
                flyingState = arg->value.I32;
            }
        }
    }

    return flyingState;
}

- (void)takeoff
{
    if ([self getFlyingState] == ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_LANDED)
    {
        _deviceController->aRDrone3->sendPilotingTakeOff(_deviceController->aRDrone3);
    }
}
private ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_ENUM getPilotingState()
{
    ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_ENUM flyingState = ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_ENUM.eARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_UNKNOWN_ENUM_VALUE;
    if (deviceController != null)
    {
        try
        {
            ARControllerDictionary dict = deviceController.getCommandElements(ARCONTROLLER_DICTIONARY_KEY_ENUM.ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED);
            if (dict != null)
            {
                ARControllerArgumentDictionary<Object> args = dict.get(ARControllerDictionary.ARCONTROLLER_DICTIONARY_SINGLE_KEY);
                if (args != null)
                {
                    Integer flyingStateInt = (Integer) args.get(ARFeatureARDrone3.ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE);
                    flyingState = ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_ENUM.getFromValue(flyingStateInt);
                }
            }
        }
        catch (ARControllerException e)
        {
            e.printStackTrace();
        }

        return flyingState;
    }
}

private void takeoff()
{
    if (ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_ENUM.ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_LANDED.equals(getPilotingState()))
    {
        ARCONTROLLER_ERROR_ENUM error = deviceController.getFeatureARDrone3().sendPilotingTakeOff();

        if (!error.equals(ARCONTROLLER_ERROR_ENUM.ARCONTROLLER_OK))
        {
            ARSALPrint.e(TAG, "Error while sending take off: " + error);
        }
    }
}

The drone changes its state. Flying state should be ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_TAKINGOFF, then ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_HOVERING or ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_FLYING.

// called when a command has been received from the drone
void onCommandReceived (eARCONTROLLER_DICTIONARY_KEY commandKey, ARCONTROLLER_DICTIONARY_ELEMENT_t *elementDictionary, void *customData)
{
    // if the command received is a flying state changed
    if ((commandKey == ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED) && (elementDictionary != NULL))
    {
        ARCONTROLLER_DICTIONARY_ARG_t *arg = NULL;
        ARCONTROLLER_DICTIONARY_ELEMENT_t *element = NULL;

        // get the command received in the device controller
        HASH_FIND_STR (elementDictionary, ARCONTROLLER_DICTIONARY_SINGLE_KEY, element);
        if (element != NULL)
        {
            // get the value
            HASH_FIND_STR (element->arguments, ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE, arg);

            if (arg != NULL)
            {
                eARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE flyingState = arg->value.I32;
            }
        }
    }
}
// called when a command has been received from the drone
void onCommandReceived (eARCONTROLLER_DICTIONARY_KEY commandKey, ARCONTROLLER_DICTIONARY_ELEMENT_t *elementDictionary, void *customData)
{
    // if the command received is a flying state changed
    if ((commandKey == ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED) && (elementDictionary != NULL))
    {
        ARCONTROLLER_DICTIONARY_ARG_t *arg = NULL;
        ARCONTROLLER_DICTIONARY_ELEMENT_t *element = NULL;

        // get the command received in the device controller
        HASH_FIND_STR (elementDictionary, ARCONTROLLER_DICTIONARY_SINGLE_KEY, element);
        if (element != NULL)
        {
            // get the value
            HASH_FIND_STR (element->arguments, ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE, arg);

            if (arg != NULL)
            {
                eARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE flyingState = arg->value.I32;
            }
        }
    }
}
@Override
public void onCommandReceived(ARDeviceController deviceController, ARCONTROLLER_DICTIONARY_KEY_ENUM commandKey, ARControllerDictionary elementDictionary)
{
    if (elementDictionary != null)
    {
        if (commandKey == ARCONTROLLER_DICTIONARY_KEY_ENUM.ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED)
        {
            ARControllerArgumentDictionary<Object> args = elementDictionary.get(ARControllerDictionary.ARCONTROLLER_DICTIONARY_SINGLE_KEY);
            if (args != null)
            {
                Integer flyingStateInt = (Integer) args.get(ARFeatureARDrone3.ARCONTROLLER_DICTIONARY_KEY_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE);
                ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_ENUM flyingState = ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_ENUM.getFromValue(flyingStateInt);
            }
        }
    }
    else
    {
        Log.e(TAG, "elementDictionary is null");
    }
}

After that, you can start piloting your drone.

Landing

When you’re done flying, you will need to land. This is how you do it: simply send the landing command.

void land(ARCONTROLLER_Device_t *deviceController)
{
    if (deviceController == NULL)
    {
        return;
    }
    eARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE flyingState = getFlyingState(deviceController);
    if (flyingState == ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_FLYING || flyingState == ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_HOVERING)
    {
        deviceController->aRDrone3->sendPilotingLanding(deviceController->aRDrone3);
    }
}
- (void)land
{
    eARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE flyingState = [self getFlyingState];
    if (flyingState == ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_FLYING || flyingState == ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_HOVERING)
    {
        _deviceController->aRDrone3->sendPilotingLanding(_deviceController->aRDrone3);
    }
}
private void land()
{
    ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_ENUM flyingState = getPilotingState();
    if (ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_ENUM.ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_HOVERING.equals(flyingState) ||
            ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_ENUM.ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_HOVERING.equals(flyingState))
    {
        ARCONTROLLER_ERROR_ENUM error = deviceController.getFeatureARDrone3().sendPilotingLanding();

        if (!error.equals(ARCONTROLLER_ERROR_ENUM.ARCONTROLLER_OK))
        {
            ARSALPrint.e(TAG, "Error while sending take off: " + error);
        }
    }
}

Piloting

The Bebop drone is piloted with angles. And the way you specifie these angles are in percentage of the max angle.
The piloting command is automatically sent by the device controller each 25ms.
If a disconnection appears, or if the commands are not received, the Bebop will set all its angle to 0 after 500ms for security reasons.

In the piloting command there are 6 params:

Here is how you set the piloting angles:

Make the drone moves forward (50% of its max angle)

deviceController->aRDrone3->setPilotingPCMDFlag(deviceController->aRDrone3, 1);
deviceController->aRDrone3->setPilotingPCMDPitch(deviceController->aRDrone3, 50);
_deviceController->aRDrone3->setPilotingPCMDFlag(_deviceController->aRDrone3, 1);
_deviceController->aRDrone3->setPilotingPCMDPitch(_deviceController->aRDrone3, 50);
deviceController.getFeatureARDrone3().setPilotingPCMDFlag((byte) 1);
deviceController.getFeatureARDrone3().setPilotingPCMDPitch((byte) 50);

Make the drone rotate to the right (50% of its max rotation speed)

deviceController->aRDrone3->setPilotingPCMDYaw(deviceController->aRDrone3, 50);
_deviceController->aRDrone3->setPilotingPCMDYaw(_deviceController->aRDrone3, 50);
deviceController.getFeatureARDrone3().setPilotingPCMDYaw((byte) 50);

Start video streaming

To start the video stream, you will need to send a command to the Bebop. When the frame are received, the callback you set at the initialisation of your device controller will be called.

Start video stream

deviceController->aRDrone3->sendMediaStreamingVideoEnable(deviceController->aRDrone3, 1);
_deviceController->aRDrone3->sendMediaStreamingVideoEnable(_deviceController- aRDrone3, 1);
deviceController.getFeatureARDrone3().sendMediaStreamingVideoEnable((byte)1);

Stop video stream

deviceController->aRDrone3->sendMediaStreamingVideoEnable(deviceController->aRDrone3, 0);
_deviceController->aRDrone3->sendMediaStreamingVideoEnable(_deviceController->aRDrone3, 0);
deviceController.getFeatureARDrone3().sendMediaStreamingVideoEnable((byte)0);

Taking a picture

The drone lets you take pictures.

Take a picture

deviceController->aRDrone3->sendMediaRecordPicture(deviceController->aRDrone3, 0);
_deviceController->aRDrone3->sendMediaRecordPicture(_deviceController->aRDrone3, 0);
deviceController.getFeatureARDrone3().sendMediaRecordPicture((byte)0);

Download pictures and videos

Once the picture or video has been taken, the Bebop stores it on its internal memory. Pictures are stored in internal_000/Bebop_Drone/media/.
To get the pictures, you can:

Here is how to do it with libARDataTransfer.

libARDataTransfer lets you get a list of all stored medias quite quickly. It also provides you a way to enrich the list of medias with their thumbnails. It also gives you the ability to download the media.

First, you will need to create the data transfer manager

Declare vars

#define DEVICE_PORT     21
#define MEDIA_FOLDER    "internal_000"

ARSAL_Thread_t threadRetreiveAllMedias;   // the thread that will do the media retrieving
ARSAL_Thread_t threadGetThumbnails;       // the thread that will download the thumbnails
ARSAL_Thread_t threadMediasDownloader;    // the thread that will download medias
ARDATATRANSFER_Manager_t *manager;        // the data transfer manager
ARUTILS_Manager_t *ftpListManager;        // an ftp that will do the list
ARUTILS_Manager_t *ftpQueueManager;       // an ftp that will do the download
#define MEDIA_FOLDER    "internal_000"

@property (nonatomic, assign) ARSAL_Thread_t threadRetreiveAllMedias;   // the thread that will do the media retrieving
@property (nonatomic, assign) ARSAL_Thread_t threadGetThumbnails;       // the thread that will download the thumbnails
@property (nonatomic, assign) ARSAL_Thread_t threadMediasDownloader;    // the thread that will download medias

@property (nonatomic, assign) ARDATATRANSFER_Manager_t *manager;        // the data transfer manager
@property (nonatomic, assign) ARUTILS_Manager_t *ftpListManager;        // an ftp that will do the list
@property (nonatomic, assign) ARUTILS_Manager_t *ftpQueueManager;       // an ftp that will do the download
private static final String MEDIA_FOLDER = "internal_000";

private AsyncTask<Void, Float, ArrayList<ARMediaObject>> getMediaAsyncTask;
private AsyncTask<Void, Float, Void> getThumbnailAsyncTask;
private Handler mFileTransferThreadHandler;
private HandlerThread mFileTransferThread;
private boolean isRunning = false;
private boolean isDownloading = false;
private final Object lock = new Object();

private ARDataTransferManager dataTransferManager;
private ARUtilsManager ftpListManager;
private ARUtilsManager ftpQueueManager;

Create the data transfer manager

void createDataTransferManager()
{
    eARDATATRANSFER_ERROR result = ARDATATRANSFER_OK;
    manager = ARDATATRANSFER_Manager_New(&result);

    if (result == ARDATATRANSFER_OK)
    {
        eARUTILS_ERROR ftpError = ARUTILS_OK;
        ftpListManager = ARUTILS_Manager_New(&ftpError);
        if(ftpError == ARUTILS_OK)
        {
            ftpQueueManager = ARUTILS_Manager_New(&ftpError);
        }

        if(ftpError == ARUTILS_OK)
        {
            ftpError = ARUTILS_Manager_InitFtp(ftpListManager, discoveryDevice, ARUTILS_DESTINATION_DRONE, ARUTILS_FTP_TYPE_GENERIC);
        }

        if(ftpError == ARUTILS_OK)
        {
            ftpError = ARUTILS_Manager_InitFtp(ftpQueueManager, discoveryDevice, ARUTILS_DESTINATION_DRONE, ARUTILS_FTP_TYPE_GENERIC);
        }

        if(ftpError != ARUTILS_OK)
        {
            result = ARDATATRANSFER_ERROR_FTP;
        }
    }
    // NO ELSE

    if (result == ARDATATRANSFER_OK)
    {
        const char *path = "the/path/to/store/downloaded/data"; // Change according to your needs, or put as an argument

        result = ARDATATRANSFER_MediasDownloader_New(manager, ftpListManager, ftpQueueManager, MEDIA_FOLDER, path);
    }
}
- (void)createDataTransferManager
{
    eARDATATRANSFER_ERROR result = ARDATATRANSFER_OK;
    _manager = ARDATATRANSFER_Manager_New(&result);

    if (result == ARDATATRANSFER_OK)
    {
        eARUTILS_ERROR ftpError = ARUTILS_OK;
        _ftpListManager = ARUTILS_Manager_New(&ftpError);
        if(ftpError == ARUTILS_OK)
        {
            _ftpQueueManager = ARUTILS_Manager_New(&ftpError);
        }

        if(ftpError == ARUTILS_OK)
        {
            ftpError = ARUTILS_Manager_InitFtp(_ftpListManager, _discoveryDevice, ARUTILS_DESTINATION_DRONE, ARUTILS_FTP_TYPE_GENERIC);
        }

        if(ftpError == ARUTILS_OK)
        {
            ftpError = ARUTILS_Manager_InitFtp(_ftpQueueManager, _discoveryDevice, ARUTILS_DESTINATION_DRONE, ARUTILS_FTP_TYPE_GENERIC);
        }

        if(ftpError != ARUTILS_OK)
        {
            result = ARDATATRANSFER_ERROR_FTP;
        }
    }
    // NO ELSE

    if (result == ARDATATRANSFER_OK)
    {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *path = [paths lastObject];

        result = ARDATATRANSFER_MediasDownloader_New(_manager, _ftpListManager, _ftpQueueManager, MEDIA_FOLDER, [path UTF8String]);
    }
}
private void createDataTransferManager() {
   ARDATATRANSFER_ERROR_ENUM result = ARDATATRANSFER_ERROR_ENUM.ARDATATRANSFER_OK;
   try
   {
       dataTransferManager = new ARDataTransferManager();
   }
   catch (ARDataTransferException e)
   {
       e.printStackTrace();
       result = ARDATATRANSFER_ERROR_ENUM.ARDATATRANSFER_ERROR;
   }

   if (result == ARDATATRANSFER_ERROR_ENUM.ARDATATRANSFER_OK)
   {
       try
       {
           ftpListManager = new ARUtilsManager();
           ftpQueueManager = new ARUtilsManager();

           ftpListManager.initFtp(context, deviceService, ARUTILS_DESTINATION_ENUM.ARUTILS_DESTINATION_DRONE, ARUTILS_FTP_TYPE_ENUM.ARUTILS_FTP_TYPE_GENERIC);
           ftpQueueManager.initFtp(context, deviceService, ARUTILS_DESTINATION_ENUM.ARUTILS_DESTINATION_DRONE, ARUTILS_FTP_TYPE_ENUM.ARUTILS_FTP_TYPE_GENERIC);
       }
       catch (ARUtilsException e)
       {
           e.printStackTrace();
           result = ARDATATRANSFER_ERROR_ENUM.ARDATATRANSFER_ERROR_FTP;
       }
   }
   if (result == ARDATATRANSFER_ERROR_ENUM.ARDATATRANSFER_OK)
   {
       // direct to external directory
       String externalDirectory = Environment.getExternalStorageDirectory().toString().concat("/ARSDKMedias/");
       try
       {
           dataTransferManager.getARDataTransferMediasDownloader().createMediasDownloader(ftpListManager, ftpQueueManager, MEDIA_FOLDER, externalDirectory);
       }
       catch (ARDataTransferException e)
       {
           e.printStackTrace();
           result = e.getError();
       }
   }

   if (result == ARDATATRANSFER_ERROR_ENUM.ARDATATRANSFER_OK)
   {
       // create a thread for the download to run the download runnable
       mFileTransferThread = new HandlerThread("FileTransferThread");
       mFileTransferThread.start();
       mFileTransferThreadHandler = new Handler(mFileTransferThread.getLooper());
   }

   if (result != ARDATATRANSFER_ERROR_ENUM.ARDATATRANSFER_OK)
   {
       // clean up here because an error happened
   }
}

Then, we can get the list of all medias on the Bebop (without their thumbnail). This operation is quite quick.

Get the list of the medias

void startMediaListThread()
{
    // first retrieve Medias without their thumbnails
    ARSAL_Thread_Create(&threadRetreiveAllMedias, ARMediaStorage_retreiveAllMediasAsync, NULL);
}

static void* ARMediaStorage_retreiveAllMediasAsync(void* arg)
{
    getAllMediaAsync();
    return NULL;
}

void getAllMediaAsync()
{
    eARDATATRANSFER_ERROR result = ARDATATRANSFER_OK;
    int mediaListCount = 0;

    if (result == ARDATATRANSFER_OK)
    {
        mediaListCount = ARDATATRANSFER_MediasDownloader_GetAvailableMediasSync(manager,0,&result);
        if (result == ARDATATRANSFER_OK)
        {
            for (int i = 0 ; i < mediaListCount && result == ARDATATRANSFER_OK; i++)
            {
                ARDATATRANSFER_Media_t * mediaObject = ARDATATRANSFER_MediasDownloader_GetAvailableMediaAtIndex(manager, i, &result);
                printf("Media %i: %s", i, mediaObject->name);
                // Do what you want with this mediaObject
            }
        }
    }
}
- (void)startMediaListThread
{
    // first retrieve Medias without their thumbnails
    ARSAL_Thread_Create(&_threadRetreiveAllMedias, ARMediaStorage_retreiveAllMediasAsync, (__bridge void *)self);
}

static void* ARMediaStorage_retreiveAllMediasAsync(void* arg)
{
    PilotingViewController *self = (__bridge PilotingViewController *)(arg);
    [self getAllMediaAsync];
    return NULL;
}

- (void)getAllMediaAsync
{
    eARDATATRANSFER_ERROR result = ARDATATRANSFER_OK;
    int mediaListCount = 0;

    if (result == ARDATATRANSFER_OK)
    {
        mediaListCount = ARDATATRANSFER_MediasDownloader_GetAvailableMediasSync(_manager,0,&result);
        if (result == ARDATATRANSFER_OK)
        {
            for (int i = 0 ; i < mediaListCount && result == ARDATATRANSFER_OK; i++)
            {
                ARDATATRANSFER_Media_t * mediaObject = ARDATATRANSFER_MediasDownloader_GetAvailableMediaAtIndex(_manager, i, &result);
                NSLog(@"Media %i: %s", i, mediaObject->name);
                // Do what you want with this mediaObject
            }
        }
    }
}
private void fetchMediasList() {
   if (getMediaAsyncTask == null)
   {
       getMediaAsyncTask = new AsyncTask<Void, Float, ArrayList<ARMediaObject>>()
       {
           @Override
           protected ArrayList<ARMediaObject> doInBackground(Void... params)
           {
               ArrayList<ARMediaObject> mediaList = null;
               synchronized (lock)
               {
                   ARDataTransferMediasDownloader mediasDownloader = null;
                   if (dataTransferManager != null)
                   {
                       mediasDownloader = dataTransferManager.getARDataTransferMediasDownloader();
                   }

                   if (mediasDownloader != null)
                   {
                       try
                       {
                           int mediaListCount = mediasDownloader.getAvailableMediasSync(false);
                           mediaList = new ArrayList<>(mediaListCount);
                           for (int i = 0; i < mediaListCount; i++)
                           {
                               ARDataTransferMedia currentMedia = mediasDownloader.getAvailableMediaAtIndex(i);
                               final ARMediaObject currentARMediaObject = new ARMediaObject();
                               currentARMediaObject.updateDataTransferMedia(getResources(), currentMedia);
                               mediaList.add(currentARMediaObject);
                           }
                       }
                       catch (ARDataTransferException e)
                       {
                           e.printStackTrace();
                           mediaList = null;
                       }
                   }
               }

               return mediaList;
           }

           @Override
           protected void onPostExecute(ArrayList<ARMediaObject> arMediaObjects)
           {
                // Do what you want with the list of media object
           }
       };
   }

   if (getMediaAsyncTask.getStatus() != AsyncTask.Status.RUNNING) {
       getMediaAsyncTask.execute();
   }
}

Once the list is received, we can start downloading the thumbnail (not needed if you don’t display thumbnails).

Download thumbnails

void startMediaThumbnailDownloadThread()
{
    // first retrieve Medias without their thumbnails
    ARSAL_Thread_Create(&threadGetThumbnails, ARMediaStorage_retreiveMediaThumbnailsSync, NULL);
}

static void* ARMediaStorage_retreiveMediaThumbnailsSync(void* arg)
{
    downloadThumbnails();
    return NULL;
}

void downloadThumbnails()
{
    ARDATATRANSFER_MediasDownloader_GetAvailableMediasAsync(manager, availableMediaCallback, NULL);
}

void availableMediaCallback (void* arg, ARDATATRANSFER_Media_t *media, int index)
{
    if (NULL != arg)
    {
        // The thumbnail image data is available in the media->thumbnail pointer.
        // The thumbnail data size is media->thumbnailSize
        // Do what you want with the image
    }
}
- (void)startMediaThumbnailDownloadThread
{
    // first retrieve Medias without their thumbnails
    ARSAL_Thread_Create(&_threadGetThumbnails, ARMediaStorage_retreiveMediaThumbnailsSync, (__bridge void *)self);
}

static void* ARMediaStorage_retreiveMediaThumbnailsSync(void* arg)
{
    PilotingViewController *self = (__bridge PilotingViewController *)(arg);
    [self downloadThumbnails];
    return NULL;
}

- (void)downloadThumbnails
{
    ARDATATRANSFER_MediasDownloader_GetAvailableMediasAsync(_manager, availableMediaCallback, (__bridge void *)self);
}

void availableMediaCallback (void* arg, ARDATATRANSFER_Media_t *media, int index)
{
    if (NULL != arg)
    {
        PilotingViewController *self = (__bridge PilotingViewController *)(arg);
        // you can alternatively call updateThumbnailWithARDATATRANSFER_Media_t if you use the ARMediaObjectDelegate
        UIImage *newThumbnail = [UIImage imageWithData:[NSData dataWithBytes:media->thumbnail length:media->thumbnailSize]];
        // Do what you want with the image
    }
}
private void fetchThumbnails() {
   if (getThumbnailAsyncTask == null)
   {
       getThumbnailAsyncTask = new AsyncTask<Void, Float, Void>()
       {
           @Override
           protected Void doInBackground(Void... params)
           {
               synchronized (lock)
               {
                   ARDataTransferMediasDownloader mediasDownloader = null;
                   if (dataTransferManager != null)
                   {
                       mediasDownloader = dataTransferManager.getARDataTransferMediasDownloader();
                   }

                   if (mediasDownloader != null)
                   {
                       try
                       {
                           // availableMediaListener is a ARDataTransferMediasDownloaderAvailableMediaListener (you can pass YourActivity.this if YourActivity implements this interface)
                           mediasDownloader.getAvailableMediasAsync(availableMediaListener, null);
                       }
                       catch (ARDataTransferException e)
                       {
                           e.printStackTrace();
                       }
                   }
               }
               return null;
           }

           @Override
           protected void onPostExecute(Void param)
           {
               adapter.notifyDataSetChanged();
           }
       };
   }

   if (getThumbnailAsyncTask.getStatus() != AsyncTask.Status.RUNNING) {
       getThumbnailAsyncTask.execute();
   }
}

@Override
public void didMediaAvailable(Object arg, final ARDataTransferMedia media, final int index)
{
   runOnUiThread(new Runnable()
   {
       @Override
       public void run()
       {
           ARMediaObject mediaObject = getMediaAtIndex(index);
           if (mediaObject != null)
           {
               mediaObject.updateThumbnailWithDataTransferMedia(getResources(), media);
               // after that, you can retrieve the thumbnail through mediaObject.getThumbnail()
           }
       }
   });
}

Next step is to download the medias.

Download medias

void downloadMedias(ARDATATRANSFER_Media_t *medias, int count)
{
    eARDATATRANSFER_ERROR result = ARDATATRANSFER_OK;
    for (int i = 0 ; i < count && result == ARDATATRANSFER_OK; i++)
    {
        ARDATATRANSFER_Media_t *media = medias[i];
        result = ARDATATRANSFER_MediasDownloader_AddMediaToQueue(manager, media, medias_downloader_progress_callback, NULL, medias_downloader_completion_callback, NULL);
    }

    if (result == ARDATATRANSFER_OK)
    {
        if (threadMediasDownloader == NULL)
        {
            // if not already started, start download thread in background
            ARSAL_Thread_Create(&threadMediasDownloader, ARDATATRANSFER_MediasDownloader_QueueThreadRun, manager);
        }
    }
}
void medias_downloader_progress_callback(void* arg, ARDATATRANSFER_Media_t *media, float percent)
{
    // the media is downloading
}

void medias_downloader_completion_callback(void* arg, ARDATATRANSFER_Media_t *media, eARDATATRANSFER_ERROR error)
{
    // the media is downloaded
}
- (void)downloadMedias:(ARDATATRANSFER_Media_t *)medias withCount:(int)count
{
    eARDATATRANSFER_ERROR result = ARDATATRANSFER_OK;
    for (int i = 0 ; i < count && result == ARDATATRANSFER_OK; i++)
    {
        ARDATATRANSFER_Media_t *media = medias[i];
        result = ARDATATRANSFER_MediasDownloader_AddMediaToQueue(_manager, media, medias_downloader_progress_callback, (__bridge void *)(self), medias_downloader_completion_callback,(__bridge void*)self);
    }

    if (result == ARDATATRANSFER_OK)
    {
        if (_threadMediasDownloader == NULL)
        {
            // if not already started, start download thread in background
            ARSAL_Thread_Create(&_threadMediasDownloader, ARDATATRANSFER_MediasDownloader_QueueThreadRun, _manager);
        }
    }
}
void medias_downloader_progress_callback(void* arg, ARDATATRANSFER_Media_t *media, float percent)
{
    // the media is downloading
}

void medias_downloader_completion_callback(void* arg, ARDATATRANSFER_Media_t *media, eARDATATRANSFER_ERROR error)
{
    // the media is downloaded
}
/**
* Add the medias to the transfer queue and, if needed, start the queue
* @param mediaToDl list of media index to download
*/
private void downloadMedias(ArrayList<Integer> mediaToDl)
{
   ARDataTransferMediasDownloader mediasDownloader = null;
   if (dataTransferManager != null)
   {
       mediasDownloader = dataTransferManager.getARDataTransferMediasDownloader();
   }

   if (mediasDownloader != null)
   {
       for (int i = 0; i < mediaToDl.size(); i++)
       {
           int mediaIndex = mediaToDl.get(i);
           ARDataTransferMedia mediaObject = null;
           try
           {
               mediaObject = dataTransferManager.getARDataTransferMediasDownloader().getAvailableMediaAtIndex(mediaIndex);
           }
           catch (ARDataTransferException e)
           {
               e.printStackTrace();
           }

           if (mediaObject != null)
           {
               try
               {
                   mediasDownloader.addMediaToQueue(mediaObject, progressListener, null, completeListener, null);
               }
               catch (ARDataTransferException e)
               {
                   e.printStackTrace();
               }
           }
       }

       if (!isRunning)
       {
           isRunning = true;
           Runnable downloaderQueueRunnable = mediasDownloader.getDownloaderQueueRunnable();
           mFileTransferThreadHandler.post(downloaderQueueRunnable);
       }
   }
   isDownloading = true;
}

@Override
public void didMediaComplete(Object arg, ARDataTransferMedia media, ARDATATRANSFER_ERROR_ENUM error)
{
    // the media is downloaded
}

@Override
public void didMediaProgress(Object arg, ARDataTransferMedia media, float percent)
{
    // the media is downloading
}

Stop downloading medias

void cancelCurrentDownload() {
    if (threadMediasDownloader != NULL)
    {
        ARDATATRANSFER_MediasDownloader_CancelQueueThread(manager);

        ARSAL_Thread_Join(threadMediasDownloader, NULL);
        ARSAL_Thread_Destroy(&threadMediasDownloader);
        threadMediasDownloader = NULL;
    }
}
- (void)cancelCurrentDownload {
    if (_threadMediasDownloader != NULL)
    {
        ARDATATRANSFER_MediasDownloader_CancelQueueThread(_manager);

        ARSAL_Thread_Join(_threadMediasDownloader, NULL);
        ARSAL_Thread_Destroy(&_threadMediasDownloader);
        _threadMediasDownloader = NULL;
    }
}
private void cancelCurrentDownload()
{
   dataTransferManager.getARDataTransferMediasDownloader().cancelQueueThread();
   isDownloading = false;
   isRunning = false;
}

When you don’t need the data transfer anymore, don’t forget to clean everything

Clean

void clean()
{
    if (threadRetreiveAllMedias != NULL)
    {
        ARDATATRANSFER_MediasDownloader_CancelGetAvailableMedias(manager);

        ARSAL_Thread_Join(threadRetreiveAllMedias, NULL);
        ARSAL_Thread_Destroy(&threadRetreiveAllMedias);
        threadRetreiveAllMedias = NULL;
    }

    if (threadGetThumbnails != NULL)
    {
        ARDATATRANSFER_MediasDownloader_CancelGetAvailableMedias(manager);

        ARSAL_Thread_Join(threadGetThumbnails, NULL);
        ARSAL_Thread_Destroy(&threadGetThumbnails);
        threadGetThumbnails = NULL;
    }

    if (threadMediasDownloader != NULL)
    {
        ARDATATRANSFER_MediasDownloader_CancelQueueThread(manager);

        ARSAL_Thread_Join(threadMediasDownloader, NULL);
        ARSAL_Thread_Destroy(&threadMediasDownloader);
        threadMediasDownloader = NULL;
    }

    ARDATATRANSFER_MediasDownloader_Delete(manager);

    ARUTILS_Manager_CloseWifiFtp(ftpListManager);
    ARUTILS_Manager_CloseWifiFtp(ftpQueueManager);

    ARUTILS_Manager_Delete(&ftpListManager);
    ARUTILS_Manager_Delete(&ftpQueueManager);
    ARDATATRANSFER_Manager_Delete(&manager);
}
- (void)clean
{
    if (_threadRetreiveAllMedias != NULL)
    {
        ARDATATRANSFER_MediasDownloader_CancelGetAvailableMedias(_manager);

        ARSAL_Thread_Join(_threadRetreiveAllMedias, NULL);
        ARSAL_Thread_Destroy(&_threadRetreiveAllMedias);
        _threadRetreiveAllMedias = NULL;
    }

    if (_threadGetThumbnails != NULL)
    {
        ARDATATRANSFER_MediasDownloader_CancelGetAvailableMedias(_manager);

        ARSAL_Thread_Join(_threadGetThumbnails, NULL);
        ARSAL_Thread_Destroy(&_threadGetThumbnails);
        _threadGetThumbnails = NULL;
    }

    if (_threadMediasDownloader != NULL)
    {
        ARDATATRANSFER_MediasDownloader_CancelQueueThread(_manager);

        ARSAL_Thread_Join(_threadMediasDownloader, NULL);
        ARSAL_Thread_Destroy(&_threadMediasDownloader);
        _threadMediasDownloader = NULL;
    }

    ARDATATRANSFER_MediasDownloader_Delete(_manager);

    ARUTILS_Manager_CloseWifiFtp(_ftpListManager);
    ARUTILS_Manager_CloseWifiFtp(_ftpQueueManager);

    ARUTILS_Manager_Delete(&_ftpListManager);
    ARUTILS_Manager_Delete(&_ftpQueueManager);
    ARDATATRANSFER_Manager_Delete(&_manager);
}
private void clean()
{
   new Thread(new Runnable()
   {
       @Override
       public void run()
       {
           cancelCurrentDownload();

           if (dataTransferManager != null)
           {
               dataTransferManager.getARDataTransferMediasDownloader().cancelGetAvailableMedias();
           }
           if (getMediaAsyncTask != null && getMediaAsyncTask.getStatus() == AsyncTask.Status.RUNNING)
           {
               synchronized (lock)
               {
                   getMediaAsyncTask.cancel(true);
               }
           }
           if (getThumbnailAsyncTask != null && getThumbnailAsyncTask.getStatus() == AsyncTask.Status.RUNNING)
           {
               synchronized (lock)
               {
                   getThumbnailAsyncTask.cancel(true);
               }
           }

           if (ftpListManager != null)
           {
               ftpListManager.closeFtp(context, deviceService);
               ftpListManager.dispose();
               ftpListManager = null;
           }
           if (ftpQueueManager != null)
           {
               ftpQueueManager.closeFtp(context, deviceService);
               ftpQueueManager.dispose();
               ftpQueueManager = null;
           }
           if (dataTransferManager != null)
           {
               dataTransferManager.dispose();
               dataTransferManager = null;
           }

           if (mFileTransferThread != null)
           {
               mFileTransferThread.quit();
               mFileTransferThread = null;
           }
       }
   }).start();
}

Messages reference

A complete list of supported messages can be found in the reference.
Or chose a product to only see the reference of the messages supported by this product:

Go deeper

If you want to build your own SDK, you can ! It is fully open sourced.

The version control is based on the repo tool, you can get it following these instructions.
Learn here how to use repo.
On Mac OS you will need execute this command to install with brew some needed tools:
brew install bash coreutils gettext pkgconfig wget python python3 autoconf libtool

Download all sources

SDK sources are hosted on github. To download them, you only have to init repo with the arsdk_manifests url:
repo init -u https://github.com/Parrot-Developers/arsdk_manifests.git

After that, you can download all the other repository automatically by executing the command:
repo sync

Then follow the How to build the SDK section.

Organisation

SDK is organized as following:

arsdk_manifests

This git repository list all other repositories that are part of or needed by the SDK.

arsdk_products

This git repository provides a build.sh that you can call to build the whole SDK.

ARSDKBuildUtils

This repo contains all the tools to create the autotools needed to build the SDK. It is called by build.sh file contained in arsdk_products.

arsdk-xml

This repository contains all the xml files describing the messages you can send to and receive from the drone.

libARCommands

This library to generate the files containing the messages you can send to and receive from the drone.

libARController

This library provides an abstraction level to connect to the drone. With this library you can create a device controller, which ables you to pilot, take pictures, receive stream (depending on the drone), and send/receive other commands from the drone.

libARDataTransfer

This library helps you transfering data from or to the drone.

libARDiscovery

This library helps you to discover from the network all supported drones.

This library helps you to generate an automated flight file.

libARMedia

This library provides an abstraction layer around the medias generated by the drones.

libARNetwork

This library is in charge of sending and receiving packets to/from the drone.

libARNetworkAL

This library provides an abstraction layer around the different networks (BLE or Wifi networks).

libARSAL

This library provides a system abstraction layer.

libARStream

This library deals with all streaming types. It packs/unpacks streams.

libARStream2

This library deals with the new h264 stream. It packs/unpacks streams and deals with rtp streams.

libARUpdater

This library helps you to update your drone. It provides functions to tests if the version is the last one and functions to update the drone.

libARUtils

This library provides utilities classes.

libmux

This library provides functions to use a mux.

libpomp

This library is a printf oriented message protocol.

Samples

You can find in this repo some examples for iOS, Android and Unix.

How to build the SDK

You should have first downloaded all the sources of the SDK.

The root folder where you have executed the repo init and repo sync command will be noted <SDK>

Required external tools

These external tools are required to build the SDK:

Linux: install these tools using your favorite package manager
OSX: you will need XCode to be installed. Then, you can use brew to install these tools.

General Build

The build is done by the ./build.sh script. You can run ./build.sh --help to know more about the building options.
The general way to build is:
./build.sh -p arsdk-VARIANT -t TASK OTHER_ARGS

Variants available are:

Unix Build

Linux: Tested on Ubuntu 14.04
OSX: Tested on 10.11.6

The command to build the SDK for Unix platform is:
./build.sh -p arsdk-native -t build-sdk -j
The output will be in <SDK>/out/Unix-base/staging/usr/

Tasks available are:

Running the samples:

In order to run the samples, you must add the <SDK>/out/arsdk-native/staging/usr/lib folder to the LD_LIBRARY_PATH environment variable. This can be done by using the <SDK>/out/Unix-base/staging/native-wrapper.sh script. This script can be used in the two following ways:

iOS Build

OSX: Tested on 10.11.6

The command to build the SDK for iOS is:
./build.sh -p arsdk-ios -t build-sdk -j
The output will be in <SDK>/out/arsdk-ios/staging/usr/

Tasks available are:

iPhone Simulator Build

OSX: Tested on 10.11.6

The command to build the SDK for iOS is:
./build.sh -p arsdk-ios_sim -t build-sdk -j
The output will be in <SDK>/out/arsdk-ios_sim/staging/usr/

Tasks available are:

Android Build

Linux: Tested on Ubuntu 14.04
OSX: Tested on 10.11.6

You will need this following parts:

The command to build the SDK for Android platform is:
./build.sh -p arsdk-android -t build-sdk -j
The output will be in <SDK>/out/arsdk-android/ARCH/staging/usr/

Tasks available are:

Clean

To do a clean build, simply delete the <SDK>/out/ folder.