Python Arduino Serial

Bonjour à tous.
J'ai un arduino qui compte des tours toutes les secondes, et je voudrais pouvoir changer la vitesse.
D'abord je demarre le compteur en envoyant le code A et un nombres de tours 100 soit dans IDE arduino (A100), ou en Python3 dans IDE arduino (b'A100')
et pour la vitesse dans IDE arduino (B5) ou en python3 dans IDE arduino (b'B5').
Tout se passe bien le temps de la communication et correcte.

Je lance le progamme Python et là pour les mêmes valeurs la liaison et très longue environ 1 seconde et le comptage s'arrête pendant ce temps.

Je ne comprends pas mon erreur.
L'un d'entre vous aurait-il une idées.

Cordialement.
Marc.

#***************** coding: UTF-8 ******************#
#**********environnement PYTHON IDE 3.7.4**********#
#*****************ARDUINO 1.8.5********************#
#******************Arduino nano********************#
#****************Tk version 8.6.9******************#

from serial import Serial
import serial.tools.list_ports
from tkinter import *
import tkinter.ttk as ttk

serial_port = None

# Liste des Ports
liste_ports = []
ports = serial.tools.list_ports.comports(include_links=False)
for port in ports:
    liste_ports.append(port.device)

# Selection du Port serie
def Selection_port(event):# sur evenement
    global serial_port
    port = combo.get()
    baudrate = '9600'
    try:
        serial_port = serial.Serial(port, baudrate, timeout=0.1) # initialise port serie forme réduite
        print ("Ouverture du port série : "+port+", vitesse : "+baudrate+" réussie")    
    except:
        #print ("Echec ouverture du port série "+port)
        tkinter.messagebox.showwarning("ERREUR communication","Echec ouverture du port série "+port)

def Valider():      
    global nombre  
    nombre = TextEntree_Nb_tour.get()    
    nombre = "A" + nombre # code commande A
    nombre = nombre.encode()
    print ("Envoi Nb tours Arduino")
    print (nombre)
    serial_port.write(nombre)# Envoi port série
    
    Recu_Arduino()
    
def Recu_Arduino():
    id = Fenetre_Principale.after(100, Recu_Arduino)# Appel de la fonction tous les xxxms
    nombre_lu_Arduino = serial_port.readline().rstrip()# Lecture ports série
    nombre_lu_Arduino = str(nombre_lu_Arduino)# transforme en string
    if('C' in nombre_lu_Arduino):
        print ("Retour Arduino")
        print (nombre_lu_Arduino)# b'Cnombre'   
        Decode_nombre_lu_Arduino = nombre_lu_Arduino[3:]# Lecture à partir du 3ème caractère
        Decode_nombre_lu_Arduino = Decode_nombre_lu_Arduino[0:-1]# Efface dernier caractère
        Decode_nombre_lu_Arduino = int(Decode_nombre_lu_Arduino)# transforme en nombre entier
        print ("nombre_lu_Arduino")
        print (Decode_nombre_lu_Arduino)
        Retour_Arduino.config(text = Decode_nombre_lu_Arduino)

def Valeur_Tours(nouvelleValeur):## nouvelle valeur 'curseur Nb Tour/sec'
    global nombre
    nombre = nouvelleValeur
    nombre = "B" + nombre # code commande B
    nombre = nombre.encode('ascii')
    print ("Envoi vitesse Arduino")
    print (nombre)
    serial_port.write(nombre)# Envoi port série
    

# Création Fenetre_Principale                 
Fenetre_Principale = Tk()
Fenetre_Principale.resizable(width=False, height=False) # pas de redimensionnement
Fenetre_Principale.geometry("600x150") # Taille Fenetre_Principale
Fenetre_Principale.title("Compteur Tour") # titre fenetre


# Création d'un widget combobox 'choix USB'
combo = ttk.Combobox(values=liste_ports, width = 6)
combo.bind('<<ComboboxSelected>>', Selection_port)# Fonction séléction du port
combo.place(x = 10, y = 5)

# création de widgets 'Label'
Affichage_Nb_tour = Label(Fenetre_Principale, text ='Nb Tours', width = 12, fg = 'RED', borderwidth=2, background='bisque', relief='sunken')
Affichage_Nb_tour.place(x = 205, y = 5)

