Sunday, 30 April 2017

Serial Data Monitor

I've received a couple of questions in the Github regarding to debugging applications with this Library and the Serial Data Monitor.

I'm having the same problem. To my knowledge the response from the modbus slave device is processed by the function get_FC3 which moves them from the au8buffer to au16reg however I am struggling to get data from them. I used a 3rd party program to ensure I am getting the correct query from my arduino and Ive used the same program to send that query to the modbus device and received the expected response. So I know the data is there I am just having some issues displaying them.


The Arduino Serial Monitor shows ASCII data. As you know, the Modbus RTU protocol handles raw binary data. Therefore it only shows ⬜ and wrong symbols instead of the real values.

When I wrote this library, I used third-party simulators either for the Master or for the Slave. I recommend you these:


Modpoll and its slave Variant: http://www.modbusdriver.com/modpoll.html

Sunday, 21 February 2016

Data frame settings for serial communication

To know more about this subject, please refer to this site.

Arduino Serial library supports several data frames apart from the common 8N1. This is accomplished through the begin method. This is fully discussed in the previous link.

I've modified my library in order to support this. There is also a new example for an Slave, which has 19200 baud, 8 data bits, even parity plus 1 stop bit.

This is exactly the same for a Modbus Master.

/**
 *  Modbus slave example 2:
 *  The purpose of this example is to link a data array
 *  from the Arduino to an external device.
 *
 *  Recommended Modbus Master: modpoll
 *  http://www.modbusdriver.com/modpoll.html
 */

#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( 19200, SERIAL_8E1 ); // 19200 baud, 8-bits, even, 1-bit stop
}

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

Saturday, 16 January 2016

RS-485 Implementation on an Arduino

RS-485 Standard

The RS-485 allows to implement a low-cost network for several devices. The aim of this post is not to copy the information gathered by Wikipedia regarding RS-485.

The RS-485 is a half-duplex protocol, so this means that its devices cannot listen and speak at the same time. The RS-422 is a protocol based on RS-485 that allows this.

Wiring RS-485

Anyway the RS-485 allows to connect several devices with only 3-wires:
  • Data+ (non-inverting)
  • Data- (inverting)
  • GND (ground or voltage reference)
Some devices mark the Data+ and Data- as B and A. I prefer to call them Data+ and Data-, because this is how they are driven and most PLC manufacturers call them so.

Its important to use the GND wire to make use that Data+ and Data- are refered along the network.

It is important to add bias 120 ohm resistors between Data+ and Data- at both edges of the network, when designing bus type networks. In star type networks, I use to install them on the longest arms.

Before starting

  • The Arduino based on the ATMEGA328 has only an UART.
  • Its UART is used for programming purposes.
  • This UART may also be used for RS-485 communication taking some precautions.

How to implement it on an Arduino 328

RS485 transceivers


The MAX485 family is a very popular set of RS485 transceivers, but there are others like those from Texas Instruments or Linear Technology.

An RS-485 typical transceiver consists on the next pins:
  • RO is a TTL output to be connected to the microcontroller RxD pin.
  • DI is a TTL input to be connected to the microcontroller TxD pin.
  • RE and DE use to be connected together and are used for the flow control: At high level RO is at high impedance and at low level RO sends everything broadcasted from the RS-485 side.
  • A (Data+) and B (Data-) are connected to the network side.

Simplest Arduino implementation

This schematic is borrowed from here.

This is the simplest implementation for RS-485 on an Arduino. Its only drawback is that RO interferes with the USB transceiver, unless D2 is at High level to put RO at high impedance.

The UNO and duemilianove include a 10K-ohm resistor in series between D0 and the FTDI chip to allow to mount other devices to the UART. Unfortunatelly the USB port is lost, unless reprogramming the Arduino.

The Modbus port

The Modbus library implements the RS-485. One of the Modbus constructors is declared as:

Modbus(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin);

where

u8txenpin may be an Arduino pin number different of 0. If so, this pin handles the transceiver flow control pins RE/DE. This is automatically done by the Library.

The Modbus library includes an example for an slave:

/**
 *  Modbus slave example 3:
 *  The purpose of this example is to link a data array
 *  from the Arduino to an external device through RS485.
 *
 *  Recommended Modbus Master: QModbus
 *  http://qmodbus.sourceforge.net/
 */

