lundi 5 mai 2014

Communication par USB entre Raspberry Pi et Arduino

Pourquoi voudrait-on établir une communication entre un Raspberry Pi et un Arduino? Maintenant que nous avons appris comment utiliser les pins GPIO du Raspberry Pi, ce dernier n'est-il pas capable d'agir de façon autonome?

Voici quelques raisons possibles:
  • Le Raspberry Pi n'est pas idéal pour accomplir des actions avec un timing précis, car  Linux pourrait décider de se consacrer temporairement à une autre tâche.   Par exemple, si vous contrôlez un moteur pas à pas avec le Raspberry Pi, le moteur pourrait s'arrêter momentanément, de temps à autres. L'Arduino est plus fiable dans ce genre de situation.
  • Vos capteurs peuvent se trouver sur un shield conçu pour être inséré sur un Arduino.  Évidemment, la disposition des pins GPIO du Raspberry Pi n'est pas la même.
  • Si vous êtes déjà un utilisateur expérimenté de l'Arduino mais un néophyte en Python/Linux/Raspberry Pi, il y a certaines choses que vous savez déjà faire très facilement avec l'Arduino, mais que vous ne savez pas encore comment faire avec le Raspberry Pi.
  • Vous désirez établir un réseau de plusieurs capteurs satellites qui se rapportent à une seule station centrale, et plusieurs Arduinos coûtent moins cher que plusieurs Raspberry Pi.
La bonne nouvelle, c'est que c'est tout à fait possible, et même relativement facile, d'établir une communication entre un Raspberry Pi et un Arduino.  Et ce ne sont pas les options qui manquent:  communication série (UART),  I2C, Ethernet, communication sans fil...

Arduino et son alimentation
Dans cet article, nous abordons l'option qui s'avère probablement la plus simple:  la communication série par l'entremise d'un câble USB:  vous allez simplement brancher l'Arduino à un des deux ports USB du Raspberry Pi, de la même façon que vous le faites lorsque vous désirez programmer l'Arduino avec un ordinateur conventionnel. Mais attention: avant de brancher l'Arduino au Raspberry Pi, assurez-vous que l'Arduino dispose de sa propre alimentation;  le Raspberry Pi pourrait avoir des ratés si sa propre alimentation devait, en plus, alimenter l'Arduino par le port USB.

Installation de la bibliothèque pyserial

Installons d'abord la bibliothèque pyserial dans le Raspberry Pi:  elle nous facilitera grandement la tâche lors de la conception de nos scripts en Python.  Pour ce faire, vous utilisez cette commande pendant que le Raspberry Pi est branché à l'internet:
     
sudo apt-get install python-serial

Envoi d'un message de l'Arduino vers le Raspberry Pi

Commençons par une communication unidirectionnelle de l'Arduino vers le Raspberry Pi.  Du côté Arduino, l'envoi de messages se fait par la commande "Serial.println".  Par exemple, le sketch ci-dessous envoie un message toutes les 3 secondes.  J'ai ajouté un compteur pour que le message ne soit pas rigoureusement identique d'une fois à l'autre.   Nous pourrons donc vérifier que tous les messages envoyés par l'Arduino sont bel et bien captés par le Raspberry Pi.


Comme vous pouvez le constater, c'est exactement comme si vous vouliez afficher un message sur le moniteur série de l'environnement de programmation Arduino; vous pouvez d'ailleurs utiliser le moniteur série pour vérifier dès maintenant que le sketch fonctionne de la façon prévue.

Du côté Raspberry Pi, à la condition que la bibliotèque PySerial ait été préalablement installée, la fonction "serial.Serial" nous permet de lire les messages reçus .  Le premier paramètre de cette fonction est le nom du port usb occupé par l'Arduino:   dans mon cas, c'était ttyACM0;  ce sera peut-être la même chose pour vous aussi, mais ce n'est pas certain.  Pour en avoir le coeur net, vous pouvez écrire cette ligne de commande:

         ls /dev/tty*

