RS485 Communication not working using Nick Gammon's RS485 library (Hardware)

Hello all,

(Edit) please move this post to “Networking, Protocols, and Devices”

First I want to say hi to the Arduino community. I have been playing around with Basic Stamp II of and on for a few years now. With some of the limitations I have been forced to move over to Ardunio, I have not looked back since.
As a hobby i am building a underwater ROV. I plan to use 2 Arduinos Megas, one on the surface sending commands below for the 6 thrusters the ROV has. In return the MEga below surface will be sending sensor readings all using Nick Gammons RS485 Library.

Now to the question: I have been able to get his example code working using software, however, using Ardunio Megas I would like to use hardware.

Here is my Master Code

#include <RS485_protocol.h>
//#include <SoftwareSerial.h>

const byte ENABLE_PIN = 24;
const byte LED_PIN = 13;
int level = 0;
int pot = A0;
byte loB, hiB;
//SoftwareSerial rs485 (10, 11);  // receive pin, transmit pin

// callback routines
  
  void fWrite (const byte what)
    {
    Serial1.write (what);  
    }
    
  int fAvailable ()
    {
    return Serial1.available ();  
    }
  
  int fRead ()
    {
    return Serial1.read ();  
    } 


void setup()
{
  Serial.begin(9600);
  Serial1.begin(9600);
  //rs485.begin (57600);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  pinMode (LED_PIN, OUTPUT);  // built-in LED
}  // end of setup
  
byte old_level = 0;

void loop()
{

  // read potentiometer
  level = analogRead (pot);
  Serial.println(level);

  // no change? forget it
  if (level == old_level)
    return;
    
  hiB = highByte(level); // splits the highbyte off the int variable "level"
  loB = lowByte(level); // splits the loghbyte off the int variable "level"
      
  // assemble message
  byte msg [] = { 
     1,    // device 1
     2,    // turn light on
     hiB, // first half of level
     loB,  // second have of level
     
  };

  // send to slave  
 digitalWrite (ENABLE_PIN, HIGH);  // enable sending
 sendMsg (fWrite, msg, sizeof msg);
 delayMicroseconds (660);
 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    
  
  // only send once per successful change
  if (received)
    old_level = level;

}  // end of loop

Here is my Slave Code.

#include <RS485_protocol.h>
//#include <SoftwareSerial.h>


//SoftwareSerial rs485 (10, 11);  // receive pin, transmit pin
int level;
byte loB, hiB;
const byte ENABLE_PIN = 24;

  void fWrite (const byte what)
    {
    Serial1.write (what);  
    }
    
  int fAvailable ()
    {
    return Serial1.available ();  
    }
  
  int fRead ()
    {
    return Serial1.read ();  
    } 
  
void setup()
{
  Serial.begin(9600);
  Serial1.begin(9600);
  //rs485.begin (57600);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  digitalWrite (ENABLE_PIN, LOW);  // Sets pint to incoming

}

void loop()
{
  byte buf [20];
  
  byte received = recvMsg (fAvailable, fRead, buf, sizeof (buf) - 1);
  
  if (received)
    {
    if (buf [0] != 1)
      return;  // not my device
      
    if (buf [1] != 2)
      return;  // unknown command
    
    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);
    digitalWrite (ENABLE_PIN, LOW);  // disable sending
    

    // Serial.println (buf [2]);  // this prints is hib from master
    // Serial.println (buf [3]);  // this prints is loB from master
    int level = word(buf [2], buf[3]);   // convert the bytes from master hiB loB back into a word not buf with brackets is the variable 
    Serial.println(level,DEC); 

    
   }  // end if something received
   
}  // end of loop

I have confirmed my wiring is correct using the Serial1 pins on the Mega because I have been able to SerialWrite(); from the master and SerialRead(); from the slave not using any library.
Could anyone point me in the right direction?

Andrew

What RS485 hardware are you using, how is it connected to the mega?

Mark

holmes4:
What RS485 hardware are you using, how is it connected to the mega?

Mark

I am using 2x MAX RS-485 chips please see attached and thank you for your help so far.

Andrew

Do you have ground connected between the master & slave also?

MAX1487-MAX491.pdf (1.3 MB)

CrossRoads:
Do you have ground connected between the master & slave also?

My mistake please see the updated image, I did not have them grounded attached is how I had it. I am able to get it to work if I use Nick’s software code.

Thank you
Andrew

CrossRoads:
Do you have ground connected between the master & slave also?

Yes I do have a ground with the A and B lines.

Why the 680 ohms to +5 & Gnd? That is not the termination shown in the datasheet.

This Maxim Tutorial also does not show termination resistors like that.
http://www.maximintegrated.com/app-notes/index.mvp/id/763
Try removing the 680’s and put 120 between the lines at each end.

CrossRoads:
Why the 680 ohms to +5 & Gnd? That is not the termination shown in the datasheet.

The idea was from a fourm i was reading “should have a pullup and a pulldown (typically 680 Ohms) somewhere on the differential lines so the state of the lines with nobody transmitting is always idle” however I am still able to get it to work via software with or without the 680’s. I have also tried it with 120’s on each end.

