Aller au contenu

Piloter un Arduino depuis un PC, en filaire, avec Python (sous Windows)

Tutorial communication pc arduino avec python, pilotage à distance depuis un PC d'une carte Arduino via le port USB et le port COM, avec exemples de code

Que diriez-vous de piloter un Arduino depuis un PC, via un script Python ? C’est en tout cas ce que je vous propose de découvrir aujourd’hui, avec quelques exemples pratiques à l’appui ! Ainsi, vous verrez comment il est facile de concevoir son propre « protocole de communication », pour faire faire à l’Arduino tout ce que l’on veut (ou presque !).

Mais avant toute chose, je vous montrerai comment j’ai imaginé les échanges PC/Arduino, et quels logiciels libres j’ai utilisé. Bien sûr, tout ceci ne vous montrera qu’une façon de faire les choses, car il y en aurait bien d’autres ! Ensuite, au travers de quelques exemples, nous verrons ensemble comment prendre le contrôle d’une sortie arduino depuis un PC, puis comment lire l’état ou le niveau d’une entrée. Enfin, il ne vous restera plus qu’à vous inspirer de tout cela, afin de créer vos propres programmes Python, pour piloter votre Arduino « comme bon vous semble », depuis votre ordi. Mais bon, une chose après l’autre ! Pour l’heure, découvrons tout cela ensemble, étape par étape 😉

Bonjour, et bienvenue sur le site Passion Electronique !

N’étant pas un expert en programmation Python, je vous montre ici simplement une façon de faire, sans que l’accent soit mis sur l’optimisation, ou la performance. En effet, le but premier de ce tuto est simplement de vous montrer comment on peut facilement faire dialoguer un Arduino et son PC, en filaire, avec des directives simples, écrites en Python. Ensuite, libre à vous de vous inspirer de tout ce qui est présenté dans cet article, et de l’adapter à vos besoins ! À noter que je travaille sous environnement Windows, et ai donc fait ce tuto sur cette base. Bien évidemment, il faudra quelque peu adapter tout ce contenu, si vous fonctionnez sous Mac ou Linux.

Principe de fonctionnement (communication filaire PC/Arduino)

Avant d’entrer dans le détail, quelques mots au sujet du principe de communication filaire entre PC et Arduino, pour lequel j’ai opté.

Protocole de communication : le prog python commande (ou questionne) et l’arduino exécute (ou répond)

Pour qu’un PC et un Arduino puissent échanger ensemble, le plus simple est que l’un des deux soit « maître » de la communication. Ainsi, personne ne parle « en même temps » que l’autre, ce qui permet d’éviter tout conflit dans les échanges. Pour ma part ici, j’ai choisi de rendre le script Python « maître » de la communication. C’est en effet le plus simple à mettre en œuvre 😉

Au niveau des règles régissant les échanges PC/Arduino (protocole de communication), j’ai là aussi opté pour quelque chose qui soit le plus simple possible, à savoir : le programme python questionne l’arduino (ou lui ordonne de faire quelque chose), et l’arduino répond au script python (ou indique qu’il a exécuté telle ou telle tâche). L’échange est donc bidirectionnel, mais « sous les ordres » du PC ! En clair : l’arduino ne pourra envoyer quelque message que ce soit de manière autonome ; il ne fera que répondre aux demandes/questionnements du PC.

Visuellement, voici comment nous pourrions représenter cela :

Echange de données entre PC et Arduino via port USB, communication bidirectionnelle pilotée par ordinateur, avec script python qui contrôle le dialogue

Support de communication : un simple câble USB, pour permettre les échanges PC <-> Arduino

Quelque part, nos PC communiquent déjà nativement avec les Arduino. Cela se fait au moment de la programmation de ces derniers, lorsqu’on les programme depuis nos ordi. Donc quoi de plus naturel que de conserver ce câble USB, pour communiquer ensuite avec nos arduino ?

Surtout que cela nous permet toujours deux choses :

  • alimenter électriquement l’Arduino depuis le PC, d’une part
  • et assurer la communication USB entre PC et Arduino pour l’échange de données, d’autre part

Au passage, il faudra bien noter quel « port COM » a été alloué par votre PC, à votre Arduino (car il sera ensuite nécessaire de le préciser, au niveau du script python). Un moyen simple de savoir cela est d’ouvrir l’IDE Arduino, et d’aller dans le menu Outils > Port (lorsque votre arduino est branché à votre PC). Ainsi, vous verrez clairement affiché quel numéro de port de communication a été attribué à votre Arduino, sur votre PC. Dans mon cas, il s’agit par exemple du port « COM3 », quand je branche mon Arduino Nano dessus :

Port COM arduino sur IDE sketch, attribué par PC en branchant câble USB, pour communiquer avec Arduino Nano, Uno, ou autre, au travers du UART

Langage de communication PC/Arduino : du texte personnalisé comme on veut, tout simplement !

Ici, on peut tout imaginer ! Ou presque 😉

En fait, peu importe la langue ou les mots qu’on prévoit d’utiliser. Seul compte le fait que PC et ARDUINO puissent mutuellement se comprendre, et associer tel mot à telle action. Et si je vous parle de « mots » ici, c’est justement parce que nous allons envoyer des commandes « texte », pour faire dialoguer PC et Arduino.

D’ailleurs, cela se fait très simplement, et vous avez déjà dû le faire, sans même en avoir pris conscience. En effet, lorsque vous faites par exemple un « Serial.println » dans votre code Arduino, cela se traduit par une chaine de caractères envoyée depuis votre Arduino vers votre ordi, qui s’affiche ensuite sur l’écran de votre ordi (via le moniteur série). Nous allons donc utiliser ce principe là ici, mais cette fois-ci, dans les deux sens !

À présent, il faut bien comprendre qu’on peut utiliser les mots que l’on veut. Par exemple, si le PC souhaite connaître la température mesurée par une sonde branchée sur un Arduino, on pourrait très bien imaginer que le PC envoie le mot « temperature » à l’Arduino, et que l’Arduino réponde par ex « 19.2 » à l’ordi (s’il fait 19,2 °C, bien entendu !). En bref, le langage de communication PC/Arduino est celui que nous construirons nous même, avec nos mots ! Tout simplement 😉

Prérequis (logiciels nécessaires)

Dans cette partie, je vais vous parler des logiciels que j’ai utilisé pour faire communiquer un Arduino avec un PC. Bien sûr, il existe bien d’autres outils et façons de faire pour faire faire communiquer un Arduino avec un PC. Mais j’ai souhaité opter pour des choses simples, communes, et faciles à prendre en main. Ainsi, même les débutants pourront suivre ce tuto, et faire leurs premiers pas pour apprendre la communication filaire PC / Arduino !

IDE Arduino (pour programmer sa carte Arduino, et retrouver le n° du port COM associé)

