Arduino RS485 Protocol Implementation

Hello everyone,

I am working on a small project right now that one "One Arduino Mega" and "Two Arduino UNOs" connected to each other over RS485 bus. The Mega serves as RS485 Master and the two UNOs serve as Slaves. I am using MAX485 modules for UART to RS485 conversion. The circuitry and connections are all fine as I am able to receive proper data from the slaves individually. However, when I connect the three together and then try to fetch the data from each slave using their slave ID, the output starts showing garbage. I have been stuck in this issue for a while now. Any help would be appreciated. I am attaching my test codes for clear understanding.

Warm regards,

  1. Master_RS485
String SLAVE_ID01 = "001#";
String SLAVE_ID02 = "002#";
String getdata;
void setup() {
  
  Serial1.begin(9600);//Using Serial1 Port
  Serial.begin(9600);
  pinMode(8, OUTPUT);//DE/RE Controling pin of RS-485
}

void loop() {

  read_slave1();
  delay(50);
  read_slave2();
  delay(50);

  //Serial.print("Data02:");
  //Serial.print(getdata);
  //Serial.println("");

}

void read_slave1() {
  digitalWrite(8,HIGH); 
  Serial1.print(SLAVE_ID01);//Write '9' and Fetch Data From Pro Mini
  Serial1.flush();
  digitalWrite(8,LOW);//DE/RE=LOW Receive Enabled M1
  delay(50);
  

  if(Serial1.available()){ //If Serial Data is available
    
    while(Serial1.available())
    { 
    getdata=Serial1.readStringUntil('
  1. Slave1_RS485
const int sens1 = A0;
const int sens2 = A1;
const int sens3 = A2;
const int sens4 = A3;
int sens1_data, sens2_data, sens3_data, sens4_data;
String sensor_data;
String getdata;

void setup() {
    
  //Serial1.begin(9600);//Uncomment for Arduino Lenardo
  Serial.begin(9600);
  //while(!Serial1);//Uncomment for Arduino Lenardo

  pinMode(13, OUTPUT);//Led Connected
  pinMode(8, OUTPUT);//DE/RE Controling pin of RS-485
}

void loop() {

  send_response();
  delay(100);
}

void send_response() {
  
  digitalWrite(8,LOW);//DE/RE=LOW Receive Enabled
  
  if (Serial.available()) {
    getdata = Serial.readStringUntil('#');
    }
    
    if(getdata=="001"){
      digitalWrite(8,HIGH);//DE/RE=HIGH Transmit Enabled 
      fetch_sens();
      Serial.print(sensor_data);
      Serial.flush();
      getdata = "";
      }
}

void fetch_sens() {
  sens1_data = analogRead(sens1);
  sens2_data = analogRead(sens2);
  sens3_data = analogRead(sens3);
  sens4_data = analogRead(sens4);

  sensor_data = "<Left>,<S1:" + String(sens1_data) + ">,<S2:" + String(sens1_data) + ">,<S3:" + String(sens3_data) + ">,<S4:" + String(sens4_data) + ">$";
}
  1. Slave2_RS485
const int sens1 = A0;
const int sens2 = A1;
const int sens3 = A2;
const int sens4 = A3;
int sens1_data, sens2_data, sens3_data, sens4_data;
String sensor_data;
String getdata;

void setup() {
    
  //Serial1.begin(9600);//Uncomment for Arduino Lenardo
  Serial.begin(9600);
  //while(!Serial1);//Uncomment for Arduino Lenardo

  pinMode(13, OUTPUT);//Led Connected
  pinMode(8, OUTPUT);//DE/RE Controling pin of RS-485
}

void loop() {
  send_response();
  delay(100);
}

void send_response() {
  
  digitalWrite(8,LOW);//DE/RE=LOW Receive Enabled
  
  if (Serial.available()) {
    getdata = Serial.readStringUntil('#');
    }
    
    if(getdata=="002"){
      digitalWrite(8,HIGH);//DE/RE=HIGH Transmit Enabled 
      fetch_sens();
      Serial.print(sensor_data);
      Serial.flush();
      getdata = "";
      }
}

void fetch_sens() {
  sens1_data = analogRead(sens1);
  sens2_data = analogRead(sens2);
  sens3_data = analogRead(sens3);
  sens4_data = analogRead(sens4);

  sensor_data = "<Right>,<S1:" + String(sens1_data) + ">,<S2:" + String(sens1_data) + ">,<S3:" + String(sens3_data) + ">,<S4:" + String(sens4_data) + ">$";
}

Master_RS485.ino (1.38 KB)

Slave_RS485.ino (1.16 KB)

Slave2_RS485.ino (1.16 KB));
    Serial.print("Data01: ");
    Serial.print(getdata);
    Serial1.flush();
    }
    Serial.println("");
    }
   
    getdata = "";
}

