Envoyer les variables de simulation à une carte Arduino

Après avoir récupéré les variables de simulation d’OpenRails, il serait désormais intéressant de pouvoir les envoyer à notre pupitre, ou plus précisément à notre carte Arduino. Pour cela, nous allons utiliser une liaison série. Il s’agit du système de communication qu’utilise une carte Arduino lorsqu’elle est reliée à un ordinateur via son câble USB.

Préparons le terrain

La première chose à faire est d’installer la bibliothèque python « pyserial » qui permet d’établir des liaisons séries depuis un programme python. On utiliser la méthode d’installation avec pip, décrite dans l’article d’introduction à Python : pip install pyserial

Par ailleurs, branchez votre carte Arduino à votre PC et ouvrez l’IDE Arduino. Allez dans Outils->Ports et notez le numéro de port COM de votre carte. Il vous sera utile par la suite. On prendra dans cet article le port COM 1 comme exemple.

Enfin ouvrez un nouveau script Python.

Côté Python : envoyez les données sur le port Série

Nous allons commencer par établir la liaison série entre notre programme Python et notre carte Arduino. Pour cela, importez la bibliothèque pyserial dans votre script puis ouvrez un port série sur le port ‘COM1’ (adaptez avec notre numéro de port).

import serial
portArduino = serial.Serial(port='COM1', baudrate=9600, timeout=0.01, writeTimeout=1)

Notez que le nom « portArduino » est arbitraire, vous pouvez choisir ce que vous souhaitez ! L’important lorsque l’on code est d’utiliser des noms de variables compréhensibles pour s’y retrouver.

Si le programme plante à ce niveau, c’est que la carte n’est pas correctement connectée ou que le port est erroné. N’hésitez pas à vérifier avec l’IDE Arduino si vous utilisez le bon port. Notez qu’il n’est pas possible d’utiliser le moniteur série de l’IDE en même temps que le programme Python, pensez donc à le fermer avant de lancer votre script !

L’envoie d’une donnée (sous forme de chaine de caractères) à la carte Arduino s’effectue simplement avec la ligne suivante :

portArduino.write(donnée)

Définir un protocole

Vous l’avez vu, l’envoie de données à la carte Arduino se fait en quelques lignes. Mais il ne suffit pas d’envoyer des données les unes derrière les autres : encore faut-il que l’Arduino comprenne ce qu’elle reçoit. Il faut donc définir une forme pour les données, qui soit toujours la même, pour que l’Arduino puisse extraire la valeur souhaitée de la chaine de caractère qu’elle reçoit. De manière schématique, cette mise en forme des données s’appelle en informatique un protocole.

Vous êtes libres de définir le protocole qui vous convient ! Pour ma part, sur les projets FerroviSim, j’ai adopté le suivant (inspiré du programme Link2FS pour les simulateurs de vol) :
– Une lettre majuscule identifie la variable qui est transmise (on peut donc transmettre 26 variables différentes),
– Elle est suivie d’un nombre de 1 à 4 chiffres qui donne la valeur numérique de la variable,
– Le nombre se termine par un « / ».

Par exemple, la vitesse est définie par la lettre « V » et la pression à la conduite générale par la lettre « G ». Pour indiquer à la carte que le train roule à 100 km/h avec la conduite générale à 5 bars, j’envoie :

« V100/G50/ »

En reprenant le dictionnaire des variables de simulation obtenu via le serveur web OpenRails, il est possible en Python de construire la chaine de caractère à envoyer de la façon suivante :

#mise en forme des données
speed = int( data_dict["SPEEDOMETER"] ) # on convertit en nombre entier
pcg = int( 10 * data_dict["BRAKE_PIPE"] ) # on passe en décibars, et on convertit en nombre entier
pcf = int( 10 * data_dict["BRAKE_CYL"] )

envoiArduino = b"V%d/G%d/F%d/"%(speed, pcg, pcf)

Notez la première étape de mise en forme des données : en effet, il faut que les données envoyées soient des nombres entiers. Pour les pressions en bar, on passe en décibar afin d’avoir une précision suffisante sans utiliser de nombre à virgule.

