Simple "Serial_Com" arduino raspberry pi

Bonjour a tous,
voila je partage mon protocole de communication entre arduino et raspberry pi.
Deux petits scripts bien commenté pour aider les novices...

  • Python 2.7 et lib "Serial" coté raspi
  • IDE arduino 1.0.3
    Code pas ou peu optimisé, pas de bug majeur mais, vos remarques seront les bien venu.
    Par exemple le cpu de la raspberry mouline plein pot :grin: si quelqu'un a une solution...

Principe :

  • carte arduino et raspberry pi relier par câble usb.
  • les scripts intègre dans leur boucle une fonction qui li le port série, stocke les msg vérifie leur intégrité (simple présence de "char" spécifiques au début et a la fin, il faudrait faire un checksum...)
  • si le msg est entier envoi un accuser de réception (sinon l' émetteur recommence l'envoi un max de 5 fois avant abandon)
  • exécute une action en fonction du msg reçu

Ici les msg entre cartes sont de type "String".
Le script python contient une fonction date_time pour un fichier log que je vous épargne.

Arduino =

/*programme qui envoi  un msg sur le port serie a destination
d'une carte raspberry pi qui elle attend ce msg pour declencher
une action et renvoi sur le port serie un accuser de reception
send_data_to_raspi("donne_teleinfo") #exemple d' envoi de data vers raspberry
*/
//##############################
//variable data port serie
char data_port_serie = '\0';//char de reception "serial.read()"
char data[256] = "";//tableau de stockage des char reçu par le port serie
String str_data_serial;//data recu transformer en string
int i = 0;//incremente le tableau de char
boolean accuser_de_reception = false;//etat de l'accuser de recption
//##############################
//variable chronos attente accuser de reception
unsigned long start_chrono;
unsigned long stop_chrono;
unsigned long total_chrono;
//###############################################################
//###############################################################
//###############################################################

void setup() 
{
  Serial.begin(9600);   
  Serial.println("Demarrage programme");
}//fin setup
//###############################################################
void loop() 
{
  
  serial_port_watcher();//li les data du port serie(raspi) si data dispo
  
}//fin loop
//###############################################################
void serial_port_watcher()
{
  memset(data,0,256);//vide le tableau des data precedents
  str_data_serial = "";//raz du string data reçu
  i=0;
  
  if (Serial.available())//attend reception data
    
   {
     delay(100); //important! laisse le temps de charger le buffer           
     while (Serial.available() > 0) //tant qu' il ya des data en reception
       {
          data_port_serie = Serial.read();//stock dans une varible chaque char reçu
          data[i++] = data_port_serie;//on ajoute au tableau les char reçu
       }
    
     data[i++] = '\0';//on fini le tableau par un char nul pour construire un string
     str_data_serial = data;//transforme le tableau en string  
     
   }//fin if serial.avaible
 
 //#####################
 //verifi l'integrité du msg
   if (str_data_serial.startsWith("<") and str_data_serial.endsWith(">"))
    { 
     //#####################
       //execute en fonction du msg reçu
       if (str_data_serial == "<donne_teleinfo>")
        {
          send_accuser_de_reception();//envoi accuser de reception
          //action ...
        }
       else if (str_data_serial == "<ok>")
        {
          accuser_de_reception = true;
        }
     }//fin de if startsWith...
//#####################
}//fin serial_port_watcher
//###############################################################
void send_data_to_raspi(String data_to_send)
{
  //fonction qui envoi des data sur le port serie,attend un accuser de reception (string "ok")
  //si pas reçu recommence l'envoi des meme data et cela un nombre de fois defini(variable "try_to_send")
  
  accuser_de_reception = false;
  long interval = 1000;//temps d'attende pour renvoi des data si pas de reponse accuser de reception
  byte try_to_send = 0;//nombre de tentative d'envoi des data
    
    while(try_to_send < 5)//boucle de tentative d'envoi retente jusqu'a x fois
      {
        
        start_chrono = millis();//demarre un chrono pour attente rx
        ++ try_to_send;//incremente le nombre de tentative
      
        Serial.println("<" +data_to_send+ ">");//envoi du msg(string passer en parametre) avec ajout des balises de debut et fin du msg pour verifier son integrité a la reception
        
         while (1)//attente reception de data pour accuser reception
           {
              //####################
              //li le port serie pour rx l'accuser de recep du raspi ("ok")
              serial_port_watcher();
              if(accuser_de_reception == true)
                {
                  return;//sort de la fonction si accuser de reception
                }
             //####################
             //verifie si le temps d'attente de reponse est depasser 
             stop_chrono = millis();
             if(stop_chrono - start_chrono > interval) 
             {
               break;//si temps d'attente accuser de reception ecouler on sort de la boucle pour re envoyer le msg
             }   
               //####################
           }//fin  while 1 (boucle attente reception accuser de reception)

      }//fin boucle de tentative d'envoi 
            
}//fin ecrit data raspi
//###############################################################
void send_accuser_de_reception()
{
  Serial.println("<ok>");
}
//##########################################################################################

Je met le script raspberry dans le post suivant car "The message exceeds the maximum allowed length (9500 characters). "

Raspberry pi =

