Arduino master/slave communication using RS485 and Modbus protocol

Hi to all!

First time on the arduino forum and a total beginner regarding arduino modbus communication.

I have a configuration: Master (Arduino MEGA ATmega2560), 2x MAX485 module (MAX485) and 1x Slave (Arduino NANO ATmega328). The wiring is like on the attached picture (wiring_01), but with MEGA and NANO instead UNO and i only have 1 slave for now. At first i tried communication without using Modbus protocol. It seemed that Master / Slave communication was successful most of the time, but it had some drawbacks, therefore i decided to upgrade it with Modbus. As i am new to this kind of communication i decided to try some existing examples.
I found some good examples of the code at google drive of simple-modbus.

I made minor changes in the codes for Slave and Master - to print some values (to check what am i sending / recieving).

Master code:

#include "SimpleModbusMaster.h"

/*
   The example will use packet1 to read a register from address 0 (the adc ch0 value)
   from the arduino slave (id=1). It will then use this value to adjust the brightness
   of an led on pin 9 using PWM.
   It will then use packet2 to write a register (its own adc ch0 value) to address 1 
   on the arduino slave (id=1) adjusting the brightness of an led on pin 9 using PWM.
*/

//////////////////// Port information ///////////////////
#define baud 9600
#define timeout 1000
#define polling 200 // the scan rate
#define retry_count 10

// used to toggle the receive/transmit pin on the driver
#define TxEnablePin 2 

#define LED 9

// The total amount of available memory on the master to store data
#define TOTAL_NO_OF_REGISTERS 1

// This is the easiest way to create new packets
// Add as many as you want. TOTAL_NO_OF_PACKETS
// is automatically updated.
enum
{
  PACKET1,
  PACKET2,
  TOTAL_NO_OF_PACKETS // leave this last entry
};

// Create an array of Packets to be configured
Packet packets[TOTAL_NO_OF_PACKETS];

// Masters register array
unsigned int regs[TOTAL_NO_OF_REGISTERS];

void setup()
{
  // Initialize each packet
  modbus_construct(&packets[PACKET1], 1, READ_HOLDING_REGISTERS, 0, 1, 0);
  modbus_construct(&packets[PACKET2], 1, PRESET_MULTIPLE_REGISTERS, 1, 1, 0);
  
  // Initialize the Modbus Finite State Machine
  modbus_configure(&Serial, baud, SERIAL_8N2, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs);
  
  pinMode(LED, OUTPUT);
  Serial.begin(9600);
}

void loop()
{
  modbus_update();
  
  regs[0] = analogRead(0); // update data to be written to arduino slave
  
  analogWrite(LED, regs[0]>>2); // constrain adc value from the arduino slave to 255
  Serial.println(regs[0]);
}

Slave code:

#include "SimpleModbusSlave.h"

/* 
   SimpleModbusSlaveV10 supports function 3, 6 & 16.
   
   This example code will receive the adc ch0 value from the arduino master. 
   It will then use this value to adjust the brightness of the led on pin 9.
   The value received from the master will be stored in address 1 in its own
   address space namely holdingRegs[].
   
   In addition to this the slaves own adc ch0 value will be stored in 
   address 0 in its own address space holdingRegs[] for the master to
   be read. The master will use this value to alter the brightness of its
   own led connected to pin 9.
   
   The modbus_update() method updates the holdingRegs register array and checks
   communication.

   Note:  
   The Arduino serial ring buffer is 64 bytes or 32 registers.
   Most of the time you will connect the arduino to a master via serial
   using a MAX485 or similar.
 
   In a function 3 request the master will attempt to read from your
   slave and since 5 bytes is already used for ID, FUNCTION, NO OF BYTES
   and two BYTES CRC the master can only request 58 bytes or 29 registers.
 
   In a function 16 request the master will attempt to write to your 
   slave and since a 9 bytes is already used for ID, FUNCTION, ADDRESS, 
   NO OF REGISTERS, NO OF BYTES and two BYTES CRC the master can only write
   54 bytes or 27 registers.
 
   Using a USB to Serial converter the maximum bytes you can send is 
   limited to its internal buffer which differs between manufactures. 
*/

#define  LED 9  

// Using the enum instruction allows for an easy method for adding and 
// removing registers. Doing it this way saves you #defining the size 
// of your slaves register array each time you want to add more registers
// and at a glimpse informs you of your slaves register layout.

//////////////// registers of your slave ///////////////////
enum 
{     
  // just add or remove registers and your good to go...
  // The first register starts at address 0
  ADC_VAL,     
  PWM_VAL,        
  HOLDING_REGS_SIZE // leave this one
  // total number of registers for function 3 and 16 share the same register array
  // i.e. the same address space
};

unsigned int holdingRegs[HOLDING_REGS_SIZE]; // function 3 and 16 register array
////////////////////////////////////////////////////////////

