Create a simple Client/Server using Modbus in C

PiggyGenius picture PiggyGenius · Apr 13, 2015 · Viewed 36.8k times · Source

I am currently working on a project which will allow different automates to communicate. To do so, I would like to create a client and a server that will talk using the modbus protocol. I am not sure if I want to be using ModBus/TCP, ModBus/RTU or ModBus/ASCII for now.

I have searched for client/server examples in C and I could find libraries but no simple example of communication. I would like to start from scratch so libraries are not what I am looking for.

What I am asking for is if someone could give me a simple code written in C for a client and/or a server that communicate using Modbus, since I am not sure of what I will be using any type of Modbus would be a great help (RTU/TCP/ASCII).

The simpler the better, what I would like the code to demonstrate is, for example : an initialization to the server, a request, an answer, closing the connection.

Thank you very much for your time.

Answer

matpop picture matpop · Apr 14, 2015

Three things:

  1. As you're developing your own client and server components, I suggest you to use Modbus only if strictly required or convenient with an eye to openness (i.e. other manufacturers must be able to communicate with your client or server components by means of a standardized protocol - and Modbus fits).
  2. Be aware that Modbus TCP isn't just Modbus RTU(/ASCII) over TCP/IP (which is still allowed, of course, also UDP would be allowed). There are some important differences to take into account.
  3. I understand that you need to understand Modbus at a deeper level. At that point, once you have an open serial channel or (listening) TCP socket inside your C program, you may just start with simple Modbus requests/responses.

Take a look at this short but quite complete description, and also at the documentation of this constantly updated library.


Here's a super-simplified RTU example for Linux, based on libmodbus.
Allow me some C99 relaxation for compactness.
In the real world you should also properly handle signals like SIGTERM, etc...
There's also a modbus_rtu_set_serial_mode (RS232 vs RS485) function for Linux kernels 2.6.28 onwards. You may find other libraries that make working with RS485 easier on your platform.

Master snippet

//Create a new RTU context with proper serial parameters (in this example,
//device name /dev/ttyS0, baud rate 9600, no parity bit, 8 data bits, 1 stop bit)
modbus_t *ctx = modbus_new_rtu("/dev/ttyS0", 9600, 'N', 8, 1);
if (!ctx) {
    fprintf(stderr, "Failed to create the context: %s\n", modbus_strerror(errno));
    exit(1);
}

if (modbus_connect(ctx) == -1) {
    fprintf(stderr, "Unable to connect: %s\n", modbus_strerror(errno));
    modbus_free(ctx);
    exit(1);
}

//Set the Modbus address of the remote slave (to 3)
modbus_set_slave(ctx, 3);


uint16_t reg[5];// will store read registers values

//Read 5 holding registers starting from address 10
int num = modbus_read_registers(ctx, 10, 5, reg);
if (num != 5) {// number of read registers is not the one expected
    fprintf(stderr, "Failed to read: %s\n", modbus_strerror(errno));
}

modbus_close(ctx);
modbus_free(ctx);

Slave snippet

//Prepare a Modbus mapping with 30 holding registers
//(plus no output coil, one input coil and two input registers)
//This will also automatically set the value of each register to 0
modbus_mapping_t *mapping = modbus_mapping_new(0, 1, 30, 2);
if (!mapping) {
    fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno));
    exit(1);
}


//Example: set register 12 to integer value 623
mapping->tab_registers[12] = 623;


modbus_t *ctx = modbus_new_rtu("/dev/ttyS0", 9600, 'N', 8, 1);
if (!ctx) {
    fprintf(stderr, "Failed to create the context: %s\n", modbus_strerror(errno));
    exit(1);
}

//Set the Modbus address of this slave (to 3)
modbus_set_slave(ctx, 3);


if (modbus_connect(ctx) == -1) {
    fprintf(stderr, "Unable to connect: %s\n", modbus_strerror(errno));
    modbus_free(ctx);
    exit(1);
}


uint8_t req[MODBUS_RTU_MAX_ADU_LENGTH];// request buffer
int len;// length of the request/response

while(1) {
    len = modbus_receive(ctx, req);
    if (len == -1) break;

    len = modbus_reply(ctx, req, len, mapping);
    if (len == -1) break;
}
printf("Exit the loop: %s\n", modbus_strerror(errno));

modbus_mapping_free(mapping);
modbus_close(ctx);
modbus_free(ctx);