# création de widgets 'Entry' Entrée nombre
TextEntree_Nb_tour = StringVar()
Entree_Nb_tour = Entry(Fenetre_Principale, textvariable = TextEntree_Nb_tour, border=3, width = 8)
Entree_Nb_tour.place(x = 320, y = 5)

# création de widgets 'Label'Affichage Arduino
Affichage_Arduino_Tour = Label(Fenetre_Principale, text ='Retour Arduino', width = 12, fg = 'RED', borderwidth=2, background='bisque', relief='sunken')
Affichage_Arduino_Tour.place(x = 400, y = 5)

# création de widgets 'Label'Retour Arduino
Retour_Arduino = Label(Fenetre_Principale, text = "", width = 5, borderwidth=2, background='white', relief='sunken', justify = LEFT)
Retour_Arduino.place(x = 515, y = 5)

# Création d'un widget Button 'Valider'
Bouton = Button(Fenetre_Principale, text ='Valider', command = Valider)
Bouton.place(x = 320, y = 40)

# Création d'un widget Scale 'curseur Nb Tour/sec'
Valeur_Nb_tour = StringVar()
Valeur_Nb_tour.set(1)# position départ du curseur Scale
Vitesse_Nb_tour = Scale(Fenetre_Principale,from_=1,to=10, orient=HORIZONTAL, length=180,width=12,label="Vitesse Tour/sec",\
                            tickinterval=1, variable=Valeur_Nb_tour, command = Valeur_Tours)
Vitesse_Nb_tour.place(x = 20, y = 30)

# démarrage :
Fenetre_Principale.mainloop()

'''
PROGRAMME ARDUINO
//***ARDUINO 1.8.5***//
//***Arduino nano Atmege 328P old Bootloader***//
//***Python IDLE version 3.6.3
//*** Tk version 8.6.6

#define BUF_LEN 128
char buffer[BUF_LEN];
char chaine_Python;
const int Led = 13;
unsigned long previousMillis = 0;
unsigned long prevuMillis = 0;
int Reste_chaine_Python_int = 0;
int Nb_tours = 0;
int compte_Nb_tours = 0;
int Etat = 0;
int Vitesse_T_sec = 1;
//*************************************************************
void setup()
{
  Serial.begin(9600);
  pinMode(Led, OUTPUT);
}
//*************************************************************
void loop()
{
  if (Serial.available() > 0)// Reçu de Python
  {
    Serial.print("Serial.available() : ");
    Serial.println(Serial.available());
    char chaine_Python = (Serial.read()); // Lit le premier octet reçu et le met dans la variable
    Serial.print("chaine_Python : ");
    Serial.println(chaine_Python);
    switch (chaine_Python) //si le premier octet correspond au code
    {
      case 65://65=A
        lecture_nombre_int();
        Nb_tours = Reste_chaine_Python_int;
        Serial.print("Nb_tours :");
        Serial.println(Nb_tours);
        Etat = 1;
        break;
      case 66://66=B
        lecture_nombre_int();
        Vitesse_T_sec = Reste_chaine_Python_int;
        Serial.print("Vitesse_T_sec :");
        Serial.println(Vitesse_T_sec);// Envoi vers Python
        break;
    }
  }
  if (Etat == 1)
  {
    Tour();
  }
}
//*************************************************************
void lecture_nombre_int()
{
  digitalWrite(Led, HIGH);
  Reste_chaine_Python_int = Serial.parseInt(); // lit le reste des octets
  digitalWrite(Led, LOW);
}
//************************************************************************
void Tour()
{
  unsigned long courentMillis = millis(); // Enregistrement du temps
  if (courentMillis - prevuMillis >= 1000 / Vitesse_T_sec)
  {
    prevuMillis = courentMillis;
    compte_Nb_tours++;
    if (compte_Nb_tours <= Nb_tours)
    {
      digitalWrite(Led, HIGH);
      //Serial.print("compte_Nb_tours : ");
      //Serial.println(compte_Nb_tours);
      snprintf(buffer, sizeof(buffer), "C%d\n\r", compte_Nb_tours);
      Serial.println(buffer);
      digitalWrite(Led, LOW);
    }
  }
}

'''