Vous devriez avoir un résultat légèrement différent selon que l'Arduino est connecté ou non au Raspberry Pi. Le nom correct devrait être celui qui apparaît dans la liste pendant que l'Arduino est branché, mais qui n'y apparaît pas quand l'Arduino est débranché du port USB.

Voici donc un sketch en Python qui permettra au Raspberry Pi d'écouter ce que lui dit l'Arduino, et d'afficher le résultat.  (Personnellement, j'utilise le logiciel WinSCP pour installer le fichier dans le Raspberry Pi).


Pour interrompre le script, vous pouvez utiliser la combinaison Ctrl-C au clavier.

Envoi d'un message du Raspberry Pi vers l'Arduino

Essayons maintenant l'autre direction:  branchons quelques LEDs à l'Arduino, et c'est le Raspberry Pi qui indiquera à l'Arduino quelles LEDs il doit éteindre ou allumer.

Donc, une LED (accompagnée de sa résistance protectrice) est branchée à chacune des pins suivantes de l'Arduino:  2, 3 et 4 (attention:  le schéma ci-dessous montre par erreur des connexions aux pins 1, 2, et 3!).


Commençons par le sketch Arduino.  Grâce à la fonction Serial.read, le sketch ci-dessous allume ou éteint les LEDs en fonction du message reçu:  il allume la LED 1 (pin 2) s'il reçoit le message "1", il éteint la LED 2 (pin 3) s'il reçoit le message "5", etc.



Pourquoi soustraire le caractère '0'?  C'est parce qu'au départ, lorsque l'Arduino reçoit le message "1", il s'agit d'un caractère dont le code ASCII est 49.  En soustrayant le caractère '0' (dont le code ASCII est 48), le caractère '1' qui a été reçu devient le nombre entier 1.

Si vous le désirez, vous pouvez vérifier que ce sketch fonctionne correctement à l'intérieur de l'environnement de programmation Arduino, avant même de brancher l'Arduino au Raspberry:  dans le moniteur Série, tapez des nombres entre 1 et 6 et voyez que les LEDs s'allument ou s'éteignent en fonction des nombres que vous envoyez.

Et voici le script en Python qui sera exécuté par le Raspberry Pi:  c'est maintenant lui qui envoie des informations à l'Arduino:


Communication bidirectionnelle entre le Raspberry Pi et l'Arduino

Finalement, voici un exemple où le Raspberry Pi demande une information à l'Arduino (l'état d'un potentiomètre dont le curseur est branché à A0 ), et affiche la réponse que lui a retournée l'Arduino.



Le sketch Arduino:


Le script en python du Raspberry Pi:

Sources:

Mon point de départ a été la lecture de cet article dans le blog d'Oscar Liang.

Yves Pelletier (Twitter:  @ElectroAmateur)