Le premier logiciel dont nous aurons besoin est « Arduino IDE » (téléchargeable ici : https://www.arduino.cc/en/software). Pour ma part, j’utilise la version 1.8.18 (et non la V2), car celle-ci est super stable (à contrario de la V2, sur laquelle j’ai relevé pas mal de bugs non corrigés, encore à l’heure actuelle).

Ce logiciel est 100 % gratuit. Il nous permettra ici de :

  • programmer notre arduino (qui sera chargé d’interpréter les ordres reçus du PC, afin de les exécuter, et d’y répondre)
  • et de déterminer d’un coup d’œil quel port COM a été alloué par le PC, pour établir le dialogue avec la carte arduino

Bien sûr, libre à vous de basculer sur un autre environnement de programmation (comme Visual Studio ou autre), si vous êtes plus à l’aise dessus !

Interpréteur Python (qui nous permettra d’exécuter notre script Python)

Le second logiciel dont nous aurons besoin, est un interpréteur Python. Ainsi, nous pourrons lui faire exécuter du code python, pour piloter l’Arduino depuis un PC.

Ici, je vous partage 2 façons de faire, qui sont 100 % gratuites toutes les deux :

Perso, je me sers aussi bien de l’un comme de l’autre, mais ai toutefois une préférence pour PyCharm ! Un conseil, si vous débutez : commencez avec Pycharm, qui propose une interface graphique conviviale, et moins austère que des « commandes en ligne windows » 😉

Nota : si vous souhaitez apprendre le langage Python ou si vous êtes débutant en la matière, je vous recommande cet excellent tutoriel, disponible sur YouTube :

Remarque : n’hésitez pas à bien « travailler » cette partie, car la « maîtrise » des bases du Python est indispensable pour bien comprendre la suite.

Librairie Pyserial (pour python)

Nous aurons besoin d’une librairie complémentaire, non présente dans l’environnement natif de Python. Il s’agit du package « Pyserial ». Cette bibliothèque nous permettra de faciliter la communication Arduino / PC, dans le script Python, afin de se focaliser sur l’essentiel.

Pour l’installer, il faudra simplement réaliser l’une ou l’autre (ou les deux) des opérations suivantes, selon si vous utilisez Python via des commandes en ligne windows, ou si vous utilisez pycharm.

Si vous utilisez Python via des Commandes en ligne Windows (CMD), il vous suffira de taper « pip install pyserial » dans l’invite de commandes, comme visible ci-dessous :

Installation pyserial sur Windows, via commande en ligne CMD, avec instruction PIP INSTALL PYSERIAL pour importer le package sous Python, et utilité

Si vous utilisez Pycharm, il faudra vous rendre dans le menu View > Tool Windows > Python Packages, puis de taper « pyserial » dans le champ de recherche en bas, lancer la recherche, et enfin, cliquer sur « Install » pour importer cette librairie (comme visible ci-dessous).

Menu package python sous PyCharm, pour importer des librairies à insérer dans programme python, avec l'IDE Py Charm sous Windows, et bibliothèques codage
Installation pyserial sous Pycharm depuis gestionnaire de packages, pour utilisation de Python sous IDE Windows, programmation avec librairie importée

Bien entendu, si vous souhaitez pouvoir utiliser la librairie « pyserial » aussi bien dans l’invite de commande Windows que dans Pycharm, il vous suffira alors de l’installer « des deux côtés » ! Encore une fois, vous êtes libre de faire comme bon vous semble 😉

Programme #1 : Blink (tempo du côté du programme Arduino)

Nous voici fin prêt pour établir notre première connexion PC ↔ Arduino, via Python ! Pour ce faire, je vous propose de voir ensemble un programme exemple qui nous permettra de faire clignoter la LED embarquée sur une carte Arduino Uno, en la pilotant depuis un PC (en somme, refaire un classique programme « Blink », mais piloté à distance cette fois-ci).

Pour arriver à faire ceci, nous allons devoir mettre en œuvre 2 programmes distincts :

  • un script Python, qui sera logé sur PC, et qui :
    • pourra faire démarrer le clignotement de la LED embarquée sur la carte arduino (en envoyant une commande arbitrairement nommée « START BLINK » vers l’arduino)
    • pourra faire stopper le clignotement de la LED embarquée sur l’arduino (en envoyant une commande arbitrairement nommée « STOP BLINK », vers l’arduino)
  • et un programme Arduino, logé dans l’Arduino, qui :
    • interprètera et exécutera les ordres provenant du PC (par exemple : activation du clignotement de la LED, si message « START BLINK » reçu, ou arrêt du clignotement de cette LED, si commande « STOP BLINK » reçue)
    • et renverra un message en retour, au script Python, pour dire que tout s’est bien passé, ou qu’une erreur a été relevée

Ainsi, la communication est ici pseudo bidirectionnelle, et séquentielle (PC vers Arduino, puis Arduino vers PC). En clair :

  • le PC envoie un ordre à l’Arduino
  • l’Arduino exécute cet ordre
  • l’Arduino répond pour dire si tout s’est bien passé, ou si une erreur s’est produite
  • et le PC affiche le message renvoyé par l’Arduino

De manière simplifiée, cela pourrait se représenter de la manière suivante :

Blink arduino piloté à distance avec Python, exemple de protocole de communication entre PC et carte Arduino Uno, et codage python sous windows IDE

Par contre, dans l’ordre, il faudra toujours :

  • programmer l’arduino en premier (en notant au passage le port COM attribué à la carte arduino, par le PC)
  • laisser le câble de programmation USB de l’arduino en place
  • puis lancer le programme Python seulement à ce moment là (en modifiant au besoin le numéro du port COM sur lequel l’arduino se trouve branché)

Au niveau des programmes, voici tout d’abord le programme Python pour ce blink piloté à distance (qui, pour rappel, sera à exécuter seulement une fois l’arduino programmé) :

import serial       # Permet d'exécuter des instructions via le port série
import sys          # Permet d'avoir accès à la fonction exit()

# Déclartion des constantes
PortDeCommunication = "COM4"
VitesseDeCommunication = 9600
DelaiDeTimeOut = 3

CaractereSpecialDebutDeCommunication = chr(0)
CaractereSpecialFinDeCommunication = chr(10)

# ==================================
# Fonction : recueilInstruction
# ==================================
def recueilInstruction():
    instruction_string = input("> ")
    return instruction_string

# ==================================
# Fonction : envoiCommande
# ==================================
def envoiCommande(commandeAenvoyer):
    commande_a_envoyer = CaractereSpecialDebutDeCommunication + commandeAenvoyer + CaractereSpecialFinDeCommunication
    commande_serie.write(commande_a_envoyer.encode())

# ==================================
# Fonction : envoiCommande
# ==================================
def afficheReponseArduino():
    reponse_arduino = commande_serie.readline()
    print(reponse_arduino.decode())

# ==================================
# Programme principal
# ==================================

# Initialisation de la liaison série
commande_serie = serial.Serial()
try:
    print()
    print("Tentative d'initialisation du port série...")
    commande_serie.port = PortDeCommunication
    commande_serie.baudrate = VitesseDeCommunication
    commande_serie.timeout = DelaiDeTimeOut
    commande_serie.open()
except:
    print("[ERREUR] Impossible d'ouvrir le port " + PortDeCommunication)
    sys.exit()
print("Connexion réussie sur le port " + PortDeCommunication + " !")

# Boucle perpétuelle de capture d'instructions, saisies manuellement par l'utilisateur (nota : EXIT permet de sortir de cette boucle)
print()
print("PRG1 - Test pilotage Blink Arduino depuis programme Python")
print("Commandes possibles =")
print("   - START BLINK (pour démarrer le clignotement de la LED Arduino)")
print("   - STOP BLINK (pour arrêter le clignotement de la LED Arduino)")
print("   - EXIT (pour arrêter le programme)")
print()
print("Entrez votre commande :")
while 1:
    commande_string = recueilInstruction()
    commande_string = commande_string.upper()       # nota : on fait passer le texte saisi en majuscule, histoire
                                                    #        de faire plus propre !
    if commande_string == "EXIT":
        break
    else:
        envoiCommande(commande_string)
        afficheReponseArduino()

# Sortie, lorsque l'utilisateur a tapé EXIT et validé par la touche ENTRÉE
print()
commande_serie.close()
print("EXIT : port série fermé, fin du programme.")

Pour expliquer simplement les choses ici, ce programme Python vous invitera à taper une commande :

  • « START BLINK » pour demander le clignotement de la LED présente sur la carte arduino
  • « STOP BLINK » pour demander l’arrêt du clignotement de la LED présente sur la carte arduino
  • et « EXIT » pour quitter le programme python

Pour info, si vous tapez « START BLINK » ou « STOP BLINK », ce programme python enverra littéralement ce texte à l’Arduino. ! Et le programme Arduino se chargera ensuite d’interpréter cela en demande de démarrage ou d’arrêt de sa LED embarquée.

D’ailleurs, en parlant du programme Arduino de ce « Blink piloté à distance », le voici :

/*
   ______               _                  _///_ _           _                   _
  /   _  \             (_)                |  ___| |         | |                 (_)
  |  [_|  |__  ___  ___ _  ___  _ __      | |__ | | ___  ___| |_ _ __ ___  _ __  _  ___  _   _  ___
  |   ___/ _ \| __|| __| |/ _ \| '_ \_____|  __|| |/ _ \/  _|  _| '__/   \| '_ \| |/   \| | | |/ _ \
  |  |  | ( ) |__ ||__ | | ( ) | | | |____| |__ | |  __/| (_| |_| | | (_) | | | | | (_) | |_| |  __/
  \__|   \__,_|___||___|_|\___/|_| [_|    \____/|_|\___|\____\__\_|  \___/|_| |_|_|\__  |\__,_|\___|
                                                                                      | |
                                                                                      \_|
  Fichier :       prg1-PiloterBlinkAdistance-ViaPython.ino
  
  Description :   Programme permettant de faire clignoter la LED embarquée sur une carte Arduino Nano,
                  à partir de commandes extérieures (via un script Python)

  Remarques :     --> la commande "START BLINK" permettra de démarrer le clignotement de la LED
                  --> la commande "STOP BLINK" permettra l'arrêt de ce clignotement
                                    
  Auteur :        Jérôme TOMSKI (https://passionelectronique.fr/)
  Créé le :       08.07.2022

*/

// Constantes
#define brocheOuEstBrancheeLaLED 13       // On va utiliser la broche D13 de l'Arduino Nano, sur laquelle est câblée la LED embarquée

// Variables
String commandeAexecuter = "";
bool commandeEnCoursDeReception = false;
bool clignotementAutorise = false;

// ========================
// Initialisation programme
// ========================
void setup() {

  // Déclaration de la broche où est branchée la LED en sortie
  pinMode(brocheOuEstBrancheeLaLED, OUTPUT);

  // Initialisation de la liaison PC <-> Arduino Nano
  Serial.begin(9600);
 
}


// =================
// Boucle principale
// =================
void loop() {

  // **********************************************************************************************************
  // L'arduino attend les instructions, qui lui sont communiquées par le PC (via le port COM / câble USB, du PC
  // **********************************************************************************************************

  // S'il y a un ou plusieurs octet(s) en attente d'être lu(s), alors on les lit un par un
  while(Serial.available() > 0) {
    
    // On récupère un octet (le plus ancien, s'il y en a plusieurs)
    char octetRecu = Serial.read();

    // Nota : afin d'éviter toute erreur de transmission, j'ai fait le choix d'ajouter des caractères spéciaux en début et fin de commande.
    //        Ainsi, chaque instruction de commande commencera par un caractère nul (char 0), et finira par un saut à la ligne (char 10).
    //        En procédant ainsi, on évitera tout instruction de commande incomplète, ce qui sécurise la transmission (même si ce n'est pas vraiment indispensable ici).

    // On teste si l'octet reçu est égal au "caractère nul" (char 0)
    if(octetRecu == char(0)) {
      commandeEnCoursDeReception = true;      // Si oui, alors on va assembler tous les octets qui suivent,
      commandeAexecuter = "";                 // jusqu'à recevoir le caractère de fin, le fameux "saut de ligne" (char 10)
    }
    
    // On teste si l'octet reçu est égal au "caractère saut de ligne" (char 10)
    if(octetRecu == char(10)) {
      commandeEnCoursDeReception = false;     // Nous avons à présent pu récolter l'ensemble des octets constituant la commande extérieure
      executeCommande(commandeAexecuter);     // Il ne reste donc plus qu'à exécuter cette dernière !
    }

    // Si ce n'est aucun de ces 2 caractères spéciaux, le caractère reçu est à concaténer aux précédents reçus (car cela signifie qu'une commande est en cours de réception)
    if((octetRecu != char(0)) && (octetRecu != char(10)) && commandeEnCoursDeReception) {
      commandeAexecuter = commandeAexecuter + octetRecu;
    }
  }

  // ***************************************************************************************************************************************
  // En dehors de toute commande transmise par le PC, l'Arduino fera son blink normalement (si ça a été préalablement demandé, bien entendu)
  // ***************************************************************************************************************************************
  if(clignotementAutorise) {
    digitalWrite(brocheOuEstBrancheeLaLED, HIGH);   // allumage de la LED embarquée
    delay(500);                                     // pause d'une demi-seconde
    digitalWrite(brocheOuEstBrancheeLaLED, LOW);    // extinction de la LED
    delay(500);                                     // et repause d'une demi-seconde
  }

}

// ==========================
// Fonction : executeCommande
// ==========================
void executeCommande(String texteDeLaCommande) {

  if(texteDeLaCommande == "START BLINK") {
      clignotementAutorise = true;
      Serial.println("[Réponse ARDUINO] Clignotement démarré !");
  } 
  
  else if(texteDeLaCommande == "STOP BLINK") {
      clignotementAutorise = false;
      Serial.println("[Réponse ARDUINO] Clignotement arrêté");
  }
  
  else {
      Serial.println("[Réponse ARDUINO] Commande '" + texteDeLaCommande + "' inconnue…");
  }

}

Si je devais résumer ce code, je dirais que tout se passe dans la boucle loop, où l’arduino « écoute » en quasi permanence tout message provenant du PC. Et lorsqu’il reçoit les octets d’un message, il les mémorise les uns après les autres, jusqu’à reformer le message en entier (avec, au passage, un caractère spécial en début et en fin de message, pour s’assurer qu’il ne manque aucun morceau au message). Ensuite :

  • l’Arduino fera clignoter sa LED, s’il a reçu une commande égale à « START BLINK »
  • l’Arduino éteindra le clignotement de sa LED, s’il a reçu une commande égale à « STOP BLINK »

Comme vous l’aurez compris, c’est l’arduino qui définit à quelle vitesse la LED va clignoter (car le programme distant, écrit en python, ne fait que lui demander de démarrer ou d’arrêter ce clignotement).

Maintenant que tout est dit, il vous faut, comme exposé précédemment, programmer votre Arduino en premier. Et dans la foulée, noter quel port COM a été attribué par votre PC, pour votre carte arduino. De mon côté, c’est le port « COM4 » qui a été alloué par mon PC, pour interagir avec ma carte Arduino Uno :

Retrouver port COM arduino dans IDE, pour savoir quel numéro de port USB a été alloué par le PC pour communiquer avec l'Arduino, en protocole série UART

Ensuite, tout en laissant votre câble de programmation USB arduino branché, il vous suffit de lancer votre programme Python.

Pour rappel, pour exécuter votre programme écrit en python, vous pourrez :

  • soit le lancer via les commandes en ligne windows
  • soit le lancer via Pycharm (à préférer, si vous débutez)

Si vous utilisez l’interpréteur de commandes windows, voici ce que cela peut donner (en notant que si jamais vous tapez une mauvaise commande, le programme arduino vous le fera savoir, en retournant un message d’erreur au programme python, qui vous l’affichera) :

Exécution programme python en ligne pour piloter LED branchée sur arduino, avec commandes START et STOP BLINK, pour démarrer ou arrêter le clignotement

À noter aussi que si jamais vous avez un problème de connexion, le script python va également vous le faire savoir, comme visible dans l’exemple ci-dessous (test effectué après avoir volontairement débranché le câble de communication USB entre PC et Arduino) :

Exemple python échec de communication via port COM, pour échanger données avec un Arduino à l'aide d'un PC Windows, exemple avec COM4 série

Enfin, si vous utilisez Pycharm, voici ce que cela donne (toujours avec des tests de « mauvaises commandes », pour voir si l’Arduino identifie bien les soucis, à ce niveau) :

Retour programme python qui communique avec arduino via port série COM, échange de commande STOP et START BLINK pour tester clignotement LED

Remarque : lorsque le script python est lancé, celui-ci « prend le contrôle » du port COM « PC-Arduino ». En conséquence, il vous sera impossible de reprogrammer votre arduino ou autre, tant que vous n’aurez pas arrêté votre programme python (via la commande « EXIT », ici). Ainsi, vous libérerez le port COM de tout usage, et pourrez en reprendre le contrôle. Dans tous les cas, pensez-y ! Sinon vous risquez fort d’avoir des erreurs du type « port indisponible », sans trop comprendre pourquoi !

Programme #2 : Chenillard (tempo du côté du programme Python)

Dans le programme précédent, nous avons vu comment un script Python peut demander à l’Arduino d’initier le clignotement de sa LED embarquée, ou d’arrêter son clignotement (la vitesse de clignotement étant définie côté arduino). Ici, nous allons prendre un peu plus le contrôle de l’arduino à distance, en pilotant nous même la LED intégrée sur l’Arduino. Du coup, on définira nous même quelle sera la fréquence de clignotement de la LED, depuis le PC, via le programme Python !

Et pour aller plus loin encore, nous n’allons plus nous limiter à une sortie arduino pilotée à distance, mais plusieurs. Par ailleurs, pour que ce soit plus visuel, nous allons réaliser ici un chenillard à 8 leds, toutes pilotées à distance, via Python, tournant sur PC. Ces LEDs seront branchées sur les sorties D2 à D9 d’un Arduino. Au final, l’Arduino ne fera que mettre à l’état haut ou à l’état bas, les sorties que le programme python lui aura demandé de mettre 😉

Pour ce faire, nous allons réaliser le montage suivant :

Réalisation chenillard arduino piloté à distance sur Python, schéma de câblage de LED sur un Arduino Nano, avec réseau de résistances et bargraphe

À noter qu’ici, j’ai préféré utiliser un bargraphe à 8 leds et un réseau de résistances, plutôt que d’avoir à sortir 8 leds « classiques », et 8 résistances individuelles. Cela nous permet de simplifier le schéma, ainsi que le câblage sur breadboard, au moment des tests !

Au niveau logiciel, voici comment vont se dérouler les choses :

Protocole de communication PC Arduino avec Python, pilotage de la transmission des messages, avec émission et réception bidirectionnelle sous windows

Grosso modo, le PC (via le script Python) enverra une commande de type « digitalWrite XX YYY », pour mettre la sortie XX à l’état YYY. Plus précisément :

  • XX correspondra à la sortie arduino visée (dans notre exemple XX vaudra D2, D3, D4, D5, D6, D7, D8, ou D9, pour viser l’une des 8 leds de notre chenillard)
  • et YYY sera égal à « LOW » ou « HIGH », selon que l’on souhaite, respectivement, mettre la sortie visée à l’état bas (0 volt) ou à l’état haut (+5V)

Voici d’ailleurs le programme python de ce chenillard :

import serial       # Permet d'exécuter des instructions via le port série
import sys          # Permet d'avoir accès à la fonction exit()
import time         # Permet d'avoir accès à la fonction sleep()

# Déclartion des constantes
PortDeCommunication = "COM3"
VitesseDeCommunication = 9600
DelaiDeTimeOut = 3

CaractereSpecialDebutDeCommunication = chr(0)
CaractereSpecialFinDeCommunication = chr(10)

DelaiAvancementChenillardEnSecondes = 0.1

# ==================================
# Fonction : envoiCommande
# ==================================
def envoiCommande(commandeAenvoyer):
    commande_a_envoyer = CaractereSpecialDebutDeCommunication + commandeAenvoyer + CaractereSpecialFinDeCommunication
    commande_serie.write(commande_a_envoyer.encode())

# ==================================
# Programme principal
# ==================================

# Initialisation de la liaison série
commande_serie = serial.Serial()
try:
    print()
    print("Tentative d'initialisation du port série...")
    commande_serie.port = PortDeCommunication
    commande_serie.baudrate = VitesseDeCommunication
    commande_serie.timeout = DelaiDeTimeOut
    commande_serie.open()
except:
    print("[ERREUR] Impossible d'ouvrir le port " + PortDeCommunication)
    sys.exit()
print("Connexion réussie sur le port " + PortDeCommunication + " !")

# Boucle perpétuelle de capture d'instructions, saisies manuellement (nota : EXIT permet de sortir de cette boucle)
print()
time.sleep(2)                   # tempo avant lancement chenillard
print("Chenillard sur D2 à D9 (8 leds, donc)")
while 1:
    envoiCommande("digitalWrite D2 HIGH")
    envoiCommande("digitalWrite D9 LOW")
    time.sleep(DelaiAvancementChenillardEnSecondes)
    envoiCommande("digitalWrite D3 HIGH")
    envoiCommande("digitalWrite D2 LOW")
    time.sleep(DelaiAvancementChenillardEnSecondes)
    envoiCommande("digitalWrite D4 HIGH")
    envoiCommande("digitalWrite D3 LOW")
    time.sleep(DelaiAvancementChenillardEnSecondes)
    envoiCommande("digitalWrite D5 HIGH")
    envoiCommande("digitalWrite D4 LOW")
    time.sleep(DelaiAvancementChenillardEnSecondes)
    envoiCommande("digitalWrite D6 HIGH")
    envoiCommande("digitalWrite D5 LOW")
    time.sleep(DelaiAvancementChenillardEnSecondes)
    envoiCommande("digitalWrite D7 HIGH")
    envoiCommande("digitalWrite D6 LOW")
    time.sleep(DelaiAvancementChenillardEnSecondes)
    envoiCommande("digitalWrite D8 HIGH")
    envoiCommande("digitalWrite D7 LOW")
    time.sleep(DelaiAvancementChenillardEnSecondes)
    envoiCommande("digitalWrite D9 HIGH")
    envoiCommande("digitalWrite D8 LOW")
    time.sleep(DelaiAvancementChenillardEnSecondes)

À noter que j’ai utilisé ici le mot clef « digitalWrite » pour communiquer avec l’Arduino, comme j’aurais pu mettre le mot « toto » ou « titi » ou autre, à la place. Ce que j’essaye de vous dire ici, c’est que la seule chose qui compte réellement est d’avoir une série de caractères que le programme Arduino saura reconnaître, pour exécuter telle ou telle commande. Du coup, ne pensez pas que parce que nous allons par la suite utiliser la commande « digitalWrite » côté arduino, qu’il fallait nécessairement mettre ce même mot côté python. Car encore une fois, j’aurais très bien pu mettre par exemple le mot « patate » côté python, à la place de « digitalWrite », du moment où le programme arduino saurait que la commande « patate » correspondrait bel et bien à une demande de faire passer une sortie donnée à un état donné. Gardez bien cela à l’esprit, car c’est seulement après coup que je me suis rendu compte qu’il pourrait y avoir un peu de confusion ici !

À présent, voici le programme arduino de ce chenillard :

/*
   ______               _                  _///_ _           _                   _
  /   _  \             (_)                |  ___| |         | |                 (_)
  |  [_|  |__  ___  ___ _  ___  _ __      | |__ | | ___  ___| |_ _ __ ___  _ __  _  ___  _   _  ___
  |   ___/ _ \| __|| __| |/ _ \| '_ \_____|  __|| |/ _ \/  _|  _| '__/   \| '_ \| |/   \| | | |/ _ \
  |  |  | ( ) |__ ||__ | | ( ) | | | |____| |__ | |  __/| (_| |_| | | (_) | | | | | (_) | |_| |  __/
  \__|   \__,_|___||___|_|\___/|_| [_|    \____/|_|\___|\____\__\_|  \___/|_| |_|_|\__  |\__,_|\___|
                                                                                      | |
                                                                                      \_|
  Fichier :       prg2-ChenillardPythonQuiPiloteArduino.ino
  
  Description :   Programme permettant d'exécuter des ordres de type "digitalWrite",
                  dictés par un programme distant écrit en Python

  Remarques :     --> la commande distante "digitalWrite" prend 2 arguments :
                            >>> le numéro de broche digitale à piloter (D2 à D9, dans ce programme)
                            >>> et l'état électrique à fixer (LOW ou HIGH)
                  --> exemple de commande distante :
                            >>> "digitalWrite D2 HIGH" (pour mettre la broche D2 de l'Arduino à l'état haut)
                            >>> "digitalWrite D5 LOW"  (pour mettre la broche D5 de l'Arduino à l'état bas)
                                    
  Auteur :        Jérôme TOMSKI (https://passionelectronique.fr/)
  Créé le :       09.07.2022

*/

// Variables
String commandeAexecuter = "";
bool commandeEnCoursDeReception = false;

// ========================
// Initialisation programme
// ========================
void setup() {

  // ****************************************************************************************************************************
  // Nota : le chenillard sera constitué de 8 LEDS, branchées sur les broches D2, D3, D4, D5, D6, D7, D8, et D9 d'un Arduino Nano
  // ****************************************************************************************************************************
  
  // Déclaration des pins D2 à D9 en "sortie"
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);

  // Initialisation de la liaison Arduino Nano <-> PC
  Serial.begin(9600);
 
}


