Create a simple Client/Server using Modbus in C
Asked Answered
C

1

13

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.

Croupier answered 13/4, 2015 at 10:4 Comment(2)
If I understood correctly ModbusTCP is just a normal TCP communication in which the data is structured as Modbus. I already created TCP server/client so that should be ok. I am thus more interested in the serial implementation such as RTU in which I am totally lost.Croupier
One way is editing with progress updates, another way is the bounty, but you have to wait at least two days. However, stay tuned, I'm writing something :)Orpheus
O
25

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);
Orpheus answered 14/4, 2015 at 14:24 Comment(11)
Thank you very much for your answer, I was looing hope. I am forced to use modbus and even if I wasn't the situation I am working on makes using modbus the, or a, logical solution. Yes I understood that Modbus TCp isn't RTU or ASCII over TCP, what I meant is that modbus TCP is like any TCP communication, the only difference is the data structure (right ?). Thank you for the links, I already got the modbus library, but I would really like an exemple in C which is not using any library.Croupier
Yes, that's what I was saying in my third point. I think that's explained quite good here.Orpheus
Well, they explain how to do it, but I would like a written example (in C at best but another language would be ok) to be sure I understand correctly before I start to implement this communication protocol. You seem to know a bit about modbus, maybe you could write a short code showing modbus RTU or ASCII (I understand modbus TCP) interaction between a client and a server ? I would love you for it !Croupier
Just updating that I still stuck on modbus serial communication whether it is RTU or ASCII.Croupier
Given all you said, in the end I really think that starting from scratch would be very unwise in your case. Though it's very important that you understand the details of the Modbus protocol variants, I do recommend you to not reinvent the wheel and go with an existing C library that fits your platform. I'm going to post a simplified RTU example (ASCII is actually a legacy nowadays) based on libmodbus as soon as I have time. In such kind of programs, you just refer to an OS device name (e.g. /dev/ttyS0, COM6); it's up to you to have that device working as rs232/rs485 serial link.Orpheus
Alright, not an example from scratch but still very, very, very, userfull. Thank you very much for taking the time of helping a complete stranger, you are what is good on this planet !! (If you can comment the code it will be even better).Croupier
Of course I'll comment it but you'll have to wait at most a couple of days, can you? :)Orpheus
Yes, of course the sooner the better but I can wait.Croupier
Thank you very much for the code, I will try to implement it as soon as I can, I am amazed by the time you took to help me and I thank you very very much.Croupier
@Orpheus Your code took me out of my misery. I wanted to implement a slave but I could not find any examples. I am able to view the holding registers in my modbus simulator but how do I display the value of the input register 1 in my c code if I set it from my simulator. Thanks in advance.Gillette
@Gillette mapping->tab_input_registers[1]Orpheus

© 2022 - 2024 — McMap. All rights reserved.