Interpretazione dati da RS-485

Eccomi qui per una nuova ricerca di aiuto.
Sto leggendo con Arduino, senza problemi, un segnale da un PLC su RS-485.
Per leggerlo ho usato la classica schedina con il MAX collegata direttamente sulla breadboard, pin to pin, con un NANO. I pin sono D12 - d11 - d10 - D9 rispettivamente per RO - RE - DE - DI.
Ho fatto così perchè se ci metto dei fili ricevo dei disturbi che invece così non ricevo.

#include <SoftwareSerial.h>
SoftwareSerial rs485 (12, 9);
float inputplc = 0;

void setup () {
  Serial.begin (9600);
  rs485.begin (4800);

  pinMode (11, OUTPUT);  // pin Data Enable
  pinMode (10, OUTPUT);  // pin Data Enable
  digitalWrite (11, LOW); // ricezione
  digitalWrite (10, LOW); // ricezione
}

void loop () {

  if (rs485.available ()) {
    inputplc = rs485.read ();

    if (inputplc == 32) {
      Serial.println (" ");
    }
    else Serial.print (inputplc);
    // delay (10);
  }
}

Il problema non è qui, ma nella INTERPRETAZIONE DEI DATI…
Trasmetto a 16 bytes e dopo ogni dato metto un H20 (a capo) almeno nel monitor ci capisco qualcosa.

Però arduino interpreta (giustamente) diversamente a seconda del tipo di variabile.
Con una INT (16 bytes) i valori non sono corretti perchè mi da:
010 per 1
020 per 2
0100 per 10
01000 per 100
ma veniamo al fatidico 256
02550 per 255
001 per 256
Da qui si capisce che un byte è andato a ZERO mentre l’altro byte è stato incrementato di 1.
Potrei prendere l’ultimo carattere per capire la cifra, ma non è così facile perchè
0110 potrebbe essere 11 ma potrebbe essere anche 1+(256*10) cioè 2561

Allora ho provato diverse combinazioni e ho scoperto che con float mi da dei valori perfettamente interpretabili:
0.001.000.00 per 1
0.00255.000.00 per 255
0.00255.001.00 per 512
0.00254.00127.00 per 32.766
0.00255.00127.00 per 32.767
quindi è chiarissimo: uno ZERO per la partenza, poi le unita o decine o centinaia fino a un max di 255, poi il moltiplicatore per 256 e due ZERI per il termine.

Ho provato con char, con double ecc… Il miglior risultato è con float e questo mi fa pensare che trasmetta a 32bit (boh!). Però questo non mi interessa: mi interessa leggere e interpretare i dati.

Con NANO come posso ottenere il valore decimale risultante?
Con una stringa? con un array?

Grazie in anticipo.

ciao steve,

scusa la domanda banale...ma non hai un documento di scambio dati del plc? anche se riuscissi a capire l'ordine dei bytes che ti arrivano...come fai a sapere a cosa si riferiscono?

Con tutti quei caratteri che riporti scusami ma non riesco a capire cosa intendi. Nell’output seriale dovresti separare uno dall’altro i valori che ricevi, e magari riportarli in esadecimale per capire meglio.

Prova ad eseguire questo codice (vedi come te l’ho modificato) e dicci cosa leggi:

#include <SoftwareSerial.h>
// Pin:
// RO connesso al pin 12
#define SerialRX 12
// DI connesso al pin 9
#define SerialTX 9
// RE
#define SerialReceiveEnable 10
// DE
#define SerialDataEnable 11

#define RS485Transmit    HIGH
#define RS485Receive     LOW

#define RS485Disable     HIGH
#define RS485Enable      LOW

SoftwareSerial rs485(SerialRX, SerialTX);
float inputplc = 0;

void setup() {
  Serial.begin(9600);
  
  rs485.begin(4800);
  
  pinMode(SerialReceiveEnable, OUTPUT);
  pinMode(SerialDataEnable, OUTPUT);
  
  digitalWrite(SerialReceiveEnable, RS485Receive);
  digitalWrite(SerialDataEnable, RS485Enable);
}