void read_slave2() {
  digitalWrite(8,HIGH);
  Serial1.print(SLAVE_ID02);//Write '9' and Fetch Data From Pro Mini
  Serial1.flush();
  digitalWrite(8,LOW);//DE/RE=LOW Receive Enabled M1
  delay(50);

if(Serial1.available()){ //If Serial Data is available
   
    while(Serial1.available())
    {
    getdata=Serial1.readStringUntil('


2. Slave1_RS485

§DISCOURSE_HOISTED_CODE_1§


3. Slave2_RS485

§DISCOURSE_HOISTED_CODE_2§


[Master_RS485.ino|attachment](upload://r91hIXynlIHE6bUfxgivWq8Azk7.ino) (1.38 KB)

[Slave_RS485.ino|attachment](upload://wiLh6GSxwKqPkyoClzMIW0Y6eXG.ino) (1.16 KB)

[Slave2_RS485.ino|attachment](upload://pgiLsB8ATgcjlzFduhTzFZZFpNg.ino) (1.16 KB));
    Serial.print("Data02: ");
    Serial.print(getdata);
    Serial1.flush();
    }
    Serial.println("");
    }
    
    getdata = "";
}
  1. Slave1_RS485
§_DISCOURSE_HOISTED_CODE_1_§
  1. Slave2_RS485
§_DISCOURSE_HOISTED_CODE_2_§

Master_RS485.ino (1.38 KB)

Slave_RS485.ino (1.16 KB)

Slave2_RS485.ino (1.16 KB)

Please edit your post. Add the sketches inline using the code tags that are expected on this forum. It's the button that looks like </>. Thanks.

When you did the tests with individual slave units, did you test to ensure each slave you tested did NOT respond to the other slave addresses?

Paul

We will probably need the connections of the transceivers as well, switching the transceiver of the master from driver to receive has some issues, also because your terminator is no longer in the proper spot in the schematic (for short wires you can probably go without, but thos are not the specs) Normally the RS485 bus is a one way street, though RDM does use 2 way communication. The main thing is that not 2 devices are in drive mode at the same time, and the longer the wires (actually the higher their total capacitance) the more important the twisted pair is.

aarg:
Please edit your post. Add the sketches inline using the code tags that are expected on this forum. It's the button that looks like </>. Thanks.

aarg, post has been modified.

Paul_KD7HB:
When you did the tests with individual slave units, did you test to ensure each slave you tested did NOT respond to the other slave addresses?

Paul

Paul, yes I did, the slaves only respond to their respective Slave IDs.

Deva_Rishi, Currently, I am only testing using a breadboard setup so there's no prolonged wires or anything. Just standard jumper wires which are very close too.

shouldn't the code only set the DE pin HIGH while transmitting, setting it LOW when flush() completes as it does in the master?

in the receiver code, it sets it LOW only before calling available() and when if getData is a match, it's sets it HIGH to send a response, but doesn't set it LOW until just before the next call to available and in the meantime, there's a 100 msec delay. what happens if the next receive data occurs during that delay.

why are there any delays?

it would make more sense for the master to send something and repeatedly check for a response until a specific amount of time has passed by capturing a timestamp and comparing it to millis().

ketchuma12:
Deva_Rishi, Currently, I am only testing using a breadboard setup so there's no prolonged wires or anything. Just standard jumper wires which are very close too.

That's not a reason to hold back information. Please show the connections.

gcjr:
shouldn't the code only set the DE pin HIGH while transmitting, setting it LOW when flush() completes as it does in the master?

in the receiver code, it sets it LOW only before calling available() and when if getData is a match, it's sets it HIGH to send a response, but doesn't set it LOW until just before the next call to available and in the meantime, there's a 100 msec delay. what happens if the next receive data occurs during that delay.

why are there any delays?

it would make more sense for the master to send something and repeatedly check for a response until a specific amount of time has passed by capturing a timestamp and comparing it to millis().

Ok I will test after removing delays and see if this solves the problem. Are there any solutions I should try along with that? I am having a feeling this may not resolve the issue.

aarg:
That's not a reason to hold back information. Please show the connections.

I will paste circuit connections for convenience. Thanks.

digitalWrite(8,LOW);//DE/RE=LOW Receive EnabledAs someone mentioned already, this should always be 'LOW' unless sending and then straight back to LOW, with a small delay after the flush... why the delay one would ask... well

if(getdata=="002"){
      fetch_sens();  // this should be done first
      digitalWrite(8,HIGH);  //  DE / !RE = HIGH Transmit Enabled
      Serial.print(sensor_data);
      Serial.flush();              // this holds the program until the buffer is clear,
      delay(3);                    // but the UART still needs to be cleared as well or you will lose the last character.
      digitalWrite(8,LOW);   // DE / !RE = LOW Receive Enabled
      getdata = "";

the same in the master sketch

Serial1.print(SLAVE_ID02);//Write '9' and Fetch Data From Pro Mini
  Serial1.flush();
  delay(3);   // make sure that the UART has been completely sent before disabling transmission.
  digitalWrite(8,LOW);//DE/RE=LOW Receive Enabled M1
  delay(50);  // this delay is only allowing the receive buffer to fill up.

As a result i suggest you wait a few ms before you start responding as well.
As for the master receive buffer, you should do reception a little differently i think. readStringUntil() is easy, but what if your character never shows up before it times out ?

aarg:
That's not a reason to hold back information. Please show the connections.

Yes please do so, there are just to many common mistakes to be made.
on the transceivers, all GND (pin5) should be connected, and all data+ & data- should be connected on both master & slave, that means all pins 7 connected, and all pins 6 connected,
do not cross them over ! or you will invert the signal, and well, garbage will come out...
There is a few other things you could do wrong so best is just to show the schematic, either hand drawn or using CAD or even windows paint will do it really nicely.

Another suggestion while i am at it, since you are working at 9600kbps, you may as well use swSerial on the slave side for the moment, and use the USB port for debugging, or vice versa. anyway i would first make sure i have back and forth communications going properly with just 1 slave, but for that i need to be sure i receive properly at the slave side before i respond so to say.. Anyway many ways to tackle your issue.

Hello Deva, extremely sorry for the late response. I got stuck somewhere.

So here are my connections.

Connections on the Master Side

  1. DE and RE on RS-485 -> digital pin 8 on MEGA.
  2. DI on RS-485 -> Pin 18 (TX1) on the MEGA.
  3. RO on RS-485 -> Pin 19 (RX1) on the MEGA.
  4. VCC on RS-485 -> 5V on the MEGA.
  5. "A" on RS-485 -> Connected to the other two RS-485 "A"
  6. "B" on RS-485 -> Connected to the other two RS-485 "B"
  7. GND common.

Connections on Slave 1

  1. DE and RE on RS-485 -> digital pin 8 on UNO.
  2. DI on RS-485 -> Pin 1 (TX) on the UNO.
  3. RO on RS-485 -> Pin 0 (RX) on the UNO.
  4. VCC on RS-485 -> 5V on the UNO.
  5. "A" on RS-485 -> Connected to the other two RS-485 "A"
  6. "B" on RS-485 -> Connected to the other two RS-485 "B"
  7. GND common.

Connections on Slave 2

  1. DE and RE on RS-485 -> digital pin 8 on UNO.
  2. DI on RS-485 -> Pin 1 (TX) on the UNO.
  3. RO on RS-485 -> Pin 0 (RX) on the UNO.
  4. VCC on RS-485 -> 5V on the UNO.
  5. "A" on RS-485 -> Connected to the other two RS-485 "A"
  6. "B" on RS-485 -> Connected to the other two RS-485 "B"
  7. GND common.

Also, here are my observations after continuous experimentation.

I am able to fetch the data properly individually. When I connect only Slave 1 on the RS-485, I get proper response. When I disconnect Slave 1 and connect Slave 2, I get proper response. However, when both are connected, either the responses merge into each other or Slave 2 doesn't respond.

When I did some debugging, I found that what happens is when Slave ID 1 is transmitted, it is accepted and Slave 1 throws it's response. The MEGA reads the response and displays it. However, that response also goes into the Serial of Slave 2, due to that mingling, the Slave 2 doesn't response when all three (1 master and 2 slaves) are connected together. Any thoughts or ideas on how I should work around that?

Thank you for all the support up till now.

However, that response also goes into the Serial of Slave 2, due to that mingling, the Slave 2 doesn't response when all three (1 master and 2 slaves) are connected together. Any thoughts or ideas on how I should work around that?

Well clearly your problem lies in this section, and i am quite sure that if you would make a request to slave 2 first, slave 1 would be non-responsive, actually it is quite simple was is going wrong.

void send_response() {
 
  digitalWrite(8,LOW);//DE/RE=LOW Receive Enabled
 
  if (Serial.available()) {
    getdata = Serial.readStringUntil('#');
    }
   
    if(getdata=="001"){
      digitalWrite(8,HIGH);//DE/RE=HIGH Transmit Enabled
      fetch_sens();
      Serial.print(sensor_data);
      Serial.flush();
      getdata = "";
      }
}

if Serial.available() is true , it i supposed to readStringUntil('#'), but what if that '#' never comes ? or only comes when after the whole response from the other slave.. then (getdata=="001") will never be true. actually what you want is to compare "001" with just the last 3 characters so something like if(getdata.substring(getdata.length()-3)=="001"){should do the trick. I am not a fan of using the String class as a buffer to receive from the Serial port though, you should really just use a c-string (since you are only looking for 3 characters a small ring buffer would work as well) and if nothing has been received for 2ms your main program can just continue with what it was doing. The way you have it set up if there are multiple requests & responses to slave 1, slave 2 will overflow it's memory (or at least the String class, which is limited on an AVR)

Deva_Rishi, Thank you so much for a great suggestion. That substring technique worked like a charm. Really appreciate all the support you've shown. Thanks again.