L’AR Drone de Parrot propose une grande souplesse d’utilisation. Vous pouvez le commander à partir de l’application iOS dédié ou de son équivalent sur Android. Mais vous pouvez également développer votre propre application ! Pour ce faire la première étape consiste à commander le drone pour lui demander d’exécuter des actions très simples. Dans cet article nous verrons comment commander l’AR Drone simplement en lui envoyant des paquets UDP. Nous enverrons au drone des commandes AT ou commandes de Hayes. Vous trouverez la liste de ces commandes dans le guide du développeur édité par Parrot. Pour notre exemple nous nous limiterons à trois commandes AT incontournables. Nous demanderons au drone de décoller puis d’attendre quelques secondes avant d’atterrir. Enfin nous verrons comment couper les moteurs du drone en plein vol ! Si vous n’êtes pas familier des commandes AT je vous invite à lire cet article que j’ai publié il y a quelques semaines (lire : Arduino – Prise en main du module SIM900) et dans lequel nous avons commandé un modem GSM en lui envoyant des commandes AT à l’aide d’une carte Arduino.

La première étape consiste donc en l’envoi de paquets UDP. Ces paquets doivent contenir la commande AT à envoyer. Ni plus ni moins ! Bien sur vous pouvez inclure plusieurs commandes au sein d’un même paquet afin de demander au drone d’effectuer plusieurs actions.

Mais qu’est-ce que l’UDP ?

Le User Datagram Protocol est l’un des principal protocole de télécommunication d’Internet. Celui-ci fait parti de la couche « Transport » de la pile de protocole TCP/IP. L’UDP ne propose aucune phase de connexion préalable à l’envoi des données. En d’autres termes c’est un protocole très simple mais peu fiable ! Pour notre application le chemin entre le client (votre ordinateur) et le serveur (l’AR Drone) est direct. Aucun risque que les paquets UDP ne soient perdus en cours de route !

Quelle est l’adresse IP du drone ?

Les paquets UDP contenant la commande AT à transmettre au drone doivent être envoyé à l’adresse IP 192.168.1.1 sur le port 5556. Il s’agît de l’adresse IP que le drone s’attribue sur le réseau WiFi qu’il créé lors de sa mise sous tension. L’AR Drone exécute toute les commandes AT qu’il reçoit sur le port 5556. D’autres ports sont dédiés à la transmissions vidéo ou aux informations de navigation telles que l’altitude ou la direction du drone. Nous aborderons ces autres canaux de communication dans un prochain article. Nous verrons notamment comment créer un serveur UDP en langage C !

Voici un client UDP pour UNIX/Linux écrit en langage C. Ce programme utilise les sockets. Un socket est une interface permettant la communication entre un système et un réseau utilisant les protocoles TCP/IP. J’ai testé ce programme sur MAC OSX mais celui-ci devrait fonctionner sans aucune modifications sur une machine Linux. Toutefois ce programme ne fonctionnera pas sur Windows. Vous voilà prévenus !

// Client UDP
// Pierre PELE
// Paul-Darius SARMADI
// Upsilonaudio.com

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h> 
#include <string.h>

#define BUFLEN 512
#define ADRESSEIP "192.168.1.1"
#define PORT 5556
#define MESSAGE "AT*REF=101,290718208\r"

void err(char *s);

int main(int argc, char** argv)
{
    struct sockaddr_in serv_addr;
    int sockfd, i, slen=sizeof(serv_addr);
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
    {
        err("socket");
    }
    bzero(&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);
    if (inet_aton(ADRESSEIP, &serv_addr.sin_addr)==0)
    {
        fprintf(stderr, "inet_aton() failed\n");
        exit(1);
    }
    if (sendto(sockfd, MESSAGE, BUFLEN, 0, (struct sockaddr*)&serv_addr, slen)==-1)
    {
        err("sendto()");
    }
    close(sockfd);
    return 0;
}

void err(char *s)
{
    perror(s);
    exit(1);
}

Vous pouvez modifier le contenu du paquet UDP ainsi que l’adresse IP et le port du serveur simplement en changeant les trois lignes suivantes. L’adresse IP et le contenu du paquet doivent être saisis entre guillemets. Pour notre exemple j’ai choisi la commande AT*REF=101,290718208 qui commande le décollage du drone.