#include <ModbusRtu.h>

// assign the Arduino pin that must be connected to RE-DE RS485 transceiver
#define TXEN 2 

// 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,TXEN); // this is slave @1 and RS-485

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

void loop() {
  slave.poll( au16data, 16 );
} 
 
The slave contains an array au16data, which is available in the RS-485 network either for read or write. Pin 2 is used for the RS-485 flow control.

Wednesday, 11 November 2015

I'm back

After some months of hard work, I've come back to the library. I'll try to check it with the new Arduino IDE and make new examples.

Monday, 16 February 2015

Sunday, 5 October 2014

A little more advanced Modbus Master

Advanced Modbus Master code

 This is a little more advanced Modbus Master:

/**
 *  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

/**
 *  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,0); // 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() {
  // telegram 0: read registers
  telegram[0].u8id = 1; // slave address
  telegram[0].u8fct = 3; // function code (this one is registers read)
  telegram[0].u16RegAdd = 0; // start address in slave
  telegram[0].u16CoilsNo = 4; // 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 = 1; // slave address
  telegram[1].u8fct = 6; // function code (this one is write a single register)
  telegram[1].u16RegAdd = 4; // start address in slave
  telegram[1].u16CoilsNo = 1; // number of elements (coils or registers) to read
  telegram[1].au16reg = au16data+4; // pointer to a memory array in the Arduino
 
  master.begin( 19200 ); // baud-rate at 19200
  master.setTimeOut( 5000 ); // if there is no answer in 5000 ms, roll over
  u32wait = millis() + 1000;
  u8state = u8query = 0; 
}

void loop() {
  switch( u8state ) {
  case 0: 
    if (millis() > u32wait) u8state++; // wait state
    break;
  case 1: 
    master.query( telegram[u8query] ); // send query (only once)
    u8state++;
 u8query++;
 if (u8query > 2) u8query = 0;
    break;
  case 2:
    master.poll(); // check incoming messages
    if (master.getState() == COM_IDLE) {
      u8state = 0;
      u32wait = millis() + 1000; 
    }
    break;
  }
  au16data[4] = analogRead( 0 );
} 
  

This code is already included in the last release of my Library, available at Github.

Now there are two queries: 0 and 1. 0 makes the same as before, while 1 adds a single-register write. This is done through the state-machine implemented in loop, which sends both queries through u8query.

Modbus RTU is not an IP-based protocol. Serial protocols require to send messages one after another and wait for their answer before sending a new message. This is done here.

Only for debug purposes, the u32wait time has been set to 1000 ms.

Understanding the code

In setup(), the master is initialised:
  • a set of queries through telegram are written;
  • the serial port is started with master.begin(19200) at 19200 baud;
  • the u8query index is reset;
  • the u8state machine state register is reset.

Modbus_t variables

telegram is an structure array based on modbus_t type. It contains the queries to the Modbus slaves. Things to define here:
  • slave id and register to access
  • what to do (read or write) and how many fields
  • where is this information linked
The .au16reg field contains a pointer to an integer array. It allows to link the Modbus protocol to the rest of the Arduino program. It could be an LCD or digital I/O or a set of devices plugged to I2C.

The loop section runs the machine state and links any process data to the integer array au16data.

The machine state

This library need that a machine state to be implemented in software in the loop section. This is for flexibility of the code.

The machine state is driven by u8state byte. It consists on 3 states:
  • 0: it is a wait phase;
  • 1: a query is sent through master.query( telegram[ u8query ] );
  • 2: the poll section waits for an answer or just gets out if there is a time-out event.
The time-out event is defined by master.setTimeOut( 5000 ) in setup(). This means that the application allows up to 5 s to wait for an answer.

Debugging the code

Debugging with Multiway

Multiway is a worthy protocol analiser written by Omron Corporation in France. It supports several industrial protocols and Modbus is between them.


Multiway has been set as

COM4
8-bit, 1-bit stop, none
Modbus slave number 1

The Master is reading registers from 0 to 3 and writes on register 4.

Link to the library upgrade

Google Drive
GitHub

Last Release published in GitHub

Today I've finally published my last release of the Modbus Library in GitHub site:

https://github.com/smarmengol/Modbus-Master-Slave-for-Arduino