New Modbus Master-Slave library

Thanks for your comments!

As you will watch, the Modbus library just needs a memory array to exchange data through the network. The difference between a Master and an Slave is that the Master sends queries and waits for answer, while the Slave waits for queries and sends answers. Your code shall fill or read this memory array.

In my previous example, this memory array was called au16data. This means than all you need is to modify the code available here and write something like this:

void setup() {
 pinMode( 4, INPUT );
 pinMode( 5, INPUT );
 pinMode( 6, INPUT );
 pinMode( 7, INPUT );

 pinMode( 8, OUTPUT );
 pinMode( 9, OUTPUT );
 pinMode( 10, OUTPUT );
 pinMode( 11, OUTPUT );

 slave.begin(19200);
}

void loop() {
 // inputs 4..7 shall be at au16data[0]
 bitWrite( au16data[0], 0, digitalRead(4) );
 bitWrite( au16data[0], 1, digitalRead(5) );
 bitWrite( au16data[0], 2, digitalRead(6) );
 bitWrite( au16data[0], 3, digitalRead(7) );

 // outputs 8..11 shall be at au16data[1]
 digitalWrite( 8, bitRead( au16data[1], 0 ));
 digitalWrite( 9, bitRead( au16data[1], 1 ));
 digitalWrite( 10, bitRead( au16data[1], 2 ));
 digitalWrite( 11, bitRead( au16data[1], 3 ));

 // analog inputs 0 and 1 at au16data[2] and au16data[3]
 au16data[2] = analogRead(0);
 au16data[3] = analogRead(1);

 slave.poll( au16data, 17 );
}

I hope that this helps to answer your question.

Greetings!

/S

Hello suby , sorry for reply so late

There are some problems

  1. I pull input high with 5V,in my scada .It's became 1 but when I don't input any Voltage it became 0 delay almost 5 sec.
    2.When I input 5V to my A0 pin int my SCADA 40002&40003 became 1024 in the same time. It's should be only 40002 became 1024.

I must thank you again for your generous help.

Hello suby , sorry for reply so late

There are some problems

  1. I input pin4 with 5V. In my scada .It's became 1 But when I didn't input any Voltage it's became 0 delay almost 5 sec.
    2.When I input 5V to my A0 pin int my SCADA 40002&40003 became 1024 in the same time. It's should be only 40002 became 1024 and 40003 still 0 because I didn't input any voltage
    3.It's seem no Analog out put

I must thank you again for your generous help.

Hi NadiaLee,

  1. I pull input high with 5V,in my scada .It's became 1 but when I don't input any Voltage it became 0 delay almost 5 sec.

I cannot check what is your wiring and your code. Please post it, because I cannot imagine what you are explaining. It may be a setup() problem with Arduino I/O.

2.When I input 5V to my A0 pin int my SCADA 40002&40003 became 1024 in the same time. It's should be only 40002 became 1024.

Try to input 3.3V to A0 and check what it happens. It seems an overlap memory problem. Please post here all your code in order to check it.
Regards,

A new release of my library and an example:

How much RAM and flash are required for a slave device using this library? Is there any chance that this library could be used to create a modbus slave on something as meager as an attiny85?

Hi voxadam,

Maybe the memory requirements for this library may fit into something as small as a attiny85 if you cut off its internal array. I'm afraid that the problems may come from the attiny85 hardware structure, that may ask for extra resources to make it work. I think that the attiny85 has no UART and this library relies on Arduino's HardwareSerial library. This means that it should rely on Stream library, which is really the basis of HardwareSerial, and therefore NewSoftwareSerial.

My idea was to make a compact library able to run inside an Atmega8 or 168, which are small devices. Indeed there is not too much price difference between an atmega8 or an attiny85, unless you plan to make a high production application.

I hope to have answered properly your questions.

It appears the dropbox link is down for your system, could you please upload again?

Yes, I've accidentally removed it from my system. I'm sorry! :grin:
Here you can find its link again:

Hi all,

I've converted this Master-Slave library into an Arduino library and added documentation and examples for IDE 1.0.5. I'm waiting for your comments.

