[Risolto] Problemi comunicazione RS485

Ciao a tutti Chiedo aiuto al forum, perchè non riesco a far funzionare una "banalissima" comunicazione half-duplex bidirezionale usando RS485... e non so proprio cos'altro provare (sono i miei primi esperimenti con questo tipo di bus).

*** Cosa voglio fare: Il mio intento è quello di far comunicare due MCU in configurazione master/slave nel modo seguente: 1) MASTER invia 20bye, per comodità sono valori FISSI, solo uno (il TERZO) è progressivo, giusto per vedere se "passa" correttamente. 2) SLAVE riceve i dati e risponde con 10byte, anche questi sono fissi, solo uno è "speciale" (ACK) giusto per controllare se è arrivato integro sul master.

*** Come lo sto facendo: - MASTER: un ATMega1284 su breadboard che impiega la seriale HW per trasmettere. - SLAVE: un Arduino UNO con IDE 1.0 e SoftwareSerial per la comunicazione (la seriale HW la uso per debug) - COLLEGAMENTI: due 751768 collegati nel modo consueto: pin 8:Vcc; 5:Gnd; 2,3:insieme-pin enable; 1,4:Rx,Tx MCU; 6,7:doppino telefonico di circa 2 mt. - LIBRERIA: Ho scaricato la libreria di nick gammon (http://www.gammon.com.au/forum/?id=11428) giusto per fare delle prove veloci; pare che diversi utenti la usino senza problemi.

*** Il problema: Succede questo. Se provo SOLO la parte della trasmissione, sembra funzionare tutto bene. Lo slave riceve i 20 byte di dati correttamente (valori FISSI da 68 fino a 87, tranne il TERZO byte, che cambia progressivamente cosi com'è giusto che sia!). Questo mi porterebbe a pensare che i collegamenti siano corretti e gli sketch caricati fanno il loro lavoro. Ecco cosa riceve lo slave:

DATI RICEVUTI SENZA risposta dello SLAVE RX: 68; 69; 10; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 11; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 12; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 13; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 14; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 15; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 16; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 17; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 18; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 19; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 20; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; (OK: notare il terzo byte che cambia progressivamente)

Se invece faccio rispondere lo SLAVE, inviando i 10 byte di risposta al master, succedono cose strane... Innanzitutto le righe dei valori ricevuti appaiono NON più "temporizzate" ma un po a casaccio, come se ci fossero dei delay random tra una riga e la successiva (collisioni???); spesso e volentieri dopo un po' il tutto si BLOCCA e devo disalimentare tutto. E poi la cosa più strana (per me): ovvero le righe dei dati ricevuti dallo slave (finchè arrivano) hanno il TERZO byte (e solo quello) non più progressivo ma...random, ecco come si mostrano le righe:

DATI RICEVUTI CON risposta dello SLAVE RX: 68; 69; 4; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 2; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 2; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 2; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 2; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 1; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 2; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 1; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 1; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 1; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; RX: 68; 69; 2; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; (ERRORE: notare il terzo byte...ad muzzum!!)

e francamente non capisco COME O CHI DIAVOLO possa alterare SOLO quel byte.

Inutile dire che i LED sul master che dovrebbero segnalare la corretta risposta dello slave dicono che: a) Non è arrivato nessun dato di risposta dallo slave (LED errore acceso) b) Il valore "speciale" (ACK) che mi aspettavo di ricevere me lo sogno!! (LED asck spento) Solo OGNI TANTO...ma raramente, sti led accendono/spengono sempre con il sistema..ad muzzum!!

Ho provato ANCHE a mettere le resistenze nella linea di trasmissione (da A verso Vcc e B verso Gnd da 1K) e/o la resistenza "tappo" da 100 ohm tra A e B ma è sempre uguale (credo siano inutili data la brevità della lunghezza del cavo).

Per ultimo, segnalo che la board del 1284 è alimentata da Arduino UNO (può essere un problema?)