void loop () {
  if ( rs485.available() ) {
    inputplc = rs485.read();
    if ( inputplc == 32 ) // Separatore=spazio???
      Serial.println();
    else 
    {
      Serial.print(inputplc, HEX);
      Serial.print(" ");
    }
  }
}

Fai copia/incolla dei dati che vedi nel monitor seriale, così proviamo a darti una mano. E magari specifica anche COSA ti aspetti di ricevere (conosci il protocollo che passa sulla tua RS485?).

Note:

  • usa i #define per definire i parametri di configurazione, diventa tutto più leggibile
  • RE e DE vanno impostati allo stesso modo quando devi ricevere per cui faresti prima a mettere RE+DE connessi insieme ad un solo pin
  • ma perché metti sempre uno spazio prima delle parentesi delle funzioni? :wink:

La cosa è più semplice, a prescindere dal protocollo. I dati che trasmetto dal plc sono :

255 H20 (H20 nella tabella ASCII è uno spazio) 511 H20 1023 H20 2047 H20 4095 H20 8191 H20 16384 H20 32.765 h20 32.766 H20 32.767 H20 K1 H20

Col mio programma ricevo questi dati, e da come spiegato sopra, sono corretti

0.00255.000.00 0.00255.001.00 0.00255.003.00 0.00255.007.00 0.00255.0015.00 0.00255.0031.00 0.000.0064.00 0.00253.00127.00 0.00254.00127.00 0.00255.00127.00 0.001.000.00

Col programma di docdoc ricevo così:

0.0000000000000000 255.0000000000000000 0.0000000000000000 0.0000000000000000 255.0000000000000000 1.0000000000000000 0.0000000000000000 255.0000000000000000 3.0000000000000000 0.0000000000000000 255.0000000000000000 7.0000000000000000 0.0000000000000000 255.0000000000000000 15.0000000000000000 0.0000000000000000 255.0000000000000000 31.0000000000000000 0.0000000000000000 0.0000000000000000 64.0000000000000000 0.0000000000000000 253.0000000000000000 127.0000000000000000 0.0000000000000000 254.0000000000000000 127.0000000000000000 0.0000000000000000 255.0000000000000000 127.0000000000000000 0.0000000000000000 1.0000000000000000 0.0000000000000000

Diciamo che "leggo meglio io", ma ciò che chiedevo è il modo migliore per estrapolare i dati e ottenere il valore finale. Nel mio programma ho un header che è 0. (zero più un punto) poi il byte meno significativo e un punto, poi il byte più significativo e il punto e poi due zeri finali (00). A me va bene cosi. - Elimino il primo zero e il punto - moltiplico il byte significativo per 256 - aggiungo il valore del byte meno significativo - elimino gli ultimi due zeri.

Per esempio questa riga 0.00253.00127.00 diventa:

127 x 256 + 253 = 32.765

ed il valore è quello che ho trasmesso io.

Come posso farlo fare a Arduino? Stringa? Array?

docdoc: .... - ma perché metti sempre uno spazio prima delle parentesi delle funzioni? ;)

Boh, così, per migliore lettura. Come vedi anche dopo una virgola, in uno scritto, metto uno spazio che molti non mettono... :)

steve-cr:
I dati che trasmetto dal plc sono :

255
H20 (H20 nella tabella ASCII è uno spazio)
511
H20

Niente, di lavorare in esadecimale non ti riesce proprio eh? :wink: Se lo spazio è per te un separatore, puoi anche indicare solamente i dati in colonna, ossia:
255
511
1023
2047
4095

Col programma di docdoc ricevo così:

0.0000000000000000 255.0000000000000000 0.0000000000000000
0.0000000000000000 255.0000000000000000 1.0000000000000000

Mah, teoricamente anche no… :wink: Vedi, nel mio codice io faccio le Serial.print() in HEX, quindi voresti avere i singoli byte in esadecimale, ma ho scritto “teoricamente” perché qui il problema è che metti in una float il byte che leggi da seriale, effettivamente non me ne ero accorto e non averti cambiato float in byte è stata una mia svista: dato che quello che devi ricevere sono degli interi e che la read() restituisce un BYTE, devi leggere byte!

