New RS485 project sample

Hey Guys,

I am starting a project using RS485 and 3 Mega2560's and never worked with this protocol before. The basic overview is that I have a master PLC box with a Mega2560 and 2 remote switch boxes - each located about 40 feet away from the master that will also have Mega2560's onboard.

The 2 remote switch boxes each have 16 simple toggle switches and I am wanting to send these switch states to the master. Very simple actually, (example) if the operator toggles a switch in remote box #1, the master 2560 will set a specific pin (hardwired to a PLC input) to HIGH. And if he toggles the pin back off, it will then set that output pin in the master2560 back to LOW.

Thats it! I dont believe the master would have to send any instructions back to the slave as I just want the master to act on the slaves switch states.
I have 12vdc at each switch box to power the Mega's and the toggle switches (which I can easily step down to 5vdc)

The whole reason for this project is to try to eliminate the need for large 20 conductor cables and expensive amphenol connectors. I'm hoping to replace these cables with cat6 or similar small footprint twisted pair cable.

I would greatly appreciate if someone had a simple sketch example or reference for the master and slave for me to get this started. If I could get a start for just a single toggle switch communication from the slave to the master for one toggle switch, I can run from there.

Much thanks in advance...

Assuming you have the switches on 2 8-bit ports of the slave Megas, you could do simple PORT reads and then send 2 bytes to the master.

