1. Le projet Geeros ou « Mais pourquoi mettre deux CPU dans un robot ? »
Avant de rentrer dans le vif du sujet, commençons par répondre à la question qui vous taraude depuis que vous avez lu le titre de cet article : mais pourquoi donc connecter un Arduino avec une carte Mini2440 ? Pourquoi utiliser les deux et pas seulement l’un ou l’autre ?
La réponse à cette question est liée au contexte de cette application. En l’occurrence, il s’agit du projet GEEROS, dont le nom signifie Gyropode Educatif Et Robot Open Source (http://geeros.3sigma.fr). Le mot « éducatif » peut faire penser à un objet dédié aux enfants de 0 à 5 ans, mais ici ce n’est pas le cas. Ce sont les étudiants de niveau lycée et supérieur (ainsi que les hackers, au sens noble du terme) qui sont visés et ce point est primordial dans les caractéristiques de l’application.
En effet, d'une part les cartes Arduino sont très utilisées dans le monde de l’éducation, mais d'autre part, il est dommage de se limiter à cette plateforme alors que les possibilités de développements complémentaires apportées par une carte « Linux embarqué » sont immenses. Enfin, pour intéresser, voire captiver les étudiants, il est important de soigner le côté ludique.
Par ailleurs, l’ouverture est essentielle pour l’apprentissage ou le hacking du robot :
- Il doit être possible de voir comment un objet technologique a été conçu, aussi bien d’un point de vue matériel que logiciel ;
- Il doit être également possible de modifier le code de tous les applicatifs pour réellement mettre les « mains dans le cambouis » et apporter ses propres modifications ;
- Même le matériel doit être ouvert pour que chacun puisse modifier ou ajouter des capteurs, des actionneurs, des modules de communication… ou même tout changer !
Nous avons essayé de répondre le mieux possible à ces besoins en concevant un robot gyropode possédant les caractéristiques suivantes :
- Il est ludique : il tient tout seul en équilibre sur deux roues (à la manière d’un Segway), ce qui est assez spectaculaire et pour certains, magique ; il peut être piloté à distance via un smartphone ou un ordinateur et transmettre à ce dernier en retour des données et de la vidéo en temps réel ;
- Le maintien en équilibre et la gestion de différents capteurs « périphériques » (suivi de ligne, détection d’obstacles, mesure de courant, …) sont gérés par une carte Arduino, ce qui permet aux étudiants de compléter leur apprentissage de ce type de matériel sur un cas très concret ;
- L’aspect « transmission de données » impose l’utilisation d’une carte Linux embarqué assez puissante (la Mini2440) avec une liaison WiFi pour transmettre la vidéo en temps réel. L’Arduino n’est en effet pas assez puissant pour ça. La Mini2440 permet par ailleurs d’augmenter l’intérêt pédagogique avec les possibilités non-exhaustives suivantes : travail sur un système Linux embarqué dans une application concrète, développement d’applications web permettant d’interfacer le robot avec un smartphone ou un ordinateur, traitement d’image sur le flux vidéo (reconnaissance de couleurs, de forme,…) ;
- L’ouverture apparaît de façon évidente : il est possible de brancher des capteurs, actionneurs,… supplémentaires sur la carte Arduino, il est possible de télécharger de nouveaux programmes sur cette dernière pour modifier l’asservissement de verticalité ou toute autre fonctionnalité. L’applicatif chargé sur la Mini2440 est également totalement open source. Tout est donc faisable, depuis la simple modification de l’existant jusqu’au chargement de sa version de Linux préférée (voir Open Silicium n°1). Il sera même possible de faire l'acquisition d'une partie des éléments du robot (par exemple, le châssis motorisé, ou le châssis + l'Arduino, ou n'importe quelle autre combinaison) et d'utiliser ses propres composants pour le reste.
Vous commencez sans doute maintenant à saisir l’intérêt d’une communication entre un Arduino et une Mini2440. En effet, dans un fonctionnement normal du gyropode piloté par un smartphone ou un ordinateur :
- L’utilisateur envoie ses commandes de trajectoire et vitesse (joystick virtuel sur un ordinateur ou informations issues de l'accéléromètre d'un smartphone) via une liaison WiFi avec la Mini2440 ;
- Cette dernière traite éventuellement ces commandes et les transmet au gyropode via une liaison série filaire (les deux cartes sont séparées de quelques centimètres) ;
- Le gyropode se soumet à ces commandes et renvoie (toujours en liaison série) des informations télémétriques à la Mini2440 ;
- Celle-ci les traite et/ou les stocke en interne éventuellement et les renvoie en WiFi à l’utilisateur.
Bien sûr, on aurait pu imaginer une liaison directe en WiFi ou en Bluetooth avec l’Arduino (moyennant l’utilisation d’un shield adéquat), mais puisque nous avons une Mini2440 embarquée dans le robot, pourquoi ne pas en profiter pleinement ? Par ailleurs, l’interface utilisateur de commande est une application web qui est exécutée par le serveur web embarqué dans la Mini2440. Son développement a été réalisé en utilisant des outils standards de développement d’applications web. Un équivalent sur l’Arduino aurait été beaucoup plus fastidieux à développer. De plus, l’Arduino est déjà très chargé avec le cœur de l’asservissement du gyropode. L’ajout de fonctions de communication trop gourmandes le mettrait dans l’incapacité de réaliser sa mission principale.
Enfin, ce type d’architecture à base d’un ordinateur embarqué central intégrant des données provenant de périphériques à base de microcontrôleurs déportés, nous semble être amené à se développer avec l’Internet des objets. On peut en effet facilement imaginer divers capteurs disséminés dans un lieu donné (pour mesurer la température, l’humidité, des taux de gaz, détecter une présence,…) gérés par des microcontrôleurs qui enverraient leurs données via une liaison série radio (moins chère, moins gourmande en énergie et de plus grande portée que le WiFi) à un ordinateur central à bas coût (type Mini2440, Raspberry Pi ou autre). Ce dernier (connecté à votre « box ») se chargerait d’agréger les données et de les présenter sous une forme ergonomique à l’utilisateur via une application web, consultable aussi bien en local que depuis l’autre bout du monde.
Une petite remarque pour ceux qui regretteraient l’utilisation d’un Arduino et de son « langage » (qui n'en est d'ailleurs pas un) dans le milieu de l’éducation, parce que trop restrictif ou pas assez formateur. Là encore, c’est très ouvert : on peut programmer un Arduino avec l'IDE et les bibliothèques éponymes, mais on peut également très bien utiliser du C beaucoup plus proche de la machine. Le plus important au final pour l’utilisateur, c’est d’avoir le choix. Ça permet ainsi aux étudiants de démarrer en douceur sans les effrayer avant de passer au C pur et dur et à la conception de leur propre carte microcontrôleur dans la suite de leur cursus. Après tout, beaucoup de champions cyclistes ont débuté avec les petites roues arrières stabilisatrices...
2. Le matériel
Entrons maintenant dans les détails techniques…
La carte microcontrôleur utilisée est une platine Romeo de chez DFRobot (http://www.dfrobot.com/index.php?route=product/product&filter_name=romeo&product_id=656).
Fig. 1 : La carte Romeo, compatible Arduino Uno
C'est une carte compatible Arduino qui se programme comme un Arduino Uno. Nous ne reviendrons pas ici sur les plateformes Arduino, celles-ci ayant été abordées dans le détail dans Open Silicium n°2. Cependant, pour ceux qui auraient loupé ce numéro et qui ne connaissent pas cette plateforme, Arduino est un projet open hardware qui comprend non seulement du matériel, mais aussi du logiciel. Le matériel se présente sous la forme de cartes faciles à utiliser à base de microcontrôleurs AVR (Atmega 8, Atmega 168, Atmega 328, Atmega 1280,...) en fonction des modèles. Les programmes s'écrivent typiquement en utilisant l'environnement de développement Arduino, en C/C++ agrémenté de bibliothèques spécifiques (ce qui conduit certains à utiliser la dénomination assez largement abusive « langage Arduino »). La programmation du microcontrôleur se fait simplement via l'ordinateur (connecté à la carte en USB) grâce au bootloader pré-chargé dans la mémoire flash. Les cartes de base ne comprenant que le microcontrôleur, il est possible d'empiler des cartes filles, appelées shields, pour ajouter des fonctionnalités spécialisées (Ethernet, WiFi, Xbee, Bluetooth, RFID, contrôle moteur,...). Vous trouverez plus de détails en français sur la page http://www.arduino.cc/fr/.
L'avantage de la carte Romeo par rapport à un Arduino officiel + shields est qu'elle intègre de façon très compacte deux ponts en H permettant de piloter via des signaux PWM les deux moteurs à courant continu du robot, à vitesse variable et dans les deux sens. Le courant maximum fourni par ce driver est de 2A par voie, ce qui convient parfaitement aux moteurs utilisés dans le gyropode. Elle propose également un accès aux entrées-sorties du microcontrôleur via différents types de broche (mâle, femelle et groupe 5V / Masse / signal), ainsi qu'un connecteur permettant de brancher directement un module Bluetooth ou APC220. Enfin, la gestion des alimentations est très bien pensée puisqu'il est possible de l'alimenter suivant diverses méthodes :
- par le connecteur USB relié à l'ordinateur,
- par une prise Jack,
- par la source d'alimentation des moteurs (configuration via un cavalier).
C'est cette dernière méthode que nous utiliserons.
Les caractéristiques détaillées sont les suivantes :
- microcontrôleur AVR Atmega 328 (8 bits, 32 Ko de mémoire flash)
- Bootloader Arduino Uno
- Compatible avec le brochage de l'Arduino Uno
- 14 entrées/sorties digitales
- 6 voies PWM
- 8 entrées analogiques 10 bits
- Connecteur ICSP pour programmation directe sans passer par le bootloader
- Interface série TTL
- Support de AREF
- Connecteur intégré pour module radio APC220 et module Bluetooth DFRobot
- Interface I2C
- Deux ponts en H, courant max 2A par voie
- 5 boutons
- Dimensions : 90x80x14 mm
- Poids : 60 g
Notons que cette carte supporte également la plupart des shields conçus pour les Arduino.
Les entrées/sorties utilisées par le robot sur cette carte sont les suivantes :
- 2 PWM et 2 sorties digitales pour le pilotage à vitesse variable et dans les deux sens des deux moteurs ;
- 4 entrées digitales (dont deux interruptions) pour les codeurs incrémentaux utilisés pour mesurer la vitesse de rotation des moteurs (nécessaire pour l'asservissement de vitesse du robot) ;
- 1 entrée analogique pour le gyroscope mesurant la vitesse de rotation du gyropode par rapport à la verticale (la vitesse de chute, en quelque sorte) ;
- 1 entrée analogique pour l'accéléromètre orienté horizontalement permettant d'estimer (via filtrage complémentaire avec le gyroscope) l’angle d'inclinaison du gyropode par rapport à la verticale ;
- 1 entrée analogique pour la mesure de la tension d'alimentation, afin d'estimer le niveau de charge restante de la batterie ;
- 1 liaison série pour la communication avec la carte Mini2440.
Il reste ainsi 6 entrées/sorties digitales (dont 4 PWM) et 5 entrées analogiques pour connecter d'autres capteurs (lumière pour suivi de ligne, infrarouge ou ultrason pour la détection d'obstacles, courant pour optimiser la consommation énergétique, ...).
La carte Mini2440 a quant à elle déjà été présentée dans Open Silicium n°1. C'est une carte à base de processeur ARM9, agrémentée d'un certain nombre de périphériques, permettant de faire de l'embarqué pour un coût abordable. Pour mémoire, voici ses caractéristiques :
- CPU : Samsung S3C2440A 405 MHz
- SDRAM : 64 Mo, bus de données 32 bits, 100 MHz
- Flash : de 128 à 1024 Mo (en fonction des modèles) de flash Nand, 2 Mo de flash Nor, BIOS installé
- Interfaces et ressources :
- - Ethernet 10/100M RJ-45(DM9000)
- - 3 x ports série (1 RS232 avec CTS et RTS)
- - 1 x USB Hôte
- - 1 x USB Esclave Type B
- - 1 x interface SD Card
- - 2 voies I2C
- - 2 voies SPI
- - Sortie stéréo audio, entrée microphone, 1 microphone sur la carte
- - Connecteur JTAG 10 broches
- - 4 LED utilisateur
- - 6 boutons utilisateur
- - Buzzer PWM
- - Potentiomètre connecté sur une entrée analogique
- - AT24C08 NVRAM pour test I2C test ou sauvegarde de paramètres
- - Interface caméra CMOS 20 broches
- - Batterie pour l'horloge temps réel
- - Entrée alimentation 5 V avec interrupteur et LED
- Fréquence oscillateur : 12 MHz
- Horloge temps réel interne
- Interfaces d'extension :
- - GPIO 34 broches au pas de 2.0 mm
- - Bus système 40 broches au pas de 2.0 mm
- - Connecteur boutons utilisateur et interruption 8 broches
- - Interface caméra CMOS 20 broches
- OS supportés :
- - Linux 2.6.2, 2.6.32
- - Windows CE.Net 5.0/6.0
- - uC/OS-II
- Dimensions : 100 mm x 100 mm
Dans le cadre de cet article, nous utiliserons le noyau Linux 2.6.32 pré-installé avec BusyBox (http://www.busybox.net). Ce dernier point aura évidemment un impact sur les commandes disponibles sur la carte, moins nombreuses et avec moins d'options que sur le Linux d'une machine de bureau.
3. Connexion de la Mini2440 sur votre réseau local, puis sur votre réseau WiFi
Avant d'aborder la communication entre l'Arduino et la Mini2440, nous allons d'abord configurer cette dernière pour qu'elle puisse communiquer en WiFi avec le monde extérieur (ordinateur ou smartphone). Cela nous permettra de transmettre les consignes de vitesse du gyropode depuis la machine cliente et de recevoir sur cette dernière le flux vidéo capturé par le robot.
Avant toute chose, nous allons configurer la Mini2440 pour qu'elle puisse rejoindre votre réseau local, communiquer via telnet avec notre machine hôte (c'est un peu plus pratique que la console série) et se connecter à Internet. Les étapes à suivre, si vous prenez une Mini2440 juste sortie de la boîte, sont les suivantes :
- Mettez l'interrupteur de la flash en position nor, branchez un câble série femelle – femelle droit entre la carte et votre ordinateur, branchez un câble Ethernet (relié à votre routeur à l'autre bout), branchez l'alimentation et commutez l'interrupteur en position on ;
- Lancez l'utilitaire de communication série : vous vous retrouvez devant le menu d'accueil du bootloader chargé par défaut, Supervivi ;
- Tapez b pour booter la carte ;
- Un petit ifconfig vous permettra de connaître l'adresse IP de la carte. Si vous avez de la chance, elle sera dans la même plage que votre réseau local. Sinon, changez-là comme ceci (adaptez bien sûr les adresses à ce qui vous convient) :
ifconfig eth0 192.168.0.199 netmask 255.255.255.0 up
route add default gw 192.168.0.1
- Éditez le fichier /etc/eth0-setting pour adapter les adresses IP, Gateway et DNS à votre réseau :
IP=192.168.0.199
Mask=255.255.255.0
Gateway=192.168.0.1
DNS=192.168.0.1
MAC=08:90:90:90:90:90
- Éteignez proprement la carte (poweroff), mettez le commutateur sur off et l'interrupteur de la flash sur nand (pour booter automatiquement sans s'arrêter sur le menu de Supervivi) ;
-Vous pouvez désormais rallumer la carte qui va alors démarrer avec les bonnes adresses IP, etc., vous permettant de communiquer directement en telnet (login root, pas de mot de passe). Vous pouvez également accéder à Internet depuis la Mini2440, pour peu que le routeur de votre réseau local y ait accès et que vous ayez au préalable sécurisé un minimum l'accès à la carte en ajoutant un mot de passe au compte root avec la commande passwd.
Notez que la dernière ligne du fichier /etc/eth0-setting ne sera pas prise en compte, ce qui serait pourtant bien utile, car toutes les Mini2440 possédant la même adresse MAC, cela pose des problèmes si on utilise plusieurs cartes en DHCP. Dans ce cas de figure, la solution consiste à installer U-Boot (voir Open Silicium n°1), ce qui permet ensuite de changer l'adresse MAC.
La Mini2440 étant embarquée dans notre robot mobile, on la voit mal rester branchée à un routeur via un câble Ethernet. Nous allons donc maintenant lui permettre de rejoindre un réseau WiFi.
Avant de décrire les différentes étapes, vérifions tout d’abord si les outils WiFi sont installés sur votre carte (a priori non). Exécutez la commande :
scan-wifi
Si vous avez en retour :
-sh: scan-wifi: not found
cela signifie que vous devez installer le kit WiFi, http://arm9download.cncncn.com/download/usb-wifi-kits-for-mini2440-linux-2.6.32.2-20100728.tar.gz. Vérifiez avant que votre version de Linux est bien linux-2.6.32.x :
[root@FriendlyARM /]# cat /proc/version
Linux version 2.6.32.2-FriendlyARM (root@localhost.localdomain) (gcc version 4.4.3 (ctng-1.6.1) ) #18 Wed Apr 13 20:06:31 HKT 2011
Vous allez télécharger le kit WiFi sur la Mini2440, au niveau de la racine. Décompressez ensuite le fichier avec la commande suivante :
tar xvzf usb-wifi-kits-for-mini2440-linux-2.6.32.2-20100728.tar.gz
Avant de ré-exécuter la commande scan-wifi, redémarrez la Mini2440 et connectez une clé USB WiFi sur un hub, lui-même connecté sur le seul port hôte USB de la Mini2440 (le hub est nécessaire, car nous aurons besoin d'un autre port USB pour brancher la webcam). J'ai choisi une TP-LINK TL-WN722N, validée par la communauté (merci au forum de FriendlyArm !). Cette clé s'avère à l'usage être une très bonne référence, en particulier grâce à son antenne externe.
Notez que le kit WiFi ne permet pas de supporter toutes les clés du commerce. En fonction de celle utilisée, il peut être nécessaire de charger au préalable une image de son firmware. La page suivante vous sera alors d'un grand secours : http://linuxwireless.org/en/users/Devices/USB.
Vous pouvez maintenant exécuter les commandes suivantes :
- Recherche des réseaux WiFi à proximité :
scan-wifi
- Connexion en WiFi au réseau de votre choix (en mode WPA2 dans l’exemple ci-dessous) :
start-wifi wpa2 essid_de_votre_reseau mot_de_passe_de_votre_reseau
Après cette dernière commande, l’adresse IP de la carte est assignée automatiquement via DHCP. Si vous souhaitez la définir vous-même, utilisez la commande suivante :
ifconfig wlan0 192.168.0.199
Remarque importante : la carte Mini2440 est alors connectée à votre réseau WiFi en mode « managed », c’est-à-dire comme (a priori) vos autres clients WiFi. Deux autres modes pourraient être utiles :
- Ad-hoc : dans ce cas, la Mini2440 servirait de point d’accès et vous pourriez y connecter directement un ordinateur ou un iPhone, mais pas un smartphone Android, cet OS ne supportant pas (à l’heure de l’écriture de cet article, espérons que ça change dans le futur) les réseaux Ad-hoc ;
- Master : ici, c’est la Mini2440 dans sa configuration de base qui ne supporte pas ce mode.
La Mini2440 est désormais en mesure de se connecter en WiFi avec le monde extérieur, nous pourrons donc lui transmettre les consignes de vitesse du gyropode via un ordinateur ou un smartphone et elle pourra renvoyer des mesures et les images de la webcam embarquée.
4. Communication série entre la Mini2440 et l'Arduino
Nous avons donc deux matériels possédant respectivement 3 et 1 liaison(s) série, sachant que l'unique liaison série de l'Arduino sert également pour la programmation via le bootloader. Ce n'est cependant pas gênant dans la mesure où en mode programmation, la communication avec la Mini2440 n'est pas utile.
Remarque importante : le niveau de tension de la liaison série sur la Mini2440 est de 3.3 V, alors qu’elle est de 5 V sur l’Arduino. Pour faire les choses proprement, il est conseillé d’adapter le niveau de tension pour le sens de transmission Arduino -> Mini2440. Cependant, la Mini2440 est réputée robuste vis-à-vis de cette différence de tension (ce que nous pouvons confirmer pour avoir essayé).
Intéressons-nous maintenant aux aspects logiciels en abordant les points suivants :
- Récupération sur la Mini2440 des données de pilotage de l’utilisateur,
- Transmission des données à l’Arduino sur la liaison série,
- Récupération des données sur l’Arduino.
Les deux derniers points nous permettrons également de détailler le protocole de communication utilisé pour l’échange des données.
Étant partisan de l’adage « les solutions les plus simples sont souvent les meilleures » et assez peu enclin à réinventer la roue, j’ai utilisé les briques de base disponibles en standard sur la Mini2440 pour développer son applicatif de communication. Je suis notamment resté sur la version du noyau Linux pré-installé sur la carte lors de son achat, sans écrire la moindre ligne de code C : la puissance du scripting bash est largement suffisante. Cela permet également d’éviter aux enseignants d’avoir à installer une chaîne de compilation croisée pour modifier l’application fournie en standard avec le gyropode. Mais rien n'empêche les hackers de le faire.
Par ailleurs, il nous a semblé important de permettre à l'utilisateur final de pouvoir utiliser le matériel (et l'OS) de son choix pour utiliser l'application cliente (celle permettant de piloter le gyropode). Cet objectif de portabilité maximale nous a conduit à créer une application web utilisant le couple HTML/JavaScript. En effet, tous les matériels susceptibles d'être utilisés pour piloter le robot possèdent un navigateur. La situation sur smartphone n'est cependant pas encore idéale, car l'accès aux mesures de l'accéléromètre, permettant d'avoir une « expérience utilisateur » optimale, n'est pas disponible avec tous les couples navigateurs/OS (en particulier si votre téléphone n'est pas récent). Certains (comme Firefox sur Android, par exemple) le proposent, d'autres non. Mais il est probable que dans un avenir proche, tous les navigateurs mobiles supporteront l'évènement DeviceMotion spécifié par le W3C.
4.1. Interface client web
La conception d'une application web servie par la Mini2440 nécessite évidemment un serveur web sur cette dernière. Nous allons utiliser le serveur pré-installé, en l'occurrence Boa, dont les principales qualités sont la rapidité et la faible empreinte mémoire. Bref, le serveur idéal pour faire de l'embarqué.
Les interfaces de pilotage sur smartphone ou sur ordinateur sont différentes, mais ont un principe de fonctionnement identique. Sur ordinateur, nous avons utilisé la bibliothèque JavaScript « Raphael » (http://raphaeljs.com) pour concevoir un joystick virtuel permettant de piloter la vitesse et l’orientation du gyropode à partir d’un navigateur web. Les détails de l’implémentation dépassent le cadre de cet article. Sur smartphone, sous condition, il est possible de récupérer les informations de l’accéléromètre du téléphone directement dans son navigateur et ceci avec un minimum de programmation. Il suffit alors d’incliner le smartphone vers l’avant pour avancer, vers l’arrière pour reculer, à gauche ou à droite pour tourner. Voici le code de la page de pilotage, http://192.168.0.199/geer.html :
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Geeros</title>
<script type="text/javascript" src="ajaxsenddiffmot.js"></script>
</head>
<body>
<!-- Affichage du flux vidéo dans un iframe -->
<iframe src="http://192.168.0.199:8080/stream_simple.html" width="640" height="480" frameborder="0">
<p>Your browser does not support iframes.</p>
</iframe>
<br />
<!-- Affichage des mesures accélérométriques et des données transmises au robot (pour debug) -->
<div style="position:absolute;top:300px;left:700px; padding-right: 0px; width: 200px;">
x: <span id="xAxis"></span><br />
y: <span id="yAxis"></span><br />
z: <span id="zAxis"></span><br />
<span id="display"></span><br />
</div>
<!-- Script principal de lecture des données accélérométriques et de transformation en consignes de vitesse -->
<script type="text/javascript">
var vref_calc=0.;
var psidotref_calc=0.;
var vref=0.;
var psidotref=0.;
var started = false;
//Accelerometer Javascript example by @karlwestin, Nerd Communications, Berlin
// Firefox, works on desktop too
window.addEventListener("MozOrientation", updateText, true);
// Mobile Safari
window.addEventListener("devicemotion", updateText, true);
function updateText(e) {
if(!!e.x)
// Firefox
updateCounter(e.x*10, e.y*10, e.z*10);
else if (!!e.accelerationIncludingGravity)
// Mobile Safari
updateCounter(e.accelerationIncludingGravity.x, e.accelerationIncludingGravity.y, e.accelerationIncludingGravity.z);
else
alert("you don't really support devicemotion, do you?");
}
function updateCounter(x, y, z) {
document.getElementById("xAxis").innerHTML = x;
document.getElementById("yAxis").innerHTML = y;
document.getElementById("zAxis").innerHTML = z;
// La commande par smartphone est activée en mettant ce dernier en position verticale
if (z<0.2) {
started = true;
}
if (started) {
if (z>=0) {
// On sature la vitesse max à 0.5 m/s en marche avant...
vref_calc = Math.max(z-0.5,0);
}
else {
// ... et à 0.2 m/s en marche arrière
vref_calc = Math.min(z+0.2,0);
}
if (y>=0) {
// On empêche la rotation si le smartphone est légèrement incliné sur la gauche ou sur la droite
if (y<0.05) {
psidotref_calc = 0;
}
else {
psidotref_calc = -3.*(y-0.05);
}
}
else {
if (y>-0.05) {
psidotref_calc = 0;
}
else {
psidotref_calc = -3.*(y+0.05);
}
}
}
// On fige la longueur de la partie décimale
vref = vref_calc.toFixed(3);
psidotref = psidotref_calc.toFixed(3);
document.getElementById("display").innerHTML = "vref: " + vref + " psidotref: " + psidotref;
}
<!-- Appel de la fonction JavaScript permettant la transmission continue des consignes vers la Mini2440 -->
ajaxsenddiffmot();
</script>
</body>
</html>
Les points les plus importants sont les suivants :
- Un listener permet, via la fonction updateText, de récupérer les accélérations du smartphone ;
- Ces informations sont envoyées à la fonction updateCounter qui les transforme en consignes de vitesse longitudinale et d'orientation, stockées dans les variables globales vref et psidotref ;
- La fonction ajaxsenddiffmot (définie dans ajaxsenddiffmot.js) est enfin appelée.
Même si vous n'êtes pas très perspicace, vous aurez compris que la suite des opérations utilise la « technologie » Ajax. Voici le code de ajaxsenddiffmot.js :
var xmlhttp;
function ajaxsenddiffmot() {
xmlhttp=GetXmlHttpObjectDiffMot();
if (xmlhttp==null) {
alert ("Browser does not support HTTP Request");
return;
}
// Racine de l'URL sur laquelle les données sont envoyées
var url="http://192.168.0.199/commandes_diffmot.cgi";
// Sécurité: consignes nulles si les vref et psidotref ne sont pas définies
if (vref==undefined) {
vref = 0.;
}
if (psidotref==undefined) {
psidotref = 0.;
}
// Les consignes sont définies avec 2 chiffres décimaux
vref = (eval(vref)).toFixed(2);
psidotref = (eval(psidotref)).toFixed(2);
// Création de la somme de contrôle
var crc = eval(vref) + eval(psidotref);
crc = crc.toFixed(2);
// Envoi des données à la Mini2440
url=url+"?vref="+encodeURIComponent(vref)+"&psidotref="+encodeURIComponent(psidotref)+"&crc="+encodeURIComponent(crc);
// "Cuisine" Ajax
xmlhttp.onreadystatechange=stateChanged;
xmlhttp.open("GET",url,true);
xmlhttp.send(null);
}
function stateChanged() {
if (xmlhttp.readyState==4) {
// Dès la fin de l'envoi des données, nouvel appel de la fonction principale
// Ceci permet un envoi de nouvelles consignes en continu
ajaxsenddiffmot();
}
}
function GetXmlHttpObjectDiffMot() {
if (window.XMLHttpRequest) {
// code for IE7+, Firefox, Chrome, Opera, Safari
return new XMLHttpRequest();
}
if (window.ActiveXObject) {
// code for IE6, IE5
return new ActiveXObject("Microsoft.XMLHTTP");
}
return null;
}
Il est important de noter que ce code est commun à toutes les plateformes, ordinateur ou smartphone. Les points importants sont les suivants :
- Les variables globales vref et psidotref sont lues et formatées avec deux décimales (ce n'est pas nécessaire d'avoir plus de précision) ;
- Une somme de contrôle très simple (addition de ces deux variables) est créée ;
- Ces trois variables (les consignes de vitesse et la somme de contrôle) sont envoyées en paramètre d'une URL spécifique, http://192.168.0.199/commandes_diffmot.cgi (voir ci-dessous) ;
- Lorsque l'opération est terminée, cette fonction ajaxsenddiffmot() est de nouveau appelée. L'envoi des consignes de vitesse au gyropode s'effectue ainsi en continu.
4.2. Transmission série des consignes de la Mini2440 vers l'Arduino
L'élément le plus important de la communication plateforme cliente / Mini2440 réside dans l'envoi des paramètres de consignes de vitesse à l'URL http://192.168.0.199/commandes_diffmot.cgi. Cette dernière pointe donc sur un script CGI qui n'est autre que le script bash suivant :
#!/bin/sh
# Script récupérant les consignes de vitesse et le CRC du client
# En-tête suivi d'une ligne blanche
echo "Content-type: text/html"
echo
QUERY=`echo $QUERY_STRING | sed -e "s/=/='/g" -e "s/&/';/g" -e "s/+/ /g" -e "s/%0d%0a/<BR>/g" -e "s/$/'/" `
eval $QUERY
echo "data:$vref,$psidotref?$crc;" > /dev/ttySAC2
Les deux premières lignes de code sont obligatoires. La 3ème montre toute la puissance de sed, qui nous permet de récupérer les trois paramètres vref, psidotref et crc. Une question légitime serait : « Mais combien faut-il d'heures de pratique, de tests, ... pour écrire une telle commande ? ». Rassurez-vous, votre moteur de recherche préféré est votre ami et vous permet de bénéficier du savoir et de l'expérience de nombreuses personnes, en particulier de celle ayant mis en ligne cette commande magique que nous avons simplement réutilisée.
Enfin, la dernière commande envoie les données sur la liaison série ttySAC2 de la Mini2440. Le protocole de communication est simple :
- Chaque flot de données commence par « data: »,
- Les deux consignes de vitesse sont séparées par une virgule,
- Le CRC est précédé d'un point d'interrogation,
- La ligne est terminée par un point-virgule.
Il est ensuite facile d'extraire les consignes de vitesse du côté de l'Arduino. Ceci est fait dans la fonction readData, qui est exécutée uniquement lorsque le gyropode est vertical (le code de l'Arduino récupère l'information de l'accéléromètre du robot, ce qui lui permet de savoir dans quelle position il se trouve) :
void readData(void) {
int index_x1;
int index_x2;
int index_x3;
int index_x4;
char charx1[7], charx2[7], charx3[7];
float vref_Mini2440_temp, psidotref_Mini2440_temp;
while(started==true) {
// Si une donnée est reçue...
if (Serial.available()>0) {
// Horodatage
timeLastReceived = millis();
// Etat "timeout" désactivé
timedOut = 0;
// Remplissage de la chaine de stockage des données
response[inCount] = Serial.read();
// Si la longueur max est atteinte ou si le caractère de
// terminaison est rencontré...
if (inCount>=INLENGTH || response[inCount]==INTERMINATOR) {
// On termine proprement la chaine
response[inCount] = '\0';
// On vide le buffer de réception
Serial.flush();
// On sort de la boucle
break;
}
// Si la longueur max n'est pas atteinte et si le caractère
// de terminaison n'est pas rencontré...
else {
// On incrémente le compteur de chaine
inCount++;
}
// Si aucune donnée n'est présente sur la ligne série...
else {
// Si on n'est pas dans l'état "Timeout" mais si on
// n'a pas reçu de données depuis un temps
// supérieur à la valeur de timeout prédéfinie
if ((millis()-timeLastReceived > TIMEOUT*1000) && (timedOut==0)) {
// Timeout déclenché, toutes les consignes sont
// mises à 0 par sécurité
vref_Mini2440 = 0.;
psidotref_Mini2440 = 0.;
timedOut = 1;
}
}
}
// La chaine étant remplie, on remet son compteur à 0
inCount = 0;
// La chaine est convertie vers le type Arduino "String"
rpyStr = (String)response;
// Récupération de la position des caractères de contrôle
// dans la chaine de données
index_x1 = rpyStr.indexOf(':');
index_x2 = rpyStr.indexOf(',');
index_x3 = rpyStr.indexOf('?');
index_x4 = rpyStr.indexOf(';');
// Si tous les caractères sont présents dans la chaine...
if (index_x1!=-1 && index_x2!=-1 && index_x3!=-1 && index_x4!=-1) {
// Extraction des données entre les caractères de contrôle
rpyStr.substring(index_x1 +1, index_x2).toCharArray(charx1,8);
rpyStr.substring(index_x2 +1, index_x3).toCharArray(charx2,8);
rpyStr.substring(index_x3 +1, index_x4).toCharArray(charx3,8);
// Transformation des données en flottant
vref_Mini2440_temp = atof(charx1);
psidotref_Mini2440_temp = atof(charx2);
crc = atof(charx3);
// Reconstitution de la somme de contrôle
crc_check = vref_Mini2440_temp + psidotref_Mini2440_temp;
// Si la somme de contrôle reconstituée correspond
// à la somme de contrôle transmise...
if (round(100*crc)==round(100*crc_check)) {
// Les consignes sont mises à jour avec les nouvelles valeurs lues
vref_Mini2440 = vref_Mini2440_temp;
psidotref_Mini2440 = psidotref_Mini2440_temp;
}
else {
// Erreur sur la somme de contrôle, traitement à effectuer
}
}
else {
// Erreur de réception des données, traitement à effectuer
}
}
Les séquences principales sont les suivantes :
- Lecture des données caractère par caractère et remplissage de la chaîne response jusqu'à sa taille maximale (30 caractères) ou jusqu'au caractère de terminaison (CR) ;
- Gestion d'un timeout : si aucune donnée n'a été reçue pendant une seconde, les consignes sont mises à zéro. Ceci permet, par sécurité, de rendre immobile le robot dans le cas de figure où il perdrait la communication alors qu'il est en train de se déplacer ;
- Extraction des données : on cherche les 4 caractères de séparation (« : », « , », « ? » et « ; ») dans la chaîne response ;
- Si tous ces caractères sont présents, ceux-ci délimitent les 3 sous-chaînes correspondant aux données que nous souhaitons lire. Nous les extrayons dans les variables charx1, charx2 et charx3. Notez que si l'un des caractères de séparation est absent, cela signifie que la chaîne est corrompue. Dans ce cas, on ne fait rien et on attend la prochaine transmission de données valides ;
- Les 3 données sont transformées en flottant, puis la somme de contrôle est reconstituée. Si elle correspond à la somme de contrôle transmise, les consignes sont affectées. Sinon, on ne fait rien et on attend la prochaine transmission de données valides.
Les consignes de vitesse sont ensuite utilisées dans le contrôle du robot. La description de ce dernier sort du cadre de cet article.
4.3. Transmission série des mesures de l'Arduino vers la Mini2440
Il est bien sûr très utile de renvoyer des mesures effectuées par l'Arduino (vitesse moteur, tension batterie, ...) vers la carte Mini2440 pour les traiter sur cette dernière ou les retransmettre au « monde extérieur » (et pas obligatoirement à l'application cliente que nous avons détaillée précédemment). En l'occurrence, le paragraphe suivant présentera une application de télémétrie.
La transmission se fait on ne peut plus simplement côté Arduino, avec par exemple le code suivant :
Serial.print(temps);
Serial.print(",");
// Vitesse moteur droit
Serial.print(omegaRight);
Serial.print(",");
// Vitesse moteur gauche
Serial.print(omegaLeft);
Serial.print(",");
Serial.print(tensionBatterie);
Serial.print("\r");
La méthode de récupération des données côté Mini2440 dépend de la façon dont on les utilise. Dans notre cas, nous les renvoyons directement sur le réseau WiFi pour qu'elles puissent être stockées et affichées en parallèle sur un ordinateur. C'est ce que nous allons voir maintenant.
5. Télémétrie depuis la Mini2440 via liaison WiFi
Notre objectif est d'afficher en temps réel sur un ordinateur les données en provenance de la Mini2440, tout en les stockant en parallèle dans un fichier pour pouvoir les analyser ensuite à tête reposée. On laissera ici de côté le smartphone, car afficher des courbes sur un appareil qui sert de télécommande évoluée n'a pas vraiment de sens.
Le premier réflexe avant de se lancer dans un développement spécifique est de rechercher l'existence d'une telle application. Une contrainte forte est liée à la cadence des calculs effectués sur l'Arduino. Dans notre cas, ceux-ci sont réalisés à la fréquence de 100 Hz, ce qui est nécessaire compte tenu de la dynamique assez rapide du système (il s'agit de contrer le plus rapidement possible la pesanteur, dont le seul but est de faire tomber le gyropode). Potentiellement, en tout cas pendant la phase de mise au point, il peut être nécessaire de récupérer les mesures à cette même cadence pour les analyser le plus finement possible.
Après de longues recherches, l'application ayant donné les meilleurs résultats s'appelle LiveGraph (http://www.live-graph.org). Elle est écrite en Java (donc a priori multiplateforme) ; elle est open source et gratuite. Voici ses caractéristiques :
- Tracé des données en temps réel pendant leur écriture dans un fichier, jusqu'à une fréquence de rafraîchissement de 100 Hz ;
- Affichage très rapide, même pour de gros volumes de données ;
- S'intègre facilement dans une autre application, quel que soit son langage de programmation ;
- API pour les applications développées en Java ;
- Format de stockage des données simple (de type CSV) et ouvert.
En résumé, LiveGraph permet d'afficher sur un graphe des données au fur et à mesure qu'elles sont écrites dans un fichier texte. Les performances sont d'ailleurs assez bluffantes. Reste à écrire dans un fichier texte depuis l'Arduino via la Mini2440...
Pour cela, une fois encore, vive la simplicité ! Et vive Netcat (voir Open Silicium n°5) ! En effet, une fois les données envoyées sur la liaison série de l'Arduino (voir plus haut), il faut sur la Mini2440 une seule ligne :
nc –l –p 5000 -l –e cat /dev/ttySAC2
Cette commande redirige le port série sur lequel sont réceptionnées les données (ttySAC2) vers le port réseau de votre choix (le 5000 en ce qui nous concerne). Vous aurez remarqué la présence en double de l'option -l. Ce n'est pas une erreur, cela permet d'avoir une persistance de l'action spécifiée par l'option -e, même si la machine cliente coupe la communication.
Côté ordinateur client, nous réceptionnons les données et nous les écrivons directement dans un fichier texte comme ceci :
nc 192.168.43.199 5000 > capture.txt
Pour utiliser LiveGraph, vous devez simplement télécharger l'application (voir l'URL de la page du projet ci-dessus) et vous assurer que vous avez installé une machine virtuelle Java (un JRE) sur votre machine. Une fois lancée, vous devez faire pointer l'application sur le fichier texte de capture. Vous verrez alors les données s'afficher en temps réel sur votre machine (Fig. 2).
Fig. 2 : LiveGraph en action. En haut à gauche, la fenêtre « Data file settings » spécifie le fichier contenant les données, ainsi que la fréquence de rafraîchissement. En bas à gauche, la fenêtre « Graph settings » permet de définir les propriétés des graphes (échelle, grille, ...). En bas à droite, la fenêtre « Data series settings » vous permet de choisir les données à afficher et éventuellement de faire des transformations simples. Enfin, la fenêtre « Plot » en haut à droite trace les données correspondant à ce que vous avez choisi et configuré, ici ce sont les vitesses de rotation des deux moteurs.
Notez qu'une seule instance de LiveGraph ne permet pas d'afficher plusieurs fenêtres de tracé, ce qui peut vite devenir gênant. Il y a heureusement une solution de contournement : il est possible de lancer plusieurs instances, chacune avec sa propre configuration. Il vous restera à fermer les 3 fenêtres de paramétrage de chaque instance pour ne conserver que les fenêtres de tracé pour éviter de saturer votre écran et votre barre des tâches.
6. Streaming vidéo depuis la Mini2440 via liaison WiFi
Un dernier point dont nous avons parlé au début : la transmission vidéo. Une fois encore, le plus simple et le plus efficace a consisté à utiliser un outil existant : mjpg-streamer. Il s'agit d'un serveur mjpeg, comme son nom l'indique, permettant d'envoyer un flux d'images sur le port 8080 par défaut. Il a par ailleurs le bon goût de fournir un binaire compilé et validé pour la Mini2440 (http://code.google.com/p/mjpg-streamer-mini2440). Il ne reste plus qu'à brancher une webcam UVC sur un des ports USB du hub connecté à la Mini2440 (comme indiqué plus haut, un hub est nécessaire pour connecter également la clé de transmission WiFi) et à lancer les deux commandes suivantes :
export LD_LIBRARY_PATH="$(pwd)"
./mjpg_streamer -o "output_http.so -w ./www" -i "input_uvc.so -r 640x480 -f 25"
mjpg-streamer fonctionne avec un système de plugins permettant de choisir l'interface d'entrée (webcam UVC, module caméra spécifique à la Mini2440, ...) et l'interface de sortie (serveur HTTP, sauvegarde dans un fichier, ...). Il est possible d'écrire ses propres plugins s'il est nécessaire de faire des actions très personnalisées.
Dans notre exemple, nous utilisons le plugin d'entrée pour les webcams UVC (paramétré ici pour un flux de 25 images par seconde, avec une résolution de 640x480 pixels) et le plugin de sortie sur serveur HTTP, dont la racine est le répertoire www dans l'installation de mjpg-streamer. Si vous connaissez un peu la Mini2440, vous aurez deviné en lisant le mot « webcam » que nous n'avons pas utilisé le module caméra qui s'achète en option avec la carte. Nous l'avons testé sans le retenir, car il fournit des images moins fluides et de moins bonne qualité pour un prix supérieur à celui d'une webcam de milieu de gamme. Cependant, il est supporté par mjpg-streamer, moyennant l'utilisation du plugin adéquat (input_s3c2410.so).
Pour afficher cette vidéo dans la même page web que celle utilisée pour contrôler le robot, il suffit de l'intégrer dans un iframe. Ceci est en effet nécessaire pour faire cohabiter le serveur web (Boa) délivrant l'application de commande sur le port 80 et le serveur mjpg-streamer sur le port 8080. Ceci est implémenté dans la page geer.html présentée plus haut. Pour mémoire, voici l'extrait correspondant :
<!-- Affichage du flux vidéo dans un iframe -->
<iframe src="http://192.168.0.199:8080/stream_simple.html" width="640" height="480" frameborder="0">
<p>Your browser does not support iframes.</p>
</iframe>
Tout ceci fonctionne aussi bien avec un ordinateur qu'avec un smartphone : vous pourrez alors vous placer dans une pièce et piloter le gyropode se trouvant dans une autre pièce en utilisant ce retour de vidéo temps réel pour vous diriger.
7. Conclusion
Une fois établi l'intérêt de faire communiquer un Arduino et une carte Mini2440, nous avons vu dans cet article que l'implémentation de cette communication sur la base d'une liaison série peut se faire assez facilement. La carte Mini2440 est d'ailleurs suffisamment puissante pour permettre d'ajouter d'autres fonctions de communication, notamment avec un ordinateur ou smartphone client, pour envoyer les consignes de vitesse, faire de la télémétrie et récupérer un flux vidéo en temps réel via une liaison WiFi.
On pourrait imaginer aller encore plus loin en permettant au robot d'aller sur Internet, sur le « Cloud » plus précisément, afin de publier son « état » (charge batterie, en train de se déplacer ou non, captures vidéo, ...) ou de bénéficier de ressources de calcul supplémentaires.