// =================
// Boucle principale
// =================
void loop() {

  // *********************************************************************************************
  // L'arduino attend les instructions, qui lui sont communiquées par le PC (via le script python)
  // *********************************************************************************************

  // S'il y a un ou plusieurs octet(s) en attente d'être lu(s), alors on les lit un par un
  while(Serial.available() > 0) {
    
    // On récupère un octet (le plus ancien d'abord, s'il y en a plusieurs)
    char octetRecu = Serial.read();

    // Nota : afin d'éviter toute erreur de transmission, j'ai fait le choix d'ajouter des caractères spéciaux en début et fin de chaine de commande.
    //        Ainsi, chaque instruction de commande commencera par un caractère nul (char 0), et finira par un saut à la ligne (char 10).
    //        En procédant ainsi, on évitera toute transmission incomplète, ou erreur de transmission.

    // On teste si l'octet reçu est égal au "caractère nul" (char 0)
    if(octetRecu == char(0)) {
      commandeEnCoursDeReception = true;      // Si oui, alors on va assembler tous les octets qui suivent,
      commandeAexecuter = "";                 // jusqu'à recevoir le "caractère saut de ligne" (char 10)
    }
    
    // On teste si l'octet reçu est égal au "caractère saut de ligne" (char 10)
    if(octetRecu == char(10)) {
      commandeEnCoursDeReception = false;     // Nous avons à présent pu récolter l'ensemble des octets constituant la commande extérieure
      executeCommande(commandeAexecuter);     // Il ne reste donc plus qu'à exécuter cette commande, maintenant intégralement reçue
    }

    // Si ce n'est aucun de ces 2 caractères spéciaux, c'est que l'instruction est en cours de réception (on ajoute donc le caractère reçu au précédent)
    if((octetRecu != char(0)) && (octetRecu != char(10)) && commandeEnCoursDeReception) {
      commandeAexecuter = commandeAexecuter + octetRecu;
    }
  }

}