18 commentaires:

  1. Un grand merci pour ce tutorial !

    RépondreSupprimer
  2. merci pour l'ensemble de votre oeuvre que je viens de découvrir.

    petit signalement: dans la partie TX_Pi Rx_Uno: le schéma montre un branchement sur les pins 1, 2 et 3 au lieu de 2,3 et 4.

    bonne continuation!

    RépondreSupprimer
  3. Super tutorial mais, question bête, vous êtes sur quel OS ? Raspbian ?

    RépondreSupprimer
  4. Bonjour,
    Le code dans ArduinoVersPiUSP.py montre une boucle infinie passant son temps à lire que quelque chose arrive sur le R Pi. Ca ne grève pas trop sa réactivité (si c'est aussi un serveur web par exemple) ? L'équivalent d'une interruption est-il possible ?

    RépondreSupprimer
    Réponses
    1. Sauf que readLine est un appel bloquant qui attend, tu peut le prouver toi même en affichant un compteur de boucle par exemple.

      Supprimer
  5. Ce commentaire a été supprimé par l'auteur.

    RépondreSupprimer
  6. Bonjour,
    J'ai un raspberry pi 2 sur lequel j'ai installé ubuntu mate 15.10.
    J'ai aussi une arduino uno Rev 3.
    J'ai des capteurs connectés à l'arduino. J'ai mis tout ce petit monde ensemble et ô joie le moniteur série de l'IDE Arduino m'indique bien les données des capteurs.
    Les choses se compliquent ici : Je souhaite désormais installer opendaqcalc qui est une macro calc (le fameux tableur) pour pouvoir importer les données des capteurs arduino sur un tableur en temps réel. Le lien : http://p.loussouarn.free.fr/arduino/arduino.html (vers la fin de la page). Problème : lorsque je clique sur "connecter" : rien ne se passe ! J'ai quasiment tout essayé pour résoudre ce problème mais sans résultat. Selon vous, qu'est ce qui fait que le raspberry fait des siennes alors qu'avec mon vieux netbook windows xp j'obtiens les données sans problèmes sur calc? Merci par avance !!!

    RépondreSupprimer
    Réponses
    1. Il y a cet article dans lequel j'avais détaillé tous les réglages à faire pour Opendaqcalc (2e partie de l'article); je n'étais pas sur Raspberry Pi, mais l'OS était quand même Linux, ça vous aidera peut-être:

      http://electroniqueamateur.blogspot.ca/2014/10/transmettre-les-donnees-darduino-vers.html#more

      Supprimer
    2. Merci pour votre réponse. J'ai fait comme il fallait (sur RASPBIAN et sur UBUNTU MATE) J'ai essayé sur windows. Tout est ok. Je suis de plus en plus convaincu qu'il s'agit d'un problème de port lié spécifiquement au raspberry. Le plus surprenant est que le moniteur série d l'arduino fonctionne impeccablement bien et minicom (moniteur du pi) aussi. Le problème est donc soit dans le logiciel opendaqcalc qui n'est pas "conçu" pour le raspberry soit dans le raspberry lui-même.... j'ai envoyé un mail au concepteur de la macro mais celui-ci ne me répond plus...

      Supprimer
  7. Bonjour, ce tuto m'a été d'une très grande utilité.
    Vous auriez une idée de comment envoyer les valeurs afficher par le fichier python vers un site web ( déjà héberger sur mon rapsberry ) ?

    Merci d'avance :)

    RépondreSupprimer
  8. Bonjour je suis en terminale et pour mon projet dan lequel je dois faire fonctionner un tapis de course à l'aide d'un raspberry je me suis basé ton tuto, et voici mon problème : lorsque l'arduino envoie les données au rasp , le rasp les lis , tout va bien , mais lorsque le rasp envoie les données à l'arduino, un messaeg d'erreur apparait me disant qu'il y a un probleme avec le packages (bibliothèque du rasp) a la ligne 471 :dans le message d'erreur, à la fin: " n=os.write(self.fd,d)
    TypeError:'str' does not support the buffer interface"
    et je ne sais pas comment le résoudre pourriez vous m'aider svp ?

    RépondreSupprimer
  9. Voici mon code pour réaliser un pont WiFi UDP avec le Raspberry Pi 3. Le PC utilise le logiciel domotique Homeseer Pro (Arduino Plugin) qui communique avec l'Arduino via le Pi.
    Ceci pour montrer un code Python non bloquant et insensible aux ruptures de connexion USB et choix USB0/USB1.
    #Python 2.7
    #Liaison PC Windows 7 Homeseer Pro 3 - Raspberry Pi 3 - Arduino Mega

    import serial
    from time import sleep
    import select
    from socket import *

    #Initialisation socket UDP
    clientSocket = socket(AF_INET, SOCK_DGRAM)
    clientSocket.bind(('192.168.1.37',5006))

    #Ports USB
    usb_ports = ('/dev/ttyUSB0', '/dev/ttyUSB1')

    #Homeseer UDP @IP et Port
    addr = ("192.168.1.22", 8888 )

    #Tempo Arduino
    sleep(1)

    #Fonction lecture UDP et envoi Arduino
    def receive():
    loop = True
    while loop:
    clientSocket.setblocking(0)
    try:
    data = clientSocket.recv(256)
    if(data):
    arduino.write(data)
    print "R:" + data
    sleep(0.1)
    else:
    loop = False
    except:
    loop = False

    #Boucle principale
    while True:
    for usb in usb_ports:
    try:
    # Ouverture du port USB
    arduino = serial.Serial(usb, 115200, timeout=0)
    print('Connected to arduio at ' + usb)

    while True:
    try:
    message =""
    # Appel fonction lecture UDP et envoi arduino
    receive()
    sleep(0.5)
    # Lecture Arduino et envoi UDP
    message = arduino.readline()
    if (message != ""):
    print "E: " + message
    clientSocket.sendto(message, addr)
    # Traitement des exceptions
    except KeyboardInterrupt:
    exit()
    except serial.SerialException:
    print('Lost connection. Trying to reconnect...')
    break
    except IOError:
    print('IOError...')
    pass
    except serial.SerialException:
    print('Arduino not found at ' + usb)
    pass
    sleep(10)

    RépondreSupprimer
  10. Un petit code non bloquant et insensible aux déconnexions USB :

    #Python 2.7
    #Liaison PC Windows 7 Homeseer Pro 3 - Raspberry Pi 3 - Arduino Mega

    import serial
    from time import sleep
    import select
    from socket import *

    #Initialisation socket UDP
    clientSocket = socket(AF_INET, SOCK_DGRAM)
    clientSocket.bind(('192.168.1.37',5006))

    #Ports USB
    usb_ports = ('/dev/ttyUSB0', '/dev/ttyUSB1')

    #Homeseer UDP @IP et Port
    addr = ("192.168.1.22", 8888 )

    #Tempo Arduino
    sleep(1)

    #Fonction lecture UDP et envoi Arduino
    def receive():
    loop = True
    while loop:
    clientSocket.setblocking(0)
    try:
    data = clientSocket.recv(256)
    if(data):
    arduino.write(data)
    print "R:" + data
    sleep(0.1)
    else:
    loop = False
    except:
    loop = False

    #Boucle principale
    while True:
    for usb in usb_ports:
    try:
    # Ouverture du port USB
    arduino = serial.Serial(usb, 115200, timeout=0)
    print('Connected to arduio at ' + usb)

    while True:
    try:
    message =""
    # Appel fonction lecture UDP et envoi arduino
    receive()
    sleep(0.5)
    # Lecture Arduino et envoi UDP
    message = arduino.readline()
    if (message != ""):
    print "E: " + message
    clientSocket.sendto(message, addr)
    # Traitement des exceptions
    except KeyboardInterrupt:
    exit()
    except serial.SerialException:
    print('Lost connection. Trying to reconnect...')
    break
    except IOError:
    print('IOError...')
    pass
    except serial.SerialException:
    print('Arduino not found at ' + usb)
    pass
    sleep(10)

    RépondreSupprimer
  11. Bonsoir , je débute avec mon Arduino Uno. Jusqu'a maintenant j'utilisais un raspberry pour lire la température de ma sonde de température. Je passe maintenant via l'arduino connecter en usb au raspberry, car je voudrais ajouter d'autre capteur plus tard. J'arrive à lire la température en ssh ( sur mon raspberry ). Mais maintenant j'aimerai affiché cette température sur mon server apache du raspberry. Comment passer du python au server ? ou du Arduino au server tout en étant connecté en usb ? Merci d'avance pour la réponse

    RépondreSupprimer

Related Posts Plugin for WordPress, Blogger...