#define ADRESSEIP "192.168.1.1"
#define PORT 5556
#define MESSAGE "AT*REF=101,290718208\r"

Pour nos expérimentations nous avons utilisé GCC en ligne de commande comme compilateur. Vous pouvez évidemment utiliser n’importe quel compilateur pour compiler ce code source à condition d’utiliser une machine UNIX/Linux. Ce qui inclut MAC OSX !

Comment compiler ce programme avec GCC ?

Pour compiler ce programme avec GCC il suffit d’exécuter la commande suivante dans une console UNIX. Le répertoire courant doit bien évidement contenir le fichier source Client_UDP.c !

gcc Client_UDP.c -o Client_UDP
Console UNIX

Cette commande créé un fichier exécutable. Celui-ci s’intitule simplement Client_UDP. Pensez à compiler le code-source dès que vous changer l’une des  directives de préprocesseur !

À quoi correspond le caractère r à la fin de la commande AT ?

Vous remarquerez dans le code source du programme que j’ai ajouté le caractère r à la fin de la commande AT. En langage C le caractère r dénote le retour chariot (Carriage return en anglais ou CR). Ce caractère de fin de ligne indique au drone la fin de la commande AT. C’est pourquoi vous devez systématiquement ajouter ce caractère à la fin de toutes les commandes AT que vous envoyez au drone. À titre informatif en ASCII le retour chariot est indexé comme le caractère 13 en notation décimale et 0D en notation hexadécimale. Ne vous laissez pas surprendre par ce caractère ! Initialement nous avions oublié d’ajouter le CR dans nos programmes. Plusieurs heures de débogage nous auront été nécessaires avant de comprendre notre erreur …

La taille du paquet UDP est fixée par défaut à 512 caractères. Vous pouvez modifier cette valeur en changeant simplement la ligne suivante dans le code-source du programme. Toutefois la taille maximale des paquets UDP est fixée à 1024 caractères. Au delà de cette limite l’AR Drone ignorera le paquet UDP.

#define BUFLEN 512

Ce dernier programme est entièrement fonctionnel mais n’est pas optimisé. En effet nous devons modifier le code source du programme et le compiler dès que nous souhaitons envoyer une nouvelle commande AT ! Ce n’est vraiment pas pratique. C’est pourquoi j’ai modifié ce programme afin de le rendre plus simple d’emploi. Sans plus attendre voici la version améliorée de ce programme.

// Client UDP
// Pierre PELE
// Paul-Darius SARMADI
// Upsilonaudio.com

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>

// Prototype des fonctions
void err(char *s);

void delai ( int secondes);

