RS485 Hardware Network 2 – Multi-Byte Communication

Edit: I don’t really make a point of saying it, but the common ground between the modules isn’t necessary.

Here’s a video of the software running on the boards.

So for this example,  there are 4 bytes being transmitted from the master to the slaves.

  1. Start of Transmission (0xDB)
  2. ID of the slave to talk to
  3. The type of command
  4. The data that is transmitted

A transmission to set the pwm value of the green led on the slave with the ID 1 to 100 would be:

0xDB 0x01 0x01 0x64

This is the schematic of the circuit for this example:

This is the code running on the master Arduino:

#include <SoftwareSerial.h>

int MAX485_Receiver_Output_PIN = 10;
int MAX485_Driver_Input_PIN = 11;
int MAX485_Driver_Output_Enable_PIN = 12;

#define NUMSWITCHES 3

int ID_switches[NUMSWITCHES] = {2, 3, 4};
int mode_switch = 5; 

byte IDs[NUMSWITCHES] = {1, 2, 3};

#define TXSIZE 4

SoftwareSerial software_serial (MAX485_Receiver_Output_PIN, MAX485_Driver_Input_PIN); // RX, TX

byte target_node_ID;

struct transmission {

  byte start = 0xDB;
  
  byte ID;
  byte type;
  byte message;

  const int tx_size = TXSIZE;
};

void setup()
{
  software_serial.begin(9600); // begin software serial
  
  pinMode(MAX485_Driver_Output_Enable_PIN, OUTPUT);
  digitalWrite(MAX485_Driver_Output_Enable_PIN, HIGH); // this disables Receiver Output Enable and enables Driver Output Enable
  
  for (int index = 0; index < NUMSWITCHES; index++)
  {
    pinMode(ID_switches[index], INPUT); 
  }
  
  pinMode(mode_switch, INPUT);
  
  target_node_ID = 0;
}

void loop()
{
  for (int index = 0; index < NUMSWITCHES; index++)
  {
    if ((byte)digitalRead(ID_switches[index]))
    {
      target_node_ID = IDs[index];
      break;
    }
  }
  
  transmission t;
  
  t.ID = target_node_ID;
  t.type = digitalRead(mode_switch);
  t.message = (byte)map(analogRead(0), 0, 1023, 0x00, 0xFF);
  
  byte message[TXSIZE] = {t.start, t.ID, t.type, t.message};
  
  software_serial.write(message, t.tx_size);
}

This is the code running on the slave Arduinos:

#include <SoftwareSerial.h>

int MAX485_Receiver_Output_PIN = 10;
int MAX485_Driver_Input_PIN = 11;
int MAX485_Driver_Output_Enable_PIN = 12;

int led_1_PIN = 5; 
int led_2_PIN = 6; 

unsigned long time = 0;  
unsigned long oldtime = 0; 
int state = 0;
int next_ID_button_PIN = 2;

#define NUMSTATES 3
int states[NUMSTATES] = {7, 8, 9};
byte IDs[NUMSTATES] = {1, 2, 3};

#define MESSAGELENGTH 3

SoftwareSerial software_serial (MAX485_Receiver_Output_PIN, MAX485_Driver_Input_PIN); // RX, TX

void setup()
{
  software_serial.begin(9600); // begin software serial
  
  pinMode(MAX485_Driver_Output_Enable_PIN, OUTPUT);
  digitalWrite(MAX485_Driver_Output_Enable_PIN, LOW);
  
  pinMode(led_1_PIN, OUTPUT);
  pinMode(led_2_PIN, OUTPUT);
  
  pinMode(next_ID_button_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(next_ID_button_PIN), next_ID, RISING); 
  
  set_LEDs();
}

void loop() // run over and over
{
  
  byte start_of_message = 0xDB;
  byte incoming_byte;
  byte message[MESSAGELENGTH];
  
  while (true)
  {
    if (software_serial.available())
    { 
      incoming_byte = software_serial.read();
      
      if (incoming_byte == start_of_message)
      {
        software_serial.readBytes(message, MESSAGELENGTH);
        process_message(message);
      }
    }
  }
}
 
void process_message(byte message[])
{
  byte system_ID = IDs[state];

  byte incoming_ID = message[0];
  byte incoming_mode = message[1];
  byte LED_brightness = message[2];
   
  if (incoming_ID == system_ID)
  {
    byte pin;
    
    if (incoming_mode == 0)
    {  
      pin = led_1_PIN;
    }
    else if (incoming_mode == 1)
    {
      pin = led_2_PIN; 
    }
    analogWrite(pin, LED_brightness);  
  }
}

void next_ID()
{
  time = millis();
  
  //soft debounce
  if (time - oldtime > 200) 
  {
    oldtime = time;
    
    state++;
    
    if (state >= (NUMSTATES))
    {
      state = 0;
    }
  }
  set_LEDs();
}

void set_LEDs()
{
  for (int index = 0; index < NUMSTATES; index++)
  {  
    if (index == state)
    {
      digitalWrite(states[index], HIGH);
    }
    else 
    {
      digitalWrite(states[index], LOW);
    }
  } 
}

13 Comments

  1. Hi,

    I notice that you have a common ground between all three RS485 modules. My understanding is that RS485 is a balanced (longish range) two wire link, is that correct or have I confused it with RS232. I ask because I have a project in mind which uses some commercial RS485 modules and an arduino.

    Regards Alan

    1. Hi Alan,
      You’re correct, the common ground isn’t necessary. You are also not the first person to point out this error, I’ll make a post updating the schematic soon.
      Best,
      Devon

  2. Nice project, but can i use standard tx and rx pins on arduino instead of wasting some more pins? I’m trying with no luck

    1. No you can’t really do this because of the way that this protocol is implemented. This prints messages to serial which will also go out over the tx and rx pins and mess everything up.

  3. can we control other slaves, i mean slaves which are not arduino instead some other controller units including sensors and relays etc.

  4. Hi devon,
    This is good.please update or send code for multiply relay controls as a example master send A0001 the slave a re ponce this activate pin4 high, master we type B1000 and B slave response and pin one high . i use A,B,C,D,etc…slaves and 4 relay model,Can you help me????

  5. Hi Devon,
    I have a project where Arduino Mega should control several (more than 20 Attinys 44)
    Will this communication work with Attinys 44 ? Mega is the Master and Attinys are slaves.

    Please reply & advice,
    Thanks
    Mike

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.