Nick’s site on where i am pulling most of (if not all) of my info is at:
http://www.gammon.com.au/forum/?id=11428

Thank you very much for your help thus far.
Andrew

If you have the 680s there as a "Failsafe" then 1K would probably be sufficient as shown here.
With just 2 devices, they are likely not needed at all.

(message OBE by your last post, I wanted to get this app note posted anyway).

Think we need Nick to weigh in on why RS485 protocol works with software serial and not hardware Serial1.

From Nick's page, looks like you still need this at both ends.

//rs485.begin (57600);

Try uncommenting these & see what happens.

CrossRoads:
From Nick's page, looks like you still need this at both ends.

//rs485.begin (57600);

Try uncommenting these & see what happens.

Thats what i thought, but when i uncomment it, i get this error.
'RS485' was not declared in this scope.

Andrew

Nick, would you happen to have any suggestions I could try...

Thanks in advance
Andrew

CrossRoads:
Why the 680 ohms to +5 & Gnd? That is not the termination shown in the datasheet.

This Maxim Tutorial also does not show termination resistors like that.
RS-485 Cable Specification Guide | Maxim Integrated
Try removing the 680's and put 120 between the lines at each end.

The 680 ? are not terminators, they are pull-up / pull-down at the master end to make sure the lines are in a determinate state at power-on.

The article mentions the 120 ? resistor, although I believe it isn't necessary if both ends are terminated, only if an end is not terminated.

CrossRoads:
From Nick's page, looks like you still need this at both ends.

//rs485.begin (57600);

Try uncommenting these & see what happens.

That's only the name of the SoftwareSerial instance (confusingly maybe). That is OK as it is.

I can't see anything wrong with your code. Can you post the debugging displays please?

byte old_level = 0;

Shouldn't that be an int? level is.

Shouldn't that be an int? level is.

Yes you are correct, thank you for that. Could you explain to me where the debugging display is? As I mentioned I am fairly new to ARduino.

Thank you.
Andrew

Here, for example:

  // read potentiometer
  level = analogRead (pot);
  Serial.println(level);

That println should be coming out of the serial monitor (via the USB cable). Does it? In your shoes I would be adding a few more here and there, like "sending", "sent" etc.

For sure I will, I will get on that and report the new code and the debug cut and paste.

I want to thank you both for helping me along.
Andrew

Okay so I ran both the Slave and Master codes with a pile more Serial.println 's. The master seems to work loop fine… however the slave kept repeating “received = recvMsg (fAvailable, fRead, buf, sizeof (buf) - 1);
byte buf [20]” over and over in the COM window. Below is the debug code I used for the slave.

include <RS485_protocol.h>
//#include <SoftwareSerial.h>


//SoftwareSerial rs485 (10, 11);  // receive pin, transmit pin
int level; // int level
byte loB, hiB; //
const byte ENABLE_PIN = 24;

  void fWrite (const byte what)
    {
    Serial3.write (what);  
    }
    
  int fAvailable ()
    {
    return Serial3.available ();  
    }
  
  int fRead ()
    {
    return Serial3.read ();  
    } 
  
void setup()
{
  Serial.begin(9600); 
  Serial3.begin(9600);
  //rs485.begin (57600);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  digitalWrite (ENABLE_PIN, LOW);  // Sets pint to incoming

}

void loop()
{
  byte buf [20];
  Serial.println("byte buf [20]");
  
  byte received = recvMsg (fAvailable, fRead, buf, sizeof (buf) - 1);
   Serial.println("received = recvMsg (fAvailable, fRead, buf, sizeof (buf) - 1);");
  if (received)
    { 
    if (buf [0] != 1)
    Serial.println("(buf [0] != 1)");
      return;  // not my device
      
    if (buf [1] != 2)
    Serial.println("(buf [1] != 2)");
      return;  // unknown command
    
    byte msg [] = {
       0,  // device 0 (master)
       3,  // turn light on command received
    };
      Serial.println("byte msg [] = {0, 3,};");
    
    delay (1);  // give the master a moment to prepare to receive
    Serial.println("delay (1);");
    digitalWrite (ENABLE_PIN, HIGH);  // enable sending
    Serial.println("(ENABLE_PIN, HIGH)");
    sendMsg (fWrite, msg, sizeof msg);
    Serial.println("sendMsg (fWrite, msg, sizeof msg);");
    digitalWrite (ENABLE_PIN, LOW);  // disable sending
    Serial.println("(ENABLE_PIN, LOW);");

    // Serial.println (buf [2]);  // this prints is hib from master
    // Serial.println (buf [3]);  // this prints is loB from master
    int level = word(buf [2], buf[3]);   // convert the bytes from master hiB loB back into a word not buf with brackets is the variable 
    Serial.println("level = word(buf [2], buf[3]");
    Serial.println(level,DEC); 

    
   } 
}  // end of loop

Please let me know of any suggestions you may have.
Thanks
Andrew