Le Hollandais Volant

Projet de Vincent Barousse

 

http://vincentbarousse.wordpress.com/2014/09/02/le-hollandais-volant/

Le Hol

Une envie de toucher à la programmation Android, un Arduino Yun sous la main et quelques bases de modélisme...
Cela donne un projet un peu hors norme : télécommander un bateau pirate Playmobil avec un Smartphone Android.

I Le matériel nécessaire

- 1 bateau pirate Playmobil

https://vincentbarousse.files.wordpress.com/2014/09/img_20140814_122352.jpg

- 1 Arduino Yun

https://vincentbarousse.files.wordpress.com/2014/09/yunparts.png

- 1 périphérique (Smartphone, tablette...) sous Android 4.1 minimum

- matériel divers de modélisme : variateur électronique, servocommande, colle...

- Logiciel Android Studio et Arduino 1.5.7 min (support du Yun)

- 2 mains

II La partie cycle

* Modification du bateau :

Rien de bien sorcier pour qui possède des bases de modélisme :
1 moteur (ici, un speed 500 E 12 v, puisque je l'avais en stock, mais un moteur beaucoup moins puissant suffirait),
1 accu 7,2 V, 1 variateur avec système BEC (fournit une alimentation stabilisée pour les servos à partir des accus de puissance),
1 arbre d'hélice,
cardan, hélice, safran... et un peu de plomb.

Vue de l'hélice et du safran (surdimensionné car je veux pouvoir naviguer à la voile, à vitesse très réduite) :

http://vincentbarousse.files.wordpress.com/2014/09/img_20140903_103224.jpg

Lors de l'équilibrage, cela donne :

https://vincentbarousse.files.wordpress.com/2014/09/img_20140819_130512.jpg

Le seul problème vient du fait que le bateau est très haut, donc nécessite beaucoup de lest.
Mais les sabords sont très bas sur l'eau, d'où fuites potentielles. J'ai donc dû les étancher avec un bon congé de colle.

* Câblage-Electricité

Rien de bien compliqué, si on prend en compte que l'Arduino ne supporte "que" 40 mA par sortie.
Ce qui peut paraître beaucoup par rapport à un Raspberry, mais qui est insuffisant pour commander 1 servo de puissance normale et un variateur comme dans notre cas.
Il faut donc ruser.

D'un côté, le variateur électronique supporte le système BEC (Battery Eliminator Circuit),
qui permet de fournir une alimentation stabilisée pour la partie commande (récepteur, servos...) de 6V à partir de la tension de la batterie principale
(7,2 V-2100 mA dans notre cas).

De l'autre, l'Arduino Yun EXIGE une alimentation 5 V stabilisée, type USB,
avec une consommation maxi de 300 mA.

J'ai donc choisi d'utiliser une batterie de type alimentation auxiliaire de téléphone portable, de 2600 mA,
qui délivre le 5 V nécessaire à la bonne santé de l'Arduino. J'utilise le BEC du variateur pour alimenter le variateur lui-même et le servo de direction.
Les masses sont mises en commun.

Et voilà l'installation :

https://vincentbarousse.files.wordpress.com/2014/09/img_20140903_103129.jpg

III Le code

Tout d'abord, un grand, giga, méga merci à ces 2 sites :

- www.open-electronics.org

- www.android.serverbox.ch

sans lesquels je serais encore en train de m'arracher les cheveux sur les commandes REST.

Le principe est simple : une fois correctement configuré grâce à son interface OpenWrt, le Yun supporte en natif la réception de commandes via une requête web.
Ce sont les commandes REST, et elles prennent la forme :

http://<Arduino_Yún_Address>/arduino /<status>/<pin>/value>>

par exemple :

http://<Arduino_Yún_Address>/arduino/mode/13/output

En clair, si j'écris dans la barre d'adresse de mon navigateur web : http://192.168.1.45/arduino/mode/13/output,
que mon Yun possède l'adresse IP "192.168.1.45", et que bien entendu mon ordinateur est sur le même sous réseau que le Yun (192.168.1.x),
je configure le pin 13 en output.

C'est simple !!! Une fois compris...

Pour le reste, tout est dans le code source Arduino :


/*
* Ce fichier fait partie de ArduinoYunREST
*
* Fichier d'origine :Copyright (C) 2013 - Andreas Rudolf
*
* Modifié par Vincent Barousse - 2014
*
* ArduinoYunREST is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* ArduinoYunREST is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ArduinoYunREST. If not, see <
http://www.gnu.org/licenses/&gt;.
*/
#include <Bridge.h>
#include <YunServer.h>
#include <YunClient.h>
#include <Servo.h>
// Ecoute sur le port par defaut 5555, le serveur web du Yun

YunServer server;

Servo servoDirection;
Servo servoGaz; // en realite un variateur electronique de modelisme, mais gere comme un servo
void setup() {
Serial.begin(9600);

// Demarrage du Bridge

Bridge.begin();

// on attend les connections depuis localhost
server.listenOnLocalhost();
server.begin();

servoGaz.attach(3); // Variateur moteur sur pin 3
servoDirection.attach(5); // Servo de direction sur pin 5

}

void loop() {
// On prend en charge les clients du serveur
YunClient client = server.accept();

// Nouveau client ?
if (client) {
// on lance la fonction "process"
process(client);

// et on ferme la connection en liberant les resources.
client.stop();
}

delay(10); // boucle toutes les 10ms
}

void process(YunClient client) {
// lit la commande
// elle est recue sous la forme "
http://YunIP/arduino/analog/pin/valeur
String command = client.readStringUntil('/');
// Si c'est une commande "analog"
if (command == "analog") {
analogCommand(client);
}

}
void analogCommand(YunClient client) {
int pin, value;

// On lit le numero du pin envoye dans la commande (dans notre cas, 3 ou 13)
pin = client.parseInt();

// Si le caracetre apres le pin est un '/' cela signifie que nous avons affaire a une URL
// avec une valeur de type: "/analog/3/120″
if (client.read() == '/') {
// On lit la valeur du pin et on execute la commande
value = client.parseInt();
if (pin==3) {

servoGaz.write(value);
Serial.println(value);
delay(15);
}
if (pin==13) {
//
servoDirection.write(value);
Serial.println(value);
delay(15);

}

// Renvoie un retour au client, non utilise dans cette application

client.print(F("Pin D"));
client.print(pin);
client.print(F(" set to analog "));
client.println(value);

// Update datastore key with the current pin value
String key = "D";
key += pin;
Bridge.put(key, String(value));
}
else {
// Read analog pin
value = analogRead(pin);

// Send feedback to client
client.print(F("Pin A"));
client.print(pin);
client.print(F(" reads analog "));
client.println(value);

// Update datastore key with the current pin value
String key = "A";
key += pin;
Bridge.put(key, String(value));
}
}


En ce qui concerne la partie télécommande depuis Android, le but est donc de :

- Se connecter au Yun en wifi (celui-ci étant configuré en point d'accès)

- Essayer de faire une jolie interface graphique (ben oui, c'est mon premier programme Android)

- Avoir 2 "sliders" dont la position renvoie une commande REST au Yun.

Cela ressemble à :

https://vincentbarousse.files.wordpress.com/2014/09/captureecran.jpg

ps : l'horloge n'a pas d'intérêt dans mon application, mais j'ai trouvé cela plus joli...

Cela donne dans le code de la partie graphique (layout) :


<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/imageView"
android:src="@drawable/unnamed"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_above="@+id/seekBar" />

<SeekBar
android:id="@+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progress="90″
android:max="180″
android:layout_above="@+id/seekBar2″
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginBottom="84dp" />

<SeekBar
android:id="@+id/seekBar2″
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progress="90″
android:max="180″
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />

<AnalogClock
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/analogClock"
android:layout_alignParentBottom="true"
android:layout_alignRight="@+id/seekBar2″
android:layout_alignEnd="@+id/seekBar2″ />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Le Hollandais Volant"
android:id="@+id/textView"
android:layout_alignParentBottom="true"
android:layout_toLeftOf="@+id/analogClock"
android:layout_toStartOf="@+id/analogClock" />


Les sliders (SeekBar) renvoient des valeurs comprises entre 0 et 180, ce qui m'évite de faire une conversion de la valeur à envoyer aux servos.

Dans le corps du programme (main.java), les principales parties sont :

- la lecture des valeurs renvoyées par les sliders :


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* 1 er seekBar, renvoie sa valeur et le nombre 13 sous la forme "13/valeur" */
mSeekBar = (SeekBar) findViewById(R.id.seekBar);
mSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
mQueue.offer("13/"+progress);
}
});