It's published here: https://drive.google.com/file/d/0B_1hk8ptKdNJSEJFVDRCQ0pkWUU/edit?usp=sharing

Cheers,

suby

Hello, seems very nice library, i will try to use WAGO PLC (750-880) and connect it to Arduino Mega2560 via RS485 and Modbus RTU. I already collecting 18 temperatures from DS18B20, humidity and pressure in Arduino, how can i write temperature and humidity values into Modbus query for Arduino? As It is not DI,DO or analog inputs.

Thanks!

Hello mdetector,

I understand that you are planning to use your Arduino as an slave device. Look that this library relies on an integer array, where master and slave read and write data.

In your scenario, your Arduino has several instruments plugged as I2C, 1-Wire or SPI and a serial port with Modbus. If you look at the "simple-slave" example, you will watch an integer array, which is initialised with constants. In your scenario, this same array must be written with values fetched from all these instruments.

The "advanced-slave" example shows this same procedure when using DI, DO and analog inputs. Your scenario is similar to this, but simplier. Every instrument is a position in the array.

Be careful with any "delay()" instance and remove it from your code.

I hope this explanation helps!

Best Regards,

The WAGO 750-880 is an Ethernet-Controller, while this library is focused for Serial communication (XBEE, RS-232, USB or RS-485). I'm afraid that you need a different library or you must use a different hardware from WAGO.

This one WAGO Global | Reliable Solutions for Many Sectors and Industries fits to the communication profile of my library.

Yes, 750-880 is Ethernet PLC, Ethernet will be used for remote access, also for tablets and smartphones (over Wi-Fi), but WAGO also have expansion RS232/RS485 module 750-652 and related library for Modbus RTU comunication, so i can access arduino through this module. Arduino in this case will be used only for exotic for industrial PLC i2C sensor readings (temperature, pressure, humidity)

This is my last release of this Library with some examples for Master and Slave devices:

Is there any One can Help me Here.
@suby as you given link i downloaded the library.I uploaded slave code. But The data assigned inthe

au16data[16] will not displayed on Qmodbus software. I have attached image for reference. It also giving CRC error Check on right hand side.

My setup

arduino UNO board with RS485 shield attached.

USB to RS485 converter to get data

QModbus to view modbus result.

#include <ModbusRtu.h>

// data array for modbus network sharing
uint16_t au16data[16] = {
  3, 1415, 9265, 4, 2, 7182, 28182, 8, 0, 0, 0, 0, 0, 0, 1, -1 };

/**
 *  Modbus object declaration
 *  u8id : node id = 0 for master, = 1..247 for slave
 *  u8serno : serial port (use 0 for Serial)
 *  u8txenpin : 0 for RS-232 and USB-FTDI 
 *               or any pin number > 1 for RS-485
 */
Modbus slave(1,0,0); // this is slave @1 and RS-232 or USB-FTDI

void setup() {
  slave.begin( 9600 ); // baud-rate at 19200
}

void loop() {
  slave.poll( au16data, 16 );
}

Function code in the QModBus should by 0x03

Hi suby,

I find some problem here.If the arduino device disconnect I have to restart my computer otherwise my computer can't build connect to arduino again.

BTW I use modscan32 to test those function
Is there any way to solve?

thanks :slight_smile:

Hello!

A late reply, sorry. Just reading my first book for some days, wanting to do rs485 for dmx, see http://ulrichradig.de, for me with a gear motor, and doing breadboard with atmega328 till my nano v3 arrives.

On the physical layer I ask myself, e.g. for the Arduino Nano V3, WHY should the USB Transceiver chip let the RX go so I could Input what the MAX or other RS-485 transceiver chip received and wants to forward to the ATmega?
Was told not to worry, but I do think about it.

For me, I have Analog.com ADM2582E with integrated DC/DC and isolated Rs-485, having Enable input.
Only remains the task of insuring the USB is shut out as long as RS-485-Program is running.

Looking at the schematics of e.g. arduino nano v3 I think that the Pin RX is in parallel to the output of the USB chip. Making it impossible to use projects that have a USART need and bring in their own transceiver chips out of the box for the smaller arduinos.

