Problema Lettura risposta modbus

Ciao a tutti, sono diversi giorni che sto cercando di far comunicare due Arduini tramite Modbus RS485. Cercherò di spiegarmi il meglio possibile.

Come librerie sto usando le due in allegato. Ma ne ho provate anche altre, praticamente tutte quelle che sono riuscito a trovare

I due comunicano tramite integrato RS485 SN65176BP.
Il problema è che il master non riesce a leggere la risposta. O meglio, mi trovo nel primo byte uno zero che non ci dovrebbe essere:
questa è la risposta che lo slave invia: 1 3 14 24 151 0 123 0 2 0 3 0 4 0 5 0 99 57 118
e questa è qualla che ricevo dal master: 0 1 3 14 24 151 0 123 0 2 0 3 0 4 0 5 0 99 57 11 e dopo aver letto la penultima cifra arduino crasha e si riavvia.

Mi correggo, nel master è come se ricevessi due risposte:

RISP: 0 
RISP: 1 16 0 0 0 2 65 200

La risposta si riferisce ad un'operazione di scrittura.

Ovviamente entrambi i messaggi sono inviati senza spazi.

Ho provato a collegare lo slave ad un master su pc, e risponde correttamente, quindi ho dato per scontato che il problema fosse nel codice del master. Ho anche provato la comunicazione tramite RS232 diretta, stesso problema.

Grazie a tutti.

In allegato il codice del master e dello slave.

modbus_slave.ino (18.5 KB)

modbus_master.ino (12.8 KB)

Elettricamente hai messo le resistenze di inizio e fine linea (Rt) ?

Certo che in quelle librerie ci sono una serie di commenti con FIXME:
Non mi sembra molto affidabile.

No, non resistenze, può essere quello il problema?

Ho escluso problemi hardware per il fatto che collegandolo ad un pc funziona.

Stasera provo a metterle e vediamo che succede.

Grazie!

Ciao, allora vi aggiorno sulla mia situazione. Ho messo le resistenze di fine linea ma ovviamente non cambia niente. Sempre solito problema. Ho fatto ulteriori prove anche con altre librerie modbus e il risultato è sempre lo stesso. Riesco a scrivere sullo slave (visto che non è strettamente necessario leggere la risposta dello slave dal master) ma al momento della lettura il master non riesce a leggere.

Ho fatto anche un'altra prova senza modbus, con due semplici sketch dove uno invia un semplice int su seriale e l'altro legge.

Sketch 1 ("Slave"):

/*
  Mega multple serial test
 
 Receives from the main serial port, sends to the others. 
 Receives from serial port 1, sends to the main serial (Serial 0).
 
 This example works only on the Arduino Mega
 
 The circuit: 
 * Any serial device attached to Serial port 1
 * Serial monitor open on Serial port 0:
 
 created 30 Dec. 2008
 modified 20 May 2012
 by Tom Igoe & Jed Roach
 
 This example code is in the public domain.
 
 */

int pin= 7;

void setup() {
  // initialize both serial ports:
  Serial.begin(9600);
  Serial1.begin(9600);
  pinMode(pin,OUTPUT);
}

void loop() {
  // read from port 1, send to port 0:
  
  if (Serial1.available()) {
    int inByte = Serial1.read();
    Serial.println(inByte);
    Serial1.flush();
     //delay(100);
     
     //digitalWrite(pin,HIGH);
     delay(10);
  
    // Serial1.write(45);
    digitalWrite(pin,LOW);
  delay(10);   
  }
  
}

Sketch 2 ("Master"):

/*
  Mega multple serial test
 
 Receives from the main serial port, sends to the others. 
 Receives from serial port 1, sends to the main serial (Serial 0).
 
 This example works only on the Arduino Mega
 
 The circuit: 
 * Any serial device attached to Serial port 1
 * Serial monitor open on Serial port 0:
 
 created 30 Dec. 2008
 modified 20 May 2012
 by Tom Igoe & Jed Roach
 
 This example code is in the public domain.
 
 */
int pin =2;

void setup() {
  // initialize both serial ports:
  Serial.begin(9600);
  Serial1.begin(9600);
  pinMode(pin,OUTPUT);
}

int i=0;
void loop() {
 i++;
  Serial.print("Ciclo ");
  Serial.println(i);
  
  if(Serial1.available()){
    Serial.println("C'e qualcosa");
    while(Serial1.available()){
    int b= Serial1.read();
    Serial.println(b);
    
    }
  }
  digitalWrite(pin,HIGH);
  delay(1);
  

  Serial1.write(1);
    
  delay(100);
  
 Serial1.flush();
  
  digitalWrite(pin,LOW);
  
  
  delay(1000);
}

Dallo "slave" leggo tranquillamente il mio intero, nel master dopo la prima scrittura mi ritrovo nella Serial1 un byte =0;

Questo è l'output su Serial del master:

Ciclo 1
Ciclo 2
Ciclo 3
C'e qualcosa
0

