Problème de communication Python-Arduino

Bonjour,
Dans le but d'une réalisation expérimentale d'asservissement, je dois faire communiquer ma carte Arduino avec Python. La carte doit allumer une led verte ou une led rouge en fonction de la valeur d'une variable déterminée par Python. Cette variable se détermine à partir d'un flux vidéo et change donc à chaque frame.
Cependant, durant la prise de valeur lorsque Python traite le flux et envoie la donnée à Arduino, aucune des deux leds s'allume et c'est seulement lorsque le flux s'arrête qu'une des deux leds s'allume.
Au début je transférais à Arduino le 'str' d'une valeur flottante sous cette forme avec Python :

ser = serial.Serial("COM3",9600)
variable = str(x)
ser.write(variable.encode("utf-8"))

Mais le fait que la valeur soit à virgule faisait clignoter les leds durant la prise de valeur car Arduino lisait deux valeurs à cause de la virgule.
J'ai donc modifié comme ceci :

ser = serial.Serial("COM3",9600)
variable = str(x)
a = str(int(float(x)))
ser.write(a.encode("utf-8"))

Pour ne garder que la valeur avant la virgule, mais maintenant aucune des leds ne réagit durant la prise de valeur, une des leds ne s'allume plus qu'après avoir arrêté le flux.
J'espère que vous trouverez des réponses à mon problème, merci d'avance.

J'ai l'impression que ça va être difficile de t'aider si on ne sait pas ce que fait l'Arduino.

Voici le code de l'Arduino :

const int ledrouge = 13;
const int ledverte = 4;
int nombre = 0;


void setup() {
 pinMode(ledrouge,OUTPUT);
 pinMode(ledverte,OUTPUT);
 Serial.begin(9600);

}

void loop() {
 if (Serial.available()) {
   nombre = Serial.parseInt();
   while (nombre < 300) {digitalWrite(ledrouge,HIGH);
                         digitalWrite(ledverte,LOW);
                         Serial.flush();
                         nombre = Serial.parseInt();}
   while (nombre >= 300) {digitalWrite(ledverte,HIGH);
                          digitalWrite(ledrouge,LOW);
                          Serial.flush();
                          nombre = Serial.parseInt();}   
 }
}

Tu ferais bien de lire ce post et de mettre ton code entre balises si tu ne veux pas le voir disparaître rapidement

En effet, je n'ai pas pris le temps de lire les règles du forum, j'ai mis mes codes entre balises

Dans tes while, je pense qu'il faudrait mettre un if (Serial.available()) avant le parseInt() sinon, le parse sort au bout d'un certain temps et tu récupères zéro au lieu de ta donnée(voir ici)

Autre chose, dans la mesure où loop est une boucle infinie, je pense que la structure de ton programme n'est pas très judicieuse avec ces 2 while qui forment des boucles bloquantes.

Je n'ai jamais testé la communication série entre un Arduino et un Raspberry, mais en cherchant un peu je trouve à chaque fois une définition côté Rapsberry comme suit :

ser = serial.Serial('/dev/ttyAMA0', 115200)

Voir ici. Ce site indique comment trouver le bon port de communication, côté Raspberry (/dev/ttyUSB0 dans son cas).

Donc :

  • Tu peux déjà augmenter la vitesse, ça ne nuira pas à ton appli,
  • Cherche le bon port et teste en définissant la liaison comme cela.

Enfin, il serait peut-être plus simple de passer des données genre 0, 1 plutôt que des données variant autour de 300. Tu pourrais faire des tests plus simples ou un switch / case côté Arduino.

if (Serial.available()) {
   nombre = Serial.parseInt();
   if (nombre ==0) {
       digitalWrite(ledrouge,HIGH);
       digitalWrite(ledverte,LOW); }
   else {
       digitalWrite(ledverte,HIGH);
       digitalWrite(ledrouge,LOW); }
  }

Merci pour vos réponses,
J'ai essayé de mettre un if (Serial.available()) avant le parseInt() dans les while et il n'y a que la led rouge qui s'allume tout au long de la prise de mesure et après, quelque soit la valeur de la variable.
J'ai également modifié ma partie loop comme ceci :

void loop() {
  if (Serial.available()) {
    nombre = Serial.parseInt();
    if (nombre < 300) {digitalWrite(ledrouge,HIGH);
                          digitalWrite(ledverte,LOW);}
    if (nombre >= 300) {digitalWrite(ledverte,HIGH);
                           digitalWrite(ledrouge,LOW);}
    Serial.flush();   
  }
}

J'obtiens le même résultat qu'au début mais le code est effectivement allégé.
J'ai aussi passé le débit à 115200 mais je ne crois pas que modifier le port ai une influence sur le programme. Pour ce qui est de l'ordre de grandeur de la variable, il est grand car ma variable varie entre les valeurs 0 et 600.
Je pense que le problème vient peut-être de la valeur de la variable transmise à Arduino. A vrai dire, je n'ai pas compris pourquoi il fallait encoder la valeur en 'str' plutôt que de simplement envoyer la valeur flottante ou entière.

