Sunday 14 September 2014

Modbus Master implementation

In my last post, I've published my last Arduino Modbus library, which contains a Modbus Master example:

/**
 *  Modbus master example 1:
 *  The purpose of this example is to query an array 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>

// data array for modbus network sharing
uint16_t au16data[16];
uint8_t u8state;

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

unsigned long u32wait;

void setup() {
  master.begin( 19200 ); // baud-rate at 19200
  master.setTimeOut( 2000 ); // if there is no answer in 2000 ms, roll over
  u32wait = millis() + 1000;
  u8state = 0; 
}

void loop() {
  switch( u8state ) {
  case 0: 
    if (millis() > u32wait) u8state++; // wait state
    break;
  case 1: 
    telegram.u8id = 1; // slave address
    telegram.u8fct = 3; // function code (this one is registers read)
    telegram.u16RegAdd = 1; // start address in slave
    telegram.u16CoilsNo = 4; // number of elements (coils or registers) to read
    telegram.au16reg = au16data; // pointer to a memory array in the Arduino

    master.query( telegram ); // send query (only once)
    u8state++;
    break;
  case 2:
    master.poll(); // check incoming messages
    if (master.getState() == COM_IDLE) {
      u8state = 0;
      u32wait = millis() + 100; 
    }
    break;
  }
} 

The master requires:
  • A memory array, which is linked to the communication network, and this role is assigned to au16data array.
  • A master object, which sends queries and receives answers through Serial port. This belongs to Modbus class and is assigned to address 0.
  • An object or a set of objects to query, which are modbus_t type;
  • A machine state to make queries and wait for answers in the loop section.

The modbus object

This is the master definition. As in a Modbus slave, there are some parameters to define it:
  • node id u8id is always 0;
  • Serial port u8SerNo must be 0 in an Arduino UNO and could be 0, 1, 2 and 3 in an Arduino Mega;
  • the txenpin is the pin that makes the flow control for the RS-485 transceiver, if any.
For XBEE, look at this.

The memory array

This is the place to link the data pooled through the network with the controller program.

For instance, in a network with an weather station and a cooling system, the master controller must fetch data from the weather station and send commands to the cooling system.

The machine state

The master should consider 3 states:
  • wait state (with no telegrams)
  • send query
  • wait for answer or generate a time-out event
  • restart
This last state should also move the query index if several queries are been processed.

Results

 This is the result when starting an slave with diagslave in a Linux box:

./diagslave /dev/ttyUSB0 -b 19200 -d 8 -s 1 -p none -m rtu -a 1

3 comments:

  1. Good evening.
    I am engineering student and my end of course project, I'm trying to make reading some sensors.
    These sensors are remote, and would like to use Modbus for communication using master and slave Arduino.

    I'm having trouble getting the values sent by the slaves when using your library.

    If possible, I would like your help with an example where a slave sends 3 values and the master shows these three values in a display.

    Grateful.

    Marcio.

    ps: sorry my English, was translated using google translator

    ReplyDelete
  2. Hi,

    I'm trying to use your library and your example for communication between Arduino Uno (Slave) and Arduino Mega (Master).

    I connect Tx of Arduino Mega to Rx of Arduino Uno, And Rx of Arduino Mega to Tx of Arduino Uno.

    I try to run your example, but the Master's registers are not changed. Would you give me some clue to troubleshoot the problem?

    Thank you.

    Best Regards,

    Matheus

    ReplyDelete
  3. Hello i am using this library... i can write values.. but i can´t read them.. The value of the response in au16data is 0.. i now that is not cero. Could you help me?

    This is my code

    #include

    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, 1, 2); // 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 = 2;// start address in slave
    telegram[0].u16CoilsNo = 2; // 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 = 16; // function code (this one is write a single register)
    telegram[1].u16RegAdd = 682; // start address in slave
    telegram[1].u16CoilsNo = 2; // number of elements (coils or registers) to write
    telegram[1].au16reg = au16data+4; // pointer to a memory array in the Arduino*/

    master.begin( 38400 ); // baud-rate at 19200
    master.setTimeOut( 5000 ); // if there is no answer in 5000 ms, roll over
    u32wait = millis() + 1000; //polling cada 1 segundo
    u8state = u8query = 0;

    Serial.begin(19200);
    Serial.setTimeout(1000);

    pinMode(12,OUTPUT);
    pinMode(13,OUTPUT);

    }

    void loop() {
    int i;
    switch ( u8state ) {
    case 0:
    if (millis() > u32wait) u8state++; // wait state
    digitalWrite(13,LOW);
    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 (au16data[0]!= 0){
    digitalWrite(13,HIGH);
    delay(1000);
    }
    if (master.getState() == COM_IDLE) {



    Serial.println("POLLING...");
    Serial.print("STATE:");
    Serial.println(master.getState());
    Serial.print("ERROR:");
    Serial.println(master.getLastError());
    Serial.print("DATA READ:");
    for (i=0;i<2;i++)
    Serial.print(au16data[i]);
    Serial.println("");
    if (au16data[0]!= 0){
    digitalWrite(13,HIGH);
    delay(1000);
    }

    u32wait = millis() + 1000;
    u8state = 0;
    }
    break;
    }

    /* else
    digitalWrite(12,LOW);*/



    au16data[4] = 7;
    au16data[5] = analogRead( 0 )*16.117;
    //
    }

    ReplyDelete