// Fonction principale
int main(int argc, char** argv)
{
    if(argc<4 || (argc>4 && argc<6) || argc>6)
    {
        printf("\nSyntaxe du programme : %s ",argv[0]);
        printf("<Adresse IP du serveur> <Port> ");
        printf("\033[%sm","31");
        printf("<Commande AT>\n");
        printf("\033[%sm","0");
        printf("\nSyntaxe alternative : %s ",argv[0]);
        printf("<Adresse IP du serveur> <Port> ");
        printf("\033[%sm","31");
        printf("<Commande AT N°1> ");
        printf("\033[%sm","0");
        printf("<Delai> ");
        printf("\033[%sm","31");
        printf("<Commande AT N°2>\n\n");
        printf("\033[%sm","0");
        exit(0);
    }
    FILE *fichier = NULL;
    char nomDuFichier[]="Notes.txt";
    fichier = fopen (nomDuFichier,"a");
    time_t now = time (NULL);
    struct tm tm_now = *localtime (&now);
    char s_now[sizeof "JJ/MM/AAAA HH:MM:SS"];
    strftime (s_now, sizeof s_now, "%d/%m/%Y %H:%M:%S", &tm_now);
    fprintf(fichier,"\n\n%s\n",s_now);
    if (fichier == NULL)
    {
        printf("\033[%sm","31");
        printf("Ouverture du fichier %s impossible !\n",nomDuFichier);
        printf("\033[%sm","0");
    }
    int p, port = 0;
    int compteur1 = strlen ( argv[2] );
    for(p=0;p<compteur1;p++)
    {
        if (argv[2][p]-'0' >= 0 && argv[2][p]-'0' <= 9)
        {
            port=port+(argv[2][p]-'0')*pow(10,compteur1-(p+1));
        }
        else
        {
            printf("\033[%sm","31");
            printf("\nERREUR : ");
            printf("\033[%sm","0");
            printf("Port incorrect\n\n");
            if (fichier != NULL)
            {
                fprintf(fichier,"ERREUR : Port incorrect\n");
            }
            exit(0);
        }
    }
    struct sockaddr_in serv_addr;
    int sockfd, i, slen=sizeof(serv_addr);
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
    {
        err("socket");
    }
    bzero(&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);
    if (inet_aton(argv[1], &serv_addr.sin_addr)==0)
    {
        fprintf(stderr, "inet_aton() failed\n");
        exit(1);
    }
    printf("\nAdresse du serveur distant : %s:%d\n",argv[1],port);
    if (fichier != NULL)
    {
        fprintf(fichier,"Adresse du serveur distant : %s:%d\n",argv[1],port);
    }
    if (argc==4)
    {
        int longueur1 = strlen ( argv[3] );
        if (longueur1 >= 1024)
        {
            printf("\033[%sm","31");
            printf("\nERREUR : ");
            printf("\033[%sm","0");
            printf("Taille du paquet incorrecte\n\n");
            if (fichier != NULL)
            {
                fprintf(fichier,"ERREUR : Taille du paquet incorrecte\n");
            }
            exit(0);
        }
        char *commandeAT1 = malloc((( longueur1 + 2 ) * sizeof ( char )));
        int z ;
        for (z = 0 ; z < longueur1 ; z ++ )
        {
            commandeAT1[z] = argv[3][z];
        }
        commandeAT1[longueur1]='\r';
        commandeAT1[longueur1+1]='\0';
        printf("Commande AT : ");
        printf("\033[%sm","31");
        printf("%s\n\n",commandeAT1);
        printf("\033[%sm","0");
        if (fichier != NULL)
        {
            fprintf(fichier,"Commande AT : %s\n",commandeAT1);
        }
        if (sendto(sockfd, commandeAT1, longueur1+2, 0, (struct sockaddr*)&serv_addr, slen)==-1)
        {
            if (fichier != NULL)
            {
                fprintf(fichier,"ERREUR : Envoi du paquet UDP impossible\n");
            }
            err("sendto()");
        }
        free(commandeAT1);
    }
    else
    {
        int longueur1 = strlen ( argv[3] );
        if (longueur1 >= 1024)
        {
            printf("\033[%sm","31");
            printf("ERREUR : ");
            printf("\033[%sm","0");
            printf("Taille du paquet incorrecte\n\n");
            if (fichier != NULL)
            {
                fprintf(fichier,"ERREUR : Taille du paquet incorrecte\n");
            }
            exit(0);
        }
        char *commandeAT1 = malloc((( longueur1 + 2 ) * sizeof ( char )));
        int z ;
        for (z = 0 ; z < longueur1 ; z ++ )
        {
            commandeAT1[z] = argv[3][z];
        }
        commandeAT1[longueur1]='\r';
        commandeAT1[longueur1+1]='\0';
        int longueur2 = strlen ( argv[5] );
        if (longueur2 >= 1024)
        {
            printf("\033[%sm","31");
            printf("ERREUR : ");
            printf("\033[%sm","0");
            printf("Taille du paquet incorrecte\n\n");
            if (fichier != NULL)
            {
                fprintf(fichier,"ERREUR : Taille du paquet incorrecte\n");
            }
            exit(0);
        }
        char *commandeAT2 = malloc((( longueur2 + 2 ) * sizeof ( char )));
        int n ;
        for (n = 0 ; n < longueur2 ; n ++ )
        {
            commandeAT2[n] = argv[5][n];
        }
        commandeAT2[longueur2]='\r';
        commandeAT2[longueur2+1]='\0';
        int delaiSecondes = 0;
        int k ;
        int compteur2 = strlen ( argv[4] );
        for(k=0;k<compteur2;k++)
        {
            if (argv[4][k]-'0' >= 0 && argv[4][k]-'0' <= 9)
            {
                delaiSecondes=delaiSecondes+(argv[4][k]-'0')*pow(10,compteur2-(k+1));
            }
            else
            {
                printf("\033[%sm","31");
                printf("\nERREUR : ");
                printf("\033[%sm","0");
                printf("Delai incorrect\n\n");
                if (fichier != NULL)
                {
                    fprintf(fichier,"ERREUR : Delai incorrect\n");
                }
                exit(0);
            }
        }
        printf("Commande AT N°1 :");
        printf("\033[%sm","31");
        printf(" %s\n",commandeAT1);
        printf("\033[%sm","0");
        printf("Commande AT N°2 :");
        printf("\033[%sm","31");
        printf(" %s\n",commandeAT2);
        printf("\033[%sm","0");
        printf("Delai : %d secondes\n\n",delaiSecondes);
        if (fichier != NULL)
        {
            fprintf(fichier,"Commande AT N°1 : %s\n",commandeAT1);
            fprintf(fichier,"Commande AT N°2 : %s\n",commandeAT2);
            fprintf(fichier,"Delai : %d secondes\n",delaiSecondes);
        }
        if (sendto(sockfd, commandeAT1, longueur1+2, 0, (struct sockaddr*)&serv_addr, slen)==-1)
        {
            if (fichier != NULL)
            {
                fprintf(fichier,"ERREUR : Envoi du paquet UDP impossible\n");
            }
            err("sendto()");
        }
        delai(delaiSecondes);
        if (sendto(sockfd, commandeAT2, longueur2+2, 0, (struct sockaddr*)&serv_addr, slen)==-1)
        {
            if (fichier != NULL)
            {
                fprintf(fichier,"ERREUR : Envoi du paquet UDP impossible\n");
            }
            err("sendto()");
        }
        free(commandeAT1);
        free(commandeAT2);
    }
    close(sockfd);
    fclose(fichier);
    return 0;
}

