Comunicazione seriale limitata?

Salve a tutti,

primo post di primo...

Sto realizzando uno sketch che muoverà degli attuatori in base ai comandi che arrivano via seriale da un PC, e fin qui nulla di nuovo.
I dati che voglio comunicare ad arduino saranno in formato RAW, ovvero valori che vanno da 0 a 255, per cui voglio usare la strategia 'un byte -> un dato'.
Nelle mie prove ho notato però un comportamento curioso riguardo la comunicazione seriale, e qui la cosa si fa interessante:

finchè i valori che trasmetto sono compresi tra 32 e 126 (set di caratteri ASCII editabili) il dato ricevuto è corretto.
Ma se invio valori al di fuori di questo intervallo, il dato ricevuto è completamente falsato secondo una logica che mi sfugge.
Ad esempio, se il valore è al di sotto di 32, il dato ricevuto è sempre 63, mentre se è superiore a 126 il risultato non è prevedibile ( ma si ripete uguale quando riceve input uguali).

Vi sottopongo il codice incriminato, e più sotto i risultati ottenuti col monitor seriale, così che chi ne ha voglia possa sperimentare quanto dico:

//variabili globali
 byte X_value = 0; 
 byte Y_value = 0; 
 byte Z_value = 0; 


void setup() {

   Serial.begin(19200); // apro la seriale
 
 }
    

void loop() {
   byte caratteri; //conta il numero di caratteri ricevuti
   byte buff[4];

   //controllo se c'è qualcosa in arrivo sulla seriale
   if (Serial.available()) {
 
     caratteri=Serial.readBytes(buff,4);
      
     if (buff[0] == 's' ){
        // Tutti i comandi in posizione neutra
        X_value = 0;  
        Y_value = 0;
        Z_value = 0;
  
     }

     if (buff[0] == 'C' ){
        // legge i tre bytes successivi
        X_value = (buff[1] > 255? 255: buff[1]);
        Y_value = (buff[2] > 255? 255: buff[2]);
        Z_value = (buff[3] > 255? 255: buff[3]);
        
      }

      Serial.print(X_value, BIN); Serial.print(" "); Serial.println(X_value, DEC);
      Serial.print(Y_value, BIN); Serial.print(" "); Serial.println(Y_value, DEC);
      Serial.print(Z_value, BIN); Serial.print(" "); Serial.println(Z_value, DEC);
      analogWrite(X_out, X_value);
      analogWrite(Y_out, Y_value);
      analogWrite(Z_out, Z_value);
   
  }
     
}

Qui gli output che ottengo dandogli per esempio 'CAAA', 'C~⌂Ç' (byte= 126, 127, 128) e 'Cüéâ' (byte= 129, 130, 131):

1000001 65
1000001 65
1000001 65

1111110 126
111111 63
11000111 199

11111100 252
11101001 233
11100010 226

Cercando in rete non ho trovato nulla che mi potesse aiutare.
Che voi sappiate è un problema noto, e quindi c'è la soluzione?
Oppure ho sbagliato qualche cosa nel codice?

Grazie in anticipo
Primo

Mmmm ...
... io prima di leggere 4 bytes mi accerterei che essi sono tutti realmente disponibili (... la Serial.available() ti dice QUANTI sono disponibili) :wink:

Guglielmo

P.S. : Comunque ho sempre preferito gestire IO la cosa, leggendo byte a byte e controllando l'eventuale timeout tra un carattere e l'altro. Capisco che questi metodi della classe Serial sono comodi e nascondono le difficoltà, ma non ti permettono di renderti veramente conto di cosa c'è dietro ... e, in caso di problemi di trasmissione ... rendono quasi impossible la diagnostica ... ::slight_smile:

come fai ad essere sicuro di trasmettere veramente i byte che credi?

Sulimarco:
come fai ad essere sicuro di trasmettere veramente i byte che credi?

... ottima osservazione ... e altro motivo per NON usare la Serial.readBytes() che nasconde tutto e non da accesso a basso livello :wink:

Guglielmo

Grazie Guglielmo,

  1. non credo che sia un problema di riempimento del buffer, tant'è che se invio dati 'legittimi' (cioè editabili) il tutto funziona perfettamente.

Ad ogni modo ho seguito il tuo consiglio e da ora in avanti continuerò a seguirlo.
Così leggo un byte alla volta, e il problema permane se uso il monitor seriale dell'IDE di arduino.
Invece se uso HyperTerminal (siamo in ambiente Windows) la seriale rileva correttamente anche gli altri valori (uso in fase di debug la combinazione ALT + 'codice carattere ASCII', rispondendo a Sulimarco).
Quindi sembrerebbe un problema derivante dall'uso del monitor seriale di Arduino in questo specifico contesto.
Ho poi notato, con HyperTerminal, che per esempio 'ALT+255' è diverso da 'ALT+0255', è il risultato corretto lo ottengo con la seconda combinazione così come per 'ALT+1' o 'ALT +01'.
Ho provato infine con Linux passando i caratteri direttamente con la redirezione dell'output sulla seriale e il tutto funziona egregiamente.

Quindi, tornando a Sulimarco, io credevo di mandargli dei valori ma la catena 'monitor seriale->sistema operativo->interfaccia' di fatto mandava altre cose.

Grazie di nuovo a tutti, ho capito e risolto.

  1. Ora però è saltato fuori che Serial.available() non mi restituisce la dimensione del buffer, ma solamente 0 se è vuoto oppure 1 se c'è qualcosa, a prescindere da quanta ce n'è. Come mai?

Primo_appo:
... Ora però è saltato fuori che Serial.available() non mi restituisce la dimensione del buffer, ma solamente 0 se è vuoto oppure 1 se c'è qualcosa, a prescindere da quanta ce n'è. Come mai?

Non dovrebbe ... guarda nel reference ... dice chiaramente :

Get the number of bytes (characters) available for reading from the serial port. This is data that's already arrived and stored in the serial receive buffer (which holds 64 bytes).

... comunque, leggendo byte a byte ... ti interessa sapere solo se c'è un byte da legge o meno :wink:

Guglielmo

Prova a postare l'ultima versione del codice.

Ciao
Marco

Avevo guardato anch'io nel reference, per questo mi sembra strano il comportamento che ho riscontrato di Serial.available().

Ad ogni modo ecco il codice minimale, Guglielmo mi perdonerà per l'utilizzo di readBytes(), ma serve solo per dare riscontro a quanto dico:

void setup() {
   Serial.begin(19200); // apro la seriale
  }
   
void loop() {
   byte numCaratteri; //contiene il numero di caratteri ricevuti
   byte buff[4];  
   
   //controllo se c'è qualcosa in arrivo sulla seriale
   if ((numCaratteri=Serial.available())>0) {
    
    Serial.println(numCaratteri);
    
    numCaratteri=Serial.readBytes(buff,4);  
    Serial.println(numCaratteri);
    for (int i=0; i< numCaratteri; i++){
         Serial.println(buff[i]);
      } 
  }    
}

Se lo fate girare, vedrete che la prima valorizzazione di numCaratteri (da Serial.available ) è sempre uguale ad 1, mentre la seconda volta è effettivamente il numero di caratteri ricevuti sulla linea seriale

... questo perché trascurate sempre che il codice gira infinite volte più velocemente di quanto possano arrivare i caratteri ...

Appena riceve 1 carattere la condizione dell'IF è vera e il risultato è 1 ... per vedere veramente il valore 4 dovresti aspettare la ricezione di tutti e 4 i caratteri con, ad esempio un while :wink:

Guglielmo

Chiarissimo!!
è proprio la spiegazione che cercavo

Grazie ancora