Cambia la definizione del float in byte ed avrai, con i dati che hai indicato, una cosa del tipo:

FF
FF 01
FF 03
FF 07
FF 0F

ciò che chiedevo è il modo migliore per estrapolare i dati e ottenere il valore finale.

Ok, quindi tu devi prendere i byte che ti arrivano e “accumularli” fino al separatore (lo spazio o ’ ’ o 0x20). Ora osservando i valoriche hai messo come esempio, la domanda è: nel protocollo hai dei valori interi di due byte (visto che vai da 255 a 32767) o ci sono altri formati? Dando per scontato che parliamo di interi, anche non limitandosi a due byte direi che puoi fare così, vedi il listato come l’ho modificato, e provalo:

#include <SoftwareSerial.h>
// Pin:
// RO connesso al pin 12
#define SerialRX 12
// DI connesso al pin 9
#define SerialTX 9
// RE
#define SerialReceiveEnable 10
// DE
#define SerialDataEnable 11

#define RS485Transmit    HIGH
#define RS485Receive     LOW

#define RS485Disable     HIGH
#define RS485Enable      LOW

SoftwareSerial rs485(SerialRX, SerialTX);
int inputplc = 0;

#define MAXBUF 2
byte buf[MAXBUF];
// Puntatore/contatore caratteri nel buffer
byte numchar = 0;

// Numero di byte che determinano il tipo di dato
#define DATO_INT 2

// Separatore
#define SEPARATORE 0x20

void setup() {
  Serial.begin(9600);
 
  rs485.begin(4800);
 
  pinMode(SerialReceiveEnable, OUTPUT);
  pinMode(SerialDataEnable, OUTPUT);
 
  digitalWrite(SerialReceiveEnable, RS485Receive);
  digitalWrite(SerialDataEnable, RS485Enable);
}

void loop () {
  if ( rs485.available() ) {
    // Leggo un byte
    char c = rs485.read();
    if ( c == SEPARATORE )
    { // Dato completo!
      // Formatto il dato in base a cosa ho ricevuto
      if ( numchar == DATO_INT )
      { // Calcolo il valore a 2 byte
        inputplc = buf[0] + buf[1] * 255;
      }
      // e lo stampo in decimale
      Serial.println(inputplc);
    }
    else
    {
      // Accumulo il byte
      if ( numchar < MAXBUF-1 )
        buf[numchar++] = c;
      }
    }
  }
}

steve-cr:
Boh, così, per migliore lettura. Come vedi anche dopo una virgola, in uno scritto, metto uno spazio che molti non mettono… :slight_smile:

Beh in realtà anche io metto sempre uno spazio dopo la virgola, ed è così che si fa, ma quello che metti tra parentesi sono i parametri della funzione che le precede, e anche “visivamente” si capisce meglio a chi stai passando quei valori e che quello che è scritto a sinistra è una funzione e non un comando… :wink:

Se cambio la variabile in int, byte, long, word oppure short, il risultato è questo:

02551 02553 02557 025515 025531 0064 0253127 0254127 0255127 010

Se la metto double o float:

0.00255.000.00 0.00255.001.00 0.00255.003.00 0.00255.007.00 0.00255.0015.00 0.00255.0031.00 0.000.0064.00 0.00253.00127.00 0.00254.00127.00 0.00255.00127.00 0.001.000.00

Se metto una String il valore non va a capo e fa solo una serie di cifre decimali (non esadecimali) che avranno anche un senso ma con la float o double il senso è compiuto.

Se metto il tuo programma:

0 0 0 0 0 0 0 0 0 0 0 0

C'era una parentesi finale in più in fondo e l'ho tolta (mi dava errore).

I dati che trasmetto sono numerici, senza virgola e il plc non mi accetta più di 32676 per cui è un 16byte.

Comunque ti ringrazio per la collaborazione e per avermi risposto.

docdoc: Niente, di lavorare in esadecimale non ti riesce proprio eh? ;) Se lo spazio è per te un separatore, puoi anche indicare solamente i dati in colonna, ossia: 255 511 1023 2047 4095 ...

Il programma nel PLC è il LADDER, quindi non è così facile come dici tu: una riga di trasmissione è tipo:

-||--------------[ MOV K1024 D11]-- M0 |-----[ MOV H20 D12]--

dove, quando ho M0, copio il valore decimale K 1024 dentro la variabile D11 e copio il valore esadecimale H20 nella variabile D12 eccetera

poi trasmetto le variabili su RS-485

steve-cr:
Se cambio la variabile in int, byte, long, word oppure short, il risultato è questo:

02551
02553

Si ma scusa se mi ripeto, la read() restituisce un byte quindi puoi metterci tutto quello che ti pare, sempre un byte è, quindi valore tra 0 e 255. Se leggi un byte tu devi metterlo in una variabile “byte”, e poi gestire l’unione di più byte a seconda del tipo di dato. Ed è quello che ho fatto nella mia versione.

Se metto il tuo programma:

C’era una parentesi finale in più in fondo e l’ho tolta (mi dava errore).

Ok, vero, ma io come puoi immaginare ho fatto le modifiche senza poter provare il codice e mi è sfuggita una graffa di troppo.

Mi sono anche accorto che ci sono due errori (ma tu il listato hai provato un attimo ad analizzarlo???): primo, devi aggiungere una istruzione “numchar = 0;” altrimenti non funziona bene perché va resettato il puntatore al buffer, e, secondo, la “if ( numchar < MAXBUF -1 )” deve essere “if ( numchar < MAXBUF )” perché al secondo byte il puntatore vale 1 e dato che MAXBUF vale 2 non entrerebbe nella if e quindi non accumulerebbe il secondo byte.

Per cui ora prova questo:

#include <SoftwareSerial.h>
// Pin:
// RO connesso al pin 12
#define SerialRX 12
// DI connesso al pin 9
#define SerialTX 9
// RE
#define SerialReceiveEnable 10
// DE
#define SerialDataEnable 11

#define RS485Transmit    HIGH
#define RS485Receive     LOW

#define RS485Disable     HIGH
#define RS485Enable      LOW

SoftwareSerial rs485(SerialRX, SerialTX);
int inputplc = 0;

#define MAXBUF 2
byte buf[MAXBUF];
// Puntatore/contatore caratteri nel buffer
byte numchar = 0;

// Numero di byte che determinano il tipo di dato
#define DATO_INT 2

// Separatore
#define SEPARATORE 0x20

void setup() {
  Serial.begin(9600);

  rs485.begin(4800);

  pinMode(SerialReceiveEnable, OUTPUT);
  pinMode(SerialDataEnable, OUTPUT);

  digitalWrite(SerialReceiveEnable, RS485Receive);
  digitalWrite(SerialDataEnable, RS485Enable);
}

void loop () {
  if ( rs485.available() ) {
    // Leggo un byte
    char c = rs485.read();
    if ( c < 16 )
      Serial.print("0");
    Serial.print(c, HEX);
    Serial.print(" ");
    if ( c == SEPARATORE )
    { // Dato completo!
      Serial.println();
      // Formatto il dato in base a cosa ho ricevuto
      inputplc = -1;
      if ( numchar == DATO_INT )
      { // Calcolo il valore a 2 byte
        inputplc = buf[0] + buf[1] * 255;
      }
      // e lo stampo in decimale
      Serial.print("Dato ricevuto: ");
      Serial.println(inputplc);
      // Reset del contatore caratteri nel buffer
      numchar = 0;
    }
    else
    { // Accumulo il byte
      if ( numchar < MAXBUF )
        buf[numchar++] = c;
    }
  }
}

Ovviamente fammi sapere se ora funziona ma mi raccomando, non prendere mai “a scatola chiusa” i listati, devi cercare di capire cosa fa soprattutto se chi ti aiuta non può simulare il tuo ambiente.
Nel caso riporta quale output vedi sulla seriale e quali dati sai che stavi ricevendo.

I dati che trasmetto sono numerici, senza virgola e il plc non mi accetta più di 32676 per cui è un 16byte.

Casomai 16 bit, quindi 2 byte per ogni elemento, ma ok, il codice che ti ho scritto prevede per ora solo quei dati quindi potrebbe funzionare.