Since ATmega168P (or ATmega328 for some clones) has only ONE RS-485 I have to disable the USB chip, cut the connection to RX pin header.

For the original FT232 chip it may be it would perhaps go tri-state, if you reset it and hold reset active.

For my cheap nano clone with cheaper CH340 USB-chip it seems the USB chip has no disable or TriState Command input. I will cut with a cutter.

I just hover on the decision to do some nasty things: Rx-Out(USB)->R1kOhm->(Pin_Atmega and Kathode of Diode) Diode -> Pullup and Rx on Atmega.
So I let the Atmega do push-up in between 1kOhm protective Resostor and diode as some pull-up or clamp of the Output Signal of the USB chip to high.
Then I can just do a normal OR with diodes.

Better would be just to buy a chip "bus multiplexer" 1-to-2.
If someone knows a decent and cheap 5 or 6pin IC doing this... This can be soldered to a pin.

Will update after doing some impedance test on the output of the usb chip when no usb is received or usb is de-plugged.

If you can, use the Atmega or xmega in bigger casings (?>=TQFP44 or so), having 2 uarts ...

At mikrocontroller.net some one had the same question, and there is a schematic with 2 Diodes and Enable pins on both chips for the Rx output.
http://www.mikrocontroller.net/topic/87492

My schematic would be


Rs485-Chip1          ATMega-1UART-µC
En1------------------<-En1 (out)

          D1