Non so più che provare...probabilmente mi sfugge qualcosa di essenziale :( Naturalmente posto (ma nel post successivo per ragioni di spazio), i due sketch che sto utilizzando.

Grazie a quanti avranno la pazienza di leggere e rispondere...

******* MASTER ATMega 1284 ********

/*
  ***** MASTER: ATMega1284 *****
  Libreria prelevata dal sito: http://www.gammon.com.au/forum/?id=11428
  **** Master ****
  It is your responsibility to turn on the "write enable" pin before and after doing a "send". 
  This configures the RS485 chip to allow writing to the network. 
*/
#include "RS485_protocol.h"

#define ADDRESS_SLAVE   0x44     // indirizzo dello slave al quale inviare i dati
#define ACK_VALUE       0xFE     // valore di controllo che mi aspetto di ricevere dallo slave

const byte ENABLE_PIN = 21;      // Pin di abilitazione RS184 (fpin 27)
const byte LED_PIN = 0;          // LED segnalazione nessuna ricezione (fpin 1)  
const byte LED_ACK = 15;         // LED segnalazione ricezione corretta (fpin 21)

// callback routines (uso una seriale del 1284)
  void fWrite (const byte what) {
    Serial.write (what);  
  }
    
  int fAvailable () {
    return Serial.available ();  
  }
  
  int fRead () {
    return Serial.read ();  
  } 

void setup() {
  Serial.begin (28800);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  pinMode (LED_PIN, OUTPUT);     // Segnala se sono stati ricevuti dati dallo slave
  pinMode (LED_ACK, OUTPUT);     // Segnala se uno dei valori ricevuti è quello che ci si aspettava
} 
  
byte value;

void loop() {
  
  // imposto un valore da passare allo slave da 0 a 199
  value = (++value) % 200;   
  
  // passo allo slave 20 byte
  byte msg [20];
  msg[0] = ADDRESS_SLAVE;     // primo byte è l'indirizzo dello slave
  msg[1] = 0x45;              // secondo byte è un valore arbitrario
  msg[2] = value;             // TERZO byte è un valore che cambia
  for (byte i=3; i<20; i++)   // dal 4rto fino al 20simo sono valori arbitrari fissi
    msg[i] = 0x44 + i;
  

    /* ****** SOLO per Seriale HARDWARE************ */
    // TRASMISSIONE DATI
    digitalWrite (ENABLE_PIN, HIGH);  // enable sending
    sendMsg (fWrite, msg, sizeof msg); 
    while (!(UCSR0A & (1 << UDRE0)))  // Wait for empty transmit buffer
       UCSR0A |= 1 << TXC0;  // mark transmission not complete
    while (!(UCSR0A & (1 << TXC0)));   // Wait for the transmission to complete
    digitalWrite (ENABLE_PIN, LOW);  // disable sending      
     
  // riceve risposta dallo slave (10 byte)
  byte buf [10];
  byte received = recvMsg (fAvailable, fRead, buf, sizeof buf);
  
  // se nessun dato ricevuto allora accendi led per segnalare ERRORE
  digitalWrite (LED_PIN, received == 0);  
  
  // se il secondo dato ricevuto è l'ACK allora accendi LED per segnalare valore corretto
  digitalWrite (LED_ACK, buf[1] == ACK_VALUE);
  
  delay(100);

}

****** SLAVE (ARDUINO UNO) *******

/*#define RISPONDE 1

/* **** Parte SLAVE per ARDUINO ATMega328P **** */
#include <SoftwareSerial.h>
#include "RS485_protocol.h"

#define ADDRESS_SLAVE   0x44     // indirizzo assegnato allo slave
#define ACK_VALUE       0xFE     // valore di controllo di risposta

SoftwareSerial rs485 (2, 3);      // receive pin, transmit pin
const byte ENABLE_PIN = 4;

void fWrite (const byte what) {
  rs485.print (what);  
}
  
int fAvailable () {
  return rs485.available ();  
}

int fRead () {
  return rs485.read ();  
}
  
void setup() {
  rs485.begin (28800);           // linea di comunicazione
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  Serial.begin(9600);            // mostra dati ricevuti sulla seriale
}

void loop() {
  // si pone in ricezione dei 20 byte...
  byte buf [20];
  byte received = recvMsg (fAvailable, fRead, buf, sizeof buf);
  
  if (received) {                              // ho ricevuto qualcosa...
    if (buf [0] != ADDRESS_SLAVE) {            // controlla se è lo slave giusto
      Serial.println("Non e' diretto a me!"); 
      return;
    }
   
    /********* SE QUESTA PARTE SI ESCLUDE LA RICEZIONE DAL MASTER E' CORRETTA *****/
    #ifdef RISPONDE
    byte msg [] = {                 // risponde inviando 10 byte al master
       0,                           // device 0 (master)
       ACK_VALUE,                   // valore di controllo   
       2,  
       3,  
       4,  
       5,  
       6,  
       7,  
       8, 
       9  
    };    
    delay (10);  // give the master a moment to prepare to receive
    digitalWrite (ENABLE_PIN, HIGH);  // enable sending
    sendMsg (fWrite, msg, sizeof msg);
    //delayMicroseconds(660);
    digitalWrite (ENABLE_PIN, LOW);  // disable sending
    #endif
   
   // stampa 20 byte ricevuti dal master sulla seriale, per controllo.
    Serial.print("RX: ");
    for (byte i=0; i<20; i++) {
      Serial.print(buf[i]);
      Serial.print("; ");    
    }
    Serial.println(""); 
          
   }  
}

Intanto ti do un suggerimento. Siccome la SoftwareSerial è guidata ora da interrupt sia in trasmissione che in ricezione, potrebbe darsi che il delay sia generato dal suo uso.

Poni inoltre la velocità di trasmissione della seriale hardware sull'Atmega328 uguale o superiore a quella della seriale software, per evitare che l'invio dei dati a 9600 rallenti eccessivamente l'esecuzione della ricezione software a 28800.

Il 3° byte potrebbe venir fuori a caso perché legge un byte che non è quello dovuto? Cioè, hai messo un controllo per far sì che la lettura inizi proprio dal 1° byte? Che succede se la lettura inizia, che so, dal 4°?

Leo, intanto grazie per aver risposto.

Ho provato, giusto per scrupolo, a impostare la seriale hw di Arduino a 38400, dando così un buon margine di distacco a quella seriale ma tutto funziona allo stesso modo. Del resto, pensando anch'io a una cosa simile, avevo già provato a escludere la seriale per il debug, basandomi solo sui LED di segnalazione del master, quindi temevo già che sarebbe servito a poco.

Il 3° byte potrebbe venir fuori a caso perché legge un byte che non è quello dovuto? Cioè, hai messo un controllo per far sì che la lettura inizi proprio dal 1° byte?

Non ho messo un "controllo" nel senso specifico del termine, ma a questo dovrebbe teoricamente pensarci il protocollo implementato nella libreria. Ma anche volendo supporre che non ci sia nessun tipo di controllo sulla lettura dell'inizio dei dati, e considerando che i dati ricevuti siano letti dall'array in sequenza, non credi sia strano che i primi 2 byte dell'array inviato siano ricevuti correttamente (valori 68 e 69), poi il terzo a casaccio mentre tutti quelli dal 4° al 20° ancora correttamente (e non può essere una coincidenza) ?? Sembra quasi che sia il MASTER a mandare quei numeri strani, anche se non so proprio in che modo. Ma il problema più importante rimane quello che il master, non riceve proprio nulla dallo slave, se non saltuariamente...

Ho fatto anche la prova a collegare direttamente la seriale hw di Arduino al posto di SoftwareSerial per togliere ogni dubbio (ovviamente rinunciando al debug sulla finestra seriale)...uguale!!

Uffa! Qualche suggerimento, prima che applichi il metodo dell'unzione dell'olio e l'imposizione delle mani?

Piccolo consiglio:

Perche' non usi la MEGA per il debug... (ha tre seriali hardware ed eviti possibili problemi dovuti all' uso della SoftSerial.

CIAO

PS. Anche io sto giocando con l' RS485, pero' utilizzo i MAX485. Se riesco a tirarne fuori qualcosa ti scrivo !

anch'io sto 'giocando' con l'RS485, avevo visto la libreria postata da te, ma alla fine ho preferito scrivermi un mio protocollino. se vuoi dargli un'occhiata sta qui: http://arduino.cc/forum/index.php/topic,104249.15.html

cmq, nel tuo codice proverei due cose con i delay: -aumenta il delay(10) di risposta dello Slave. -metti un piccolo delay subito dopo aver abilitato l'enable pin di invio del master (anche se in realtà hai detto che trasmette bene..) -mi chiedo, ma non c'è bisogno di svuotare il buffer dopo ogni ricezione corretta?

Davide.

Perche' non usi la MEGA per il debug... (ha tre seriali hardware ed eviti possibili problemi dovuti all' uso della SoftSerial.

He he, per la semplice quanto validissima ragione che io NON possiedo un MEGA :) E comunque se leggi attentamente il post vedrai che, tra le svariate prove, ho fatto anche quella di collegare i due MCU direttamente con le seriali HW, ma senza risultato (Quel fantastico MCU che è l'ATMega1284 dipspone, tra le altre cose, anche di due seriali hw :D )

@dab77: grazie, darò un'occhiata al post che hai linkato; in verità l'intento originale è quello di scrivermi un protocollino per il mio uso specifico, ma volevo intanto fare delle prove rapide proprio per testare il tipo e la velocità di comunicazione.

aumenta il delay(10) di risposta dello Slave.

Già fatto, se guardi il codice originale, addirittura il valore è "delay(1)". L'ho pure aumentato ulteriormente ma...niet.

metti un piccolo delay subito dopo aver abilitato l'enable pin di invio del master

Si provato...ma credo che proprio la trasmissione non abbia particolari problemi.

mi chiedo, ma non c'è bisogno di svuotare il buffer dopo ogni ricezione corretta?

Se per buffer intendi il vettore msg, allora no, non c'è bisogno. Infatti ad ogni ciclo di loop viene ricreato con byte msg [] = {...}; se invece intendi metodi tipo flush()...mah, ho provato a piazzarlo prima, dopo, durante il ciclo di lettura dei dati.

Forse mi converrebbe mollare sta libreria e passare a qualcosa di più "artigianale"...

dalubar: - COLLEGAMENTI: due 751768 collegati nel modo consueto: pin 8:Vcc; 5:Gnd; 2,3:insieme-pin enable; 1,4:Rx,Tx MCU; 6,7:doppino telefonico di circa 2 mt.

hai anche collegato le resistenze terminali da 120 ohm nel tuo bus?

ma in trasmissione gli va bene, quindi penso che resistenze varie e/o cablaggi non c'entrano. forse solo il pin-enable dello Slave magari fa male contatto. Prova con un tester se veramente va alto.

Per il flush ho letto che è diverso dalla IDE 02x alla 1.0, ma non mi ci sono soffermato un granchè.

perchè non provi a fare solo una trasmissione dallo Slave al Master? così sai se cablaggi e codice vanno bene. a quel punto deve per forza essere un problema di timing di trasmissione.

Risolto!

E' andato tutto nell'unico modo "umano" nel quale poteva andare (avevo già prenotato la seduta con l'esorcista). In realtà non c'era proprio niente che non andava, era tutto a posto; cablaggio, connessioni, sketch, cavo, tutto funzionava perfettamente...tutto TRANNE una cosa: la MCU! Ho scoperto (con un piccolo dolore) che il mio ATMega1284 ha seri problemi esistenziali...sostituendolo con un ATMega328 stand-alone è andato subito tutto a posto.

Ma in effetti, non poteva essere diversamente...quel 3° byte (e solo quello) cambiato arbitrariamente e senza nessuna ragione apparente che mi ha fatto impazzire (vedi post precedenti) doveva per forza avere origini "interne" e di tipo hardware.

Grazie, comunque, a quanti hanno dato il loro contributo :)

dalubar: Ho scoperto (con un piccolo dolore) che il mio ATMega1284 ha seri problemi esistenziali...

Partito? E come?

Non potrei dirlo con certezza...ci ho lavorato tantissimo e non ha mai dato problemi. Solo una volta, ha beccato una botta di alimentazione un po' forte a causa di un cornutissimo alimentatore difettoso; mi ricordo che quella volta si è "sprogrammato" quasi come avesse subito una programmazione HV, ho dovuto ricaricare il bootloader per reimpostare i fuse, ma stranamente ho dovuto farlo per un paio di volte di seguito. Quando lo collegavo sembrava..."ubriaco", poi si è ripreso e ha continuato a funzionare come se niente fosse. Ma probabilmente da allora non è più lo stesso... :(