Comunicazione seriale tra raspberry e arduino

Salve a tutti, sto facendo un piccolo esercizio nel far comunicare il mio Raspberry pi 3B+ con un Arduino Uno collegati tramite cavo usb.

Questo è il codice del raspberry

#!/usr/bin/env python
import RPi.GPIO as GPIO
import serial
import time

BtnPin = 12

Led_status = 0
arduinoSerialData = serial.Serial('/dev/ttyACM0',9600)

def setup():
        GPIO.setmode(GPIO.BOARD)
        GPIO.setup(BtnPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

def loop():
        GPIO.add_event_detect(BtnPin, GPIO.FALLING, callback=swLed, bouncetime=200)
        while True:
                time.sleep(1)

def swLed(ev=None):
        global Led_status
        Led_status = not Led_status
        if Led_status == 1:
                print 'led on'
                arduinoSerialData.write("01|led_acceso")
        else:
                print 'led off'
                arduinoSerialData.write("01|led_spento")

def destroy():
        arduinoSerialData.write("01|led_spento")
        GPIO.cleanup()

if __name__ == '__main__':
        setup()
        try:
                loop()
        except KeyboardInterrupt:
                destroy()

Questo è il codice di arduino

const int led = 13;
String data;
String stringa;

/******************************
 * formato data --> "01|STRINGA"
 ******************************/

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(led, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  while (Serial.available() > 0) {
    data = Serial.readString();
    data = data.substring(0, data.length()-2);
    Serial.println("Stringa che ho letto: "+data+" | lunghezza: "+data.length());
    if(data.substring(0,3).equals("01|")){
      stringa = data.substring(3);
    }
  }
  if(stringa.equals("led_acceso")){
    digitalWrite(led,HIGH);
  }
  if(stringa.equals("led_spento")){
    digitalWrite(led,LOW);
  }
}

In sostanza io premo un pulsante collegato al raspberry e lui tramite seriale dice all'arduino di accendere o spegnere un led.

Per controllare il messaggio seriale che arriva ad arduino ho creato un altro programmino per raspberry che lo eseguo su un'altra shell, qui di seguito il codice

#!/usr/bin/env python
import serial

db = 9600
arduinoSerialData = serial.Serial('/dev/ttyACM0',db)

print "db =",db

def loop():
        while True:
                if(arduinoSerialData.inWaiting()>0):
                        myData = arduinoSerialData.readline()
                        print myData


if __name__ == '__main__':
        loop()

Grazie a questo programmino ho scoperto che ad arduino legge dalla seriale 01|led_acce oppure 01|led_spen

Come mai il messaggio non arriva tutto?
PS. Ho fatto prove a scrivere un messaggio più breve, tipo "acceso" e "spento" ma ad arduino arriva "acce" o "spen"

Alessio

Sei al tuo 33esimo post ed ancora non sai che nella sezione in lingua Inglese si può scrivere SOLO in Inglese ? ? ? :o

Per questa volta il tuo post è stato spostato nella giusta sezione del forum Italiano, ma per il futuro cortesemente presta più attenzione.

Grazie,

Guglielmo

perchè fai questo?

data = data.substring(0, data.length()-2);

Potrebbe essere quest'struzione?

data.length()-2

Da quel che dici ti vengono sempre troncati gli ultimi due caratteri quieni proverei a rimuovere quel -2.

P.S. = Visto che stai creando un progetto per esercizio ti consiglio di sostituire nel codice di Arduino tutte le variabili della classe String e relative funzioni e utilizzare le stringhe classiche del C (Array di char) su una MCU con poca memoria e senza GarbageCollector come Arduino la classe String porta ad avere un sacco di problemi difficilmente debuggabili e comprensibili con relativi arrabbiature e mal di testa :wink:

ORSO2001:
perchè fai questo?

data = data.substring(0, data.length()-2);

perchè quando prima stavo facendo i test dal monito seriale dell'ide risultavano sempre due caratteri in più della stringa ed era un a capo

Provo a togliere quell'istruzione perchè forse cambia se le stringhe vengono mandate dall'ide o da raspberry

L'ho provato e funziona, come mai quando nei test provavo a mandare delle stringhe dal monitor seriale dell'ide mi aggiungeva a fine stringa a capo e di conseguenza dovevo eliminare gli ultimi due caratteri per far funzionare il tutto

fabpolli:
Potrebbe essere quest'struzione?

data.length()-2

Da quel che dici ti vengono sempre troncati gli ultimi due caratteri quieni proverei a rimuovere quel -2.

P.S. = Visto che stai creando un progetto per esercizio ti consiglio di sostituire nel codice di Arduino tutte le variabili della classe String e relative funzioni e utilizzare le stringhe classiche del C (Array di char) su una MCU con poca memoria e senza GarbageCollector come Arduino la classe String porta ad avere un sacco di problemi difficilmente debuggabili e comprensibili con relativi arrabbiature e mal di testa :wink:

Potresti farmi un esempio di lettura da seriale oppure di confronto con array char per capire la differenza

perchè, forse, nel monitor seriale avevi lasciato abilitato nl e cr

ORSO2001:
perchè, forse, nel monitor seriale avevi lasciato abilitato nl e cr

Ma se il codice Python non invia i CR+LF (e non lo fa), come fa Serial.readString di Arduino a capire che una riga è finita?

Ah, ok, vedo che c'è un timeout...

Ma è comunque una comunicazione errata perché per ogni stringa deve passare il timeout.

Se si legge con readString, allora il codice python deve inviare CR+LF:

arduinoSerialData.write("01|led_acceso" + "\r\n")

Anzi... per evitare incompatibilità tra versioni, quando si trasmettono byte consiglio di esplicitarlo sempre:

arduinoSerialData.write(b"01|led_acceso" + b"\r\n")

il reference dice:

Serial.readString() reads characters from the serial buffer into a string. The function terminates if it times out

Alessingo:
Potresti farmi un esempio di lettura da seriale oppure di confronto con array char per capire la differenza

Certo che posso, innanzitutto occorre definire l'array di char che ospiterà le stringhe in arrivo, supponiamo che i tuoi messaggi in arrivo siano lunghi al massimo 15 caratteri

char lettoDaSeriale[16];

Nota che è lungo 16 perché ogni array di char deve contenere il carattere terminatore \0.
Per leggere da seriale, puoi seguire l'esempio:

byte charPos=0;
while(Serial.available())
{
  char carattereLetto = Serial.read(); //leggo un carattere in arrivo dalla seriale
  lettoDaSeriale[charPos] = carattereLetto;  //Lo metto nell'array che contine l'intero messaggio
  charPos++; //incremento la posizione della posizione dove inserire il carattere letto da seriale
}
lettoDaSeriale[charPos] = '\0'; //Metto il carattere terminatore alla fine del messaggio letto

Per i successivi confronti, copia di parti di stringa ecc l'argomento è ambpi e occorre studiarsi un po' le funzioni del C strcmp, strncpy e tante altre.
Abituati da subito a usare la versione con la "n" (strncpy anziché strcpy).
Altra cosa propedeutica al maneggiare gli array di char, al posto dell'inserimento del carattere terminatore puoi provare a "resettare" l'intero array prima della lettura da seriale con la funzione memset

P.S. = Il codice di lettura della seriale può essere notevolmente migliorato, sintetizzato, ottimizzato l'ho scritto appositamente lungo a fini documentativi e di facilità di comprensione

fabpolli:
Certo che posso, innanzitutto occorre definire l'array di char che ospiterà le stringhe in arrivo, supponiamo che i tuoi messaggi in arrivo siano lunghi al massimo 15 caratteri

char lettoDaSeriale[16];

Nota che è lungo 16 perché ogni array di char deve contenere il carattere terminatore \0.
Per leggere da seriale, puoi seguire l'esempio:

byte charPos=0;

while(Serial.available())
{
  char carattereLetto = Serial.read(); //leggo un carattere in arrivo dalla seriale
  lettoDaSeriale[charPos] = carattereLetto;  //Lo metto nell'array che contine l'intero messaggio
  charPos++; //incremento la posizione della posizione dove inserire il carattere letto da seriale
}
lettoDaSeriale[charPos] = '\0'; //Metto il carattere terminatore alla fine del messaggio letto



Per i successivi confronti, copia di parti di stringa ecc l'argomento è ambpi e occorre studiarsi un po' le funzioni del C [strcmp](https://www.tutorialspoint.com/c_standard_library/c_function_strncmp.htm), [strncpy](https://www.tutorialspoint.com/c_standard_library/c_function_strncpy.htm) e tante altre.
Abituati da subito a usare la versione con la "n" (strncpy anziché strcpy).
Altra cosa propedeutica al maneggiare gli array di char, al posto dell'inserimento del carattere terminatore puoi provare a "resettare" l'intero array prima della lettura da seriale con la funzione [memset](https://www.tutorialspoint.com/c_standard_library/c_function_memset.htm)

P.S. = Il codice di lettura della seriale può essere notevolmente migliorato, sintetizzato, ottimizzato l'ho scritto appositamente lungo a fini documentativi e di facilità di comprensione

Grazie fabpolli

fabpolli:
P.S. = Il codice di lettura della seriale può essere notevolmente migliorato, sintetizzato, ottimizzato l'ho scritto appositamente lungo a fini documentativi e di facilità di comprensione

come minimo serve di fermarsi dopo aver letto l'ultimo carattere, prima di andare a sbordare fuori dall'array
e magari anche una maniera per dire: "ultimo carattere trovato" per far partire il trattamento dei dati ricevuti

Data la premessa "...innanzitutto occorre definire l'array di char che ospiterà le stringhe in arrivo, supponiamo che i tuoi messaggi in arrivo siano lunghi al massimo 15 caratteri" inserire controlli, sicurezze, operazioni sarebbe stato fuorviante per l'utente cheè alla prima esperienza e non ha dimestichezza con la seriale.
Era meglio se postavo l'uso della seriale con interrupt o forse l'avrebbe confuso e portato, magari, a restare sulla classe String secondo te?
Il PS era li apposta a indicare che il tutto era volutamente non complesso, definito, ottimizzato, ecc.

Io volevo solo indicare alcune delle cose da aggiungere, mica criticare
Vai avanti così, che vai bene