// Définition des fonctions
void err(char *s)
{
    perror(s);
    exit(1);
}

void delai ( int delaiSecondes )
{
    while ( clock()/CLOCKS_PER_SEC < delaiSecondes)
    {
    }
}

Le contenu du paquet UDP et l’adresse du serveur sont saisis par l’utilisateur au moment de l’exécution du programme. Pas besoin de compiler le code source pour envoyer un nouveau paquet UDP ! Ce dernier programme offre d’autres fonctions comme l’affichage sur la console des différentes informations relatives à l’envoi des paquets UDP comme les codes d’erreur ou  encore la création d’un fichier Notes.txt qui relate tous les paquets UDP envoyés par l’utilisateur ainsi que les adresses IP et les ports associés. La taille des paquets UDP est également optimisée. En d’autres termes ce programme offre plus de souplesse à l’utilisateur.

Comment utiliser ce programme ?

Ce programme utilise les paramètres de la ligne de commande. Voici la syntaxe exacte du programme. Le port doit être un nombre entier compris entre 1024 et 65535.

./Client_UDP_2 <Adresse IP du serveur> <Port> <Commande AT>
Console UNIX

Pour rappel sous UNIX il suffit de tapper le nom du programme précédé de ./ dans une console pour exécuter le programme. Le programme doit bien évidemment se trouver dans le répertoire courant. Par exemple il suffit d’exécuter la commande suivante dans une console UNIX pour faire décoller le drone. Ni plus ni moins !

./Client_UDP_2 192.168.1.1 5556 AT*REF=101,290718208
Console UNIX

Cette commande ordonne au drone de décoller. Bien évidemment vous devez vous connectez au réseau WiFi créé par l’AR Drone avant d’exécuter cette commande UNIX ! Sans quoi votre drone ne bougera pas d’une hélice.

Pourquoi n’y a t-il pas de caractère r à la fin de la commande AT dans cette commande UNIX ?

Ce dernier programme rajoute automatiquement la caractère r à la fin des commandes AT que vous envoyez au drone. Vous n’avez donc à vous soucier de ce caractère lorsque vous saisissez vos commandes AT dans les paramètres de la ligne de commande.

Voici un autre exemple à utiliser avec modération puisqu’il s’agit de l’arrêt total des moteurs du drone. Même si celui-ci est en plein vol ! De manière plus détaillée cette commande envoie un signal d’urgence au drone. Voici la commande à exécuter.