Rx1------|<|---o----->-RX (In) (+Pull-Up (eg. 10k)
               |  
               |  
USB-Chip   D2  |  
Rx2--R-o--|<|--/
       |                       
       \-------------<-En2 (clamp Rx2) (Out)


R ca. 0,6..1kOhm. Choose to ensure High>0,6Vcc and Low<0.2Vcc is guaranteed and no one is fried...

Good luck!

Ands

Hello ,

I Am trying to use this library and examples with multiple slaves. At proteus I manage to get analog data from 1 slave . But if I put the second one on the line it sendsnothing.

The thing ı wanna do is if any slave analogread is over some value it will slave.poll and the masterwill get it and digitalwrite a led which belongs to that slave id.

I am using MAX487 at proteus. As ı said One slave is working great . Slave is A Nano , Master is a MEGA 2560

And I read the au16data[X] from a virtual terminal connected to serial1

How can I take the second or .... ID slave data and use it at master..

Can you help me please ..

The Master Code ...

/**
 *  Modbus master example 2:
 *  The purpose of this example is to query several sets of data
 *  from an external Modbus slave device. 
 *  The link media can be USB or RS232.
 *
 *  Recommended Modbus slave: 
 *  diagslave http://www.modbusdriver.com/diagslave.html
 *
 *  In a Linux box, run 
 *  "./diagslave /dev/ttyUSB0 -b 19200 -d 8 -s 1 -p none -m rtu -a 1"
 * This is:
 * serial port /dev/ttyUSB0 at 19200 baud 8N1
 * RTU mode and address @1
 */

#include <ModbusRtu.h>

uint16_t au16data[16]; //!< data array for modbus network sharing
uint8_t u8state; //!< machine state
uint8_t u8query; //!< pointer to message query
uint8_t u8txenpin;
int Slaves;
/**
 *  Modbus object declaration
 *  u8id : node id = 0 for master, = 1..247 for slave
 *  u8serno : serial port (use 0 for Serial)
 *  u8txenpin : 0 for RS-232 and USB-FTDI 
 *               or any pin number > 1 for RS-485
 */
 
Modbus master(0,0,3); // this is master and RS-232 or USB-FTDI

/**
 * This is an structe which contains a query to an slave device
 */
modbus_t telegram[2];

unsigned long u32wait;

void setup() {
  Serial1.begin(9600);
  Slaves = 1;
  
  master.begin( 9600 ); // baud-rate at 19200
  master.setTimeOut( 500 ); // if there is no answer in 500 ms, roll over
   u32wait = millis() + 100;
  u8state = u8query = 0;
}

void loop() {

//settelegrams();
Communicate();
PrintDetails();
}


void settelegrams()
{
   Slaves = Slaves + 1;
   Serial1.println(Slaves);
  if (Slaves = 247){
    Slaves = 1;
    }
    
  telegram[0].u8id = Slaves; // slave address
  telegram[0].u8fct = 3; // function code (this one is registers read)
  telegram[0].u16RegAdd = 0; // start address in slave
  telegram[0].u16CoilsNo = 6; // number of elements (coils or registers) to read
  telegram[0].au16reg = au16data; // pointer to a memory array in the Arduino
  
    // telegram 1: write a single register
  telegram[1].u8id = Slaves; // slave address
  telegram[1].u8fct = 6; // function code (this one is write a single register)
  telegram[1].u16RegAdd = 6; // start address in slave
  telegram[1].u16CoilsNo = 1; // number of elements (coils or registers) to read
  telegram[1].au16reg = au16data+6; // pointer to a memory array in the Arduino

  }
  
  void Communicate()
{
 
 
  
  switch( u8state ) {
  case 0: 
    if (millis() > u32wait) u8state++; // wait state
    break;
  case 1: 
   settelegrams();
   master.query( telegram[u8query] ); // send query (only once)
    //master.query( telegram[0] ); 
    u8state++;
 u8query++;
           
 if (u8query > 2) u8query = 0;
    break;
  case 2:
    master.poll(); // check incoming messages
    if (master.getState() == COM_IDLE) {
      u8state = 0;
           u32wait = millis() + 100; 
                  }
    break;
  }
 
}
  void PrintDetails()
  {
    
  Serial1.print("-- ID : ");
 Serial1.print(au16data[0]);
 Serial1.print(" -- ");
 Serial1.print(" A0 : ");
  Serial1.print(au16data[1] , DEC);
  Serial1.print(" -- ");
 Serial1.print(" AData : ");
  Serial1.println(au16data[2] , DEC);
  
  }

The Slave Code : ....

/**
 *  Modbus SLAVE
 *  
 */

#include <ModbusRtu.h>
#define ID   1

Modbus slave(ID, 0, 2); // this is slave ID and seri port and RS-485 DE pin
boolean led;
int8_t state = 0;
unsigned long tempus;
int IRData;
int SensData;
// data array for modbus network sharing
uint16_t au16data[6];

/**
 *  Setup procedure
 */
void setup() {
  IRData = analogRead(A0);
  SensData = analogRead(A1);
  io_setup(); // I/O settings

  // start communication
  slave.begin( 9600 );

  tempus = millis() + 100;
  digitalWrite(9, HIGH );
}

/**
 *  Loop procedure
 */
void loop() {
  IRData = analogRead(A0);
  SensData = analogRead(A1);
  // poll messages
  // blink led pin on each valid message
  
 

  if (state > 2) {
    tempus = millis() + 100;
    digitalWrite(9, HIGH);
  }
  if (millis() > tempus) digitalWrite(9, LOW );

  // link the Arduino pins to the Modbus array
  if (IRData > 400){
  io_poll();
   state = slave.poll( au16data, 6 );
  }
    if (SensData > 200){
  io_poll();
   state = slave.poll( au16data, 6 );
  }
} 

// pin 13 is reserved to show a successful query

void io_setup() {
  // define i/o
   pinMode(9, OUTPUT ); // this is for the UNO led pin
}

/**
 *  Link between the Arduino pins and the Modbus array
 */
void io_poll() {

  // read analog inputs
  au16data[0] = (ID);
  au16data[1] = analogRead(A0);
  au16data[2] = analogRead(A1);

  // diagnose communication
  au16data[3] = slave.getInCnt();
  au16data[4] = slave.getOutCnt();
  au16data[5] = slave.getErrCnt();
}

The Proteus Project , wiring Jpgs. ino files and hex files in this link.. ...

https://drive.google.com/file/d/0B1WsLrPNzICXdGNVdXJLM0R3QzA/view?usp=sharing

Thank You for starting this New Modbus Lİbrary. I hope to learn how to and do some usefull stuff with your Library ..