I. Introduction▲
Ce tutoriel est un touche à tout incroyable. Lorsque nous bricolons à la fois avec un Arduino et un Raspberry Pi et que nous voulons les faire communiquer, il nous est impossible de faire simple. Il nous faut tout d'abord maîtriser l'IDE de l'Arduino, se débrouiller avec sa programmation en langage C et ensuite faire l'équivalent sur le Raspberry Pi. Pour un informaticien désirant approfondir ses connaissances, c'est le bonheur absolu pour un investissement relativement modéré. J'ai essayé ici d'être le plus concis possible dans mon exposé et de ne pas trop entrer dans les détails à cause du nombre de sujets abordés !
J'ai utilisé l'IDE de l'Arduino uniquement dans sa version PC et non dans sa variante Web avec stockage sur le Cloud. Il est évidemment possible de l'installer sur Ubuntu ou macOS.
Il y a divers possibilités de communiquer entre un Arduino et un Pi et j'ai choisi ici celle du câble USB. Par exemple l'article sur ce site, Piloter Arduino depuis un PC avec Python et Boa Constructor, traite aussi de ce sujet d'une manière différente. D'autres solutions tout aussi ludiques seraient possibles avec une interface Wi-Fi (Web ou socket) voire au travers d'une interface série RX/TX sur les broches des Pi et Arduino. Une alternative ESP8266 bon marché pour remplacer l'Arduino, également programmable avec l’IDE Arduino, est aussi envisageable.
La solution du câble USB est principalement intéressante par sa simplicité. Nous aurons aussi besoin de ce même câble pour communiquer avec le PC lors du développement du logiciel de l'Arduino. Le logiciel du Raspberry Pi sera développé tout d'abord en langage interprété Python pour vérifier l'interface : nous sortirons le câble USB du PC pour le connecter au Pi. Ensuite je passerai en Java, avec du code développé sur le PC avec Eclipse, afin de maîtriser le protocole de communication entre l'Arduino et le Pi. Déposer un capteur sur une platine d'expérimentation connecté à l'Arduino et exécuter les diverses étapes conduisant au programme Java sur le Pi requièrent les mêmes techniques et méthodes que décrites dans mon livre, Programmer en Java pour le Raspberry Pi 3, sorti chez Eyrolles en janvier 2019. Pour le Raspberry Pi, les capteurs ou autres composants sont normalement connectés à son port GPIO. Mais, dans certain cas, comme pour le LM35 qui est un capteur de température analogique, l’utilisation directe sans une carte supplémentaire analogique n’est pas possible. Une interface de ce type est directement disponible sur l'Arduino, sur la partie des broches analogiques. Un bricoleur aura peut-être déjà d'autres circuits disponibles, avec du logiciel pour récupérer des mesures particulières ou d'autres résultats. Il lui suffira de piquer les quelques instructions ci-dessous pour les ajouter à du code existant. Ce type d'interface avec le câble USB est tout à fait envisageable lorsque les deux Arduino et Pi sont proches. Nous irons chercher dans cet article-ci la distance d'un objet avec un capteur à ultrasons.
II. Schéma Fritzing▲
Le schéma Fritzing qui suit contient trois composants :
- un LM35, capteur analogique de température ;
- deux composants digitaux :
- un relais,
- un capteur à ultrasons.
Un exemple de sketch Arduino suivra avec le capteur à ultrasons qui mesurera la distance avec un objet en déplacement pour enclencher le relais en fonction de cette distance. Pour le LM35 qui mesure de température ambiante, un autre sketch pourrait être développé par le lecteur, voire par moi-même dans un article à venir. Je l'ai aussi laissé pour montrer que la platine de démonstration est nécessaire lorsque l'alimentation et/ou la terre sont utilisées par plusieurs composants. Le relais peut être branché sur un appareil connecté au réseau électrique domestique (220-230 Volt). Lorsqu'il faudra connecter le relais sur un câble ou une prise 220 Volt, il faudra se méfier lors du montage en le manipulant correctement et évidemment non connecté.
Des exemples de code Python et Java pour ces composants pour le Raspberry Pi se trouvent dans mon livre, mais pas pour le LM35 que j'ai remplacé par un (ou plusieurs) Dallas DS18B20A sur le port GPIO du Pi avec le protocole 1-Wire.
Dans ce diagramme Fritzing (http://fritzing.org/home/) il faudra bien suivre les couleurs pour retrouver les broches. Pour la description de cet article, j'avais construit le circuit avant de faire le diagramme. Il n'est donc pas optimisé pour le côté visuel. Sans le capteur de température, il serait plus dépouillé :
Nous retrouverons la description des broches utilisées dans le sketch Arduino (croquis en français). Le câble USB sera branché au PC lors de la préparation du croquis et pour l'alimenter. Le relais côté réseau électrique pourra rester non connecté pendant le développement : des clics significatifs lors des enclenchements et déclenchements suffiront à nous indiquer que cela fonctionne.
III. Capteur à ultrasons et relais▲
Pour l'édition, la compilation et le téléversement de croquis, nous devons utiliser l’IDE de l’Arduino, . J'ai travaillé ici avec la version 1.8.8. Il faudra s’assurer que le Type de carte sous Outils est correct. Si le type d'Arduino n'est pas visible, il faudra utiliser le Gestionnaire de carte sous la même rubrique du menu. Nous pourrons alors le rechercher et le télécharger. Dans mon cas, j’ai utilisé mon tout vieux Arduino Duemilanove. Le port, COM3 pour moi, apparaîtra sous Windows 10 dans la rubrique Ports (Explorateur de fichier / PC / Gérer / Gestionnaire de périphérique). Dans l'IDE, il doit être correctement spécifié sous Outils Ports où la liste des ports devraient apparaître de toute façon. En retirant et remettant le câble, le COM à utiliser sera aussi visible et identifiable.
Nous chargerons le fichier relais_distance.ino, voire le contenu qui suit, dans l’IDE de l’Arduino :
//Définition des broches
const
int
relayPin =
7
; //fil violet
const
int
trigPin =
9
; //fil orange
const
int
echoPin =
10
; //fil vert
//Définition des variables
long
duration;
int
distance;
int
nbignore =
0
;
unsigned
long
time =
0
;
void
setup
(
) {
Serial.begin
(
9600
); //vitesse communication série
Serial.println
(
"
Lancement de relais_distance
"
);
pinMode
(
relayPin, OUTPUT);
digitalWrite
(
relayPin, LOW);
pinMode
(
trigPin, OUTPUT); //trigPin comme Output
pinMode
(
echoPin, INPUT); //echoPin comme Input
}
void
loop
(
) {
delay
(
100
); //Pas trop souvent, 100ms
//Clear le trigPin
digitalWrite
(
trigPin, LOW);
delayMicroseconds
(
2
);
//trigPin HIGH state pour 10 micro seconds
digitalWrite
(
trigPin, HIGH);
delayMicroseconds
(
10
);
digitalWrite
(
trigPin, LOW);
//Lecture echoPin pour l'onde sonore retour en microsecondes
duration =
pulseIn
(
echoPin, HIGH);
//Calcul de distance
distance =
duration*
0
.034
/
2
;
nbignore++
; //2 premiers ignorés
if
(
nbignore >
2
) {
if
(
time ==
0
) {
if
((
distance >
5
) &&
(
distance <
100
)) {
//Montre la distance sur le port série
Serial.print
(
"
Distance:
"
);
Serial.println
(
distance);
Serial.println
(
"
Relais ON
"
);
digitalWrite
(
relayPin, HIGH);
time =
millis
(
);
}
}
else
{
if
((
millis
(
) -
time) >
4000
) {
if
((
distance <=
5
) ||
(
distance >=
100
)) {
Serial.println
(
"
Relais OFF
"
);
digitalWrite
(
relayPin, LOW);
time =
0
;
}
}
}
}
}
Le nom du fichier du sketch (croquis) est ici relais_distance.ino et il doit se trouver dans un répertoire relais_distance. Il est conseillé de stocker tous nos croquis Arduino ou ESP8266, dans un répertoire unique. Pour travailler avec l'IDE d'Arduino, je préfère y mettre un raccourci sur le bureau de mon PC et ensuite je sélectionne le croquis dans le menu : Fichier / Ouvert récemment.
Dans cette image,
représentant le premier morceau du code de relais_distance dans l'IDE de l'Arduino, nous remarquerons que j'ai déjà cliqué sur le second bouton du menu, en haut à gauche, la flèche direction droite, pour compiler et télécharger le croquis.
Si le circuit de l'Arduino est correctement monté, nous entendrons déjà les clics significatifs du relais qui fonctionne. Nous pouvons évidemment juste compiler le croquis pour vérification, sans avoir connecté nécessairement notre Arduino et ceci avec le premier bouton V.
Dans le chapitre 19 de mon livre sur le Raspberry Pi, dédié à l'utilisation d’un capteur à ultrasons et d’un bouton-poussoir, j'explique la difficulté que j'ai eue sur un Raspberry Pi, de programmer le script Python pour les tests du capteur avant de passer à la version Java en utilisant le Pi4J. Je n'étais pas certain du bon fonctionnement de mon capteur, j'en ai donc commandé un second et l'ai tout d'abord branché sur un Arduino.
Les deux articles suivants dédiés au Raspberry Pi peuvent aussi être consultés :
- Utiliser des capteurs avec le port GPIO - Partie 3 ;
- Construisez votre propre système de détection d'obstacles au stationnement.
Les instructions clefs sont les deux delayMicroseconds() qui permettent de générer le signal ultrasonique pendant 10 micro-secondes avant d'enclencher la broche Echo avec pulseIn(echoPin, HIGH) qui nous retourne la largeur du signal afin de calculer le temps de réponse et donc la distance.
Le 0.034/2 nous permet de déterminer la distance en fonction de la vitesse du son. Le train d’ultrasons parcourt deux fois la distance : à l’aller, puis au retour après réflexion sur l’obstacle. De plus, nous ignorons les deux premières mesures qui apparaissaient incorrectes durant mes premiers tests, comme si le « moteur du capteur devait chauffer ».
Si nous déposons notre capteur à ultrasons simplement sur une table, nous risquons d'avoir des réflexions étranges et aléatoires. Une idée m'est venue en créant par exemple un tube de 7 cm de diamètre à partir d'un carton au format A3 : ce n'est vraiment pas bon du tout. Les ondes partent dans tous les sens et sont réfléchies sur les parois du tube. Il faut donc bien orienter le capteur et l’éloigner de tous les objets aux alentours.
Ce sketch n’est pas grandiose, mais plus ou moins fonctionnel. Il demanderait quelques adaptations suivant des besoins particuliers (nous ferons des exercices). Ici nous activerons le relais pendant 4 secs, lorsque des objets seront en déplacement devant le capteur et à une distance compris entre 5 et 100cm de celui-ci. Le numéro des broches est indiqué en début de sketch. Les deux composants sont alimentés en 5V et une platine d'expérimentation est donc nécessaire.
Pour exécuter le croquis ci-dessus, nous devons le faire depuis l'IDE avec le menu Outils / Moniteur série. Si nous utilisons différents croquis ou micro-contrôleurs avec d'autres configurations, il faudra corriger la vitesse, soit dans le croquis, ici Serial.begin(9600), soit dans l'IDE dans la fenêtre d'exécution :
Une mauvaise vitesse se verra rapidement par des caractères illisibles dans la fenêtre d'exécution. Cette vitesse n'a rien à voir avec la vitesse de téléchargement que j'ai toujours spécifiée au maximum, avec mon câble USB relativement court.
Voici donc l'exécution du script, où j'ai fait passer un objet (mon fameux tube), au dessus du capteur, plusieurs fois et à une distance variable entre 12 et 86cm :
Le lecteur pourra déjà constater que les messages venant des Serial.println("") du croquis, sont les mêmes pour montrer le résultat dans cette fenêtre du moniteur série de l'IDE, que ceux qui seront reçus plus tard par un client (un script Python ou un programme Java) avec une communication série sur le port et le câble USB.
Exercice 1 pour le lecteur : modifier le sketch Arduino afin que la variable time retrouve ses 4 secs, mais seulement si un déplacement significatif, disons 10%, a été constaté durant la phase ON du relais.
Exercice 2 pour le lecteur : remplacer l'Arduino Duemilanove par un ESP8266, par exemple un RobotDyn Wi-Fi D1 R2 à 4$. Ce dernier possède moins de broches, mais nous aiderait dans notre apprentissage, où nous pourrions plus tard, par exemple, envoyer la distance avec un protocole de communication socket et non avec le câble USB. Il faudra modifier les broches du sketch Arduino pour utiliser les broches correctes de cet autre modèle de micro-contrôleur. Par exemple ici :
//Définition des broches du RobotDyn D1 R2
const
int
relayPin =
16
; //D0
const
int
trigPin =
2
; //D4
const
int
echoPin =
14
; //D5
Il faudra évidemment corriger la position des fils sur le schéma Fritzing (et pourquoi pas installer ce joli outil son notre PC pour jouer avec). Si nous essayons de télécharger le script Arduino ci-dessus sans adaptations, nous pourrions nous retrouver avec un micro-contrôleur totalement bloqué. Un reset serait nécessaire : brancher le GPIO 0 (D3) au GND et presser le bouton Reset. Retirer ce dernier pont et le câble USB, le remettre et refaire une compilation puis un téléchargement (pour ce RobotDyn mettre le modèle de carte à WeMos D1 R2 & mini).
IV. Préparation sur le Raspberry Pi▲
Avant de passer au code Java que nous développerons et vérifierons sur un PC Windows et avec Eclipse, nous allons vérifier la communication entre l'Arduino et le Raspberry Pi avec un script Python.
J'ai utilisé le Raspberry Pi 3 Model A+, le dernier né des Pi (novembre 2018), puissant et bon marché (25$), avec un Raspbian (le Linux du Pi) installé sans interface écran, donc avec un accès depuis le PC Windows avec l'outil de communication ssh PuTTY. Les instructions d'installation et d'utilisation sont expliquées dans mon livre voire sur de nombreux sites Web. Une simple recherche de « Raspberry Pi PuTTY » sur le web nous indiquera par exemple aussi que ssh est l'alternative Linux.
Si nous avons un Raspberry Pi avec un Raspbian récent, la commande dans une console du Pi
sudo apt-get install python-serial
n'est pas nécessaire. Si nous l'exécutons tout de même, nous recevrons une indication que le logiciel Python pour communiquer avec le port USB est déjà installé.
Ensuite, il faudra déterminer avec la commande Linux ls, quel périphérique (/dev ) est utilisé. Nous voyons ici que je me trouve dans le répertoire /home/pi/python que j'ai créé pour y déposer mes scripts Python :
pi@raspberrypi:~/python $ ls /dev/tty*
Nous recevrons quelque chose comme :
……………………
/dev/tty10 /dev/tty19 /dev/tty27 /dev/tty35 /dev/tty43 /dev/tty51 /dev/tty6 /dev/ttyprintk
/dev/tty11 /dev/tty2 /dev/tty28 /dev/tty36 /dev/tty44 /dev/tty52 /dev/tty60 /dev/ttyUSB0
/dev/tty12 /dev/tty20 /dev/tty29 /dev/tty37 /dev/tty45 /dev/tty53 /dev/tty61
……………………
Nous essayerons la commande Linux cat sur ce device /dev/ttyUSB0 qui nous semble être le bon :
pi@raspberrypi:~ $ cat /dev/ttyUSB0
Lancement de relais_distance
Distance: 11
Relais ON
Relais OFF
^C
pi@raspberrypi:~/python $
Le message Lancement de relais_distance est bien le premier println de notre script Arduino ci-dessus. Un Ctrl-C va interrompre la commande Linux cat.
V. Un script Python sur le Raspberry Pi▲
Il est difficile de faire plus simple pour ce script Python qui suit. Les programmeurs Python expérimentés, dont je ne suis pas, en feront vite une extension.
Nous éditerons le script arduino_usb1.py directement sur le Pi avec un éditeur, par exemple vi pour moi, voire nano et dans un répertoire comme par exemple /home/pi/python (attention à l’indentation du code) :
# coding: utf-8
import
time
import
serial
ser =
serial.Serial
(
'/dev/ttyUSB0'
, 9600
)
while
1
:
print
(
ser.readline
(
))
time.sleep
(
0.1
)
Et il fonctionne comme un cat sous Linux. Dans une console PuTTY depuis notre PC :
pi@raspberrypi:~/python $ python arduino_usb1.py
Lancement de relais_distance
Distance: 75
Relais ON
Relais OFF
Exercice 3 pour le lecteur : modifier le script Python pour que l'interruption avec un Ctrl-C fonctionne correctement. De plus, n'afficher aucun des messages reçus, comme les ON et OFF. A partir de la ligne « Distance » reçue de l'Arduino, conserver les distances minimales et maximales et montrer la dernière distance mesurée suivie du minimum et du maximum (par exemple : objet détecté à 75cm (min 12 et max 99)).
VI. Une classe Java pour communiquer avec l'Arduino▲
Pour cette partie, il faudra maîtriser Eclipse et la programmation Java. Je ne vais ici ni expliquer l'installation d'Eclipse, ni son utilisation, ni sa configuration. La version 8 de Java est utilisée, car c'est celle supportée sur le Raspberry Pi 3.
Nous allons à présent écrire une jolie petite classe Java USBPort, dans un projet Java sous Eclipse et sur le même PC Windows utilisé pour l'IDE de l'Arduino. Cette classe USBport fera un travail similaire à celui de l'exemple Python et pourrait être étendue à souhait :
import
java.io.IOException;
import
java.io.RandomAccessFile;
public
class
USBPort implements
Runnable {
private
Thread monThread;
private
RandomAccessFile rAF;
private
String portName;
public
USBPort
(
String portName) {
this
.portName =
portName;
monThread =
new
Thread
(
this
);
}
public
void
start
(
) {
try
{
rAF =
new
java.io.RandomAccessFile
(
portName, "rwd"
);
}
catch
(
Exception e) {
System.out.println
(
"start "
+
e.toString
(
));
}
monThread.start
(
);
}
public
void
run
(
) {
System.out.println
(
"Lecture du port "
+
portName);
for
(
int
i =
0
; i <
10
; i++
) {
String response =
null
;
try
{
response =
rAF.readLine
(
);
}
catch
(
IOException e) {
System.out.println
(
e.getMessage
(
));
}
System.out.println
(
response);
}
System.out.println
(
"USBPort terminé (run)"
);
System.exit
(
0
);
}
public
static
void
main
(
String[] args) {
String portDevice =
"COM3"
;
if
(
args.length >
0
) {
portDevice =
args[0
];
}
USBPort usbPort =
new
USBPort
(
portDevice);
usbPort.start
(
);
try
{
Thread.sleep
(
50000
);
}
catch
(
InterruptedException e) {
}
System.out.println
(
"USBPort terminé"
);
System.exit
(
0
);
}
}
La partie communication se fait dans un thread (Runnable) et il faudra se méfier qu'il ne reste pas en suspens lors d'un débogage sous Eclipse. Une explication complète de ce code occuperait à nouveau trop de place ici. Le lecteur devrait pouvoir se débrouiller.
Nous rebrancherons notre câble USB, cette fois-ci sur le PC. Le port COM3 dans mon cas a été identifié avec l’IDE de l’Arduino et dépend du connecteur du PC utilisé. En ayant testé l’interface COM avec l’IDE, la bonne configuration sera mise. Si aucun résultat n’est retourné, il faudra adapter correctement le port COM correspondant.
La commande mode COM3 exécutée dans une fenêtre DOS CMD va nous fournir l’information et une commande mode COM3 baud=9600 pourrait être nécessaire. Nous avions indiqué la vitesse avec l’instruction Serial.begin(9600); dans le sketch.
Si nous exécutions java USBport dans le répertoire bin de notre Workspace d’Eclipse, avec
D:\EclipseWorkspace………….\bin>java USBPort COM3
nous verrions le même résultat que dans la console d'Eclipse de ce projet Java. Par exemple :
Lecture du port COM3
Lancement de relais_distance
Distance: 13
Relais ON
Relais OFF
COM3 peut être passé par le args[] du main() de Java.
Exercice 4 pour le lecteur : adapter le code Java pour vérifier les cas d'erreurs avec le COM3 inexistant ou en utilisation (par exemple actif sur l'IDE de l'Arduino). Ne pas limiter à 10 messages et aussi coder l'exercice 3 précédent en Java (objet détecté à 75cm (min 12 et max 99)).
VII. Exécuter USBPort sur le Raspberry Pi▲
Comme décrit dans mon livre à plusieurs occasions, nous transférerons le fichier USBPort.class, depuis le répertoire bin du projet dans le Workspace d’Eclipse, sur le Raspberry Pi dans un répertoire comme /home/pi/java et avec WinScp. Il faudra alors faire passer le câble USB du PC sur le Raspberry Pi, évidemment ! C'est donc le Pi qui alimentera à présent l'Arduino. Magnifique !
La commande ici sera: java USBPort /dev/ttyUSB0
avec le /dev précédemment déterminé. Le résultat sera le même que ci-dessus avec la commande cat ou le script Python arduino_usb1.py.
En cas de difficulté avec la configuration du port, la commande stty, équivalente à MODE sous DOS, pourra être utilisée. Par exemple:
sudo stty -F /dev/ttyUSB0 -a
sudo stty -F /dev/ttyUSB0 9600 cs8 -cstopb -parenb
VIII. Conclusion▲
Il nous restera à améliorer notre script Arduino, faire quelque chose d’un peu plus intelligent comme envoyer des données dans l’autre direction et pareil en Java sur ce /dev/ttyUSB0. Le Raspberry Pi pourrait envoyer et adapter les deux valeurs 5 et 100cm et l'Arduino les garder en EEPROM ! Nous pourrions par exemple ajouter une LED rouge, voire un buzzer, sur le GPIO du Pi, pour indiquer si le relais est actif sur l'Arduino, ou encore stocker chaque passage de notre chat dans une base de données SQLite et envoyer par courriel une statistique journalière avec les heures et les distances. S'il fallait identifier si le chat s'éloignait ou non du capteur, il faudrait alors le faire sur l'Arduino. Enfin, un programmeur expérimenté et passionné de Java comme moi, préférera déplacer le capteur à ultrasons sur le GPIO du Raspberry Pi, le vérifier avec Python et le programmer avec une multitude de fonctionnalités diverses en Java.
En considérant, les sujets présentés dans cet article, comme les langages C, Python et Java, l'incontournable Eclipse, les outils Windows et Linux (Raspbian), les circuits et composants électroniques et les protocoles et outils de communication, le lecteur se rendra vite compte que le Raspberry Pi 3 est définitivement un environnement exceptionnel de formation informatique et professionnelle.