lundi 5 mai 2014

Communication par USB entre Raspberry Pi et Arduino

Cet article a été mis à jour le 29 décembre 2020. Tous les scripts sont maintenant compatibles avec Python 3.

Il existe plusieurs options possibles pour établir une communication entre un Raspberry Pi et un Arduino: communication série (UART), I2C, Ethernet, communication sans fil...

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. Nous allons simplement brancher l'Arduino à un des ports USB du Raspberry Pi, de la même façon que nous le faisons lorsque nous désirons programmer l'Arduino avec un ordinateur conventionnel. 

Arduino et son alimentation
La bibliothèque pyserial

De nos jours, la bibliothèque pyserial est présente par défaut dans la distribution de base du Raspberry Pi OS, mais à tout hasard, si elle n'y est pas, vous pouvez l'installer avec une ou l'autre de ces deux commandes:

pip3 install pyserial

...ou encore...
      
sudo apt-get install python3-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 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 ttyUSB0;  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 instruction dans le terminal:

         ls /dev/tty*

La liste qui s'affiche devrait être légèrement différente 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 du Raspberry Pi.

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. 


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 broches suivantes de l'Arduino:  2, 3 et 4 .



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 (broche 2) s'il reçoit le message "1", il éteint la LED 2 (broche 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)

24 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
  12. Merci bcp pour ce tuto. c'est ma première connexion du raspberry - arduino. tout fonctionne à merveille. encore une fois merci.

    RépondreSupprimer
  13. Bonjour à tous,
    Je sais pas si je suis le seul, mais j'ai eu une erreur lors de l'envoi du "Rasberry Pi vers l'arduino" avec la ligne ser.write(str(compteur))

    Erreur:
    TypeError('unicode strings are not supported, please encode to bytes: {!r}'.format(seq))
    Après quelques recherches et si j'ai bien compris, cela fonctionne avec Python 2.7 mais pas avec Python3.
    Pour corriger cela il faut encoder sous format ASCII ou UTF8 avant de l'envoyer à l'arduino via la fonction ser.write().
    En remplaçant ser.write(str(compteur)) par ser.write(str(compteur).encode('UTF8')) ou ser.write(str(compteur).encode('ASCII'))

    le problème est réglé !

    En tout cas Merci pour ce Tutorial très clair et pas à pas !

    RépondreSupprimer
  14. Effectivement, j'ai eu le meme problème avec ma raspberry 3. Merci d'avoir précisé la solution ;)

    RépondreSupprimer
  15. bonjour mon pb je souhaite récupérer l'info de ser.readline() mais si je fais data = ser.readline() cela bloque pouvez-vous m aider ?
    merci

    RépondreSupprimer
  16. Un tuto très clair et sympa. Merci beaucoup. Et un grand merci à Monbeaussa car j'ai un peu galéré avec la compatibilité python 2.7 vers python 3 !
    En core Merci.

    RépondreSupprimer
  17. s'ils vous plait c'est quoi la methode la plus avantageuse la communcation par usb ou I2C

    RépondreSupprimer