Problema com comunicação RS485 Master / Slave

Olá,

Já postei a dúvida no fórum Network, Protocols and Devices, mas como estão demorando um pouco para entender, posso ter um pouco de dificuldade em entender a resposta em inglês, e o pessoal do fórum em português são muito prestativos, decidi postar aqui também.

Resumindo: consigo fazer a comunicação Master → Slave utilizando as funções do Nick Gammon, mas não consigo enviar o feedback. Tenho um loop no Master que fica aguardando a resposta, mas o Master fica parado neste loop.

Seguem os códigos:

Master:

#include "RS485_protocol.h"
//#include <NewSoftSerial.h>

const byte ENABLE_PIN = 4;
const byte LED_PIN = 13;
int led;
byte msg[2];

// callback routines

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);  // built-in LED
  led = 0;
}  // end of setup

void loop ()
{

  if (led == 0)
    memcpy (msg, (byte[]){1,1}, sizeof msg);  // device 1, turn on
  else if (led == 1)
    memcpy (msg, (byte[]){2,1}, sizeof msg);  // device 2, turn on
  else if (led == 2)
    memcpy (msg, (byte[]){1,0}, sizeof msg);  // device 1, turn off
  else if (led == 3) {
    memcpy (msg, (byte[]){2,0}, sizeof msg);  // device 2, turn off
    led = -1;
  }

  led++;

  // send to slave  
  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

  // receive response  
  byte buf [10];
  byte received = recvMsg (fAvailable, fRead, buf, sizeof buf);

  digitalWrite (LED_PIN, received == 0);  // turn on LED if error   */ 

  delay (500);

}  // end of loop

Slave 1:

#include "RS485_protocol.h"

const byte ENABLE_PIN = 4;
const byte LED_PIN = 13;
const byte ERROR_PIN = 9;
const byte SLAVE_NUMBER = 1;
const byte GREEN_PIN = 8;

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

int fRead ()
{
  return Serial.read ();  
}
  
void setup()
{
  Serial.begin (28800);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  pinMode (ERROR_PIN, OUTPUT);
  pinMode (GREEN_PIN, OUTPUT);
  pinMode (LED_PIN, OUTPUT);
  digitalWrite (ENABLE_PIN, LOW);
}

void loop()
{
  byte buf [20];
  
  byte received = recvMsg (fAvailable, fRead, buf, sizeof (buf) - 1);
  
  if (received)
    {
    digitalWrite (ERROR_PIN, LOW);
    if (buf [0] != SLAVE_NUMBER) {
      digitalWrite (GREEN_PIN, HIGH);
      return;  // not my device
    }
    digitalWrite (GREEN_PIN, LOW);
    digitalWrite (LED_PIN, buf[1]);  // set light level

    byte msg [] = {
       0,  // device 0 (master)
       3,  // turn light on command received
    };
    
    delay (1);  // give the master a moment to prepare to receive
    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
    
    
   }  else { // if nothing received
     digitalWrite (ERROR_PIN, HIGH);
   }  // end receive if
   
}  // end of loop

No Master, o LED fica sempre aceso (indicando erro na leitura), e no Slave, o LED de erro acende entre comandos bem sucedidos.

Alguma idéia?

De onde tiraste este código?

Eu posso estar errado, mas olhando assim de repente parece que envias 2 bytes e tentas ler 20...

bubulindo:
De onde tiraste este código?

Eu posso estar errado, mas olhando assim de repente parece que envias 2 bytes e tentas ler 20…

Do mesmo site que peguei a biblioteca: Gammon Forum : Electronics : Microprocessors : RS485 communications

Você não está errado, mas não acredito que isso seja problema, pois o master também envia 2 bytes e o slave tenta ler 20 e pega os 2 bytes tranquilamente.

Pergunta idiota...

Estás a usar um Uno ou 2009?

Uno (master) e um ATMega328p na protoboard funcionando com os 8 MHz internos.

Um problema que eu vejo é que o Uno tem dois chips ligados aos pinos 0 e 1 (que suponho seja o que estás a usar).

É sempre uma possibilidade que o chip que faz a traducão entre USB e TTL esteja a estragar isto.

Experimenta com a NewSoftSerial, como o Gammon tem no seu site e vê o resultado. Se tiveres a oportunidade de usar dois chips em vez da placa do Arduino seria o ideal pois tiravas a limpo que não tinhas problemas de ligacão.

O hardware está de acordo com o que ele tem no site dele?

Hum... agora entendi o motivo do NewSoftSerial... alimentar o Uno pela porta DC ao invés do USB pode resolver?

De qualquer forma, vou tentar trocar de portas. Mas acho que o problema é de sincronização, algo assim.

Sobre o hardware, só não coloquei os resistores, mas como por enquanto está tudo na protoboard, está tranquilo.

Existe um motivo pelo qual as resistências estão lá...

Alimientar pela porta DC não ajuda porque o outro chip continua activo.

Existe, melhorar o sinal para longas distâncias, aumentando a diferença entre as linhas A e B. Mas na protoboard, não precisa, até onde eu li e entendi. Inclusive, no datasheet nem fala nessas resistências entre as linhas e o ground/5v, apenas em uma resistência entre as linhas A e B.