Che di base è lo stesso problema che avevo con le librerie modbus. Mi sfugge qualcosa?? Ultima cosa, che magari può essere utile, come board utilizzo un Mega2560 e un Seeduino AtMega1280.

Nei tuoi sketch non vedo la gestione del cambio di stato dei chip 65176, cioè non li commuti da RX e TX per scelta o perché li imposti via hardware?

Io ho usato il bus 485 per il progetto congiunto con Menniti della centralina programmabile che è stata pubblicata sullo scorso numero di Elettronica In. Ho però avuto dei problemi nel gestire i dati durante la commutazione RX/TX, non so se può esere anche il tuo caso. In pratica dovevo materialmente svuotare i registri del buffer hardware della seriale. Ho notato infatti che anche se il buffer seriale software veniva dato per vuoto, in realtà l'ultimo byte poteva essere spedito in ritardo restando nel registro interno del microcontrollore. E questo dava noia perché avevo sempre dati non trasmessi. E questi dati spesso mi confondevano il programma.

Questo è il pezzo di codice che ho messo prima di iniziare la ricezione, che uso per svuotare il buffer:

    if (RS485) { //passo in RX
        while (!(UCSR0A & (1 << TXC0))); //attendo che il buffer TX si svuoti
        flushSerial(); //svuoto il buffer RX
        digitalWrite(RXTX_RS485, RS485RX);
    }

La digitalWrite comanda il pin a cui sono agganciati i piedini RX/TX del Max485 (identico al 65176/75176).
Mentre questo è il codice usato per tornare in TX.

    if (RS485) { //torno in TX
        UCSR0A |= (1 << TXC0); //imposto il buffer TX come vuoto (1-->resetta il bit)
        digitalWrite(RXTX_RS485, RS485TX);
    }

flushSerial è questo:

//svuoto il buffer in ricezione della seriale
void flushSerial() {
    while (Serial.available()) {
        byte temp = Serial.read();
    }
}

ripristino cioè il vecchio comportamento della Serial.flush fino all'IDE 1.0.

Una domanda, forse stupida. TXC0 è lo stesso anche se per la trasmissione uso una seriale diversa da Serial, per esempio Serial1, Serial 2 ecc...?

Quel codice era di esempio, è scritto per l'Atmega328. Devi prendere il datasheet dell'Atmega2560 e vedere quali sono i registri corrispondenti.
A "naso" mi sento di dirti che se usi Serial1, può darsi che sia il registro UCSR1A ed il bit TXC1, ma non posso confermare senza vedere la documentazione. Quindi, controlla tu :wink:

Vi aggiorno con i miei progressi. Inanzi tutto ho cambiato librerire, adesso uso quelle in allegato. Per fare una prova ho messo da parte i due chip rs485 e collegato i due arduini tramite seriale diretta. Risultato? Funziona, i due arduini comunicano, il master riesce a scrivere e leggere lo slave tramite protocollo ModBus.

Il problema è che non mi funziona con le due 485, è come se il master leggesse solo metà risposta dello slave. Come ha scritto leo72 le due librerie implementano la "pulizia" del registro UCSRXA (con X il numero della seriale, dalle informazioni che ho trovato in rete sembra sia corretto).

Un dubbio, come comando io uso un solo pin dell'arduino per comandare la 485, dove ho i due pin RE e DE "ponticellati".

PS: uso il primo file sullo slave, il secondo come master.

Modbus.cpp (21.7 KB)

ModbusMaster.cpp (26.6 KB)

Sembra che adesso funzioni anche in RS485. Ho rimosso i vari delay() che c'erano nelle librerie (alcuni aggiunti anche da me durante le prove) e sistematto bene i registri UCSRXA prima e dopo la ricezione e usando il vecchio flush()

//svuoto il buffer in ricezione della seriale
void flushSerial() {
    while (Serial.available()) {
        byte temp = Serial.read();
    }
}

come suggerito da leo72. Unica cosa che mi rimane da fare, con una lettura una volta al secondo, certe volte sbaglia e ritorna errore.

Allego uno zip con le librerie funzionanti (almeno per me) e con gli sketch di test che ho usato, magari servono a qualcuno.

ModbusRS485_Test.rar (359 KB)

leo72:
Questo è il pezzo di codice che ho messo prima di iniziare la ricezione, che uso per svuotare il buffer:

    if (RS485) { //passo in RX

while (!(UCSR0A & (1 << TXC0))); //attendo che il buffer TX si svuoti
        flushSerial(); //svuoto il buffer RX
        digitalWrite(RXTX_RS485, RS485RX);
    }






if (RS485) { //torno in TX
        UCSR0A |= (1 << TXC0); //imposto il buffer TX come vuoto (1-->resetta il bit)
        digitalWrite(RXTX_RS485, RS485TX);
    }




flushSerial è questo:


//svuoto il buffer in ricezione della seriale
void flushSerial() {
    while (Serial.available()) {
        byte temp = Serial.read();
    }
}

Chi mi spiega come funziona questo codice? Nello specifico cosa fa?