// getting past the setup() stuff:
void loop(){
inputPortC = PINC; // D37 to D30  - check the bit orders, MSB to LSB order may be swapped from
inputPortL = PINL; // D49 to D42   - the software names and the actual hardware of the chip.
if (inputPortC != oldPortC){
Serial1.write (inputPortC); // send the value if it changed
oldPortC = inputPortC; // save the new value as the old value
}
if (inputPortL != oldPortL){
Serial1.write (inputPortL);
oldPortL = inputPortL;
}

Maybe add another byte to each send so you know what port is being sent.
Or change the logic a little, capture both and if either one is different, send both all the time.
Then use 2 different ports on the receiving Mega so there is no interfence if both remotes should send at the same time.

Master receives the data and does whatever, like a port write:

if (Serial2.available() >1){ // say you are sending both bytes together
incomingByte = Serial2.read();
PORTC = incomingByte; // D30 to D37
incomingByte = Serial2.read();
PORTL = incomingByte; // D42 to D49
}
if (Serial3.available() >1){ // say you are sending both bytes together
incomingByte = Serial3.read();
PORTF = incomingByte; // A0 to A7
incomingByte = Serial3.read();
PORTK = incomingByte; // A8 to A15
}

Crossroads,

With your help I have the 485 working great and have since modified my approach to this. The data transmission speed is perfect for sending a single port (byte) to the master, but I cannot seem to figure out the proper syntax to send slave ports "A", "C", and "L" at one shot and break it back down to 3 single bytes for the master.

I've searched the forums and couldnt find anything related other than converting to strings.

Do you have any more hints you could share?

Thanks again for the kick start!

Here is my slave sketch to send:

#include <SoftwareSerial.h>
/*-----( Declare Constants and Pin Numbers )-----*/
#define SSerialRX        10  //Serial Receive pin
#define SSerialTX        11  //Serial Transmit pin
#define SSerialTxControl 3   //RS485 Direction control
#define RS485Transmit    HIGH
#define RS485Receive     LOW


SoftwareSerial RS485Serial(SSerialRX, SSerialTX); // RX, TX
/*-----( Declare Variables )-----*/
int byteReceived;
int byteSend;

byte inputPortA;
byte oldPortA;
byte inputPortC;
byte oldPortC;
byte inputPortL;
byte oldPortL;

void setup()  //==========================================================
{
   
pinMode(SSerialTxControl, OUTPUT);   
digitalWrite(SSerialTxControl, RS485Receive);  
RS485Serial.begin(9600);  
  
  pinMode(22, INPUT_PULLUP);
  pinMode(23, INPUT_PULLUP);
  pinMode(24, INPUT_PULLUP);
  pinMode(25, INPUT_PULLUP);
  pinMode(26, INPUT_PULLUP);
  pinMode(27, INPUT_PULLUP);
  pinMode(28, INPUT_PULLUP);
  pinMode(29, INPUT_PULLUP);
  //===PORTC  
  pinMode(30, INPUT_PULLUP);
  pinMode(31, INPUT_PULLUP);
  pinMode(32, INPUT_PULLUP);
  pinMode(33, INPUT_PULLUP);
  pinMode(34, INPUT_PULLUP);
  pinMode(35, INPUT_PULLUP);
  pinMode(36, INPUT_PULLUP);
  pinMode(37, INPUT_PULLUP);
  //===PORTL  
  pinMode(42, INPUT_PULLUP);
  pinMode(43, INPUT_PULLUP);
  pinMode(44, INPUT_PULLUP);
  pinMode(45, INPUT_PULLUP);
  pinMode(46, INPUT_PULLUP);
  pinMode(47, INPUT_PULLUP);
  pinMode(48, INPUT_PULLUP);
  pinMode(49, INPUT_PULLUP);
  
 
}

//---------------------------------------------------------------------------------------------------------------------------
void loop() 
{
inputPortA = PINA; // Pins 22 - 29
inputPortC = PINC; // 30 - 37
inputPortL = PINL; // 42 - 49

        if (inputPortA != oldPortA){
        byteSend = inputPortA;
        sendState();
        oldPortA = inputPortA; // save the new value as the old value
        }

        if (inputPortC != oldPortC){
        byteSend = inputPortC;
        sendState();
        oldPortC = inputPortC; // save the new value as the old value
        }
                 
        if (inputPortL != oldPortL){
        byteSend = inputPortL;
        sendState();
        oldPortL = inputPortL; // save the new value as the old value
        }
}

void sendState()
{ 
            digitalWrite(SSerialTxControl, RS485Transmit);  // Enable RS485 Transmit    
              RS485Serial.write(byteSend); // Send the byte back
              delay(10);   
              digitalWrite(SSerialTxControl, RS485Receive);  // Disable RS485 Transmit 
}

Do you want to send all 3 bytes even if only 1 has changed?
Add a flag to indicate one changed, and if any compare has set the flag then send all 3 bytes.

        if (inputPortA != oldPortA){
        byteA = inputPortA;
        dataToSend = 1;
        oldPortA = inputPortA; // save the new value as the old value
        }

Do that for all 3.

After the 3rd one:
if (dataToSend == 1){
sendState();
}
[/code]
Then send all 3

void sendState()[/color]
dataToSend = 0; // clear the flag
            digitalWrite(SSerialTxControl, RS485Transmit);  // Enable RS485 Transmit    
              RS485Serial.write(byteA); // Send the byte back
RS485Serial.write(byteA); // Send the byte back
RS485Serial.write(byteA); // Send the byte back
              delay(10); 
              digitalWrite(SSerialTxControl, RS485Receive);  // Disable RS485 Transmit

Why are you using software serial for the slave?

Crossroads,

Thanks for the quick reply. Quite angry at myself that with these strong storms kicking up around here, I left work in a rush without my boards to work on tonight.

For your first question, I would much rather send only one byte if only one has changed. I just didn't know how to decipher on the receiving end, which port the incoming byte was meant for (PORTA, PORTC, or PORTL).

I see what you mean about the flag's now for sending but just want to clarify something, when you said "do that for all 3", do I need to use a different variable (byteA, byteC, byteL) for each byte to send? I noticed on your 2nd code block, you wrote:

RS485Serial.write(byteA); // Send the byte back
RS485Serial.write(byteA); // Send the byte back
RS485Serial.write(byteA); // Send the byte back

I'm guessing it should be the 3 different variables?

So on the receiving end, if the sent bytes could be for all or any one of the 3 ports, how would I go about telling the master which one of the 3 ports was the incoming byte intended for?

As for using Software Serial, this is my first shot at serial communications and I have done a lot of research and found a few options and this is one that I was able to get to work and figured its what I needed to use. Now that I'm getting a little better grip on this and have some basic understandings, do you think I should just use the TX-RX pins instead? It DOES seem like there might be a lot of un-wanted overhead whenever a library is used. I'll try the standard TX-RX pins in the morning, I also used the Software Serial library for the receiving master sketch as well.

I appreciate your help in figuring this out...Thx

Here is the simple Master Sketch, I'll get rid of SSerial tomorrow on this one also:

#include <SoftwareSerial.h>
/*-----( Declare Constants and Pin Numbers )-----*/
#define SSerialRX2        10  //Serial Receive pin
#define SSerialTX2        11  //Serial Transmit pin
#define SSerialTxControl 3   //RS485 Direction control
#define RS485Transmit    HIGH
#define RS485Receive     LOW
#define Pin13LED         13
SoftwareSerial RS485Serial(SSerialRX2, SSerialTX2); // RX, TX
/*-----( Declare Variables )-----*/
int byteReceived;
int byteSend;

byte incomingByte;



void setup()   /****** SETUP******/
{
  pinMode(Pin13LED, OUTPUT);   
  pinMode(SSerialTxControl, OUTPUT);    
  digitalWrite(SSerialTxControl, RS485Receive);  // Init Transceiver   
  RS485Serial.begin(9600);   // set the data rate 
 //===============================SETUP@=======================================================
//===PORTA  
  pinMode(22, OUTPUT);
  pinMode(23, OUTPUT);
  pinMode(24, OUTPUT);
  pinMode(25, OUTPUT);
  pinMode(26, OUTPUT);
  pinMode(27, OUTPUT);
  pinMode(28, OUTPUT);
  pinMode(29, OUTPUT);
//===PORTC  
  pinMode(30, OUTPUT);
  pinMode(31, OUTPUT);
  pinMode(32, OUTPUT);
  pinMode(33, OUTPUT);
  pinMode(34, OUTPUT);
  pinMode(35, OUTPUT);
  pinMode(36, OUTPUT);
  pinMode(37, OUTPUT);
//===PORTL  
  pinMode(42, OUTPUT);
  pinMode(43, OUTPUT);
  pinMode(44, OUTPUT);
  pinMode(45, OUTPUT);
  pinMode(46, OUTPUT);
  pinMode(47, OUTPUT);
  pinMode(48, OUTPUT);
  pinMode(49, OUTPUT);
}//--(end setup )---

//-----------------------------------------------------------------------------------------------------------------------------
void loop() 
{
  

  if (RS485Serial.available())  //Look for data from other Arduino
   {
          incomingByte = RS485Serial.read();

          
          PORTA = incomingByte; // D22 - D29
  /*--------------------Need to figure out how to delegate these ports from incoming bytes
          PORTC = incomingByte; // D30 to D37
          PORTL = incomingByte; // D42 to D49
  */
   }  

}//--(end main loop )---

Yes, thi should have been
RS485Serial.write(byteA); // Send the byte back
RS485Serial.write(byteB); // Send the byte back
RS485Serial.write(byteC); // Send the byte back

When I posted, the some format stuff got copied in and threw me off.

If you want just one at a time, send more data to identify it.
"S1", "A", byteA for slave1, byte A, and it's data.

Mega has 4 hardware serial ports, I'd use 'em.
Master can receive on Serial1, Serial2, Serial3, if used for Slave1, Slave2, Slave3, then the slaves only need to send "A" and the value, the master knows who sent it by the port it came in on.
Or just have the slaves send 3 bytes every time no matter what, let the master compare to prior values and determine if any changed.

CrossRoads,

I did as you suggested and completely removed Software Serial and everything is sending and receiving perfect-and fast.

I have just one step left to figure out and (sadly) still a bit confused. I would be perfectly satisfied with always sending all 3 bytes whenever a change occurs and I have success with the code example posted below, the part I cant figure out is this:
Any time a switch state changes, the master receives the 3 bytes with carriage return in between. I need to somehow tell the master that the first byte goes to PORTA, the 2nd byte to PORTC, and the 3rd byte to PORTL.
I've noticed looking at the output (in the serial monitor) that all 3 bytes are always received in order and never mixed up...every single time, so thats a good thing.

I'm assuming that maybe when the data is coming in ,I could put the 3 bytes in a buffer like I used to send them and possibly tell the master that the first incoming message =PORTA, 2nd message to PORTC, and 3rd message to PORTL... Then clear the buffer and wait for another transmission.

Hopefully I am correct in thinking here, I would really appreciate any possible hints or help to get this translated to code... I've just about worn out my keyboard experimenting but no luck.

Thanks a lot,

Slave Loop with Que Flags added:

void loop() 
{
  
inputPortA = PINA; // Pins 22 - 29
inputPortC = PINC; // 30 - 37
inputPortL = PINL; // 42 - 49

  if (inputPortA != oldPortA){
       byteSendA = inputPortA;
       msg[0] = byteSendA;
       msgQue = 1;
       oldPortA = inputPortA; // save the new value as the old value
      }

        if (inputPortC != oldPortC){
        byteSendC = inputPortC;
        msg[1] = byteSendC;
        msgQue = 1;
        oldPortC = inputPortC; // save the new value as the old value
        }
                 
        if (inputPortL != oldPortL){
        byteSendL = inputPortL;
        msg[2] = byteSendL;
        msgQue = 1;
        oldPortL = inputPortL; // save the new value as the old value
        }

 
 //-----------------send new que if there is a change
 if (msgQue == 1){
   sendState(); 
 }
}

void sendState()
{ 

 //char byteSendAll = byteSendC, byteSendA, byteSendL;
              digitalWrite(SerialTxControl, RS485Transmit);  // Enable RS485 Transmit      
              Serial.write(msg, 3); // Send the byte back
              msgQue = 0; // Clear the Que tag
              delay(10);   
              digitalWrite(SerialTxControl, RS485Receive);  // Disable RS485 Transmit 
}

Master Receiving End:

void loop() 
{
  

  if (Serial.available()>0)  //Look for data from other Arduino
   {
          incomingByte = Serial.read();
          PORTA = incomingByte; // D22 - D29  - 1st byte of the incomming data
          PORTC = incomingByte; // D30 to D37 - 2nd byte of the incomming data
          PORTL = incomingByte; // D42 to D49 - 3rd byte of the incomming data
          
   }  

}//--(end main loop )---

Three ports should be changing when these 3 lines are executed:

  if (Serial.available()>2)  //Look for 3 bytes of data from other Arduino - with no carriage return
   {
          incomingByte = Serial.read(); // read a byte
          PORTA = incomingByte; // D22 - D29  - 1st byte of the incomming data
          incomingByte = Serial.read();  read a byte
          PORTC = incomingByte; // D30 to D37 - 2nd byte of the incomming data
          incomingByte = Serial.read();  read a byte
          PORTL = incomingByte; // D42 to D49 - 3rd byte of the incomming data
          
   }

UGH!,

That was it, Thank you very much.

So for my own sanity and knowledge here, the incoming bytes are held in the buffer automatically until they are read? And by stating if(Serial.available()>2) we make sure that the 3 bytes are in the buffer then we can read and execute one at a time in the order they were brought into the Serial buffer? I assume that reading the bytes will clear the buffer out or is there a good practice to manually clear it out?

Now that I have the communication working here, In this situation with your experience, would you take any safety measures like echoing the state of the ports back to the slave and test for error checking purposes?

Thanks again for your help and patience... This is working perfectly now!