Boas,

A ver o teu esquema sem ver o código, vejo que lhe faltam as 2 resistências fim de linha que normalmente são de 120 ohm.

E para mais RS485 não é nenhum protocolo “soft” mas sim hard

aqui te deixo alguns links que pesquisei em tempos para comunicar com uma carta de entrada/saídas via rs485 ( vê os meus antigos posts)

http://arduino-info.wikispaces.com/RS485-Brick

Cumprimentos

Zé tretas

LegendBR: Existe, melhorar o sinal para longas distâncias, aumentando a diferença entre as linhas A e B. Mas na protoboard, não precisa, até onde eu li e entendi. Inclusive, no datasheet nem fala nessas resistências entre as linhas e o ground/5v, apenas em uma resistência entre as linhas A e B.

Ok... tu obviamente sabes o que está errado no sistema logo não me parece que te consiga ajudar muito mais.

É, acho que era algum problema de timing… enfim, acabei deixando a biblioteca do Nick Gammon de lado e desenvolvi a minha própria, com base no protocolo que encontrei neste link.

Depois de alguns ajustes, já estou conseguindo comunicar os arduinos pelo RS495, com uma comunicação bem robusta (com o protocolo tenho acesso a MUITOS comandos, agora só preciso definir os comandos de acordo com o design da minha rede), utilização relativamente simples, e rápida o suficiente para minha necessidade, sem precisar fazer delay para não ter problema de sincronia (estou conseguindo enviar um comando após o outro, sem delay entre eles, e todos os comandos estão sendo entendidos).

Ah, e não estou com resistores entre as pernas A e B dos MAX485. Como falei, uma vez que eles estão no protoboard, não precisam desse artifício. Pelo o que eu entendi, esses resistores ajudam a melhorar o sinal para longas distâncias, para aumentar a diferença entre a linha A e B, melhorando assim o sinal (pelo menos foi assim que eu entendi, depois testo em um sistema real, com maiores distâncias, onde o sinal pode sofrer interferências).

Ola a uns tempos também andei a brincar com o RS485 e desenvolvi uma pequena lib para o que eu queria. Nao está terminada e deve ter coisas menos correctas mas funciona para o meu fim.Tem coisas que podem ser melhor optimizadas, nao estou a implementar ACK dos slaves inda ,nem tempos de timeout de resposta dos slaves …
Digamos que a deixei ficar assim por falta de tempo e ter o projecto na gaveta à espera de ser sacado fora.
No entanto pode te dar uma visão do que fiz e fazer melhor do que fiz.
Também falta me depois colocar isso tudo dentro de uma class c++ para ser possível instanciar um objecto

RS485.h (467 Bytes)

RS485.cpp (2.8 KB)

HugoPT: Ola a uns tempos também andei a brincar com o RS485 e desenvolvi uma pequena lib para o que eu queria. Nao está terminada e deve ter coisas menos correctas mas funciona para o meu fim.Tem coisas que podem ser melhor optimizadas, nao estou a implementar ACK dos slaves inda ,nem tempos de timeout de resposta dos slaves ... Digamos que a deixei ficar assim por falta de tempo e ter o projecto na gaveta à espera de ser sacado fora. No entanto pode te dar uma visão do que fiz e fazer melhor do que fiz. Também falta me depois colocar isso tudo dentro de uma class c++ para ser possível instanciar um objecto

Então, fiz como você, mas já implementei o ACK dos slaves e coloquei dentro de um classe. Uma diferença que percebi pelo o .h que você passou é que utilizo um protocolo fechado, onde cada comando tem 15 bytes, e me parece que sua biblioteca deixa isso aberto.

A noite envio minha lib para vocês darem uma olhada.

Demorou, mas segue minha biblioteca para comunição RS485.

Como dito anteriormente, o protocolo utilizado é o descrito neste link.

No construtor é informado o id do dispositivo, um pointer para o Stream (&Serial, ou um SoftwareSerial) e o pino de controle envio/leitura do RS485.

Depois é só enviar a mensagem pelos métodos sendENQ, sendACK e sendNAK, de acordo com o que se deseja. Para receber a mensagem, deve-se ficar aguardando a mensagem (waitForMessage(long)), e depois verificar se houve atualização na mensagem (updated()). Só será recebida a mensagem se ela foi destinada ao dispositivo.

Via de regra, o master tem id 1 e os slaves quaisquer outros ids. O master envia comando para o slave, e aguarda resposta do mesmo, confirmando que a mensagem foi recebida (ACK). Os slaves sempre ficam aguardando comando, e quando recebem respondem dizendo que entenderam o comando (ACK) ou que não entenderam (NAK). Nessa resposta pode estar a informação desejada pelo master.

MasterSlaveFuji.h (1.16 KB)

MasterSlaveFuji.cpp (5.91 KB)

Seu delay de 500ms está altíssimo. Tente reduzí-lo para 2 a 5 ms.

AlxDroidDev: Seu delay de 500ms está altíssimo. Tente reduzí-lo para 2 a 5 ms.

Na verdade, acabei desenvolvendo uma biblioteca (veja meu último post), que acabou com esse problema de sincronização. Mas valeu pela dica, de qualquer forma ;).

Muito obrigado LegendBR.