Author Topic: Simple Modbus Simulator  (Read 6812 times)

Offline mhoneywill

  • Full Member
  • ***
  • Posts: 173
    • View Profile
Simple Modbus Simulator
« on: September 07, 2009, 05:22:54 PM »
Hi Mark,

I want to make a simple modbus simulator that will resond to all 247 modbus addresses, I want it to compute the response depending on what address is read. i.e. if the input register at address 1 is read I would like the data returned to be 1, if adress 2 is read the data should be 2 etc. This is for testing purposes.

I know I could use MODBUS_SHARED_SERIAL_INTERFACES but this seems like a bit of overkill. I thought I might be able to use fnMODBUSuserFunction but how do I get this to be called for every address?

Cheers

Martin

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3013
    • View Profile
    • uTasker
Re: Simple Modbus Simulator
« Reply #1 on: September 07, 2009, 09:03:37 PM »
Hi Martin

The slave will always check for its address so to build a simulator which will respond on each address may require a promiscuous mode (a new flag) to be added. This would then allow the user callback to be used.

However I think that it is also easily achieved by configuring the slave as a gateway instead. As a gateway all receptions will pass though the master callback. In case of serial reception (which seems to be what you are working with) it will be with the the type SERIAL_ROUTE_FROM_SLAVE). In this function you can then use fnMODBUS_transmit() to send any answer back that you like.

Eg:

static int fnMODBUSmaster(int iType, MODBUS_RX_FUNCTION *modbus_rx_function)
{
    switch (iType) {
    case SERIAL_ROUTE_FROM_SLAVE:
        {
        unsigned char ucTest[6];                                         // the message to be sent must have the address at the start and 2 additional bytes space at the end for a check sum to be added. Do not use const data!
        ucTest[1] = modbus_rx_function->ucFunctionCode;
        ucTest[2] = 1;
        ucTest[3] = 3;
        ucTest[0] = modbus_rx_function->ucSourceAddress;                 // our address
        return (fnMODBUS_transmit(modbus_rx_function, ucTest, (sizeof(ucTest)))); // answer with pre-defined response
        }
...
    }
}


Here a fixed MODBUS_READ_COILS function response is sent back but you can generate any content as well as changing function code and even slave addresses. Probably this will allow you to create a simulator which responds to each address and sends data back containing its own address, plus far more too.

If it doesn't work out I don't see any harm in adding a promiscuous parameter flag either to allow the slave part to do it as well.

regards

Mark

P.S. The master call back can also be renamed to simulator_callback() or something in case its name is a bit confusing (it is a user function and so its actual name is not important (the example is just more usually a master type function)).

Offline mhoneywill

  • Full Member
  • ***
  • Posts: 173
    • View Profile
Re: Simple Modbus Simulator
« Reply #2 on: September 08, 2009, 12:31:34 AM »
Thanks Mark,

I found that

static int fnMODBUSsimulator(int iType, MODBUS_RX_FUNCTION *modbus_rx_function)
{
    switch (iType) {
    case SERIAL_ROUTE_FROM_SLAVE:
        {
        unsigned char ucTest[7];                                         // the message to be sent must have the address at the start and 2 additional bytes space at the end for a check sum to be added. Do not use const data!
        ucTest[0] = modbus_rx_function->ucSourceAddress;                 // our address
        ucTest[1] = modbus_rx_function->ucFunctionCode;
        ucTest[2] = 2;
        ucTest[3] = 0;                 // our address
        ucTest[4] = modbus_rx_function->ucSourceAddress;                 // our address
        return (fnMODBUS_transmit(modbus_rx_function, ucTest, (sizeof(ucTest)))); // answer with pre-defined response
        }
    }
    return 0;
}

Did what I wanted (Note your example didn't quite work, its byte count was wrong and only returned 1 byte not 2).

All I had to do was define the slave to be a gateway as below      

(MODBUS_MODE_RTU | MODBUS_SERIAL_SLAVE | MODBUS_RS485_POSITIVE | MODBUS_SERIAL_GATEWAY),         // default to RTU mode as slave - serial port 0

Cheers

Martin
« Last Edit: September 08, 2009, 07:20:01 AM by mhoneywill »