J'ai pas trop d'expérience dans ce domaine, mais j'ai vu pas mal de tutos où ils envoient des 'char' et un char c'est un byte (à une constante près), mais c'était pour faire discuter deux Arduino en BT. Le problème vient peut-être du type de donnée envoyée, c'est pourquoi je te suggérais d'envoyer la donnée la plus simple possible (0 ou 1)

J'ai modifié mon programme Python de tel manière qu'il envoie 1 à Arduino si ma valeur est inférieur à 300 (la led rouge est censée s'allumer) et 0 à Arduino si ma valeur est supérieur ou égale à 300 (la led verte est censée s'allumer). Il se passe ceci :

  • Aucune des leds ne s'allume durant la prise de valeurs
  • Lorsque j'arrête la prise de valeur (donc le flux vidéo), si ma dernière valeur prise était 0, la led verte s'allume
  • Si cette valeur était 1, la led rouge ne s'allume pas

Je n'arrive pas à comprendre pourquoi les leds ne s'allument pas durant la prise de valeurs et accessoirement pourquoi la rouge ne s'allume pas lorsque ma dernière valeur prise est 1
Voici le code de la boucle :

void loop() {
  if (Serial.available()) {
    nombre = Serial.parseInt();
    if (nombre == 1) {digitalWrite(ledrouge,HIGH);
                          digitalWrite(ledverte,LOW);}
    if (nombre == 0) {digitalWrite(ledverte,HIGH);
                           digitalWrite(ledrouge,LOW);}
    Serial.flush();   
  }
}

Et le code Python :

variable = x
if int(x) < 300 :
            a = 1
if int(x) >= 300 :
            a = 0
ser.write(str(a).encode("utf-8"))

sarkozozo:

ser.write(str(a).encode("utf-8"))

Côté Python tu encodes la valeur comme un caractère (utf-8) c-à- tu envoies 0x30 ou 0x31
et côté Arduino tu attends un nombre (0 ou 1)

Côté python, if faut que tu envoies un entier sur 16 bits (0 ou 1), et non pas un caractère.

Tu veux dire que je dois remplacer ser.write(str(a).encode("utf-8")) par ser.write(a) ? Car ça ne change pas le problème

Bonsoir,

@biggil, en fait le programme de sarkozozo décodera bien la chaîne de caractères envoyée, c'est le rôle de parseInt justement.

AMHA, le problème vient du fait que les entiers sont envoyés les uns à la suite des autres sans aucun séparateur entre eux (c'est pour cela que parseInt ne fait pas la différence entre eux ;-)). Une solution est d'ajouter un caractère séparateur qui n'est pas un chiffre après chaque nombre, genre un ";" ou un "\n".

exemple : ser.write((a+";").encode("utf-8"))

Salut

Grande confusion.

Ton code ARDUINO attend une chaîne de caractères contenant un certain nombre de digits.
par exemple "1234".
Ensuite parseInt() transforme cette chaîne en entier binaire : 1234.

Cela veut dire que côté python, il faut bien envoyer une chaîne de caractères.

# Transformer un float en float à l'aide de la fonction float ne sert à rien.

>>> a = str(int(float(123.4)))
>>> a
'123'
>>> a = str(int(123.4))
>>> a
'123'
>>>
# Encoder une chaîne de caractères contenant de digits en UTF-8 ne sert à rien.
# C'est utile uniquement si la chaîne contient des caractères accentués

>>> a
'123'
>>> a.encode("utf-8")
'123'
# Cette ligne ne sert à rien. Tu initialise un variable 'variable' à la valeur de x et elle ne sert à rien ensuite :

variable = x

Autrement ton code doit fonctionner.
Essaye ça :

>>> import time
>>> ser.write("1"); time.sleep(1) ; ser.write("0"); time.sleep(1); ser.write("0"); time.sleep(1); ser.write("1"); time.sleep(1) ; ser.write("0");

@supercc en remplaçant ser.write(str(a).encode("utf-8")) par ser.write((str(a)+";").encode("utf-8")) ça marche parfaitement

Merci pour votre aide !

Avec plaisir :wink:

Sans encode("utf-8") cela marchera aussi.
Les caractères '0' à '9' ne sont pas accentués que je sache, le point-virgule non plus.

@+

>>> import serial
>>> ser = serial.Serial("/dev/ttyUSB2", 9600)
>>> a = 20
>>> ser.write((str(a)+";").encode("utf-8"))
3
>>> ser.write((str(a)+";"))
3

Dans les deux cas l'envoi de trois caractères est effectué.
Les deux lignes suivantes produisent le même effet :

>>> (str(a)+";").encode("utf-8")
'20;'
>>> (str(a)+";")
'20;'

@+

Je termine avec UTF-8 pour souligner le danger d'utilisation de la méthode encode("utf-8") non maîtrisée, pour ceux que cela intéresse.

>>> "René".encode("UTF-8")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0x82 in position 3: ordinal not in range(128)

# avec ser.write, vous aurez le même problème
ser = serial.Serial("René", 9600)

Une bonne habitude :
Commencer un script python par :

# -*- coding: utf-8 -*-

S'il y a des caractères accentués dans votre script, y compris dans les commentaires, PYTHON vous fichera la paix.

@+