// ==========================
// Fonction : executeCommande
// ==========================
void executeCommande(String texteDeLaCommande) {

  // Remarque : on attend ici une commande du type "digitalWrite numéroDeBroche etatElectrique" (donc 3 termes, avec des espaces de séparation)
  //            (par ex : 'digitalWrite D4 LOW', pour mettre la sortie D4 à l'état bas)

  // *********************************************************************
  // On récupère le 1er terme (qui doit être égal au texte "digitalWrite")
  // *********************************************************************
  int indexDuPremierEspaceDeSeparation = texteDeLaCommande.indexOf(" ");
  if(indexDuPremierEspaceDeSeparation == -1) {
    Serial.println("[ERREUR ARDUINO] Aucun espace de séparation trouvé. Commande attendue : 'digitalWrite broche_num output_level'");
    return;     // Si jamais on ne trouve aucun espace de séparation dans la commande, alors on quitte cette fonction
  }
  
  String premierTerme = texteDeLaCommande.substring(0, indexDuPremierEspaceDeSeparation);
  if(premierTerme != "digitalWrite") {
    Serial.println("[ERREUR ARDUINO] Le nom du 1er terme doit être 'digitalWrite'. Or vous avez saisi '" + premierTerme + "'");
    return;     // Si jamais le 1er terme de la commande n'est pas égal au texte "digitalWrite", alors on quitte cette fonction
  }

  // ************************************************************************
  // On récupère le 2ème terme (qui indique quelle broche doit être utilisée)
  // ************************************************************************
  String texteSuivant = texteDeLaCommande.substring(indexDuPremierEspaceDeSeparation +1 , texteDeLaCommande.length());
  int indexDuSecondEspaceDeSeparation = texteSuivant.indexOf(" ");
  if(indexDuSecondEspaceDeSeparation == -1) {
    Serial.println("[ERREUR ARDUINO] Second espace introuvable. Commande attendue : 'digitalWrite broche_num output_level'");
    return;     // Si jamais on ne trouve pas le second espace de séparation dans la commande, alors on quitte cette fonction
  }

  String secondTerme = texteSuivant.substring(0, indexDuSecondEspaceDeSeparation);
  if(secondTerme.substring(0,1) != "D") {
    Serial.println("[ERREUR ARDUINO] Le second terme doit commencer par un 'D' majuscule. Là vous avez mis un '" + secondTerme.substring(0,1) + "'");
    return;     // Si jamais le 2nd terme ne commence pas par D, alors on quitte également cette fonction
  }

  if(secondTerme.length() != 2) {
    Serial.println("[ERREUR ARDUINO] Le 2nd terme doit comporter deux caractères, du style Dx. Commande attendue : 'digitalWrite broche_num output_level'");
    return;     // Si jamais le 2nd terme ne contient pas au moins 2 caractères, alors on quitte cette fonction
  }

  char caractereNumeroDeBroche = secondTerme.charAt(1);
    if(!isDigit(caractereNumeroDeBroche)) {
      Serial.println("[ERREUR ARDUINO] Le numéro de broche doit être numérique, de type Dx. Commande attendue : 'digitalWrite broche_num output_level'");
      return;     // Si jamais le numéro de broche n'est pas numérique, on quitte cette fonction
   }
  int intNumeroDeBroche = secondTerme.substring(1,2).toInt();

  if(intNumeroDeBroche < 2 || intNumeroDeBroche > 9) {
    Serial.println("[ERREUR ARDUINO] Le numéro de broche doit compris entre D2 et D9. Là vous avez mis '" + secondTerme.substring(1,2) + "'");
    return;     // Si jamais le numéro de broche n'est pas compris entre 2 et 9, alors on quitte cette fonction
  }

  // ************************************************************************
  // On récupère le 3ème terme (qui indique l'état de la broche, LOW ou HIGH)
  // ************************************************************************
  String troisiemeTerme = texteSuivant.substring(indexDuSecondEspaceDeSeparation +1 , texteSuivant.length());

  if((troisiemeTerme != "LOW") && (troisiemeTerme != "HIGH")) {
    Serial.println("[ERREUR ARDUINO] Le 3ème terme doit être LOW ou HIGH. Là, vous avez entré la valeur '" + troisiemeTerme + "'");
    return;     // Si jamais le numéro de broche n'est pas compris entre 2 et 9, alors on quitte cette fonction
  }

  // **********************************************************************
  // Et on fixe la sortie susvisée (D2 à D9) à l'état demandé (LOW ou HIGH)
  // **********************************************************************
  if(troisiemeTerme == "LOW")
    digitalWrite(intNumeroDeBroche, LOW);
    
  if(troisiemeTerme == "HIGH")
    digitalWrite(intNumeroDeBroche, HIGH);
  
}