./Client_UDP_2 192.168.1.1 5556 AT*REF=102,290717696
Console UNIX

La liste complète des commandes AT avec lesquelles l’AR Drone est compatible figure dans le guide du développeur édité (en anglais) par Parrot. Vous pouvez télécharger ce document .PDF en suivant ce lien. Vous en aurez besoin si vous envisagez de créer un logiciel permettant de commander le drone.

Enfin vous pouvez utiliser ce programme pour envoyer au drone deux commandes AT séparées chacune d’un délai. Aucune modifications du programme n’est nécessaire ! Il suffit d’utiliser une syntaxe différentes lors de l’exécution du programme. Voici la syntaxe à suivre pour envoyer deux commandes.

./Client_UDP_2 <Adresse IP du serveur> <Port> <Commande AT N°1> <Delai> <Commande AT N°2>
Console UNIX

Lors de l’exécution de cette dernière commande le programme envoi la première commande AT au drone puis s’interrompt pendant le laps de temps indiqué par l’utilisateur avant d’envoyer la seconde commande AT. Le délai est exprimé en secondes.

Voici un exemple. La commande UNIX suivante ordonne au drone de décoller puis s’interrompt pendant 10 secondes avant d’ordonner au drone d’atterrir.

./Client_UDP_2 192.168.1.1 5556 AT*REF=101,290718208 10 AT*REF=102,290717696
Console UNIX

Pour finir voici une vidéo dans laquelle nous envoyons quelques commandes AT à un AR Drone 2.0 en utilisant les programmes ci-dessus. Nous avons filmé cette vidéo dans le forum de Télécom SudParis. N’hésitez pas à nous faire part de vos suggestions les plus diverses dans les commentaires !

Voici un résumé des commandes AT que nous avons utilisé dans les exemples de cet article.

  • AT*REF=101,290718208 – Décollage du drone
  • AT*REF=102,290717696 – Atterrissage du drone
  • AT*REF=102,290717696 – Envoi d’un signal d’urgence au drone – Arrêt des moteurs

Cet article constitue le premier élément d’une série de tutoriels dédiés à l’AR Drone. Nous publierons ces tutoriels au fil des semaines à venir. Dans nos prochains articles nous aborderons d’autres commandes AT afin notamment de déplacer le drone durant sa phase de vol. Nous verrons également comment recevoir et afficher à l’écran toutes informations relatives au vol telles que l’autonomie de la batterie ou l’altitude du drone. Si vous êtes intéressés par la programmation de l’AR Drone n’hésitez pas à revenir régulièrement sur ce blog !

Comment commander l’AR Drone depuis un PC Windows ?

Vous avez été plusieurs à me demander comment porter ce programme sur Windows. Nous travaillons actuellement à une version Windows de ce programme. Nous publierons celle-ci sur GitHub et sur ce blog dès qu’elle sera finalisée ! Un peu de patience …

Je souhaiterais rajouter une petite note à propos du protocole Telnet. Un petit bonus. En effet l’AR Drone n’est autre qu’un système embarqué qui fonctionne sous une distribution Linux. Vous pouvez donc vous connecter à l’AR Drone depuis votre ordinateur en utilisant Telnet. Ce faisant vous pouvez exécuter quelques commandes. Vous pouvez notamment redémarrer le drone. Cette fonctionnalité est bien plus pratique qu’il n’y paraît ! En effet vous aurez remarqué lors de vos expérimentations que le drone a tendance a entrer dans un mode qui n’autorise aucune opération lorsqu’on le malmène. Ce qui nécessite de débrancher sa batterie pour le redémarrer … À moins d’utiliser Telnet !

Voici les commandes à exécuter pour redémarrer le drone :

$ telnet 192.168.1.1 //Connexion au drone
$ reboot
Redémarrage du drone

Et voilà ! Ca paraît simple mais c’est très utile. À bientôt !

Vous êtes intéressés par l’AR Drone ? Soutenez nous en achetant un AR Drone sur Amazon ! Merci !

Pierre Pelé
Paul-Darius Sarmadi

Mise à jour 25/02/2014: Ajout d’une note à propos de la version Windows.
Mise à jour 13/03/2014: Ajout d’une note à propos du protocole Telnet.