/* 2 eme seekBar, renvoie sa valeur et le nombre 3 sous la forme "3/valeur" */
mSeekBar2 = (SeekBar) findViewById(R.id.seekBar2);
mSeekBar2.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
mQueue.offer("3/"+progress);
}
});
}


- et la connexion à l'Arduino pour envoi des données :


/* C'est ici que commence la gestion de l'envoi des valeurs au Yun */

private ArrayBlockingQueue<String> mQueue = new ArrayBlockingQueue<String>(100);
private AtomicBoolean mStop = new AtomicBoolean(false);

private static Thread sNetworkThreadSend = null;
private final Runnable mNetworkRunnableSend = new Runnable() {

@Override
public void run() {
log("starting network thread for sending");
/* On crée une url sous la forme "
http://YunIP/arduino/analog/&#8221; ...*/
String urlBase = "
http://"+ARDUINO_IP_ADDRESS+"/arduino/analog/&#8221;;
String url;

try {
while(!mStop.get()){
String val = mQueue.take();
/* ...
Et on y rajoute la commande ex : "http://YunIP/arduino/analog/13/90&#8243; */

HttpClient httpClient = new DefaultHttpClient();
url = urlBase.concat(String.valueOf(val));
log("executing httpClient request");
HttpResponse response = httpClient.execute(new HttpGet(url));

}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}

log("returning from network thread for sending");
sNetworkThreadSend = null;
}
};


Pour ceux que cela intéresse, le code source du projet Android Studio est ici : (utiliser la fonction télécharger le contenu lu lien du navigateur,
retirer le .doc et déziper).

ArduinoYun_teleco.zip

Conclusions-améliorations

Le délai de réaction est un peu lent, et l'interface Android réagit au click mais pas au toucher, ce sont 2 points à améliorer.

La distance de réception s'avère un peu juste (environ 15 m). Cela paraît faible pour du wifi.
Je vais tester avec une antenne externe branchée sur le Yun.

 



   
AllinBox sur Facebook Flux RSS Forum