Dans le principe, ce code arduino est identique à l’exemple précédent. À ceci près que j’ai rajouté du code pour vous montrer comment faire du « contrôle d’erreur » côté arduino, au cas où la commande python ne serait pas « bien » formatée ou orthographiée (c’est à dire sous la forme « digitalWrite Dx LOW/HIGH »).

Remarque : comme dans l’exemple précédent, il faudra tout d’abord programmer l’arduino, et ensuite seulement, exécuter le programme python. Au passage, il faudra vérifier quel « port COM » a été attribué par votre PC à votre carte arduino, et éventuellement modifier la valeur renseignée par défaut dans le script python (variable « PortDeCommunication »). Dans mon cas, mon Arduino Nano est raccordé au port COM3, comme visible dans l’image ci-dessous, au moment de sa programmation avec l’IDE Arduino.

Port USB arduino identifié par IDE Arduino, pour communication via port COM d'un Nano ou autre modèle, permettant le pilotage distant avec Python

Si tout est bon, vous devriez voir votre chenillard s’illuminer de manière perpétuelle, de droite à gauche. Enfin… si vous avez câblé vos leds de la même façon que moi, sur votre breadboard, comme visible ci-dessous :

Câblage chenillard arduino sur breadboard, avec bargraphe de LED rouges et réseau de résistances pour limitation de courant, version Nano pilotage distant