On peut évidemment ajouter autant de variables que l’on souhaite (jusqu’à 26 étant donnée l’identification par une lettre), avec à chaque fois une lettre différente.
Je conseille cependant de ne pas envoyer trop de variables à la fois car la lecture de la chaîne reçue côté Arduino est gourmand en ressource et risque de ralentir l’exécution du programme. Pour ma part, j’ai une dizaine de variables à envoyer que j’ai séparé en 2 salves.

Vous pouvez désormais intégrer ce code dans le code d’acquisition des variables depuis OpenRails, pour envoyer à intervalle régulier les données du jeu à la carte Arduino.

Remarque : n’augmentez pas trop la fréquence d’envoi, au risque de saturer l’Arduino et de provoquer un plantage côté carte et côté Python. Je dirais que 2 envois par seconde sont suffisants.

Côté Arduino : recevoir les données

Passons maintenant dans l’IDE Arduino pour écrire un programme capable de recevoir les données envoyées sur le port série par le script Python.

La première chose à faire est d’ouvrir le port série côté Arduino, avec la même vitesse de transmission que le programma Python (baudrate) :

void setup(){
       Serial.begin(9600);
}

Remarque : si vous utilisez une carte Arduino Due avec la bibliothèque Keyboard, vous avez normalement connecté votre carte au PC à l’aide du port USB « native ». Dans ce cas, c’est le port SerialUSB qu’il faut initialiser :

void setup(){
       SerialUSB.begin(9600);
}

Pour la suite, on se placera dans ce cas avec utilisation de SerialUSB.

Une fois le code initialisé, la carte Arduino va devoir attendre que le programme Python lui envoie des données. Pour cela, à chaque tour de boucle (void loop), elle teste si SerialUSB.available() renvoie « True ». Si quelque chose est en attente sur le port série, on récupère le premier caractère à l’aide de la fonction getChar() définie plus bas et récupérée du programme « Link2FS » .

On regarde ensuite si ce caractère reçu est l’une des lettres d’identification prévue par notre protocole. Si c’est le cas, on appelle une fonction également définie plus bas qui va lire les chiffres qui suivent et ainsi reconstituer la valeur de la variable. Les commentaires expliquent le fonctionnement de cette fonction.

// variables de stockage des données :
int vitesse = 0;
int pressionCG = 0;
int pressionCF = 0;

int CodeIn;

void loop(){

	if(SerialUSB.available()){ //vérifie si des données sont en attente sur le port série
      	CodeIn = getChar();   //si oui, lit le premier caractère

    	// En fonction de la lettre d'identification, on donne à la variable correspondante la valeur obtenue par lecture des caractères suivant

    	// vitesse :
    	if(CodeIn=='V') { vitesse = data(); } 
    	// pression conduite générale :
    	if(CodeIn=='G') { pressionCG = data(); }
    	// pression cylindres de frein
    	if(CodeIn=='F') { pressionCF = data(); }

    }
}

// code réccupéré de "link2fs" #####################################
void getChar()// Get a character from the serial buffer(Dont touch)
{
    while(SerialUSB.available() == 0); // wait for data (Dont touch)
    return((char)SerialUSB.read()); // (Dont touch) Thanks Doug
}
// code récupéré de "link2fs" #####################################

int data()
{
  String valeur=""; // on initilalise la variable qui contient la vitesse
  valeur += getChar(); // on lit le premier carctère
  char verify = getChar(); 
  int nbChiffres = 0;
  while(verify != '/' && nbChiffres < 4) // si on n'est pas arrivé au bout du nombre (slash ou déjà 4 chiffres)
  { 
    valeur += verify; // on ajoute le caractère suivant caractère (chiffre)
    nbChiffres += 1;
    verify = getChar();
  }

  int valeurInt = valeur.toInt(); // on convertit la chaine de caractère en Integer (nombre entier)
  return valeurInt; // on renvoie l'angle de positionnement du servomoteur
}

Branchez votre carte Arduino, lancez OpenRails, lancez votre script Python : les variables simulation sont désormais envoyées à intervalles régulier à la carte Arduino.

Dans de prochains articles, nous étudierons comment utiliser ces données pour, par exemple, les afficher sur des manomètres ou autres voltmètres et ampèremètres.

Une réflexion sur “Envoyer les variables de simulation à une carte Arduino

  1. Pingback: Commander OpenRails à partir d’un potentiomètre | FerroviSim

Laisser un commentaire