steve-cr: Il programma nel PLC è il LADDER, quindi non è così facile come dici tu: una riga di trasmissione è tipo: -||--------------[ MOV K1024 D11]-- M0 |-----[ MOV H20 D12]-- dove, quando ho M0, copio il valore decimale K 1024 dentro la variabile D11 e copio il valore esadecimale H20 nella variabile D12 eccetera poi trasmetto le variabili su RS-485

Questa informazione però è totalmente diversa da quella che hai dato prima, per cui non capisco: cosa ricevi su Arduino tramite la RS485?

Se il formato è quello che hai detto finora, ossia dei byte (che a coppie rappresentano dei valori interi 16 bit) con separatore un byte 0x20 ossia uno spazio, allora il listato che ho postato sopra dovrebbe funzionare (a meno di qualche altro problemino, che se vuoi possiamo vedere insieme).

Se invece i dati sono in un formato differente, il relativo parser su Arduino deve saperlo, per cui la domanda diventa nuovamente: cosa ricevi esattamente sulla seriale di Arduino?

Dato ricevuto: -8416 00 01G7UI35 00 W Dato ricevuto: -511 00 01G7UI35 01 W Dato ricevuto: -511 00 01G7UI35 03 W Dato ricevuto: -511 00 01G7UI35 07 W Dato ricevuto: -511 00 01G7UI35 0F W Dato ricevuto: -511 00 01G7UI35 V W Dato ricevuto: -511 00 00 1Q W Dato ricevuto: 0 00 01G7UI33 3D W Dato ricevuto: -1021 00 01G7UI34 3D W Dato ricevuto: -766 00 01G7UI35 3D W Dato ricevuto: -511 00 01 00 W Dato ricevuto: 255

Hai ragione docdoc, io il programma lo controllo, ma sono molto debole in questa parte di C (array, buffer, hex eccetera.) Sono più "terra-terra", sono più un On-Off, write read e altre cose "basiche" :)

Dato ricevuto: 0 00 0FFFFFFFF 00 20 Dato ricevuto: -511 00 0FFFFFFFF 01 20 Dato ricevuto: -511 00 0FFFFFFFF 03 20 Dato ricevuto: -511 00 0FFFFFFFF 07 20 Dato ricevuto: -511 00 0FFFFFFFF 0F 20 Dato ricevuto: -511 00 0FFFFFFFF 1F 20 Dato ricevuto: -511 00 00 40 20 Dato ricevuto: 0 00 0FFFFFFFD 7F 20 Dato ricevuto: -1021 00 0FFFFFFFE 7F 20 Dato ricevuto: -766 00 0FFFFFFFF 7F 20 Dato ricevuto: -511 00 01 00 20 Dato ricevuto: 255

C'erano delle " in più...

steve-cr:
Dato ricevuto: -8416
00 01G7UI35 00 W
Dato ricevuto: -511
00 01G7UI35 01 W

Uhm, voglio aiutarti ma qui qualcosa non mi quadra ancora.

Prova questo programmino che fa semplicemente un dump seriale dei caratteri ricevuti dalla RS485 e postami l’output così cerco di capire meglio:

#include <SoftwareSerial.h>
// Pin:
// RO connesso al pin 12
#define SerialRX 12
// DI connesso al pin 9
#define SerialTX 9
// RE
#define SerialReceiveEnable 10
// DE
#define SerialDataEnable 11

#define RS485Transmit    HIGH
#define RS485Receive     LOW

#define RS485Disable     HIGH
#define RS485Enable      LOW

SoftwareSerial rs485(SerialRX, SerialTX);
int inputplc = 0;

// Separatore
#define SEPARATORE 0x20

void setup() {
  Serial.begin(9600);

  rs485.begin(4800);

  pinMode(SerialReceiveEnable, OUTPUT);
  pinMode(SerialDataEnable, OUTPUT);

  digitalWrite(SerialReceiveEnable, RS485Receive);
  digitalWrite(SerialDataEnable, RS485Enable);
}