Autre remarque : compte tenu que nous communiquons via le port COM, et que la vitesse de transmission est limitée par celui-ci, il aurait bien évidemment été plus avisé de programmer ce chenillard côté arduino, plutôt que python. Car si l’on devait augmenter la vitesse de ce chenillard, il y aurait à coup sûr une limite de vitesse de transmission, que nous ne pourrions dépasser (même si cela ne se verrait pas forcément à l’œil nu). Cela étant dit, gardez bien à l’esprit que ceci n’est qu’un exemple de communication, pour apprendre à mettre sur pied une communication entre Python et Arduino. L’optimisation est donc mise volontairement de côté ici 😉

Programme #3 : Lecture à distance des entrées digitales ou analogiques d’un Arduino

Maintenant que nous avons vu comment piloter les sorties d’un arduino depuis un programme python, il nous reste plus qu’à voir comment lire une entrée Arduino, numérique ou analogique, depuis un script Python !

Le but de cet exemple sera de pouvoir lire l’état de toute entrée numérique (D2 à D9), et valeur de toute entrée analogique (A0 à A5). Mais pour faire nos essais, nous brancherons simplement un potentiomètre par exemple sur l’entrée A2 de notre arduino, afin de tester la partie « mesure de valeur analogique à distance ». Pour cela, nous allons nous servir du montage suivant, très simple :

Schéma branchement potentiomètre 10k sur Arduino Nano, pour faire varier tension sur entrée analogique, et lire valeur avec analogRead via Python

Avec ce câblage là en particulier, nous pourrons lire depuis l’ordi la valeur analogique de l’entrée analogique A2 (valeur 10 bits pouvant aller de 0 à 1023, qui sera l’image de la tension 0 à +5V) du potentiomètre branché sur cette entrée. Bien sûr, il faudra adapter ce schéma pour lire les autres entrées analogiques (A0, A1, A3, A4, et A5). De même pour les entrées digitales (D2 à D9).

Nota : je me suis limité ici aux entrées numériques D2 à D9, afin de ne pas toucher aux entrées D0/D1 qui servent à la communication UART de l’arduino (TX/RX), et ne pas avoir à faire du contrôle de format sur 2 chiffres (avec D10, D11, D12, et D13), afin de ne pas compliquer le code arduino).

Comme toujours, ce sera le programme Python qui enverra des demandes de lecture, et l’Arduino répondra. Ces requêtes, plus globalement, seront prototypées de la manière suivante :

  • analogRead A[0..5] pour lire la valeur d’une entrée analogique (par exemple : « analogRead A2 » pour lire la valeur de l’entrée analogique A2 de l’arduino) ; c’est d’ailleurs ce que le montage ci-dessus nous permettra de faire.
  • digitalRead D[2..9] pour lire l’état d’une entrée numérique (par exemple : « digitalRead D3 » pour lire l’état de l’entrée numérique D3 de l’arduino) ; là, il faudra manuellement tester les entrées souhaitées, en les reliant à GND (0 volt) ou à VCC (+5V), pour respectivement simuler un état bas, ou un état haut.

Côté programme Python, voici ce qu’il comprend :

import serial       # Permet d'exécuter des instructions via le port série
import sys          # Permet d'avoir accès à la fonction exit()

# Déclartion des constantes
PortDeCommunication = "COM3"
VitesseDeCommunication = 9600
DelaiDeTimeOut = 3

CaractereSpecialDebutDeCommunication = chr(0)
CaractereSpecialFinDeCommunication = chr(10)

# ==================================
# Fonction : recueilInstruction
# ==================================
def recueilInstruction():
    instruction_string = input("> ")
    return instruction_string

# ==================================
# Fonction : envoiCommande
# ==================================
def envoiCommande(commandeAenvoyer):
    commande_a_envoyer = CaractereSpecialDebutDeCommunication + commandeAenvoyer + CaractereSpecialFinDeCommunication
    commande_serie.write(commande_a_envoyer.encode())

# ==================================
# Fonction : envoiCommande
# ==================================
def afficheReponseArduino():
    reponse_arduino = commande_serie.readline()
    print(reponse_arduino.decode())

# ==================================
# Programme principal
# ==================================

# Initialisation de la liaison série
commande_serie = serial.Serial()
try:
    print()
    print("Tentative d'initialisation du port série...")
    commande_serie.port = PortDeCommunication
    commande_serie.baudrate = VitesseDeCommunication
    commande_serie.timeout = DelaiDeTimeOut
    commande_serie.open()
except:
    print("[ERREUR] Impossible d'ouvrir le port " + PortDeCommunication)
    sys.exit()
print("Connexion réussie sur le port " + PortDeCommunication + " !")

# Boucle perpétuelle de capture d'instructions, saisies manuellement (nota : EXIT permet de mettre fin à ce script)
print()
print("PRG3 - Test lecture entrées digitales/analogiques d'un Arduino, depuis un programme Python")
print("Commandes possibles =")
print("   - analogRead A[0..5]     (par exemple : analogRead A2 pour lire la valeur de l'entrée A2")
print("   - digitalRead D[2..9]    (par exemple : digitalRead D7 pour lire la valeur de l'entrée D7")
print("   - exit                   (pour quitter ce programme)")
print("Attention aux majuscules/minuscules, quand vous écrivez vos commandes !")
print()
print("Entrez votre commande :")
while 1:
    commande_string = recueilInstruction()

    if commande_string == "exit":
        break
    else:
        envoiCommande(commande_string)
        afficheReponseArduino()

# Sortie, lorsque l'utilisateur a tapé EXIT et validé par la touche ENTRÉE
print()
commande_serie.close()
print("EXIT : fermeture du port " + PortDeCommunication + ". Fin du programme.")