pour les mêmes valeurs la liaison et très longue environ 1 seconde

lisez la doc de

 Serial.parseInt();

et le timeout par défaut...

Si vous voulez comprendre comment bien écouter le port série vous pouvez jeter un oeil à mon petit tuto sur le sujet

(éventuellement merci d'éditer votre post ci dessus et de séparer les 2 codes pour qu'ils soient lisibles séparément, là c'est le souk :slight_smile: )

Bonjour, je vous remercie.
J'ai donc lu votre tuto, j'ai corrigé, tout fonctionne correctement.

Programme Arduino

//***ARDUINO 1.8.5***//
//***Arduino nano***//


#define BUF_LEN 128
char buffer[BUF_LEN];
char chaine_Python;
const int Led = 13;
unsigned long previousMillis = 0;
unsigned long prevuMillis = 0;
int Reste_chaine_Python_int = 0;
int Nb_tours = 0;
int compte_Nb_tours = 0;
int Etat = 0;
int Vitesse_T_sec = 1;
int Temps_tour = 0;
//*************************************************************
void setup()
{
 Serial.begin(9600);
 pinMode(Led, OUTPUT);
}
//*************************************************************
void loop()
{
 if (Serial.available() > 0)// Reçu de Python
 {
 Serial.print("Serial.available() : ");
 Serial.println(Serial.available());
 char chaine_Python = (Serial.read()); // Lit le premier octet reçu et le met dans la variable
 Serial.print("chaine_Python : ");
 Serial.println(chaine_Python);
 switch (chaine_Python) //si le premier octet correspond au code
 {
 case 65://65=A
 lecture_nombre_int();
 Nb_tours = Reste_chaine_Python_int;
 Serial.print("Nb_tours :");
 Serial.println(Nb_tours);
 Etat = 1;
 break;
 case 66://66=B
 lecture_nombre_int();
 Vitesse_T_sec = Reste_chaine_Python_int;
 Serial.print("Vitesse_T_sec :");
 Serial.println(Vitesse_T_sec);// Envoi vers Python
 break;
 }
 }
 if (Etat == 1)
 {
 Tour();
 }
}
//*************************************************************
void lecture_nombre_int()
{
 digitalWrite(Led, HIGH);
 Reste_chaine_Python_int = Serial.parseInt(); // lit le reste des octets
 digitalWrite(Led, LOW);
}
//************************************************************************
void Tour()
{
 unsigned long courentMillis = millis(); // Enregistrement du temps

 if ((compte_Nb_tours <= Nb_tours) && (courentMillis - prevuMillis >= 1000 / Vitesse_T_sec))
 {
 Temps_tour = 1000 / Vitesse_T_sec;
 Serial.print("Temps_tour :");
 Serial.println(Temps_tour);
 prevuMillis = courentMillis;
 digitalWrite(Led, HIGH);
 snprintf(buffer, sizeof(buffer), "C%d\n\r", compte_Nb_tours);
 Serial.println(buffer);
 digitalWrite(Led, LOW);
 compte_Nb_tours++;
 }
}

Programme Python

#***************** coding: UTF-8 ******************#
#**********environnement PYTHON IDE 3.7.4**********#
#****************Tk version 8.6.9******************#
#*****************ARDUINO 1.8.5********************#
#*****Arduino nano Atmege 328P old Bootloader******#

from serial import Serial
import serial.tools.list_ports
from tkinter import *
import tkinter.ttk as ttk

serial_port = None

# Liste des Ports
liste_ports = []
ports = serial.tools.list_ports.comports(include_links=False)
for port in ports:
    liste_ports.append(port.device)

# Selection du Port serie
def Selection_port(event):# sur evenement
    global serial_port
    port = combo.get()
    baudrate = '9600'
    try:
        serial_port = serial.Serial(port, baudrate, timeout=0.1) # initialise port serie forme réduite
        print ("Ouverture du port série : "+port+", vitesse : "+baudrate+" réussie")    
    except:
        #print ("Echec ouverture du port série "+port)
        tkinter.messagebox.showwarning("ERREUR communication","Echec ouverture du port série "+port)

def Valider():      
    global nombre  
    nombre = TextEntree_Nb_tour.get()    
    nombre = "A" + nombre + "\r" # code commande A
    nombre = nombre.encode()
    print ("Envoi Nb tours Arduino")
    print (nombre)
    serial_port.write(nombre)# Envoi port série
    
    Recu_Arduino()
    
def Recu_Arduino():
    id = Fenetre_Principale.after(100, Recu_Arduino)# Appel de la fonction tous les xxxms
    nombre_lu_Arduino = serial_port.readline().rstrip()# Lecture ports série
    nombre_lu_Arduino = str(nombre_lu_Arduino)# transforme en string
    if('C' in nombre_lu_Arduino):
        print ("Retour Arduino")
        print (nombre_lu_Arduino)# b'Cnombre'   
        Decode_nombre_lu_Arduino = nombre_lu_Arduino[3:]# Lecture à partir du 3ème caractère
        Decode_nombre_lu_Arduino = Decode_nombre_lu_Arduino[0:-1]# Efface dernier caractère
        Decode_nombre_lu_Arduino = int(Decode_nombre_lu_Arduino)# transforme en nombre entier
        print ("nombre_lu_Arduino")
        print (Decode_nombre_lu_Arduino)
        Retour_Arduino.config(text = Decode_nombre_lu_Arduino)

def Valeur_Tours(nouvelleValeur):## nouvelle valeur 'curseur Nb Tour/sec'
    global nombre
    nombre = nouvelleValeur
    nombre = "B" + nombre + "\r" # code commande B
    nombre = nombre.encode()
    print ("Envoi vitesse Arduino")
    print (nombre)
    serial_port.write(nombre)# Envoi port série
    

# Création Fenetre_Principale                 
Fenetre_Principale = Tk()
Fenetre_Principale.resizable(width=False, height=False) # pas de redimensionnement
Fenetre_Principale.geometry("600x150") # Taille Fenetre_Principale
Fenetre_Principale.title("Compteur Tour") # titre fenetre


# Création d'un widget combobox 'choix USB'
combo = ttk.Combobox(values=liste_ports, width = 6)
combo.bind('<<ComboboxSelected>>', Selection_port)# Fonction séléction du port
combo.place(x = 10, y = 5)

# création de widgets 'Label'
Affichage_Nb_tour = Label(Fenetre_Principale, text ='Nb Tours', width = 12, fg = 'RED', borderwidth=2, background='bisque', relief='sunken')
Affichage_Nb_tour.place(x = 205, y = 5)

# création de widgets 'Entry' Entrée nombre
TextEntree_Nb_tour = StringVar()
Entree_Nb_tour = Entry(Fenetre_Principale, textvariable = TextEntree_Nb_tour, border=3, width = 8)
Entree_Nb_tour.place(x = 320, y = 5)

# création de widgets 'Label'Affichage Arduino
Affichage_Arduino_Tour = Label(Fenetre_Principale, text ='Retour Arduino', width = 12, fg = 'RED', borderwidth=2, background='bisque', relief='sunken')
Affichage_Arduino_Tour.place(x = 400, y = 5)

# création de widgets 'Label'Retour Arduino
Retour_Arduino = Label(Fenetre_Principale, text = "", width = 5, borderwidth=2, background='white', relief='sunken', justify = LEFT)
Retour_Arduino.place(x = 515, y = 5)

# Création d'un widget Button 'Valider'
Bouton = Button(Fenetre_Principale, text ='Valider', command = Valider)
Bouton.place(x = 320, y = 40)

# Création d'un widget Scale 'curseur Nb Tour/sec'
Valeur_Nb_tour = StringVar()
Valeur_Nb_tour.set(1)# position départ du curseur Scale
Vitesse_Nb_tour = Scale(Fenetre_Principale,from_=1,to=5, orient=HORIZONTAL, length=180,width=12,label="Vitesse Tour/sec",\
                            tickinterval=1, variable=Valeur_Nb_tour, command = Valeur_Tours)
Vitesse_Nb_tour.place(x = 20, y = 30)

# démarrage :
Fenetre_Principale.mainloop()

C'est peut être moins le souk?
Cordialement.
Marc.

QUINQUIN07:
C'est peut être moins le souk?

oui :slight_smile:

généralement on met "\r\n" dans cet ordre et la lecture telle quel n'est pas idéale, vous dépendez de parseInt() et de son timeout. Certes on peut estimer que tous les caractères seront bien arrivés en général mais le plus "propre" serait de lire jusqu'à la marque de fin de message '\r' (ou '\n' suivant comment vous l'envoyez) et ensuite de décoder.

Ok je vais étudier tous cela.
Merci.
Marc.

Bonjour, si j'ai bien compris ou on réduit le timeout de parseInt(),
ou on lit la chaine complète dans un tableau et on interprète comme ceci :

#define BUF_LEN 128
char buffer[BUF_LEN];
char Tableau_chaine_Python[10];
char chaine_Python;
int Position = 0;
unsigned long previousMillis = 0;
unsigned long prevuMillis = 0;
int Reste_chaine_Python_int = 0;
int Nb_tours = 0;
int compte_Nb_tours = 0;
int Etat = 0;
int Vitesse_T_sec = 1;
int Temps_tour = 0;
//*************************************************************
void setup()
{
  Serial.begin(9600);
}
//*************************************************************
void loop()
{
  if (Serial.available() > 0)// Reçu de Python
  {
    chaine_Python = (Serial.read()); // Lecture
    Tableau_chaine_Python[Position] = chaine_Python;
    Position ++;
  }
  if (Tableau_chaine_Python[Position - 1] == '\r') // Si le dernier caractère et le retour chariot
  {
    switch (Tableau_chaine_Python[0])
    {
      case 65://65=A : // Si le 1er élément position 0 du tableau est un A (code)
        lecture_nombre_int();
        Nb_tours = Reste_chaine_Python_int;
        Etat = 1;
        break;
      case 66://66=B : // Si le 1er élément position 0 est un B (code)
        lecture_nombre_int();
        Vitesse_T_sec = Reste_chaine_Python_int;
        break;
    }
  }
  if (Etat == 1)
  {
    Tour();
  }
}
//*************************************************************
void lecture_nombre_int()
{
  for (int i = 1; i < (Position); i++)
      // on commence la lecture au 2eme élément position 1
      {
         // On decale les éléments dans le tableau
        Tableau_chaine_Python[i-1] = Tableau_chaine_Python[i];
      }
      //convertit la chaîne en entier
      Reste_chaine_Python_int = atoi(Tableau_chaine_Python); 
      // Remise à zéro de la chaîne de caractères
      for (int i = 0; i < Position; i++)
      {
        Tableau_chaine_Python[i] = 0;
      }
      Position = 0; // RAZ
}
//************************************************************************
void Tour()
{
  unsigned long courentMillis = millis(); // Enregistrement du temps
  if ((compte_Nb_tours <= Nb_tours) && (courentMillis - prevuMillis >= 1000 / Vitesse_T_sec))
  {
    Temps_tour = 1000 / Vitesse_T_sec;
    prevuMillis = courentMillis;
    // envoie du comptage vers Python
    snprintf(buffer, sizeof(buffer), "C%d\n\r", compte_Nb_tours);
    Serial.println(buffer);
    compte_Nb_tours++;
  }
}

Mais je ne sais pas quel est le meilleur choix et le plus rapide!
Marc.

Le plus rapide c'est de ne pas avoir de timeOut, donc de gérer les choses au fur et à mesure qu'elles arrivent

attention dans votre code quand vous faites

if (Serial.available() > 0)// Reçu de Python
  {
    chaine_Python = (Serial.read()); // Lecture
    Tableau_chaine_Python[Position] = chaine_Python;
    Position ++;
  }


  if (Tableau_chaine_Python[Position - 1] == '\r') // Si le dernier caractère et le retour chariot
  {
    switch (Tableau_chaine_Python[0]) ...

le second if s'effectue en permanence. Vous ne devriez tester que si vous avez reçu quelque chose, donc mettre ce second if() au sein du premier. si vous lisez mon tuto que j'ai pointé plus haut il y a du code pour récupérer un buffer d'entrée correctement pour pouvoir ensuite l'analyser

d'un point de vue analyse, le switch() est une bonne idée, mais mettez directement les caractères attendus au lieu de leur code ASCII, ce sera plus lisible

 switch (Tableau_chaine_Python[0])
    {
      case 'A' : 
        ...
        break;
      case 'B' :
        ...