void loop () {
  if ( rs485.available() ) {
    Serial.print(rs485.read(), HEX");
    Serial.print(" ");
    if ( c == SEPARATORE )
      Serial.println();
  }
}

Ti ringrazio per l'aiuto

Ho corretto qualche errore (li metti apposta, vero?, per controllare se sto attento... )

0 FFFFFFFF 0 20 0 FFFFFFFF 1 20 0 FFFFFFFF 3 20 0 FFFFFFFF 7 20 0 FFFFFFFF F 20 0 FFFFFFFF 1F 20 0 0 40 20 0 FFFFFFFD 7F 20 0 FFFFFFFE 7F 20 0 FFFFFFFF 7F 20 0 1 0 20

void loop () {
  if ( rs485.available() ) {
    char c = rs485.read();
    Serial.print(c, HEX);
    Serial.print(" ");
    if ( c == SEPARATORE )
      Serial.println();
  }
}

Si, praticamente il tuo output è in esadecimale e il mio all’inizio era in decimale, quindi, per esempio, FF 1F va letto come 1F FF, quindi 8.191 e il successivo 00 40 va letto come 40 00 che è 16.384.

steve-cr:
Ho corretto qualche errore (li metti apposta, vero?, per controllare se sto attento… )

No, è perché lo scrivo “al volo” (mi ero accorto di aver rimesso delle virgolette di troppo, ma poi ho modificato il post, forse tu l’hai catturato prima della mia correzione :wink: ).
Puoi postare quindi il codice che hai corretto e che corrisponde all’output che hai postato? Ad esempio non capisco bene perché “FFFFFFFF” invece di “FF”, e perché tu abbia anche degli zeri iniziali (quasi come se il dato fosse in realtà di 3 byte non 2!).

Prova a definire “byte c” invece di “char c” ed aggiungi la stampa dello zero iniziale ossia questo loop():

void loop () {
  if ( rs485.available() ) {
    byte c = rs485.read();
    if ( c<16 )
      Serial.print("0");
    Serial.print(c, HEX);
    Serial.print(" ");
    if ( c == SEPARATORE )
      Serial.println();
  }
}

Comunque mi pare confermata più o meno la struttura, se mi fai questo dump provo a darti una mano a completare correttamente il codice di acquisizione dei dati.

Meglio

00 FF 00 20 00 FF 01 20 00 FF 03 20 00 FF 07 20 00 FF 0F 20 00 FF 1F 20 00 00 40 20 00 FD 7F 20 00 FE 7F 20 00 FF 7F 20 00 01 00 20

Ok, quindi in realtà ricevi 4 byte, di cui il primo è sempre 0x00 (byte di inizio pacchetto) e l’ultimo sempre 0x20, ed in mezzo due byte per il dato.

Prova allora così (ti ho aggiunto un po’ di commenti così dovresti poter seguire meglio il criterio):

#include <SoftwareSerial.h>
// Pin:
// RO connesso al pin 12
#define SerialRX 12
// DI connesso al pin 9
#define SerialTX 9
// RE
#define SerialReceiveEnable 10
// DE
#define SerialDataEnable 11

#define RS485Transmit    HIGH
#define RS485Receive     LOW

#define RS485Disable     HIGH
#define RS485Enable      LOW

SoftwareSerial rs485(SerialRX, SerialTX);
int inputplc = 0;

// Delimitatori del pacchetto dati
#define START_BYTE 0x00
#define END_BYTE 0x20

// Buffer del dato
#define MAXBUF 8
byte buf[MAXBUF];
// Puntatore/contatore caratteri presenti nel buffer
byte numchar = 0;

// Flag per indicare che è iniziato un blocco
bool blocco = false;

// Numero di byte che determinano il tipo di dato
#define DATO_INT 2

void setup() {
  Serial.begin(9600);

  rs485.begin(4800);

  pinMode(SerialReceiveEnable, OUTPUT);
  pinMode(SerialDataEnable, OUTPUT);

  digitalWrite(SerialReceiveEnable, RS485Receive);
  digitalWrite(SerialDataEnable, RS485Enable);
}

void loop () {
  if ( rs485.available() ) {
    // Leggo un byte
    char c = rs485.read();
    
    // Visualizzo il byte
    if ( c < 16 )
      Serial.print("0");
    Serial.print(c, HEX);
    Serial.print(" ");
    
    // Se non ho già iniziato il blocco
    if ( blocco == false )
    { // Se è il byte di start
      if ( c == START_BYTE )        
          blocco = true; // mi limito ad attivare il flag
      return; // In ogni caso ignoro il dato.
    }
    
    // Quindi ora sono in un blocco, questo è il byte finale?
    if ( c == END_BYTE )
    { // Si, è il byte finale!
      // Ora formatto il dato in base a cosa ho ricevuto
      // (uso uno switch per eventuali future espansioni)
      switch (numchar)
      {
        case DATO_INT:
          // Calcolo il valore a 2 byte
          inputplc = buf[0] + buf[1] * 256;
          break;
        default:
          // Formato sconosciuto
          inputplc = -1;
          break;
      }
      // e lo stampo in decimale
      Serial.println();
      Serial.print("Dato ricevuto: ");
      Serial.println(inputplc);
      // Reset del contatore caratteri nel buffer
      numchar = 0;
      // Reset del flag
      blocco = false;
    }
    else
    { // Il byte fa parte del dato, quindi lo accumulo nel buffer
      if ( numchar < MAXBUF )
        buf[numchar++] = c;
    }
  }
}

PS: ovviamente non l’ho provato, ma ad occhio mi pare che possa funzionare correttamente. Fammi sapere.

Eccomi, sono solo andato a farmi un giro in montagna (sul vulcano) e sono appena ritornato. PERFETTO ! Il programma funziona e ho capito come hai fatto. I 4 byte non sono 4 perché il mio H20 è un dato come un altro che faccio trasmettere al PLC cioè:

-||--------------[ MOV K255 D11]-- M0 |-----[ MOV H20 D12]-- |-----[ MOV K511 D13]-- |-----[ MOV H20 D14]-- |-----[ MOV K1023 D15]-- |-----[ MOV H20 D16]-- |-----[ MOV K2047 D17]-- |-----[ MOV H20 D18]-- ............

e ho fatto così dall'inizio per "sgrossare il problema" e riconoscere subito i dati intermezzandoli con uno spazio. Però, adesso che abbiamo (hai) capito come è il protocollo, posso toglierli tutti e vedere se riesco a caprili anche senza H20 modificando la trasmissione così:

-||--------------[ MOV K255 D11]-- M0 |-----[ MOV K511 D12]-- |-----[ MOV K1023 D13]-- |-----[ MOV K2047 D14]-- ............

Intanto ti ringrazio infinitamente con un bel doppio karma. Se non me la cavo col resto (prendo i dati, li converto, li trasmetto su wifi con ESP32, vado su cloud, ma questo è già funziona) magari ci risentiamo....

Beh non conosco quel PLC e quindi neanche quei comandi "assembler" ;) con "K", "H" eccetera, comunque bene, sono contento di averti aiutato. :D

Però in linea di massima cercando di interpretare un poco (conoscendo vari linguaggi assembler, dallo Z80 in poi...) mi pare di capire che il MOV sia un "move", che imposta/copia un dato su un "qualcosa" che forse è un indirizzo o una serie di registri ("Dxx"), e con "H" si intende un valore esadecimale ("H20" è il nostro 0x20 ossia lo spazio), mentre non so cosa sia "K". In ogni caso visto che stai mandando dei dati, ti consiglierei comunque di definire un protocollo anche minimale per evitare di interpretare erroneamente i dati, se hanno un significato diciamo "posizionale" (a meno che non siano solo una sequenza di dati per cui non è importante sapere cosa sia cosa). Magari mandando comunque un byte iniziale (es. uno 0x00) ed uno finale (es. 0xFF) definiti con valori possibilmente non presenti nei pacchetti dati, o una sequenza di start (es. 0x00 0xFF) ed una di stop (es. 0x10 0xFE) sempre con lo stesso vincolo, fino a creare un protocollo bidirezionale che preveda una conferma (ACK) da parte di Arduino al PLC. Tutto dipende quindi, come puoi immaginare (e puoi però saperlo solo tu che conosci l'ambito del progetto), dall'eventuale "importanza" dei dati trasmessi.