# -*- coding: cp1252 -*-

#programme qui envoi  un msg sur le port serie a destination
#d'une carte arduino qui elle attend ce msg pour declencher
#une action et renvoi sur le port serie un accuser de reception
#exemple d' envoi de data vers arduino = send_data_to_arduino("donne_teleinfo")

import serial
import time #fonction heure_date + chrono
import threading #utilisation multi-threading

#########################################################################
##########                      Class                          ##########
#########################################################################

class Serial_Port_Watcher(threading.Thread): #thread qui li le port serie

    
    def __init__(self):
        threading.Thread.__init__(self)
        self.running = False#continu ou stop le thread
        self.accuser_de_reception = False#stop les tentative d'envoi de msg si bien reçut
        
    def run(self):
        self.running = True
        while self.running:# scrutation du buffer d'entree

            try:
                if serial_port.inWaiting():#inWaiting vient de "pyserial"
                    li_data_port_com()#appel la foction qui lit et verifie les datas
            except:
                pass
            
    def stop(self):
        self.running = False


###########################################################################
#############                    Fonctions                 ################
###########################################################################

def li_data_port_com():
        
    global msg_port_serie
       
    msg_port_serie = serial_port.readline()
    if (msg_port_serie != ""):
        msg_port_serie = msg_port_serie.rstrip() #on enleve le "\n" et le char null
        #print("msg => " + msg_port_serie) #debug du msg recu
        
        if(msg_port_serie.startswith("<") & msg_port_serie.endswith(">")): #on verifie l' integrité du msg (commence et fini par les balises "<...>"
            msg_is_ok(msg_port_serie) #appel la fonction de traitement du msg si celui ci et "entier"
            
        else:
            print("Erreur lecture port com = " + msg_port_serie)
            #log_serial_data("Erreur lecture port com = " + msg_port_serie)

    #serial_port.flushInput()
    #serial_port.flushOutput()

########################

def msg_is_ok(msg_ok):
#gestion des data recut sur le port serie (Arduino "UNO")
    
    print("RX serial port => " + msg_ok)#debug

    if (msg_ok != "<ok>"):
        send_accuser_de_reception()#vu que le msg est entier (et != accuser de reception venant de arduino) on accuse reception
        
    if (msg_ok == "<ok>"):
        data_watcher.accuser_de_reception = True#stop le ré-envoi du msg
        print("RX serial port => accuser de reception")
        
########################
         
def send_data_to_arduino(data_to_send):
#fonction qui envoi des data sur le port serie,attend un accuser de reception (string "ok")
#si pas reçu recommence l'envoi des meme data et cela un nombre de fois defini(variable "try_to_send")
    data_watcher.accuser_de_reception = False
    try_to_send = 0#raz nombre de tentative d'envoi des data

    while(try_to_send < 5):#boucle de tentative d'envoi retente jusqu'a x fois
        
        serial_port.write("<" + data_to_send + ">")
        try_to_send += 1 #incremente le nombre de tentative
        print("TX ok... try_to_send = " + str(try_to_send))
        time.sleep(1)#laisse le temps de recevoir accuser de recption

        if (data_watcher.accuser_de_reception == True):
            return#sort de la fonction si le msg est bien envoyer

########################

def send_accuser_de_reception():
    serial_port.write("<ok>")#envoi accuse reception
    print("TX serial port => accuser de reception")

########################

def connexion_arduino():

    global etat_connexion_arduino
    global serial_port

    try:
        serial_port = serial.Serial("COM3")#("/dev/ttyUSB0")
        serial_port.baudrate=9600
        etat_connexion_arduino = True
        print("connexion arduino ok")
        
    except:
        etat_connexion_arduino = False
        print("Erreur connexion arduino")
        pass
  
########################

def date():

    if time.localtime().tm_mday < 10 :
        day = str(0) + str(time.localtime().tm_mday)
    else:
        day = str(time.localtime().tm_mday)
        
    if time.localtime().tm_mon < 10:
        mon = str(0) + str(time.localtime().tm_mon)
    else:
        mon = str(time.localtime().tm_mon)

    date = day + "/" + mon + "/" + str(time.localtime().tm_year)
    return date

########################

def heure():
    
    if time.localtime().tm_hour < 10 :
        hh = str(0) + str(time.localtime().tm_hour)
    else:
        hh = str(time.localtime().tm_hour)
        
    if time.localtime().tm_min < 10:
        mm = str(0) + str(time.localtime().tm_min)
    else:
        mm = str(time.localtime().tm_min)

    if time.localtime().tm_sec < 10:
        sec = str(0) + str(time.localtime().tm_sec)
    else:
        sec = str(time.localtime().tm_sec)
                 
    heure = hh + ":" + mm + ":" + sec
    return heure

  
########################

def stop_prog():
    
    data_watcher.stop()
    serial_port.close()#ferme port ouvert dans connexion ardino
    print("stop programme")

#########################################################################
##########                       Main                          ##########
#########################################################################

print("Demarrage programme")
connexion_arduino()

#initialyse les objets
data_watcher = Serial_Port_Watcher()

#démarre les threads
data_watcher.start()

time.sleep(15)#tempo auto stop a virer dans votre prog
stop_prog()