Et côté programme Arduino, voici ce qu’on retrouve :

/*
   ______               _                  _///_ _           _                   _
  /   _  \             (_)                |  ___| |         | |                 (_)
  |  [_|  |__  ___  ___ _  ___  _ __      | |__ | | ___  ___| |_ _ __ ___  _ __  _  ___  _   _  ___
  |   ___/ _ \| __|| __| |/ _ \| '_ \_____|  __|| |/ _ \/  _|  _| '__/   \| '_ \| |/   \| | | |/ _ \
  |  |  | ( ) |__ ||__ | | ( ) | | | |____| |__ | |  __/| (_| |_| | | (_) | | | | | (_) | |_| |  __/
  \__|   \__,_|___||___|_|\___/|_| [_|    \____/|_|\___|\____\__\_|  \___/|_| |_|_|\__  |\__,_|\___|
                                                                                      | |
                                                                                      \_|
  Fichier :       prg3-CommandeLectureEntreeArduinoDepuisPython.ino
  
  Description :   Programme permettant d'exécuter des ordres de type "digitalRead" et "analogRead",
                  dictés par un programme distant écrit en Python

  Remarques :     --> la commande distante "digitalRead" prend 1 argument :
                            >>> le numéro de broche digitale à lire (de D2 à D9, ici)
                  --> la commande distante "analogRead" prend 1 argument :
                            >>> le numéro de broche analogique à lire (de A0 à A5, ici)
                                    
  Auteur :        Jérôme TOMSKI (https://passionelectronique.fr/)
  Créé le :       09.07.2022

*/

// Variables
String commandeAexecuter = "";
bool commandeEnCoursDeReception = false;

// ========================
// Initialisation programme
// ========================
void setup() {

  // Passage des broches D2 à D9 en entrée
  pinMode(2, INPUT);  pinMode(3, INPUT);  pinMode(4, INPUT);  pinMode(5, INPUT);
  pinMode(6, INPUT);  pinMode(7, INPUT);  pinMode(8, INPUT);  pinMode(9, INPUT);

  // Passage des broches A0 à A5 en entrée
  pinMode(A0, INPUT);  pinMode(A1, INPUT);  pinMode(A2, INPUT);
  pinMode(A3, INPUT);  pinMode(A4, INPUT);  pinMode(A5, INPUT);
  
  // Initialisation de la liaison série : PC <-> Arduino Nano
  Serial.begin(9600);
 
}


// =================
// Boucle principale
// =================
void loop() {

  // ****************************************************************************
  // L'arduino attend les instructions, qui lui sont communiquées par l'extérieur
  // ****************************************************************************

  // S'il y a un ou plusieurs octet(s) en attente d'être lu(s), alors on les lit un par un
  while(Serial.available() > 0) {
    
    // On récupère un octet (le plus ancien, s'il y en a plusieurs)
    char octetRecu = Serial.read();

    // Nota : afin d'éviter toute erreur de transmission, j'ai fait le choix d'ajouter des caractères spéciaux en début et fin de chaine de commande.
    //        Ainsi, chaque instruction de commande commencera par un caractère nul (char 0), et finira par un saut à la ligne (char 10).
    //        En procédant ainsi, on évitera tout instruction de commande incomplète (c'est plus sûr, même si ce n'est pas vraiment nécessaire ici).

    // On teste si l'octet reçu est égal au "caractère nul" (char 0)
    if(octetRecu == char(0)) {
      commandeEnCoursDeReception = true;      // Si oui, alors on va assembler tous les octets qui suivent,
      commandeAexecuter = "";                 // jusqu'à recevoir le "caractère saut de ligne" (char 10)
    }
    
    // On teste si l'octet reçu est égal au "caractère saut de ligne" (char 10)
    if(octetRecu == char(10)) {
      commandeEnCoursDeReception = false;     // Nous avons à présent pu récolter l'ensemble des octets constituant la commande extérieure
      executeCommande(commandeAexecuter);
    }

    // Si ce n'est aucun de ces 2 caractères spéciaux, et qu'une commande est en cours de réception
    if((octetRecu != char(0)) && (octetRecu != char(10)) && commandeEnCoursDeReception) {
      commandeAexecuter = commandeAexecuter + octetRecu;
    }
  }

}

// ==========================
// Fonction : executeCommande
// ==========================
void executeCommande(String texteDeLaCommande) {

  // Remarque : on attend ici une commande du type "digitalRead numéroDeBroche", ou "analogRead numéroDeBroche" (donc 2 termes, séparés par un espace)
  //            (par ex : 'digitalRead D7' pour lire l'entrée D7 ; ou 'analogRead A5' pour lire l'entrée A5)

  // ***************************************************************************************
  // On récupère le 1er terme (qui doit être égal à la valeur "digitalRead" ou "analogRead")
  // ***************************************************************************************
  int indexDuPremierEspaceDeSeparation = texteDeLaCommande.indexOf(" ");
  if(indexDuPremierEspaceDeSeparation == -1) {
    Serial.println("[ERREUR ARDUINO] Aucun espace de séparation trouvé. Commande attendue : 'digitalRead broche_num' ou 'analogRead broche_num'");
    return;     // Si jamais on ne trouve aucun espace de séparation dans la commande, on quitte cette fonction
  }
  
  String premierTerme = texteDeLaCommande.substring(0, indexDuPremierEspaceDeSeparation);
  if((premierTerme != "digitalRead") && (premierTerme != "analogRead")) {
    Serial.println("[ERREUR ARDUINO] Le nom du 1er terme doit être 'analogRead' ou 'digitalRead'. Or vous avez saisi '" + premierTerme + "'");
    return;     // Si jamais le 1er terme de la commande n'est pas égal au texte "digitalRead" ou "analogRead", on quitte cette fonction
  }

  // *******************************************************************************
  // On récupère le 2ème terme (qui indique quelle broche on doit prendre en compte)
  // *******************************************************************************
  String secondTerme = texteDeLaCommande.substring(indexDuPremierEspaceDeSeparation +1 , texteDeLaCommande.length());
  if((secondTerme.substring(0,1) != "D") && (secondTerme.substring(0,1) != "A")) {
    Serial.println("[ERREUR ARDUINO] Le second terme doit commencer par un 'A' ou un 'D' majuscule. Là vous avez mis un '" + secondTerme.substring(0,1) + "'");
    return;     // Si jamais le 2nd terme ne commence pas par un D ou un A, alors on quitte également cette fonction
  }

  if(secondTerme.length() != 2) {
    Serial.println("[ERREUR ARDUINO] Le 2nd terme doit comporter deux caractères seulement (Ax ou Dx)");
    return;     // Si jamais le 2nd terme ne contient pas au moins 2 caractères, alors on quitte cette fonction
  }

  char caractereNumeroDeBroche = secondTerme.charAt(1);
  if(!isDigit(caractereNumeroDeBroche)) {
    Serial.println("[ERREUR ARDUINO] Le numéro de broche doit être numérique, de type Dx ou Ax");
    return;     // Si jamais le numéro de broche n'est pas numérique, on quitte cette fonction
  }
  String strTypeDeBroche = secondTerme.substring(0,1);
  String strNumeroDeBroche = secondTerme.substring(1,2);
  int intNumeroDeBroche = strNumeroDeBroche.toInt();

  if(premierTerme == "digitalRead" && strTypeDeBroche != "D") {
    Serial.println("[ERREUR ARDUINO] Le numéro de broche digitale doit commencer par la lettre D. Là vous avez mis '" + strTypeDeBroche + "'");
    return;     // Si jamais le numéro de broche digitale ne commence pas par la lettre D, alors on quitte cette fonction
  }
  if(strTypeDeBroche == "D" && (intNumeroDeBroche < 2 || intNumeroDeBroche > 9)) {
    Serial.println("[ERREUR ARDUINO] Le numéro de broche digitale doit compris entre D2 et D9. Là vous avez mis '" + strNumeroDeBroche + "'");
    return;     // Si jamais le numéro de broche digitale n'est pas compris entre 2 et 9 compris, alors on quitte cette fonction
  }
  
  if(premierTerme == "analogRead" && strTypeDeBroche != "A") {
    Serial.println("[ERREUR ARDUINO] Le numéro de broche analogique doit commencer par la lettre A. Là vous avez mis '" + strTypeDeBroche + "'");
    return;     // Si jamais le numéro de broche analogique ne commence pas par la lettre A, alors on quitte cette fonction
  }
  if(premierTerme == "analogRead" && (intNumeroDeBroche < 0 || intNumeroDeBroche > 5)) {
    Serial.println("[ERREUR ARDUINO] Le numéro de broche analogique doit compris entre A0 et A5. Là vous avez mis '" + strNumeroDeBroche + "'");
    return;     // Si jamais le numéro de broche analogique n'est pas compris entre 0 et 5 compris, alors on quitte cette fonction
  }

  // *********************************************
  // Résultat
  // *********************************************
  if(premierTerme == "digitalRead") {
    Serial.println("Entrée digitale D" + strNumeroDeBroche + " = " + digitalRead(intNumeroDeBroche));
  }
  
  if(premierTerme == "analogRead") {
    int valeurAnalogiqueLue = 0;
    switch(intNumeroDeBroche) {
      case 0:
        valeurAnalogiqueLue = analogRead(A0);
        break;
      case 1:
        valeurAnalogiqueLue = analogRead(A1);
        break;
      case 2:
        valeurAnalogiqueLue = analogRead(A2);
        break;
      case 3:
        valeurAnalogiqueLue = analogRead(A3);
        break;
      case 4:
        valeurAnalogiqueLue = analogRead(A4);
        break;
      case 5:
        valeurAnalogiqueLue = analogRead(A5);
        break;
      default:
        break;
    }
    Serial.println("Entrée analogique A" + strNumeroDeBroche + " = " + valeurAnalogiqueLue);
  }

}