void setup()
{
  /* parameters(HardwareSerial* SerialPort,
                long baudrate, 
		unsigned char byteFormat,
                unsigned char ID, 
                unsigned char transmit enable pin, 
                unsigned int holding registers size,
                unsigned int* holding register array)
  */
  
  /* Valid modbus byte formats are:
     SERIAL_8N2: 1 start bit, 8 data bits, 2 stop bits
     SERIAL_8E1: 1 start bit, 8 data bits, 1 Even parity bit, 1 stop bit
     SERIAL_8O1: 1 start bit, 8 data bits, 1 Odd parity bit, 1 stop bit
     
     You can obviously use SERIAL_8N1 but this does not adhere to the
     Modbus specifications. That said, I have tested the SERIAL_8N1 option 
     on various commercial masters and slaves that were suppose to adhere
     to this specification and was always able to communicate... Go figure.
     
     These byte formats are already defined in the Arduino global name space. 
  */
	
  modbus_configure(&Serial, 9600, SERIAL_8N2, 1, 2, HOLDING_REGS_SIZE, holdingRegs);

  // modbus_update_comms(baud, byteFormat, id) is not needed but allows for easy update of the
  // port variables and slave id dynamically in any function.
  modbus_update_comms(9600, SERIAL_8N2, 1);
  
  pinMode(LED, OUTPUT);
  Serial.begin(9600);
}

void loop()
{
  // modbus_update() is the only method used in loop(). It returns the total error
  // count since the slave started. You don't have to use it but it's useful
  // for fault finding by the modbus master.
  
  modbus_update();
  
  holdingRegs[ADC_VAL] = analogRead(A0); // update data to be read by the master to adjust the PWM
  
  analogWrite(LED, holdingRegs[PWM_VAL]>>2); // constrain adc value from the arduino master to 255
//  Serial.println("Vrednost na kanalu A0 = ");
//  Serial.print(holdingRegs[ADC_VAL]);
  Serial.println(PWM_VAL);
  Serial.println(holdingRegs[ADC_VAL]);
  
  /* Note:
     The use of the enum instruction is not needed. You could set a maximum allowable
     size for holdinRegs[] by defining HOLDING_REGS_SIZE using a constant and then access 
     holdingRegs[] by "Index" addressing. 
     I.e.
     holdingRegs[0] = analogRead(A0);
     analogWrite(LED, holdingRegs[1]/4);
  */
  
}

If i understand correctlly the configuration should do this: Master's LED should glow corresponding to the value on A0 of the Slave. And Vice Versa - Slave's LED should glow corresponding to the value on port 0 of the Master.
The result i got is that only Master's LED is glowing.
I have tried to replace that analogRead(0) in Master's code with analogRead(A0), but i only get to the point that i can to adjust Master's LED with Master's A0.
I have checked the wiring a few times - everything seems to be OK.

I don't know what am i missing. Maybe someone could give me some directions. For a start it would be perfect if i would be able to read the value A0 from Slave and for example print this particular value on Master's serial print.

Thank you in advance.

I have a configuration: Master (Arduino MEGA ATmega2560), 2x MAX485 module (MAX485) and 1x Slave (Arduino NANO ATmega328).

Remember: you cannot use that RS-485 module if you have more than one slave because it has the bus termination on-board.

I found some good examples of the code at google drive of simple-modbus.

Unfortunately, the SimpleModbus{Master,Slave} libraries aren't as simple as the name might imply at least if you're not already familiar with Modbus usage on PLCs.

If you're used to communication in the Arduino style, I recommend to use other libraries. I use this library, it implements both master and slave side. The ModbusMaster library available in the IDE library manager is a good choice for the master side too.

I made minor changes in the codes for Slave and Master - to print some values (to check what am i sending / recieving).

You must not do that if you use the Serial interface. On the Mega2560 you have the choice to switch to one of the other three interfaces (p.e. Serial1) but on the Nano I recommend to do the debugging with other tools (p.e. LED or a display). You cannot use the same serial interface for debugging and Modbus communication.

If i understand correctlly the configuration should do this: Master's LED should glow corresponding to the value on A0 of the Slave. And Vice Versa - Slave's LED should glow corresponding to the value on port 0 of the Master.
The result i got is that only Master's LED is glowing.

No, the master is using it's own read to let the LED glow:

  regs[0] = analogRead(0); // update data to be written to arduino slave
 
  analogWrite(LED, regs[0]>>2); // constrain adc value from the arduino slave to 255

If you change regs[0] to regs[1] in the first line, your results may be better, given you fix the issue with the serial interfaces.

Hi!

Thanks for your answer!

I will try the solutions you suggested. I don't know why I didn't notice that Serial thing. :frowning:

Remember: you cannot use that RS-485 module if you have more than one slave because it has the bus termination on-board.

What if I use more RS-485 modules - like in the picture in my original post?

Thank you.

What if I use more RS-485 modules - like in the picture in my original post?

That pictures shows a wrong wiring anyway (star wiring isn't supported by RS-485). You have to connect the boards serially.
You can use your modules only for the first and the last node on this cable, so the master and one of the slaves. For all other slaves you either have to desolder the termination and idling resistors (3 pieces) or buy a module without these resistors.
Theoretically you might even get problems with two of these boards as the idling resistors (A to Vcc and B to GND) are on each board and they should exist only once per bus.

1 Like