Comme toujours jusqu’à maintenant, il faudra programmer l’arduino en premier, avant de lancer le script python sur son PC. Et si tout se passe bien, vous devriez ainsi pouvoir lire la valeur des entrées de votre arduino, depuis votre programme Python. Voici d’ailleurs ce que j’ai obtenu de mon côté en utilisant PyCharm, avec 3 positions différentes de potentiomètre (tourné à « zéro », tourné à peu près au milieu, puis tourné « complètement à fond ») :

Exemple pycharm lecture entrée analogique arduino à distance, au travers du port COM d'un PC sous windows, avec mesure 10 bits et commandes distantes

Comme vous pouvez le constater, les valeurs retournées se situent bien entre 0 et 1023, ce qui correspond bien à la valeur retournée par la fonction arduino « analogRead » (qui renvoie un nombre entier positif, codé sur 10 bits). Pour info, voici une photo du câblage que j’ai effectué sur breadboard :

Câblage potentiomètre sur Arduino Nano avec fils dupont sur breadboard, pour lecture valeur analogique à distance, via script python sur PC

Bien sûr, ce programme permet également de lire des entrées numériques, comme évoqué plus haut. Pour tester cela, il vous suffira de mettre l’une des entrées D2 à D9 à zéro (GND) ou à +5V (VCC), et de lancer la commande « digitalWrite D… » depuis python pour la cibler. Voici ce que ça donne de mon côté, en mettant par exemple la ligne D3 de l’Arduino à la masse, puis à +Vcc :

Exemple log pycharm de commande lecture d'état des entrées d'un Arduino, branché en USB sur port COM d'un ordinateur PC fonctionnant sous Windows

Conclusion

Bien entendu, il ne s’agit là que d’une introduction au pilotage d’un Arduino depuis un PC, en utilisant le langage Python, pour vous montrer ce qu’il est possible de faire. Cela vous montre d’ailleurs une façon d’établir « les mots permettant la communication » entre PC et Arduino, mais il y aurait bien d’autres façons de faire les choses !). Dans tous les cas, je voulais que cet article reste simple et ludique avant tout, à fin pédagogique, c’est pourquoi j’ai essayé de simplifier les choses au maximum.

Toutefois, si vous souhaitez pousser les capacités de pilotage de votre Arduino avec votre PC via Python, rien ne vous en empêche. En effet, vous pourriez parfaitement prendre totalement le contrôle des entrées/sorties arduino, par exemple en pilotant les sorties PWM à distance, ou en pilotant des modules i2c depuis votre ordi, … et tout ça, avec Python (ou tout autre langage informatique côté PC, d’ailleurs !). Les seules limites véritables étant celles de l’arduino en lui-même, le fait qu’il faille toujours un cordon qui le relie à votre PC, et surtout … votre imagination !

Du reste, comme vous avez certainement pu le constater, les exemples de code arduino sont assez lourds, dès lors qu’on fait du contrôle d’erreur. En fait, j’ai tenu à vous montrer cela car c’est selon moi quelque chose d’incontournable. Sinon, sans dispositif de contrôle de communication, vous pourriez avoir des erreurs ou dysfonctionnements aléatoires, parfois très durs à expliquer !

Cela étant dit, vous pouvez certainement à présent imaginer le potentiel d’une telle communication, que vous pouvez d’ailleurs personnaliser comme bon vous semble (notamment à partir de ce que je vous ai montré précédemment). Car, au final, c’est vous qui écrirez le langage que vous souhaitez, les mots que Python doit envoyer, et les mots que Arduino doit savoir interpréter. Donc : à vous de jouer, maintenant !

Bon courage à vous 😉
Jérôme.

À découvrir aussi : le chien de garde arduino (watchdog), explications et exemples de code !

Ce contenu vous plaît ? Alors abonnez-vous à la Newsletter pour ne rien louper !

(*) Mis à jour le 17/10/2022

2 commentaires sur “Piloter un Arduino depuis un PC, en filaire, avec Python (sous Windows)”

  1. Bonjour,

    C’est une approche intéressante d’utiliser la liaison « série » pour communiquer entre un PC et un Arduino. Python est à la mode actuellement, mais le même genre de script aurait pu être créé avec Perl par exemple.

    Juste un détail : dans le premier script, tu utilises deux fois « upper » pour la même chaine de caractères : une fois dans la fonction « def envoiCommande(commandeAenvoyer) » et une fois à l’appel de la fonction : « commande_string = commande_string.upper() ».

    Sinon, bravo pour le travail effectué et pour le partage.
    Une idée pour la suite : utiliser un ESP8266 et envoyer les commandes à effectuer via WiFi (voir les tutos de Eric Peronnin).

    1. Salut Eric !

      Merci pour la remontée de cette coquille … elle m’avait échappé ! Du coup c’est bon, c’est corrigé !

      Du reste, j’avoue n’avoir jamais programmé en Perl. Je vais donc étudier ce langage pendant mon temps libre, pour éventuellement m’en servir à l’occasion d’un prochain article ! Concernant le WiFi, c’est prévu. C’est juste qu’il y a tellement de sujets passionnants à aborder … qu’il faut bien que je choisisse lesquels je fais en premier, et lesquels je fais ensuite 😉

      Merci encore pour ton retour !

      Bonne soirée à toi.
      Jérôme.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Symbole danger point d'exclamation site PSE, triangle jaune avec contour noir, texte noir alerte au milieu Afin de filtrer au maximum les messages de type "spam" ou "inappropriés", chaque commentaire est soumis à modération, et validé manuellement. Du coup, il se peut que certains commentaires ne soient pas publiés, ou sinon, avec un peu de retard.

Étiquettes: