From 7f48e0fddc544eb1ed7e4893da8c94555d668c67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Sun, 5 Apr 2020 23:09:47 +0200 Subject: [PATCH 001/210] Move malloc before starting unit tests --- tests/unit-test-client.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/unit-test-client.c b/tests/unit-test-client.c index e95ea69fa..b2d2e6324 100644 --- a/tests/unit-test-client.c +++ b/tests/unit-test-client.c @@ -108,13 +108,6 @@ int main(int argc, char *argv[]) modbus_free(ctx); return -1; } - modbus_get_response_timeout(ctx, &new_response_to_sec, &new_response_to_usec); - - printf("** UNIT TESTING **\n"); - - printf("1/1 No response timeout modification on connect: "); - ASSERT_TRUE(old_response_to_sec == new_response_to_sec && - old_response_to_usec == new_response_to_usec, ""); /* Allocate and initialize the memory to store the bits */ nb_points = (UT_BITS_NB > UT_INPUT_BITS_NB) ? UT_BITS_NB : UT_INPUT_BITS_NB; @@ -127,6 +120,13 @@ int main(int argc, char *argv[]) tab_rp_registers = (uint16_t *) malloc(nb_points * sizeof(uint16_t)); memset(tab_rp_registers, 0, nb_points * sizeof(uint16_t)); + printf("** UNIT TESTING **\n"); + + printf("1/1 No response timeout modification on connect: "); + modbus_get_response_timeout(ctx, &new_response_to_sec, &new_response_to_usec); + ASSERT_TRUE(old_response_to_sec == new_response_to_sec && + old_response_to_usec == new_response_to_usec, ""); + printf("\nTEST WRITE/READ:\n"); /** COIL BITS **/ From 0814ecf778c8d9b3e8bbf520503849de4eee7710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Bollo?= Date: Thu, 17 Sep 2020 14:57:15 +0200 Subject: [PATCH 002/210] Fix warning issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When compiling with gcc and option -Wconversion it fixes the warning message warning: conversion from ‘X’ {aka ‘x’} to ‘Y’ {aka ‘y’} may change value Signed-off-by: José Bollo --- src/modbus.h | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/modbus.h b/src/modbus.h index c63f5ceb4..24808ead5 100644 --- a/src/modbus.h +++ b/src/modbus.h @@ -249,24 +249,28 @@ MODBUS_API int modbus_reply_exception(modbus_t *ctx, const uint8_t *req, ((int64_t)tab_int16[(index) + 1] << 32) | \ ((int64_t)tab_int16[(index) + 2] << 16) | \ (int64_t)tab_int16[(index) + 3]) -#define MODBUS_GET_INT32_FROM_INT16(tab_int16, index) ((tab_int16[(index)] << 16) | tab_int16[(index) + 1]) -#define MODBUS_GET_INT16_FROM_INT8(tab_int8, index) ((tab_int8[(index)] << 8) | tab_int8[(index) + 1]) +#define MODBUS_GET_INT32_FROM_INT16(tab_int16, index) \ + (((int32_t)tab_int16[(index) ] << 16) | \ + (int32_t)tab_int16[(index) + 1]) +#define MODBUS_GET_INT16_FROM_INT8(tab_int8, index) \ + (((int16_t)tab_int8[(index) ] << 8) | \ + (int16_t)tab_int8[(index) + 1]) #define MODBUS_SET_INT16_TO_INT8(tab_int8, index, value) \ do { \ - tab_int8[(index)] = (value) >> 8; \ - tab_int8[(index) + 1] = (value) & 0xFF; \ + ((int8_t*)(tab_int8))[(index) ] = (int8_t)((value) >> 8); \ + ((int8_t*)(tab_int8))[(index) + 1] = (int8_t)(value); \ } while (0) #define MODBUS_SET_INT32_TO_INT16(tab_int16, index, value) \ do { \ - tab_int16[(index) ] = (value) >> 16; \ - tab_int16[(index) + 1] = (value); \ + ((int16_t*)(tab_int16))[(index) ] = (int16_t)((value) >> 16); \ + ((int16_t*)(tab_int16))[(index) + 1] = (int16_t)(value); \ } while (0) #define MODBUS_SET_INT64_TO_INT16(tab_int16, index, value) \ do { \ - tab_int16[(index) ] = (value) >> 48; \ - tab_int16[(index) + 1] = (value) >> 32; \ - tab_int16[(index) + 2] = (value) >> 16; \ - tab_int16[(index) + 3] = (value); \ + ((int16_t*)(tab_int16))[(index) ] = (int16_t)((value) >> 48); \ + ((int16_t*)(tab_int16))[(index) + 1] = (int16_t)((value) >> 32); \ + ((int16_t*)(tab_int16))[(index) + 2] = (int16_t)((value) >> 16); \ + ((int16_t*)(tab_int16))[(index) + 3] = (int16_t)(value); \ } while (0) MODBUS_API void modbus_set_bits_from_byte(uint8_t *dest, int idx, const uint8_t value); From 9d3b12861e7bd6880dd0cbdcaed94249ba75765f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Thu, 24 Sep 2020 22:37:57 +0200 Subject: [PATCH 003/210] Add modbus_[get|set]_indication_timeout to doc build --- doc/Makefile.am | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/Makefile.am b/doc/Makefile.am index 5a52c0409..08eba6d6d 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -3,6 +3,8 @@ TXT3 = \ modbus_connect.txt \ modbus_flush.txt \ modbus_free.txt \ + modbus_get_indication_timeout.txt \ + modbus_get_slave.txt \ modbus_get_byte_from_bits.txt \ modbus_get_byte_timeout.txt \ modbus_get_float.txt \ @@ -12,7 +14,6 @@ TXT3 = \ modbus_get_float_dcba.txt \ modbus_get_header_length.txt \ modbus_get_response_timeout.txt \ - modbus_get_slave.txt \ modbus_get_socket.txt \ modbus_mapping_free.txt \ modbus_mapping_new.txt \ @@ -48,6 +49,7 @@ TXT3 = \ modbus_set_float_badc.txt \ modbus_set_float_cdab.txt \ modbus_set_float_dcba.txt \ + modbus_set_indication_timeout.txt \ modbus_set_response_timeout.txt \ modbus_set_slave.txt \ modbus_set_socket.txt \ From 1d758ca214bfb792c82c6d8e545c2e3e00fa05c0 Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Sat, 28 Nov 2020 10:16:27 +1100 Subject: [PATCH 004/210] docs: fix simple typo, reponse -> response There is a small typo in tests/bandwidth-client.c. Should read `response` rather than `reponse`. --- tests/bandwidth-client.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/bandwidth-client.c b/tests/bandwidth-client.c index 9fa07f60b..d1328c9c0 100644 --- a/tests/bandwidth-client.c +++ b/tests/bandwidth-client.c @@ -116,7 +116,7 @@ int main(int argc, char *argv[]) printf("* %d KiB/s\n", rate); printf("\n"); - /* TCP: Query and reponse header and values */ + /* TCP: Query and response header and values */ bytes = 12 + 9 + (nb_points / 8) + ((nb_points % 8) ? 1 : 0); printf("Values and TCP Modbus overhead:\n"); printf("* %d x %d bytes\n", n_loop, bytes); @@ -153,7 +153,7 @@ int main(int argc, char *argv[]) printf("* %d KiB/s\n", rate); printf("\n"); - /* TCP:Query and reponse header and values */ + /* TCP:Query and response header and values */ bytes = 12 + 9 + (nb_points * sizeof(uint16_t)); printf("Values and TCP Modbus overhead:\n"); printf("* %d x %d bytes\n", n_loop, bytes); @@ -192,7 +192,7 @@ int main(int argc, char *argv[]) printf("* %d KiB/s\n", rate); printf("\n"); - /* TCP:Query and reponse header and values */ + /* TCP:Query and response header and values */ bytes = 12 + 9 + (nb_points * sizeof(uint16_t)); printf("Values and TCP Modbus overhead:\n"); printf("* %d x %d bytes\n", n_loop, bytes); From ae66153244149574c38b8e98f48a9e0b91ea3b62 Mon Sep 17 00:00:00 2001 From: Anton Bondarev Date: Fri, 19 Mar 2021 16:09:49 +0300 Subject: [PATCH 005/210] Update README.md Add Embox RTOS to the list of supported OSes --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ad8ea4af3..a0902923b 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,11 @@ License 3.0 (Unported) (). The official website is [www.libmodbus.org](http://www.libmodbus.org). -The library is written in C and designed to run on Linux, Mac OS X, FreeBSD and +The library is written in C and designed to run on Linux, Mac OS X, FreeBSD, Embox, QNX and Windows. +You can use the library on MCUs with Embox RTOS. + Installation ------------ @@ -55,6 +57,8 @@ To compile under OS X with [homebrew](http://mxcl.github.com/homebrew/), you will need to install the following dependencies first: `brew install autoconf automake libtool`. +To build under Embox, you have to use its build system. + Documentation ------------- From 26c851636f15a5ca310183d417a481cee64510a9 Mon Sep 17 00:00:00 2001 From: Richard Ash Date: Mon, 22 Jun 2020 14:53:36 +0100 Subject: [PATCH 006/210] Install the NEWS and AUTHORS files Fix #513 --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index a0a165e18..cc1482d4f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -7,7 +7,7 @@ pkgconfig_DATA = libmodbus.pc EXTRA_DIST = libmodbus.pc.in CLEANFILES += libmodbus.pc -dist_doc_DATA = MIGRATION README.md +dist_doc_DATA = MIGRATION README.md AUTHORS NEWS SUBDIRS = src doc From 486f277d32fdf0b5b52cdaef42a70a613201e441 Mon Sep 17 00:00:00 2001 From: Richard Ash Date: Mon, 22 Jun 2020 15:09:21 +0100 Subject: [PATCH 007/210] Include the test LICENSE in tarball Fix for #542 --- tests/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Makefile.am b/tests/Makefile.am index 38fa21c09..7302c8d73 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,4 +1,4 @@ -EXTRA_DIST = README.md unit-tests.sh +EXTRA_DIST = README.md unit-tests.sh LICENSE noinst_PROGRAMS = \ bandwidth-server-one \ From bcfd111486ea5c4f9752172074a0fef9f233fb1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Tue, 11 May 2021 00:14:13 +0200 Subject: [PATCH 008/210] Add .clabot --- .clabot | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .clabot diff --git a/.clabot b/.clabot new file mode 100644 index 000000000..89d3453e2 --- /dev/null +++ b/.clabot @@ -0,0 +1,4 @@ +{ + "contributors": ["mhei", "jbysewski"], + "message": "We require contributors to sign our Contributor License Agreement. In order for us to review and merge your code, please fill https://forms.gle/5635zjphDo5JEJQSA to get yourself added." +} From 1f84d40a008894570fe272602cd829b8a946d2fa Mon Sep 17 00:00:00 2001 From: Stefan Nilsson Date: Tue, 12 May 2020 23:34:40 +0200 Subject: [PATCH 009/210] typo --- src/modbus-rtu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modbus-rtu.c b/src/modbus-rtu.c index 56c812ca3..d602ee750 100644 --- a/src/modbus-rtu.c +++ b/src/modbus-rtu.c @@ -353,7 +353,7 @@ static int _modbus_rtu_pre_check_confirmation(modbus_t *ctx, const uint8_t *req, } } -/* The check_crc16 function shall return 0 is the message is ignored and the +/* The check_crc16 function shall return 0 if the message is ignored and the message length if the CRC is valid. Otherwise it shall return -1 and set errno to EMBBADCRC. */ static int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg, From 56d1d95d4f82c91bb7475bf3ad63776321ad877e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Tue, 11 May 2021 00:24:45 +0200 Subject: [PATCH 010/210] Replace .dir-locals.el (Emacs) by .editorconfig --- .dir-locals.el | 4 ---- .editorconfig | 12 ++++++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) delete mode 100644 .dir-locals.el create mode 100644 .editorconfig diff --git a/.dir-locals.el b/.dir-locals.el deleted file mode 100644 index 501d1d0a9..000000000 --- a/.dir-locals.el +++ /dev/null @@ -1,4 +0,0 @@ -((nil . ((indent-tabs-mode . nil) - (c-basic-offset . 4) - (fill-column . 80)))) - diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..640a2a203 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 + +# Tab indentation (no size specified) +[Makefile] +indent_style = tab From d25150ac61e3396563a430b79140aa8d3daa9270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Tue, 11 May 2021 00:55:05 +0200 Subject: [PATCH 011/210] Fix many typos Thanks to @peternewman --- ISSUE_TEMPLATE.md | 44 ++++++++++++------------ MIGRATION | 2 +- NEWS | 2 +- doc/modbus_mapping_new_start_address.txt | 4 +-- doc/modbus_receive_confirmation.txt | 2 +- doc/modbus_reply.txt | 2 +- doc/modbus_reply_exception.txt | 2 +- doc/modbus_set_float_badc.txt | 2 +- src/modbus-rtu.c | 2 +- src/modbus-tcp-private.h | 2 +- src/modbus-tcp.c | 4 +-- src/modbus.c | 2 +- tests/README.md | 2 +- tests/bandwidth-client.c | 8 ++--- tests/bandwidth-server-one.c | 2 +- tests/unit-test-client.c | 4 +-- 16 files changed, 43 insertions(+), 43 deletions(-) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 06d145756..dfd038e86 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,40 +1,40 @@ Please read the following carefully before submitting this new issue. - - Please ensure, that you are really reporting a bug. When in doubt, - post a message on https://groups.google.com/forum/#!forum/libmodbus - or send an email to libmodbus@googlegroups.com +- Please ensure, that you are really reporting a bug. When in doubt, post a + message on or send an + email to libmodbus@googlegroups.com - - Please do not open issues to ask questions about using libmodbus. - Use the mailing list for this as there are many more people reading - that list, who could help you. +- Please do not open issues to ask questions about using libmodbus. Use the + mailing list for this as there are many more people reading that list, who + could help you. - - When using libmodbus from a distribution (Debian, Fedora...), please - report the bug first in the bug tracker of the distribution. The - reason for doing so is that the package maintainer should have a chance - to look at the issue first as it might be a packaging error. If/when - the package maintainer comes to the conclusion that is really an upstream - bug, then he/she will usually report it here by himself/herself. - This is because he/she is interested in staying in the notification chain - to decide about a backport as soon as a bugfix is available. - Otherwise you (distribution user) will be asked to do so explicitely. +- When using libmodbus from a distribution (Debian, Fedora...), please report + the bug first in the bug tracker of the distribution. The reason for doing so + is that the package maintainer should have a chance to look at the issue first + as it might be a packaging error. If/when the package maintainer comes to the + conclusion that is really an upstream bug, then he/she will usually report it + here by himself/herself. This is because he/she is interested in staying in + the notification chain to decide about a backport as soon as a bugfix is + available. Otherwise you (distribution user) will be asked to do so + explicitly. -When you get here and you are still convinced that you want report a bug: +When you get here and you are still convinced that you want to report a bug: - - *Use a clear and decriptive title* for the issue to identify +- *Use a clear and decriptive title* for the issue to identify - - *Which version of libmodbus are you using?* you can obtain this information +- *Which version of libmodbus are you using?* you can obtain this information from your package manager or by running `pkg-config --modversion libmodbus`. You can provide the sha1 of the commit if you have fetched the code with `git`. - - *Which operating system are you using?* +- *Which operating system are you using?* - - *Describe the exact steps which reproduce the problem* in as many details as +- *Describe the exact steps which reproduce the problem* in as many details as possible. For example, the software/equipement which runs the Modbus server, how the clients are connected (TCP, RTU, ASCII) and the source code you are using. - - *Enable the debug mode*, libmodbus provides a function to display the content +- *Enable the debug mode*, libmodbus provides a function to display the content of the Modbus messages and it's very convenient to analyze issues - (http://libmodbus.org/docs/latest/modbus_set_debug.html). + (). Good bug reports provide right and quick fixes! diff --git a/MIGRATION b/MIGRATION index 095703c1c..7a182b572 100644 --- a/MIGRATION +++ b/MIGRATION @@ -16,7 +16,7 @@ dynamically allocated structure modbus_t. - all function and constants are respectively prefixed by modbus_ or MODBUS_. -- the POSIX error conventions are used (if an error occured, -1 or NULL is +- the POSIX error conventions are used (if an error occurred, -1 or NULL is returned and errno is set accordingly). - coil status and discretes inputs are just bits and force/preset actions have diff --git a/NEWS b/NEWS index 9e68da778..cf92de22f 100644 --- a/NEWS +++ b/NEWS @@ -423,7 +423,7 @@ libmodbus 2.1.0 (2010-03-24) - Fix report slave ID request Patch (bzr) provided by Paul Fertser. - Fix #425604 - Conditional jump or move depends on uninitialised value(s) - Occurs on first occurence of slave timeout. + Occurs on first occurrence of slave timeout. Reported by Henrik Munktell. - Fix #457200 - FreeBSD support Patch provided by Norbert Koch. diff --git a/doc/modbus_mapping_new_start_address.txt b/doc/modbus_mapping_new_start_address.txt index ec7bfdb35..0eaaa8ee2 100644 --- a/doc/modbus_mapping_new_start_address.txt +++ b/doc/modbus_mapping_new_start_address.txt @@ -21,9 +21,9 @@ The _modbus_mapping_new_start_address()_ function shall allocate four arrays to store bits, input bits, registers and inputs registers. The pointers are stored in modbus_mapping_t structure. All values of the arrays are initialized to zero. -The different starting adresses make it possible to place the mapping at any +The different starting addresses make it possible to place the mapping at any address in each address space. This way, you can give access to values stored -at high adresses without allocating memory from the address zero, for eg. to +at high addresses without allocating memory from the address zero, for eg. to make available registers from 10000 to 10009, you can use: [source,c] diff --git a/doc/modbus_receive_confirmation.txt b/doc/modbus_receive_confirmation.txt index 118ee2b5c..290d1f6ac 100644 --- a/doc/modbus_receive_confirmation.txt +++ b/doc/modbus_receive_confirmation.txt @@ -30,7 +30,7 @@ avoid crashes of your server. RETURN VALUE ------------ The function shall store the confirmation request in _rsp_ and return the -response length if sucessful. The returned request length can be zero if the +response length if successful. The returned request length can be zero if the indication request is ignored (eg. a query for another slave in RTU mode). Otherwise it shall return -1 and set errno. diff --git a/doc/modbus_reply.txt b/doc/modbus_reply.txt index 0b29d6f2c..6b71d11c2 100644 --- a/doc/modbus_reply.txt +++ b/doc/modbus_reply.txt @@ -3,7 +3,7 @@ modbus_reply(3) NAME ---- -modbus_reply - send a reponse to the received request +modbus_reply - send a response to the received request SYNOPSIS diff --git a/doc/modbus_reply_exception.txt b/doc/modbus_reply_exception.txt index 7e6324f94..b2170be3b 100644 --- a/doc/modbus_reply_exception.txt +++ b/doc/modbus_reply_exception.txt @@ -3,7 +3,7 @@ modbus_reply_exception(3) NAME ---- -modbus_reply_exception - send an exception reponse +modbus_reply_exception - send an exception response SYNOPSIS diff --git a/doc/modbus_set_float_badc.txt b/doc/modbus_set_float_badc.txt index 8df8ca958..d41d777d7 100644 --- a/doc/modbus_set_float_badc.txt +++ b/doc/modbus_set_float_badc.txt @@ -15,7 +15,7 @@ SYNOPSIS DESCRIPTION ----------- The *modbus_set_float_badc()* function shall set a float to 4 bytes in swapped -bytes Modbus format (BADC insted of ABCD). The _dest_ array must be pointer on +bytes Modbus format (BADC instead of ABCD). The _dest_ array must be pointer on two 16 bits values to be able to store the full result of the conversion. diff --git a/src/modbus-rtu.c b/src/modbus-rtu.c index d602ee750..6e09e1c4c 100644 --- a/src/modbus-rtu.c +++ b/src/modbus-rtu.c @@ -842,7 +842,7 @@ static int _modbus_rtu_connect(modbus_t *ctx) ONCLR ant others needs OPOST to be enabled */ - /* Raw ouput */ + /* Raw output */ tios.c_oflag &=~ OPOST; /* C_CC Control characters diff --git a/src/modbus-tcp-private.h b/src/modbus-tcp-private.h index 780587c70..164c8c727 100644 --- a/src/modbus-tcp-private.h +++ b/src/modbus-tcp-private.h @@ -14,7 +14,7 @@ #define _MODBUS_TCP_CHECKSUM_LENGTH 0 /* In both structures, the transaction ID must be placed on first position - to have a quick access not dependant of the TCP backend */ + to have a quick access not dependent of the TCP backend */ typedef struct _modbus_tcp { /* Extract from MODBUS Messaging on TCP/IP Implementation Guide V1.0b (page 23/46): diff --git a/src/modbus-tcp.c b/src/modbus-tcp.c index fc2ff24b8..516427370 100644 --- a/src/modbus-tcp.c +++ b/src/modbus-tcp.c @@ -154,7 +154,7 @@ static int _modbus_tcp_prepare_response_tid(const uint8_t *req, int *req_length) static int _modbus_tcp_send_msg_pre(uint8_t *req, int req_length) { - /* Substract the header length to the message length */ + /* Subtract the header length to the message length */ int mbap_length = req_length - 6; req[4] = mbap_length >> 8; @@ -905,7 +905,7 @@ modbus_t* modbus_new_tcp_pi(const char *node, const char *service) dest_size = sizeof(char) * _MODBUS_TCP_PI_SERVICE_LENGTH; ret_size = strlcpy(ctx_tcp_pi->service, service, dest_size); } else { - /* Empty service is not allowed, error catched below. */ + /* Empty service is not allowed, error caught below. */ ret_size = 0; } diff --git a/src/modbus.c b/src/modbus.c index 41a421359..43e8a3592 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -1406,7 +1406,7 @@ int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint1 int rc; int req_length; /* The request length can not exceed _MIN_REQ_LENGTH - 2 and 4 bytes to - * store the masks. The ugly substraction is there to remove the 'nb' value + * store the masks. The ugly subtraction is there to remove the 'nb' value * (2 bytes) which is not used. */ uint8_t req[_MIN_REQ_LENGTH + 2]; diff --git a/tests/README.md b/tests/README.md index 810dc8c70..df684d41f 100644 --- a/tests/README.md +++ b/tests/README.md @@ -21,7 +21,7 @@ programs are essential to test the Modbus protocol implementation and libmodbus behavior. - `bandwidth-server-one`, `bandwidth-server-many-up` and `bandwidth-client` - return very useful information about the performance of transfert rate between + return very useful information about the performance of transfer rate between the server and the client. `bandwidth-server-one` can only handles one connection at once with a client whereas `bandwidth-server-many-up` opens a connection for each new clients (with a limit). diff --git a/tests/bandwidth-client.c b/tests/bandwidth-client.c index d1328c9c0..e8a7ef0d0 100644 --- a/tests/bandwidth-client.c +++ b/tests/bandwidth-client.c @@ -59,7 +59,7 @@ int main(int argc, char *argv[]) use_backend = RTU; n_loop = 100; } else { - printf("Usage:\n %s [tcp|rtu] - Modbus client to measure data bandwith\n\n", argv[0]); + printf("Usage:\n %s [tcp|rtu] - Modbus client to measure data bandwidth\n\n", argv[0]); exit(1); } } else { @@ -104,7 +104,7 @@ int main(int argc, char *argv[]) elapsed = end - start; rate = (n_loop * nb_points) * G_MSEC_PER_SEC / (end - start); - printf("Transfert rate in points/seconds:\n"); + printf("Transfer rate in points/seconds:\n"); printf("* %d points/s\n", rate); printf("\n"); @@ -141,7 +141,7 @@ int main(int argc, char *argv[]) elapsed = end - start; rate = (n_loop * nb_points) * G_MSEC_PER_SEC / (end - start); - printf("Transfert rate in points/seconds:\n"); + printf("Transfer rate in points/seconds:\n"); printf("* %d registers/s\n", rate); printf("\n"); @@ -180,7 +180,7 @@ int main(int argc, char *argv[]) elapsed = end - start; rate = (n_loop * nb_points) * G_MSEC_PER_SEC / (end - start); - printf("Transfert rate in points/seconds:\n"); + printf("Transfer rate in points/seconds:\n"); printf("* %d registers/s\n", rate); printf("\n"); diff --git a/tests/bandwidth-server-one.c b/tests/bandwidth-server-one.c index 8971d0763..2ce17f1f3 100644 --- a/tests/bandwidth-server-one.c +++ b/tests/bandwidth-server-one.c @@ -38,7 +38,7 @@ int main(int argc, char *argv[]) } else if (strcmp(argv[1], "rtu") == 0) { use_backend = RTU; } else { - printf("Usage:\n %s [tcp|rtu] - Modbus client to measure data bandwith\n\n", argv[0]); + printf("Usage:\n %s [tcp|rtu] - Modbus client to measure data bandwidth\n\n", argv[0]); exit(1); } } else { diff --git a/tests/unit-test-client.c b/tests/unit-test-client.c index b2d2e6324..e66c417dd 100644 --- a/tests/unit-test-client.c +++ b/tests/unit-test-client.c @@ -533,7 +533,7 @@ int main(int argc, char *argv[]) rc = modbus_report_slave_id(ctx, NB_REPORT_SLAVE_ID, tab_rp_bits); ASSERT_TRUE(rc == NB_REPORT_SLAVE_ID, ""); - /* Slave ID is an arbitraty number for libmodbus */ + /* Slave ID is an arbitrary number for libmodbus */ ASSERT_TRUE(rc > 0, ""); /* Run status indicator is ON */ @@ -622,7 +622,7 @@ int main(int argc, char *argv[]) printf("1/2 Too small byte timeout (3ms < 5ms): "); ASSERT_TRUE(rc == -1 && errno == ETIMEDOUT, ""); - /* Wait remaing bytes before flushing */ + /* Wait remaining bytes before flushing */ usleep(11 * 5000); modbus_flush(ctx); From 31f779185d3d2a7b212b508ea321f4e449f3de85 Mon Sep 17 00:00:00 2001 From: Marc Haber Date: Mon, 19 Nov 2018 09:09:57 +0100 Subject: [PATCH 012/210] cosmetic changes in man page standardizing itemization --- doc/libmodbus.txt | 155 ++++++++++++++++++++++------------------------ 1 file changed, 74 insertions(+), 81 deletions(-) diff --git a/doc/libmodbus.txt b/doc/libmodbus.txt index 241203712..e24650989 100644 --- a/doc/libmodbus.txt +++ b/doc/libmodbus.txt @@ -72,17 +72,17 @@ master (ortherwise other slaves may ignore master requests when one of the slave is not responding). Create a Modbus RTU context:: - linkmb:modbus_new_rtu[3] + - linkmb:modbus_new_rtu[3] Set the serial mode:: - linkmb:modbus_rtu_get_serial_mode[3] - linkmb:modbus_rtu_set_serial_mode[3] - linkmb:modbus_rtu_get_rts[3] - linkmb:modbus_rtu_set_rts[3] - linkmb:modbus_rtu_set_custom_rts[3] - linkmb:modbus_rtu_get_rts_delay[3] - linkmb:modbus_rtu_set_rts_delay[3] + - linkmb:modbus_rtu_get_serial_mode[3] + - linkmb:modbus_rtu_set_serial_mode[3] + - linkmb:modbus_rtu_get_rts[3] + - linkmb:modbus_rtu_set_rts[3] + - linkmb:modbus_rtu_set_custom_rts[3] + - linkmb:modbus_rtu_get_rts_delay[3] + - linkmb:modbus_rtu_set_rts_delay[3] TCP (IPv4) Context @@ -92,7 +92,7 @@ TCP/IPv4 networks. It does not require a checksum calculation as lower layer takes care of the same. Create a Modbus TCP context:: - linkmb:modbus_new_tcp[3] + - linkmb:modbus_new_tcp[3] TCP PI (IPv4 and IPv6) Context @@ -105,7 +105,7 @@ Contrary to the TCP IPv4 only backend, the TCP PI backend offers hostname resolution but it consumes about 1Kb of additional memory. Create a Modbus TCP context:: - linkmb:modbus_new_tcp_pi[3] + - linkmb:modbus_new_tcp_pi[3] Common @@ -115,65 +115,63 @@ Before using any libmodbus functions, the caller must allocate and initialize a are provided to modify and free a 'context': Free libmodbus context:: - linkmb:modbus_free[3] + - linkmb:modbus_free[3] Set slave ID:: - linkmb:modbus_set_slave[3] + - linkmb:modbus_set_slave[3] Enable debug mode:: - linkmb:modbus_set_debug[3] + - linkmb:modbus_set_debug[3] Timeout settings:: - linkmb:modbus_get_byte_timeout[3] - linkmb:modbus_set_byte_timeout[3] - linkmb:modbus_get_response_timeout[3] - linkmb:modbus_set_response_timeout[3] - linkmb:modbus_get_indication_timeout[3] - linkmb:modbus_set_indication_timeout[3] + - linkmb:modbus_get_byte_timeout[3] + - linkmb:modbus_set_byte_timeout[3] + - linkmb:modbus_get_response_timeout[3] + - linkmb:modbus_set_response_timeout[3] Error recovery mode:: - linkmb:modbus_set_error_recovery[3] + - linkmb:modbus_set_error_recovery[3] Setter/getter of internal socket:: - linkmb:modbus_set_socket[3] - linkmb:modbus_get_socket[3] + - linkmb:modbus_set_socket[3] + - linkmb:modbus_get_socket[3] Information about header:: - linkmb:modbus_get_header_length[3] + - linkmb:modbus_get_header_length[3] Macros for data manipulation:: -- MODBUS_GET_HIGH_BYTE(data), extracts the high byte from a byte -- MODBUS_GET_LOW_BYTE(data), extracts the low byte from a byte -- MODBUS_GET_INT64_FROM_INT16(tab_int16, index), builds an int64 from the four - first int16 starting at tab_int16[index] -- MODBUS_GET_INT32_FROM_INT16(tab_int16, index), builds an int32 from the two - first int16 starting at tab_int16[index] -- MODBUS_GET_INT16_FROM_INT8(tab_int8, index), builds an int16 from the two - first int8 starting at tab_int8[index] -- MODBUS_SET_INT16_TO_INT8(tab_int8, index, value), set an int16 value into - the two first bytes starting at tab_int8[index] -- MODBUS_SET_INT32_TO_INT16(tab_int16, index, value), set an int32 value into - the two first int16 starting at tab_int16[index] -- MODBUS_SET_INT64_TO_INT16(tab_int16, index, value), set an int64 value into - the four first int16 starting at tab_int16[index] + - MODBUS_GET_HIGH_BYTE(data), extracts the high byte from a byte + - MODBUS_GET_LOW_BYTE(data), extracts the low byte from a byte + - MODBUS_GET_INT64_FROM_INT16(tab_int16, index), builds an int64 from the four + first int16 starting at tab_int16[index] + - MODBUS_GET_INT32_FROM_INT16(tab_int16, index), builds an int32 from the two + first int16 starting at tab_int16[index] + - MODBUS_GET_INT16_FROM_INT8(tab_int8, index), builds an int16 from the two + first int8 starting at tab_int8[index] + - MODBUS_SET_INT16_TO_INT8(tab_int8, index, value), set an int16 value into + the two first bytes starting at tab_int8[index] + - MODBUS_SET_INT32_TO_INT16(tab_int16, index, value), set an int32 value into + the two first int16 starting at tab_int16[index] + - MODBUS_SET_INT64_TO_INT16(tab_int16, index, value), set an int64 value into + the four first int16 starting at tab_int16[index] Handling of bits and bytes:: - linkmb:modbus_set_bits_from_byte[3] - linkmb:modbus_set_bits_from_bytes[3] - linkmb:modbus_get_byte_from_bits[3] + - linkmb:modbus_set_bits_from_byte[3] + - linkmb:modbus_set_bits_from_bytes[3] + - linkmb:modbus_get_byte_from_bits[3] Set or get float numbers:: - linkmb:modbus_get_float_abcd[3] - linkmb:modbus_set_float_abcd[3] - linkmb:modbus_get_float_badc[3] - linkmb:modbus_set_float_badc[3] - linkmb:modbus_get_float_cdab[3] - linkmb:modbus_set_float_cdab[3] - linkmb:modbus_get_float_dcba[3] - linkmb:modbus_set_float_dcba[3] - linkmb:modbus_get_float[3] (deprecated) - linkmb:modbus_set_float[3] (deprecated) + - linkmb:modbus_get_float_abcd[3] + - linkmb:modbus_set_float_abcd[3] + - linkmb:modbus_get_float_badc[3] + - linkmb:modbus_set_float_badc[3] + - linkmb:modbus_get_float_cdab[3] + - linkmb:modbus_set_float_cdab[3] + - linkmb:modbus_get_float_dcba[3] + - linkmb:modbus_set_float_dcba[3] + - linkmb:modbus_get_float[3] (deprecated) + - linkmb:modbus_set_float[3] (deprecated) @@ -183,13 +181,13 @@ The following functions are provided to establish and close a connection with Modbus devices: Establish a connection:: - linkmb:modbus_connect[3] + - linkmb:modbus_connect[3] Close a connection:: - linkmb:modbus_close[3] + - linkmb:modbus_close[3] Flush a connection:: - linkmb:modbus_flush[3] + - linkmb:modbus_flush[3] Client @@ -199,50 +197,45 @@ them from/to remote devices. The following functions are used by the clients to send Modbus requests: Read data:: - linkmb:modbus_read_bits[3] - linkmb:modbus_read_input_bits[3] - linkmb:modbus_read_registers[3] - linkmb:modbus_read_input_registers[3] - linkmb:modbus_report_slave_id[3] + - linkmb:modbus_read_bits[3] + - linkmb:modbus_read_input_bits[3] + - linkmb:modbus_read_registers[3] + - linkmb:modbus_read_input_registers[3] + - linkmb:modbus_report_slave_id[3] Write data:: - linkmb:modbus_write_bit[3] - linkmb:modbus_write_register[3] - linkmb:modbus_write_bits[3] - linkmb:modbus_write_registers[3] + - linkmb:modbus_write_bit[3] + - linkmb:modbus_write_register[3] + - linkmb:modbus_write_bits[3] + - linkmb:modbus_write_registers[3] Write and read data:: - linkmb:modbus_write_and_read_registers[3] + - linkmb:modbus_write_and_read_registers[3] Raw requests:: - linkmb:modbus_send_raw_request[3] - linkmb:modbus_receive_confirmation[3] + - linkmb:modbus_send_raw_request[3] + - linkmb:modbus_receive_confirmation[3] Reply an exception:: - linkmb:modbus_reply_exception[3] + - linkmb:modbus_reply_exception[3] Server ~~~~~~ The server is waiting for request from clients and must answer when it is -concerned by the request. +concerned by the request. The libmodbus offers the following functions to +handle requests: -In TCP mode, you must not use the usual linkmb:modbus_connect[3] to establish the connection but a pair of accept/listen calls:: - linkmb:modbus_tcp_listen[3] - linkmb:modbus_tcp_accept[3] - linkmb:modbus_tcp_pi_listen[3] - linkmb:modbus_tcp_pi_accept[3] +Data mapping:: + - linkmb:modbus_mapping_new[3] + - linkmb:modbus_mapping_free[3] -then the data can be received with:: - linkmb:modbus_receive[3] +Receive:: + - linkmb:modbus_receive[3] -and a response can be send with:: - linkmb:modbus_reply[3] - linkmb:modbus_reply_exception[3] - -To handle the mapping of your Modbus data, you must use: - linkmb:modbus_mapping_new[3] - linkmb:modbus_mapping_free[3] +Reply:: + - linkmb:modbus_reply[3] + - linkmb:modbus_reply_exception[3] ERROR HANDLING From d22f72cbc020177b839bf97cafaed9c147bf2176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Wed, 6 Oct 2021 14:30:16 +0200 Subject: [PATCH 013/210] Add FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..6d16da712 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [stephane] From d9054d40ccfbf40210c3680d4ff027edc8e79484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Mon, 18 Oct 2021 15:25:32 +0200 Subject: [PATCH 014/210] Add the baud rate of 256k for Windows (closes #603) --- src/modbus-rtu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modbus-rtu.c b/src/modbus-rtu.c index 6e09e1c4c..01e703d46 100644 --- a/src/modbus-rtu.c +++ b/src/modbus-rtu.c @@ -496,6 +496,9 @@ static int _modbus_rtu_connect(modbus_t *ctx) case 250000: dcb.BaudRate = 250000; break; + case 256000: + dcb.BaudRate = 256000; + break; case 460800: dcb.BaudRate = 460800; break; From 4dbef3551645e510b8fabcd6c0f5925c7d27b860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Mon, 18 Oct 2021 15:27:54 +0200 Subject: [PATCH 015/210] Remove duplicate ';' (closes #602) --- tests/unit-test-server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit-test-server.c b/tests/unit-test-server.c index 7002b10c6..6e0af93e1 100644 --- a/tests/unit-test-server.c +++ b/tests/unit-test-server.c @@ -92,7 +92,7 @@ int main(int argc, char*argv[]) /* Initialize values of INPUT REGISTERS */ for (i=0; i < UT_INPUT_REGISTERS_NB; i++) { - mb_mapping->tab_input_registers[i] = UT_INPUT_REGISTERS_TAB[i];; + mb_mapping->tab_input_registers[i] = UT_INPUT_REGISTERS_TAB[i]; } if (use_backend == TCP) { From ebc4f4788678eda8d99e23be87c61a662a9d26b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Mon, 18 Oct 2021 15:29:22 +0200 Subject: [PATCH 016/210] Fix position of CC flags in documentation (closes #599) --- doc/libmodbus.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/libmodbus.txt b/doc/libmodbus.txt index e24650989..716c684aa 100644 --- a/doc/libmodbus.txt +++ b/doc/libmodbus.txt @@ -11,8 +11,7 @@ SYNOPSIS -------- *#include * -*cc* \`pkg-config --cflags --libs libmodbus` 'files' - +*cc* 'files' \`pkg-config --cflags --libs libmodbus` DESCRIPTION ----------- From 410b148f740e78df066cd67fef5c4219e0b0a270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Fri, 7 Jan 2022 15:43:07 +0100 Subject: [PATCH 017/210] Replace obsolete AC_PROG_CC_STDC by AC_PROG_CC --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 070879c9a..ca5cada9f 100644 --- a/configure.ac +++ b/configure.ac @@ -30,7 +30,7 @@ AC_INIT([libmodbus], AC_CONFIG_SRCDIR([src/modbus.c]) AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([check-news foreign 1.11 -Wall -Wno-portability silent-rules tar-pax subdir-objects]) -AC_PROG_CC_STDC +AC_PROG_CC AC_USE_SYSTEM_EXTENSIONS AC_SYS_LARGEFILE AC_CONFIG_MACRO_DIR([m4]) From 89f5c50415748e82e98c143f80bc888e389f030f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Fri, 7 Jan 2022 15:53:53 +0100 Subject: [PATCH 018/210] Fix typo in comment (closes #588) Thank you @qqq89513 --- src/modbus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modbus.c b/src/modbus.c index 43e8a3592..99f333b85 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -1139,7 +1139,7 @@ int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest) return nb; } -/* Reads the data from a remove device and put that data into an array */ +/* Reads the data from a remote device and put that data into an array */ static int read_registers(modbus_t *ctx, int function, int addr, int nb, uint16_t *dest) { From bd1c85f2d39cba13c031f019b005f564cf799f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Fri, 7 Jan 2022 16:10:10 +0100 Subject: [PATCH 019/210] Add SECURITY.md (closes #613) --- SECURITY.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..2dff79e46 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,18 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 3.1.x | :white_check_mark: | +| 3.0.x | :x: | + +## Reporting a Vulnerability + +To report a vulnerability, you can send a mail to the maintainer at +Stéphane Raimbault . + +Once the vulnerability is fixed, new releases will be published and an issue +will be created to disclose the vulnerability. + +The reporter name will be credited (if you wish). From b4ef4c17d618eba0adccc4c7d9e9a1ef809fc9b6 Mon Sep 17 00:00:00 2001 From: Michael Heimpold Date: Sat, 8 Jan 2022 20:00:50 +0100 Subject: [PATCH 020/210] modbus_reply: fix copy & paste error in sanity check (fixes #614) While handling MODBUS_FC_WRITE_AND_READ_REGISTERS, both address offsets must be checked, i.e. the read and the write address must be within the mapping range. At the moment, only the read address was considered, it looks like a simple copy and paste error, so let's fix it. Signed-off-by: Michael Heimpold --- src/modbus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modbus.c b/src/modbus.c index 99f333b85..e13bd0df7 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -961,7 +961,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, nb_write, nb, MODBUS_MAX_WR_WRITE_REGISTERS, MODBUS_MAX_WR_READ_REGISTERS); } else if (mapping_address < 0 || (mapping_address + nb) > mb_mapping->nb_registers || - mapping_address < 0 || + mapping_address_write < 0 || (mapping_address_write + nb_write) > mb_mapping->nb_registers) { rsp_length = response_exception( ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE, From 3da2d01916ef118aba30a1951d89e0ca0f90e2f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Sun, 9 Jan 2022 00:17:00 +0100 Subject: [PATCH 021/210] Bump version to 3.1.7 --- NEWS | 26 ++++++++++++++++++++++++++ configure.ac | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index cf92de22f..0e42a6a34 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,29 @@ +libmodbus 3.1.7 (2022-01-09) +============================ + +- modbus_reply: fix copy & paste error in sanity check (fixes #614) +- Add SECURITY.md (closes #613) +- Fix typo in comment (closes #588) +- Replace obsolete AC_PROG_CC_STDC by AC_PROG_CC +- Fix position of CC flags in documentation (closes #599) +- Remove duplicate ';' (closes #602) +- Add the baud rate of 256k for Windows (closes #603) +- cosmetic changes in man page standardizing itemization +- Fix many typos +- Replace .dir-locals.el (Emacs) by .editorconfig +- Include the test LICENSE in tarball +- Install the NEWS and AUTHORS files +- Update README.md +- docs: fix simple typo, reponse -> response +- Add modbus_[get|set]_indication_timeout to doc build +- Fix warning issues +- Move malloc before starting unit tests +- Fixed MODBUS_GET_* macros in case of negative values +- SPDX: change LGPL-2.1+ to LGPL-2.1-or-later + +Thank you to @yegorich, @i-ky, @jobol, @timgates42, @anton-bondarev, Richard +Ash, @stefannilsson, Marc Haber, @qqq89513 and @mhei. + libmodbus 3.1.6 (2019-07-31) ============================ diff --git a/configure.ac b/configure.ac index ca5cada9f..b65d1aedc 100644 --- a/configure.ac +++ b/configure.ac @@ -13,7 +13,7 @@ # m4_define([libmodbus_version_major], [3]) m4_define([libmodbus_version_minor], [1]) -m4_define([libmodbus_version_micro], [6]) +m4_define([libmodbus_version_micro], [7]) m4_define([libmodbus_release_status], [m4_if(m4_eval(libmodbus_version_minor % 2), [1], [snapshot], [release])]) From b546de99c3939438feecab67127b58dd9966418b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Mon, 10 Jan 2022 21:45:49 +0100 Subject: [PATCH 022/210] Minor changes to NEWS --- NEWS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 0e42a6a34..5fa172b26 100644 --- a/NEWS +++ b/NEWS @@ -21,8 +21,8 @@ libmodbus 3.1.7 (2022-01-09) - Fixed MODBUS_GET_* macros in case of negative values - SPDX: change LGPL-2.1+ to LGPL-2.1-or-later -Thank you to @yegorich, @i-ky, @jobol, @timgates42, @anton-bondarev, Richard -Ash, @stefannilsson, Marc Haber, @qqq89513 and @mhei. +Thank you to @yegorich, @i-ky, @jobol, @timgates42, @anton-bondarev, +@richardash1981, @stefannilsson, @Zugschlus, @qqq89513 and @mhei. libmodbus 3.1.6 (2019-07-31) ============================ From fb43eaabb7e71f50a4f709a4dc827ae88cd33cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Tue, 25 Jan 2022 22:58:34 +0100 Subject: [PATCH 023/210] Fix typos (closes #620) --- CONTRIBUTING.md | 4 ++-- ISSUE_TEMPLATE.md | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 346619325..b46f2e44d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ Take care to read the documentation at http://libmodbus.org/documentation/. https://groups.google.com/forum/#!forum/libmodbus or send an email to libmodbus@googlegroups.com -- *Use a clear and decriptive title* for the issue to identify +- *Use a clear and descriptive title* for the issue to identify - *Which version of libmodbus are you using?* you can obtain this information from your package manager or by running `pkg-config --modversion libmodbus`. @@ -19,7 +19,7 @@ You can provide the sha1 of the commit if you have fetched the code with `git`. - *Which operating system are you using?* - *Describe the exact steps which reproduce the problem* in as many details as -possible. For example, the software/equipement which runs the Modbus server, how +possible. For example, the software/equipment which runs the Modbus server, how the clients are connected (TCP, RTU, ASCII) and the source code you are using. - *Enable the debug mode*, libmodbus provides a function to display the content diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index dfd038e86..905288921 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -20,7 +20,7 @@ Please read the following carefully before submitting this new issue. When you get here and you are still convinced that you want to report a bug: -- *Use a clear and decriptive title* for the issue to identify +- *Use a clear and descriptive title* for the issue to identify - *Which version of libmodbus are you using?* you can obtain this information from your package manager or by running `pkg-config --modversion libmodbus`. @@ -29,7 +29,7 @@ When you get here and you are still convinced that you want to report a bug: - *Which operating system are you using?* - *Describe the exact steps which reproduce the problem* in as many details as - possible. For example, the software/equipement which runs the Modbus server, how + possible. For example, the software/equipment which runs the Modbus server, how the clients are connected (TCP, RTU, ASCII) and the source code you are using. - *Enable the debug mode*, libmodbus provides a function to display the content @@ -62,11 +62,11 @@ everything up to and including the following line which starts with ---. <...> -## Expected behaviour +## Expected behavior <...> -## Actual behaviour +## Actual behavior <...> From 36075e265d4db036ef9dfe426eac06ea451b7eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Tue, 25 Jan 2022 23:58:21 +0100 Subject: [PATCH 024/210] Add many contributors to .clabot --- .clabot | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.clabot b/.clabot index 89d3453e2..271d74c36 100644 --- a/.clabot +++ b/.clabot @@ -1,4 +1,18 @@ { - "contributors": ["mhei", "jbysewski"], - "message": "We require contributors to sign our Contributor License Agreement. In order for us to review and merge your code, please fill https://forms.gle/5635zjphDo5JEJQSA to get yourself added." + "contributors": [ + "mhei", + "jbysewski", + "peternewman", + "vvck", + "jiriki86", + "sirsoweird", + "sebastianpsm", + "merkag", + "bobbybelieve", + "ccdmuro", + "ndunks", + "fcntlcc", + "jetforme" + ], + "message": "We require contributors to sign our Contributor License Agreement. In order for us to review and merge your code, please fill https://forms.gle/5635zjphDo5JEJQSA to get added. Your document will be manually checked by the maintainer. Be patient..." } From 00ecdd24cf7c70c20ceda0d5b17d21e0c30d07c2 Mon Sep 17 00:00:00 2001 From: Rick M Date: Sun, 9 Jan 2022 19:34:00 -0800 Subject: [PATCH 025/210] Minor documentation correction Sorry for the two PRs, I did one then realized I should check the other --- doc/modbus_set_response_timeout.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/modbus_set_response_timeout.txt b/doc/modbus_set_response_timeout.txt index 47caf61b0..e99e38c14 100644 --- a/doc/modbus_set_response_timeout.txt +++ b/doc/modbus_set_response_timeout.txt @@ -34,7 +34,7 @@ ERRORS ------ *EINVAL*:: The argument _ctx_ is NULL, or both _to_sec_ and _to_usec_ are zero, or _to_usec_ -is larger than 1000000. +is larger than 999999. EXAMPLE From ffa9e2c063ef34953896e73c191f63b5d9fe94ef Mon Sep 17 00:00:00 2001 From: Rick M Date: Sun, 9 Jan 2022 19:32:03 -0800 Subject: [PATCH 026/210] Minor documentation correction --- doc/modbus_set_byte_timeout.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/modbus_set_byte_timeout.txt b/doc/modbus_set_byte_timeout.txt index 84e73ae72..86c644c1e 100644 --- a/doc/modbus_set_byte_timeout.txt +++ b/doc/modbus_set_byte_timeout.txt @@ -38,7 +38,7 @@ errno. ERRORS ------ *EINVAL*:: -The argument _ctx_ is NULL or _to_usec_ is larger than 1000000. +The argument _ctx_ is NULL or _to_usec_ is larger than 999999. SEE ALSO From a766f17ded0f69681b509093f073f362f75c4bf3 Mon Sep 17 00:00:00 2001 From: begasus Date: Sun, 9 Jan 2022 15:34:02 +0000 Subject: [PATCH 027/210] Fix network library detection on Haiku --- configure.ac | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure.ac b/configure.ac index b65d1aedc..a90a75e6b 100644 --- a/configure.ac +++ b/configure.ac @@ -101,6 +101,9 @@ AC_LIBMODBUS_CHECK_BUILD_DOC # workaround that problem and Cygwin doesn't define MSG_DONTWAIT. AC_CHECK_DECLS([__CYGWIN__]) +# Check for network function in libnetwork for Haiku +AC_SEARCH_LIBS(accept, network socket) + # Checks for library functions. AC_CHECK_FUNCS([accept4 getaddrinfo gettimeofday inet_ntoa select socket strerror strlcpy]) From 976e7d6634808a35e8b8f1ae588a62b6c4bc3cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Wed, 26 Jan 2022 00:13:15 +0100 Subject: [PATCH 028/210] Fix typo on connection (closes #586). Thank you @peternewman. --- doc/modbus_rtu_set_rts.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/modbus_rtu_set_rts.txt b/doc/modbus_rtu_set_rts.txt index 98a231eda..f746ba457 100644 --- a/doc/modbus_rtu_set_rts.txt +++ b/doc/modbus_rtu_set_rts.txt @@ -55,7 +55,7 @@ modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485); modbus_rtu_set_rts(ctx, MODBUS_RTU_RTS_UP); if (modbus_connect(ctx) == -1) { - fprintf(stderr, "Connexion failed: %s\n", modbus_strerror(errno)); + fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); modbus_free(ctx); return -1; } From 7c8dbd29e5a924f3bb157f0d37ff522bd821e0af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Fri, 28 Jan 2022 11:51:14 +0100 Subject: [PATCH 029/210] Add rm5248 to .clabot --- .clabot | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.clabot b/.clabot index 271d74c36..a0572b4fe 100644 --- a/.clabot +++ b/.clabot @@ -12,7 +12,8 @@ "ccdmuro", "ndunks", "fcntlcc", - "jetforme" + "jetforme", + "rm5248" ], "message": "We require contributors to sign our Contributor License Agreement. In order for us to review and merge your code, please fill https://forms.gle/5635zjphDo5JEJQSA to get added. Your document will be manually checked by the maintainer. Be patient..." } From 1a503b7f4ba5ac6c889c2206a48dfae355680de8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Tue, 22 Feb 2022 00:24:05 +0100 Subject: [PATCH 030/210] Fix comment about EMBUNKEXC (closes #566) This native libmodbus error code is defined but not used. --- src/modbus.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modbus.c b/src/modbus.c index e13bd0df7..4c7a33a76 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -332,7 +332,6 @@ static int compute_data_length_after_meta(modbus_t *ctx, uint8_t *msg, and errno is set to one of the values defined below: - ECONNRESET - EMBBADDATA - - EMBUNKEXC - ETIMEDOUT - read() or recv() error codes */ From 49af73debd756be68497a61bd53a07c02673da96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZ=20Lin=20=28=E6=9E=97=E4=B8=8A=E6=99=BA=29?= Date: Thu, 20 Dec 2018 13:35:31 +0800 Subject: [PATCH 031/210] Fix float endianness issue on big endian architecture. It converts float values depending on what order they come in. This patch was modified from rm5248 [1] [1] https://github.com/synexxus/libmodbus/commit/a511768e7fe7ec52d7bae1d9ae04e33f87a59627 --- src/modbus-data.c | 110 ++++++++++++++++++++++++++++++++------- tests/unit-test-client.c | 22 +++++--- tests/unit-test.h.in | 41 +++++++++++++-- 3 files changed, 141 insertions(+), 32 deletions(-) diff --git a/src/modbus-data.c b/src/modbus-data.c index 5b0a6ed60..3c5d1829d 100644 --- a/src/modbus-data.c +++ b/src/modbus-data.c @@ -123,9 +123,18 @@ float modbus_get_float_abcd(const uint16_t *src) { float f; uint32_t i; + uint8_t a, b, c, d; - i = ntohl(((uint32_t)src[0] << 16) + src[1]); - memcpy(&f, &i, sizeof(float)); + a = (src[0] >> 8) & 0xFF; + b = (src[0] >> 0) & 0xFF; + c = (src[1] >> 8) & 0xFF; + d = (src[1] >> 0) & 0xFF; + + i = (a << 24) | + (b << 16) | + (c << 8) | + (d << 0); + memcpy(&f, &i, 4); return f; } @@ -135,9 +144,18 @@ float modbus_get_float_dcba(const uint16_t *src) { float f; uint32_t i; + uint8_t a, b, c, d; - i = ntohl(bswap_32((((uint32_t)src[0]) << 16) + src[1])); - memcpy(&f, &i, sizeof(float)); + a = (src[0] >> 8) & 0xFF; + b = (src[0] >> 0) & 0xFF; + c = (src[1] >> 8) & 0xFF; + d = (src[1] >> 0) & 0xFF; + + i = (d << 24) | + (c << 16) | + (b << 8) | + (a << 0); + memcpy(&f, &i, 4); return f; } @@ -147,9 +165,18 @@ float modbus_get_float_badc(const uint16_t *src) { float f; uint32_t i; + uint8_t a, b, c, d; - i = ntohl((uint32_t)(bswap_16(src[0]) << 16) + bswap_16(src[1])); - memcpy(&f, &i, sizeof(float)); + a = (src[0] >> 8) & 0xFF; + b = (src[0] >> 0) & 0xFF; + c = (src[1] >> 8) & 0xFF; + d = (src[1] >> 0) & 0xFF; + + i = (b << 24) | + (a << 16) | + (d << 8) | + (c << 0); + memcpy(&f, &i, 4); return f; } @@ -159,9 +186,18 @@ float modbus_get_float_cdab(const uint16_t *src) { float f; uint32_t i; + uint8_t a, b, c, d; - i = ntohl((((uint32_t)src[1]) << 16) + src[0]); - memcpy(&f, &i, sizeof(float)); + a = (src[0] >> 8) & 0xFF; + b = (src[0] >> 0) & 0xFF; + c = (src[1] >> 8) & 0xFF; + d = (src[1] >> 0) & 0xFF; + + i = (c << 24) | + (d << 16) | + (a << 8) | + (b << 0); + memcpy(&f, &i, 4); return f; } @@ -176,50 +212,84 @@ float modbus_get_float(const uint16_t *src) memcpy(&f, &i, sizeof(float)); return f; + } /* Set a float to 4 bytes for Modbus w/o any conversion (ABCD) */ void modbus_set_float_abcd(float f, uint16_t *dest) { uint32_t i; + uint8_t *out = (uint8_t*) dest; + uint8_t a, b, c, d; memcpy(&i, &f, sizeof(uint32_t)); - i = htonl(i); - dest[0] = (uint16_t)(i >> 16); - dest[1] = (uint16_t)i; + a = (i >> 24) & 0xFF; + b = (i >> 16) & 0xFF; + c = (i >> 8) & 0xFF; + d = (i >> 0) & 0xFF; + + out[0] = a; + out[1] = b; + out[2] = c; + out[3] = d; } /* Set a float to 4 bytes for Modbus with byte and word swap conversion (DCBA) */ void modbus_set_float_dcba(float f, uint16_t *dest) { uint32_t i; + uint8_t *out = (uint8_t*) dest; + uint8_t a, b, c, d; memcpy(&i, &f, sizeof(uint32_t)); - i = bswap_32(htonl(i)); - dest[0] = (uint16_t)(i >> 16); - dest[1] = (uint16_t)i; + a = (i >> 24) & 0xFF; + b = (i >> 16) & 0xFF; + c = (i >> 8) & 0xFF; + d = (i >> 0) & 0xFF; + + out[0] = d; + out[1] = c; + out[2] = b; + out[3] = a; + } /* Set a float to 4 bytes for Modbus with byte swap conversion (BADC) */ void modbus_set_float_badc(float f, uint16_t *dest) { uint32_t i; + uint8_t *out = (uint8_t*) dest; + uint8_t a, b, c, d; memcpy(&i, &f, sizeof(uint32_t)); - i = htonl(i); - dest[0] = (uint16_t)bswap_16(i >> 16); - dest[1] = (uint16_t)bswap_16(i & 0xFFFF); + a = (i >> 24) & 0xFF; + b = (i >> 16) & 0xFF; + c = (i >> 8) & 0xFF; + d = (i >> 0) & 0xFF; + + out[0] = b; + out[1] = a; + out[2] = d; + out[3] = c; } /* Set a float to 4 bytes for Modbus with word swap conversion (CDAB) */ void modbus_set_float_cdab(float f, uint16_t *dest) { uint32_t i; + uint8_t *out = (uint8_t*) dest; + uint8_t a, b, c, d; memcpy(&i, &f, sizeof(uint32_t)); - i = htonl(i); - dest[0] = (uint16_t)i; - dest[1] = (uint16_t)(i >> 16); + a = (i >> 24) & 0xFF; + b = (i >> 16) & 0xFF; + c = (i >> 8) & 0xFF; + d = (i >> 0) & 0xFF; + + out[0] = c; + out[1] = d; + out[2] = a; + out[3] = b; } /* DEPRECATED - Set a float to 4 bytes in a sort of Modbus format! */ diff --git a/tests/unit-test-client.c b/tests/unit-test-client.c index e66c417dd..9f1c70eb1 100644 --- a/tests/unit-test-client.c +++ b/tests/unit-test-client.c @@ -27,6 +27,7 @@ int send_crafted_request(modbus_t *ctx, int function, uint16_t max_value, uint16_t bytes, int backend_length, int backend_offset); int equal_dword(uint16_t *tab_reg, const uint32_t value); +int is_memory_equal(const void *s1, const void *s2, size_t size); #define BUG_REPORT(_cond, _format, _args ...) \ printf("\nLine %d: assertion error for '%s': " _format "\n", __LINE__, # _cond, ## _args) @@ -40,6 +41,11 @@ int equal_dword(uint16_t *tab_reg, const uint32_t value); } \ }; +int is_memory_equal(const void *s1, const void *s2, size_t size) +{ + return (memcmp(s1, s2, size) == 0); +} + int equal_dword(uint16_t *tab_reg, const uint32_t value) { return ((tab_reg[0] == (value >> 16)) && (tab_reg[1] == (value & 0xFFFF))); } @@ -287,26 +293,26 @@ int main(int argc, char *argv[]) /** FLOAT **/ printf("1/4 Set/get float ABCD: "); modbus_set_float_abcd(UT_REAL, tab_rp_registers); - ASSERT_TRUE(equal_dword(tab_rp_registers, UT_IREAL_ABCD), "FAILED Set float ABCD"); - real = modbus_get_float_abcd(tab_rp_registers); + ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_ABCD_SET, 4), "FAILED Set float ABCD"); + real = modbus_get_float_abcd(UT_IREAL_ABCD_GET); ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); printf("2/4 Set/get float DCBA: "); modbus_set_float_dcba(UT_REAL, tab_rp_registers); - ASSERT_TRUE(equal_dword(tab_rp_registers, UT_IREAL_DCBA), "FAILED Set float DCBA"); - real = modbus_get_float_dcba(tab_rp_registers); + ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_DCBA_SET, 4), "FAILED Set float DCBA"); + real = modbus_get_float_dcba(UT_IREAL_DCBA_GET); ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); printf("3/4 Set/get float BADC: "); modbus_set_float_badc(UT_REAL, tab_rp_registers); - ASSERT_TRUE(equal_dword(tab_rp_registers, UT_IREAL_BADC), "FAILED Set float BADC"); - real = modbus_get_float_badc(tab_rp_registers); + ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_BADC_SET, 4), "FAILED Set float BADC"); + real = modbus_get_float_badc(UT_IREAL_BADC_GET); ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); printf("4/4 Set/get float CDAB: "); modbus_set_float_cdab(UT_REAL, tab_rp_registers); - ASSERT_TRUE(equal_dword(tab_rp_registers, UT_IREAL_CDAB), "FAILED Set float CDAB"); - real = modbus_get_float_cdab(tab_rp_registers); + ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_CDAB_SET, 4), "FAILED Set float CDAB"); + real = modbus_get_float_cdab(UT_IREAL_CDAB_GET); ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); printf("\nAt this point, error messages doesn't mean the test has failed\n"); diff --git a/tests/unit-test.h.in b/tests/unit-test.h.in index dca826f46..4ffa254fb 100644 --- a/tests/unit-test.h.in +++ b/tests/unit-test.h.in @@ -56,12 +56,45 @@ const uint16_t UT_INPUT_REGISTERS_ADDRESS = 0x108; const uint16_t UT_INPUT_REGISTERS_NB = 0x1; const uint16_t UT_INPUT_REGISTERS_TAB[] = { 0x000A }; +/* + * This float value is 0x47F12000 (in big-endian format). + * In Little-endian(intel) format, it will be stored in memory as follows: + * 0x00 0x20 0xF1 0x47 + * + * You can check this with the following code: + + float fl = UT_REAL; + uint8_t *inmem = (uint8_t*)&fl; + int x; + for(x = 0; x < 4; x++){ + printf("0x%02X ", inmem[ x ]); + } + printf("\n"); + */ const float UT_REAL = 123456.00; -const uint32_t UT_IREAL_ABCD = 0x0020F147; -const uint32_t UT_IREAL_DCBA = 0x47F12000; -const uint32_t UT_IREAL_BADC = 0x200047F1; -const uint32_t UT_IREAL_CDAB = 0xF1470020; +/* + * The following arrays assume that 'A' is the MSB, + * and 'D' is the LSB. + * Thus, the following is the case: + * A = 0x47 + * B = 0xF1 + * C = 0x20 + * D = 0x00 + * + * There are two sets of arrays: one to test that the setting is correct, + * the other to test that the getting is correct. + * Note that the 'get' values must be constants in processor-endianness, + * as libmodbus will convert all words to processor-endianness as they come in. + */ +const uint8_t UT_IREAL_ABCD_SET[] = {0x47, 0xF1, 0x20, 0x00}; +const uint16_t UT_IREAL_ABCD_GET[] = {0x47F1, 0x2000}; +const uint8_t UT_IREAL_DCBA_SET[] = {0x00, 0x20, 0xF1, 0x47}; +const uint16_t UT_IREAL_DCBA_GET[] = {0x0020, 0xF147}; +const uint8_t UT_IREAL_BADC_SET[] = {0xF1, 0x47, 0x00, 0x20}; +const uint16_t UT_IREAL_BADC_GET[] = {0xF147, 0x0020}; +const uint8_t UT_IREAL_CDAB_SET[] = {0x20, 0x00, 0x47, 0xF1}; +const uint16_t UT_IREAL_CDAB_GET[] = {0x2000, 0x47F1}; /* const uint32_t UT_IREAL_ABCD = 0x47F12000); const uint32_t UT_IREAL_DCBA = 0x0020F147; From a2e0e4546e23cefaee95545668f8eaa891f88c9e Mon Sep 17 00:00:00 2001 From: Mochamad Arifin Date: Fri, 31 Dec 2021 10:40:50 +0700 Subject: [PATCH 032/210] Make sub-command more clear --- tests/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/README.md b/tests/README.md index df684d41f..39bd813f3 100644 --- a/tests/README.md +++ b/tests/README.md @@ -6,7 +6,7 @@ LICENSE file). After installation, you can use pkg-config to compile these tests. For example, to compile random-test-server run: -gcc random-test-server.c -o random-test-server `pkg-config --libs --cflags libmodbus` +`gcc random-test-server.c -o random-test-server $(pkg-config --libs --cflags libmodbus)` - `random-test-server` is necessary to launch a server before running random-test-client. By default, it receives and replies to Modbus query on the From 27b90deddba7c4d7c5d7d8494f1aec9ef6d69cea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Thu, 26 Oct 2017 12:15:55 +0200 Subject: [PATCH 033/210] Swap CRC bytes in request data but not at CRC computing (#397) --- src/modbus-rtu.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/modbus-rtu.c b/src/modbus-rtu.c index 01e703d46..21d4dc60b 100644 --- a/src/modbus-rtu.c +++ b/src/modbus-rtu.c @@ -137,9 +137,9 @@ static uint16_t crc16(uint8_t *buffer, uint16_t buffer_length) /* pass through message buffer */ while (buffer_length--) { - i = crc_hi ^ *buffer++; /* calculate the CRC */ - crc_hi = crc_lo ^ table_crc_hi[i]; - crc_lo = table_crc_lo[i]; + i = crc_lo ^ *buffer++; /* calculate the CRC */ + crc_lo = crc_hi ^ table_crc_hi[i]; + crc_hi = table_crc_lo[i]; } return (crc_hi << 8 | crc_lo); @@ -155,8 +155,11 @@ static int _modbus_rtu_prepare_response_tid(const uint8_t *req, int *req_length) static int _modbus_rtu_send_msg_pre(uint8_t *req, int req_length) { uint16_t crc = crc16(req, req_length); - req[req_length++] = crc >> 8; + + /* According to the MODBUS specs (p. 14), the low order byte of the CRC comes + * first in the RTU message */ req[req_length++] = crc & 0x00FF; + req[req_length++] = crc >> 8; return req_length; } @@ -374,7 +377,7 @@ static int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg, } crc_calculated = crc16(msg, msg_length - 2); - crc_received = (msg[msg_length - 2] << 8) | msg[msg_length - 1]; + crc_received = (msg[msg_length - 1] << 8) | msg[msg_length - 2]; /* Check CRC of msg */ if (crc_calculated == crc_received) { From cfeca264b4a5bbfdcfe7e2d4ddba9093b88e75f1 Mon Sep 17 00:00:00 2001 From: Pascal Bach Date: Tue, 19 Jul 2022 22:36:41 +0200 Subject: [PATCH 034/210] address check in single register / coil responses added (#463) Address check in single register / coil responses added According to Modbus standard the address of single register / coils request and response must match Co-authored-by: Heinrich Gsponer --- src/modbus.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/modbus.c b/src/modbus.c index 4c7a33a76..69aedf245 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -555,6 +555,8 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req, function < 0x80) { int req_nb_value; int rsp_nb_value; + int resp_addr_ok = TRUE; + int resp_data_ok = TRUE; /* Check function code */ if (function != req[offset]) { @@ -591,6 +593,10 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req, break; case MODBUS_FC_WRITE_MULTIPLE_COILS: case MODBUS_FC_WRITE_MULTIPLE_REGISTERS: + /* address in request and response must be equal */ + if ((req[offset + 1] != rsp[offset + 1]) || (req[offset + 2] != rsp[offset + 2])) { + resp_addr_ok = FALSE; + } /* N Write functions */ req_nb_value = (req[offset + 3] << 8) + req[offset + 4]; rsp_nb_value = (rsp[offset + 3] << 8) | rsp[offset + 4]; @@ -599,17 +605,31 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req, /* Report slave ID (bytes received) */ req_nb_value = rsp_nb_value = rsp[offset + 1]; break; + case MODBUS_FC_WRITE_SINGLE_COIL: + case MODBUS_FC_WRITE_SINGLE_REGISTER: + /* address in request and response must be equal */ + if ((req[offset + 1] != rsp[offset + 1]) || (req[offset + 2] != rsp[offset + 2])) { + resp_addr_ok = FALSE; + } + /* data in request and response must be equal */ + if ((req[offset + 3] != rsp[offset + 3]) || (req[offset + 4] != rsp[offset + 4])) { + resp_data_ok = FALSE; + } + /* 1 Write functions & others */ + req_nb_value = rsp_nb_value = 1; + break; default: /* 1 Write functions & others */ req_nb_value = rsp_nb_value = 1; + break; } - if (req_nb_value == rsp_nb_value) { + if ((req_nb_value == rsp_nb_value) && (resp_addr_ok == TRUE) && (resp_data_ok == TRUE)) { rc = rsp_nb_value; } else { if (ctx->debug) { fprintf(stderr, - "Quantity not corresponding to the request (%d != %d)\n", + "Received data not corresponding to the request (%d != %d)\n", rsp_nb_value, req_nb_value); } From e18875c03ac6df78accc82f761f8139ab67a0849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Fri, 29 Jul 2022 12:22:30 +0200 Subject: [PATCH 035/210] Add contributors to .clabot --- .clabot | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.clabot b/.clabot index a0572b4fe..09795a115 100644 --- a/.clabot +++ b/.clabot @@ -13,7 +13,11 @@ "ndunks", "fcntlcc", "jetforme", - "rm5248" + "rm5248", + "alongl", + "woodsnake", + "taikiakita", + "embeddedmz" ], "message": "We require contributors to sign our Contributor License Agreement. In order for us to review and merge your code, please fill https://forms.gle/5635zjphDo5JEJQSA to get added. Your document will be manually checked by the maintainer. Be patient..." } From d7eeff9b89dbbc9b5e6be490cc5fb86ba31cbdb6 Mon Sep 17 00:00:00 2001 From: along Date: Mon, 7 Mar 2022 18:22:26 +0800 Subject: [PATCH 036/210] VCLinkerTool version 1.0.0 to 1.0, NO link error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even the newest visual studio use /VERSION:major[.minor] . It means link.exe /VERSION:1.0 just need two version number . If Version = 1.0.0 , when visual studio link the program , vs will produce link error: ``` 1>"/OUT:C:\Users\Lenovo\Desktop\libmodbus-master\src\win32\modbus.dll" "/VERSION:1.0.0" /INCREMENTAL:NO /NOLOGO ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /MANIFEST "/MANIFESTUAC:level='asInvoker' uiAccess='false'" /manifest:embed /DEBUG "/PDB:C:\Users\Lenovo\Desktop\libmodbus-master\src\win32\modbus.pdb" /MAP /SUBSYSTEM:CONSOLE /TLBID:1 "/IMPLIB:C:\Users\Lenovo\Desktop\libmodbus-master\src\win32\modbus.lib" /MACHINE:X86 /SAFESEH /DLL "C:\Users\Lenovo\Desktop\libmodbus-master\src\win32\/modbus.res" 1>"Debug\modbus-data.obj" 1>"Debug\modbus-rtu.obj" 1>"Debug\modbus-tcp.obj" 1>Debug\modbus.obj 1>LINK : fatal error LNK1117: option“VERSION:1.0.0” grammatical error ``` see: https://docs.microsoft.com/en-us/cpp/build/reference/version-version-information?view=msvc-160 --- src/win32/modbus.vcproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/win32/modbus.vcproj b/src/win32/modbus.vcproj index 134a252be..a7acb6b5c 100644 --- a/src/win32/modbus.vcproj +++ b/src/win32/modbus.vcproj @@ -78,7 +78,7 @@ Date: Fri, 12 Aug 2022 10:34:12 +0200 Subject: [PATCH 037/210] Add jcarrano to CLA --- .clabot | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.clabot b/.clabot index 09795a115..d53002de8 100644 --- a/.clabot +++ b/.clabot @@ -17,7 +17,8 @@ "alongl", "woodsnake", "taikiakita", - "embeddedmz" + "embeddedmz", + "jcarrano" ], "message": "We require contributors to sign our Contributor License Agreement. In order for us to review and merge your code, please fill https://forms.gle/5635zjphDo5JEJQSA to get added. Your document will be manually checked by the maintainer. Be patient..." } From bba193cf712dc27e9d4f220fb69c6d34a1b7107d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Fri, 12 Aug 2022 10:37:47 +0200 Subject: [PATCH 038/210] Update .gitignore --- .gitignore | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 9e9a23911..b3c7d9632 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +# Temporary files *~ *.swp *.o @@ -7,10 +8,15 @@ *.trs .deps .libs +.DS_Store + +# Emacs GPATH GRTAGS GSYMS GTAGS + +# Generated by Autotools INSTALL Makefile Makefile.in @@ -27,20 +33,25 @@ Makefile.in /missing /libmodbus.pc /stamp-h1 -/*.sublime-* -/.vscode src/modbus-version.h src/win32/modbus.dll.manifest +tests/unit-test.h + +/*.sublime-* +/.vscode + +# Binary tests/bandwidth-client tests/bandwidth-server-many-up tests/bandwidth-server-one tests/random-test-client tests/random-test-server tests/unit-test-client -tests/unit-test.h tests/unit-test-server tests/version tests/stamp-h2 + +# Documentation doc/*.html doc/*.3 doc/*.7 From f7461b99f6ed9178248c41e88fabe257440bd67a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Fri, 12 Aug 2022 10:44:05 +0200 Subject: [PATCH 039/210] Add build check to CI --- .github/workflows/c-cpp.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/c-cpp.yml diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml new file mode 100644 index 000000000..a3b915aa0 --- /dev/null +++ b/.github/workflows/c-cpp.yml @@ -0,0 +1,23 @@ +name: Build libmodbus + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: configure + run: ./configure + - name: make + run: make + - name: make check + run: make check + - name: make distcheck + run: make distcheck From 88af31cb54ad2b3968c65d09d78733693119e1a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Fri, 12 Aug 2022 10:45:56 +0200 Subject: [PATCH 040/210] Add autogen.sh call --- .github/workflows/build.yml | 22 ++++++++++++++++++++++ .github/workflows/c-cpp.yml | 23 ----------------------- 2 files changed, 22 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 .github/workflows/c-cpp.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..dfb86f813 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,22 @@ +name: Build libmodbus + +on: + push: + branches: ["master"] + pull_request: + branches: ["master"] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: configure + run: ./autogen.sh && ./configure + - name: make + run: make + - name: make check + run: make check + - name: make distcheck + run: make distcheck diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml deleted file mode 100644 index a3b915aa0..000000000 --- a/.github/workflows/c-cpp.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Build libmodbus - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: configure - run: ./configure - - name: make - run: make - - name: make check - run: make check - - name: make distcheck - run: make distcheck From a8b9a39200b0b041f6e8e1ef3edabb3e02aed19a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Fri, 12 Aug 2022 10:47:40 +0200 Subject: [PATCH 041/210] Remove check target (duplicate of distcheck) --- .github/workflows/build.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dfb86f813..77d7b3531 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,5 @@ jobs: run: ./autogen.sh && ./configure - name: make run: make - - name: make check - run: make check - name: make distcheck run: make distcheck From be00a9c9f8a188e8adabcc667d5250eb5f6506ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Fri, 12 Aug 2022 10:54:18 +0200 Subject: [PATCH 042/210] Replace Travis CI badge by GitHub CI --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a0902923b..56bbf1ab3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ A groovy modbus library ======================= -[![Build Status](https://travis-ci.org/stephane/libmodbus.svg?branch=master)](https://travis-ci.org/stephane/libmodbus) +![Build Status](https://github.com/stephane/libmodbus/actions/workflows/build.yml/badge.svg) Overview -------- From 06dc37ff68311fba97497f735db5984ac24a0094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Mon, 15 Aug 2022 20:25:45 +0200 Subject: [PATCH 043/210] Merge NEWS with v3.0.x branch --- NEWS | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/NEWS b/NEWS index 5fa172b26..3f8a97dcd 100644 --- a/NEWS +++ b/NEWS @@ -272,6 +272,31 @@ RTS flow control. - RTS flow control with modbus_rtu_set_rts and modbus_rtu_get_rts functions by Torello Querci and Stéphane Raimbault. +libmodbus 3.0.8 (2019-07-31) +============================ + +- Fix awful typo in fix for VD-1301 vulnerability. + Thank you @karlp. + +libmodbus 3.0.7 (2019-07-29) +============================ + +- Backport fixes for VD-1301 and VD-1302 vulnerabilities +- Move WINVER definition before other includes (closes #350) +- Replace signed int by unsigned + +libmodbus 3.0.6 (2014-02-21) +============================ + +- Backport fix remote buffer overflow vulnerability on write requests +- Replace deprecated INCLUDES by AM_CPPFLAGS + +libmodbus 3.0.5 (2013-10-06) +============================ + +- Fix remote buffer overflow vulnerability +- Fix receiving of incorrect queries in write_single + libmodbus 3.0.4 (2012-05-08) ============================ From db1cbc593501590eef16bbf9e0746290ac9116b9 Mon Sep 17 00:00:00 2001 From: Mohamed Amine Mzoughi Date: Mon, 4 Jul 2022 16:13:04 +0200 Subject: [PATCH 044/210] Fixed MODBUS_ERROR_RECOVERY_LINK not working on Windows. --- src/modbus.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/src/modbus.c b/src/modbus.c index 69aedf245..3b7c322eb 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -182,7 +182,21 @@ static int send_msg(modbus_t *ctx, uint8_t *msg, int msg_length) rc = ctx->backend->send(ctx, msg, msg_length); if (rc == -1) { _error_print(ctx, NULL); - if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) { + if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK && + ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_TCP) { +#ifdef _WIN32 + const int wsa_err = WSAGetLastError(); + if (wsa_err == WSAENETRESET || wsa_err == WSAENOTCONN || wsa_err == WSAENOTSOCK || + wsa_err == WSAESHUTDOWN || wsa_err == WSAEHOSTUNREACH || wsa_err == WSAECONNABORTED || + wsa_err == WSAECONNRESET || wsa_err == WSAETIMEDOUT) { + modbus_close(ctx); + _sleep_response_timeout(ctx); + modbus_connect(ctx); + } else { + _sleep_response_timeout(ctx); + modbus_flush(ctx); + } +#else int saved_errno = errno; if ((errno == EBADF || errno == ECONNRESET || errno == EPIPE)) { @@ -194,6 +208,7 @@ static int send_msg(modbus_t *ctx, uint8_t *msg, int msg_length) modbus_flush(ctx); } errno = saved_errno; +#endif } } } while ((ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) && @@ -345,6 +360,9 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type) int length_to_read; int msg_length = 0; _step_t step; +#ifdef _WIN32 + int wsa_err; +#endif if (ctx->debug) { if (msg_type == MSG_INDICATION) { @@ -386,7 +404,17 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type) rc = ctx->backend->select(ctx, &rset, p_tv, length_to_read); if (rc == -1) { _error_print(ctx, "select"); - if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) { + if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK && + ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_TCP) { +#ifdef _WIN32 + wsa_err = WSAGetLastError(); + + // no equivalent to ETIMEDOUT when select fails on Windows + if (wsa_err == WSAENETDOWN || wsa_err == WSAENOTSOCK) { + modbus_close(ctx); + modbus_connect(ctx); + } +#else int saved_errno = errno; if (errno == ETIMEDOUT) { @@ -397,6 +425,7 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type) modbus_connect(ctx); } errno = saved_errno; +#endif } return -1; } @@ -409,7 +438,19 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type) if (rc == -1) { _error_print(ctx, "read"); +#ifdef _WIN32 + wsa_err = WSAGetLastError(); if ((ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) && + (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_TCP) && + (wsa_err == WSAENOTCONN || wsa_err == WSAENETRESET || wsa_err == WSAENOTSOCK || + wsa_err == WSAESHUTDOWN || wsa_err == WSAECONNABORTED || wsa_err == WSAETIMEDOUT || + wsa_err == WSAECONNRESET)) { + modbus_close(ctx); + modbus_connect(ctx); + } +#else + if ((ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) && + (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_TCP) && (errno == ECONNRESET || errno == ECONNREFUSED || errno == EBADF)) { int saved_errno = errno; @@ -418,6 +459,7 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type) /* Could be removed by previous calls */ errno = saved_errno; } +#endif return -1; } From c0ee9dfe43055856c062fd466e26d14fd7fa5529 Mon Sep 17 00:00:00 2001 From: Mohamed Amine Mzoughi Date: Mon, 4 Jul 2022 16:48:37 +0200 Subject: [PATCH 045/210] Fixed a typo error in a comment. --- src/modbus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modbus.c b/src/modbus.c index 3b7c322eb..e173dc587 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -1467,7 +1467,7 @@ int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint1 int rc; int req_length; /* The request length can not exceed _MIN_REQ_LENGTH - 2 and 4 bytes to - * store the masks. The ugly subtraction is there to remove the 'nb' value + * store the masks. The ugly substraction is there to remove the 'nb' value * (2 bytes) which is not used. */ uint8_t req[_MIN_REQ_LENGTH + 2]; From 9b679b7c3b267f7a5d87e26b8f94138edd76f1d9 Mon Sep 17 00:00:00 2001 From: Mohamed Amine Mzoughi Date: Wed, 3 Aug 2022 10:46:33 +0200 Subject: [PATCH 046/210] Do not change the behavior for non-windows OS. --- src/modbus.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/modbus.c b/src/modbus.c index e173dc587..a18b1a8ea 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -182,8 +182,7 @@ static int send_msg(modbus_t *ctx, uint8_t *msg, int msg_length) rc = ctx->backend->send(ctx, msg, msg_length); if (rc == -1) { _error_print(ctx, NULL); - if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK && - ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_TCP) { + if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) { #ifdef _WIN32 const int wsa_err = WSAGetLastError(); if (wsa_err == WSAENETRESET || wsa_err == WSAENOTCONN || wsa_err == WSAENOTSOCK || @@ -404,8 +403,7 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type) rc = ctx->backend->select(ctx, &rset, p_tv, length_to_read); if (rc == -1) { _error_print(ctx, "select"); - if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK && - ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_TCP) { + if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) { #ifdef _WIN32 wsa_err = WSAGetLastError(); From ef3c4bc989a4774402b89880d1afd54b1b6a1b67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Wed, 17 Aug 2022 16:31:30 +0200 Subject: [PATCH 047/210] Reduce memory use of TCP PI backend (closes #621) - allocate exact memory required to store node and service strings instead of around 1kb of static memory. - accept NULL value of service to use default Modbus port number (502) - unit test updated The new documentation will be updated in another commit. --- src/modbus-tcp-private.h | 7 ++--- src/modbus-tcp.c | 68 +++++++++++++++++++--------------------- tests/unit-test-client.c | 3 -- 3 files changed, 34 insertions(+), 44 deletions(-) diff --git a/src/modbus-tcp-private.h b/src/modbus-tcp-private.h index 164c8c727..698f0e8b8 100644 --- a/src/modbus-tcp-private.h +++ b/src/modbus-tcp-private.h @@ -27,18 +27,15 @@ typedef struct _modbus_tcp { char ip[16]; } modbus_tcp_t; -#define _MODBUS_TCP_PI_NODE_LENGTH 1025 -#define _MODBUS_TCP_PI_SERVICE_LENGTH 32 - typedef struct _modbus_tcp_pi { /* Transaction ID */ uint16_t t_id; /* TCP port */ int port; /* Node */ - char node[_MODBUS_TCP_PI_NODE_LENGTH]; + char *node; /* Service */ - char service[_MODBUS_TCP_PI_SERVICE_LENGTH]; + char *service; } modbus_tcp_pi_t; #endif /* MODBUS_TCP_PRIVATE_H */ diff --git a/src/modbus-tcp.c b/src/modbus-tcp.c index 516427370..70df8b6c4 100644 --- a/src/modbus-tcp.c +++ b/src/modbus-tcp.c @@ -740,7 +740,20 @@ static int _modbus_tcp_select(modbus_t *ctx, fd_set *rset, struct timeval *tv, i } static void _modbus_tcp_free(modbus_t *ctx) { - free(ctx->backend_data); + if (ctx->backend_data) { + free(ctx->backend_data); + } + free(ctx); +} + +static void _modbus_tcp_pi_free(modbus_t *ctx) { + if (ctx->backend_data) { + modbus_tcp_pi_t *ctx_tcp_pi = ctx->backend_data; + free(ctx_tcp_pi->node); + free(ctx_tcp_pi->service); + free(ctx->backend_data); + } + free(ctx); } @@ -786,7 +799,7 @@ const modbus_backend_t _modbus_tcp_pi_backend = { _modbus_tcp_close, _modbus_tcp_flush, _modbus_tcp_select, - _modbus_tcp_free + _modbus_tcp_pi_free }; modbus_t* modbus_new_tcp(const char *ip, int port) @@ -858,8 +871,6 @@ modbus_t* modbus_new_tcp_pi(const char *node, const char *service) { modbus_t *ctx; modbus_tcp_pi_t *ctx_tcp_pi; - size_t dest_size; - size_t ret_size; ctx = (modbus_t *)malloc(sizeof(modbus_t)); if (ctx == NULL) { @@ -879,47 +890,32 @@ modbus_t* modbus_new_tcp_pi(const char *node, const char *service) return NULL; } ctx_tcp_pi = (modbus_tcp_pi_t *)ctx->backend_data; + ctx_tcp_pi->node = NULL; + ctx_tcp_pi->service = NULL; - if (node == NULL) { - /* The node argument can be empty to indicate any hosts */ - ctx_tcp_pi->node[0] = 0; - } else { - dest_size = sizeof(char) * _MODBUS_TCP_PI_NODE_LENGTH; - ret_size = strlcpy(ctx_tcp_pi->node, node, dest_size); - if (ret_size == 0) { - fprintf(stderr, "The node string is empty\n"); - modbus_free(ctx); - errno = EINVAL; - return NULL; - } - - if (ret_size >= dest_size) { - fprintf(stderr, "The node string has been truncated\n"); - modbus_free(ctx); - errno = EINVAL; - return NULL; - } - } - - if (service != NULL) { - dest_size = sizeof(char) * _MODBUS_TCP_PI_SERVICE_LENGTH; - ret_size = strlcpy(ctx_tcp_pi->service, service, dest_size); + if (node != NULL) { + ctx_tcp_pi->node = strdup(node); } else { - /* Empty service is not allowed, error caught below. */ - ret_size = 0; + /* The node argument can be empty to indicate any hosts */ + ctx_tcp_pi->node = strdup(""); } - if (ret_size == 0) { - fprintf(stderr, "The service string is empty\n"); + if (ctx_tcp_pi->node == NULL) { modbus_free(ctx); - errno = EINVAL; + errno = ENOMEM; return NULL; } - if (ret_size >= dest_size) { - fprintf(stderr, "The service string has been truncated\n"); + if (service != NULL && service[0] != '\0') { + ctx_tcp_pi->service = strdup(service); + } else { + /* Default Modbus port number */ + ctx_tcp_pi->service = strdup("502"); + } + + if (ctx_tcp_pi->service == NULL) { modbus_free(ctx); - errno = EINVAL; + errno = ENOMEM; return NULL; } diff --git a/tests/unit-test-client.c b/tests/unit-test-client.c index 9f1c70eb1..4c6b416d1 100644 --- a/tests/unit-test-client.c +++ b/tests/unit-test-client.c @@ -681,9 +681,6 @@ int main(int argc, char *argv[]) ctx = modbus_new_rtu("/dev/dummy", 0, 'A', 0, 0); ASSERT_TRUE(ctx == NULL && errno == EINVAL, ""); - ctx = modbus_new_tcp_pi(NULL, NULL); - ASSERT_TRUE(ctx == NULL && errno == EINVAL, ""); - printf("\nALL TESTS PASS WITH SUCCESS.\n"); success = TRUE; From da87669697ae0e4397356665dafd151bb6d4795a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Wed, 17 Aug 2022 18:00:04 +0200 Subject: [PATCH 048/210] Rewrite documentation with Material for mkdocs - remove doc build from autotools - don't depend anymore on asciidoc - don't provide man pages anymore - new illustrations - provide mkdocs instructions --- .gitignore | 20 +- CONTRIBUTING.md | 4 +- ISSUE_TEMPLATE.md | 2 +- Makefile.am | 2 +- README.md | 44 +- acinclude.m4 | 29 - configure.ac | 5 - doc/Makefile.am | 88 --- doc/asciidoc.conf | 51 -- doc/libmodbus.txt | 279 -------- doc/modbus_free.txt | 33 - doc/modbus_get_byte_from_bits.txt | 35 - doc/modbus_get_byte_timeout.txt | 50 -- doc/modbus_get_float.txt | 39 -- doc/modbus_get_float_abcd.txt | 39 -- doc/modbus_get_float_badc.txt | 39 -- doc/modbus_get_float_cdab.txt | 39 -- doc/modbus_get_float_dcba.txt | 39 -- doc/modbus_get_header_length.txt | 35 - doc/modbus_get_indication_timeout.txt | 53 -- doc/modbus_get_response_timeout.txt | 52 -- doc/modbus_get_slave.txt | 41 -- doc/modbus_get_socket.txt | 35 - doc/modbus_mapping_free.txt | 34 - doc/modbus_mapping_new.txt | 69 -- doc/modbus_mapping_new_start_address.txt | 81 --- doc/modbus_mask_write_register.txt | 41 -- doc/modbus_new_rtu.txt | 91 --- doc/modbus_new_tcp_pi.txt | 77 --- doc/modbus_read_bits.txt | 48 -- doc/modbus_read_input_bits.txt | 47 -- doc/modbus_read_input_registers.txt | 51 -- doc/modbus_receive.txt | 42 -- doc/modbus_reply.txt | 52 -- doc/modbus_reply_exception.txt | 57 -- doc/modbus_report_slave_id.txt | 61 -- doc/modbus_rtu_get_rts.txt | 47 -- doc/modbus_rtu_get_rts_delay.txt | 46 -- doc/modbus_rtu_get_serial_mode.txt | 53 -- doc/modbus_rtu_set_custom_rts.txt | 45 -- doc/modbus_rtu_set_rts_delay.txt | 46 -- doc/modbus_rtu_set_serial_mode.txt | 56 -- doc/modbus_set_bits_from_byte.txt | 36 -- doc/modbus_set_bits_from_bytes.txt | 36 -- doc/modbus_set_float.txt | 36 -- doc/modbus_set_float_abcd.txt | 38 -- doc/modbus_set_float_badc.txt | 38 -- doc/modbus_set_float_cdab.txt | 39 -- doc/modbus_set_float_dcba.txt | 37 -- doc/modbus_set_indication_timeout.txt | 48 -- doc/modbus_write_and_read_registers.txt | 51 -- doc/modbus_write_bit.txt | 38 -- doc/modbus_write_bits.txt | 45 -- doc/modbus_write_register.txt | 38 -- doc/modbus_write_registers.txt | 38 -- docs/assets/client-sensors.excalidraw | 606 ++++++++++++++++++ docs/assets/client-sensors.webp | Bin 0 -> 36052 bytes docs/assets/server-grafana.excalidraw | 488 ++++++++++++++ docs/assets/server-grafana.webp | Bin 0 -> 37256 bytes docs/index.md | 263 ++++++++ doc/modbus_close.txt => docs/modbus_close.md | 37 +- .../modbus_connect.md | 38 +- doc/modbus_flush.txt => docs/modbus_flush.md | 25 +- docs/modbus_free.md | 19 + docs/modbus_get_byte_from_bits.md | 26 + docs/modbus_get_byte_timeout.md | 38 ++ docs/modbus_get_float.md | 31 + docs/modbus_get_float_abcd.md | 29 + docs/modbus_get_float_badc.md | 29 + docs/modbus_get_float_cdab.md | 29 + docs/modbus_get_float_dcba.md | 29 + docs/modbus_get_header_length.md | 21 + docs/modbus_get_indication_timeout.md | 39 ++ docs/modbus_get_response_timeout.md | 40 ++ docs/modbus_get_slave.md | 29 + docs/modbus_get_socket.md | 25 + docs/modbus_mapping_free.md | 24 + docs/modbus_mapping_new.md | 60 ++ docs/modbus_mapping_new_start_address.md | 85 +++ docs/modbus_mask_write_register.md | 30 + docs/modbus_new_rtu.md | 77 +++ .../modbus_new_tcp.md | 55 +- docs/modbus_new_tcp_pi.md | 62 ++ docs/modbus_read_bits.md | 36 ++ docs/modbus_read_input_bits.md | 35 + docs/modbus_read_input_registers.md | 39 ++ .../modbus_read_registers.md | 56 +- docs/modbus_receive.md | 32 + .../modbus_receive_confirmation.md | 50 +- docs/modbus_reply.md | 39 ++ docs/modbus_reply_exception.md | 45 ++ docs/modbus_report_slave_id.md | 52 ++ docs/modbus_rtu_get_rts.md | 35 + docs/modbus_rtu_get_rts_delay.md | 31 + docs/modbus_rtu_get_serial_mode.md | 41 ++ docs/modbus_rtu_set_custom_rts.md | 32 + .../modbus_rtu_set_rts.md | 46 +- docs/modbus_rtu_set_rts_delay.md | 31 + docs/modbus_rtu_set_serial_mode.md | 43 ++ .../modbus_send_raw_request.md | 41 +- docs/modbus_set_bits_from_byte.md | 27 + docs/modbus_set_bits_from_bytes.md | 26 + .../modbus_set_byte_timeout.md | 45 +- .../modbus_set_debug.md | 35 +- .../modbus_set_error_recovery.md | 47 +- docs/modbus_set_float.md | 29 + docs/modbus_set_float_abcd.md | 28 + docs/modbus_set_float_badc.md | 28 + docs/modbus_set_float_cdab.md | 29 + docs/modbus_set_float_dcba.md | 27 + docs/modbus_set_indication_timeout.md | 36 ++ .../modbus_set_response_timeout.md | 52 +- .../modbus_set_slave.md | 42 +- .../modbus_set_socket.md | 37 +- .../modbus_strerror.md | 45 +- .../modbus_tcp_accept.md | 48 +- .../modbus_tcp_listen.md | 50 +- .../modbus_tcp_pi_accept.md | 48 +- .../modbus_tcp_pi_listen.md | 48 +- docs/modbus_write_and_read_registers.md | 43 ++ docs/modbus_write_bit.md | 28 + docs/modbus_write_bits.md | 33 + docs/modbus_write_register.md | 28 + docs/modbus_write_registers.md | 28 + mkdocs.yml | 17 + 125 files changed, 3317 insertions(+), 3120 deletions(-) delete mode 100644 acinclude.m4 delete mode 100644 doc/Makefile.am delete mode 100644 doc/asciidoc.conf delete mode 100644 doc/libmodbus.txt delete mode 100644 doc/modbus_free.txt delete mode 100644 doc/modbus_get_byte_from_bits.txt delete mode 100644 doc/modbus_get_byte_timeout.txt delete mode 100644 doc/modbus_get_float.txt delete mode 100644 doc/modbus_get_float_abcd.txt delete mode 100644 doc/modbus_get_float_badc.txt delete mode 100644 doc/modbus_get_float_cdab.txt delete mode 100644 doc/modbus_get_float_dcba.txt delete mode 100644 doc/modbus_get_header_length.txt delete mode 100644 doc/modbus_get_indication_timeout.txt delete mode 100644 doc/modbus_get_response_timeout.txt delete mode 100644 doc/modbus_get_slave.txt delete mode 100644 doc/modbus_get_socket.txt delete mode 100644 doc/modbus_mapping_free.txt delete mode 100644 doc/modbus_mapping_new.txt delete mode 100644 doc/modbus_mapping_new_start_address.txt delete mode 100644 doc/modbus_mask_write_register.txt delete mode 100644 doc/modbus_new_rtu.txt delete mode 100644 doc/modbus_new_tcp_pi.txt delete mode 100644 doc/modbus_read_bits.txt delete mode 100644 doc/modbus_read_input_bits.txt delete mode 100644 doc/modbus_read_input_registers.txt delete mode 100644 doc/modbus_receive.txt delete mode 100644 doc/modbus_reply.txt delete mode 100644 doc/modbus_reply_exception.txt delete mode 100644 doc/modbus_report_slave_id.txt delete mode 100644 doc/modbus_rtu_get_rts.txt delete mode 100644 doc/modbus_rtu_get_rts_delay.txt delete mode 100644 doc/modbus_rtu_get_serial_mode.txt delete mode 100644 doc/modbus_rtu_set_custom_rts.txt delete mode 100644 doc/modbus_rtu_set_rts_delay.txt delete mode 100644 doc/modbus_rtu_set_serial_mode.txt delete mode 100644 doc/modbus_set_bits_from_byte.txt delete mode 100644 doc/modbus_set_bits_from_bytes.txt delete mode 100644 doc/modbus_set_float.txt delete mode 100644 doc/modbus_set_float_abcd.txt delete mode 100644 doc/modbus_set_float_badc.txt delete mode 100644 doc/modbus_set_float_cdab.txt delete mode 100644 doc/modbus_set_float_dcba.txt delete mode 100644 doc/modbus_set_indication_timeout.txt delete mode 100644 doc/modbus_write_and_read_registers.txt delete mode 100644 doc/modbus_write_bit.txt delete mode 100644 doc/modbus_write_bits.txt delete mode 100644 doc/modbus_write_register.txt delete mode 100644 doc/modbus_write_registers.txt create mode 100644 docs/assets/client-sensors.excalidraw create mode 100644 docs/assets/client-sensors.webp create mode 100644 docs/assets/server-grafana.excalidraw create mode 100644 docs/assets/server-grafana.webp create mode 100644 docs/index.md rename doc/modbus_close.txt => docs/modbus_close.md (53%) rename doc/modbus_connect.txt => docs/modbus_connect.md (61%) rename doc/modbus_flush.txt => docs/modbus_flush.md (54%) create mode 100644 docs/modbus_free.md create mode 100644 docs/modbus_get_byte_from_bits.md create mode 100644 docs/modbus_get_byte_timeout.md create mode 100644 docs/modbus_get_float.md create mode 100644 docs/modbus_get_float_abcd.md create mode 100644 docs/modbus_get_float_badc.md create mode 100644 docs/modbus_get_float_cdab.md create mode 100644 docs/modbus_get_float_dcba.md create mode 100644 docs/modbus_get_header_length.md create mode 100644 docs/modbus_get_indication_timeout.md create mode 100644 docs/modbus_get_response_timeout.md create mode 100644 docs/modbus_get_slave.md create mode 100644 docs/modbus_get_socket.md create mode 100644 docs/modbus_mapping_free.md create mode 100644 docs/modbus_mapping_new.md create mode 100644 docs/modbus_mapping_new_start_address.md create mode 100644 docs/modbus_mask_write_register.md create mode 100644 docs/modbus_new_rtu.md rename doc/modbus_new_tcp.txt => docs/modbus_new_tcp.md (57%) create mode 100644 docs/modbus_new_tcp_pi.md create mode 100644 docs/modbus_read_bits.md create mode 100644 docs/modbus_read_input_bits.md create mode 100644 docs/modbus_read_input_registers.md rename doc/modbus_read_registers.txt => docs/modbus_read_registers.md (55%) create mode 100644 docs/modbus_receive.md rename doc/modbus_receive_confirmation.txt => docs/modbus_receive_confirmation.md (51%) create mode 100644 docs/modbus_reply.md create mode 100644 docs/modbus_reply_exception.md create mode 100644 docs/modbus_report_slave_id.md create mode 100644 docs/modbus_rtu_get_rts.md create mode 100644 docs/modbus_rtu_get_rts_delay.md create mode 100644 docs/modbus_rtu_get_serial_mode.md create mode 100644 docs/modbus_rtu_set_custom_rts.md rename doc/modbus_rtu_set_rts.txt => docs/modbus_rtu_set_rts.md (73%) create mode 100644 docs/modbus_rtu_set_rts_delay.md create mode 100644 docs/modbus_rtu_set_serial_mode.md rename doc/modbus_send_raw_request.txt => docs/modbus_send_raw_request.md (68%) create mode 100644 docs/modbus_set_bits_from_byte.md create mode 100644 docs/modbus_set_bits_from_bytes.md rename doc/modbus_set_byte_timeout.txt => docs/modbus_set_byte_timeout.md (54%) rename doc/modbus_set_debug.txt => docs/modbus_set_debug.md (51%) rename doc/modbus_set_error_recovery.txt => docs/modbus_set_error_recovery.md (70%) create mode 100644 docs/modbus_set_float.md create mode 100644 docs/modbus_set_float_abcd.md create mode 100644 docs/modbus_set_float_badc.md create mode 100644 docs/modbus_set_float_cdab.md create mode 100644 docs/modbus_set_float_dcba.md create mode 100644 docs/modbus_set_indication_timeout.md rename doc/modbus_set_response_timeout.txt => docs/modbus_set_response_timeout.md (53%) rename doc/modbus_set_slave.txt => docs/modbus_set_slave.md (78%) rename doc/modbus_set_socket.txt => docs/modbus_set_socket.md (63%) rename doc/modbus_strerror.txt => docs/modbus_strerror.md (50%) rename doc/modbus_tcp_accept.txt => docs/modbus_tcp_accept.md (50%) rename doc/modbus_tcp_listen.txt => docs/modbus_tcp_listen.md (61%) rename doc/modbus_tcp_pi_accept.txt => docs/modbus_tcp_pi_accept.md (50%) rename doc/modbus_tcp_pi_listen.txt => docs/modbus_tcp_pi_listen.md (55%) create mode 100644 docs/modbus_write_and_read_registers.md create mode 100644 docs/modbus_write_bit.md create mode 100644 docs/modbus_write_bits.md create mode 100644 docs/modbus_write_register.md create mode 100644 docs/modbus_write_registers.md create mode 100644 mkdocs.yml diff --git a/.gitignore b/.gitignore index b3c7d9632..8c04291ec 100644 --- a/.gitignore +++ b/.gitignore @@ -10,11 +10,10 @@ .libs .DS_Store -# Emacs -GPATH -GRTAGS -GSYMS -GTAGS +# Editors +/*.sublime-* +/.vscode +/.venv # Generated by Autotools INSTALL @@ -28,17 +27,17 @@ Makefile.in /configure.scan /depcomp /install-sh +/libmodbus.pc /libtool /ltmain.sh /missing -/libmodbus.pc /stamp-h1 src/modbus-version.h src/win32/modbus.dll.manifest tests/unit-test.h -/*.sublime-* -/.vscode +# mkdocs +/site # Binary tests/bandwidth-client @@ -50,8 +49,3 @@ tests/unit-test-client tests/unit-test-server tests/version tests/stamp-h2 - -# Documentation -doc/*.html -doc/*.3 -doc/*.7 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b46f2e44d..7495eadaf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ How Do I Submit A Good Bug Report? Please, don't send direct emails to Stéphane Raimbault unless you want commercial support. -Take care to read the documentation at http://libmodbus.org/documentation/. +Take care to read the documentation at http://libmodbus.org/. - *Be sure it's a bug before creating an issue*, in doubt, post a message on https://groups.google.com/forum/#!forum/libmodbus or send an email to @@ -24,6 +24,6 @@ the clients are connected (TCP, RTU, ASCII) and the source code you are using. - *Enable the debug mode*, libmodbus provides a function to display the content of the Modbus messages and it's very convenient to analyze issues -(http://libmodbus.org/docs/latest/modbus_set_debug.html). +(http://libmodbus.org/docs/modbus_set_debug/). Good bug reports provide right and quick fixes! diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 905288921..ad341470b 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -34,7 +34,7 @@ When you get here and you are still convinced that you want to report a bug: - *Enable the debug mode*, libmodbus provides a function to display the content of the Modbus messages and it's very convenient to analyze issues - (). + (). Good bug reports provide right and quick fixes! diff --git a/Makefile.am b/Makefile.am index cc1482d4f..202135e53 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,7 +9,7 @@ CLEANFILES += libmodbus.pc dist_doc_DATA = MIGRATION README.md AUTHORS NEWS -SUBDIRS = src doc +SUBDIRS = src if BUILD_TESTS SUBDIRS += tests diff --git a/README.md b/README.md index 56bbf1ab3..89f0c19db 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,8 @@ -A groovy modbus library -======================= +# A groovy modbus library ![Build Status](https://github.com/stephane/libmodbus/actions/workflows/build.yml/badge.svg) -Overview --------- +## Overview libmodbus is a free software library to send/receive data with a device which respects the Modbus protocol. This library can use a serial port or an Ethernet @@ -15,21 +13,15 @@ Protocol Reference Guide which can be obtained from [www.modbus.org](http://www. The license of libmodbus is *LGPL v2.1 or later*. -The documentation is available as manual pages (`man libmodbus` to read general -description and list of available functions) or Web pages -[www.libmodbus.org/documentation/](http://libmodbus.org/documentation/). The -documentation is licensed under the Creative Commons Attribution-ShareAlike -License 3.0 (Unported) (). - -The official website is [www.libmodbus.org](http://www.libmodbus.org). +The official website is [www.libmodbus.org](http://www.libmodbus.org). The +website contains the latest version of the documentation. The library is written in C and designed to run on Linux, Mac OS X, FreeBSD, Embox, QNX and Windows. You can use the library on MCUs with Embox RTOS. -Installation ------------- +## Installation You will only need to install automake, autoconf, libtool and a C compiler (gcc or clang) to compile the library and asciidoc and xmlto to generate the @@ -59,19 +51,7 @@ automake libtool`. To build under Embox, you have to use its build system. -Documentation -------------- - -The documentation is available [online](http://libmodbus.org/documentation) or -as manual pages after installation. - -The documentation is based on -[AsciiDoc](http://www.methods.co.nz/asciidoc/). Only man pages are built -by default with `make` command, you can run `make htmldoc` in *doc* directory -to generate HTML files. - -Testing -------- +## Testing Some tests are provided in *tests* directory, you can freely edit the source code to fit your needs (it's Free Software :). @@ -87,7 +67,15 @@ By default, all TCP unit tests will be executed (see --help for options). It's also possible to run the unit tests with `make check`. -To report a bug or to contribute --------------------------------- +## To report a bug or to contribute See [CONTRIBUTING](CONTRIBUTING.md) document. + +## Documentation + +You can serve the local documentation with: + +```shell +pip install mkdocs-material +mkdocs serve +``` diff --git a/acinclude.m4 b/acinclude.m4 deleted file mode 100644 index 47c797135..000000000 --- a/acinclude.m4 +++ /dev/null @@ -1,29 +0,0 @@ -dnl ############################################################################## -dnl # AC_LIBMODBUS_CHECK_BUILD_DOC # -dnl # Check whether to build documentation and install man-pages # -dnl ############################################################################## -AC_DEFUN([AC_LIBMODBUS_CHECK_BUILD_DOC], [{ - # Allow user to disable doc build - AC_ARG_WITH([documentation], [AS_HELP_STRING([--without-documentation], - [disable documentation build even if asciidoc and xmlto are present [default=no]])]) - - if test "x$with_documentation" = "xno"; then - ac_libmodbus_build_doc="no" - else - # Determine whether or not documentation should be built and installed. - ac_libmodbus_build_doc="yes" - # Check for asciidoc and xmlto and don't build the docs if these are not installed. - AC_CHECK_PROG(ac_libmodbus_have_asciidoc, asciidoc, yes, no) - AC_CHECK_PROG(ac_libmodbus_have_xmlto, xmlto, yes, no) - if test "x$ac_libmodbus_have_asciidoc" = "xno" -o "x$ac_libmodbus_have_xmlto" = "xno"; then - ac_libmodbus_build_doc="no" - fi - fi - - AC_MSG_CHECKING([whether to build documentation]) - AC_MSG_RESULT([$ac_libmodbus_build_doc]) - if test "x$ac_libmodbus_build_doc" = "xno"; then - AC_MSG_WARN([The tools to build the documentation aren't installed]) - fi - AM_CONDITIONAL(BUILD_DOC, test "x$ac_libmodbus_build_doc" = "xyes") -}]) diff --git a/configure.ac b/configure.ac index a90a75e6b..ad26abd24 100644 --- a/configure.ac +++ b/configure.ac @@ -94,9 +94,6 @@ AC_CHECK_HEADERS([ \ unistd.h \ ]) -# Check whether to build docs / install man pages -AC_LIBMODBUS_CHECK_BUILD_DOC - # Cygwin defines IPTOS_LOWDELAY but can't handle that flag so it's necessary to # workaround that problem and Cygwin doesn't define MSG_DONTWAIT. AC_CHECK_DECLS([__CYGWIN__]) @@ -161,7 +158,6 @@ AC_CONFIG_FILES([ src/modbus-version.h src/win32/modbus.dll.manifest tests/Makefile - doc/Makefile libmodbus.pc ]) @@ -179,6 +175,5 @@ AC_MSG_RESULT([ cflags: ${CFLAGS} ldflags: ${LDFLAGS} - documentation: ${ac_libmodbus_build_doc} tests: ${enable_tests} ]) diff --git a/doc/Makefile.am b/doc/Makefile.am deleted file mode 100644 index 08eba6d6d..000000000 --- a/doc/Makefile.am +++ /dev/null @@ -1,88 +0,0 @@ -TXT3 = \ - modbus_close.txt \ - modbus_connect.txt \ - modbus_flush.txt \ - modbus_free.txt \ - modbus_get_indication_timeout.txt \ - modbus_get_slave.txt \ - modbus_get_byte_from_bits.txt \ - modbus_get_byte_timeout.txt \ - modbus_get_float.txt \ - modbus_get_float_abcd.txt \ - modbus_get_float_badc.txt \ - modbus_get_float_cdab.txt \ - modbus_get_float_dcba.txt \ - modbus_get_header_length.txt \ - modbus_get_response_timeout.txt \ - modbus_get_socket.txt \ - modbus_mapping_free.txt \ - modbus_mapping_new.txt \ - modbus_mapping_new_start_address.txt \ - modbus_mask_write_register.txt \ - modbus_new_rtu.txt \ - modbus_new_tcp_pi.txt \ - modbus_new_tcp.txt \ - modbus_read_bits.txt \ - modbus_read_input_bits.txt \ - modbus_read_input_registers.txt \ - modbus_read_registers.txt \ - modbus_receive_confirmation.txt \ - modbus_receive.txt \ - modbus_reply_exception.txt \ - modbus_reply.txt \ - modbus_report_slave_id.txt \ - modbus_rtu_get_serial_mode.txt \ - modbus_rtu_set_serial_mode.txt \ - modbus_rtu_get_rts.txt \ - modbus_rtu_set_rts.txt \ - modbus_rtu_set_custom_rts.txt \ - modbus_rtu_get_rts_delay.txt \ - modbus_rtu_set_rts_delay.txt \ - modbus_send_raw_request.txt \ - modbus_set_bits_from_bytes.txt \ - modbus_set_bits_from_byte.txt \ - modbus_set_byte_timeout.txt \ - modbus_set_debug.txt \ - modbus_set_error_recovery.txt \ - modbus_set_float.txt \ - modbus_set_float_abcd.txt \ - modbus_set_float_badc.txt \ - modbus_set_float_cdab.txt \ - modbus_set_float_dcba.txt \ - modbus_set_indication_timeout.txt \ - modbus_set_response_timeout.txt \ - modbus_set_slave.txt \ - modbus_set_socket.txt \ - modbus_strerror.txt \ - modbus_tcp_accept.txt \ - modbus_tcp_pi_accept.txt \ - modbus_tcp_listen.txt \ - modbus_tcp_pi_listen.txt \ - modbus_write_and_read_registers.txt \ - modbus_write_bits.txt \ - modbus_write_bit.txt \ - modbus_write_registers.txt \ - modbus_write_register.txt -TXT7 = libmodbus.txt - -EXTRA_DIST = asciidoc.conf $(TXT3) $(TXT7) - -MAN3 = $(TXT3:%.txt=%.3) -MAN7 = $(TXT7:%.txt=%.7) - -if BUILD_DOC -man3_MANS = $(MAN3) -man7_MANS = $(MAN7) -endif - -HTML = $(TXT3:%.txt=%.html) $(TXT7:%.txt=%.html) - -htmldoc: $(HTML) - -.txt.html: - asciidoc -d manpage -b xhtml11 -f asciidoc.conf -alibmodbus_version=@LIBMODBUS_VERSION@ $< - -.txt.3 .txt.7: - a2x --doctype manpage --format manpage -alibmodbus_version=@LIBMODBUS_VERSION@ $< - -CLEANFILES = *.3 *.7 *.html diff --git a/doc/asciidoc.conf b/doc/asciidoc.conf deleted file mode 100644 index 39e67e441..000000000 --- a/doc/asciidoc.conf +++ /dev/null @@ -1,51 +0,0 @@ -[paradef-default] -literal-style=template="literalparagraph" - -[macros] -(?su)[\\]?(?Plinkmb):(?P\S*?)\[(?P.*?)\]= - -ifdef::backend-docbook[] -[linkmb-inlinemacro] -{0%{target}} -{0#} -{0#{target}{0}} -{0#} -endif::backend-docbook[] - -ifdef::backend-xhtml11[] -[linkmb-inlinemacro] -{target}{0?({0})} -endif::backend-xhtml11[] - -ifdef::doctype-manpage[] -ifdef::backend-docbook[] -[header] -template::[header-declarations] - - -{mantitle} -{manvolnum} -libmodbus -v{libmodbus_version} -libmodbus Manual - - - {manname} - {manpurpose} - -endif::backend-docbook[] -endif::doctype-manpage[] - -ifdef::backend-xhtml11[] -[footer] - -{disable-javascript%

} - - - -endif::backend-xhtml11[] diff --git a/doc/libmodbus.txt b/doc/libmodbus.txt deleted file mode 100644 index 716c684aa..000000000 --- a/doc/libmodbus.txt +++ /dev/null @@ -1,279 +0,0 @@ -libmodbus(7) -============ - - -NAME ----- -libmodbus - a fast and portable Modbus library - - -SYNOPSIS --------- -*#include * - -*cc* 'files' \`pkg-config --cflags --libs libmodbus` - -DESCRIPTION ------------ -libmodbus is a library to send/receive data with a device which respects the -Modbus protocol. This library contains various backends to communicate over -different networks (eg. serial in RTU mode or Ethernet in TCP/IPv6). The -http://www.modbus.org site provides documentation about the protocol at -http://www.modbus.org/specs.php. - -libmodbus provides an abstraction of the lower communication layers and offers -the same API on all supported platforms. - -This documentation presents an overview of libmodbus concepts, describes how -libmodbus abstracts Modbus communication with different hardware and platforms -and provides a reference manual for the functions provided by the libmodbus -library. - - -Contexts -~~~~~~~~ -The Modbus protocol contains many variants (eg. serial RTU or Ethernet TCP), to -ease the implementation of a variant, the library was designed to use a backend -for each variant. The backends are also a convenient way to fulfill other -requirements (eg. real-time operations). Each backend offers a specific function -to create a new 'modbus_t' context. The 'modbus_t' context is an opaque -structure containing all necessary information to establish a connection with -other Modbus devices according to the selected variant. - -You can choose the best context for your needs among: - -RTU Context -^^^^^^^^^^^ -The RTU backend (Remote Terminal Unit) is used in serial communication and makes -use of a compact, binary representation of the data for protocol -communication. The RTU format follows the commands/data with a cyclic redundancy -check checksum as an error check mechanism to ensure the reliability of -data. Modbus RTU is the most common implementation available for Modbus. A -Modbus RTU message must be transmitted continuously without inter-character -hesitations (extract from Wikipedia, Modbus, http://en.wikipedia.org/wiki/Modbus -(as of Mar. 13, 2011, 20:51 GMT). - -The Modbus RTU framing calls a slave, a device/service which handle Modbus -requests, and a master, a client which send requests. The communication is -always initiated by the master. - -Many Modbus devices can be connected together on the same physical link so -before sending a message, you must set the slave (receiver) with -linkmb:modbus_set_slave[3]. If you're running a slave, its slave number will be -used to filter received messages. - -The libmodbus implementation of RTU isn't time based as stated in original -Modbus specification, instead all bytes are sent as fast as possible and a -response or an indication is considered complete when all expected characters -have been received. This implementation offers very fast communication but you -must take care to set a response timeout of slaves less than response timeout of -master (ortherwise other slaves may ignore master requests when one of the slave -is not responding). - -Create a Modbus RTU context:: - - linkmb:modbus_new_rtu[3] - - -Set the serial mode:: - - linkmb:modbus_rtu_get_serial_mode[3] - - linkmb:modbus_rtu_set_serial_mode[3] - - linkmb:modbus_rtu_get_rts[3] - - linkmb:modbus_rtu_set_rts[3] - - linkmb:modbus_rtu_set_custom_rts[3] - - linkmb:modbus_rtu_get_rts_delay[3] - - linkmb:modbus_rtu_set_rts_delay[3] - - -TCP (IPv4) Context -^^^^^^^^^^^^^^^^^^ -The TCP backend implements a Modbus variant used for communications over -TCP/IPv4 networks. It does not require a checksum calculation as lower layer -takes care of the same. - -Create a Modbus TCP context:: - - linkmb:modbus_new_tcp[3] - - -TCP PI (IPv4 and IPv6) Context -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The TCP PI (Protocol Independent) backend implements a Modbus variant used for -communications over TCP IPv4 and IPv6 networks. It does not require a checksum -calculation as lower layer takes care of the same. - -Contrary to the TCP IPv4 only backend, the TCP PI backend offers hostname -resolution but it consumes about 1Kb of additional memory. - -Create a Modbus TCP context:: - - linkmb:modbus_new_tcp_pi[3] - - -Common -^^^^^^ -Before using any libmodbus functions, the caller must allocate and initialize a -'modbus_t' context with functions explained above, then the following functions -are provided to modify and free a 'context': - -Free libmodbus context:: - - linkmb:modbus_free[3] - -Set slave ID:: - - linkmb:modbus_set_slave[3] - -Enable debug mode:: - - linkmb:modbus_set_debug[3] - -Timeout settings:: - - linkmb:modbus_get_byte_timeout[3] - - linkmb:modbus_set_byte_timeout[3] - - linkmb:modbus_get_response_timeout[3] - - linkmb:modbus_set_response_timeout[3] - -Error recovery mode:: - - linkmb:modbus_set_error_recovery[3] - -Setter/getter of internal socket:: - - linkmb:modbus_set_socket[3] - - linkmb:modbus_get_socket[3] - -Information about header:: - - linkmb:modbus_get_header_length[3] - -Macros for data manipulation:: - - - MODBUS_GET_HIGH_BYTE(data), extracts the high byte from a byte - - MODBUS_GET_LOW_BYTE(data), extracts the low byte from a byte - - MODBUS_GET_INT64_FROM_INT16(tab_int16, index), builds an int64 from the four - first int16 starting at tab_int16[index] - - MODBUS_GET_INT32_FROM_INT16(tab_int16, index), builds an int32 from the two - first int16 starting at tab_int16[index] - - MODBUS_GET_INT16_FROM_INT8(tab_int8, index), builds an int16 from the two - first int8 starting at tab_int8[index] - - MODBUS_SET_INT16_TO_INT8(tab_int8, index, value), set an int16 value into - the two first bytes starting at tab_int8[index] - - MODBUS_SET_INT32_TO_INT16(tab_int16, index, value), set an int32 value into - the two first int16 starting at tab_int16[index] - - MODBUS_SET_INT64_TO_INT16(tab_int16, index, value), set an int64 value into - the four first int16 starting at tab_int16[index] - -Handling of bits and bytes:: - - linkmb:modbus_set_bits_from_byte[3] - - linkmb:modbus_set_bits_from_bytes[3] - - linkmb:modbus_get_byte_from_bits[3] - -Set or get float numbers:: - - linkmb:modbus_get_float_abcd[3] - - linkmb:modbus_set_float_abcd[3] - - linkmb:modbus_get_float_badc[3] - - linkmb:modbus_set_float_badc[3] - - linkmb:modbus_get_float_cdab[3] - - linkmb:modbus_set_float_cdab[3] - - linkmb:modbus_get_float_dcba[3] - - linkmb:modbus_set_float_dcba[3] - - linkmb:modbus_get_float[3] (deprecated) - - linkmb:modbus_set_float[3] (deprecated) - - - -Connection -~~~~~~~~~~ -The following functions are provided to establish and close a connection with -Modbus devices: - -Establish a connection:: - - linkmb:modbus_connect[3] - -Close a connection:: - - linkmb:modbus_close[3] - -Flush a connection:: - - linkmb:modbus_flush[3] - - -Client -~~~~~~ -The Modbus protocol defines different data types and functions to read and write -them from/to remote devices. The following functions are used by the clients to -send Modbus requests: - -Read data:: - - linkmb:modbus_read_bits[3] - - linkmb:modbus_read_input_bits[3] - - linkmb:modbus_read_registers[3] - - linkmb:modbus_read_input_registers[3] - - linkmb:modbus_report_slave_id[3] - -Write data:: - - linkmb:modbus_write_bit[3] - - linkmb:modbus_write_register[3] - - linkmb:modbus_write_bits[3] - - linkmb:modbus_write_registers[3] - -Write and read data:: - - linkmb:modbus_write_and_read_registers[3] - -Raw requests:: - - linkmb:modbus_send_raw_request[3] - - linkmb:modbus_receive_confirmation[3] - -Reply an exception:: - - linkmb:modbus_reply_exception[3] - - -Server -~~~~~~ -The server is waiting for request from clients and must answer when it is -concerned by the request. The libmodbus offers the following functions to -handle requests: - -Data mapping:: - - linkmb:modbus_mapping_new[3] - - linkmb:modbus_mapping_free[3] - -Receive:: - - linkmb:modbus_receive[3] - -Reply:: - - linkmb:modbus_reply[3] - - linkmb:modbus_reply_exception[3] - - -ERROR HANDLING --------------- -The libmodbus functions handle errors using the standard conventions found on -POSIX systems. Generally, this means that upon failure a libmodbus function -shall return either a NULL value (if returning a pointer) or a negative value -(if returning an integer), and the actual error code shall be stored in the -'errno' variable. - -The *modbus_strerror()* function is provided to translate libmodbus-specific -error codes into error message strings; for details refer to -linkmb:modbus_strerror[3]. - - -MISCELLANEOUS -------------- -The _LIBMODBUS_VERSION_STRING_ constant indicates the libmodbus version the -program has been compiled against. The variables 'libmodbus_version_major', -'libmodbus_version_minor', 'libmodbus_version_micro' give the version the -program is linked against. - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - - - -RESOURCES ---------- -Main web site: - -Report bugs on the issue tracker at -. - - -COPYING -------- -Free use of this software is granted under the terms of the GNU Lesser General -Public License (LGPL v2.1+). For details see the file `COPYING.LESSER` included -with the libmodbus distribution. diff --git a/doc/modbus_free.txt b/doc/modbus_free.txt deleted file mode 100644 index f7b12f070..000000000 --- a/doc/modbus_free.txt +++ /dev/null @@ -1,33 +0,0 @@ -modbus_free(3) -============== - - -NAME ----- -modbus_free - free a libmodbus context - - -SYNOPSIS --------- -*void modbus_free(modbus_t *'ctx');* - - -DESCRIPTION ------------ -The *modbus_free()* function shall free an allocated modbus_t structure. - - -RETURN VALUE ------------- -There is no return values. - - -SEE ALSO --------- -linkmb:libmodbus[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_get_byte_from_bits.txt b/doc/modbus_get_byte_from_bits.txt deleted file mode 100644 index e0828f1a6..000000000 --- a/doc/modbus_get_byte_from_bits.txt +++ /dev/null @@ -1,35 +0,0 @@ -modbus_get_byte_from_bits(3) -============================ - -NAME ----- -modbus_get_byte_from_bits - get the value from many bits - - -SYNOPSIS --------- -*uint8_t modbus_get_byte_from_bits(const uint8_t *'src', int 'index', unsigned int 'nb_bits');* - - -DESCRIPTION ------------ -The *modbus_get_byte_from_bits()* function shall extract a value from many -bits. All _nb_bits_ bits from _src_ at position _index_ will be read as a -single value. To obtain a full byte, set nb_bits to 8. - - -RETURN VALUE ------------- -The function shall return a byte containing the bits read. - - -SEE ALSO --------- -linkmb:modbus_set_bits_from_byte[3] -linkmb:modbus_set_bits_from_bytes[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_get_byte_timeout.txt b/doc/modbus_get_byte_timeout.txt deleted file mode 100644 index d7ba0704e..000000000 --- a/doc/modbus_get_byte_timeout.txt +++ /dev/null @@ -1,50 +0,0 @@ -modbus_get_byte_timeout(3) -========================== - - -NAME ----- -modbus_get_byte_timeout - get timeout between bytes - - -SYNOPSIS --------- -*int modbus_get_byte_timeout(modbus_t *'ctx', uint32_t *'to_sec', uint32_t *'to_usec');* - - -DESCRIPTION ------------ -The *modbus_get_byte_timeout()* function shall store the timeout interval -between two consecutive bytes of the same message in the _to_sec_ and _to_usec_ -arguments. - - -RETURN VALUE ------------- -The function shall return 0 if successful. Otherwise it shall return -1 and set -errno. - - -EXAMPLE -------- -[source,c] -------------------- -uint32_t to_sec; -uint32_t to_usec; - -/* Save original timeout */ -modbus_get_byte_timeout(ctx, &to_sec, &to_usec); -------------------- - - -SEE ALSO --------- -linkmb:modbus_set_byte_timeout[3] -linkmb:modbus_get_response_timeout[3] -linkmb:modbus_set_response_timeout[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_get_float.txt b/doc/modbus_get_float.txt deleted file mode 100644 index 023c0bb50..000000000 --- a/doc/modbus_get_float.txt +++ /dev/null @@ -1,39 +0,0 @@ -modbus_get_float(3) -=================== - - -NAME ----- -modbus_get_float - get a float value from 2 registers - - -SYNOPSIS --------- -*float modbus_get_float(const uint16_t *'src');* - -Warning, this function is *deprecated* since libmodbus v3.2.0 and has been -replaced by *modbus_get_float_dcba()*. - -DESCRIPTION ------------ -The *modbus_get_float()* function shall get a float from 4 bytes in Modbus -format (DCBA byte order). The _src_ array must be a pointer on two 16 bits -values, for example, if the first word is set to 0x4465 and the second to -0x229a, the float value will be 916.540649. - - -RETURN VALUE ------------- -The function shall return a float. - - -SEE ALSO --------- -linkmb:modbus_set_float[3] -linkmb:modbus_set_float_dcba[3] -linkmb:modbus_get_float_dcba[3] - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_get_float_abcd.txt b/doc/modbus_get_float_abcd.txt deleted file mode 100644 index 6a491b29f..000000000 --- a/doc/modbus_get_float_abcd.txt +++ /dev/null @@ -1,39 +0,0 @@ -modbus_get_float_abcd(3) -======================== - - -NAME ----- -modbus_get_float_abcd - get a float value from 2 registers in ABCD byte order - - -SYNOPSIS --------- -*float modbus_get_float_abcd(const uint16_t *'src');* - - -DESCRIPTION ------------ -The *modbus_get_float_abcd()* function shall get a float from 4 bytes in usual -Modbus format. The _src_ array must be a pointer on two 16 bits values, for -example, if the first word is set to 0x0020 and the second to 0xF147, the float -value will be read as 123456.0. - - -RETURN VALUE ------------- -The function shall return a float. - - -SEE ALSO --------- -linkmb:modbus_set_float_abcd[3] -linkmb:modbus_get_float_badc[3] -linkmb:modbus_get_float_cdab[3] -linkmb:modbus_get_float_dcba[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_get_float_badc.txt b/doc/modbus_get_float_badc.txt deleted file mode 100644 index 317770d47..000000000 --- a/doc/modbus_get_float_badc.txt +++ /dev/null @@ -1,39 +0,0 @@ -modbus_get_float_badc(3) -======================== - - -NAME ----- -modbus_get_float_badc - get a float value from 2 registers in BADC byte order - - -SYNOPSIS --------- -*float modbus_get_float_badc(const uint16_t *'src');* - - -DESCRIPTION ------------ -The *modbus_get_float_badc()* function shall get a float from 4 bytes with -swapped bytes (BADC instead of ABCD). The _src_ array must be a pointer on two -16 bits values, for example, if the first word is set to 0x2000 and the second -to 0x47F1, the float value will be read as 123456.0. - - -RETURN VALUE ------------- -The function shall return a float. - - -SEE ALSO --------- -linkmb:modbus_set_float_badc[3] -linkmb:modbus_get_float_abcd[3] -linkmb:modbus_get_float_cdab[3] -linkmb:modbus_get_float_dcba[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_get_float_cdab.txt b/doc/modbus_get_float_cdab.txt deleted file mode 100644 index 82261b726..000000000 --- a/doc/modbus_get_float_cdab.txt +++ /dev/null @@ -1,39 +0,0 @@ -modbus_get_float_cdab(3) -======================== - - -NAME ----- -modbus_get_float_cdab - get a float value from 2 registers in CDAB byte order - - -SYNOPSIS --------- -*float modbus_get_float_cdab(const uint16_t *'src');* - - -DESCRIPTION ------------ -The *modbus_get_float_cdab()* function shall get a float from 4 bytes with -swapped words (CDAB order instead of ABCD). The _src_ array must be a pointer on -two 16 bits values, for example, if the first word is set to F147 and the second -to 0x0020, the float value will be read as 123456.0. - - -RETURN VALUE ------------- -The function shall return a float. - - -SEE ALSO --------- -linkmb:modbus_set_float_cdab[3] -linkmb:modbus_get_float_abcd[3] -linkmb:modbus_get_float_badc[3] -linkmb:modbus_get_float_dcba[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_get_float_dcba.txt b/doc/modbus_get_float_dcba.txt deleted file mode 100644 index cd98a048c..000000000 --- a/doc/modbus_get_float_dcba.txt +++ /dev/null @@ -1,39 +0,0 @@ -modbus_get_float_dcba(3) -======================== - - -NAME ----- -modbus_get_float_dcba - get a float value from 2 registers in DCBA byte order - - -SYNOPSIS --------- -*float modbus_get_float_dcba(const uint16_t *'src');* - - -DESCRIPTION ------------ -The *modbus_get_float_dcba()* function shall get a float from 4 bytes in -inversed Modbus format (DCBA order instead of ABCD). The _src_ array must be a -pointer on two 16 bits values, for example, if the first word is set to 0x47F1 -and the second to 0x2000, the float value will be read as 123456.0. - - -RETURN VALUE ------------- -The function shall return a float. - - -SEE ALSO --------- -linkmb:modbus_set_float_dcba[3] -linkmb:modbus_get_float_abcd[3] -linkmb:modbus_get_float_badc[3] -linkmb:modbus_get_float_cdab[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_get_header_length.txt b/doc/modbus_get_header_length.txt deleted file mode 100644 index 79f7cd96d..000000000 --- a/doc/modbus_get_header_length.txt +++ /dev/null @@ -1,35 +0,0 @@ -modbus_get_header_length(3) -=========================== - - -NAME ----- -modbus_get_header_length - retrieve the current header length - - -SYNOPSIS --------- -*int modbus_get_header_length(modbus_t *'ctx');* - - -DESCRIPTION ------------ -The *modbus_get_header_length()* function shall retrieve the current header -length from the backend. This function is convenient to manipulate a message and -so its limited to low-level operations. - - -RETURN VALUE ------------- -The header length as integer value. - - -SEE ALSO --------- -linkmb:libmodbus[7] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_get_indication_timeout.txt b/doc/modbus_get_indication_timeout.txt deleted file mode 100644 index 3af39d9cd..000000000 --- a/doc/modbus_get_indication_timeout.txt +++ /dev/null @@ -1,53 +0,0 @@ -modbus_get_indication_timeout(3) -================================ - - -NAME ----- -modbus_get_indication_timeout - get timeout used to wait for an indication (request received by a server). - -SYNOPSIS --------- -*int modbus_get_indication_timeout(modbus_t *'ctx', uint32_t *'to_sec', uint32_t *'to_usec');* - - -DESCRIPTION ------------ - -The *modbus_get_indication_timeout()* function shall store the timeout interval -used to wait for an indication in the _to_sec_ and _to_usec_ arguments. -Indication is the term used by the Modbus protocol to designate a request -received by the server. - -The default value is zero, it means the server will wait forever. - - -RETURN VALUE ------------- -The function shall return 0 if successful. Otherwise it shall return -1 and set -errno. - - -EXAMPLE -------- -[source,c] -------------------- -uint32_t to_sec; -uint32_t to_usec; - -/* Save original timeout */ -modbus_get_indication_timeout(ctx, &to_sec, &to_usec); -------------------- - - -SEE ALSO --------- -linkmb:modbus_set_indication_timeout[3] -linkmb:modbus_get_response_timeout[3] -linkmb:modbus_set_response_timeout[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_get_response_timeout.txt b/doc/modbus_get_response_timeout.txt deleted file mode 100644 index 33558022c..000000000 --- a/doc/modbus_get_response_timeout.txt +++ /dev/null @@ -1,52 +0,0 @@ -modbus_get_response_timeout(3) -============================== - - -NAME ----- -modbus_get_response_timeout - get timeout for response - - -SYNOPSIS --------- -*int modbus_get_response_timeout(modbus_t *'ctx', uint32_t *'to_sec', uint32_t *'to_usec');* - - -DESCRIPTION ------------ -The *modbus_get_response_timeout()* function shall return the timeout interval -used to wait for a response in the _to_sec_ and _to_usec_ arguments. - - -RETURN VALUE ------------- -The function shall return 0 if successful. Otherwise it shall return -1 and set -errno. - - -EXAMPLE -------- -[source,c] -------------------- -uint32_t old_response_to_sec; -uint32_t old_response_to_usec; - -/* Save original timeout */ -modbus_get_response_timeout(ctx, &old_response_to_sec, &old_response_to_usec); - -/* Define a new and too short timeout! */ -modbus_set_response_timeout(ctx, 0, 0); -------------------- - - -SEE ALSO --------- -linkmb:modbus_set_response_timeout[3] -linkmb:modbus_get_byte_timeout[3] -linkmb:modbus_set_byte_timeout[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_get_slave.txt b/doc/modbus_get_slave.txt deleted file mode 100644 index 89e2ac019..000000000 --- a/doc/modbus_get_slave.txt +++ /dev/null @@ -1,41 +0,0 @@ -modbus_get_slave(3) -=================== - - -NAME ----- -modbus_get_slave - get slave number in the context - - -SYNOPSIS --------- -*int modbus_get_slave(modbus_t *'ctx');* - - -DESCRIPTION ------------ -The *modbus_get_slave()* function shall get the slave number in the libmodbus -context. - - -RETURN VALUE ------------- -The function shall return the slave number if successful. Otherwise it shall return -1 -and set errno to one of the values defined below. - - -ERRORS ------- -*EINVAL*:: -The libmodbus context is undefined. - - -SEE ALSO --------- -linkmb:modbus_set_slave[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_get_socket.txt b/doc/modbus_get_socket.txt deleted file mode 100644 index 8645fa4e2..000000000 --- a/doc/modbus_get_socket.txt +++ /dev/null @@ -1,35 +0,0 @@ -modbus_get_socket(3) -==================== - - -NAME ----- -modbus_get_socket - get the current socket of the context - - -SYNOPSIS --------- -*int modbus_get_socket(modbus_t *'ctx');* - - -DESCRIPTION ------------ -The *modbus_get_socket()* function shall return the current socket or file -descriptor of the libmodbus context. - - -RETURN VALUE ------------- -The function returns the current socket or file descriptor of the context if -successful. Otherwise it shall return -1 and set errno. - - -SEE ALSO --------- -linkmb:modbus_set_socket[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_mapping_free.txt b/doc/modbus_mapping_free.txt deleted file mode 100644 index 0a99f6233..000000000 --- a/doc/modbus_mapping_free.txt +++ /dev/null @@ -1,34 +0,0 @@ -modbus_mapping_free(3) -===================== - - -NAME ----- -modbus_mapping_free - free a modbus_mapping_t structure - - -SYNOPSIS --------- -*void modbus_mapping_free(modbus_mapping_t *'mb_mapping');* - - -DESCRIPTION ------------ -The function shall free the four arrays of mb_mapping_t structure and finally -the mb_mapping_t referenced by _mb_mapping_. - - -RETURN VALUE ------------- -There is no return values. - - -SEE ALSO --------- -linkmb:modbus_mapping_new[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_mapping_new.txt b/doc/modbus_mapping_new.txt deleted file mode 100644 index 71cde1d63..000000000 --- a/doc/modbus_mapping_new.txt +++ /dev/null @@ -1,69 +0,0 @@ -modbus_mapping_new(3) -===================== - - -NAME ----- -modbus_mapping_new - allocate four arrays of bits and registers - - -SYNOPSIS --------- -*modbus_mapping_t* modbus_mapping_new(int 'nb_bits', int 'nb_input_bits', int 'nb_registers', int 'nb_input_registers');* - - -DESCRIPTION ------------ -The *modbus_mapping_new()* function shall allocate four arrays to store bits, -input bits, registers and inputs registers. The pointers are stored in -modbus_mapping_t structure. All values of the arrays are initialized to zero. - -This function is equivalent to a call of the -linkmb:modbus_mapping_new_start_address[3] function with all start addresses to -`0`. - -If it isn't necessary to allocate an array for a specific type of data, you can -pass the zero value in argument, the associated pointer will be NULL. - -This function is convenient to handle requests in a Modbus server/slave. - - -RETURN VALUE ------------- -The function shall return the new allocated structure if successful. Otherwise -it shall return NULL and set errno. - - -ERRORS ------- -*ENOMEM*:: -Not enough memory - - -EXAMPLE -------- -[source,c] -------------------- -/* The first value of each array is accessible from the 0 address. */ -mb_mapping = modbus_mapping_new(BITS_ADDRESS + BITS_NB, - INPUT_BITS_ADDRESS + INPUT_BITS_NB, - REGISTERS_ADDRESS + REGISTERS_NB, - INPUT_REGISTERS_ADDRESS + INPUT_REGISTERS_NB); -if (mb_mapping == NULL) { - fprintf(stderr, "Failed to allocate the mapping: %s\n", - modbus_strerror(errno)); - modbus_free(ctx); - return -1; -} -------------------- - -SEE ALSO --------- -linkmb:modbus_mapping_free[3] -linkmb:modbus_mapping_new_start_address[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_mapping_new_start_address.txt b/doc/modbus_mapping_new_start_address.txt deleted file mode 100644 index 0eaaa8ee2..000000000 --- a/doc/modbus_mapping_new_start_address.txt +++ /dev/null @@ -1,81 +0,0 @@ -modbus_mapping_new_start_address(3) -=================================== - - -NAME ----- -modbus_mapping_new_start_address - allocate four arrays of bits and registers accessible from their starting addresses - - -SYNOPSIS --------- -*modbus_mapping_t* modbus_mapping_new_start_address(int 'start_bits', int 'nb_bits', - int 'start_input_bits', int 'nb_input_bits', - int 'start_registers', int 'nb_registers', - int 'start_input_registers', int 'nb_input_registers');* - - -DESCRIPTION ------------ -The _modbus_mapping_new_start_address()_ function shall allocate four arrays to -store bits, input bits, registers and inputs registers. The pointers are stored -in modbus_mapping_t structure. All values of the arrays are initialized to zero. - -The different starting addresses make it possible to place the mapping at any -address in each address space. This way, you can give access to values stored -at high addresses without allocating memory from the address zero, for eg. to -make available registers from 10000 to 10009, you can use: - -[source,c] -------------------- -mb_mapping = modbus_mapping_new_start_address(0, 0, 0, 0, 10000, 10, 0, 0); -------------------- - -With this code, only 10 registers (`uint16_t`) are allocated. - -If it isn't necessary to allocate an array for a specific type of data, you can -pass the zero value in argument, the associated pointer will be NULL. - -This function is convenient to handle requests in a Modbus server/slave. - - -RETURN VALUE ------------- -The _modbus_mapping_new_start_address()_ function shall return the new allocated structure if -successful. Otherwise it shall return NULL and set errno. - - -ERRORS ------- -ENOMEM:: -Not enough memory - - -EXAMPLE -------- -[source,c] -------------------- -/* The first value of each array is accessible at the defined address. - The end address is ADDRESS + NB - 1. */ -mb_mapping = modbus_mapping_new_start_address(BITS_ADDRESS, BITS_NB, - INPUT_BITS_ADDRESS, INPUT_BITS_NB, - REGISTERS_ADDRESS, REGISTERS_NB, - INPUT_REGISTERS_ADDRESS, INPUT_REGISTERS_NB); -if (mb_mapping == NULL) { - fprintf(stderr, "Failed to allocate the mapping: %s\n", - modbus_strerror(errno)); - modbus_free(ctx); - return -1; -} -------------------- - -SEE ALSO --------- -linkmb:modbus_mapping_new[3] -linkmb:modbus_mapping_free[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_mask_write_register.txt b/doc/modbus_mask_write_register.txt deleted file mode 100644 index 7365503ca..000000000 --- a/doc/modbus_mask_write_register.txt +++ /dev/null @@ -1,41 +0,0 @@ -modbus_mask_write_register(3) -============================= - - -NAME ----- -modbus_mask_write_register - mask a single register - - -SYNOPSIS --------- -*int modbus_mask_write_register(modbus_t *'ctx', int 'addr', uint16_t 'and', uint16_t 'or');* - - -DESCRIPTION ------------ -The *modbus_mask_write_register()* function shall modify the value of the -holding register at the address 'addr' of the remote device using the algorithm: - - new value = (current value AND 'and') OR ('or' AND (NOT 'and')) - -The function uses the Modbus function code 0x16 (mask single register). - - -RETURN VALUE ------------- -The function shall return 1 if successful. Otherwise it shall return -1 and set -errno. - - -SEE ALSO --------- -linkmb:modbus_read_registers[3] -linkmb:modbus_write_registers[3] - - -AUTHORS -------- -Martijn de Gouw -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_new_rtu.txt b/doc/modbus_new_rtu.txt deleted file mode 100644 index 7a8eaacf3..000000000 --- a/doc/modbus_new_rtu.txt +++ /dev/null @@ -1,91 +0,0 @@ -modbus_new_rtu(3) -================= - - -NAME ----- -modbus_new_rtu - create a libmodbus context for RTU - - -SYNOPSIS --------- -*modbus_t *modbus_new_rtu(const char *'device', int 'baud', char 'parity', int 'data_bit', int 'stop_bit');* - - - -DESCRIPTION ------------ -The *modbus_new_rtu()* function shall allocate and initialize a _modbus_t_ -structure to communicate in RTU mode on a serial line. - -The _device_ argument specifies the name of the serial port handled by the OS, -eg. "/dev/ttyS0" or "/dev/ttyUSB0". On Windows, it's necessary to prepend COM -name with "\\.\" for COM number greater than 9, eg. "\\\\.\\COM10". See -http://msdn.microsoft.com/en-us/library/aa365247(v=vs.85).aspx for details - -The _baud_ argument specifies the baud rate of the communication, eg. 9600, -19200, 57600, 115200, etc. - -The _parity_ argument can have one of the following values::: -* _N_ for none -* _E_ for even -* _O_ for odd - -The _data_bits_ argument specifies the number of bits of data, the allowed -values are 5, 6, 7 and 8. - -The _stop_bits_ argument specifies the bits of stop, the allowed values are 1 -and 2. - -Once the _modbus_t_ structure is initialized, you must set the slave of your -device with linkmb:modbus_set_slave[3] and connect to the serial bus with -linkmb:modbus_connect[3]. - -RETURN VALUE ------------- -The function shall return a pointer to a _modbus_t_ structure if -successful. Otherwise it shall return NULL and set errno to one of the values -defined below. - - -ERRORS ------- -*EINVAL*:: -An invalid argument was given. - -*ENOMEM*:: -Out of memory. Possibly, the application hits its memory limit and/or whole -system is running out of memory. - - -EXAMPLE -------- -[source,c] -------------------- -modbus_t *ctx; - -ctx = modbus_new_rtu("/dev/ttyUSB0", 115200, 'N', 8, 1); -if (ctx == NULL) { - fprintf(stderr, "Unable to create the libmodbus context\n"); - return -1; -} - -modbus_set_slave(ctx, YOUR_DEVICE_ID); - -if (modbus_connect(ctx) == -1) { - fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); - modbus_free(ctx); - return -1; -} -------------------- - -SEE ALSO --------- -linkmb:modbus_new_tcp[3] -linkmb:modbus_free[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_new_tcp_pi.txt b/doc/modbus_new_tcp_pi.txt deleted file mode 100644 index 494c49382..000000000 --- a/doc/modbus_new_tcp_pi.txt +++ /dev/null @@ -1,77 +0,0 @@ -modbus_new_tcp_pi(3) -==================== - - -NAME ----- -modbus_new_tcp_pi - create a libmodbus context for TCP Protocol Independent - - -SYNOPSIS --------- -*modbus_t *modbus_new_tcp_pi(const char *'node', const char *'service');* - - -DESCRIPTION ------------ -The *modbus_new_tcp_pi()* function shall allocate and initialize a modbus_t -structure to communicate with a Modbus TCP IPv4 or IPv6 server. - -The _node_ argument specifies the host name or IP address of the host to connect -to, eg. "192.168.0.5" , "::1" or "server.com". A NULL value can be used to -listen any addresses in server mode. - -The _service_ argument is the service name/port number to connect to. To use the -default Modbus port use the string "502". On many Unix systems, it’s -convenient to use a port number greater than or equal to 1024 because it’s not -necessary to have administrator privileges. - - -RETURN VALUE ------------- -The function shall return a pointer to a *modbus_t* structure if -successful. Otherwise it shall return NULL and set errno to one of the values -defined below. - - -ERRORS ------- -*EINVAL*:: -The node string is empty or has been truncated. The service string is empty or -has been truncated. - -*ENOMEM*:: -Out of memory. Possibly, the application hits its memory limit and/or whole -system is running out of memory. - - -EXAMPLE -------- -[source,c] -------------------- -modbus_t *ctx; - -ctx = modbus_new_tcp_pi("::1", "1502"); -if (ctx == NULL) { - fprintf(stderr, "Unable to allocate libmodbus context\n"); - return -1; -} - -if (modbus_connect(ctx) == -1) { - fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); - modbus_free(ctx); - return -1; -} -------------------- - -SEE ALSO --------- -linkmb:modbus_new_tcp[3] -linkmb:modbus_tcp_pi_listen[3] -linkmb:modbus_free[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_read_bits.txt b/doc/modbus_read_bits.txt deleted file mode 100644 index 1e7fd1e0f..000000000 --- a/doc/modbus_read_bits.txt +++ /dev/null @@ -1,48 +0,0 @@ -modbus_read_bits(3) -=================== - - -NAME ----- -modbus_read_bits - read many bits - - -SYNOPSIS --------- -*int modbus_read_bits(modbus_t *'ctx', int 'addr', int 'nb', uint8_t *'dest');* - - -DESCRIPTION ------------ -The *modbus_read_bits()* function shall read the status of the _nb_ bits (coils) -to the address _addr_ of the remote device. The result of reading is stored in -_dest_ array as unsigned bytes (8 bits) set to `TRUE` or `FALSE`. - -You must take care to allocate enough memory to store the results in _dest_ -(at least _nb_ * sizeof(uint8_t)). - -The function uses the Modbus function code 0x01 (read coil status). - - -RETURN VALUE ------------- -The function shall return the number of read bits if successful. Otherwise it -shall return -1 and set errno. - - -ERRORS ------- -*EMBMDATA*:: -Too many bits requested - - -SEE ALSO --------- -linkmb:modbus_write_bit[3] -linkmb:modbus_write_bits[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_read_input_bits.txt b/doc/modbus_read_input_bits.txt deleted file mode 100644 index d7fd3e0a7..000000000 --- a/doc/modbus_read_input_bits.txt +++ /dev/null @@ -1,47 +0,0 @@ -modbus_read_input_bits(3) -========================= - - -NAME ----- -modbus_read_input_bits - read many input bits - - -SYNOPSIS --------- -*int modbus_read_input_bits(modbus_t *'ctx', int 'addr', int 'nb', uint8_t *'dest');* - - -DESCRIPTION ------------ -The *modbus_read_input_bits()* function shall read the content of the _nb_ input -bits to the address _addr_ of the remote device. The result of reading is stored -in _dest_ array as unsigned bytes (8 bits) set to _TRUE_ or _FALSE_. - -You must take care to allocate enough memory to store the results in _dest_ -(at least _nb_ * sizeof(uint8_t)). - -The function uses the Modbus function code 0x02 (read input status). - - -RETURN VALUE ------------- -The function shall return the number of read input status if -successful. Otherwise it shall return -1 and set errno. - - -ERRORS ------- -*EMBMDATA*:: -Too many discrete inputs requested - - -SEE ALSO --------- -linkmb:modbus_read_input_registers[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_read_input_registers.txt b/doc/modbus_read_input_registers.txt deleted file mode 100644 index 20a537672..000000000 --- a/doc/modbus_read_input_registers.txt +++ /dev/null @@ -1,51 +0,0 @@ -modbus_read_input_registers(3) -============================== - - -NAME ----- -modbus_read_input_registers - read many input registers - - -SYNOPSIS --------- -*int modbus_read_input_registers(modbus_t *'ctx', int 'addr', int 'nb', uint16_t *'dest');* - - -DESCRIPTION ------------ -The *modbus_read_input_registers()* function shall read the content of the _nb_ -input registers to address _addr_ of the remote device. The result of the -reading is stored in _dest_ array as word values (16 bits). - -You must take care to allocate enough memory to store the results in _dest_ (at -least _nb_ * sizeof(uint16_t)). - -The function uses the Modbus function code 0x04 (read input registers). The -holding registers and input registers have different historical meaning, but -nowadays it's more common to use holding registers only. - - -RETURN VALUE ------------- -The function shall return the number of read input registers if -successful. Otherwise it shall return -1 and set errno. - - -ERRORS ------- -*EMBMDATA*:: -Too many bits requested - - -SEE ALSO --------- -linkmb:modbus_read_input_bits[3] -linkmb:modbus_write_register[3] -linkmb:modbus_write_registers[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_receive.txt b/doc/modbus_receive.txt deleted file mode 100644 index 460d19b75..000000000 --- a/doc/modbus_receive.txt +++ /dev/null @@ -1,42 +0,0 @@ -modbus_receive(3) -================= - - -NAME ----- -modbus_receive - receive an indication request - - -SYNOPSIS --------- -*int modbus_receive(modbus_t *'ctx', uint8_t *'req');* - - -DESCRIPTION ------------ -The *modbus_receive()* function shall receive an indication request from the -socket of the context _ctx_. This function is used by Modbus slave/server to -receive and analyze indication request sent by the masters/clients. - -If you need to use another socket or file descriptor than the one defined in the -context _ctx_, see the function linkmb:modbus_set_socket[3]. - - -RETURN VALUE ------------- -The function shall store the indication request in _req_ and return the request -length if successful. The returned request length can be zero if the indication -request is ignored (eg. a query for another slave in RTU mode). Otherwise it -shall return -1 and set errno. - - -SEE ALSO --------- -linkmb:modbus_set_socket[3] -linkmb:modbus_reply[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_reply.txt b/doc/modbus_reply.txt deleted file mode 100644 index 6b71d11c2..000000000 --- a/doc/modbus_reply.txt +++ /dev/null @@ -1,52 +0,0 @@ -modbus_reply(3) -=============== - -NAME ----- -modbus_reply - send a response to the received request - - -SYNOPSIS --------- -*int modbus_reply(modbus_t *'ctx', const uint8_t *'req', int 'req_length', modbus_mapping_t *'mb_mapping'); - - -DESCRIPTION ------------ -The *modbus_reply()* function shall send a response to received request. The -request _req_ given in argument is analyzed, a response is then built and sent -by using the information of the modbus context _ctx_. - -If the request indicates to read or write a value the operation will done in the -modbus mapping _mb_mapping_ according to the type of the manipulated data. - -If an error occurs, an exception response will be sent. - -This function is designed for Modbus server. - - -RETURN VALUE ------------- -The function shall return the length of the response sent if -successful. Otherwise it shall return -1 and set errno. - - -ERRORS ------- -*EMBMDATA*:: -Sending has failed - -See also the errors returned by the syscall used to send the response (eg. send -or write). - - -SEE ALSO --------- -linkmb:modbus_reply_exception[3] -linkmb:libmodbus[7] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_reply_exception.txt b/doc/modbus_reply_exception.txt deleted file mode 100644 index b2170be3b..000000000 --- a/doc/modbus_reply_exception.txt +++ /dev/null @@ -1,57 +0,0 @@ -modbus_reply_exception(3) -========================= - -NAME ----- -modbus_reply_exception - send an exception response - - -SYNOPSIS --------- -*int modbus_reply_exception(modbus_t *'ctx', const uint8_t *'req', unsigned int 'exception_code'); - - -DESCRIPTION ------------ -The *modbus_reply_exception()* function shall send an exception response based -on the 'exception_code' in argument. - -The libmodbus provides the following exception codes: - -* MODBUS_EXCEPTION_ILLEGAL_FUNCTION (1) -* MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS (2) -* MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE (3) -* MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE (4) -* MODBUS_EXCEPTION_ACKNOWLEDGE (5) -* MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY (6) -* MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE (7) -* MODBUS_EXCEPTION_MEMORY_PARITY (8) -* MODBUS_EXCEPTION_NOT_DEFINED (9) -* MODBUS_EXCEPTION_GATEWAY_PATH (10) -* MODBUS_EXCEPTION_GATEWAY_TARGET (11) - -The initial request _req_ is required to build a valid response. - - -RETURN VALUE ------------- -The function shall return the length of the response sent if -successful. Otherwise it shall return -1 and set errno. - - -ERRORS ------- -*EINVAL*:: -The exception code is invalid - - -SEE ALSO --------- -linkmb:modbus_reply[3] -linkmb:libmodbus[7] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_report_slave_id.txt b/doc/modbus_report_slave_id.txt deleted file mode 100644 index 6aedec679..000000000 --- a/doc/modbus_report_slave_id.txt +++ /dev/null @@ -1,61 +0,0 @@ -modbus_report_slave_id(3) -========================= - - -NAME ----- -modbus_report_slave_id - returns a description of the controller - - -SYNOPSIS --------- -*int modbus_report_slave_id(modbus_t *'ctx', int 'max_dest', uint8_t *'dest');* - - -DESCRIPTION ------------ -The *modbus_report_slave_id()* function shall send a request to the controller -to obtain a description of the controller. - -The response stored in _dest_ contains: - -* the slave ID, this unique ID is in reality not unique at all so it's not - possible to depend on it to know how the information are packed in the - response. -* the run indicator status (0x00 = OFF, 0xFF = ON) -* additional data specific to each controller. For example, libmodbus returns - the version of the library as a string. - -The function writes at most _max_dest_ bytes from the response to _dest_ so -you must ensure that _dest_ is large enough. - -RETURN VALUE ------------- -The function shall return the number of read data if successful. - -If the output was truncated due to the _max_dest_ limit then the return value is -the number of bytes which would have been written to _dest_ if enough space had -been available. Thus, a return value greater than _max_dest_ means that the -response data was truncated. - -Otherwise it shall return -1 and set errno. - -EXAMPLE -------- -[source,c] -------------------- -uint8_t tab_bytes[MODBUS_MAX_PDU_LENGTH]; - -... - -rc = modbus_report_slave_id(ctx, MODBUS_MAX_PDU_LENGTH, tab_bytes); -if (rc > 1) { - printf("Run Status Indicator: %s\n", tab_bytes[1] ? "ON" : "OFF"); -} -------------------- - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_rtu_get_rts.txt b/doc/modbus_rtu_get_rts.txt deleted file mode 100644 index 559c80b6e..000000000 --- a/doc/modbus_rtu_get_rts.txt +++ /dev/null @@ -1,47 +0,0 @@ -modbus_rtu_get_rts(3) -===================== - - -NAME ----- -modbus_rtu_get_rts - get the current RTS mode in RTU - - -SYNOPSIS --------- -*int modbus_rtu_get_rts(modbus_t *'ctx');* - - -DESCRIPTION ------------ -The *modbus_rtu_get_rts()* function shall get the current Request To Send mode -of the libmodbus context _ctx_. The possible returned values are: - -* MODBUS_RTU_RTS_NONE -* MODBUS_RTU_RTS_UP -* MODBUS_RTU_RTS_DOWN - -This function can only be used with a context using a RTU backend. - - -RETURN VALUE ------------- -The function shall return the current RTS mode if successful. Otherwise it shall -return -1 and set errno. - - -ERRORS ------- -*EINVAL*:: -The libmodbus backend is not RTU. - - -SEE ALSO --------- -linkmb:modbus_rtu_set_rts[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_rtu_get_rts_delay.txt b/doc/modbus_rtu_get_rts_delay.txt deleted file mode 100644 index 43853d453..000000000 --- a/doc/modbus_rtu_get_rts_delay.txt +++ /dev/null @@ -1,46 +0,0 @@ -modbus_rtu_get_rts_delay(3) -=========================== - - -NAME ----- -modbus_rtu_get_rts_delay - get the current RTS delay in RTU - - -SYNOPSIS --------- -*int modbus_rtu_get_rts_delay(modbus_t *'ctx');* - - -DESCRIPTION ------------ - -The _modbus_rtu_get_rts_delay()_ function shall get the current Request To Send -delay period of the libmodbus context 'ctx'. - -This function can only be used with a context using a RTU backend. - - -RETURN VALUE ------------- -The _modbus_rtu_get_rts_delay()_ function shall return the current RTS delay in -microseconds if successful. Otherwise it shall return -1 and set errno. - - -ERRORS ------- -*EINVAL*:: -The libmodbus backend is not RTU. - - -SEE ALSO --------- -linkmb:modbus_rtu_set_rts_delay[3] - - -AUTHORS -------- -Jimmy Bergström - -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_rtu_get_serial_mode.txt b/doc/modbus_rtu_get_serial_mode.txt deleted file mode 100644 index b58072836..000000000 --- a/doc/modbus_rtu_get_serial_mode.txt +++ /dev/null @@ -1,53 +0,0 @@ -modbus_rtu_get_serial_mode(3) -============================= - - -NAME ----- -modbus_rtu_get_serial_mode - get the current serial mode - - -SYNOPSIS --------- -*int modbus_rtu_get_serial_mode(modbus_t *'ctx');* - - -DESCRIPTION ------------ -The *modbus_rtu_get_serial_mode()* function shall return the serial mode -currently used by the libmodbus context: - -*MODBUS_RTU_RS232*:: the serial line is set for RS232 communication. RS-232 - (Recommended Standard 232) is the traditional name for a series of standards - for serial binary single-ended data and control signals connecting between a - DTE (Data Terminal Equipment) and a DCE (Data Circuit-terminating - Equipment). It is commonly used in computer serial ports - -*MODBUS_RTU_RS485*:: the serial line is set for RS485 communication. EIA-485, - also known as TIA/EIA-485 or RS-485, is a standard defining the electrical - characteristics of drivers and receivers for use in balanced digital multipoint - systems. This standard is widely used for communications in industrial - automation because it can be used effectively over long distances and in - electrically noisy environments. - -This function is only available on Linux kernels 2.6.28 onwards and can only be -used with a context using a RTU backend. - - -RETURN VALUE ------------- -The function shall return `MODBUS_RTU_RS232` or `MODBUS_RTU_RS485` if -successful. Otherwise it shall return -1 and set errno to one of the values -defined below. - - -ERRORS ------- -*EINVAL*:: -The current libmodbus backend is not RTU. - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_rtu_set_custom_rts.txt b/doc/modbus_rtu_set_custom_rts.txt deleted file mode 100644 index c17bd3d1f..000000000 --- a/doc/modbus_rtu_set_custom_rts.txt +++ /dev/null @@ -1,45 +0,0 @@ -modbus_rtu_set_custom_rts(3) -============================ - - -NAME ----- -modbus_rtu_set_custom_rts - set a function to be used for custom RTS implementation - - -SYNOPSIS --------- -*int modbus_rtu_set_custom_rts(modbus_t *'ctx', void (*'set_rts') (modbus_t *ctx, int on))* - - -DESCRIPTION ------------ -The _modbus_rtu_set_custom_rts()_ function shall set a custom function to be -called when the RTS pin is to be set before and after a transmission. By default -this is set to an internal function that toggles the RTS pin using an ioctl -call. - -Note that this function adheres to the RTS mode, the values MODBUS_RTU_RTS_UP or -MODBUS_RTU_RTS_DOWN must be used for the function to be called. - -This function can only be used with a context using a RTU backend. - - -RETURN VALUE ------------- -The _modbus_rtu_set_custom_rts()_ function shall return 0 if successful. -Otherwise it shall return -1 and set errno to one of the values defined below. - - -ERRORS ------- -*EINVAL*:: -The libmodbus backend is not RTU. - - -AUTHORS -------- -Jimmy Bergström - -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_rtu_set_rts_delay.txt b/doc/modbus_rtu_set_rts_delay.txt deleted file mode 100644 index 39af7df3e..000000000 --- a/doc/modbus_rtu_set_rts_delay.txt +++ /dev/null @@ -1,46 +0,0 @@ -modbus_rtu_set_rts_delay(3) -=========================== - - -NAME ----- -modbus_rtu_set_rts_delay - set the RTS delay in RTU - - -SYNOPSIS --------- -*int modbus_rtu_set_rts_delay(modbus_t *'ctx', int 'us');* - - -DESCRIPTION ------------ - -The _modbus_rtu_set_rts_delay()_ function shall set the Request To Send delay -period of the libmodbus context 'ctx'. - -This function can only be used with a context using a RTU backend. - - -RETURN VALUE ------------- -The _modbus_rtu_set_rts_delay()_ function shall return 0 if successful. -Otherwise it shall return -1 and set errno. - - -ERRORS ------- -*EINVAL*:: -The libmodbus backend is not RTU or a negative delay was specified. - - -SEE ALSO --------- -linkmb:modbus_rtu_get_rts_delay[3] - - -AUTHORS -------- -Jimmy Bergström - -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_rtu_set_serial_mode.txt b/doc/modbus_rtu_set_serial_mode.txt deleted file mode 100644 index 7086dc4a8..000000000 --- a/doc/modbus_rtu_set_serial_mode.txt +++ /dev/null @@ -1,56 +0,0 @@ -modbus_rtu_set_serial_mode(3) -============================= - - -NAME ----- -modbus_rtu_set_serial_mode - set the serial mode - - -SYNOPSIS --------- -*int modbus_rtu_set_serial_mode(modbus_t *'ctx', int 'mode');* - - -DESCRIPTION ------------ -The *modbus_rtu_set_serial_mode()* function shall set the selected serial -mode: - -*MODBUS_RTU_RS232*:: the serial line is set for RS232 communication. RS-232 - (Recommended Standard 232) is the traditional name for a series of standards - for serial binary single-ended data and control signals connecting between a - DTE (Data Terminal Equipment) and a DCE (Data Circuit-terminating - Equipment). It is commonly used in computer serial ports - -*MODBUS_RTU_RS485*:: the serial line is set for RS485 communication. EIA-485, - also known as TIA/EIA-485 or RS-485, is a standard defining the electrical - characteristics of drivers and receivers for use in balanced digital multipoint - systems. This standard is widely used for communications in industrial - automation because it can be used effectively over long distances and in - electrically noisy environments. - -This function is only supported on Linux kernels 2.6.28 onwards. - - -RETURN VALUE ------------- -The function shall return 0 if successful. Otherwise it shall return -1 and set -errno to one of the values defined below. - - -ERRORS ------- -*EINVAL*:: -The current libmodbus backend is not RTU. - -*ENOTSUP*:: -The function is not supported on your platform. - -If the call to ioctl() fails, the error code of ioctl will be returned. - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_set_bits_from_byte.txt b/doc/modbus_set_bits_from_byte.txt deleted file mode 100644 index 72b2949cb..000000000 --- a/doc/modbus_set_bits_from_byte.txt +++ /dev/null @@ -1,36 +0,0 @@ -modbus_set_bits_from_byte(3) -============================ - - -NAME ----- -modbus_set_bits_from_byte - set many bits from a single byte value - - -SYNOPSIS --------- -*void modbus_set_bits_from_byte(uint8_t *'dest', int 'index', const uint8_t 'value');* - - -DESCRIPTION ------------ -The *modbus_set_bits_from_byte()* function shall set many bits from a single byte. -All 8 bits from the byte _value_ will be written to _dest_ array starting at -_index_ position. - - -RETURN VALUE ------------- -There is no return values. - - -SEE ALSO --------- -linkmb:modbus_set_bits_from_byte[3] -linkmb:modbus_set_bits_from_bytes[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_set_bits_from_bytes.txt b/doc/modbus_set_bits_from_bytes.txt deleted file mode 100644 index cfbb8ed46..000000000 --- a/doc/modbus_set_bits_from_bytes.txt +++ /dev/null @@ -1,36 +0,0 @@ -modbus_set_bits_from_bytes(3) -============================ - - -NAME ----- -modbus_set_bits_from_bytes - set many bits from an array of bytes - - -SYNOPSIS --------- -*void modbus_set_bits_from_bytes(uint8_t *'dest', int 'index', unsigned int 'nb_bits', const uint8_t *'tab_byte');* - - -DESCRIPTION ------------ -The *modbus_set_bits_from_bytes* function shall set bits by reading an array of -bytes. All the bits of the bytes read from the first position of the array -_tab_byte_ are written as bits in the _dest_ array starting at position _index_. - - -RETURN VALUE ------------- -There is no return values. - - -SEE ALSO --------- -linkmb:modbus_set_bits_from_byte[3] -linkmb:modbus_get_byte_from_bits[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_set_float.txt b/doc/modbus_set_float.txt deleted file mode 100644 index f12402283..000000000 --- a/doc/modbus_set_float.txt +++ /dev/null @@ -1,36 +0,0 @@ -modbus_set_float(3) -=================== - -NAME ----- -modbus_set_float - set a float value from 2 registers - - -SYNOPSIS --------- -*void modbus_set_float(float 'f', uint16_t *'dest');* - -Warning, this function is *deprecated* since libmodbus v3.2.0 and has been -replaced by *modbus_set_float_dcba()*. - -DESCRIPTION ------------ -The *modbus_set_float()* function shall set a float to 4 bytes in Modbus format -(ABCD). The _dest_ array must be pointer on two 16 bits values to be able to -store the full result of the conversion. - - -RETURN VALUE ------------- -There is no return values. - - -SEE ALSO --------- -linkmb:modbus_get_float[3] -linkmb:modbus_set_float_dcba[3] - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_set_float_abcd.txt b/doc/modbus_set_float_abcd.txt deleted file mode 100644 index 386cbef54..000000000 --- a/doc/modbus_set_float_abcd.txt +++ /dev/null @@ -1,38 +0,0 @@ -modbus_set_float_abcd(3) -======================== - - -NAME ----- -modbus_set_float_abcd - set a float value in 2 registers using ABCD byte order - - -SYNOPSIS --------- -*void modbus_set_float_abcd(float 'f', uint16_t *'dest');* - - -DESCRIPTION ------------ -The *modbus_set_float_abcd()* function shall set a float to 4 bytes in usual -Modbus format. The _dest_ array must be pointer on two 16 bits values to be able -to store the full result of the conversion. - - -RETURN VALUE ------------- -There is no return values. - - -SEE ALSO --------- -linkmb:modbus_get_float_abcd[3] -linkmb:modbus_set_float_badc[3] -linkmb:modbus_set_float_cdab[3] -linkmb:modbus_set_float_dcba[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_set_float_badc.txt b/doc/modbus_set_float_badc.txt deleted file mode 100644 index d41d777d7..000000000 --- a/doc/modbus_set_float_badc.txt +++ /dev/null @@ -1,38 +0,0 @@ -modbus_set_float_badc(3) -======================== - - -NAME ----- -modbus_set_float_badc - set a float value in 2 registers using BADC byte order - - -SYNOPSIS --------- -*void modbus_set_float_badc(float 'f', uint16_t *'dest');* - - -DESCRIPTION ------------ -The *modbus_set_float_badc()* function shall set a float to 4 bytes in swapped -bytes Modbus format (BADC instead of ABCD). The _dest_ array must be pointer on -two 16 bits values to be able to store the full result of the conversion. - - -RETURN VALUE ------------- -There is no return values. - - -SEE ALSO --------- -linkmb:modbus_get_float_badc[3] -linkmb:modbus_set_float_abcd[3] -linkmb:modbus_set_float_cdab[3] -linkmb:modbus_set_float_dcba[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_set_float_cdab.txt b/doc/modbus_set_float_cdab.txt deleted file mode 100644 index 3a0372570..000000000 --- a/doc/modbus_set_float_cdab.txt +++ /dev/null @@ -1,39 +0,0 @@ -modbus_set_float_cdab(3) -======================== - - -NAME ----- -modbus_set_float_cdab - set a float value in 2 registers using CDAB byte order - - -SYNOPSIS --------- -*void modbus_set_float_cdab(float 'f', uint16_t *'dest');* - - -DESCRIPTION ------------ -The *modbus_set_float_cdab()* function shall set a float to 4 bytes in swapped -words Modbus format (CDAB order instead of ABCD). The _dest_ array must be -pointer on two 16 bits values to be able to store the full result of the -conversion. - - -RETURN VALUE ------------- -There is no return values. - - -SEE ALSO --------- -linkmb:modbus_get_float_cdab[3] -linkmb:modbus_set_float_abcd[3] -linkmb:modbus_set_float_badc[3] -linkmb:modbus_set_float_dcba[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_set_float_dcba.txt b/doc/modbus_set_float_dcba.txt deleted file mode 100644 index 578ae6f64..000000000 --- a/doc/modbus_set_float_dcba.txt +++ /dev/null @@ -1,37 +0,0 @@ -modbus_set_float_dcba(3) -======================== - - -NAME ----- -modbus_set_float_dcba - set a float value in 2 registers using DCBA byte order - - -SYNOPSIS --------- -*void modbus_set_float_dcba(float 'f', uint16_t *'dest');* - - -DESCRIPTION ------------ -The *modbus_set_float_dcba()* function shall set a float to 4 bytes in inverted -Modbus format (DCBA order). The _dest_ array must be pointer on two 16 bits -values to be able to store the full result of the conversion. - - -RETURN VALUE ------------- -There is no return values. - - -SEE ALSO --------- -linkmb:modbus_get_float_dcba[3] -linkmb:modbus_set_float[3] -linkmb:modbus_get_float[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_set_indication_timeout.txt b/doc/modbus_set_indication_timeout.txt deleted file mode 100644 index 6524bdc57..000000000 --- a/doc/modbus_set_indication_timeout.txt +++ /dev/null @@ -1,48 +0,0 @@ -modbus_set_indication_timeout(3) -================================ - - -NAME ----- -modbus_set_indication_timeout - set timeout between indications - - -SYNOPSIS --------- -*void modbus_set_indication_timeout(modbus_t *'ctx', uint32_t 'to_sec', uint32_t 'to_usec');* - - -DESCRIPTION ------------ -The *modbus_set_indication_timeout()* function shall set the timeout interval used by -a server to wait for a request from a client. - -The value of _to_usec_ argument must be in the range 0 to 999999. - -If both _to_sec_ and _to_usec_ are zero, this timeout will not be used at all. -In this case, the server will wait forever. - - -RETURN VALUE ------------- -The function shall return 0 if successful. Otherwise it shall return -1 and set -errno. - - -ERRORS ------- -*EINVAL*:: -The argument _ctx_ is NULL or _to_usec_ is larger than 1000000. - - -SEE ALSO --------- -linkmb:modbus_get_indication_timeout[3] -linkmb:modbus_get_response_timeout[3] -linkmb:modbus_set_response_timeout[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_write_and_read_registers.txt b/doc/modbus_write_and_read_registers.txt deleted file mode 100644 index e9b14bd4a..000000000 --- a/doc/modbus_write_and_read_registers.txt +++ /dev/null @@ -1,51 +0,0 @@ -modbus_write_and_read_registers(3) -================================== - - -NAME ----- -modbus_write_and_read_registers - write and read many registers in a single transaction - - -SYNOPSIS --------- -*int modbus_write_and_read_registers(modbus_t *'ctx', int 'write_addr', int 'write_nb', const uint16_t *'src', int 'read_addr', int 'read_nb', const uint16_t *'dest');* - - -DESCRIPTION ------------ -The *modbus_write_and_read_registers()* function shall write the content of the -_write_nb_ holding registers from the array 'src' to the address _write_addr_ of -the remote device then shall read the content of the _read_nb_ holding registers -to the address _read_addr_ of the remote device. The result of reading is stored -in _dest_ array as word values (16 bits). - -You must take care to allocate enough memory to store the results in _dest_ -(at least _nb_ * sizeof(uint16_t)). - -The function uses the Modbus function code 0x17 (write/read registers). - - -RETURN VALUE ------------- -The function shall return the number of read registers if successful. Otherwise -it shall return -1 and set errno. - - -ERRORS ------- -*EMBMDATA*:: -Too many registers requested, Too many registers to write - - -SEE ALSO --------- -linkmb:modbus_read_registers[3] -linkmb:modbus_write_register[3] -linkmb:modbus_write_registers[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_write_bit.txt b/doc/modbus_write_bit.txt deleted file mode 100644 index 3b4df9e30..000000000 --- a/doc/modbus_write_bit.txt +++ /dev/null @@ -1,38 +0,0 @@ -modbus_write_bit(3) -=================== - - -NAME ----- -modbus_write_bit - write a single bit - - -SYNOPSIS --------- -*int modbus_write_bit(modbus_t *'ctx', int 'addr', int 'status');* - - -DESCRIPTION ------------ -The *modbus_write_bit()* function shall write the status of _status_ at the -address _addr_ of the remote device. The value must be set to `TRUE` or `FALSE`. - -The function uses the Modbus function code 0x05 (force single coil). - - -RETURN VALUE ------------- -The function shall return 1 if successful. Otherwise it shall return -1 and set -errno. - - -SEE ALSO --------- -linkmb:modbus_read_bits[3] -linkmb:modbus_write_bits[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_write_bits.txt b/doc/modbus_write_bits.txt deleted file mode 100644 index 7f1af8488..000000000 --- a/doc/modbus_write_bits.txt +++ /dev/null @@ -1,45 +0,0 @@ -modbus_write_bits(3) -==================== - - -NAME ----- -modbus_write_bits - write many bits - - -SYNOPSIS --------- -*int modbus_write_bits(modbus_t *'ctx', int 'addr', int 'nb', const uint8_t *'src');* - - -DESCRIPTION ------------ -The *modbus_write_bits()* function shall write the status of the _nb_ bits -(coils) from _src_ at the address _addr_ of the remote device. The -_src_ array must contains bytes set to `TRUE` or `FALSE`. - -The function uses the Modbus function code 0x0F (force multiple coils). - - -RETURN VALUE ------------- -The function shall return the number of written bits if successful. Otherwise it -shall return -1 and set errno. - - -ERRORS ------- -*EMBMDATA*:: -Writing too many bits - - -SEE ALSO --------- -linkmb:modbus_read_bits[3] -linkmb:modbus_write_bit[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_write_register.txt b/doc/modbus_write_register.txt deleted file mode 100644 index 781b3af47..000000000 --- a/doc/modbus_write_register.txt +++ /dev/null @@ -1,38 +0,0 @@ -modbus_write_register(3) -======================== - - -NAME ----- -modbus_write_register - write a single register - - -SYNOPSIS --------- -*int modbus_write_register(modbus_t *'ctx', int 'addr', const uint16_t 'value');* - - -DESCRIPTION ------------ -The *modbus_write_register()* function shall write the value of _value_ -holding registers at the address _addr_ of the remote device. - -The function uses the Modbus function code 0x06 (preset single register). - - -RETURN VALUE ------------- -The function shall return 1 if successful. Otherwise it shall return -1 and set -errno. - - -SEE ALSO --------- -linkmb:modbus_read_registers[3] -linkmb:modbus_write_registers[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_write_registers.txt b/doc/modbus_write_registers.txt deleted file mode 100644 index a5654fb98..000000000 --- a/doc/modbus_write_registers.txt +++ /dev/null @@ -1,38 +0,0 @@ -modbus_write_registers(3) -========================= - - -NAME ----- -modbus_write_registers - write many registers - - -SYNOPSIS --------- -*int modbus_write_registers(modbus_t *'ctx', int 'addr', int 'nb', const uint16_t *'src');* - - -DESCRIPTION ------------ -The *modbus_write_registers()* function shall write the content of the _nb_ -holding registers from the array _src_ at address _addr_ of the remote device. - -The function uses the Modbus function code 0x10 (preset multiple registers). - - -RETURN VALUE ------------- -The function shall return the number of written registers if -successful. Otherwise it shall return -1 and set errno. - - -SEE ALSO --------- -linkmb:modbus_write_register[3] -linkmb:modbus_read_registers[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/docs/assets/client-sensors.excalidraw b/docs/assets/client-sensors.excalidraw new file mode 100644 index 000000000..bb8eba605 --- /dev/null +++ b/docs/assets/client-sensors.excalidraw @@ -0,0 +1,606 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 473, + "versionNonce": 1256506895, + "isDeleted": false, + "id": "ox1Blt2bzl0onmQfB7ZAN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 827.69921875, + "y": 210.68359375, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 251.7109375, + "height": 259.47265625, + "seed": 1508024704, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "sE5xq9Fz5VDTWcJGhJizg", + "type": "arrow" + }, + { + "id": "HZoAI_wR8CyRR9fVaFwSl", + "type": "arrow" + } + ], + "updated": 1660298248381, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 585, + "versionNonce": 720617423, + "isDeleted": false, + "id": "rjHz2X8U0ZDEyckj_tTSw", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 854.0546875, + "y": 287.16015625, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 199, + "height": 160, + "seed": 2100367744, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "vJNRjoY0dZcqOBw5RzuHn", + "type": "arrow" + } + ], + "updated": 1660298281789, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Reads temperatures\nfrom various\nModbus sensors (servers)\n\nS1: read index 0 -> 28\nS2: read index 0 -> 26\n...\n", + "baseline": 154, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Reads temperatures\nfrom various\nModbus sensors (servers)\n\nS1: read index 0 -> 28\nS2: read index 0 -> 26\n...\n" + }, + { + "type": "rectangle", + "version": 534, + "versionNonce": 2127380463, + "isDeleted": false, + "id": "mDgu1gSg34HbU-NAHPB30", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 420.2109375, + "y": 212.580078125, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 216.16406250000006, + "height": 172.06640624999997, + "seed": 1336837760, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "sE5xq9Fz5VDTWcJGhJizg", + "type": "arrow" + } + ], + "updated": 1660298020282, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 653, + "versionNonce": 2127484513, + "isDeleted": false, + "id": "UxvTnld8qh188IzV4uJ2q", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 443.79296875, + "y": 286.3515625, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 178, + "height": 80, + "seed": 759374208, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1660298185311, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Measures temperature\nand hygrometry:\n0: 28°C\n1: 32% ", + "baseline": 74, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Measures temperature\nand hygrometry:\n0: 28°C\n1: 32% " + }, + { + "type": "text", + "version": 349, + "versionNonce": 1469666511, + "isDeleted": false, + "id": "F6UUk6_B6uALmjxcHLYw4", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 488.19921875, + "y": 237.921875, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 79, + "height": 25, + "seed": 354006656, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1660298032950, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Sensor 1", + "baseline": 18, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Sensor 1" + }, + { + "type": "text", + "version": 224, + "versionNonce": 864770191, + "isDeleted": false, + "id": "v7q2uvVFHZBvjr-y_ZJKl", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 884.5546875, + "y": 238.328125, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 150, + "height": 25, + "seed": 2108436864, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1660299132232, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "libmodbus client", + "baseline": 18, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "libmodbus client" + }, + { + "type": "arrow", + "version": 925, + "versionNonce": 27208751, + "isDeleted": false, + "id": "sE5xq9Fz5VDTWcJGhJizg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 656.03125, + "y": 313.29799967005374, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 154.05078125, + "height": 8.294130761394001, + "seed": 455209344, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1660298248381, + "link": null, + "locked": false, + "startBinding": { + "elementId": "mDgu1gSg34HbU-NAHPB30", + "focus": 0.08648410639065775, + "gap": 19.65625 + }, + "endBinding": { + "elementId": "ox1Blt2bzl0onmQfB7ZAN", + "focus": 0.08133464876815498, + "gap": 17.6171875 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 154.05078125, + 8.294130761394001 + ] + ] + }, + { + "type": "text", + "version": 709, + "versionNonce": 650449167, + "isDeleted": false, + "id": "Q6P32mRyop5JlKGPB3tei", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 5.552321937284518, + "x": 679.345703125, + "y": 433.1444738051471, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 79, + "height": 15, + "seed": 1091054019, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1660298261423, + "link": null, + "locked": false, + "fontSize": 11.542968749999993, + "fontFamily": 1, + "text": "TCP or serial", + "baseline": 10, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "TCP or serial" + }, + { + "type": "text", + "version": 707, + "versionNonce": 1999616833, + "isDeleted": false, + "id": "vETmEnYJoC4MKv7ysqjAv", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0.06779103263247777, + "x": 683.3417968749999, + "y": 281.21582031250006, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 79, + "height": 15, + "seed": 1043479501, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1660298255264, + "link": null, + "locked": false, + "fontSize": 11.542968749999993, + "fontFamily": 1, + "text": "TCP or serial", + "baseline": 10, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "TCP or serial" + }, + { + "type": "rectangle", + "version": 587, + "versionNonce": 85990017, + "isDeleted": false, + "id": "pHhCP3DIU5fE4v3yi3obG", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 426.513671875, + "y": 424.2646484375, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 216.16406250000006, + "height": 172.06640624999997, + "seed": 1733016847, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "sE5xq9Fz5VDTWcJGhJizg", + "type": "arrow" + }, + { + "id": "HZoAI_wR8CyRR9fVaFwSl", + "type": "arrow" + } + ], + "updated": 1660298157321, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 708, + "versionNonce": 1824105825, + "isDeleted": false, + "id": "3oXICnKHoOzNPyb-PxNbt", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 450.095703125, + "y": 498.0361328125, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 178, + "height": 80, + "seed": 1378197345, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1660298208836, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Measures temperature\nand hygrometry:\n0: 26°C\n1: 40% ", + "baseline": 74, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Measures temperature\nand hygrometry:\n0: 26°C\n1: 40% " + }, + { + "type": "text", + "version": 400, + "versionNonce": 747588289, + "isDeleted": false, + "id": "rD_cbOE0Mwz-sfB0YqcaJ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 494.501953125, + "y": 449.6064453125, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 88, + "height": 25, + "seed": 68109103, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1660298172011, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Sensor 2", + "baseline": 18, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Sensor 2" + }, + { + "type": "arrow", + "version": 1150, + "versionNonce": 1550306895, + "isDeleted": false, + "id": "HZoAI_wR8CyRR9fVaFwSl", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 666.49609375, + "y": 532.2655116636315, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 145.8515625, + "height": 133.46442554799017, + "seed": 1409289601, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1660298248381, + "link": null, + "locked": false, + "startBinding": { + "elementId": "pHhCP3DIU5fE4v3yi3obG", + "focus": 0.7718436212944663, + "gap": 23.818359375 + }, + "endBinding": { + "elementId": "ox1Blt2bzl0onmQfB7ZAN", + "focus": 0.28922965891088237, + "gap": 15.3515625 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 145.8515625, + -133.46442554799017 + ] + ] + }, + { + "id": "feq5Rn2_gEHIfIzRPvNu6", + "type": "rectangle", + "x": 832.283203125, + "y": 515.0556640625, + "width": 255, + "height": 76.1171875, + "angle": 0, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "strokeSharpness": "sharp", + "seed": 826152751, + "version": 65, + "versionNonce": 19459567, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "n4_rv3VhjyMXLSFrP52Vw" + }, + { + "id": "vJNRjoY0dZcqOBw5RzuHn", + "type": "arrow" + } + ], + "updated": 1660298281790, + "link": null, + "locked": false + }, + { + "id": "n4_rv3VhjyMXLSFrP52Vw", + "type": "text", + "x": 837.283203125, + "y": 540.6142578125, + "width": 245, + "height": 25, + "angle": 0, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "strokeSharpness": "sharp", + "seed": 555916079, + "version": 20, + "versionNonce": 1538604143, + "isDeleted": false, + "boundElements": null, + "updated": 1660298276528, + "link": null, + "locked": false, + "text": "Database or logs", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "baseline": 18, + "containerId": "feq5Rn2_gEHIfIzRPvNu6", + "originalText": "Database or logs" + }, + { + "id": "vJNRjoY0dZcqOBw5RzuHn", + "type": "arrow", + "x": 958.509765625, + "y": 472.4150390625, + "width": 0, + "height": 39.33984375, + "angle": 0, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "strokeSharpness": "round", + "seed": 1036508641, + "version": 20, + "versionNonce": 2130978977, + "isDeleted": false, + "boundElements": null, + "updated": 1660298281790, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 39.33984375 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "rjHz2X8U0ZDEyckj_tTSw", + "focus": -0.04979978015075378, + "gap": 25.2548828125 + }, + "endBinding": { + "elementId": "feq5Rn2_gEHIfIzRPvNu6", + "focus": -0.009987745098039217, + "gap": 3.30078125 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/assets/client-sensors.webp b/docs/assets/client-sensors.webp new file mode 100644 index 0000000000000000000000000000000000000000..c827fb51b95e78df35b99f87c1f6e1deeaa8e2dc GIT binary patch literal 36052 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 literal 0 HcmV?d00001 diff --git a/docs/assets/server-grafana.excalidraw b/docs/assets/server-grafana.excalidraw new file mode 100644 index 000000000..96e48b7ca --- /dev/null +++ b/docs/assets/server-grafana.excalidraw @@ -0,0 +1,488 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 938, + "versionNonce": 1589965569, + "isDeleted": false, + "id": "ox1Blt2bzl0onmQfB7ZAN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 471.66796875, + "y": 61.640625, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 251.7109375, + "height": 346.86328125, + "seed": 1508024704, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "sE5xq9Fz5VDTWcJGhJizg", + "type": "arrow" + }, + { + "id": "RdDFVItfRo8k8NarDHSp-", + "type": "arrow" + } + ], + "updated": 1660300922199, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1153, + "versionNonce": 977576399, + "isDeleted": false, + "id": "rjHz2X8U0ZDEyckj_tTSw", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 498.0234375, + "y": 138.1171875, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 187, + "height": 240, + "seed": 2100367744, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1660300922199, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Collect data from CPU,\nIO and memory with\ntimestamp\n\n0: 0.1 %\n1: 1456 b/s\n2: 4567 b\n3: 1660190233 (time)\n...\n\n100: false (alert)\n", + "baseline": 234, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Collect data from CPU,\nIO and memory with\ntimestamp\n\n0: 0.1 %\n1: 1456 b/s\n2: 4567 b\n3: 1660190233 (time)\n...\n\n100: false (alert)\n" + }, + { + "type": "rectangle", + "version": 772, + "versionNonce": 1558709263, + "isDeleted": false, + "id": "mDgu1gSg34HbU-NAHPB30", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 896.8984375, + "y": 44.853515625, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 216.16406250000006, + "height": 172.06640624999997, + "seed": 1336837760, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "sE5xq9Fz5VDTWcJGhJizg", + "type": "arrow" + } + ], + "updated": 1660300917063, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 607, + "versionNonce": 747285039, + "isDeleted": false, + "id": "F6UUk6_B6uALmjxcHLYw4", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 964.88671875, + "y": 70.1953125, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 84, + "height": 25, + "seed": 354006656, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1660300917063, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Grafana", + "baseline": 18, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Grafana" + }, + { + "type": "text", + "version": 556, + "versionNonce": 1932158625, + "isDeleted": false, + "id": "v7q2uvVFHZBvjr-y_ZJKl", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 528.5234375, + "y": 89.28515625, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 159, + "height": 25, + "seed": 2108436864, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1660300922199, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "libmodbus server", + "baseline": 18, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "libmodbus server" + }, + { + "type": "arrow", + "version": 2401, + "versionNonce": 1251011297, + "isDeleted": false, + "id": "sE5xq9Fz5VDTWcJGhJizg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 748.0898437500001, + "y": 161.19331310212854, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 134.5039062499999, + "height": 30.970574872077407, + "seed": 455209344, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1660300922199, + "link": null, + "locked": false, + "startBinding": { + "elementId": "ox1Blt2bzl0onmQfB7ZAN", + "gap": 24.710937500000114, + "focus": -0.19349510698149744 + }, + "endBinding": { + "elementId": "mDgu1gSg34HbU-NAHPB30", + "gap": 14.3046875, + "focus": 0.2600477394392373 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 134.5039062499999, + -30.970574872077407 + ] + ] + }, + { + "type": "text", + "version": 1184, + "versionNonce": 1589324399, + "isDeleted": false, + "id": "Q6P32mRyop5JlKGPB3tei", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 6.077759617018872, + "x": 746.0369822154072, + "y": 102.90361683493032, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 111, + "height": 29, + "seed": 1091054019, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1660300917063, + "link": null, + "locked": false, + "fontSize": 11.542968749999993, + "fontFamily": 1, + "text": "TCP read requests\n(polling)", + "baseline": 25, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "TCP read requests\n(polling)" + }, + { + "id": "ViVqW_nxoEJO1DpluuMzF", + "type": "image", + "x": 923.3162172379032, + "y": 116.828369140625, + "width": 163.32850302419354, + "height": 63.289794921875, + "angle": 0, + "strokeColor": "transparent", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "strokeSharpness": "round", + "seed": 953641007, + "version": 332, + "versionNonce": 1122873217, + "isDeleted": false, + "boundElements": null, + "updated": 1660300917064, + "link": null, + "locked": false, + "status": "saved", + "fileId": "fe56123c11422301d020f581b74d4397ab49e99c", + "scale": [ + 1, + 1 + ] + }, + { + "id": "RdDFVItfRo8k8NarDHSp-", + "type": "arrow", + "x": 898.5878906250001, + "y": 340.31123325850484, + "width": 158.6875000000001, + "height": 28.44727928318008, + "angle": 0, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "strokeSharpness": "round", + "seed": 595270337, + "version": 1096, + "versionNonce": 1842538177, + "isDeleted": false, + "boundElements": null, + "updated": 1660300922199, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -158.6875000000001, + -28.44727928318008 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "ro1CNQcmtpkib2r-uhEJo", + "gap": 10.179687499999886, + "focus": -0.37043558235421187 + }, + "endBinding": { + "elementId": "ox1Blt2bzl0onmQfB7ZAN", + "gap": 16.521484375, + "focus": 0.2615821499314503 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "ro1CNQcmtpkib2r-uhEJo", + "type": "rectangle", + "x": 908.767578125, + "y": 248.8369140625, + "width": 216.01953125, + "height": 154, + "angle": 0, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "strokeSharpness": "sharp", + "seed": 1261656367, + "version": 298, + "versionNonce": 866798383, + "isDeleted": false, + "boundElements": [ + { + "id": "RdDFVItfRo8k8NarDHSp-", + "type": "arrow" + }, + { + "type": "text", + "id": "zWdPKja_yF9g4yVY1kvXH" + } + ], + "updated": 1660300917064, + "link": null, + "locked": false + }, + { + "id": "zWdPKja_yF9g4yVY1kvXH", + "type": "text", + "x": 913.767578125, + "y": 253.8369140625, + "width": 206, + "height": 75, + "angle": 0, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "strokeSharpness": "sharp", + "seed": 76196961, + "version": 380, + "versionNonce": 1800831809, + "isDeleted": false, + "boundElements": null, + "updated": 1660300917064, + "link": null, + "locked": false, + "text": "\nTriggers alert when\nservice is down", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "baseline": 68, + "containerId": "ro1CNQcmtpkib2r-uhEJo", + "originalText": "\nTriggers alert when\nservice is down" + }, + { + "type": "text", + "version": 1386, + "versionNonce": 2109148495, + "isDeleted": false, + "id": "DZ2rUAdMymO7ajB4Xr_Kf", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0.17070993938211565, + "x": 774.3998831194928, + "y": 298.59613347989244, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 107, + "height": 15, + "seed": 109244929, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1660300917064, + "link": null, + "locked": false, + "fontSize": 11.542968749999993, + "fontFamily": 1, + "text": "TCP write request", + "baseline": 10, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "TCP write request" + }, + { + "id": "Os6j9M5Ipt4Ay74nFj29L", + "type": "text", + "x": 937.599609375, + "y": 348.8330078125, + "width": 163, + "height": 30, + "angle": 0, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "strokeSharpness": "sharp", + "seed": 577132609, + "version": 307, + "versionNonce": 654734113, + "isDeleted": false, + "boundElements": null, + "updated": 1660300917064, + "link": null, + "locked": false, + "text": "Write True to address 100\non issue", + "fontSize": 12.1, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "baseline": 26, + "containerId": null, + "originalText": "Write True to address 100\non issue" + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": { + "fe56123c11422301d020f581b74d4397ab49e99c": { + "mimeType": "image/jpeg", + "id": "fe56123c11422301d020f581b74d4397ab49e99c", + "dataURL": "data:image/jpeg;base64,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", + "created": 1660300358240 + } + } +} \ No newline at end of file diff --git a/docs/assets/server-grafana.webp b/docs/assets/server-grafana.webp new file mode 100644 index 0000000000000000000000000000000000000000..636dc3a6f6890ec21f95578650da259fdaa96ab4 GIT binary patch literal 37256 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 h00000000000000000000000000000000000006rJluQ5s literal 0 HcmV?d00001 diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..9601cc826 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,263 @@ +# libmodbus + +A featureful and portable Open Source Modbus library. + +## Description + +libmodbus is a library to send/receive data with a device which respects the +Modbus protocol. This library contains various backends to communicate over +different networks (eg. serial in RTU mode or Ethernet in TCP IPv4/IPv6). The + site provides documentation about the [Modbus +Specifications and Implementation Guides](http://www.modbus.org/specs.php). + +libmodbus provides an abstraction of the lower communication layers and offers +the same API on all supported platforms. + +This documentation presents an overview of libmodbus concepts, describes how +libmodbus abstracts Modbus communication with different hardware and platforms +and provides a reference manual for the functions provided by the libmodbus +library. + +## Use cases + +The library can be used to write a: + +- **client**, the application reads/writes data from various devices. +- **server**, the application provides data to several clients. + +
+ +
A libmodbus client that reads only the temperatures from sensors.
+
+ +
+ +
A libmodbus server that exposes data to a Grafana service.
+
+ +## Contexts + +The Modbus protocol supports several transport protocols (eg. serial RTU, +Ethernet TCP) called backends in *libmodbus*. + +The first step is to allocate and set a `modbus_t` context according to the +required backend (RTU or TCP) with a dedicated function, such as +[modbus_new_rtu](modbus_new_rtu). +The function will return an opaque structure called `modbus_t` containing all +necessary information to establish a connection with other Modbus devices +according to the selected backend. + +Once this context has been created, you can use use the common API provided by +*libmodbus* to read/write or set the various timeouts. With this common API, +it's easy to switch the backend of your application from RTU to TCP IPv6 for +example. + +### RTU Context + +The RTU backend (Remote Terminal Unit) is used in serial communication and makes +use of a compact, binary representation of the data for protocol communication. +The RTU format follows the commands/data with a cyclic redundancy check checksum +as an error check mechanism to ensure the reliability of data. Modbus RTU is the +most common implementation available for Modbus. A Modbus RTU message must be +transmitted continuously without inter-character hesitations (extract from +Wikipedia, [Modbus](http://en.wikipedia.org/wiki/Modbus) as of Mar. 13, 2011, +20:51 GMT). + +The Modbus RTU framing calls a slave, a device/service which handle Modbus +requests, and a master, a client which send requests. The communication is +always initiated by the master. + +Many Modbus devices can be connected together on the same physical link so +before sending a message, you must set the slave (receiver) with +[modbus_set_slave](mobus_set_slave). If you're running a slave, its slave number +will be used to filter received messages. + +The libmodbus implementation of RTU isn't time based as stated in original +Modbus specification, instead all bytes are sent as fast as possible and a +response or an indication is considered complete when all expected characters +have been received. This implementation offers very fast communication but you +must take care to set a response timeout of slaves less than response timeout of +master (ortherwise other slaves may ignore master requests when one of the slave +is not responding). + +To create a Modbus RTU context, you should use [modbus_new_rtu](modbus_new_rtu). + +You can tweak the serial mode with the following functions: + +- [modbus_rtu_get_serial_mode](modbus_rtu_get_serial_mode) +- [modbus_rtu_set_serial_mode](modbus_rtu_set_serial_mode) +- [modbus_rtu_get_rts](modbus_rtu_get_rts) +- [modbus_rtu_set_rts](modbus_rtu_set_rts) +- [modbus_rtu_set_custom_rts](modbus_rtu_set_custom_rts) +- [modbus_rtu_get_rts_delay](modbus_rtu_get_rts_delay) +- [modbus_rtu_set_rts_delay](modbus_rtu_set_rts_delay) + +### TCP (IPv4) Context + +The TCP backend implements a Modbus variant used for communications over +TCP/IPv4 networks. It does not require a checksum calculation as lower layer +takes care of the same. + +To create a Modbus TCP context, you should use [modbus_new_tcp](modbus_new_tcp). + +### TCP PI (IPv4 and IPv6) Context + +The TCP PI (Protocol Independent) backend implements a Modbus variant used for +communications over TCP IPv4 and IPv6 networks. It does not require a checksum +calculation as lower layer takes care of the same. + +Contrary to the TCP IPv4 only backend, the TCP PI backend offers hostname +resolution but it consumes about 1Kb of additional memory. + +Create a Modbus TCP PI context, you should use [modbus_new_tcp_pi](modbus_new_tcp_pi). + +## Connection + +The following functions are provided to establish and close a connection with +Modbus devices: + +- [modbus_connect](modbus_connect) establishes a connection. +- [modbus_close](modbus_close) closes a connection. +- [modbus_flush](modbus_flush) flushed a connection. + +In RTU, you should define the slave ID of your client with +[modbus_set_slave](modbus_set_slave). + +To analyse the exchanged data, you can enable the debug mode with +[modbus_set_debug](modbus_set_debug). + +Once you have completed the communication or at the end of your program, you +should free the resources with the common function, [modbus_free](modbus_free) + +## Reads and writes from the client + +The Modbus protocol defines different data types and functions to read and write +them from/to remote devices. The following functions are used by the clients to +send Modbus requests: + +To read data: + +- [modbus_read_bits](modbus_read_bits) +- [modbus_read_input_bits](modbus_read_input_bits) +- [modbus_read_registers](modbus_read_registers) +- [modbus_read_input_registers](modbus_read_input_registers) +- [modbus_report_slave_id](modbus_report_slave_id) + +To write data: + +- [modbus_write_bit](modbus_write_bit) +- [modbus_write_register](modbus_write_register) +- [modbus_write_bits](modbus_write_bits) +- [modbus_write_registers](modbus_write_registers) + +To write and read data in a single operation: + +- [modbus_write_and_read_registers](modbus_write_and_read_registers) + +To send and receive low-level requests: + +- [modbus_send_raw_request](modbus_send_raw_request) +- [modbus_receive_confirmation](modbus_receive_confirmation) + +To reply to an exception: + +- [modbus_reply_exception](modbus_reply_exception) + +## Handling requests from server + +The server is waiting for request from clients and must answer when it is +concerned by the request. The libmodbus offers the following functions to +handle requests: + +Data mapping: + +- [modbus_mapping_new](modbus_mapping_new) +- [modbus_mapping_free](modbus_mapping_free) + +Receive: + +- [modbus_receive](modbus_receive) + +Reply: + +- [modbus_reply](modbus_reply) +- [modbus_reply_exception](modbus_reply_exception) + +## Advanced functions + +Timeout settings: + +- [modbus_get_byte_timeout](modbus_get_byte_timeout) +- [modbus_set_byte_timeout](modbus_set_byte_timeout) +- [modbus_get_response_timeout](modbus_get_response_timeout) +- [modbus_set_response_timeout](modbus_set_response_timeout) + +Error recovery mode: + +- [modbus_set_error_recovery](modbus_set_error_recovery) + +Setter/getter of internal socket: + +- [modbus_set_socket](modbus_set_socket) +- [modbus_get_socket](modbus_get_socket) + +Information about header: + +- [modbus_get_header_length](modbus_get_header_length) + +## Data handling + +Macros for data manipulation: + +- `MODBUS_GET_HIGH_BYTE(data)`, extracts the high byte from a byte +- `MODBUS_GET_LOW_BYTE(data)`, extracts the low byte from a byte +- `MODBUS_GET_INT64_FROM_INT16(tab_int16, index)`, builds an int64 from the four first int16 starting at tab_int16[index] +- `MODBUS_GET_INT32_FROM_INT16(tab_int16, index)`, builds an int32 from the two first int16 starting at tab_int16[index] +- `MODBUS_GET_INT16_FROM_INT8(tab_int8, index)`, builds an int16 from the two first int8 starting at tab_int8[index] +- `MODBUS_SET_INT16_TO_INT8(tab_int8, index, value)`, set an int16 value into the two first bytes starting at tab_int8[index] +- `MODBUS_SET_INT32_TO_INT16(tab_int16, index, value)`, set an int32 value into the two first int16 starting at tab_int16[index] +- `MODBUS_SET_INT64_TO_INT16(tab_int16, index, value)`, set an int64 value into the four first int16 starting at tab_int16[index] + +Handling of bits and bytes: + +- [modbus_set_bits_from_byte](modbus_set_bits_from_byte) +- [modbus_set_bits_from_bytes](modbus_set_bits_from_bytes) +- [modbus_get_byte_from_bits](modbus_get_byte_from_bits) + +Set or get float numbers: + +- [modbus_get_float_abcd](modbus_get_float_abcd) +- [modbus_set_float_abcd](modbus_set_float_abcd) +- [modbus_get_float_badc](modbus_get_float_badc) +- [modbus_set_float_badc](modbus_set_float_badc) +- [modbus_get_float_cdab](modbus_get_float_cdab) +- [modbus_set_float_cdab](modbus_set_float_cdab) +- [modbus_get_float_dcba](modbus_get_float_dcba) +- [modbus_set_float_dcba](modbus_set_float_dcba) +- [modbus_get_float](modbus_get_float) **deprecated** +- [modbus_set_float](modbus_set_float) **deprecated** + +## Error handling + +The libmodbus functions handle errors using the standard conventions found on +POSIX systems. Generally, this means that upon failure a libmodbus function +shall return either a NULL value (if returning a pointer) or a negative value +(if returning an integer), and the actual error code shall be stored in the +`errno` variable. + +The *modbus_strerror()* function is provided to translate libmodbus-specific +error codes into error message strings; for details refer to +[modbus_strerror](modbus_strerror). + +## Miscellaneous + +The `_LIBMODBUS_VERSION_STRING_` constant indicates the libmodbus version the +program has been compiled against. The variables 'libmodbus_version_major', +'libmodbus_version_minor', 'libmodbus_version_micro' give the version the +program is linked against. + +## Copying + +Free use of this software is granted under the terms of the GNU Lesser General +Public License (LGPL v2.1+). For details see the file `COPYING.LESSER` included +with the libmodbus distribution. diff --git a/doc/modbus_close.txt b/docs/modbus_close.md similarity index 53% rename from doc/modbus_close.txt rename to docs/modbus_close.md index 5a0afc0fd..6fe760666 100644 --- a/doc/modbus_close.txt +++ b/docs/modbus_close.md @@ -1,32 +1,27 @@ -modbus_close(3) -=============== +# modbus_close +## Name -NAME ----- modbus_close - close a Modbus connection +## Synopsis -SYNOPSIS --------- -*void modbus_close(modbus_t *'ctx');* +```c +void modbus_close(modbus_t *ctx); +``` +## Description -DESCRIPTION ------------ The *modbus_close()* function shall close the connection established with the backend set in the context. +## Return value -RETURN VALUE ------------- There is no return value. +## Example -EXAMPLE -------- -[source,c] -------------------- +```c modbus_t *ctx; ctx = modbus_new_tcp("127.0.0.1", 502); @@ -38,14 +33,8 @@ if (modbus_connect(ctx) == -1) { modbus_close(ctx); modbus_free(ctx); -------------------- +``` -SEE ALSO --------- -linkmb:modbus_connect[3] +## See also - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - +- [modbus_connect](modbus_connect) diff --git a/doc/modbus_connect.txt b/docs/modbus_connect.md similarity index 61% rename from doc/modbus_connect.txt rename to docs/modbus_connect.md index a4437ec96..f3bf5e3f1 100644 --- a/doc/modbus_connect.txt +++ b/docs/modbus_connect.md @@ -1,35 +1,30 @@ -modbus_connect(3) -================= +# modbus_connect +## Name -NAME ----- modbus_connect - establish a Modbus connection +## Synopsis -SYNOPSIS --------- -*int modbus_connect(modbus_t *'ctx');* +```c +int modbus_connect(modbus_t *ctx); +``` +## Description -DESCRIPTION ------------ The *modbus_connect()* function shall establish a connection to a Modbus server, a network or a bus using the context information of libmodbus context given in argument. +## Return value -RETURN VALUE ------------- The function shall return 0 if successful. Otherwise it shall return -1 and set errno to one of the values defined by the system calls of the underlying platform. +## Example -EXAMPLE -------- -[source,c] -------------------- +```c modbus_t *ctx; ctx = modbus_new_tcp("127.0.0.1", 502); @@ -38,15 +33,8 @@ if (modbus_connect(ctx) == -1) { modbus_free(ctx); return -1; } -------------------- +``` +## See also -SEE ALSO --------- -linkmb:modbus_close[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - +- [modbus_close](modbus_close) diff --git a/doc/modbus_flush.txt b/docs/modbus_flush.md similarity index 54% rename from doc/modbus_flush.txt rename to docs/modbus_flush.md index f4f9b166c..5a9f09be6 100644 --- a/doc/modbus_flush.txt +++ b/docs/modbus_flush.md @@ -1,30 +1,21 @@ -modbus_flush(3) -=============== +# modbus_flush +## Name -NAME ----- modbus_flush - flush non-transmitted data +## Synopsis -SYNOPSIS --------- -*int modbus_flush(modbus_t *'ctx');* +```c +int modbus_flush(modbus_t *ctx); +``` +## Description -DESCRIPTION ------------ The *modbus_flush()* function shall discard data received but not read to the socket or file descriptor associated to the context 'ctx'. +## Return value -RETURN VALUE ------------- The function shall return 0 or the number of flushed bytes if successful. Otherwise it shall return -1 and set errno. - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/docs/modbus_free.md b/docs/modbus_free.md new file mode 100644 index 000000000..816f8d306 --- /dev/null +++ b/docs/modbus_free.md @@ -0,0 +1,19 @@ +# modbus_free + +## Name + +modbus_free - free a libmodbus context + +## Synopsis + +```c +void modbus_free(modbus_t *ctx); +``` + +## Description + +The *modbus_free()* function shall free an allocated modbus_t structure. + +## Return value + +There is no return values. diff --git a/docs/modbus_get_byte_from_bits.md b/docs/modbus_get_byte_from_bits.md new file mode 100644 index 000000000..84b4b6500 --- /dev/null +++ b/docs/modbus_get_byte_from_bits.md @@ -0,0 +1,26 @@ +# modbus_get_byte_from_bits + +## Name + +modbus_get_byte_from_bits - get the value from many bits + +## Synopsis + +```c +uint8_t modbus_get_byte_from_bits(const uint8_t *src, int index, unsigned int nb_bits); +``` + +## Description + +The *modbus_get_byte_from_bits()* function shall extract a value from many +bits. All `nb_bits` bits from `src` at position `index` will be read as a +single value. To obtain a full byte, set nb_bits to 8. + +## Return value + +The function shall return a byte containing the bits read. + +## See also + +- [modbus_set_bits_from_byte](modbus_set_bits_from_byte) +- [modbus_set_bits_from_bytes](modbus_set_bits_from_bytes) diff --git a/docs/modbus_get_byte_timeout.md b/docs/modbus_get_byte_timeout.md new file mode 100644 index 000000000..04a5732cb --- /dev/null +++ b/docs/modbus_get_byte_timeout.md @@ -0,0 +1,38 @@ +# modbus_get_byte_timeout + +## Name + +modbus_get_byte_timeout - get timeout between bytes + +## Synopsis + +```c +int modbus_get_byte_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec); +``` + +## Description + +The *modbus_get_byte_timeout()* function shall store the timeout interval +between two consecutive bytes of the same message in the `to_sec` and `to_usec` +arguments. + +## Return value + +The function shall return 0 if successful. Otherwise it shall return -1 and set +errno. + +## Example + +```c +uint32_t to_sec; +uint32_t to_usec; + +/* Save original timeout */ +modbus_get_byte_timeout(ctx, &to_sec, &to_usec); +``` + +## See also + +- [modbus_set_byte_timeout](modbus_set_byte_timeout) +- [modbus_get_response_timeout](modbus_get_response_timeout) +- [modbus_set_response_timeout](modbus_set_response_timeout) diff --git a/docs/modbus_get_float.md b/docs/modbus_get_float.md new file mode 100644 index 000000000..8eec56043 --- /dev/null +++ b/docs/modbus_get_float.md @@ -0,0 +1,31 @@ +# modbus_get_float + +## Name + +modbus_get_float - get a float value from 2 registers + +## Synopsis + +```c +float modbus_get_float(const uint16_t *src); +``` + +Warning, this function is *deprecated* since libmodbus v3.2.0 and has been +replaced by *modbus_get_float_dcba()*. + +## Description + +The *modbus_get_float()* function shall get a float from 4 bytes in Modbus +format (DCBA byte order). The `src` array must be a pointer on two 16 bits +values, for example, if the first word is set to 0x4465 and the second to +0x229a, the float value will be 916.540649. + +## Return value + +The function shall return a float. + +## See also + +- [modbus_set_float](modbus_set_float) +- [modbus_set_float_dcba](modbus_set_float_dcba) +- [modbus_get_float_dcba](modbus_get_float_dcba) diff --git a/docs/modbus_get_float_abcd.md b/docs/modbus_get_float_abcd.md new file mode 100644 index 000000000..2f21271ce --- /dev/null +++ b/docs/modbus_get_float_abcd.md @@ -0,0 +1,29 @@ +# modbus_get_float_abcd + +## Name + +modbus_get_float_abcd - get a float value from 2 registers in ABCD byte order + +## Synopsis + +```c +float modbus_get_float_abcd(const uint16_t *src); +``` + +## Description + +The *modbus_get_float_abcd()* function shall get a float from 4 bytes in usual +Modbus format. The `src` array must be a pointer on two 16 bits values, for +example, if the first word is set to 0x0020 and the second to 0xF147, the float +value will be read as 123456.0. + +## Return value + +The function shall return a float. + +## See also + +- [modbus_set_float_abcd](modbus_set_float_abcd) +- [modbus_get_float_badc](modbus_get_float_badc) +- [modbus_get_float_cdab](modbus_get_float_cdab) +- [modbus_get_float_dcba](modbus_get_float_dcba) diff --git a/docs/modbus_get_float_badc.md b/docs/modbus_get_float_badc.md new file mode 100644 index 000000000..a730e8cfd --- /dev/null +++ b/docs/modbus_get_float_badc.md @@ -0,0 +1,29 @@ +# modbus_get_float_badc + +## Name + +modbus_get_float_badc - get a float value from 2 registers in BADC byte order + +## Synopsis + +```c +float modbus_get_float_badc(const uint16_t *src); +``` + +## Description + +The *modbus_get_float_badc()* function shall get a float from 4 bytes with +swapped bytes (BADC instead of ABCD). The `src` array must be a pointer on two +16 bits values, for example, if the first word is set to 0x2000 and the second +to 0x47F1, the float value will be read as 123456.0. + +## Return value + +The function shall return a float. + +## See also + +- [modbus_set_float_badc](modbus_set_float_badc) +- [modbus_get_float_abcd](modbus_get_float_abcd) +- [modbus_get_float_cdab](modbus_get_float_cdab) +- [modbus_get_float_dcba](modbus_get_float_dcba) diff --git a/docs/modbus_get_float_cdab.md b/docs/modbus_get_float_cdab.md new file mode 100644 index 000000000..fb4ba3550 --- /dev/null +++ b/docs/modbus_get_float_cdab.md @@ -0,0 +1,29 @@ +# modbus_get_float_cdab + +## Name + +modbus_get_float_cdab - get a float value from 2 registers in CDAB byte order + +## Synopsis + +```c +float modbus_get_float_cdab(const uint16_t *src); +``` + +## Description + +The *modbus_get_float_cdab()* function shall get a float from 4 bytes with +swapped words (CDAB order instead of ABCD). The `src` array must be a pointer on +two 16 bits values, for example, if the first word is set to F147 and the second +to 0x0020, the float value will be read as 123456.0. + +## Return value + +The function shall return a float. + +## See also + +- [modbus_set_float_cdab](modbus_set_float_cdab) +- [modbus_get_float_abcd](modbus_get_float_abcd) +- [modbus_get_float_badc](modbus_get_float_badc) +- [modbus_get_float_dcba](modbus_get_float_dcba) diff --git a/docs/modbus_get_float_dcba.md b/docs/modbus_get_float_dcba.md new file mode 100644 index 000000000..0d22c352c --- /dev/null +++ b/docs/modbus_get_float_dcba.md @@ -0,0 +1,29 @@ +# modbus_get_float_dcba + +## Name + +modbus_get_float_dcba - get a float value from 2 registers in DCBA byte order + +## Synopsis + +```c +float modbus_get_float_dcba(const uint16_t *src); +``` + +## Description + +The *modbus_get_float_dcba()* function shall get a float from 4 bytes in +inverted Modbus format (DCBA order instead of ABCD). The `src` array must be a +pointer on two 16 bits values, for example, if the first word is set to 0x47F1 +and the second to 0x2000, the float value will be read as 123456.0. + +## Return value + +The function shall return a float. + +## See also + +- [modbus_set_float_dcba](modbus_set_float_dcba) +- [modbus_get_float_abcd](modbus_get_float_abcd) +- [modbus_get_float_badc](modbus_get_float_badc) +- [modbus_get_float_cdab](modbus_get_float_cdab) diff --git a/docs/modbus_get_header_length.md b/docs/modbus_get_header_length.md new file mode 100644 index 000000000..df1ed6669 --- /dev/null +++ b/docs/modbus_get_header_length.md @@ -0,0 +1,21 @@ +# modbus_get_header_length + +## Name + +modbus_get_header_length - retrieve the current header length + +## Synopsis + +```c +int modbus_get_header_length(modbus_t *ctx); +``` + +## Description + +The *modbus_get_header_length()* function shall retrieve the current header +length from the backend. This function is convenient to manipulate a message and +so its limited to low-level operations. + +## Return value + +The header length as integer value. diff --git a/docs/modbus_get_indication_timeout.md b/docs/modbus_get_indication_timeout.md new file mode 100644 index 000000000..11a9f866c --- /dev/null +++ b/docs/modbus_get_indication_timeout.md @@ -0,0 +1,39 @@ +# modbus_get_indication_timeout + +## Name + +modbus_get_indication_timeout - get timeout used to wait for an indication (request received by a server). + +## Synopsis + +```c +int modbus_get_indication_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec); +``` + +## Description + +The *modbus_get_indication_timeout()* function shall store the timeout interval +used to wait for an indication in the `to_sec` and `to_usec` arguments. +Indication is the term used by the Modbus protocol to designate a request +received by the server. + +The default value is zero, it means the server will wait forever. + +## Return value + +The function shall return 0 if successful. Otherwise it shall return -1 and set +errno. + +```c +uint32_t to_sec; +uint32_t to_usec; + +/* Save original timeout */ +modbus_get_indication_timeout(ctx, &to_sec, &to_usec); +``` + +## See also + +- [modbus_set_indication_timeout](modbus_set_indication_timeout) +- [modbus_get_response_timeout](modbus_get_response_timeout) +- [modbus_set_response_timeout](modbus_set_response_timeout) diff --git a/docs/modbus_get_response_timeout.md b/docs/modbus_get_response_timeout.md new file mode 100644 index 000000000..93464c221 --- /dev/null +++ b/docs/modbus_get_response_timeout.md @@ -0,0 +1,40 @@ +# modbus_get_response_timeout + +## Name + +modbus_get_response_timeout - get timeout for response + +## Synopsis + +```c +int modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec); +``` + +## Description + +The *modbus_get_response_timeout()* function shall return the timeout interval +used to wait for a response in the `to_sec` and `to_usec` arguments. + +## Return value + +The function shall return 0 if successful. Otherwise it shall return -1 and set +errno. + +Example: + +```c +uint32_t old_response_to_sec; +uint32_t old_response_to_usec; + +/* Save original timeout */ +modbus_get_response_timeout(ctx, &old_response_to_sec, &old_response_to_usec); + +/* Define a new and too short timeout! */ +modbus_set_response_timeout(ctx, 0, 0); +``` + +## See also + +- [modbus_set_response_timeout](modbus_set_response_timeout) +- [modbus_get_byte_timeout](modbus_get_byte_timeout) +- [modbus_set_byte_timeout](modbus_set_byte_timeout) diff --git a/docs/modbus_get_slave.md b/docs/modbus_get_slave.md new file mode 100644 index 000000000..7a5496e0f --- /dev/null +++ b/docs/modbus_get_slave.md @@ -0,0 +1,29 @@ +# modbus_get_slave + +## Name + +modbus_get_slave - get slave number in the context + +## Synopsis + +```c +int modbus_get_slave(modbus_t *ctx); +``` + +## Description + +The *modbus_get_slave()* function shall get the slave number in the libmodbus +context. + +## Return value + +The function shall return the slave number if successful. Otherwise it shall +return -1 and set errno to one of the values defined below. + +## Errors + +- *EINVAL*, the libmodbus context is undefined. + +## See also + +- [modbus_set_slave](modbus_set_slave) diff --git a/docs/modbus_get_socket.md b/docs/modbus_get_socket.md new file mode 100644 index 000000000..086cff5c0 --- /dev/null +++ b/docs/modbus_get_socket.md @@ -0,0 +1,25 @@ +# modbus_get_socket + +## Name + +modbus_get_socket - get the current socket of the context + +## Synopsis + +```c +int modbus_get_socket(modbus_t *'ctx'); +``` + +## Description + +The *modbus_get_socket()* function shall return the current socket or file +descriptor of the libmodbus context. + +## Return value + +The function returns the current socket or file descriptor of the context if +successful. Otherwise it shall return -1 and set errno. + +## See also + +- [modbus_set_socket](modbus_set_socket) diff --git a/docs/modbus_mapping_free.md b/docs/modbus_mapping_free.md new file mode 100644 index 000000000..380f618be --- /dev/null +++ b/docs/modbus_mapping_free.md @@ -0,0 +1,24 @@ +# modbus_mapping_free + +## Name + +modbus_mapping_free - free a modbus_mapping_t structure + +## Synopsis + +```c +void modbus_mapping_free(modbus_mapping_t *mb_mapping); +``` + +## Description + +The function shall free the four arrays of mb_mapping_t structure and finally +the mb_mapping_t referenced by `mb_mapping`. + +## Return value + +There is no return values. + +## See also + +- [modbus_mapping_new](modbus_mapping_new) diff --git a/docs/modbus_mapping_new.md b/docs/modbus_mapping_new.md new file mode 100644 index 000000000..fdc11c560 --- /dev/null +++ b/docs/modbus_mapping_new.md @@ -0,0 +1,60 @@ +# modbus_mapping_new + +## Name + +modbus_mapping_new - allocate four arrays of bits and registers + +## Synopsis + +```c +modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits, int nb_registers, int nb_input_registers); +``` + +## Description + +The *modbus_mapping_new()* function shall allocate four arrays to store bits, +input bits, registers and inputs registers. The pointers are stored in +modbus_mapping_t structure. All values of the arrays are initialized to zero. + +This function is equivalent to a call of the +[modbus_mapping_new_start_address](modbus_mapping_new_start_address) function +with all start addresses to `0`. + +If it isn't necessary to allocate an array for a specific type of data, you can +pass the zero value in argument, the associated pointer will be NULL. + +This function is convenient to handle requests in a Modbus server/slave. + +## Return value + +The function shall return the new allocated structure if successful. Otherwise +it shall return NULL and set errno. + +## Errors + +- *ENOMEM*, not enough memory. + +## Example + +```c +/* The first value of each array is accessible from the 0 address. */ +mb_mapping = modbus_mapping_new( + BITS_ADDRESS + BITS_NB, + INPUT_BITS_ADDRESS + INPUT_BITS_NB, + REGISTERS_ADDRESS + REGISTERS_NB, + INPUT_REGISTERS_ADDRESS + INPUT_REGISTERS_NB +); +if (mb_mapping == NULL) { + fprintf( + stderr, "Failed to allocate the mapping: %s\n", + modbus_strerror(errno) + ); + modbus_free(ctx); + return -1; +} +``` + +## See also + +- [modbus_mapping_free](modbus_mapping_free) +- [modbus_mapping_new_start_address](modbus_mapping_new_start_address) diff --git a/docs/modbus_mapping_new_start_address.md b/docs/modbus_mapping_new_start_address.md new file mode 100644 index 000000000..edc267bc4 --- /dev/null +++ b/docs/modbus_mapping_new_start_address.md @@ -0,0 +1,85 @@ +# modbus_mapping_new_start_address + +## Name + +modbus_mapping_new_start_address - allocate four arrays of bits and registers accessible from their starting addresses + +## Synopsis + +```c +modbus_mapping_t* modbus_mapping_new_start_address( + int start_bits, int nb_bits, + int start_input_bits, int nb_input_bits, + int start_registers, int nb_registers, + int start_input_registers, int nb_input_registers); +``` + +## Description + +The `modbus_mapping_new_start_address()` function shall allocate four arrays to +store bits, input bits, registers and inputs registers. The pointers are stored +in modbus_mapping_t structure. All values of the arrays are initialized to zero. + +The different starting addresses make it possible to place the mapping at any +address in each address space. This way, you can give access to the clients at +values stored at high addresses without allocating memory from the address zero, +for eg. to make available registers from 340 to 349, you can use: + +```c +mb_mapping = modbus_mapping_new_start_address(0, 0, 0, 0, 340, 10, 0, 0); +``` + +The newly created `mb_mapping` will have the following arrays: + +- `tab_bits` set to NULL +- `tab_input_bits` set to NULL +- `tab_input_registers` allocated to store 10 registers (`uint16_t`) +- `tab_registers` set to NULL. + +The clients can read the first register by using the address 340 in its request. +On the server side, you should use the first index of the array to set the value +at this client address: + +```c +mb_mapping->tab_registers[0] = 42; +``` + +If it isn't necessary to allocate an array for a specific type of data, you can +pass the zero value in argument, the associated pointer will be NULL. + +This function is convenient to handle requests in a Modbus server/slave. + +## Return value + +The `modbus_mapping_new_start_address()` function shall return the new allocated structure if +successful. Otherwise it shall return NULL and set errno. + +## Errors + +- *ENOMEM*, not enough memory. + +## Example + +```c +/* The first value of each array is accessible at the defined address. +The end address is ADDRESS + NB - 1. */ +mb_mapping = modbus_mapping_new_start_address( + BITS_ADDRESS, BITS_NB, + INPUT_BITS_ADDRESS, INPUT_BITS_NB, + REGISTERS_ADDRESS, REGISTERS_NB, + INPUT_REGISTERS_ADDRESS, INPUT_REGISTERS_NB +); +if (mb_mapping == NULL) { + fprintf( + stderr, "Failed to allocate the mapping: %s\n", + modbus_strerror(errno) + ); + modbus_free(ctx); + return -1; +} +``` + +## See also + +- [modbus_mapping_new](modbus_mapping_new) +- [modbus_mapping_free](modbus_mapping_free) diff --git a/docs/modbus_mask_write_register.md b/docs/modbus_mask_write_register.md new file mode 100644 index 000000000..c6eb60e9f --- /dev/null +++ b/docs/modbus_mask_write_register.md @@ -0,0 +1,30 @@ +# modbus_mask_write_register + +## Name + +modbus_mask_write_register - mask a single register + +## Synopsis + +```c +int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and, uint16_t or); +``` + +## Description + +The *modbus_mask_write_register()* function shall modify the value of the +holding register at the address 'addr' of the remote device using the algorithm: + + new value = (current value AND 'and') OR ('or' AND (NOT 'and')) + +The function uses the Modbus function code 0x16 (mask single register). + +## Return value + +The function shall return 1 if successful. Otherwise it shall return -1 and set +errno. + +## See also + +- [modbus_read_registers](modbus_read_registers) +- [modbus_write_registers](modbus_write_registers) diff --git a/docs/modbus_new_rtu.md b/docs/modbus_new_rtu.md new file mode 100644 index 000000000..33fd5ec67 --- /dev/null +++ b/docs/modbus_new_rtu.md @@ -0,0 +1,77 @@ +# modbus_new_rtu + +## Name + +modbus_new_rtu - create a libmodbus context for RTU + +## Synopsis + +```c +modbus_t *modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit); +``` + +## Description + +The *modbus_new_rtu()* function shall allocate and initialize a `modbus_t` +structure to communicate in RTU mode on a serial line. + +The `device` argument specifies the name of the serial port handled by the OS, +eg. "/dev/ttyS0" or "/dev/ttyUSB0". On Windows, it's necessary to prepend COM +name with "\\.\" for COM number greater than 9, eg. "\\\\.\\COM10". See +http://msdn.microsoft.com/en-us/library/aa365247(v=vs.85).aspx for details + +The `baud` argument specifies the baud rate of the communication, eg. 9600, +19200, 57600, 115200, etc. + +The `parity` argument can have one of the following values: + +- `N` for none +- `E` for even +- `O` for odd + +The `data_bits` argument specifies the number of bits of data, the allowed +values are 5, 6, 7 and 8. + +The `stop_bits` argument specifies the bits of stop, the allowed values are 1 +and 2. + +Once the `modbus_t` structure is initialized, you must set the slave of your +device with [modbus_set_slave](modbus_set_slave) and connect to the serial bus with +[modbus_connect](modbus_connect). + +## Return value + +The function shall return a pointer to a `modbus_t` structure if +successful. Otherwise it shall return NULL and set errno to one of the values +defined below. + +## Errors + +- *EINVAL*, an invalid argument was given. +- *ENOMEM*, out of memory. Possibly, the application hits its memory limit + and/or whole system is running out of memory. + +## Example + +```c +modbus_t *ctx; + +ctx = modbus_new_rtu("/dev/ttyUSB0", 115200, 'N', 8, 1); +if (ctx == NULL) { + fprintf(stderr, "Unable to create the libmodbus context\n"); + return -1; +} + +modbus_set_slave(ctx, YOUR_DEVICE_ID); + +if (modbus_connect(ctx) == -1) { + fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); + modbus_free(ctx); + return -1; +} +``` + +## See also + +- [modbus_new_tcp](modbus_new_tcp) +- [modbus_free](modbus_free) diff --git a/doc/modbus_new_tcp.txt b/docs/modbus_new_tcp.md similarity index 57% rename from doc/modbus_new_tcp.txt rename to docs/modbus_new_tcp.md index 5447aa63a..30ab07f52 100644 --- a/doc/modbus_new_tcp.txt +++ b/docs/modbus_new_tcp.md @@ -1,53 +1,44 @@ -modbus_new_tcp(3) -================= +# modbus_new_tcp +## Name -NAME ----- modbus_new_tcp - create a libmodbus context for TCP/IPv4 +## Synopsis -SYNOPSIS --------- -*modbus_t *modbus_new_tcp(const char *'ip', int 'port');* +```c +modbus_t *modbus_new_tcp(const char *ip, int port); +``` +## Description -DESCRIPTION ------------ The *modbus_new_tcp()* function shall allocate and initialize a modbus_t structure to communicate with a Modbus TCP IPv4 server. -The _ip_ argument specifies the IP address of the server to which the client +The `ip` argument specifies the IP address of the server to which the client wants to establish a connection. A NULL value can be used to listen any addresses in server mode. -The _port_ argument is the TCP port to use. Set the port to +The `port` argument is the TCP port to use. Set the port to `MODBUS_TCP_DEFAULT_PORT` to use the default one (502). It’s convenient to use a port number greater than or equal to 1024 because it’s not necessary to have administrator privileges. +## Return value -RETURN VALUE ------------- The function shall return a pointer to a *modbus_t* structure if successful. Otherwise it shall return NULL and set errno to one of the values defined below. +## Errors -ERRORS ------- -*EINVAL*:: -An invalid IP address was given. +- *EINVAL*, an invalid IP address was given. +- *ENOMEM*, out of memory. Possibly, the application hits its memory limit + and/or whole system is running out of memory. -*ENOMEM*:: -Out of memory. Possibly, the application hits its memory limit and/or whole -system is running out of memory. +## Example - -EXAMPLE -------- -[source,c] -------------------- +```c modbus_t *ctx; ctx = modbus_new_tcp("127.0.0.1", 1502); @@ -61,15 +52,9 @@ if (modbus_connect(ctx) == -1) { modbus_free(ctx); return -1; } -------------------- - -SEE ALSO --------- -linkmb:modbus_tcp_listen[3] -linkmb:modbus_free[3] +``` +## See also -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - +- [modbus_tcp_listen](modbus_tcp_listen) +- [modbus_free](modbus_free) diff --git a/docs/modbus_new_tcp_pi.md b/docs/modbus_new_tcp_pi.md new file mode 100644 index 000000000..90d430872 --- /dev/null +++ b/docs/modbus_new_tcp_pi.md @@ -0,0 +1,62 @@ +# modbus_new_tcp_pi + +## Name + +modbus_new_tcp_pi - create a libmodbus context for TCP Protocol Independent + +## Synopsis + +```c +*modbus_t *modbus_new_tcp_pi(const char *node, const char *service); +``` + +## Description + +The *modbus_new_tcp_pi()* function shall allocate and initialize a modbus_t +structure to communicate with a Modbus TCP IPv4 or IPv6 server. + +The `node` argument specifies the host name or IP address of the host to connect +to, eg. "192.168.0.5" , "::1" or "server.com". A NULL value can be used to +listen any addresses in server mode. + +The `service` argument is the service name/port number to connect to. To use the +default Modbus port, you can provide an NULL value or the string "502". On many +Unix systems, it’s convenient to use a port number greater than or equal to 1024 +because it’s not necessary to have administrator privileges. + +:octicons-tag-24: v3.1.8 handles NULL value for `service` (no *EINVAL* error). + +## Return value + +The function shall return a pointer to a *modbus_t* structure if +successful. Otherwise it shall return NULL and set errno to one of the values +defined below. + +## Errors + +- *ENOMEM*, out of memory. Possibly, the application hits its memory limit + and/or whole system is running out of memory. + +## Example + +```c +modbus_t *ctx; + +ctx = modbus_new_tcp_pi("::1", "1502"); +if (ctx == NULL) { + fprintf(stderr, "Unable to allocate libmodbus context\n"); + return -1; +} + +if (modbus_connect(ctx) == -1) { + fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); + modbus_free(ctx); + return -1; +} +``` + +## See also + +- [modbus_new_tcp](modbus_new_tcp) +- [modbus_tcp_pi_listen](modbus_tcp_pi_listen) +- [modbus_free](modbus_free) diff --git a/docs/modbus_read_bits.md b/docs/modbus_read_bits.md new file mode 100644 index 000000000..9e30976c5 --- /dev/null +++ b/docs/modbus_read_bits.md @@ -0,0 +1,36 @@ +# modbus_read_bits + +## Name + +modbus_read_bits - read many bits + +## Synopsis + +```c +int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest); +``` + +## Description + +The *modbus_read_bits()* function shall read the status of the `nb` bits (coils) +to the address `addr` of the remote device. The result of reading is stored in +`dest` array as unsigned bytes (8 bits) set to `TRUE` or `FALSE`. + +You must take care to allocate enough memory to store the results in `dest` +(at least `nb` * sizeof(uint8_t)). + +The function uses the Modbus function code 0x01 (read coil status). + +## Return value + +The function shall return the number of read bits if successful. Otherwise it +shall return -1 and set errno. + +## Errors + +- *EMBMDATA*, too many bits requested + +## See also + +- [modbus_write_bit](modbus_write_bit) +- [modbus_write_bits](modbus_write_bits) diff --git a/docs/modbus_read_input_bits.md b/docs/modbus_read_input_bits.md new file mode 100644 index 000000000..2991cfb0e --- /dev/null +++ b/docs/modbus_read_input_bits.md @@ -0,0 +1,35 @@ +# modbus_read_input_bits + +## Name + +modbus_read_input_bits - read many input bits + +## Synopsis + +```c +int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest); +``` + +## Description + +The *modbus_read_input_bits()* function shall read the content of the `nb` input +bits to the address `addr` of the remote device. The result of reading is stored +in `dest` array as unsigned bytes (8 bits) set to `TRUE` or `FALSE`. + +You must take care to allocate enough memory to store the results in `dest` +(at least `nb` * sizeof(uint8_t)). + +The function uses the Modbus function code 0x02 (read input status). + +## Return value + +The function shall return the number of read input status if +successful. Otherwise it shall return -1 and set errno. + +## Errors + +- *EMBMDATA*, too many discrete inputs requested + +## See also + +- [modbus_read_input_registers](modbus_read_input_registers) diff --git a/docs/modbus_read_input_registers.md b/docs/modbus_read_input_registers.md new file mode 100644 index 000000000..3e74a5fb9 --- /dev/null +++ b/docs/modbus_read_input_registers.md @@ -0,0 +1,39 @@ +# modbus_read_input_registers + +## Name + +modbus_read_input_registers - read many input registers + +## Synopsis + +```c +int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest); +``` + +## Description + +The *modbus_read_input_registers()* function shall read the content of the `nb` +input registers to address `addr` of the remote device. The result of the +reading is stored in `dest` array as word values (16 bits). + +You must take care to allocate enough memory to store the results in `dest` (at +least `nb` * sizeof(uint16_t)). + +The function uses the Modbus function code 0x04 (read input registers). The +holding registers and input registers have different historical meaning, but +nowadays it's more common to use holding registers only. + +## Return value + +The function shall return the number of read input registers if +successful. Otherwise it shall return -1 and set errno. + +## Errors + +- *EMBMDATA*, too many bits requested. + +## See also + +- [modbus_read_input_bits](modbus_read_input_bits) +- [modbus_write_register](modbus_write_register) +- [modbus_write_registers](modbus_write_registers) diff --git a/doc/modbus_read_registers.txt b/docs/modbus_read_registers.md similarity index 55% rename from doc/modbus_read_registers.txt rename to docs/modbus_read_registers.md index dd29fdcd7..6150da924 100644 --- a/doc/modbus_read_registers.txt +++ b/docs/modbus_read_registers.md @@ -1,45 +1,38 @@ -modbus_read_registers(3) -======================== +# modbus_read_registers +## Name -NAME ----- modbus_read_registers - read many registers +## Synopsis -SYNOPSIS --------- -*int modbus_read_registers(modbus_t *'ctx', int 'addr', int 'nb', uint16_t *'dest');* +```c +int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest); +``` +## Description -DESCRIPTION ------------ -The *modbus_read_registers()* function shall read the content of the _nb_ -holding registers to the address _addr_ of the remote device. The result of -reading is stored in _dest_ array as word values (16 bits). +The *modbus_read_registers()* function shall read the content of the `nb` +holding registers to the address `addr` of the remote device. The result of +reading is stored in `dest` array as word values (16 bits). -You must take care to allocate enough memory to store the results in _dest_ -(at least _nb_ * sizeof(uint16_t)). +You must take care to allocate enough memory to store the results in `dest` +(at least `nb` * sizeof(uint16_t)). The function uses the Modbus function code 0x03 (read holding registers). +## Return value -RETURN VALUE ------------- The function shall return the number of read registers if successful. Otherwise it shall return -1 and set errno. +## Errors -ERRORS ------- -*EMBMDATA*:: -Too many registers requested +- *EMBMDATA*, too many registers requested. +## Example -EXAMPLE -------- -[source,c] -------------------- +```c modbus_t *ctx; uint16_t tab_reg[64]; int rc; @@ -64,16 +57,9 @@ for (i=0; i < rc; i++) { modbus_close(ctx); modbus_free(ctx); -------------------- +``` +## See also -SEE ALSO --------- -linkmb:modbus_write_register[3] -linkmb:modbus_write_registers[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - +- [modbus_write_register](modbus_write_register) +- [modbus_write_registers](modbus_write_registers) diff --git a/docs/modbus_receive.md b/docs/modbus_receive.md new file mode 100644 index 000000000..fa3c54846 --- /dev/null +++ b/docs/modbus_receive.md @@ -0,0 +1,32 @@ +# modbus_receive + +## Name + +modbus_receive - receive an indication request + +## Synopsis + +```c +int modbus_receive(modbus_t *'ctx', uint8_t *'req'); +``` + +## Description + +The *modbus_receive()* function shall receive an indication request from the +socket of the context `ctx`. This function is used by Modbus slave/server to +receive and analyze indication request sent by the masters/clients. + +If you need to use another socket or file descriptor than the one defined in the +context `ctx`, see the function [modbus_set_socket](modbus_set_socket). + +## Return value + +The function shall store the indication request in `req` and return the request +length if successful. The returned request length can be zero if the indication +request is ignored (eg. a query for another slave in RTU mode). Otherwise it +shall return -1 and set errno. + +## See also + +- [modbus_set_socket](modbus_set_socket) +- [modbus_reply](modbus_reply) diff --git a/doc/modbus_receive_confirmation.txt b/docs/modbus_receive_confirmation.md similarity index 51% rename from doc/modbus_receive_confirmation.txt rename to docs/modbus_receive_confirmation.md index 290d1f6ac..700e6c40b 100644 --- a/doc/modbus_receive_confirmation.txt +++ b/docs/modbus_receive_confirmation.md @@ -1,53 +1,43 @@ -modbus_receive_confirmation(3) -============================== +# modbus_receive_confirmation +## Name -NAME ----- modbus_receive_confirmation - receive a confirmation request +## Synopsis -SYNOPSIS --------- -*int modbus_receive_confirmation(modbus_t *'ctx', uint8_t *'rsp');* +```c +int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp); +``` +## Description -DESCRIPTION ------------ The *modbus_receive_confirmation()* function shall receive a request via the -socket of the context _ctx_. This function must be used for debugging purposes +socket of the context `ctx`. This function must be used for debugging purposes because the received response isn't checked against the initial request. This function can be used to receive request not handled by the library. -The maximum size of the response depends on the used backend, in RTU the _rsp_ -array must be _MODBUS_RTU_MAX_ADU_LENGTH_ bytes and in TCP it must be -_MODBUS_TCP_MAX_ADU_LENGTH_ bytes. If you want to write code compatible with -both, you can use the constant _MODBUS_MAX_ADU_LENGTH_ (maximum value of all +The maximum size of the response depends on the used backend, in RTU the `rsp` +array must be `MODBUS_RTU_MAX_ADU_LENGTH` bytes and in TCP it must be +`MODBUS_TCP_MAX_ADU_LENGTH` bytes. If you want to write code compatible with +both, you can use the constant `MODBUS_MAX_ADU_LENGTH` (maximum value of all libmodbus backends). Take care to allocate enough memory to store responses to avoid crashes of your server. +## Return value -RETURN VALUE ------------- -The function shall store the confirmation request in _rsp_ and return the +The function shall store the confirmation request in `rsp` and return the response length if successful. The returned request length can be zero if the indication request is ignored (eg. a query for another slave in RTU mode). Otherwise it shall return -1 and set errno. -EXAMPLE -------- -[source,c] -------------------- +## Example + +```c uint8_t rsp[MODBUS_MAX_ADU_LENGTH]; rc = modbus_receive_confirmation(ctx, rsp); -------------------- - -SEE ALSO --------- -linkmb:modbus_send_raw_request[3] +``` +## See also -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - +- [modbus_send_raw_request](modbus_send_raw_request) diff --git a/docs/modbus_reply.md b/docs/modbus_reply.md new file mode 100644 index 000000000..2ca3090b7 --- /dev/null +++ b/docs/modbus_reply.md @@ -0,0 +1,39 @@ +# modbus_reply + +## Name + +modbus_reply - send a response to the received request + +## Synopsis + +```c +int modbus_reply(modbus_t *ctx, const uint8_t *req, int req_length, modbus_mapping_t *mb_mapping); +``` + +## Description + +The *modbus_reply()* function shall send a response to received request. The +request `req` given in argument is analyzed, a response is then built and sent +by using the information of the modbus context `ctx`. + +If the request indicates to read or write a value the operation will done in the +modbus mapping `mb_mapping` according to the type of the manipulated data. + +If an error occurs, an exception response will be sent. + +This function is designed for Modbus server. + +## Return value + +The function shall return the length of the response sent if +successful. Otherwise it shall return -1 and set errno. + +## Errors + +- *EMBMDATA*, sending has failed + +See also the errors returned by the syscall used to send the response (eg. send or write). + +## See also + +- [modbus_reply_exception](modbus_reply_exception) diff --git a/docs/modbus_reply_exception.md b/docs/modbus_reply_exception.md new file mode 100644 index 000000000..bba24a5c0 --- /dev/null +++ b/docs/modbus_reply_exception.md @@ -0,0 +1,45 @@ +# modbus_reply_exception + +## Name + +modbus_reply_exception - send an exception response + +## Synopsis + +```c +int modbus_reply_exception(modbus_t *ctx, const uint8_t *req, unsigned int exception_code); +``` + +## Description + +The *modbus_reply_exception()* function shall send an exception response based +on the 'exception_code' in argument. + +The libmodbus provides the following exception codes: + +- MODBUS_EXCEPTION_ILLEGAL_FUNCTION (1) +- MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS (2) +- MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE (3) +- MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE (4) +- MODBUS_EXCEPTION_ACKNOWLEDGE (5) +- MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY (6) +- MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE (7) +- MODBUS_EXCEPTION_MEMORY_PARITY (8) +- MODBUS_EXCEPTION_NOT_DEFINED (9) +- MODBUS_EXCEPTION_GATEWAY_PATH (10) +- MODBUS_EXCEPTION_GATEWAY_TARGET (11) + +The initial request `req` is required to build a valid response. + +## Return value + +The function shall return the length of the response sent if +successful. Otherwise it shall return -1 and set errno. + +## Errors + +- *EINVAL*, the exception code is invalid + +## See also + +- [modbus_reply](modbus_reply) diff --git a/docs/modbus_report_slave_id.md b/docs/modbus_report_slave_id.md new file mode 100644 index 000000000..d02c57648 --- /dev/null +++ b/docs/modbus_report_slave_id.md @@ -0,0 +1,52 @@ +# modbus_report_slave_id + +## Name + +modbus_report_slave_id - returns a description of the controller + +## Synopsis + +```c +int modbus_report_slave_id(modbus_t *ctx, int max_dest, uint8_t *dest); +``` + +## Description + +The *modbus_report_slave_id()* function shall send a request to the controller +to obtain a description of the controller. + +The response stored in `dest` contains: + +- the slave ID, this unique ID is in reality not unique at all so it's not + possible to depend on it to know how the information are packed in the + response. +- the run indicator status (0x00 = OFF, 0xFF = ON) +- additional data specific to each controller. For example, libmodbus returns + the version of the library as a string. + +The function writes at most `max_dest` bytes from the response to `dest` so +you must ensure that `dest` is large enough. + +## Return value + +The function shall return the number of read data if successful. + +If the output was truncated due to the `max_dest` limit then the return value is +the number of bytes which would have been written to `dest` if enough space had +been available. Thus, a return value greater than `max_dest` means that the +response data was truncated. + +Otherwise it shall return -1 and set errno. + +## Example + +```c +uint8_t tab_bytes[MODBUS_MAX_PDU_LENGTH]; + +... + +rc = modbus_report_slave_id(ctx, MODBUS_MAX_PDU_LENGTH, tab_bytes); +if (rc > 1) { + printf("Run Status Indicator: %s\n", tab_bytes[1] ? "ON" : "OFF"); +} +``` diff --git a/docs/modbus_rtu_get_rts.md b/docs/modbus_rtu_get_rts.md new file mode 100644 index 000000000..16b2d5374 --- /dev/null +++ b/docs/modbus_rtu_get_rts.md @@ -0,0 +1,35 @@ +# modbus_rtu_get_rts + +## Name + +modbus_rtu_get_rts - get the current RTS mode in RTU + +## Synopsis + +```c +int modbus_rtu_get_rts(modbus_t *ctx); +``` + +## Description + +The *modbus_rtu_get_rts()* function shall get the current Request To Send mode +of the libmodbus context `ctx`. The possible returned values are: + +- `MODBUS_RTU_RTS_NONE` +- `MODBUS_RTU_RTS_UP` +- `MODBUS_RTU_RTS_DOWN` + +This function can only be used with a context using a RTU backend. + +## Return value + +The function shall return the current RTS mode if successful. Otherwise it shall +return -1 and set errno. + +## Errors + +- *EINVAL*, the libmodbus backend is not RTU. + +## See also + +- [modbus_rtu_set_rts](modbus_rtu_set_rts) diff --git a/docs/modbus_rtu_get_rts_delay.md b/docs/modbus_rtu_get_rts_delay.md new file mode 100644 index 000000000..236fecf81 --- /dev/null +++ b/docs/modbus_rtu_get_rts_delay.md @@ -0,0 +1,31 @@ +# modbus_rtu_get_rts_delay + +## Name + +modbus_rtu_get_rts_delay - get the current RTS delay in RTU + +## Synopsis + +```c +int modbus_rtu_get_rts_delay(modbus_t *ctx); +``` + +## Description + +The `modbus_rtu_get_rts_delay()` function shall get the current Request To Send +delay period of the libmodbus context 'ctx'. + +This function can only be used with a context using a RTU backend. + +## Return value + +The `modbus_rtu_get_rts_delay()` function shall return the current RTS delay in +microseconds if successful. Otherwise it shall return -1 and set errno. + +## Errors + +- *EINVAL*, the libmodbus backend is not RTU. + +## See also + +- [modbus_rtu_set_rts_delay](modbus_rtu_set_rts_delay) diff --git a/docs/modbus_rtu_get_serial_mode.md b/docs/modbus_rtu_get_serial_mode.md new file mode 100644 index 000000000..0715694fa --- /dev/null +++ b/docs/modbus_rtu_get_serial_mode.md @@ -0,0 +1,41 @@ +# modbus_rtu_get_serial_mode + +## Name + +modbus_rtu_get_serial_mode - get the current serial mode + +## Synopsis + +```c +int modbus_rtu_get_serial_mode(modbus_t *ctx); +``` + +## Description + +The *modbus_rtu_get_serial_mode()* function shall return the serial mode +currently used by the libmodbus context: + +- **MODBUS_RTU_RS232**, the serial line is set for RS232 communication. RS-232 + (Recommended Standard 232) is the traditional name for a series of standards + for serial binary single-ended data and control signals connecting between a + DTE (Data Terminal Equipment) and a DCE (Data Circuit-terminating Equipment). + It is commonly used in computer serial ports + +- **MODBUS_RTU_RS485**, the serial line is set for RS485 communication. EIA-485, + also known as TIA/EIA-485 or RS-485, is a standard defining the electrical + characteristics of drivers and receivers for use in balanced digital + multipoint systems. This standard is widely used for communications in + industrial automation because it can be used effectively over long distances + and in electrically noisy environments. This function is only available on + Linux kernels 2.6.28 onwards and can only be used with a context using a RTU + backend. + +## Return value + +The function shall return `MODBUS_RTU_RS232` or `MODBUS_RTU_RS485` if +successful. Otherwise it shall return -1 and set errno to one of the values +defined below. + +## Errors + +- *EINVAL*, the current libmodbus backend is not RTU. diff --git a/docs/modbus_rtu_set_custom_rts.md b/docs/modbus_rtu_set_custom_rts.md new file mode 100644 index 000000000..9b3230b54 --- /dev/null +++ b/docs/modbus_rtu_set_custom_rts.md @@ -0,0 +1,32 @@ +# modbus_rtu_set_custom_rts + +## Name + +modbus_rtu_set_custom_rts - set a function to be used for custom RTS implementation + +## Synopsis + +```c +int modbus_rtu_set_custom_rts(modbus_t *ctx, void (*set_rts) (modbus_t *ctx, int on)) +``` + +## Description + +The `modbus_rtu_set_custom_rts()` function shall set a custom function to be +called when the RTS pin is to be set before and after a transmission. By default +this is set to an internal function that toggles the RTS pin using an ioctl +call. + +Note that this function adheres to the RTS mode, the values `MODBUS_RTU_RTS_UP` or +`MODBUS_RTU_RTS_DOWN` must be used for the function to be called. + +This function can only be used with a context using a RTU backend. + +## Return value + +The `modbus_rtu_set_custom_rts()` function shall return 0 if successful. +Otherwise it shall return -1 and set errno to one of the values defined below. + +## Errors + +- *EINVAL*, the libmodbus backend is not RTU. diff --git a/doc/modbus_rtu_set_rts.txt b/docs/modbus_rtu_set_rts.md similarity index 73% rename from doc/modbus_rtu_set_rts.txt rename to docs/modbus_rtu_set_rts.md index f746ba457..d903e31fb 100644 --- a/doc/modbus_rtu_set_rts.txt +++ b/docs/modbus_rtu_set_rts.md @@ -1,19 +1,17 @@ -modbus_rtu_set_rts(3) -===================== +# modbus_rtu_set_rts +## Name -NAME ----- modbus_rtu_set_rts - set the RTS mode in RTU +## Synopsis -SYNOPSIS --------- -*int modbus_rtu_set_rts(modbus_t *'ctx', int 'mode')* +```c +int modbus_rtu_set_rts(modbus_t *ctx, int mode) +``` +## Description -DESCRIPTION ------------ The *modbus_rtu_set_rts()* function shall set the Request To Send mode to communicate on a RS485 serial bus. By default, the mode is set to `MODBUS_RTU_RTS_NONE` and no signal is issued before writing data on the wire. @@ -28,24 +26,20 @@ RTS flag. This function can only be used with a context using a RTU backend. +## Return value -RETURN VALUE ------------- The function shall return 0 if successful. Otherwise it shall return -1 and set errno to one of the values defined below. +## Errors -ERRORS ------- -*EINVAL*:: -The libmodbus backend isn't RTU or the mode given in argument is invalid. +- *EINVAL*, the libmodbus backend isn't RTU or the mode given in argument is invalid. +## Example -EXAMPLE -------- -.Enable the RTS mode with positive polarity -[source,c] -------------------- +Enable the RTS mode with positive polarity: + +```c modbus_t *ctx; uint16_t tab_reg[10]; @@ -68,14 +62,8 @@ if (rc == -1) { modbus_close(ctx); modbus_free(ctx); -------------------- - -SEE ALSO --------- -linkmb:modbus_rtu_get_rts[3] +``` +## See also -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - +- [modbus_rtu_get_rts](modbus_rtu_get_rts) diff --git a/docs/modbus_rtu_set_rts_delay.md b/docs/modbus_rtu_set_rts_delay.md new file mode 100644 index 000000000..3c0cc9736 --- /dev/null +++ b/docs/modbus_rtu_set_rts_delay.md @@ -0,0 +1,31 @@ +# modbus_rtu_set_rts_delay + +## Name + +modbus_rtu_set_rts_delay - set the RTS delay in RTU + +## Synopsis + +```c +int modbus_rtu_set_rts_delay(modbus_t *ctx, int us); +``` + +## Description + +The `modbus_rtu_set_rts_delay()` function shall set the Request To Send delay +period of the libmodbus context 'ctx'. + +This function can only be used with a context using a RTU backend. + +## Return value + +The `modbus_rtu_set_rts_delay()` function shall return 0 if successful. +Otherwise it shall return -1 and set errno. + +## Errors + +- *EINVAL*, the libmodbus backend is not RTU or a negative delay was specified. + +## See also + +- [modbus_rtu_get_rts_delay](modbus_rtu_get_rts_delay) diff --git a/docs/modbus_rtu_set_serial_mode.md b/docs/modbus_rtu_set_serial_mode.md new file mode 100644 index 000000000..d20aabd2c --- /dev/null +++ b/docs/modbus_rtu_set_serial_mode.md @@ -0,0 +1,43 @@ +# modbus_rtu_set_serial_mode + +## Name + +modbus_rtu_set_serial_mode - set the serial mode + +## Synopsis + +```c +int modbus_rtu_set_serial_mode(modbus_t *ctx, int mode); +``` + +## Description + +The *modbus_rtu_set_serial_mode()* function shall set the selected serial +mode: + +- **MODBUS_RTU_RS232**, the serial line is set for RS232 communication. RS-232 + (Recommended Standard 232) is the traditional name for a series of standards + for serial binary single-ended data and control signals connecting between a + DTE (Data Terminal Equipment) and a DCE (Data Circuit-terminating Equipment). + It is commonly used in computer serial ports. + +- **MODBUS_RTU_RS485**, the serial line is set for RS485 communication. +EIA-485, also known as TIA/EIA-485 or RS-485, is a standard defining the +electrical characteristics of drivers and receivers for use in balanced +digital multipoint systems. This standard is widely used for communications +in industrial automation because it can be used effectively over long +distances and in electrically noisy environments. + +This function is only supported on Linux kernels 2.6.28 onwards. + +## Return value + +The function shall return 0 if successful. Otherwise it shall return -1 and set +errno to one of the values defined below. + +## Errors + +- *EINVAL*, the current libmodbus backend is not RTU. +- *ENOTSUP*, the function is not supported on your platform. + +If the call to `ioctl()` fails, the error code of ioctl will be returned. diff --git a/doc/modbus_send_raw_request.txt b/docs/modbus_send_raw_request.md similarity index 68% rename from doc/modbus_send_raw_request.txt rename to docs/modbus_send_raw_request.md index 39f836d28..99b65b8ff 100644 --- a/doc/modbus_send_raw_request.txt +++ b/docs/modbus_send_raw_request.md @@ -1,23 +1,21 @@ -modbus_send_raw_request(3) -========================== +# modbus_send_raw_request +## Name -NAME ----- modbus_send_raw_request - send a raw request +## Synopsis -SYNOPSIS --------- -*int modbus_send_raw_request(modbus_t *'ctx', const uint8_t *'raw_req', int 'raw_req_length');* +```c +int modbus_send_raw_request(modbus_t *ctx, const uint8_t *raw_req, int raw_req_length); +``` +## Description -DESCRIPTION ------------ The *modbus_send_raw_request()* function shall send a request via the socket of -the context _ctx_. This function must be used for debugging purposes because you +the context `ctx`. This function must be used for debugging purposes because you have to take care to make a valid request by hand. The function only adds to the -message, the header or CRC of the selected backend, so _raw_req_ must start and +message, the header or CRC of the selected backend, so `raw_req` must start and contain at least a slave/unit identifier and a function code. This function can be used to send request not handled by the library. @@ -25,18 +23,15 @@ The public header of libmodbus provides a list of supported Modbus functions codes, prefixed by `MODBUS_FC_` (eg. `MODBUS_FC_READ_HOLDING_REGISTERS`), to help build of raw requests. +## Return value -RETURN VALUE ------------- The function shall return the full message length, counting the extra data relating to the backend, if successful. Otherwise it shall return -1 and set errno. +## Example -EXAMPLE -------- -[source,c] -------------------- +```c modbus_t *ctx; /* Read 5 holding registers from address 1 */ uint8_t raw_req[] = { 0xFF, MODBUS_FC_READ_HOLDING_REGISTERS, 0x00, 0x01, 0x0, 0x05 }; @@ -55,14 +50,8 @@ modbus_receive_confirmation(ctx, rsp); modbus_close(ctx); modbus_free(ctx); -------------------- +``` -SEE ALSO --------- -linkmb:modbus_receive_confirmation[3] +## See also - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - +- [modbus_receive_confirmation](modbus_receive_confirmation) diff --git a/docs/modbus_set_bits_from_byte.md b/docs/modbus_set_bits_from_byte.md new file mode 100644 index 000000000..702b9ba76 --- /dev/null +++ b/docs/modbus_set_bits_from_byte.md @@ -0,0 +1,27 @@ +# modbus_set_bits_from_byte + +## Name + +modbus_set_bits_from_byte - set many bits from a single byte value + + +## Synopsis + +```c +void modbus_set_bits_from_byte(uint8_t *dest, int index, const uint8_t value); +``` + +## Description + +The *modbus_set_bits_from_byte()* function shall set many bits from a single +byte. All 8 bits from the byte `value` will be written to `dest` array starting +at `index` position. + +## Return value + +There is no return values. + +## See also + +- [modbus_set_bits_from_byte](modbus_set_bits_from_byte) +- [modbus_set_bits_from_bytes](modbus_set_bits_from_bytes) diff --git a/docs/modbus_set_bits_from_bytes.md b/docs/modbus_set_bits_from_bytes.md new file mode 100644 index 000000000..2dd0dbde7 --- /dev/null +++ b/docs/modbus_set_bits_from_bytes.md @@ -0,0 +1,26 @@ +# modbus_set_bits_from_bytes + +## Name + +modbus_set_bits_from_bytes - set many bits from an array of bytes + +## Synopsis + +```c +void modbus_set_bits_from_bytes(uint8_t *dest, int index, unsigned int nb_bits, const uint8_t *tab_byte); +``` + +## Description + +The *modbus_set_bits_from_bytes* function shall set bits by reading an array of +bytes. All the bits of the bytes read from the first position of the array +`tab_byte` are written as bits in the `dest` array starting at position `index`. + +## Return value + +There is no return values. + +## See also + +- [modbus_set_bits_from_byte](modbus_set_bits_from_byte) +- [modbus_get_byte_from_bits](modbus_get_byte_from_bits) diff --git a/doc/modbus_set_byte_timeout.txt b/docs/modbus_set_byte_timeout.md similarity index 54% rename from doc/modbus_set_byte_timeout.txt rename to docs/modbus_set_byte_timeout.md index 86c644c1e..5bed59d97 100644 --- a/doc/modbus_set_byte_timeout.txt +++ b/docs/modbus_set_byte_timeout.md @@ -1,54 +1,43 @@ -modbus_set_byte_timeout(3) -========================== +# modbus_set_byte_timeout +## Name -NAME ----- modbus_set_byte_timeout - set timeout between bytes +## Synopsis -SYNOPSIS --------- -*void modbus_set_byte_timeout(modbus_t *'ctx', uint32_t 'to_sec', uint32_t 'to_usec');* +```c +void modbus_set_byte_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec); +``` +## Description -DESCRIPTION ------------ The *modbus_set_byte_timeout()* function shall set the timeout interval between two consecutive bytes of the same message. The timeout is an upper bound on the amount of time elapsed before *select()* returns, if the time elapsed is longer than the defined timeout, an `ETIMEDOUT` error will be raised by the function waiting for a response. -The value of _to_usec_ argument must be in the range 0 to 999999. +The value of `to_usec` argument must be in the range 0 to 999999. -If both _to_sec_ and _to_usec_ are zero, this timeout will not be used at all. +If both `to_sec` and `to_usec` are zero, this timeout will not be used at all. In this case, *modbus_set_response_timeout()* governs the entire handling of the response, the full confirmation response must be received before expiration of the response timeout. When a byte timeout is set, the response timeout is only used to wait for until the first byte of the response. +## Return value -RETURN VALUE ------------- The function shall return 0 if successful. Otherwise it shall return -1 and set errno. +## Errors -ERRORS ------- -*EINVAL*:: -The argument _ctx_ is NULL or _to_usec_ is larger than 999999. +- *EINVAL*, The argument `ctx` is NULL or `to_usec` is larger than 999999. +## See also -SEE ALSO --------- -linkmb:modbus_get_byte_timeout[3] -linkmb:modbus_get_response_timeout[3] -linkmb:modbus_set_response_timeout[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - +- [modbus_get_byte_timeout](modbus_get_byte_timeout) +- [modbus_get_response_timeout](modbus_get_response_timeout) +- [modbus_set_response_timeout](modbus_set_response_timeout) +w diff --git a/doc/modbus_set_debug.txt b/docs/modbus_set_debug.md similarity index 51% rename from doc/modbus_set_debug.txt rename to docs/modbus_set_debug.md index 3154c2199..b68a17377 100644 --- a/doc/modbus_set_debug.txt +++ b/docs/modbus_set_debug.md @@ -1,38 +1,29 @@ -modbus_set_debug(3) -=================== +# modbus_set_debug + +## Name -NAME ----- modbus_set_debug - set debug flag of the context +## Synopsis -SYNOPSIS --------- -*int modbus_set_debug(modbus_t *'ctx', int 'flag');* +```c +int modbus_set_debug(modbus_t *ctx, int flag); +``` +## Description -DESCRIPTION ------------ The *modbus_set_debug()* function shall set the debug flag of the *modbus_t* -context by using the argument _flag_. By default, the boolean flag is set to -`FALSE`. When the _flag_ value is set to `TRUE`, many verbose messages are +context by using the argument `flag`. By default, the boolean flag is set to +`FALSE`. When the `flag` value is set to `TRUE`, many verbose messages are displayed on stdout and stderr. For example, this flag is useful to display the bytes of the Modbus messages. -[verse] -___________________ +```text [00][14][00][00][00][06][12][03][00][6B][00][03] Waiting for a confirmation... <00><14><00><00><00><09><12><03><06><02><2B><00><00><00><00> -___________________ +``` +## Return value -RETURN VALUE ------------- The function shall return 0 if successful. Otherwise it shall return -1 and set errno. - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - diff --git a/doc/modbus_set_error_recovery.txt b/docs/modbus_set_error_recovery.md similarity index 70% rename from doc/modbus_set_error_recovery.txt rename to docs/modbus_set_error_recovery.md index b5fc521c2..f4e634f41 100644 --- a/doc/modbus_set_error_recovery.txt +++ b/docs/modbus_set_error_recovery.md @@ -1,22 +1,20 @@ -modbus_set_error_recovery(3) -============================ +# modbus_set_error_recovery +## Name -NAME ----- modbus_set_error_recovery - set the error recovery mode +## Synopsis -SYNOPSIS --------- -*int modbus_set_error_recovery(modbus_t *'ctx', modbus_error_recovery_mode 'error_recovery');* +```c +int modbus_set_error_recovery(modbus_t *ctx, modbus_error_recovery_mode error_recovery); +``` +## Description -DESCRIPTION ------------ The *modbus_set_error_recovery()* function shall set the error recovery mode to apply when the connection fails or the byte received is not expected. The -argument _error_recovery_ may be bitwise-or'ed with zero or more of the +argument `error_recovery` may be bitwise-or'ed with zero or more of the following constants. By default there is no error recovery (`MODBUS_ERROR_RECOVERY_NONE`) so the @@ -42,29 +40,20 @@ The modes are mask values and so they are complementary. It's not recommended to enable error recovery for slave/server. +## Return value -RETURN VALUE ------------- The function shall return 0 if successful. Otherwise it shall return -1 and set errno to one of the values defined below. +## Errors -ERRORS ------- -*EINVAL*:: -The value of the argument _error_recovery_ is not positive. +- *EINVAL*, the value of the argument `error_recovery` is not positive. +## Example -EXAMPLE -------- -[source,c] -------------------- -modbus_set_error_recovery(ctx, - MODBUS_ERROR_RECOVERY_LINK | - MODBUS_ERROR_RECOVERY_PROTOCOL); -------------------- - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - +```c +modbus_set_error_recovery( + ctx, + MODBUS_ERROR_RECOVERY_LINK | MODBUS_ERROR_RECOVERY_PROTOCOL +); +``` diff --git a/docs/modbus_set_float.md b/docs/modbus_set_float.md new file mode 100644 index 000000000..6d5a52437 --- /dev/null +++ b/docs/modbus_set_float.md @@ -0,0 +1,29 @@ +# modbus_set_float + +## Name + +modbus_set_float - set a float value from 2 registers + +## Synopsis + +```c +void modbus_set_float(float f, uint16_t *dest); +``` + +Warning, this function is *deprecated* since libmodbus v3.2.0 and has been +replaced by *modbus_set_float_dcba()*. + +## Description + +The *modbus_set_float()* function shall set a float to 4 bytes in Modbus format +(ABCD). The `dest` array must be pointer on two 16 bits values to be able to +store the full result of the conversion. + +## Return value + +There is no return values. + +## See also + +- [modbus_get_float](modbus_get_float) +- [modbus_set_float_dcba](modbus_set_float_dcba) diff --git a/docs/modbus_set_float_abcd.md b/docs/modbus_set_float_abcd.md new file mode 100644 index 000000000..8a3c65af7 --- /dev/null +++ b/docs/modbus_set_float_abcd.md @@ -0,0 +1,28 @@ +# modbus_set_float_abcd + +## Name + +modbus_set_float_abcd - set a float value in 2 registers using ABCD byte order + +## Synopsis + +```c +void modbus_set_float_abcd(float f, uint16_t *dest); +``` + +## Description + +The *modbus_set_float_abcd()* function shall set a float to 4 bytes in usual +Modbus format. The `dest` array must be pointer on two 16 bits values to be able +to store the full result of the conversion. + +## Return value + +There is no return values. + +## See also + +- [modbus_get_float_abcd](modbus_get_float_abcd) +- [modbus_set_float_badc](modbus_set_float_badc) +- [modbus_set_float_cdab](modbus_set_float_cdab) +- [modbus_set_float_dcba](modbus_set_float_dcba) diff --git a/docs/modbus_set_float_badc.md b/docs/modbus_set_float_badc.md new file mode 100644 index 000000000..e763eecc0 --- /dev/null +++ b/docs/modbus_set_float_badc.md @@ -0,0 +1,28 @@ +# modbus_set_float_badc + +## Name + +modbus_set_float_badc - set a float value in 2 registers using BADC byte order + +## Synopsis + +```c +void modbus_set_float_badc(float f, uint16_t *dest); +``` + +## Description + +The *modbus_set_float_badc()* function shall set a float to 4 bytes in swapped +bytes Modbus format (BADC instead of ABCD). The `dest` array must be pointer on +two 16 bits values to be able to store the full result of the conversion. + +## Return value + +There is no return values. + +## See also + +- [modbus_get_float_badc](modbus_get_float_badc) +- [modbus_set_float_abcd](modbus_set_float_abcd) +- [modbus_set_float_cdab](modbus_set_float_cdab) +- [modbus_set_float_dcba](modbus_set_float_dcba) diff --git a/docs/modbus_set_float_cdab.md b/docs/modbus_set_float_cdab.md new file mode 100644 index 000000000..588dbbfdb --- /dev/null +++ b/docs/modbus_set_float_cdab.md @@ -0,0 +1,29 @@ +# modbus_set_float_cdab + +## Name + +modbus_set_float_cdab - set a float value in 2 registers using CDAB byte order + +## Synopsis + +```c +void modbus_set_float_cdab(float f, uint16_t *dest); +``` + +## Description + +The *modbus_set_float_cdab()* function shall set a float to 4 bytes in swapped +words Modbus format (CDAB order instead of ABCD). The `dest` array must be +pointer on two 16 bits values to be able to store the full result of the +conversion. + +## Return value + +There is no return values. + +## See also + +- [modbus_get_float_cdab](modbus_get_float_cdab) +- [modbus_set_float_abcd](modbus_set_float_abcd) +- [modbus_set_float_badc](modbus_set_float_badc) +- [modbus_set_float_dcba](modbus_set_float_dcba) diff --git a/docs/modbus_set_float_dcba.md b/docs/modbus_set_float_dcba.md new file mode 100644 index 000000000..04f0d98ef --- /dev/null +++ b/docs/modbus_set_float_dcba.md @@ -0,0 +1,27 @@ +# modbus_set_float_dcba + +## Name + +modbus_set_float_dcba - set a float value in 2 registers using DCBA byte order + +## Synopsis + +```c +void modbus_set_float_dcba(float f, uint16_t *dest); +``` + +## Description + +The *modbus_set_float_dcba()* function shall set a float to 4 bytes in inverted +Modbus format (DCBA order). The `dest` array must be pointer on two 16 bits +values to be able to store the full result of the conversion. + +## Return value + +There is no return values. + +## See also + +- [modbus_get_float_dcba](modbus_get_float_dcba) +- [modbus_set_float](modbus_set_float) +- [modbus_get_float](modbus_get_float) diff --git a/docs/modbus_set_indication_timeout.md b/docs/modbus_set_indication_timeout.md new file mode 100644 index 000000000..2fc5c591b --- /dev/null +++ b/docs/modbus_set_indication_timeout.md @@ -0,0 +1,36 @@ +# modbus_set_indication_timeout + +## Name + +modbus_set_indication_timeout - set timeout between indications + +## Synopsis + +```c +void modbus_set_indication_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec); +``` + +## Description + +The *modbus_set_indication_timeout()* function shall set the timeout interval used by +a server to wait for a request from a client. + +The value of `to_usec` argument must be in the range 0 to 999999. + +If both `to_sec` and `to_usec` are zero, this timeout will not be used at all. +In this case, the server will wait forever. + +## Return value + +The function shall return 0 if successful. Otherwise it shall return -1 and set +errno. + +## Errors + +- *EINVAL*, the argument `ctx` is NULL or `to_usec` is larger than 1000000. + +## See also + +- [modbus_get_indication_timeout](modbus_get_indication_timeout) +- [modbus_get_response_timeout](modbus_get_response_timeout) +- [modbus_set_response_timeout](modbus_set_response_timeout) diff --git a/doc/modbus_set_response_timeout.txt b/docs/modbus_set_response_timeout.md similarity index 53% rename from doc/modbus_set_response_timeout.txt rename to docs/modbus_set_response_timeout.md index e99e38c14..8378692fd 100644 --- a/doc/modbus_set_response_timeout.txt +++ b/docs/modbus_set_response_timeout.md @@ -1,19 +1,17 @@ -modbus_set_response_timeout(3) -============================== +# modbus_set_response_timeout +## Name -NAME ----- modbus_set_response_timeout - set timeout for response +## Synopsis -SYNOPSIS --------- -*int modbus_set_response_timeout(modbus_t *'ctx', uint32_t 'to_sec', uint32_t 'to_usec');* +```c +int modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec); +``` +## Description -DESCRIPTION ------------ The *modbus_set_response_timeout()* function shall set the timeout interval used to wait for a response. When a byte timeout is set, if elapsed time for the first byte of response is longer than the given timeout, an `ETIMEDOUT` error @@ -21,26 +19,21 @@ will be raised by the function waiting for a response. When byte timeout is disabled, the full confirmation response must be received before expiration of the response timeout. -The value of _to_usec_ argument must be in the range 0 to 999999. +The value of `to_usec` argument must be in the range 0 to 999999. +## Return value -RETURN VALUE ------------- The function shall return 0 if successful. Otherwise it shall return -1 and set errno. +## Errors -ERRORS ------- -*EINVAL*:: -The argument _ctx_ is NULL, or both _to_sec_ and _to_usec_ are zero, or _to_usec_ -is larger than 999999. +- *EINVAL*, the argument `ctx` is NULL, or both `to_sec` and `to_usec` are zero, + or `to_usec` is larger than 999999. +## Example -EXAMPLE -------- -[source,c] -------------------- +```c uint32_t old_response_to_sec; uint32_t old_response_to_usec; @@ -49,17 +42,10 @@ modbus_get_response_timeout(ctx, &old_response_to_sec, &old_response_to_usec); /* Define a new timeout of 200ms */ modbus_set_response_timeout(ctx, 0, 200000); -------------------- +``` +## See also -SEE ALSO --------- -linkmb:modbus_get_response_timeout[3] -linkmb:modbus_get_byte_timeout[3] -linkmb:modbus_set_byte_timeout[3] - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - +- [modbus_get_response_timeout](modbus_get_response_timeout) +- [modbus_get_byte_timeout](modbus_get_byte_timeout) +- [modbus_set_byte_timeout](modbus_set_byte_timeout) diff --git a/doc/modbus_set_slave.txt b/docs/modbus_set_slave.md similarity index 78% rename from doc/modbus_set_slave.txt rename to docs/modbus_set_slave.md index 7f9ecb099..c79d21502 100644 --- a/doc/modbus_set_slave.txt +++ b/docs/modbus_set_slave.md @@ -1,19 +1,17 @@ -modbus_set_slave(3) -=================== +# modbus_set_slave +## Name -NAME ----- modbus_set_slave - set slave number in the context +## Synopsis -SYNOPSIS --------- -*int modbus_set_slave(modbus_t *'ctx', int 'slave');* +```c +int modbus_set_slave(modbus_t *ctx, int slave); +``` +## Description -DESCRIPTION ------------ The *modbus_set_slave()* function shall set the slave number in the libmodbus context. @@ -35,23 +33,18 @@ remote device or software drops the requests! The special value The broadcast address is `MODBUS_BROADCAST_ADDRESS`. This special value must be use when you want all Modbus devices of the network receive the request. +## Return value -RETURN VALUE ------------- The function shall return 0 if successful. Otherwise it shall return -1 and set errno to one of the values defined below. +## Errors -ERRORS ------- -*EINVAL*:: -The slave number is invalid. +- *EINVAL*, the slave number is invalid. +## Example -EXAMPLE -------- -[source,c] -------------------- +```c modbus_t *ctx; ctx = modbus_new_rtu("/dev/ttyUSB0", 115200, 'N', 8, 1); @@ -72,13 +65,8 @@ if (modbus_connect(ctx) == -1) { modbus_free(ctx); return -1; } -------------------- +``` -SEE ALSO --------- -linkmb:modbus_get_slave[3] +## See also -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - +- [modbus_get_slave](modbus_get_slave) diff --git a/doc/modbus_set_socket.txt b/docs/modbus_set_socket.md similarity index 63% rename from doc/modbus_set_socket.txt rename to docs/modbus_set_socket.md index 49e5d1ff0..8df91a015 100644 --- a/doc/modbus_set_socket.txt +++ b/docs/modbus_set_socket.md @@ -1,33 +1,28 @@ -modbus_set_socket(3) -==================== +# modbus_set_socket +## Name -NAME ----- modbus_set_socket - set socket of the context +## Synopsis -SYNOPSIS --------- -*int modbus_set_socket(modbus_t *'ctx', int 's');* +```c +int modbus_set_socket(modbus_t *ctx, int s); +``` +## Description -DESCRIPTION ------------ The *modbus_set_socket()* function shall set the socket or file descriptor in the libmodbus context. This function is useful for managing multiple client connections to the same server. +## Return value -RETURN VALUE ------------- The function shall return 0 if successful. Otherwise it shall return -1 and set errno. +## Example -EXAMPLE -------- -[source,c] -------------------- +```c ctx = modbus_new_tcp("127.0.0.1", 1502); server_socket = modbus_tcp_listen(ctx, NB_CONNECTION); @@ -43,14 +38,8 @@ if (FD_ISSET(master_socket, &rdset)) { modbus_reply(ctx, query, rc, mb_mapping); } } -------------------- +``` -SEE ALSO --------- -linkmb:modbus_get_socket[3] +## See also - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - +- [modbus_get_socket](modbus_get_socket) diff --git a/doc/modbus_strerror.txt b/docs/modbus_strerror.md similarity index 50% rename from doc/modbus_strerror.txt rename to docs/modbus_strerror.md index 9cc355f1a..3c65a366e 100644 --- a/doc/modbus_strerror.txt +++ b/docs/modbus_strerror.md @@ -1,54 +1,39 @@ -modbus_strerror(3) -================= +# modbus_strerror +## Name -NAME ----- modbus_strerror - return the error message +## Synopsis -SYNOPSIS --------- -*const char *modbus_strerror(int 'errnum');* +```c +const char *modbus_strerror(int errnum); +``` +## Description -DESCRIPTION ------------ The *modbus_strerror()* function shall return a pointer to an error message -string corresponding to the error number specified by the _errnum_ argument. As +string corresponding to the error number specified by the `errnum` argument. As libmodbus defines additional error numbers over and above those defined by the operating system, applications should use *modbus_strerror()* in preference to the standard *strerror()* function. +## Return value -RETURN VALUE ------------- The *modbus_strerror()* function shall return a pointer to an error message string. +## Errors -ERRORS ------- No errors are defined. +## Example -EXAMPLE -------- -.Display an error message when a Modbus connection cannot be established -[source,c] -------------------- +Display an error message when a Modbus connection cannot be established + +```c if (modbus_connect(ctx) == -1) { fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); abort(); } -------------------- - -SEE ALSO --------- -linkmb:libmodbus - - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - +``` diff --git a/doc/modbus_tcp_accept.txt b/docs/modbus_tcp_accept.md similarity index 50% rename from doc/modbus_tcp_accept.txt rename to docs/modbus_tcp_accept.md index 4c46d9079..16f75c857 100644 --- a/doc/modbus_tcp_accept.txt +++ b/docs/modbus_tcp_accept.md @@ -1,37 +1,32 @@ -modbus_tcp_accept(3) -==================== +# modbus_tcp_accept +## Name -NAME ----- modbus_tcp_accept - accept a new connection on a TCP Modbus socket (IPv4) +## Synopsis -SYNOPSIS --------- -*int modbus_tcp_accept(modbus_t *'ctx', int *'s);* +```c +int modbus_tcp_accept(modbus_t *ctx, int *s); +``` +## Description -DESCRIPTION ------------ The *modbus_tcp_accept()* function shall extract the first connection on the queue of pending connections, create a new socket and store it in libmodbus -context given in argument. If available, _accept4()_ with `SOCK_CLOEXEC` will be -called instead of *accept()*. +context given in argument. If available, `accept4()` with `SOCK_CLOEXEC` will be +called instead of `accept()`. +## Return value -RETURN VALUE ------------- The function shall return a new socket if successful. Otherwise it shall return -1 and set errno. +## Example -EXAMPLE -------- For detailed example, see unit-test-server.c source file in tests directory. -[source,c] -------------------- +```c ... ctx = modbus_new_tcp("127.0.0.1", 502); @@ -42,15 +37,10 @@ modbus_tcp_accept(ctx, &s); close(s) modbus_free(ctx); -------------------- - -SEE ALSO --------- -linkmb:modbus_tcp_pi_accept[3] -linkmb:modbus_tcp_listen[3] -linkmb:modbus_tcp_pi_listen[3] - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - +``` + +## See also + +- [modbus_tcp_pi_accept](modbus_tcp_pi_accept) +- [modbus_tcp_listen](modbus_tcp_listen) +- [modbus_tcp_pi_listen](modbus_tcp_pi_listen) diff --git a/doc/modbus_tcp_listen.txt b/docs/modbus_tcp_listen.md similarity index 61% rename from doc/modbus_tcp_listen.txt rename to docs/modbus_tcp_listen.md index 640bbbbfe..bf090c5cd 100644 --- a/doc/modbus_tcp_listen.txt +++ b/docs/modbus_tcp_listen.md @@ -1,42 +1,37 @@ -modbus_tcp_listen(3) -==================== +# modbus_tcp_listen +## Name -NAME ----- modbus_tcp_listen - create and listen a TCP Modbus socket (IPv4) -SYNOPSIS --------- -*int modbus_tcp_listen(modbus_t *'ctx', int 'nb_connection');* +## Synopsis +```c +int modbus_tcp_listen(modbus_t *ctx, int nb_connection); +``` + +## Description -DESCRIPTION ------------ The *modbus_tcp_listen()* function shall create a socket and listen to maximum -_nb_connection_ incoming connections on the specified IP address. The context -_ctx _must be allocated and initialized with linkmb:modbus_new_tcp[3] before to +`nb_connection` incoming connections on the specified IP address. The context +`ctx` must be allocated and initialized with [modbus_new_tcp](modbus_new_tcp) before to set the IP address to listen, if IP address is set to NULL or '0.0.0.0', any addresses will be listen. +## Return value -RETURN VALUE ------------- The function shall return a new socket if successful. Otherwise it shall return -1 and set errno. +## Example -EXAMPLE -------- For detailed examples, see source files in tests directory: - unit-test-server.c, simple but handle only one connection - bandwidth-server-many-up.c, handles several connections at once - -[source,c] -------------------- +```c ... /* To listen any addresses on port 502 */ @@ -58,15 +53,10 @@ if (select(server_socket + 1, &refset, NULL, NULL, NULL) == -1) { close(server_socket); modbus_free(ctx); -------------------- - -SEE ALSO --------- -linkmb:modbus_new_tcp[3] -linkmb:modbus_tcp_accept[3] -linkmb:modbus_tcp_pi_listen[3] - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - +``` + +## See also + +- [modbus_new_tcp](modbus_new_tcp) +- [modbus_tcp_accept](modbus_tcp_accept) +- [modbus_tcp_pi_listen](modbus_tcp_pi_listen) diff --git a/doc/modbus_tcp_pi_accept.txt b/docs/modbus_tcp_pi_accept.md similarity index 50% rename from doc/modbus_tcp_pi_accept.txt rename to docs/modbus_tcp_pi_accept.md index a84dc43df..6ee0ccb95 100644 --- a/doc/modbus_tcp_pi_accept.txt +++ b/docs/modbus_tcp_pi_accept.md @@ -1,37 +1,32 @@ -modbus_tcp_pi_accept(3) -======================= +# modbus_tcp_pi_accept +## Name -NAME ----- modbus_tcp_pi_accept - accept a new connection on a TCP PI Modbus socket (IPv6) +## Synopsis -SYNOPSIS --------- -*int modbus_tcp_pi_accept(modbus_t *'ctx', int *'s);* +```c +int modbus_tcp_pi_accept(modbus_t *ctx, int *s); +``` +## Description -DESCRIPTION ------------ The *modbus_tcp_pi_accept()* function shall extract the first connection on the queue of pending connections, create a new socket and store it in libmodbus -context given in argument. If available, _accept4()_ with `SOCK_CLOEXEC` will be -called instead of *accept()*. +context given in argument. If available, `accept4()` with `SOCK_CLOEXEC` will be +called instead of `accept()`. +## Return value -RETURN VALUE ------------- The function shall return a new socket if successful. Otherwise it shall return -1 and set errno. +## Example -EXAMPLE -------- For detailed example, see unit-test-server.c source file in tests directory. -[source,c] -------------------- +```c ... ctx = modbus_new_tcp_pi("::0", 502); @@ -42,15 +37,10 @@ modbus_tcp_pi_accept(ctx, &s); close(s) modbus_free(ctx); -------------------- - -SEE ALSO --------- -linkmb:modbus_tcp_pi_accept[3] -linkmb:modbus_tcp_listen[3] -linkmb:modbus_tcp_pi_listen[3] - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - +``` + +## See also + +- [modbus_tcp_pi_accept](modbus_tcp_pi_accept) +- [modbus_tcp_listen](modbus_tcp_listen) +- [modbus_tcp_pi_listen](modbus_tcp_pi_listen) diff --git a/doc/modbus_tcp_pi_listen.txt b/docs/modbus_tcp_pi_listen.md similarity index 55% rename from doc/modbus_tcp_pi_listen.txt rename to docs/modbus_tcp_pi_listen.md index 2a501ee6c..d83280805 100644 --- a/doc/modbus_tcp_pi_listen.txt +++ b/docs/modbus_tcp_pi_listen.md @@ -1,41 +1,35 @@ -modbus_tcp_pi_listen(3) -======================= +# modbus_tcp_pi_listen +## Name -NAME ----- modbus_tcp_pi_listen - create and listen a TCP PI Modbus socket (IPv6) +## Synopsis -SYNOPSIS --------- -*int modbus_tcp_pi_listen(modbus_t *'ctx', int 'nb_connection');* +```c +int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection); +``` +## Description -DESCRIPTION ------------ The *modbus_tcp_pi_listen()* function shall create a socket and listen to -maximum _nb_connection_ incoming connections on the specified nodes. The -context *ctx* must be allocated and initialized with linkmb:modbus_new_tcp_pi[3] +maximum `nb_connection` incoming connections on the specified nodes. The +context *ctx* must be allocated and initialized with [modbus_new_tcp_pi](modbus_new_tcp_pi) before to set the node to listen, if node is set to NULL or '0.0.0.0', any addresses will be listen. +## Return value -RETURN VALUE ------------- The function shall return a new socket if successful. Otherwise it shall return -1 and set errno. - -EXAMPLE -------- +## Example For detailed examples, see source files in tests directory: - unit-test-server.c, simple but handle only one connection -[source,c] -------------------- +```c ... ctx = modbus_new_tcp_pi("::0", "502"); @@ -48,20 +42,14 @@ for (;;) { } ... -mclose(s); +modbus_close(s); modbus_free(ctx); -------------------- +``` - bandwidth-server-many-up.c, handles several connections at once +## See also -SEE ALSO --------- -linkmb:modbus_new_tcp_pi[3] -linkmb:modbus_tcp_pi_accept[3] -linkmb:modbus_tcp_listen[3] - -AUTHORS -------- -The libmodbus documentation was written by Stéphane Raimbault - +- [modbus_new_tcp_pi](modbus_new_tcp_pi) +- [modbus_tcp_pi_accept](modbus_tcp_pi_accept) +- [modbus_tcp_listen](modbus_tcp_listen) diff --git a/docs/modbus_write_and_read_registers.md b/docs/modbus_write_and_read_registers.md new file mode 100644 index 000000000..143cd54b0 --- /dev/null +++ b/docs/modbus_write_and_read_registers.md @@ -0,0 +1,43 @@ +# modbus_write_and_read_registers + +## Name + +modbus_write_and_read_registers - write and read many registers in a single transaction + +## Synopsis + +```c +int modbus_write_and_read_registers( + modbus_t *ctx, + int write_addr, int write_nb, const uint16_t *src, + int read_addr, int read_nb, const uint16_t *dest +); +``` + +## Description + +The *modbus_write_and_read_registers()* function shall write the content of the +`write_nb` holding registers from the array 'src' to the address `write_addr` of +the remote device then shall read the content of the `read_nb` holding registers +to the address `read_addr` of the remote device. The result of reading is stored +in `dest` array as word values (16 bits). + +You must take care to allocate enough memory to store the results in `dest` +(at least `nb` * sizeof(uint16_t)). + +The function uses the Modbus function code 0x17 (write/read registers). + +## Return value + +The function shall return the number of read registers if successful. Otherwise +it shall return -1 and set errno. + +## Errors + +- *EMBMDATA*, too many registers requested, Too many registers to write + +## See also + +- [modbus_read_registers](modbus_read_registers) +- [modbus_write_register](modbus_write_register) +- [modbus_write_registers](modbus_write_registers) diff --git a/docs/modbus_write_bit.md b/docs/modbus_write_bit.md new file mode 100644 index 000000000..232534a98 --- /dev/null +++ b/docs/modbus_write_bit.md @@ -0,0 +1,28 @@ +# modbus_write_bit + +## Name + +modbus_write_bit - write a single bit + +## Synopsis + +```c +int modbus_write_bit(modbus_t *ctx, int addr, int status); +``` + +## Description + +The *modbus_write_bit()* function shall write the status of `status` at the +address `addr` of the remote device. The value must be set to `TRUE` or `FALSE`. + +The function uses the Modbus function code 0x05 (force single coil). + +## Return value + +The function shall return 1 if successful. Otherwise it shall return -1 and set +errno. + +## See also + +- [modbus_read_bits](modbus_read_bits) +- [modbus_write_bits](modbus_write_bits) diff --git a/docs/modbus_write_bits.md b/docs/modbus_write_bits.md new file mode 100644 index 000000000..34beb98ae --- /dev/null +++ b/docs/modbus_write_bits.md @@ -0,0 +1,33 @@ +# modbus_write_bits + +## Name + +modbus_write_bits - write many bits + +## Synopsis + +```c +int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src); +``` + +## Description + +The *modbus_write_bits()* function shall write the status of the `nb` bits +(coils) from `src` at the address `addr` of the remote device. The +`src` array must contains bytes set to `TRUE` or `FALSE`. + +The function uses the Modbus function code 0x0F (force multiple coils). + +## Return value + +The function shall return the number of written bits if successful. Otherwise it +shall return -1 and set errno. + +## Errors + +- *EMBMDATA*, writing too many bits. + +## See also + +- [modbus_read_bits](modbus_read_bits) +- [modbus_write_bit](modbus_write_bit) diff --git a/docs/modbus_write_register.md b/docs/modbus_write_register.md new file mode 100644 index 000000000..bf66c8e08 --- /dev/null +++ b/docs/modbus_write_register.md @@ -0,0 +1,28 @@ +# modbus_write_register + +## Name + +modbus_write_register - write a single register + +## Synopsis + +```c +int modbus_write_register(modbus_t *ctx, int addr, const uint16_t value); +``` + +## Description + +The *modbus_write_register()* function shall write the value of `value` +holding registers at the address `addr` of the remote device. + +The function uses the Modbus function code 0x06 (preset single register). + +## Return value + +The function shall return 1 if successful. Otherwise it shall return -1 and set +errno. + +## See also + +- [modbus_read_registers](modbus_read_registers) +- [modbus_write_registers](modbus_write_registers) diff --git a/docs/modbus_write_registers.md b/docs/modbus_write_registers.md new file mode 100644 index 000000000..a89f22c3f --- /dev/null +++ b/docs/modbus_write_registers.md @@ -0,0 +1,28 @@ +# modbus_write_registers + +## Name + +modbus_write_registers - write many registers + +## Synopsis + +```c +int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src); +``` + +## Description + +The *modbus_write_registers()* function shall write the content of the `nb` +holding registers from the array `src` at address `addr` of the remote device. + +The function uses the Modbus function code 0x10 (preset multiple registers). + +## Return value + +The function shall return the number of written registers if +successful. Otherwise it shall return -1 and set errno. + +## See also + +- [modbus_write_register](modbus_write_register) +- [modbus_read_registers](modbus_read_registers) diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 000000000..61358fda1 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,17 @@ +site_name: libmodbus + +theme: + name: material + font: false + +markdown_extensions: + - md_in_html + - attr_list + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences + - pymdownx.emoji: + emoji_index: !!python/name:materialx.emoji.twemoji + emoji_generator: !!python/name:materialx.emoji.to_svg From f5939cf740313daf966103fc3a769f103da7ab2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Wed, 17 Aug 2022 18:30:36 +0200 Subject: [PATCH 049/210] Bump version to 3.1.8 --- NEWS | 15 +++++++++++++++ configure.ac | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 3f8a97dcd..ef1997be4 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,18 @@ +libmodbus 3.1.8 (2022-08-17) +============================ + +- Major rewrite of documentation with Material for mkdocs +- Reduce memory use of TCP PI backend (closes #621) +- Fixed MODBUS_ERROR_RECOVERY_LINK not working on Windows (@embeddedmz) +- Replace Travis CI by GitHub CI +- Fix linker error for Windows (VCLinkerTool) +- Address check in single register / coil responses added (#463) +- Swap CRC bytes in request data but not at CRC computing (#397) +- Fix float endianness issue on big endian architecture +- Fix comment about EMBUNKEXC (closes #566) +- Fix network library detection on Haiku +- Fix typos (closes #620) + libmodbus 3.1.7 (2022-01-09) ============================ diff --git a/configure.ac b/configure.ac index ad26abd24..f170159dd 100644 --- a/configure.ac +++ b/configure.ac @@ -13,7 +13,7 @@ # m4_define([libmodbus_version_major], [3]) m4_define([libmodbus_version_minor], [1]) -m4_define([libmodbus_version_micro], [7]) +m4_define([libmodbus_version_micro], [8]) m4_define([libmodbus_release_status], [m4_if(m4_eval(libmodbus_version_minor % 2), [1], [snapshot], [release])]) From c4875ed2c0e1eed6b9dd0da2d36f65da90d4246d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Wed, 17 Aug 2022 20:46:17 +0200 Subject: [PATCH 050/210] Remove travis.yml --- .travis.yml | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e04b38808..000000000 --- a/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: c - -compiler: - - gcc - - clang - -script: ./autogen.sh && ./configure && make && make check From b9488b2c45a26daed7c733dde98f7ad28a6c709b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Wed, 17 Aug 2022 22:21:56 +0200 Subject: [PATCH 051/210] Minor adjustments to MD files --- CONTRIBUTING.md | 7 +++---- ISSUE_TEMPLATE.md | 4 ++-- tests/README.md | 8 ++++++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7495eadaf..11f454f8d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,4 @@ -How Do I Submit A Good Bug Report? ----------------------------------- +# How Do I Submit A Good Bug Report? Please, don't send direct emails to Stéphane Raimbault unless you want commercial support. @@ -7,8 +6,8 @@ commercial support. Take care to read the documentation at http://libmodbus.org/. - *Be sure it's a bug before creating an issue*, in doubt, post a message on - https://groups.google.com/forum/#!forum/libmodbus or send an email to - libmodbus@googlegroups.com + or send an email to + - *Use a clear and descriptive title* for the issue to identify diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index ad341470b..9d87f4ba0 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -62,11 +62,11 @@ everything up to and including the following line which starts with ---. <...> -## Expected behavior +## Actual behavior if applicable <...> -## Actual behavior +## Expected behavior or suggestion <...> diff --git a/tests/README.md b/tests/README.md index 39bd813f3..14608159b 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,8 +1,12 @@ -# License +# Tests + +## License + Test programs of this directory are provided under BSD license (see associated LICENSE file). -# Compilation +## Compilation + After installation, you can use pkg-config to compile these tests. For example, to compile random-test-server run: From 5ce6b8a9dcaacfae417434492624f10d5b0452cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Thu, 18 Aug 2022 00:27:50 +0200 Subject: [PATCH 052/210] Remove year range in Copyright --- src/modbus-data.c | 2 +- src/modbus-private.h | 2 +- src/modbus-rtu-private.h | 2 +- src/modbus-rtu.c | 2 +- src/modbus-rtu.h | 2 +- src/modbus-tcp-private.h | 2 +- src/modbus-tcp.c | 2 +- src/modbus-tcp.h | 2 +- src/modbus-version.h.in | 2 +- src/modbus.c | 2 +- src/modbus.h | 2 +- tests/bandwidth-client.c | 2 +- tests/bandwidth-server-many-up.c | 2 +- tests/bandwidth-server-one.c | 2 +- tests/random-test-client.c | 2 +- tests/random-test-server.c | 2 +- tests/unit-test-client.c | 2 +- tests/unit-test-server.c | 2 +- tests/unit-test.h.in | 2 +- tests/version.c | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/modbus-data.c b/src/modbus-data.c index 3c5d1829d..f9c6615f9 100644 --- a/src/modbus-data.c +++ b/src/modbus-data.c @@ -1,5 +1,5 @@ /* - * Copyright © 2010-2014 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: LGPL-2.1-or-later */ diff --git a/src/modbus-private.h b/src/modbus-private.h index 198baeffd..df2ca6d92 100644 --- a/src/modbus-private.h +++ b/src/modbus-private.h @@ -1,5 +1,5 @@ /* - * Copyright © 2010-2012 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: LGPL-2.1-or-later */ diff --git a/src/modbus-rtu-private.h b/src/modbus-rtu-private.h index 1c7d4781a..8c1d59db0 100644 --- a/src/modbus-rtu-private.h +++ b/src/modbus-rtu-private.h @@ -1,5 +1,5 @@ /* - * Copyright © 2001-2011 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: LGPL-2.1-or-later */ diff --git a/src/modbus-rtu.c b/src/modbus-rtu.c index 21d4dc60b..00a2faf63 100644 --- a/src/modbus-rtu.c +++ b/src/modbus-rtu.c @@ -1,5 +1,5 @@ /* - * Copyright © 2001-2011 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: LGPL-2.1-or-later */ diff --git a/src/modbus-rtu.h b/src/modbus-rtu.h index fa3765521..9bf4547ca 100644 --- a/src/modbus-rtu.h +++ b/src/modbus-rtu.h @@ -1,5 +1,5 @@ /* - * Copyright © 2001-2011 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: LGPL-2.1-or-later */ diff --git a/src/modbus-tcp-private.h b/src/modbus-tcp-private.h index 698f0e8b8..80ef4708b 100644 --- a/src/modbus-tcp-private.h +++ b/src/modbus-tcp-private.h @@ -1,5 +1,5 @@ /* - * Copyright © 2001-2011 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: LGPL-2.1-or-later */ diff --git a/src/modbus-tcp.c b/src/modbus-tcp.c index 70df8b6c4..015d66f23 100644 --- a/src/modbus-tcp.c +++ b/src/modbus-tcp.c @@ -1,5 +1,5 @@ /* - * Copyright © 2001-2013 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: LGPL-2.1-or-later */ diff --git a/src/modbus-tcp.h b/src/modbus-tcp.h index d67c2393e..83cf508fb 100644 --- a/src/modbus-tcp.h +++ b/src/modbus-tcp.h @@ -1,5 +1,5 @@ /* - * Copyright © 2001-2010 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: LGPL-2.1-or-later */ diff --git a/src/modbus-version.h.in b/src/modbus-version.h.in index 8473d6597..90c942b34 100644 --- a/src/modbus-version.h.in +++ b/src/modbus-version.h.in @@ -1,5 +1,5 @@ /* - * Copyright © 2010-2014 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/modbus.c b/src/modbus.c index a18b1a8ea..362a4c885 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -1,5 +1,5 @@ /* - * Copyright © 2001-2011 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: LGPL-2.1-or-later * diff --git a/src/modbus.h b/src/modbus.h index 24808ead5..70a6c22a8 100644 --- a/src/modbus.h +++ b/src/modbus.h @@ -1,5 +1,5 @@ /* - * Copyright © 2001-2013 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: LGPL-2.1-or-later */ diff --git a/tests/bandwidth-client.c b/tests/bandwidth-client.c index e8a7ef0d0..f579d8891 100644 --- a/tests/bandwidth-client.c +++ b/tests/bandwidth-client.c @@ -1,5 +1,5 @@ /* - * Copyright © 2008-2014 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/tests/bandwidth-server-many-up.c b/tests/bandwidth-server-many-up.c index 0f00f3a2f..063ffd9b5 100644 --- a/tests/bandwidth-server-many-up.c +++ b/tests/bandwidth-server-many-up.c @@ -1,5 +1,5 @@ /* - * Copyright © 2008-2014 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/tests/bandwidth-server-one.c b/tests/bandwidth-server-one.c index 2ce17f1f3..bdb488d58 100644 --- a/tests/bandwidth-server-one.c +++ b/tests/bandwidth-server-one.c @@ -1,5 +1,5 @@ /* - * Copyright © 2008-2014 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/tests/random-test-client.c b/tests/random-test-client.c index 2ebbded5b..04da17d16 100644 --- a/tests/random-test-client.c +++ b/tests/random-test-client.c @@ -1,5 +1,5 @@ /* - * Copyright © 2008-2014 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/tests/random-test-server.c b/tests/random-test-server.c index 03bd87056..1db781fa3 100644 --- a/tests/random-test-server.c +++ b/tests/random-test-server.c @@ -1,5 +1,5 @@ /* - * Copyright © 2008-2014 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/tests/unit-test-client.c b/tests/unit-test-client.c index 4c6b416d1..f25977f92 100644 --- a/tests/unit-test-client.c +++ b/tests/unit-test-client.c @@ -1,5 +1,5 @@ /* - * Copyright © 2008-2014 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/tests/unit-test-server.c b/tests/unit-test-server.c index 6e0af93e1..f6e2ebb3e 100644 --- a/tests/unit-test-server.c +++ b/tests/unit-test-server.c @@ -1,5 +1,5 @@ /* - * Copyright © 2008-2014 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/tests/unit-test.h.in b/tests/unit-test.h.in index 4ffa254fb..98c14d698 100644 --- a/tests/unit-test.h.in +++ b/tests/unit-test.h.in @@ -1,5 +1,5 @@ /* - * Copyright © 2008-2014 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/tests/version.c b/tests/version.c index d6db67966..0b266d134 100644 --- a/tests/version.c +++ b/tests/version.c @@ -1,5 +1,5 @@ /* - * Copyright © 2008-2014 Stéphane Raimbault + * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: BSD-3-Clause */ From 987d5af1498cdc10dce784a199bac68572a4c16d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Thu, 18 Aug 2022 01:05:21 +0200 Subject: [PATCH 053/210] Move migration content to libmodbus.org --- MIGRATION | 53 ----------------------------------------------------- Makefile.am | 2 +- 2 files changed, 1 insertion(+), 54 deletions(-) delete mode 100644 MIGRATION diff --git a/MIGRATION b/MIGRATION deleted file mode 100644 index 7a182b572..000000000 --- a/MIGRATION +++ /dev/null @@ -1,53 +0,0 @@ -============================================= -Migration notes from the 2.0 series (for 3.0) -============================================= - -The 3.0 release use a brand new API and this document covers only the general -changes: - -- the structure modbus_param_t is gone and is replaced by a new opaque and -dynamically allocated structure modbus_t. - -- the slave argument is no more an argument of the Modbus functions, you need to - call modbus_set_slave first. - -- the public header file is smaller so some internal defines aren't accessible - anymore. - -- all function and constants are respectively prefixed by modbus_ or MODBUS_. - -- the POSIX error conventions are used (if an error occurred, -1 or NULL is - returned and errno is set accordingly). - -- coil status and discretes inputs are just bits and force/preset actions have - been renamed to write actions. - -We hope you'll enjoy the new API to accept the migration burden! - -============================================= -Migration notes from the 1.2 series (for 2.0) -============================================= - -Init -==== - -modbus_init_tcp requires a third new argument, the port number. - -modbus_init_tcp(modbus_param_t *mb_param, char *ip_address, int port) - -Set the port to MODBUS_TCP_DEFAULT_PORT to use the default one -(502). It's convenient to use a port number greater than or equal to -1024 because it's not necessary to be root to use this port number. - - -Pointers of data -================ - -The coil and input status are now stored in an array of type uint8_t -(in 1.2.X series, array of type int was used). So now, you need to -pass a pointer of type uint8_t to use read_coil_status(), for example. - -The holding and input registers are now stored in an array of type -uint16_t. - -These changes reduce the memory consumption. diff --git a/Makefile.am b/Makefile.am index 202135e53..5e9fc7ce3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -7,7 +7,7 @@ pkgconfig_DATA = libmodbus.pc EXTRA_DIST = libmodbus.pc.in CLEANFILES += libmodbus.pc -dist_doc_DATA = MIGRATION README.md AUTHORS NEWS +dist_doc_DATA = AUTHORS NEWS README.md SUBDIRS = src From 192fac7c1a078f7648312f6e8ae8c811459ffd5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Thu, 18 Aug 2022 13:36:21 +0200 Subject: [PATCH 054/210] Avoid negative value in FD_SET call --- src/modbus.c | 7 +++++++ tests/unit-test-client.c | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/modbus.c b/src/modbus.c index 362a4c885..83ffdc451 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -371,6 +371,13 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type) } } + if (ctx->s == -1) { + if (ctx->debug) { + fprintf(stderr, "ERROR The connection is not established.\n"); + } + return -1; + } + /* Add a file descriptor to the set */ FD_ZERO(&rset); FD_SET(ctx->s, &rset); diff --git a/tests/unit-test-client.c b/tests/unit-test-client.c index f25977f92..fd4062297 100644 --- a/tests/unit-test-client.c +++ b/tests/unit-test-client.c @@ -786,6 +786,13 @@ int test_server(modbus_t *ctx, int use_backend) modbus_get_response_timeout(ctx, &old_response_to_sec, &old_response_to_usec); modbus_set_response_timeout(ctx, 0, 600000); + int old_s = modbus_get_socket(ctx); + modbus_set_socket(ctx, -1); + rc = modbus_receive(ctx, rsp); + modbus_set_socket(ctx, old_s); + printf("* modbus_receive with invalid socket: "); + ASSERT_TRUE(rc == -1, "FAILED (%d)\n", rc); + req_length = modbus_send_raw_request(ctx, read_raw_req, READ_RAW_REQ_LEN); printf("* modbus_send_raw_request: "); ASSERT_TRUE(req_length == (backend_length + 5), "FAILED (%d)\n", req_length); From 6914cf96f8f933e0849264ef36c9c662b850ee79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Thu, 18 Aug 2022 13:39:00 +0200 Subject: [PATCH 055/210] Test socket against positive value instead of -1 --- src/modbus-rtu.c | 4 ++-- src/modbus-tcp.c | 8 ++++---- src/modbus.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/modbus-rtu.c b/src/modbus-rtu.c index 00a2faf63..38f6deb1b 100644 --- a/src/modbus-rtu.c +++ b/src/modbus-rtu.c @@ -595,7 +595,7 @@ static int _modbus_rtu_connect(modbus_t *ctx) #endif ctx->s = open(ctx_rtu->device, flags); - if (ctx->s == -1) { + if (ctx->s < 0) { if (ctx->debug) { fprintf(stderr, "ERROR Can't open the device %s (%s)\n", ctx_rtu->device, strerror(errno)); @@ -1135,7 +1135,7 @@ static void _modbus_rtu_close(modbus_t *ctx) (int)GetLastError()); } #else - if (ctx->s != -1) { + if (ctx->s >= 0) { tcsetattr(ctx->s, TCSANOW, &ctx_rtu->old_tios); close(ctx->s); ctx->s = -1; diff --git a/src/modbus-tcp.c b/src/modbus-tcp.c index 015d66f23..4b454c4f0 100644 --- a/src/modbus-tcp.c +++ b/src/modbus-tcp.c @@ -323,7 +323,7 @@ static int _modbus_tcp_connect(modbus_t *ctx) #endif ctx->s = socket(PF_INET, flags, 0); - if (ctx->s == -1) { + if (ctx->s < 0) { return -1; } @@ -432,7 +432,7 @@ static int _modbus_tcp_pi_connect(modbus_t *ctx) /* Closes the network connection and socket in TCP mode */ static void _modbus_tcp_close(modbus_t *ctx) { - if (ctx->s != -1) { + if (ctx->s >= 0) { shutdown(ctx->s, SHUT_RDWR); close(ctx->s); ctx->s = -1; @@ -674,7 +674,7 @@ int modbus_tcp_accept(modbus_t *ctx, int *s) ctx->s = accept(*s, (struct sockaddr *)&addr, &addrlen); #endif - if (ctx->s == -1) { + if (ctx->s < 0) { return -1; } @@ -704,7 +704,7 @@ int modbus_tcp_pi_accept(modbus_t *ctx, int *s) ctx->s = accept(*s, (struct sockaddr *)&addr, &addrlen); #endif - if (ctx->s == -1) { + if (ctx->s < 0) { return -1; } diff --git a/src/modbus.c b/src/modbus.c index 83ffdc451..0dbf82b7e 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -371,7 +371,7 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type) } } - if (ctx->s == -1) { + if (ctx->s < 0) { if (ctx->debug) { fprintf(stderr, "ERROR The connection is not established.\n"); } From aea0b02d6de0574c3db81c64e55aa5b05646291b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Thu, 18 Aug 2022 13:52:37 +0200 Subject: [PATCH 056/210] Add 0x34d to .clabot --- .clabot | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.clabot b/.clabot index d53002de8..9ff284abb 100644 --- a/.clabot +++ b/.clabot @@ -18,7 +18,8 @@ "woodsnake", "taikiakita", "embeddedmz", - "jcarrano" + "jcarrano", + "0x34d" ], "message": "We require contributors to sign our Contributor License Agreement. In order for us to review and merge your code, please fill https://forms.gle/5635zjphDo5JEJQSA to get added. Your document will be manually checked by the maintainer. Be patient..." } From 51d366f710a3f98e583ca19d275591ae3a25c8df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Mon, 3 Oct 2022 22:09:10 +0200 Subject: [PATCH 057/210] Add jordanjohnson56 to CLA --- .clabot | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.clabot b/.clabot index 9ff284abb..f9db92036 100644 --- a/.clabot +++ b/.clabot @@ -19,7 +19,8 @@ "taikiakita", "embeddedmz", "jcarrano", - "0x34d" + "0x34d", + "jordanjohnson56" ], "message": "We require contributors to sign our Contributor License Agreement. In order for us to review and merge your code, please fill https://forms.gle/5635zjphDo5JEJQSA to get added. Your document will be manually checked by the maintainer. Be patient..." } From 6ab18dddb3b8e6c510f41637a32c8b384db923d0 Mon Sep 17 00:00:00 2001 From: Hans Ulrich Niedermann Date: Wed, 7 Sep 2022 21:12:38 +0200 Subject: [PATCH 058/210] Have autogen.sh fail if autoreconf fails If the autoreconf program does not succeed, autogen.sh should not succeed either. The definition of "succeed" being "exit with exit code 0". --- autogen.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/autogen.sh b/autogen.sh index c49e3bc92..57a2b60c8 100755 --- a/autogen.sh +++ b/autogen.sh @@ -6,9 +6,11 @@ if autoreconf --install --symlink --force; then echo "------------------------------------------------------" echo else + s="$?" echo echo "--------------------------" echo "Running autoreconf failed." echo "--------------------------" echo + exit "$s" fi From 14a435371d9218f4c05028e85870a2ec7750695c Mon Sep 17 00:00:00 2001 From: Hans Ulrich Niedermann Date: Wed, 7 Sep 2022 21:15:25 +0200 Subject: [PATCH 059/210] Have autoreconf fail if LT_INIT is not defined If the LT_INIT macro is not available in a *.m4 file to be found during the autoreconf run, it is impossible to build a useful configure script. Therefore, we blacklist the LT_INIT macro so when it appears unexpanded in the configure script, autoreconf will report the failure instead of silently creating a broken configure. This is generally a good idea for any macro not shipped with Automake or Autoconf. --- configure.ac | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.ac b/configure.ac index f170159dd..71041fdb7 100644 --- a/configure.ac +++ b/configure.ac @@ -73,7 +73,9 @@ esac AM_CONDITIONAL(OS_WIN32, test "$os_win32" = "true") AM_CONDITIONAL(OS_QNX, test "$os_qnx" = "true") +m4_pattern_forbid([^LT_INIT])dnl LT_INIT([disable-static win32-dll pic-only]) + AC_CHECK_HEADERS([ \ arpa/inet.h \ byteswap.h \ From 54c0923c7586e1b36c299f46a8c1e25c87ad8e62 Mon Sep 17 00:00:00 2001 From: Jordan Johnson Date: Sun, 4 Sep 2022 18:33:28 -0400 Subject: [PATCH 060/210] Fix doc links --- docs/index.md | 116 +++++++++++------------ docs/modbus_close.md | 2 +- docs/modbus_connect.md | 2 +- docs/modbus_get_byte_from_bits.md | 4 +- docs/modbus_get_byte_timeout.md | 6 +- docs/modbus_get_float.md | 6 +- docs/modbus_get_float_abcd.md | 8 +- docs/modbus_get_float_badc.md | 8 +- docs/modbus_get_float_cdab.md | 8 +- docs/modbus_get_float_dcba.md | 8 +- docs/modbus_get_indication_timeout.md | 6 +- docs/modbus_get_response_timeout.md | 6 +- docs/modbus_get_slave.md | 2 +- docs/modbus_get_socket.md | 2 +- docs/modbus_mapping_free.md | 2 +- docs/modbus_mapping_new.md | 6 +- docs/modbus_mapping_new_start_address.md | 4 +- docs/modbus_mask_write_register.md | 4 +- docs/modbus_new_rtu.md | 8 +- docs/modbus_new_tcp.md | 4 +- docs/modbus_new_tcp_pi.md | 6 +- docs/modbus_read_bits.md | 4 +- docs/modbus_read_input_bits.md | 2 +- docs/modbus_read_input_registers.md | 6 +- docs/modbus_read_registers.md | 4 +- docs/modbus_receive.md | 6 +- docs/modbus_receive_confirmation.md | 2 +- docs/modbus_reply.md | 2 +- docs/modbus_reply_exception.md | 2 +- docs/modbus_rtu_get_rts.md | 2 +- docs/modbus_rtu_get_rts_delay.md | 2 +- docs/modbus_rtu_set_rts.md | 2 +- docs/modbus_rtu_set_rts_delay.md | 2 +- docs/modbus_send_raw_request.md | 2 +- docs/modbus_set_bits_from_byte.md | 4 +- docs/modbus_set_bits_from_bytes.md | 4 +- docs/modbus_set_byte_timeout.md | 6 +- docs/modbus_set_float.md | 4 +- docs/modbus_set_float_abcd.md | 8 +- docs/modbus_set_float_badc.md | 8 +- docs/modbus_set_float_cdab.md | 8 +- docs/modbus_set_float_dcba.md | 6 +- docs/modbus_set_indication_timeout.md | 6 +- docs/modbus_set_response_timeout.md | 6 +- docs/modbus_set_slave.md | 2 +- docs/modbus_set_socket.md | 2 +- docs/modbus_tcp_accept.md | 6 +- docs/modbus_tcp_listen.md | 8 +- docs/modbus_tcp_pi_accept.md | 6 +- docs/modbus_tcp_pi_listen.md | 8 +- docs/modbus_write_and_read_registers.md | 6 +- docs/modbus_write_bit.md | 4 +- docs/modbus_write_bits.md | 4 +- docs/modbus_write_register.md | 4 +- docs/modbus_write_registers.md | 4 +- 55 files changed, 185 insertions(+), 185 deletions(-) diff --git a/docs/index.md b/docs/index.md index 9601cc826..230e0ffc7 100644 --- a/docs/index.md +++ b/docs/index.md @@ -42,7 +42,7 @@ Ethernet TCP) called backends in *libmodbus*. The first step is to allocate and set a `modbus_t` context according to the required backend (RTU or TCP) with a dedicated function, such as -[modbus_new_rtu](modbus_new_rtu). +[modbus_new_rtu](modbus_new_rtu.md). The function will return an opaque structure called `modbus_t` containing all necessary information to establish a connection with other Modbus devices according to the selected backend. @@ -69,7 +69,7 @@ always initiated by the master. Many Modbus devices can be connected together on the same physical link so before sending a message, you must set the slave (receiver) with -[modbus_set_slave](mobus_set_slave). If you're running a slave, its slave number +[modbus_set_slave](modbus_set_slave.md). If you're running a slave, its slave number will be used to filter received messages. The libmodbus implementation of RTU isn't time based as stated in original @@ -80,17 +80,17 @@ must take care to set a response timeout of slaves less than response timeout of master (ortherwise other slaves may ignore master requests when one of the slave is not responding). -To create a Modbus RTU context, you should use [modbus_new_rtu](modbus_new_rtu). +To create a Modbus RTU context, you should use [modbus_new_rtu](modbus_new_rtu.md). You can tweak the serial mode with the following functions: -- [modbus_rtu_get_serial_mode](modbus_rtu_get_serial_mode) -- [modbus_rtu_set_serial_mode](modbus_rtu_set_serial_mode) -- [modbus_rtu_get_rts](modbus_rtu_get_rts) -- [modbus_rtu_set_rts](modbus_rtu_set_rts) -- [modbus_rtu_set_custom_rts](modbus_rtu_set_custom_rts) -- [modbus_rtu_get_rts_delay](modbus_rtu_get_rts_delay) -- [modbus_rtu_set_rts_delay](modbus_rtu_set_rts_delay) +- [modbus_rtu_get_serial_mode](modbus_rtu_get_serial_mode.md) +- [modbus_rtu_set_serial_mode](modbus_rtu_set_serial_mode.md) +- [modbus_rtu_get_rts](modbus_rtu_get_rts.md) +- [modbus_rtu_set_rts](modbus_rtu_set_rts.md) +- [modbus_rtu_set_custom_rts](modbus_rtu_set_custom_rts.md) +- [modbus_rtu_get_rts_delay](modbus_rtu_get_rts_delay.md) +- [modbus_rtu_set_rts_delay](modbus_rtu_set_rts_delay.md) ### TCP (IPv4) Context @@ -98,7 +98,7 @@ The TCP backend implements a Modbus variant used for communications over TCP/IPv4 networks. It does not require a checksum calculation as lower layer takes care of the same. -To create a Modbus TCP context, you should use [modbus_new_tcp](modbus_new_tcp). +To create a Modbus TCP context, you should use [modbus_new_tcp](modbus_new_tcp.md). ### TCP PI (IPv4 and IPv6) Context @@ -109,25 +109,25 @@ calculation as lower layer takes care of the same. Contrary to the TCP IPv4 only backend, the TCP PI backend offers hostname resolution but it consumes about 1Kb of additional memory. -Create a Modbus TCP PI context, you should use [modbus_new_tcp_pi](modbus_new_tcp_pi). +Create a Modbus TCP PI context, you should use [modbus_new_tcp_pi](modbus_new_tcp_pi.md). ## Connection The following functions are provided to establish and close a connection with Modbus devices: -- [modbus_connect](modbus_connect) establishes a connection. -- [modbus_close](modbus_close) closes a connection. -- [modbus_flush](modbus_flush) flushed a connection. +- [modbus_connect](modbus_connect.md) establishes a connection. +- [modbus_close](modbus_close.md) closes a connection. +- [modbus_flush](modbus_flush.md) flushed a connection. In RTU, you should define the slave ID of your client with -[modbus_set_slave](modbus_set_slave). +[modbus_set_slave](modbus_set_slave.md). To analyse the exchanged data, you can enable the debug mode with -[modbus_set_debug](modbus_set_debug). +[modbus_set_debug](modbus_set_debug.md). Once you have completed the communication or at the end of your program, you -should free the resources with the common function, [modbus_free](modbus_free) +should free the resources with the common function, [modbus_free](modbus_free.md) ## Reads and writes from the client @@ -137,31 +137,31 @@ send Modbus requests: To read data: -- [modbus_read_bits](modbus_read_bits) -- [modbus_read_input_bits](modbus_read_input_bits) -- [modbus_read_registers](modbus_read_registers) -- [modbus_read_input_registers](modbus_read_input_registers) -- [modbus_report_slave_id](modbus_report_slave_id) +- [modbus_read_bits](modbus_read_bits.md) +- [modbus_read_input_bits](modbus_read_input_bits.md) +- [modbus_read_registers](modbus_read_registers.md) +- [modbus_read_input_registers](modbus_read_input_registers.md) +- [modbus_report_slave_id](modbus_report_slave_id.md) To write data: -- [modbus_write_bit](modbus_write_bit) -- [modbus_write_register](modbus_write_register) -- [modbus_write_bits](modbus_write_bits) -- [modbus_write_registers](modbus_write_registers) +- [modbus_write_bit](modbus_write_bit.md) +- [modbus_write_register](modbus_write_register.md) +- [modbus_write_bits](modbus_write_bits.md) +- [modbus_write_registers](modbus_write_registers.md) To write and read data in a single operation: -- [modbus_write_and_read_registers](modbus_write_and_read_registers) +- [modbus_write_and_read_registers](modbus_write_and_read_registers.md) To send and receive low-level requests: -- [modbus_send_raw_request](modbus_send_raw_request) -- [modbus_receive_confirmation](modbus_receive_confirmation) +- [modbus_send_raw_request](modbus_send_raw_request.md) +- [modbus_receive_confirmation](modbus_receive_confirmation.md) To reply to an exception: -- [modbus_reply_exception](modbus_reply_exception) +- [modbus_reply_exception](modbus_reply_exception.md) ## Handling requests from server @@ -171,39 +171,39 @@ handle requests: Data mapping: -- [modbus_mapping_new](modbus_mapping_new) -- [modbus_mapping_free](modbus_mapping_free) +- [modbus_mapping_new](modbus_mapping_new.md) +- [modbus_mapping_free](modbus_mapping_free.md) Receive: -- [modbus_receive](modbus_receive) +- [modbus_receive](modbus_receive.md) Reply: -- [modbus_reply](modbus_reply) -- [modbus_reply_exception](modbus_reply_exception) +- [modbus_reply](modbus_reply.md) +- [modbus_reply_exception](modbus_reply_exception.md) ## Advanced functions Timeout settings: -- [modbus_get_byte_timeout](modbus_get_byte_timeout) -- [modbus_set_byte_timeout](modbus_set_byte_timeout) -- [modbus_get_response_timeout](modbus_get_response_timeout) -- [modbus_set_response_timeout](modbus_set_response_timeout) +- [modbus_get_byte_timeout](modbus_get_byte_timeout.md) +- [modbus_set_byte_timeout](modbus_set_byte_timeout.md) +- [modbus_get_response_timeout](modbus_get_response_timeout.md) +- [modbus_set_response_timeout](modbus_set_response_timeout.md) Error recovery mode: -- [modbus_set_error_recovery](modbus_set_error_recovery) +- [modbus_set_error_recovery](modbus_set_error_recovery.md) Setter/getter of internal socket: -- [modbus_set_socket](modbus_set_socket) -- [modbus_get_socket](modbus_get_socket) +- [modbus_set_socket](modbus_set_socket.md) +- [modbus_get_socket](modbus_get_socket.md) Information about header: -- [modbus_get_header_length](modbus_get_header_length) +- [modbus_get_header_length](modbus_get_header_length.md) ## Data handling @@ -220,22 +220,22 @@ Macros for data manipulation: Handling of bits and bytes: -- [modbus_set_bits_from_byte](modbus_set_bits_from_byte) -- [modbus_set_bits_from_bytes](modbus_set_bits_from_bytes) -- [modbus_get_byte_from_bits](modbus_get_byte_from_bits) +- [modbus_set_bits_from_byte](modbus_set_bits_from_byte.md) +- [modbus_set_bits_from_bytes](modbus_set_bits_from_bytes.md) +- [modbus_get_byte_from_bits](modbus_get_byte_from_bits.md) Set or get float numbers: -- [modbus_get_float_abcd](modbus_get_float_abcd) -- [modbus_set_float_abcd](modbus_set_float_abcd) -- [modbus_get_float_badc](modbus_get_float_badc) -- [modbus_set_float_badc](modbus_set_float_badc) -- [modbus_get_float_cdab](modbus_get_float_cdab) -- [modbus_set_float_cdab](modbus_set_float_cdab) -- [modbus_get_float_dcba](modbus_get_float_dcba) -- [modbus_set_float_dcba](modbus_set_float_dcba) -- [modbus_get_float](modbus_get_float) **deprecated** -- [modbus_set_float](modbus_set_float) **deprecated** +- [modbus_get_float_abcd](modbus_get_float_abcd.md) +- [modbus_set_float_abcd](modbus_set_float_abcd.md) +- [modbus_get_float_badc](modbus_get_float_badc.md) +- [modbus_set_float_badc](modbus_set_float_badc.md) +- [modbus_get_float_cdab](modbus_get_float_cdab.md) +- [modbus_set_float_cdab](modbus_set_float_cdab.md) +- [modbus_get_float_dcba](modbus_get_float_dcba.md) +- [modbus_set_float_dcba](modbus_set_float_dcba.md) +- [modbus_get_float](modbus_get_float.md) **deprecated** +- [modbus_set_float](modbus_set_float.md) **deprecated** ## Error handling @@ -247,7 +247,7 @@ shall return either a NULL value (if returning a pointer) or a negative value The *modbus_strerror()* function is provided to translate libmodbus-specific error codes into error message strings; for details refer to -[modbus_strerror](modbus_strerror). +[modbus_strerror](modbus_strerror.md). ## Miscellaneous diff --git a/docs/modbus_close.md b/docs/modbus_close.md index 6fe760666..05cc080eb 100644 --- a/docs/modbus_close.md +++ b/docs/modbus_close.md @@ -37,4 +37,4 @@ modbus_free(ctx); ## See also -- [modbus_connect](modbus_connect) +- [modbus_connect](modbus_connect.md) diff --git a/docs/modbus_connect.md b/docs/modbus_connect.md index f3bf5e3f1..541d9d982 100644 --- a/docs/modbus_connect.md +++ b/docs/modbus_connect.md @@ -37,4 +37,4 @@ if (modbus_connect(ctx) == -1) { ## See also -- [modbus_close](modbus_close) +- [modbus_close](modbus_close.md) diff --git a/docs/modbus_get_byte_from_bits.md b/docs/modbus_get_byte_from_bits.md index 84b4b6500..3b684694a 100644 --- a/docs/modbus_get_byte_from_bits.md +++ b/docs/modbus_get_byte_from_bits.md @@ -22,5 +22,5 @@ The function shall return a byte containing the bits read. ## See also -- [modbus_set_bits_from_byte](modbus_set_bits_from_byte) -- [modbus_set_bits_from_bytes](modbus_set_bits_from_bytes) +- [modbus_set_bits_from_byte](modbus_set_bits_from_byte.md) +- [modbus_set_bits_from_bytes](modbus_set_bits_from_bytes.md) diff --git a/docs/modbus_get_byte_timeout.md b/docs/modbus_get_byte_timeout.md index 04a5732cb..7ba027fa4 100644 --- a/docs/modbus_get_byte_timeout.md +++ b/docs/modbus_get_byte_timeout.md @@ -33,6 +33,6 @@ modbus_get_byte_timeout(ctx, &to_sec, &to_usec); ## See also -- [modbus_set_byte_timeout](modbus_set_byte_timeout) -- [modbus_get_response_timeout](modbus_get_response_timeout) -- [modbus_set_response_timeout](modbus_set_response_timeout) +- [modbus_set_byte_timeout](modbus_set_byte_timeout.md) +- [modbus_get_response_timeout](modbus_get_response_timeout.md) +- [modbus_set_response_timeout](modbus_set_response_timeout.md) diff --git a/docs/modbus_get_float.md b/docs/modbus_get_float.md index 8eec56043..fb0d9a9a0 100644 --- a/docs/modbus_get_float.md +++ b/docs/modbus_get_float.md @@ -26,6 +26,6 @@ The function shall return a float. ## See also -- [modbus_set_float](modbus_set_float) -- [modbus_set_float_dcba](modbus_set_float_dcba) -- [modbus_get_float_dcba](modbus_get_float_dcba) +- [modbus_set_float](modbus_set_float.md) +- [modbus_set_float_dcba](modbus_set_float_dcba.md) +- [modbus_get_float_dcba](modbus_get_float_dcba.md) diff --git a/docs/modbus_get_float_abcd.md b/docs/modbus_get_float_abcd.md index 2f21271ce..886da88d5 100644 --- a/docs/modbus_get_float_abcd.md +++ b/docs/modbus_get_float_abcd.md @@ -23,7 +23,7 @@ The function shall return a float. ## See also -- [modbus_set_float_abcd](modbus_set_float_abcd) -- [modbus_get_float_badc](modbus_get_float_badc) -- [modbus_get_float_cdab](modbus_get_float_cdab) -- [modbus_get_float_dcba](modbus_get_float_dcba) +- [modbus_set_float_abcd](modbus_set_float_abcd.md) +- [modbus_get_float_badc](modbus_get_float_badc.md) +- [modbus_get_float_cdab](modbus_get_float_cdab.md) +- [modbus_get_float_dcba](modbus_get_float_dcba.md) diff --git a/docs/modbus_get_float_badc.md b/docs/modbus_get_float_badc.md index a730e8cfd..e256ec496 100644 --- a/docs/modbus_get_float_badc.md +++ b/docs/modbus_get_float_badc.md @@ -23,7 +23,7 @@ The function shall return a float. ## See also -- [modbus_set_float_badc](modbus_set_float_badc) -- [modbus_get_float_abcd](modbus_get_float_abcd) -- [modbus_get_float_cdab](modbus_get_float_cdab) -- [modbus_get_float_dcba](modbus_get_float_dcba) +- [modbus_set_float_badc](modbus_set_float_badc.md) +- [modbus_get_float_abcd](modbus_get_float_abcd.md) +- [modbus_get_float_cdab](modbus_get_float_cdab.md) +- [modbus_get_float_dcba](modbus_get_float_dcba.md) diff --git a/docs/modbus_get_float_cdab.md b/docs/modbus_get_float_cdab.md index fb4ba3550..2bf7eed5b 100644 --- a/docs/modbus_get_float_cdab.md +++ b/docs/modbus_get_float_cdab.md @@ -23,7 +23,7 @@ The function shall return a float. ## See also -- [modbus_set_float_cdab](modbus_set_float_cdab) -- [modbus_get_float_abcd](modbus_get_float_abcd) -- [modbus_get_float_badc](modbus_get_float_badc) -- [modbus_get_float_dcba](modbus_get_float_dcba) +- [modbus_set_float_cdab](modbus_set_float_cdab.md) +- [modbus_get_float_abcd](modbus_get_float_abcd.md) +- [modbus_get_float_badc](modbus_get_float_badc.md) +- [modbus_get_float_dcba](modbus_get_float_dcba.md) diff --git a/docs/modbus_get_float_dcba.md b/docs/modbus_get_float_dcba.md index 0d22c352c..458d985d5 100644 --- a/docs/modbus_get_float_dcba.md +++ b/docs/modbus_get_float_dcba.md @@ -23,7 +23,7 @@ The function shall return a float. ## See also -- [modbus_set_float_dcba](modbus_set_float_dcba) -- [modbus_get_float_abcd](modbus_get_float_abcd) -- [modbus_get_float_badc](modbus_get_float_badc) -- [modbus_get_float_cdab](modbus_get_float_cdab) +- [modbus_set_float_dcba](modbus_set_float_dcba.md) +- [modbus_get_float_abcd](modbus_get_float_abcd.md) +- [modbus_get_float_badc](modbus_get_float_badc.md) +- [modbus_get_float_cdab](modbus_get_float_cdab.md) diff --git a/docs/modbus_get_indication_timeout.md b/docs/modbus_get_indication_timeout.md index 11a9f866c..833fddd36 100644 --- a/docs/modbus_get_indication_timeout.md +++ b/docs/modbus_get_indication_timeout.md @@ -34,6 +34,6 @@ modbus_get_indication_timeout(ctx, &to_sec, &to_usec); ## See also -- [modbus_set_indication_timeout](modbus_set_indication_timeout) -- [modbus_get_response_timeout](modbus_get_response_timeout) -- [modbus_set_response_timeout](modbus_set_response_timeout) +- [modbus_set_indication_timeout](modbus_set_indication_timeout.md) +- [modbus_get_response_timeout](modbus_get_response_timeout.md) +- [modbus_set_response_timeout](modbus_set_response_timeout.md) diff --git a/docs/modbus_get_response_timeout.md b/docs/modbus_get_response_timeout.md index 93464c221..33c7de90e 100644 --- a/docs/modbus_get_response_timeout.md +++ b/docs/modbus_get_response_timeout.md @@ -35,6 +35,6 @@ modbus_set_response_timeout(ctx, 0, 0); ## See also -- [modbus_set_response_timeout](modbus_set_response_timeout) -- [modbus_get_byte_timeout](modbus_get_byte_timeout) -- [modbus_set_byte_timeout](modbus_set_byte_timeout) +- [modbus_set_response_timeout](modbus_set_response_timeout.md) +- [modbus_get_byte_timeout](modbus_get_byte_timeout.md) +- [modbus_set_byte_timeout](modbus_set_byte_timeout.md) diff --git a/docs/modbus_get_slave.md b/docs/modbus_get_slave.md index 7a5496e0f..4e7a95859 100644 --- a/docs/modbus_get_slave.md +++ b/docs/modbus_get_slave.md @@ -26,4 +26,4 @@ return -1 and set errno to one of the values defined below. ## See also -- [modbus_set_slave](modbus_set_slave) +- [modbus_set_slave](modbus_set_slave.md) diff --git a/docs/modbus_get_socket.md b/docs/modbus_get_socket.md index 086cff5c0..585437988 100644 --- a/docs/modbus_get_socket.md +++ b/docs/modbus_get_socket.md @@ -22,4 +22,4 @@ successful. Otherwise it shall return -1 and set errno. ## See also -- [modbus_set_socket](modbus_set_socket) +- [modbus_set_socket](modbus_set_socket.md) diff --git a/docs/modbus_mapping_free.md b/docs/modbus_mapping_free.md index 380f618be..2da2924ac 100644 --- a/docs/modbus_mapping_free.md +++ b/docs/modbus_mapping_free.md @@ -21,4 +21,4 @@ There is no return values. ## See also -- [modbus_mapping_new](modbus_mapping_new) +- [modbus_mapping_new](modbus_mapping_new.md) diff --git a/docs/modbus_mapping_new.md b/docs/modbus_mapping_new.md index fdc11c560..4056fa9ce 100644 --- a/docs/modbus_mapping_new.md +++ b/docs/modbus_mapping_new.md @@ -17,7 +17,7 @@ input bits, registers and inputs registers. The pointers are stored in modbus_mapping_t structure. All values of the arrays are initialized to zero. This function is equivalent to a call of the -[modbus_mapping_new_start_address](modbus_mapping_new_start_address) function +[modbus_mapping_new_start_address](modbus_mapping_new_start_address.md) function with all start addresses to `0`. If it isn't necessary to allocate an array for a specific type of data, you can @@ -56,5 +56,5 @@ if (mb_mapping == NULL) { ## See also -- [modbus_mapping_free](modbus_mapping_free) -- [modbus_mapping_new_start_address](modbus_mapping_new_start_address) +- [modbus_mapping_free](modbus_mapping_free.md) +- [modbus_mapping_new_start_address](modbus_mapping_new_start_address.md) diff --git a/docs/modbus_mapping_new_start_address.md b/docs/modbus_mapping_new_start_address.md index edc267bc4..588c49eef 100644 --- a/docs/modbus_mapping_new_start_address.md +++ b/docs/modbus_mapping_new_start_address.md @@ -81,5 +81,5 @@ if (mb_mapping == NULL) { ## See also -- [modbus_mapping_new](modbus_mapping_new) -- [modbus_mapping_free](modbus_mapping_free) +- [modbus_mapping_new](modbus_mapping_new.md) +- [modbus_mapping_free](modbus_mapping_free.md) diff --git a/docs/modbus_mask_write_register.md b/docs/modbus_mask_write_register.md index c6eb60e9f..b0bfee995 100644 --- a/docs/modbus_mask_write_register.md +++ b/docs/modbus_mask_write_register.md @@ -26,5 +26,5 @@ errno. ## See also -- [modbus_read_registers](modbus_read_registers) -- [modbus_write_registers](modbus_write_registers) +- [modbus_read_registers](modbus_read_registers.md) +- [modbus_write_registers](modbus_write_registers.md) diff --git a/docs/modbus_new_rtu.md b/docs/modbus_new_rtu.md index 33fd5ec67..d9187dbcb 100644 --- a/docs/modbus_new_rtu.md +++ b/docs/modbus_new_rtu.md @@ -36,8 +36,8 @@ The `stop_bits` argument specifies the bits of stop, the allowed values are 1 and 2. Once the `modbus_t` structure is initialized, you must set the slave of your -device with [modbus_set_slave](modbus_set_slave) and connect to the serial bus with -[modbus_connect](modbus_connect). +device with [modbus_set_slave](modbus_set_slave.md) and connect to the serial bus with +[modbus_connect](modbus_connect.md). ## Return value @@ -73,5 +73,5 @@ if (modbus_connect(ctx) == -1) { ## See also -- [modbus_new_tcp](modbus_new_tcp) -- [modbus_free](modbus_free) +- [modbus_new_tcp](modbus_new_tcp.md) +- [modbus_free](modbus_free.md) diff --git a/docs/modbus_new_tcp.md b/docs/modbus_new_tcp.md index 30ab07f52..57ba1a896 100644 --- a/docs/modbus_new_tcp.md +++ b/docs/modbus_new_tcp.md @@ -56,5 +56,5 @@ if (modbus_connect(ctx) == -1) { ## See also -- [modbus_tcp_listen](modbus_tcp_listen) -- [modbus_free](modbus_free) +- [modbus_tcp_listen](modbus_tcp_listen.md) +- [modbus_free](modbus_free.md) diff --git a/docs/modbus_new_tcp_pi.md b/docs/modbus_new_tcp_pi.md index 90d430872..c1eca2386 100644 --- a/docs/modbus_new_tcp_pi.md +++ b/docs/modbus_new_tcp_pi.md @@ -57,6 +57,6 @@ if (modbus_connect(ctx) == -1) { ## See also -- [modbus_new_tcp](modbus_new_tcp) -- [modbus_tcp_pi_listen](modbus_tcp_pi_listen) -- [modbus_free](modbus_free) +- [modbus_new_tcp](modbus_new_tcp.md) +- [modbus_tcp_pi_listen](modbus_tcp_pi_listen.md) +- [modbus_free](modbus_free.md) diff --git a/docs/modbus_read_bits.md b/docs/modbus_read_bits.md index 9e30976c5..bb3e78a7b 100644 --- a/docs/modbus_read_bits.md +++ b/docs/modbus_read_bits.md @@ -32,5 +32,5 @@ shall return -1 and set errno. ## See also -- [modbus_write_bit](modbus_write_bit) -- [modbus_write_bits](modbus_write_bits) +- [modbus_write_bit](modbus_write_bit.md) +- [modbus_write_bits](modbus_write_bits.md) diff --git a/docs/modbus_read_input_bits.md b/docs/modbus_read_input_bits.md index 2991cfb0e..d41af2eb4 100644 --- a/docs/modbus_read_input_bits.md +++ b/docs/modbus_read_input_bits.md @@ -32,4 +32,4 @@ successful. Otherwise it shall return -1 and set errno. ## See also -- [modbus_read_input_registers](modbus_read_input_registers) +- [modbus_read_input_registers](modbus_read_input_registers.md) diff --git a/docs/modbus_read_input_registers.md b/docs/modbus_read_input_registers.md index 3e74a5fb9..b012622ce 100644 --- a/docs/modbus_read_input_registers.md +++ b/docs/modbus_read_input_registers.md @@ -34,6 +34,6 @@ successful. Otherwise it shall return -1 and set errno. ## See also -- [modbus_read_input_bits](modbus_read_input_bits) -- [modbus_write_register](modbus_write_register) -- [modbus_write_registers](modbus_write_registers) +- [modbus_read_input_bits](modbus_read_input_bits.md) +- [modbus_write_register](modbus_write_register.md) +- [modbus_write_registers](modbus_write_registers.md) diff --git a/docs/modbus_read_registers.md b/docs/modbus_read_registers.md index 6150da924..cde30229c 100644 --- a/docs/modbus_read_registers.md +++ b/docs/modbus_read_registers.md @@ -61,5 +61,5 @@ modbus_free(ctx); ## See also -- [modbus_write_register](modbus_write_register) -- [modbus_write_registers](modbus_write_registers) +- [modbus_write_register](modbus_write_register.md) +- [modbus_write_registers](modbus_write_registers.md) diff --git a/docs/modbus_receive.md b/docs/modbus_receive.md index fa3c54846..7f1c5b63c 100644 --- a/docs/modbus_receive.md +++ b/docs/modbus_receive.md @@ -17,7 +17,7 @@ socket of the context `ctx`. This function is used by Modbus slave/server to receive and analyze indication request sent by the masters/clients. If you need to use another socket or file descriptor than the one defined in the -context `ctx`, see the function [modbus_set_socket](modbus_set_socket). +context `ctx`, see the function [modbus_set_socket](modbus_set_socket.md). ## Return value @@ -28,5 +28,5 @@ shall return -1 and set errno. ## See also -- [modbus_set_socket](modbus_set_socket) -- [modbus_reply](modbus_reply) +- [modbus_set_socket](modbus_set_socket.md) +- [modbus_reply](modbus_reply.md) diff --git a/docs/modbus_receive_confirmation.md b/docs/modbus_receive_confirmation.md index 700e6c40b..68ddeff15 100644 --- a/docs/modbus_receive_confirmation.md +++ b/docs/modbus_receive_confirmation.md @@ -40,4 +40,4 @@ rc = modbus_receive_confirmation(ctx, rsp); ## See also -- [modbus_send_raw_request](modbus_send_raw_request) +- [modbus_send_raw_request](modbus_send_raw_request.md) diff --git a/docs/modbus_reply.md b/docs/modbus_reply.md index 2ca3090b7..f3451e439 100644 --- a/docs/modbus_reply.md +++ b/docs/modbus_reply.md @@ -36,4 +36,4 @@ See also the errors returned by the syscall used to send the response (eg. send ## See also -- [modbus_reply_exception](modbus_reply_exception) +- [modbus_reply_exception](modbus_reply_exception.md) diff --git a/docs/modbus_reply_exception.md b/docs/modbus_reply_exception.md index bba24a5c0..640e43527 100644 --- a/docs/modbus_reply_exception.md +++ b/docs/modbus_reply_exception.md @@ -42,4 +42,4 @@ successful. Otherwise it shall return -1 and set errno. ## See also -- [modbus_reply](modbus_reply) +- [modbus_reply](modbus_reply.md) diff --git a/docs/modbus_rtu_get_rts.md b/docs/modbus_rtu_get_rts.md index 16b2d5374..35940bc94 100644 --- a/docs/modbus_rtu_get_rts.md +++ b/docs/modbus_rtu_get_rts.md @@ -32,4 +32,4 @@ return -1 and set errno. ## See also -- [modbus_rtu_set_rts](modbus_rtu_set_rts) +- [modbus_rtu_set_rts](modbus_rtu_set_rts.md) diff --git a/docs/modbus_rtu_get_rts_delay.md b/docs/modbus_rtu_get_rts_delay.md index 236fecf81..ec93744bc 100644 --- a/docs/modbus_rtu_get_rts_delay.md +++ b/docs/modbus_rtu_get_rts_delay.md @@ -28,4 +28,4 @@ microseconds if successful. Otherwise it shall return -1 and set errno. ## See also -- [modbus_rtu_set_rts_delay](modbus_rtu_set_rts_delay) +- [modbus_rtu_set_rts_delay](modbus_rtu_set_rts_delay.md) diff --git a/docs/modbus_rtu_set_rts.md b/docs/modbus_rtu_set_rts.md index d903e31fb..5150c0cbd 100644 --- a/docs/modbus_rtu_set_rts.md +++ b/docs/modbus_rtu_set_rts.md @@ -66,4 +66,4 @@ modbus_free(ctx); ## See also -- [modbus_rtu_get_rts](modbus_rtu_get_rts) +- [modbus_rtu_get_rts](modbus_rtu_get_rts.md) diff --git a/docs/modbus_rtu_set_rts_delay.md b/docs/modbus_rtu_set_rts_delay.md index 3c0cc9736..1ab1258b5 100644 --- a/docs/modbus_rtu_set_rts_delay.md +++ b/docs/modbus_rtu_set_rts_delay.md @@ -28,4 +28,4 @@ Otherwise it shall return -1 and set errno. ## See also -- [modbus_rtu_get_rts_delay](modbus_rtu_get_rts_delay) +- [modbus_rtu_get_rts_delay](modbus_rtu_get_rts_delay.md) diff --git a/docs/modbus_send_raw_request.md b/docs/modbus_send_raw_request.md index 99b65b8ff..66ceceaa2 100644 --- a/docs/modbus_send_raw_request.md +++ b/docs/modbus_send_raw_request.md @@ -54,4 +54,4 @@ modbus_free(ctx); ## See also -- [modbus_receive_confirmation](modbus_receive_confirmation) +- [modbus_receive_confirmation](modbus_receive_confirmation.md) diff --git a/docs/modbus_set_bits_from_byte.md b/docs/modbus_set_bits_from_byte.md index 702b9ba76..a3c4122d9 100644 --- a/docs/modbus_set_bits_from_byte.md +++ b/docs/modbus_set_bits_from_byte.md @@ -23,5 +23,5 @@ There is no return values. ## See also -- [modbus_set_bits_from_byte](modbus_set_bits_from_byte) -- [modbus_set_bits_from_bytes](modbus_set_bits_from_bytes) +- [modbus_set_bits_from_byte](modbus_set_bits_from_byte.md) +- [modbus_set_bits_from_bytes](modbus_set_bits_from_bytes.md) diff --git a/docs/modbus_set_bits_from_bytes.md b/docs/modbus_set_bits_from_bytes.md index 2dd0dbde7..a968d5c67 100644 --- a/docs/modbus_set_bits_from_bytes.md +++ b/docs/modbus_set_bits_from_bytes.md @@ -22,5 +22,5 @@ There is no return values. ## See also -- [modbus_set_bits_from_byte](modbus_set_bits_from_byte) -- [modbus_get_byte_from_bits](modbus_get_byte_from_bits) +- [modbus_set_bits_from_byte](modbus_set_bits_from_byte.md) +- [modbus_get_byte_from_bits](modbus_get_byte_from_bits.md) diff --git a/docs/modbus_set_byte_timeout.md b/docs/modbus_set_byte_timeout.md index 5bed59d97..25c0a7912 100644 --- a/docs/modbus_set_byte_timeout.md +++ b/docs/modbus_set_byte_timeout.md @@ -37,7 +37,7 @@ errno. ## See also -- [modbus_get_byte_timeout](modbus_get_byte_timeout) -- [modbus_get_response_timeout](modbus_get_response_timeout) -- [modbus_set_response_timeout](modbus_set_response_timeout) +- [modbus_get_byte_timeout](modbus_get_byte_timeout.md) +- [modbus_get_response_timeout](modbus_get_response_timeout.md) +- [modbus_set_response_timeout](modbus_set_response_timeout.md) w diff --git a/docs/modbus_set_float.md b/docs/modbus_set_float.md index 6d5a52437..34fc0a1a8 100644 --- a/docs/modbus_set_float.md +++ b/docs/modbus_set_float.md @@ -25,5 +25,5 @@ There is no return values. ## See also -- [modbus_get_float](modbus_get_float) -- [modbus_set_float_dcba](modbus_set_float_dcba) +- [modbus_get_float](modbus_get_float.md) +- [modbus_set_float_dcba](modbus_set_float_dcba.md) diff --git a/docs/modbus_set_float_abcd.md b/docs/modbus_set_float_abcd.md index 8a3c65af7..070893eb1 100644 --- a/docs/modbus_set_float_abcd.md +++ b/docs/modbus_set_float_abcd.md @@ -22,7 +22,7 @@ There is no return values. ## See also -- [modbus_get_float_abcd](modbus_get_float_abcd) -- [modbus_set_float_badc](modbus_set_float_badc) -- [modbus_set_float_cdab](modbus_set_float_cdab) -- [modbus_set_float_dcba](modbus_set_float_dcba) +- [modbus_get_float_abcd](modbus_get_float_abcd.md) +- [modbus_set_float_badc](modbus_set_float_badc.md) +- [modbus_set_float_cdab](modbus_set_float_cdab.md) +- [modbus_set_float_dcba](modbus_set_float_dcba.md) diff --git a/docs/modbus_set_float_badc.md b/docs/modbus_set_float_badc.md index e763eecc0..c0559efb0 100644 --- a/docs/modbus_set_float_badc.md +++ b/docs/modbus_set_float_badc.md @@ -22,7 +22,7 @@ There is no return values. ## See also -- [modbus_get_float_badc](modbus_get_float_badc) -- [modbus_set_float_abcd](modbus_set_float_abcd) -- [modbus_set_float_cdab](modbus_set_float_cdab) -- [modbus_set_float_dcba](modbus_set_float_dcba) +- [modbus_get_float_badc](modbus_get_float_badc.md) +- [modbus_set_float_abcd](modbus_set_float_abcd.md) +- [modbus_set_float_cdab](modbus_set_float_cdab.md) +- [modbus_set_float_dcba](modbus_set_float_dcba.md) diff --git a/docs/modbus_set_float_cdab.md b/docs/modbus_set_float_cdab.md index 588dbbfdb..d91470bf1 100644 --- a/docs/modbus_set_float_cdab.md +++ b/docs/modbus_set_float_cdab.md @@ -23,7 +23,7 @@ There is no return values. ## See also -- [modbus_get_float_cdab](modbus_get_float_cdab) -- [modbus_set_float_abcd](modbus_set_float_abcd) -- [modbus_set_float_badc](modbus_set_float_badc) -- [modbus_set_float_dcba](modbus_set_float_dcba) +- [modbus_get_float_cdab](modbus_get_float_cdab.md) +- [modbus_set_float_abcd](modbus_set_float_abcd.md) +- [modbus_set_float_badc](modbus_set_float_badc.md) +- [modbus_set_float_dcba](modbus_set_float_dcba.md) diff --git a/docs/modbus_set_float_dcba.md b/docs/modbus_set_float_dcba.md index 04f0d98ef..8f6e48272 100644 --- a/docs/modbus_set_float_dcba.md +++ b/docs/modbus_set_float_dcba.md @@ -22,6 +22,6 @@ There is no return values. ## See also -- [modbus_get_float_dcba](modbus_get_float_dcba) -- [modbus_set_float](modbus_set_float) -- [modbus_get_float](modbus_get_float) +- [modbus_get_float_dcba](modbus_get_float_dcba.md) +- [modbus_set_float](modbus_set_float.md) +- [modbus_get_float](modbus_get_float.md) diff --git a/docs/modbus_set_indication_timeout.md b/docs/modbus_set_indication_timeout.md index 2fc5c591b..cb61b51b4 100644 --- a/docs/modbus_set_indication_timeout.md +++ b/docs/modbus_set_indication_timeout.md @@ -31,6 +31,6 @@ errno. ## See also -- [modbus_get_indication_timeout](modbus_get_indication_timeout) -- [modbus_get_response_timeout](modbus_get_response_timeout) -- [modbus_set_response_timeout](modbus_set_response_timeout) +- [modbus_get_indication_timeout](modbus_get_indication_timeout.md) +- [modbus_get_response_timeout](modbus_get_response_timeout.md) +- [modbus_set_response_timeout](modbus_set_response_timeout.md) diff --git a/docs/modbus_set_response_timeout.md b/docs/modbus_set_response_timeout.md index 8378692fd..99f15bac6 100644 --- a/docs/modbus_set_response_timeout.md +++ b/docs/modbus_set_response_timeout.md @@ -46,6 +46,6 @@ modbus_set_response_timeout(ctx, 0, 200000); ## See also -- [modbus_get_response_timeout](modbus_get_response_timeout) -- [modbus_get_byte_timeout](modbus_get_byte_timeout) -- [modbus_set_byte_timeout](modbus_set_byte_timeout) +- [modbus_get_response_timeout](modbus_get_response_timeout.md) +- [modbus_get_byte_timeout](modbus_get_byte_timeout.md) +- [modbus_set_byte_timeout](modbus_set_byte_timeout.md) diff --git a/docs/modbus_set_slave.md b/docs/modbus_set_slave.md index c79d21502..7d6042c6c 100644 --- a/docs/modbus_set_slave.md +++ b/docs/modbus_set_slave.md @@ -69,4 +69,4 @@ if (modbus_connect(ctx) == -1) { ## See also -- [modbus_get_slave](modbus_get_slave) +- [modbus_get_slave](modbus_get_slave.md) diff --git a/docs/modbus_set_socket.md b/docs/modbus_set_socket.md index 8df91a015..cdf811b5b 100644 --- a/docs/modbus_set_socket.md +++ b/docs/modbus_set_socket.md @@ -42,4 +42,4 @@ if (FD_ISSET(master_socket, &rdset)) { ## See also -- [modbus_get_socket](modbus_get_socket) +- [modbus_get_socket](modbus_get_socket.md) diff --git a/docs/modbus_tcp_accept.md b/docs/modbus_tcp_accept.md index 16f75c857..6deef5981 100644 --- a/docs/modbus_tcp_accept.md +++ b/docs/modbus_tcp_accept.md @@ -41,6 +41,6 @@ modbus_free(ctx); ## See also -- [modbus_tcp_pi_accept](modbus_tcp_pi_accept) -- [modbus_tcp_listen](modbus_tcp_listen) -- [modbus_tcp_pi_listen](modbus_tcp_pi_listen) +- [modbus_tcp_pi_accept](modbus_tcp_pi_accept.md) +- [modbus_tcp_listen](modbus_tcp_listen.md) +- [modbus_tcp_pi_listen](modbus_tcp_pi_listen.md) diff --git a/docs/modbus_tcp_listen.md b/docs/modbus_tcp_listen.md index bf090c5cd..cdf308cd4 100644 --- a/docs/modbus_tcp_listen.md +++ b/docs/modbus_tcp_listen.md @@ -15,7 +15,7 @@ int modbus_tcp_listen(modbus_t *ctx, int nb_connection); The *modbus_tcp_listen()* function shall create a socket and listen to maximum `nb_connection` incoming connections on the specified IP address. The context -`ctx` must be allocated and initialized with [modbus_new_tcp](modbus_new_tcp) before to +`ctx` must be allocated and initialized with [modbus_new_tcp](modbus_new_tcp.md) before to set the IP address to listen, if IP address is set to NULL or '0.0.0.0', any addresses will be listen. @@ -57,6 +57,6 @@ modbus_free(ctx); ## See also -- [modbus_new_tcp](modbus_new_tcp) -- [modbus_tcp_accept](modbus_tcp_accept) -- [modbus_tcp_pi_listen](modbus_tcp_pi_listen) +- [modbus_new_tcp](modbus_new_tcp.md) +- [modbus_tcp_accept](modbus_tcp_accept.md) +- [modbus_tcp_pi_listen](modbus_tcp_pi_listen.md) diff --git a/docs/modbus_tcp_pi_accept.md b/docs/modbus_tcp_pi_accept.md index 6ee0ccb95..85a182065 100644 --- a/docs/modbus_tcp_pi_accept.md +++ b/docs/modbus_tcp_pi_accept.md @@ -41,6 +41,6 @@ modbus_free(ctx); ## See also -- [modbus_tcp_pi_accept](modbus_tcp_pi_accept) -- [modbus_tcp_listen](modbus_tcp_listen) -- [modbus_tcp_pi_listen](modbus_tcp_pi_listen) +- [modbus_tcp_pi_accept](modbus_tcp_pi_accept.md) +- [modbus_tcp_listen](modbus_tcp_listen.md) +- [modbus_tcp_pi_listen](modbus_tcp_pi_listen.md) diff --git a/docs/modbus_tcp_pi_listen.md b/docs/modbus_tcp_pi_listen.md index d83280805..796631f1c 100644 --- a/docs/modbus_tcp_pi_listen.md +++ b/docs/modbus_tcp_pi_listen.md @@ -14,7 +14,7 @@ int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection); The *modbus_tcp_pi_listen()* function shall create a socket and listen to maximum `nb_connection` incoming connections on the specified nodes. The -context *ctx* must be allocated and initialized with [modbus_new_tcp_pi](modbus_new_tcp_pi) +context *ctx* must be allocated and initialized with [modbus_new_tcp_pi](modbus_new_tcp_pi.md) before to set the node to listen, if node is set to NULL or '0.0.0.0', any addresses will be listen. @@ -50,6 +50,6 @@ modbus_free(ctx); ## See also -- [modbus_new_tcp_pi](modbus_new_tcp_pi) -- [modbus_tcp_pi_accept](modbus_tcp_pi_accept) -- [modbus_tcp_listen](modbus_tcp_listen) +- [modbus_new_tcp_pi](modbus_new_tcp_pi.md) +- [modbus_tcp_pi_accept](modbus_tcp_pi_accept.md) +- [modbus_tcp_listen](modbus_tcp_listen.md) diff --git a/docs/modbus_write_and_read_registers.md b/docs/modbus_write_and_read_registers.md index 143cd54b0..105f8148d 100644 --- a/docs/modbus_write_and_read_registers.md +++ b/docs/modbus_write_and_read_registers.md @@ -38,6 +38,6 @@ it shall return -1 and set errno. ## See also -- [modbus_read_registers](modbus_read_registers) -- [modbus_write_register](modbus_write_register) -- [modbus_write_registers](modbus_write_registers) +- [modbus_read_registers](modbus_read_registers.md) +- [modbus_write_register](modbus_write_register.md) +- [modbus_write_registers](modbus_write_registers.md) diff --git a/docs/modbus_write_bit.md b/docs/modbus_write_bit.md index 232534a98..ee3d2b2a9 100644 --- a/docs/modbus_write_bit.md +++ b/docs/modbus_write_bit.md @@ -24,5 +24,5 @@ errno. ## See also -- [modbus_read_bits](modbus_read_bits) -- [modbus_write_bits](modbus_write_bits) +- [modbus_read_bits](modbus_read_bits.md) +- [modbus_write_bits](modbus_write_bits.md) diff --git a/docs/modbus_write_bits.md b/docs/modbus_write_bits.md index 34beb98ae..742c91293 100644 --- a/docs/modbus_write_bits.md +++ b/docs/modbus_write_bits.md @@ -29,5 +29,5 @@ shall return -1 and set errno. ## See also -- [modbus_read_bits](modbus_read_bits) -- [modbus_write_bit](modbus_write_bit) +- [modbus_read_bits](modbus_read_bits.md) +- [modbus_write_bit](modbus_write_bit.md) diff --git a/docs/modbus_write_register.md b/docs/modbus_write_register.md index bf66c8e08..244c978ef 100644 --- a/docs/modbus_write_register.md +++ b/docs/modbus_write_register.md @@ -24,5 +24,5 @@ errno. ## See also -- [modbus_read_registers](modbus_read_registers) -- [modbus_write_registers](modbus_write_registers) +- [modbus_read_registers](modbus_read_registers.md) +- [modbus_write_registers](modbus_write_registers.md) diff --git a/docs/modbus_write_registers.md b/docs/modbus_write_registers.md index a89f22c3f..0b1d6a8b3 100644 --- a/docs/modbus_write_registers.md +++ b/docs/modbus_write_registers.md @@ -24,5 +24,5 @@ successful. Otherwise it shall return -1 and set errno. ## See also -- [modbus_write_register](modbus_write_register) -- [modbus_read_registers](modbus_read_registers) +- [modbus_write_register](modbus_write_register.md) +- [modbus_read_registers](modbus_read_registers.md) From 014a9fcd2ebb730004a2bf63e1709369bc587ce8 Mon Sep 17 00:00:00 2001 From: Alexander Polleti Date: Sun, 15 Dec 2019 14:48:19 +0100 Subject: [PATCH 061/210] test the protocol id for 0 previous test would fail if one byte was non zero. --- src/modbus-tcp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/modbus-tcp.c b/src/modbus-tcp.c index 4b454c4f0..7210fb7ac 100644 --- a/src/modbus-tcp.c +++ b/src/modbus-tcp.c @@ -188,6 +188,7 @@ static int _modbus_tcp_check_integrity(modbus_t *ctx, uint8_t *msg, const int ms static int _modbus_tcp_pre_check_confirmation(modbus_t *ctx, const uint8_t *req, const uint8_t *rsp, int rsp_length) { + unsigned int protocol_id; /* Check transaction ID */ if (req[0] != rsp[0] || req[1] != rsp[1]) { if (ctx->debug) { @@ -199,10 +200,11 @@ static int _modbus_tcp_pre_check_confirmation(modbus_t *ctx, const uint8_t *req, } /* Check protocol ID */ - if (rsp[2] != 0x0 && rsp[3] != 0x0) { + protocol_id = (rsp[2] << 8) + rsp[3]; + if (protocol_id != 0x0) { if (ctx->debug) { fprintf(stderr, "Invalid protocol ID received 0x%X (not 0x0)\n", - (rsp[2] << 8) + rsp[3]); + protocol_id); } errno = EMBBADDATA; return -1; From b0c05667dfc60cc82a14109561853deb92404b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Wed, 4 Aug 2021 13:29:46 +0200 Subject: [PATCH 062/210] Fix double negative in tests --- tests/unit-test-client.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit-test-client.c b/tests/unit-test-client.c index fd4062297..fdc42ae0f 100644 --- a/tests/unit-test-client.c +++ b/tests/unit-test-client.c @@ -496,7 +496,7 @@ int main(int argc, char *argv[]) ASSERT_TRUE(rc == -1 && errno == ETIMEDOUT, ""); rc = modbus_set_slave(ctx, MODBUS_BROADCAST_ADDRESS); - ASSERT_TRUE(rc != -1, "Invalid broadcast address"); + ASSERT_TRUE(rc == 0, "Invalid broadcast address"); rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, UT_REGISTERS_NB, tab_rp_registers); @@ -508,7 +508,7 @@ int main(int argc, char *argv[]) ASSERT_TRUE(rc == UT_REGISTERS_NB, ""); rc = modbus_set_slave(ctx, MODBUS_BROADCAST_ADDRESS); - ASSERT_TRUE(rc != -1, "Invalid broacast address"); + ASSERT_TRUE(rc == 0, "Invalid broacast address"); rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, UT_REGISTERS_NB, tab_rp_registers); From f0db03dd4816b26cd55432aac820c61eeeccd96b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Tue, 4 Oct 2022 01:20:54 +0200 Subject: [PATCH 063/210] New quirks handler (closes #38 #533) Useful functions when you are confronted with equipment which does not respect the protocol, which behaves strangely or when you wish to move away from the standard. Thank you @mhei for the great initial version. --- docs/index.md | 5 +++++ docs/modbus_disable_quirks.md | 37 +++++++++++++++++++++++++++++++++++ docs/modbus_enable_quirks.md | 35 +++++++++++++++++++++++++++++++++ src/modbus-private.h | 1 + src/modbus-rtu.c | 4 +++- src/modbus-tcp.c | 4 +++- src/modbus.c | 33 ++++++++++++++++++++++++++++--- src/modbus.h | 10 ++++++++++ tests/unit-test-client.c | 19 +++++++++++++++++- 9 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 docs/modbus_disable_quirks.md create mode 100644 docs/modbus_enable_quirks.md diff --git a/docs/index.md b/docs/index.md index 230e0ffc7..cefa9ce51 100644 --- a/docs/index.md +++ b/docs/index.md @@ -251,6 +251,11 @@ error codes into error message strings; for details refer to ## Miscellaneous +To deviate from the Modbus standard, you can enable or disable quirks with: + +- [modbus_disable_quirks](modbus_disable_quirks.md) +- [modbus_enable_quirks](modbus_enable_quirks.md) + The `_LIBMODBUS_VERSION_STRING_` constant indicates the libmodbus version the program has been compiled against. The variables 'libmodbus_version_major', 'libmodbus_version_minor', 'libmodbus_version_micro' give the version the diff --git a/docs/modbus_disable_quirks.md b/docs/modbus_disable_quirks.md new file mode 100644 index 000000000..8942e66a4 --- /dev/null +++ b/docs/modbus_disable_quirks.md @@ -0,0 +1,37 @@ +# modbus_disable_quirks + +## Name + +modbus_disable_quirks - disable a list of quirks according to a mask + +## Synopsis + +```c +int modbus_disable_quirks(modbus_t *ctx, unsigned int quirks_mask); +``` + +## Description + +The function shall disable the quirks according to the provided mask. It's +useful to revert changes applied by a previous call to +[modbus_enable_quirks](modbus_enable_quirks.md) + +To reset all quirks, you can use the specific value `MODBUS_QUIRK_ALL`. + +```c +modbus_enable_quirks(ctx, MODBUS_QUIRK_MAX_SLAVE | MODBUS_QUIRK_REPLY_TO_BROADCAST); + +... + +// Reset all quirks +modbus_disable_quirks(ctx, MODBUS_QUIRK_ALL); +``` + +## Return value + +The function shall return 0 if successful. Otherwise it shall return -1 and set +errno. + +## See also + +- [modbus_enable_quirks](modbus_enable_quirks.md) diff --git a/docs/modbus_enable_quirks.md b/docs/modbus_enable_quirks.md new file mode 100644 index 000000000..c5b26196d --- /dev/null +++ b/docs/modbus_enable_quirks.md @@ -0,0 +1,35 @@ +# modbus_enable_quirks + +## Name + +modbus_enable_quirks - enable a list of quirks according to a mask + +## Synopsis + +```c +int modbus_enable_quirks(modbus_t *ctx, unsigned int quirks_mask); +``` + +## Description + +The function is only useful when you are confronted with equipment which does +not respect the protocol, which behaves strangely or when you wish to move away +from the standard. + +In that case, you can enable a specific quirk to workaround the issue, libmodbus +offers the following flags: + +- `MODBUS_QUIRK_MAX_SLAVE` allows slave adresses between 247 and 255. +- `MODBUS_QUIRK_REPLY_TO_BROADCAST` force a reply to a broacast request when the + device is a slave in RTU mode (should be enabled on the slave device). + +You can combine the flags by using the OR logical operator. + +## Return value + +The function shall return 0 if successful. Otherwise it shall return -1 and set +errno. + +## See also + +- [modbus_disable_quirks](modbus_disable_quirks.md) diff --git a/src/modbus-private.h b/src/modbus-private.h index df2ca6d92..b79658e9c 100644 --- a/src/modbus-private.h +++ b/src/modbus-private.h @@ -96,6 +96,7 @@ struct _modbus { int s; int debug; int error_recovery; + int quirks; struct timeval response_timeout; struct timeval byte_timeout; struct timeval indication_timeout; diff --git a/src/modbus-rtu.c b/src/modbus-rtu.c index 38f6deb1b..6c37b48b4 100644 --- a/src/modbus-rtu.c +++ b/src/modbus-rtu.c @@ -91,8 +91,10 @@ static const uint8_t table_crc_lo[] = { * internal slave ID in slave mode */ static int _modbus_set_slave(modbus_t *ctx, int slave) { + int max_slave = (ctx->quirks & MODBUS_QUIRK_MAX_SLAVE) ? 255 : 247; + /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */ - if (slave >= 0 && slave <= 247) { + if (slave >= 0 && slave <= max_slave) { ctx->slave = slave; } else { errno = EINVAL; diff --git a/src/modbus-tcp.c b/src/modbus-tcp.c index 7210fb7ac..171cea604 100644 --- a/src/modbus-tcp.c +++ b/src/modbus-tcp.c @@ -76,8 +76,10 @@ static int _modbus_tcp_init_win32(void) static int _modbus_set_slave(modbus_t *ctx, int slave) { + int max_slave = (ctx->quirks & MODBUS_QUIRK_MAX_SLAVE) ? 255 : 247; + /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */ - if (slave >= 0 && slave <= 247) { + if (slave >= 0 && slave <= max_slave) { ctx->slave = slave; } else if (slave == MODBUS_TCP_SLAVE) { /* The special value MODBUS_TCP_SLAVE (0xFF) can be used in TCP mode to diff --git a/src/modbus.c b/src/modbus.c index 0dbf82b7e..e82411c7d 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -1063,9 +1063,13 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, break; } - /* Suppress any responses when the request was a broadcast */ - return (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU && - slave == MODBUS_BROADCAST_ADDRESS) ? 0 : send_msg(ctx, rsp, rsp_length); + /* Suppress any responses in RTU when the request was a broadcast, excepted when quirk is enabled. */ + if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU && + slave == MODBUS_BROADCAST_ADDRESS && + !(ctx->quirks & MODBUS_QUIRK_REPLY_TO_BROADCAST)) { + return 0; + } + return send_msg(ctx, rsp, rsp_length); } int modbus_reply_exception(modbus_t *ctx, const uint8_t *req, @@ -1635,6 +1639,7 @@ void _modbus_init_common(modbus_t *ctx) ctx->debug = FALSE; ctx->error_recovery = MODBUS_ERROR_RECOVERY_NONE; + ctx->quirks = MODBUS_QUIRK_NONE; ctx->response_timeout.tv_sec = 0; ctx->response_timeout.tv_usec = _RESPONSE_TIMEOUT; @@ -1789,6 +1794,28 @@ int modbus_get_header_length(modbus_t *ctx) return ctx->backend->header_length; } +int modbus_enable_quirks(modbus_t *ctx, uint32_t quirks_mask) { + if (ctx == NULL) { + errno = EINVAL; + return -1; + } + + /* Enable quirks that have a true value at their index in the mask */ + ctx->quirks |= quirks_mask; + return 0; +} + +int modbus_disable_quirks(modbus_t *ctx, uint32_t quirks_mask) { + if (ctx == NULL) { + errno = EINVAL; + return -1; + } + + /* Disable quirks that have a true value at ther index in the mask */ + ctx->quirks &= ~quirks_mask; + return 0; +} + int modbus_connect(modbus_t *ctx) { if (ctx == NULL) { diff --git a/src/modbus.h b/src/modbus.h index 70a6c22a8..ee4ad768b 100644 --- a/src/modbus.h +++ b/src/modbus.h @@ -176,6 +176,14 @@ typedef enum MODBUS_ERROR_RECOVERY_PROTOCOL = (1<<2) } modbus_error_recovery_mode; +typedef enum +{ + MODBUS_QUIRK_NONE = 0, + MODBUS_QUIRK_MAX_SLAVE = (1<<1), + MODBUS_QUIRK_REPLY_TO_BROADCAST = (1<<2), + MODBUS_QUIRK_ALL = 0xFF +} modbus_quirks; + MODBUS_API int modbus_set_slave(modbus_t* ctx, int slave); MODBUS_API int modbus_get_slave(modbus_t* ctx); MODBUS_API int modbus_set_error_recovery(modbus_t *ctx, modbus_error_recovery_mode error_recovery); @@ -237,6 +245,8 @@ MODBUS_API int modbus_reply(modbus_t *ctx, const uint8_t *req, int req_length, modbus_mapping_t *mb_mapping); MODBUS_API int modbus_reply_exception(modbus_t *ctx, const uint8_t *req, unsigned int exception_code); +MODBUS_API int modbus_enable_quirks(modbus_t *ctx, unsigned int quirks_mask); +MODBUS_API int modbus_disable_quirks(modbus_t *ctx, unsigned int quirks_mask); /** * UTILS FUNCTIONS diff --git a/tests/unit-test-client.c b/tests/unit-test-client.c index fdc42ae0f..f8c4ab8ce 100644 --- a/tests/unit-test-client.c +++ b/tests/unit-test-client.c @@ -451,9 +451,26 @@ int main(int argc, char *argv[]) printf("* modbus_write_registers: "); ASSERT_TRUE(rc == -1 && errno == EMBMDATA, ""); - /** SLAVE REPLY **/ + /** SLAVE ADDRESS **/ old_slave = modbus_get_slave(ctx); + printf("\nTEST SLAVE ADDRESS:\n"); + + printf("1/2 Not compliant slave address is refused: "); + rc = modbus_set_slave(ctx, 248); + ASSERT_TRUE(rc == -1, "Slave address of 248 shouldn't be allowed"); + + printf("2/2 Not compliant slave address is allowed: "); + modbus_enable_quirks(ctx, MODBUS_QUIRK_MAX_SLAVE); + rc = modbus_set_slave(ctx, 248); + ASSERT_TRUE(rc == 0, "Not compliant slave address should have been accepted"); + + modbus_disable_quirks(ctx, MODBUS_QUIRK_MAX_SLAVE); + rc = modbus_set_slave(ctx, old_slave); + ASSERT_TRUE(rc == 0, "Uanble to restore slave value") + + /** SLAVE REPLY **/ + printf("\nTEST SLAVE REPLY:\n"); modbus_set_slave(ctx, INVALID_SERVER_ID); rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, From 59d34d2085d3d15bf0df2db16490770da8dfa66a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Tue, 4 Oct 2022 22:23:24 +0200 Subject: [PATCH 064/210] Fix bitwise OR in documentation --- docs/modbus_enable_quirks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modbus_enable_quirks.md b/docs/modbus_enable_quirks.md index c5b26196d..f209ad1dd 100644 --- a/docs/modbus_enable_quirks.md +++ b/docs/modbus_enable_quirks.md @@ -23,7 +23,7 @@ offers the following flags: - `MODBUS_QUIRK_REPLY_TO_BROADCAST` force a reply to a broacast request when the device is a slave in RTU mode (should be enabled on the slave device). -You can combine the flags by using the OR logical operator. +You can combine the flags by using the bitwise OR operator. ## Return value From d5512d9b684f1580e21b660b188e5e58fa1b7b84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Sat, 15 Oct 2022 21:00:01 +0200 Subject: [PATCH 065/210] Improve doc about slave ID in RTU --- docs/modbus_new_rtu.md | 36 +++++++++++++++++++++---- docs/modbus_read_bits.md | 2 +- docs/modbus_read_input_bits.md | 2 +- docs/modbus_read_input_registers.md | 2 +- docs/modbus_read_registers.md | 2 +- docs/modbus_set_slave.md | 27 +++++++++++-------- docs/modbus_write_and_read_registers.md | 2 +- 7 files changed, 52 insertions(+), 21 deletions(-) diff --git a/docs/modbus_new_rtu.md b/docs/modbus_new_rtu.md index d9187dbcb..15dac8e3b 100644 --- a/docs/modbus_new_rtu.md +++ b/docs/modbus_new_rtu.md @@ -35,9 +35,24 @@ values are 5, 6, 7 and 8. The `stop_bits` argument specifies the bits of stop, the allowed values are 1 and 2. -Once the `modbus_t` structure is initialized, you must set the slave of your -device with [modbus_set_slave](modbus_set_slave.md) and connect to the serial bus with -[modbus_connect](modbus_connect.md). +Once the `modbus_t` structure is initialized, you can connect to the serial bus +with [modbus_connect](modbus_connect.md). + +In RTU, your program can act as server or client: + +- **server** is called *slave* in Modbus terminology, your program will expose + data to the network by processing and answering the requests of one of several + clients. It up to you to define the slave ID of your service with + [modbus_set_slave](modbus_set_slave.md), this ID should be used by the client + to communicate with your program. + +- **client** is called *master* in Modbus terminology, your program will send + requests to servers to read or write data from them. Before issuing the + requests, you should define the slave ID of the remote device with + [modbus_set_slave](modbus_set_slave.md). The slave ID is not an argument of + the read/write functions because it's very frequent to talk with only one + server so you can set it once and for all. The slave ID it not used in TCP + communications so this way the API is common to both. ## Return value @@ -53,8 +68,16 @@ defined below. ## Example +In this example, the program will open a serial communication on USB. All +subsequent calls such as read or write of registers will be sent on the wire and +the request will be visible to all connected devices. According to the Modbus +protocol, only the master associated to slave ID 10 will process and answer your +requests. + ```c +const int REMOTE_ID = 10; modbus_t *ctx; +uint16_t tab_reg[10]; ctx = modbus_new_rtu("/dev/ttyUSB0", 115200, 'N', 8, 1); if (ctx == NULL) { @@ -62,13 +85,16 @@ if (ctx == NULL) { return -1; } -modbus_set_slave(ctx, YOUR_DEVICE_ID); - if (modbus_connect(ctx) == -1) { fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); modbus_free(ctx); return -1; } + +modbus_set_slave(ctx, REMOTE_ID); + +// Read 2 registers from address 0 of server ID 10. +modbus_read_registers(ctx, 0, 2, tab_reg); ``` ## See also diff --git a/docs/modbus_read_bits.md b/docs/modbus_read_bits.md index bb3e78a7b..67965142b 100644 --- a/docs/modbus_read_bits.md +++ b/docs/modbus_read_bits.md @@ -17,7 +17,7 @@ to the address `addr` of the remote device. The result of reading is stored in `dest` array as unsigned bytes (8 bits) set to `TRUE` or `FALSE`. You must take care to allocate enough memory to store the results in `dest` -(at least `nb` * sizeof(uint8_t)). +(at least `nb * sizeof(uint8_t)`). The function uses the Modbus function code 0x01 (read coil status). diff --git a/docs/modbus_read_input_bits.md b/docs/modbus_read_input_bits.md index d41af2eb4..421434d80 100644 --- a/docs/modbus_read_input_bits.md +++ b/docs/modbus_read_input_bits.md @@ -17,7 +17,7 @@ bits to the address `addr` of the remote device. The result of reading is store in `dest` array as unsigned bytes (8 bits) set to `TRUE` or `FALSE`. You must take care to allocate enough memory to store the results in `dest` -(at least `nb` * sizeof(uint8_t)). +(at least `nb * sizeof(uint8_t)`). The function uses the Modbus function code 0x02 (read input status). diff --git a/docs/modbus_read_input_registers.md b/docs/modbus_read_input_registers.md index b012622ce..937c30576 100644 --- a/docs/modbus_read_input_registers.md +++ b/docs/modbus_read_input_registers.md @@ -17,7 +17,7 @@ input registers to address `addr` of the remote device. The result of the reading is stored in `dest` array as word values (16 bits). You must take care to allocate enough memory to store the results in `dest` (at -least `nb` * sizeof(uint16_t)). +least `nb * sizeof(uint16_t)`). The function uses the Modbus function code 0x04 (read input registers). The holding registers and input registers have different historical meaning, but diff --git a/docs/modbus_read_registers.md b/docs/modbus_read_registers.md index cde30229c..8f853cc7c 100644 --- a/docs/modbus_read_registers.md +++ b/docs/modbus_read_registers.md @@ -17,7 +17,7 @@ holding registers to the address `addr` of the remote device. The result of reading is stored in `dest` array as word values (16 bits). You must take care to allocate enough memory to store the results in `dest` -(at least `nb` * sizeof(uint16_t)). +(at least `nb * sizeof(uint16_t)`). The function uses the Modbus function code 0x03 (read holding registers). diff --git a/docs/modbus_set_slave.md b/docs/modbus_set_slave.md index 7d6042c6c..efdeea386 100644 --- a/docs/modbus_set_slave.md +++ b/docs/modbus_set_slave.md @@ -15,17 +15,22 @@ int modbus_set_slave(modbus_t *ctx, int slave); The *modbus_set_slave()* function shall set the slave number in the libmodbus context. -The behavior depends of network and the role of the device: - -*RTU*:: -Define the slave ID of the remote device to talk in master mode or set the -internal slave ID in slave mode. According to the protocol, a Modbus device must -only accept message holding its slave number or the special broadcast number. - -*TCP*:: -The slave number is only required in TCP if the message must reach a device on a -serial network. Some not compliant devices or software (such as modpoll) uses -the slave ID as unit identifier, that's incorrect (cf page 23 of Modbus +It is usually only required to set the slave ID in **RTU**. The meaning of this +ID will be different if your program acts as client (master) or server (slave). + +As **RTU client**, *modbus_set_slave()* sets the ID of the remote device you +want to communicate. Be sure to set the slave ID before issuing any Modbus +requests on the serial bus. If you communicate with several servers (slaves), +you can set the slave ID of the remote device before each request. + +As **RTU server**, the slave ID allows the various clients to reach your +service. You should use a free ID, once set, this ID should be known by the +clients of the network. According to the protocol, a Modbus device must only +accept message holding its slave number or the special broadcast number. + +In **TCP**, the slave number is only required if the message must reach a device +on a serial network. Some not compliant devices or software (such as modpoll) +uses the slave ID as unit identifier, that's incorrect (cf page 23 of Modbus Messaging Implementation Guide v1.0b) but without the slave value, the faulty remote device or software drops the requests! The special value `MODBUS_TCP_SLAVE` (0xFF) can be used in TCP mode to restore the default value. diff --git a/docs/modbus_write_and_read_registers.md b/docs/modbus_write_and_read_registers.md index 105f8148d..646cb939c 100644 --- a/docs/modbus_write_and_read_registers.md +++ b/docs/modbus_write_and_read_registers.md @@ -23,7 +23,7 @@ to the address `read_addr` of the remote device. The result of reading is stored in `dest` array as word values (16 bits). You must take care to allocate enough memory to store the results in `dest` -(at least `nb` * sizeof(uint16_t)). +(at least `nb * sizeof(uint16_t)`). The function uses the Modbus function code 0x17 (write/read registers). From dd45f19a6efc20d6032b424a067294086a315899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Tue, 18 Oct 2022 10:58:35 +0200 Subject: [PATCH 066/210] Add .clang-format and format code (closes #394) Thank you @pboettch for initial proposal. --- .clang-format | 56 ++++ src/modbus-data.c | 53 ++-- src/modbus-private.h | 49 +-- src/modbus-rtu-private.h | 11 +- src/modbus-rtu.c | 245 ++++++++------- src/modbus-rtu.h | 15 +- src/modbus-tcp-private.h | 6 +- src/modbus-tcp.c | 115 +++---- src/modbus-tcp.h | 18 +- src/modbus-version.h.in | 18 +- src/modbus.c | 501 +++++++++++++++++-------------- src/modbus.h | 220 ++++++++------ src/win32/config.h.win32 | 2 +- tests/bandwidth-client.c | 23 +- tests/bandwidth-server-many-up.c | 29 +- tests/bandwidth-server-one.c | 20 +- tests/random-test-client.c | 79 +++-- tests/random-test-server.c | 5 +- tests/unit-test-client.c | 493 ++++++++++++++++-------------- tests/unit-test-server.c | 79 ++--- tests/unit-test.h.in | 2 + tests/version.c | 10 +- 22 files changed, 1130 insertions(+), 919 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..9521e9f09 --- /dev/null +++ b/.clang-format @@ -0,0 +1,56 @@ +--- +BasedOnStyle: LLVM +AlignArrayOfStructures: Left +AlignOperands: true +AlignConsecutiveAssignments: false +AlignConsecutiveMacros: true +AlignEscapedNewlines: Left +AlignTrailingComments: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BinPackArguments: false +BinPackParameters: false +ColumnLimit: 90 +ConstructorInitializerAllOnOneLineOrOnePerLine: true +IncludeBlocks: Preserve +IndentWidth: 4 +ObjCBlockIndentWidth: 4 +PointerAlignment: Right +ReferenceAlignment: Right +SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatementsExceptForEachMacros +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SeparateDefinitionBlocks: Always +UseTab: Never +PPIndentWidth: 2 diff --git a/src/modbus-data.c b/src/modbus-data.c index f9c6615f9..8aeef0cc7 100644 --- a/src/modbus-data.c +++ b/src/modbus-data.c @@ -6,6 +6,7 @@ #include +// clang-format off #ifndef _MSC_VER # include #else @@ -69,6 +70,7 @@ static inline uint32_t bswap_32(uint32_t x) return (bswap_16(x & 0xffff) << 16) | (bswap_16(x >> 16)); } #endif +// clang-format on /* Sets many bits from a single byte value (all 8 bits of the byte value are set) */ @@ -76,14 +78,16 @@ void modbus_set_bits_from_byte(uint8_t *dest, int idx, const uint8_t value) { int i; - for (i=0; i < 8; i++) { - dest[idx+i] = (value & (1 << i)) ? 1 : 0; + for (i = 0; i < 8; i++) { + dest[idx + i] = (value & (1 << i)) ? 1 : 0; } } /* Sets many bits from a table of bytes (only the bits between idx and idx + nb_bits are set) */ -void modbus_set_bits_from_bytes(uint8_t *dest, int idx, unsigned int nb_bits, +void modbus_set_bits_from_bytes(uint8_t *dest, + int idx, + unsigned int nb_bits, const uint8_t *tab_byte) { unsigned int i; @@ -99,8 +103,7 @@ void modbus_set_bits_from_bytes(uint8_t *dest, int idx, unsigned int nb_bits, /* Gets the byte value from many bits. To obtain a full byte, set nb_bits to 8. */ -uint8_t modbus_get_byte_from_bits(const uint8_t *src, int idx, - unsigned int nb_bits) +uint8_t modbus_get_byte_from_bits(const uint8_t *src, int idx, unsigned int nb_bits) { unsigned int i; uint8_t value = 0; @@ -111,8 +114,8 @@ uint8_t modbus_get_byte_from_bits(const uint8_t *src, int idx, nb_bits = 8; } - for (i=0; i < nb_bits; i++) { - value |= (src[idx+i] << i); + for (i = 0; i < nb_bits; i++) { + value |= (src[idx + i] << i); } return value; @@ -130,10 +133,7 @@ float modbus_get_float_abcd(const uint16_t *src) c = (src[1] >> 8) & 0xFF; d = (src[1] >> 0) & 0xFF; - i = (a << 24) | - (b << 16) | - (c << 8) | - (d << 0); + i = (a << 24) | (b << 16) | (c << 8) | (d << 0); memcpy(&f, &i, 4); return f; @@ -151,10 +151,7 @@ float modbus_get_float_dcba(const uint16_t *src) c = (src[1] >> 8) & 0xFF; d = (src[1] >> 0) & 0xFF; - i = (d << 24) | - (c << 16) | - (b << 8) | - (a << 0); + i = (d << 24) | (c << 16) | (b << 8) | (a << 0); memcpy(&f, &i, 4); return f; @@ -172,10 +169,7 @@ float modbus_get_float_badc(const uint16_t *src) c = (src[1] >> 8) & 0xFF; d = (src[1] >> 0) & 0xFF; - i = (b << 24) | - (a << 16) | - (d << 8) | - (c << 0); + i = (b << 24) | (a << 16) | (d << 8) | (c << 0); memcpy(&f, &i, 4); return f; @@ -193,10 +187,7 @@ float modbus_get_float_cdab(const uint16_t *src) c = (src[1] >> 8) & 0xFF; d = (src[1] >> 0) & 0xFF; - i = (c << 24) | - (d << 16) | - (a << 8) | - (b << 0); + i = (c << 24) | (d << 16) | (a << 8) | (b << 0); memcpy(&f, &i, 4); return f; @@ -208,18 +199,17 @@ float modbus_get_float(const uint16_t *src) float f; uint32_t i; - i = (((uint32_t)src[1]) << 16) + src[0]; + i = (((uint32_t) src[1]) << 16) + src[0]; memcpy(&f, &i, sizeof(float)); return f; - } /* Set a float to 4 bytes for Modbus w/o any conversion (ABCD) */ void modbus_set_float_abcd(float f, uint16_t *dest) { uint32_t i; - uint8_t *out = (uint8_t*) dest; + uint8_t *out = (uint8_t *) dest; uint8_t a, b, c, d; memcpy(&i, &f, sizeof(uint32_t)); @@ -238,7 +228,7 @@ void modbus_set_float_abcd(float f, uint16_t *dest) void modbus_set_float_dcba(float f, uint16_t *dest) { uint32_t i; - uint8_t *out = (uint8_t*) dest; + uint8_t *out = (uint8_t *) dest; uint8_t a, b, c, d; memcpy(&i, &f, sizeof(uint32_t)); @@ -251,14 +241,13 @@ void modbus_set_float_dcba(float f, uint16_t *dest) out[1] = c; out[2] = b; out[3] = a; - } /* Set a float to 4 bytes for Modbus with byte swap conversion (BADC) */ void modbus_set_float_badc(float f, uint16_t *dest) { uint32_t i; - uint8_t *out = (uint8_t*) dest; + uint8_t *out = (uint8_t *) dest; uint8_t a, b, c, d; memcpy(&i, &f, sizeof(uint32_t)); @@ -277,7 +266,7 @@ void modbus_set_float_badc(float f, uint16_t *dest) void modbus_set_float_cdab(float f, uint16_t *dest) { uint32_t i; - uint8_t *out = (uint8_t*) dest; + uint8_t *out = (uint8_t *) dest; uint8_t a, b, c, d; memcpy(&i, &f, sizeof(uint32_t)); @@ -298,6 +287,6 @@ void modbus_set_float(float f, uint16_t *dest) uint32_t i; memcpy(&i, &f, sizeof(uint32_t)); - dest[0] = (uint16_t)i; - dest[1] = (uint16_t)(i >> 16); + dest[0] = (uint16_t) i; + dest[1] = (uint16_t) (i >> 16); } diff --git a/src/modbus-private.h b/src/modbus-private.h index b79658e9c..b0bb1ab04 100644 --- a/src/modbus-private.h +++ b/src/modbus-private.h @@ -7,6 +7,7 @@ #ifndef MODBUS_PRIVATE_H #define MODBUS_PRIVATE_H +// clang-format off #ifndef _MSC_VER # include # include @@ -15,8 +16,9 @@ # include typedef int ssize_t; #endif -#include +// clang-format on #include +#include #include "modbus.h" @@ -36,11 +38,11 @@ MODBUS_BEGIN_DECLS #define _MODBUS_EXCEPTION_RSP_LENGTH 5 /* Timeouts in microsecond (0.5 s) */ -#define _RESPONSE_TIMEOUT 500000 -#define _BYTE_TIMEOUT 500000 +#define _RESPONSE_TIMEOUT 500000 +#define _BYTE_TIMEOUT 500000 typedef enum { - _MODBUS_BACKEND_TYPE_RTU=0, + _MODBUS_BACKEND_TYPE_RTU = 0, _MODBUS_BACKEND_TYPE_TCP } modbus_backend_type_t; @@ -69,24 +71,25 @@ typedef struct _modbus_backend { unsigned int header_length; unsigned int checksum_length; unsigned int max_adu_length; - int (*set_slave) (modbus_t *ctx, int slave); - int (*build_request_basis) (modbus_t *ctx, int function, int addr, - int nb, uint8_t *req); - int (*build_response_basis) (sft_t *sft, uint8_t *rsp); - int (*prepare_response_tid) (const uint8_t *req, int *req_length); - int (*send_msg_pre) (uint8_t *req, int req_length); - ssize_t (*send) (modbus_t *ctx, const uint8_t *req, int req_length); - int (*receive) (modbus_t *ctx, uint8_t *req); - ssize_t (*recv) (modbus_t *ctx, uint8_t *rsp, int rsp_length); - int (*check_integrity) (modbus_t *ctx, uint8_t *msg, - const int msg_length); - int (*pre_check_confirmation) (modbus_t *ctx, const uint8_t *req, - const uint8_t *rsp, int rsp_length); - int (*connect) (modbus_t *ctx); - void (*close) (modbus_t *ctx); - int (*flush) (modbus_t *ctx); - int (*select) (modbus_t *ctx, fd_set *rset, struct timeval *tv, int msg_length); - void (*free) (modbus_t *ctx); + int (*set_slave)(modbus_t *ctx, int slave); + int (*build_request_basis)( + modbus_t *ctx, int function, int addr, int nb, uint8_t *req); + int (*build_response_basis)(sft_t *sft, uint8_t *rsp); + int (*prepare_response_tid)(const uint8_t *req, int *req_length); + int (*send_msg_pre)(uint8_t *req, int req_length); + ssize_t (*send)(modbus_t *ctx, const uint8_t *req, int req_length); + int (*receive)(modbus_t *ctx, uint8_t *req); + ssize_t (*recv)(modbus_t *ctx, uint8_t *rsp, int rsp_length); + int (*check_integrity)(modbus_t *ctx, uint8_t *msg, const int msg_length); + int (*pre_check_confirmation)(modbus_t *ctx, + const uint8_t *req, + const uint8_t *rsp, + int rsp_length); + int (*connect)(modbus_t *ctx); + void (*close)(modbus_t *ctx); + int (*flush)(modbus_t *ctx); + int (*select)(modbus_t *ctx, fd_set *rset, struct timeval *tv, int msg_length); + void (*free)(modbus_t *ctx); } modbus_backend_t; struct _modbus { @@ -114,4 +117,4 @@ size_t strlcpy(char *dest, const char *src, size_t dest_size); MODBUS_END_DECLS -#endif /* MODBUS_PRIVATE_H */ +#endif /* MODBUS_PRIVATE_H */ diff --git a/src/modbus-rtu-private.h b/src/modbus-rtu-private.h index 8c1d59db0..01e6a9188 100644 --- a/src/modbus-rtu-private.h +++ b/src/modbus-rtu-private.h @@ -19,11 +19,11 @@ #include #endif -#define _MODBUS_RTU_HEADER_LENGTH 1 -#define _MODBUS_RTU_PRESET_REQ_LENGTH 6 -#define _MODBUS_RTU_PRESET_RSP_LENGTH 2 +#define _MODBUS_RTU_HEADER_LENGTH 1 +#define _MODBUS_RTU_PRESET_REQ_LENGTH 6 +#define _MODBUS_RTU_PRESET_RSP_LENGTH 2 -#define _MODBUS_RTU_CHECKSUM_LENGTH 2 +#define _MODBUS_RTU_CHECKSUM_LENGTH 2 #if defined(_WIN32) #if !defined(ENOTSUP) @@ -32,6 +32,7 @@ /* WIN32: struct containing serial handle and a receive buffer */ #define PY_BUF_SIZE 512 + struct win32_ser { /* File handle */ HANDLE fd; @@ -67,7 +68,7 @@ typedef struct _modbus_rtu { int rts; int rts_delay; int onebyte_time; - void (*set_rts) (modbus_t *ctx, int on); + void (*set_rts)(modbus_t *ctx, int on); #endif /* To handle many slaves on the same link */ int confirmation_to_ignore; diff --git a/src/modbus-rtu.c b/src/modbus-rtu.c index 6c37b48b4..29c6c174d 100644 --- a/src/modbus-rtu.c +++ b/src/modbus-rtu.c @@ -4,10 +4,10 @@ * SPDX-License-Identifier: LGPL-2.1-or-later */ -#include -#include #include #include +#include +#include #include #ifndef _MSC_VER #include @@ -16,8 +16,8 @@ #include "modbus-private.h" -#include "modbus-rtu.h" #include "modbus-rtu-private.h" +#include "modbus-rtu.h" #if HAVE_DECL_TIOCSRS485 || HAVE_DECL_TIOCM_RTS #include @@ -29,63 +29,47 @@ /* Table of CRC values for high-order byte */ static const uint8_t table_crc_hi[] = { - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, - 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, - 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, - 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, - 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, - 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, - 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, - 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, - 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, - 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, - 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, - 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, - 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 -}; + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, + 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, + 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40}; /* Table of CRC values for low-order byte */ static const uint8_t table_crc_lo[] = { - 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, - 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, - 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, - 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, - 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, - 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, - 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, - 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, - 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, - 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, - 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, - 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, - 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, - 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, - 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, - 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, - 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, - 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, - 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, - 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, - 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, - 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, - 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, - 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, - 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, - 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 -}; + 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, + 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, + 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, + 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, + 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, + 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, + 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, + 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, + 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, + 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, + 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, + 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, + 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, + 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, + 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, + 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, + 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, + 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, + 0x41, 0x81, 0x80, 0x40}; /* Define the slave ID of the remote device to talk in master mode or set the * internal slave ID in slave mode */ @@ -105,9 +89,8 @@ static int _modbus_set_slave(modbus_t *ctx, int slave) } /* Builds a RTU request header */ -static int _modbus_rtu_build_request_basis(modbus_t *ctx, int function, - int addr, int nb, - uint8_t *req) +static int _modbus_rtu_build_request_basis( + modbus_t *ctx, int function, int addr, int nb, uint8_t *req) { assert(ctx->slave != -1); req[0] = ctx->slave; @@ -135,7 +118,7 @@ static uint16_t crc16(uint8_t *buffer, uint16_t buffer_length) { uint8_t crc_hi = 0xFF; /* high CRC byte initialized */ uint8_t crc_lo = 0xFF; /* low CRC byte initialized */ - unsigned int i; /* will index into CRC lookup */ + unsigned int i; /* will index into CRC lookup */ /* pass through message buffer */ while (buffer_length--) { @@ -186,8 +169,7 @@ static void win32_ser_init(struct win32_ser *ws) } /* FIXME Try to remove length_to_read -> max_len argument, only used by win32 */ -static int win32_ser_select(struct win32_ser *ws, int max_len, - const struct timeval *tv) +static int win32_ser_select(struct win32_ser *ws, int max_len, const struct timeval *tv) { COMMTIMEOUTS comm_to; unsigned int msec = 0; @@ -237,8 +219,7 @@ static int win32_ser_select(struct win32_ser *ws, int max_len, } } -static int win32_ser_read(struct win32_ser *ws, uint8_t *p_msg, - unsigned int max_len) +static int win32_ser_read(struct win32_ser *ws, uint8_t *p_msg, unsigned int max_len) { unsigned int n = ws->n_bytes; @@ -277,7 +258,9 @@ static ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_lengt #if defined(_WIN32) modbus_rtu_t *ctx_rtu = ctx->backend_data; DWORD n_bytes = 0; - return (WriteFile(ctx_rtu->w_ser.fd, req, req_length, &n_bytes, NULL)) ? (ssize_t)n_bytes : -1; + return (WriteFile(ctx_rtu->w_ser.fd, req, req_length, &n_bytes, NULL)) + ? (ssize_t) n_bytes + : -1; #else #if HAVE_DECL_TIOCM_RTS modbus_rtu_t *ctx_rtu = ctx->backend_data; @@ -332,7 +315,7 @@ static int _modbus_rtu_receive(modbus_t *ctx, uint8_t *req) static ssize_t _modbus_rtu_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length) { #if defined(_WIN32) - return win32_ser_read(&((modbus_rtu_t *)ctx->backend_data)->w_ser, rsp, rsp_length); + return win32_ser_read(&((modbus_rtu_t *) ctx->backend_data)->w_ser, rsp, rsp_length); #else return read(ctx->s, rsp, rsp_length); #endif @@ -340,8 +323,10 @@ static ssize_t _modbus_rtu_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length) static int _modbus_rtu_flush(modbus_t *); -static int _modbus_rtu_pre_check_confirmation(modbus_t *ctx, const uint8_t *req, - const uint8_t *rsp, int rsp_length) +static int _modbus_rtu_pre_check_confirmation(modbus_t *ctx, + const uint8_t *req, + const uint8_t *rsp, + int rsp_length) { /* Check responding slave is the slave we requested (except for broacast * request) */ @@ -349,7 +334,8 @@ static int _modbus_rtu_pre_check_confirmation(modbus_t *ctx, const uint8_t *req, if (ctx->debug) { fprintf(stderr, "The responding slave %d isn't the requested slave %d\n", - rsp[0], req[0]); + rsp[0], + req[0]); } errno = EMBBADSLAVE; return -1; @@ -361,8 +347,7 @@ static int _modbus_rtu_pre_check_confirmation(modbus_t *ctx, const uint8_t *req, /* The check_crc16 function shall return 0 if the message is ignored and the message length if the CRC is valid. Otherwise it shall return -1 and set errno to EMBBADCRC. */ -static int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg, - const int msg_length) +static int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg, const int msg_length) { uint16_t crc_calculated; uint16_t crc_received; @@ -386,8 +371,10 @@ static int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg, return msg_length; } else { if (ctx->debug) { - fprintf(stderr, "ERROR CRC received 0x%0X != CRC calculated 0x%0X\n", - crc_received, crc_calculated); + fprintf(stderr, + "ERROR CRC received 0x%0X != CRC calculated 0x%0X\n", + crc_received, + crc_calculated); } if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) { @@ -412,8 +399,11 @@ static int _modbus_rtu_connect(modbus_t *ctx) if (ctx->debug) { printf("Opening %s at %d bauds (%c, %d, %d)\n", - ctx_rtu->device, ctx_rtu->baud, ctx_rtu->parity, - ctx_rtu->data_bit, ctx_rtu->stop_bit); + ctx_rtu->device, + ctx_rtu->baud, + ctx_rtu->parity, + ctx_rtu->data_bit, + ctx_rtu->stop_bit); } #if defined(_WIN32) @@ -424,19 +414,16 @@ static int _modbus_rtu_connect(modbus_t *ctx) /* ctx_rtu->device should contain a string like "COMxx:" xx being a decimal * number */ - ctx_rtu->w_ser.fd = CreateFileA(ctx_rtu->device, - GENERIC_READ | GENERIC_WRITE, - 0, - NULL, - OPEN_EXISTING, - 0, - NULL); + ctx_rtu->w_ser.fd = CreateFileA( + ctx_rtu->device, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); /* Error checking */ if (ctx_rtu->w_ser.fd == INVALID_HANDLE_VALUE) { if (ctx->debug) { - fprintf(stderr, "ERROR Can't open the device %s (LastError %d)\n", - ctx_rtu->device, (int)GetLastError()); + fprintf(stderr, + "ERROR Can't open the device %s (LastError %d)\n", + ctx_rtu->device, + (int) GetLastError()); } return -1; } @@ -445,8 +432,9 @@ static int _modbus_rtu_connect(modbus_t *ctx) ctx_rtu->old_dcb.DCBlength = sizeof(DCB); if (!GetCommState(ctx_rtu->w_ser.fd, &ctx_rtu->old_dcb)) { if (ctx->debug) { - fprintf(stderr, "ERROR Error getting configuration (LastError %d)\n", - (int)GetLastError()); + fprintf(stderr, + "ERROR Error getting configuration (LastError %d)\n", + (int) GetLastError()); } CloseHandle(ctx_rtu->w_ser.fd); ctx_rtu->w_ser.fd = INVALID_HANDLE_VALUE; @@ -519,8 +507,10 @@ static int _modbus_rtu_connect(modbus_t *ctx) default: dcb.BaudRate = CBR_9600; if (ctx->debug) { - fprintf(stderr, "WARNING Unknown baud rate %d for %s (B9600 used)\n", - ctx_rtu->baud, ctx_rtu->device); + fprintf(stderr, + "WARNING Unknown baud rate %d for %s (B9600 used)\n", + ctx_rtu->baud, + ctx_rtu->device); } } @@ -576,8 +566,9 @@ static int _modbus_rtu_connect(modbus_t *ctx) /* Setup port */ if (!SetCommState(ctx_rtu->w_ser.fd, &dcb)) { if (ctx->debug) { - fprintf(stderr, "ERROR Error setting new configuration (LastError %d)\n", - (int)GetLastError()); + fprintf(stderr, + "ERROR Error setting new configuration (LastError %d)\n", + (int) GetLastError()); } CloseHandle(ctx_rtu->w_ser.fd); ctx_rtu->w_ser.fd = INVALID_HANDLE_VALUE; @@ -599,8 +590,10 @@ static int _modbus_rtu_connect(modbus_t *ctx) ctx->s = open(ctx_rtu->device, flags); if (ctx->s < 0) { if (ctx->debug) { - fprintf(stderr, "ERROR Can't open the device %s (%s)\n", - ctx_rtu->device, strerror(errno)); + fprintf(stderr, + "ERROR Can't open the device %s (%s)\n", + ctx_rtu->device, + strerror(errno)); } return -1; } @@ -682,7 +675,7 @@ static int _modbus_rtu_connect(modbus_t *ctx) break; #endif #ifdef B1152000 - case 1152000: + case 1152000: speed = B1152000; break; #endif @@ -716,13 +709,13 @@ static int _modbus_rtu_connect(modbus_t *ctx) if (ctx->debug) { fprintf(stderr, "WARNING Unknown baud rate %d for %s (B9600 used)\n", - ctx_rtu->baud, ctx_rtu->device); + ctx_rtu->baud, + ctx_rtu->device); } } /* Set the baud rate */ - if ((cfsetispeed(&tios, speed) < 0) || - (cfsetospeed(&tios, speed) < 0)) { + if ((cfsetispeed(&tios, speed) < 0) || (cfsetospeed(&tios, speed) < 0)) { close(ctx->s); ctx->s = -1; return -1; @@ -757,7 +750,7 @@ static int _modbus_rtu_connect(modbus_t *ctx) /* Stop bit (1 or 2) */ if (ctx_rtu->stop_bit == 1) - tios.c_cflag &=~ CSTOPB; + tios.c_cflag &= ~CSTOPB; else /* 2 */ tios.c_cflag |= CSTOPB; @@ -765,11 +758,11 @@ static int _modbus_rtu_connect(modbus_t *ctx) PARODD Use odd parity instead of even */ if (ctx_rtu->parity == 'N') { /* None */ - tios.c_cflag &=~ PARENB; + tios.c_cflag &= ~PARENB; } else if (ctx_rtu->parity == 'E') { /* Even */ tios.c_cflag |= PARENB; - tios.c_cflag &=~ PARODD; + tios.c_cflag &= ~PARODD; } else { /* Odd */ tios.c_cflag |= PARENB; @@ -851,7 +844,7 @@ static int _modbus_rtu_connect(modbus_t *ctx) */ /* Raw output */ - tios.c_oflag &=~ OPOST; + tios.c_oflag &= ~OPOST; /* C_CC Control characters VMIN Minimum number of characters to read @@ -1044,7 +1037,7 @@ int modbus_rtu_set_rts(modbus_t *ctx, int mode) return -1; } -int modbus_rtu_set_custom_rts(modbus_t *ctx, void (*set_rts) (modbus_t *ctx, int on)) +int modbus_rtu_set_custom_rts(modbus_t *ctx, void (*set_rts)(modbus_t *ctx, int on)) { if (ctx == NULL) { errno = EINVAL; @@ -1079,7 +1072,7 @@ int modbus_rtu_get_rts_delay(modbus_t *ctx) if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) { #if HAVE_DECL_TIOCM_RTS modbus_rtu_t *ctx_rtu; - ctx_rtu = (modbus_rtu_t *)ctx->backend_data; + ctx_rtu = (modbus_rtu_t *) ctx->backend_data; return ctx_rtu->rts_delay; #else if (ctx->debug) { @@ -1104,7 +1097,7 @@ int modbus_rtu_set_rts_delay(modbus_t *ctx, int us) if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) { #if HAVE_DECL_TIOCM_RTS modbus_rtu_t *ctx_rtu; - ctx_rtu = (modbus_rtu_t *)ctx->backend_data; + ctx_rtu = (modbus_rtu_t *) ctx->backend_data; ctx_rtu->rts_delay = us; return 0; #else @@ -1128,13 +1121,15 @@ static void _modbus_rtu_close(modbus_t *ctx) #if defined(_WIN32) /* Revert settings */ if (!SetCommState(ctx_rtu->w_ser.fd, &ctx_rtu->old_dcb) && ctx->debug) { - fprintf(stderr, "ERROR Couldn't revert to configuration (LastError %d)\n", - (int)GetLastError()); + fprintf(stderr, + "ERROR Couldn't revert to configuration (LastError %d)\n", + (int) GetLastError()); } if (!CloseHandle(ctx_rtu->w_ser.fd) && ctx->debug) { - fprintf(stderr, "ERROR Error while closing handle (LastError %d)\n", - (int)GetLastError()); + fprintf(stderr, + "ERROR Error while closing handle (LastError %d)\n", + (int) GetLastError()); } #else if (ctx->s >= 0) { @@ -1156,13 +1151,13 @@ static int _modbus_rtu_flush(modbus_t *ctx) #endif } -static int _modbus_rtu_select(modbus_t *ctx, fd_set *rset, - struct timeval *tv, int length_to_read) +static int +_modbus_rtu_select(modbus_t *ctx, fd_set *rset, struct timeval *tv, int length_to_read) { int s_rc; #if defined(_WIN32) - s_rc = win32_ser_select(&((modbus_rtu_t *)ctx->backend_data)->w_ser, - length_to_read, tv); + s_rc = win32_ser_select( + &((modbus_rtu_t *) ctx->backend_data)->w_ser, length_to_read, tv); if (s_rc == 0) { errno = ETIMEDOUT; return -1; @@ -1172,7 +1167,7 @@ static int _modbus_rtu_select(modbus_t *ctx, fd_set *rset, return -1; } #else - while ((s_rc = select(ctx->s+1, rset, NULL, NULL, tv)) == -1) { + while ((s_rc = select(ctx->s + 1, rset, NULL, NULL, tv)) == -1) { if (errno == EINTR) { if (ctx->debug) { fprintf(stderr, "A non blocked signal was caught\n"); @@ -1195,15 +1190,17 @@ static int _modbus_rtu_select(modbus_t *ctx, fd_set *rset, return s_rc; } -static void _modbus_rtu_free(modbus_t *ctx) { +static void _modbus_rtu_free(modbus_t *ctx) +{ if (ctx->backend_data) { - free(((modbus_rtu_t *)ctx->backend_data)->device); + free(((modbus_rtu_t *) ctx->backend_data)->device); free(ctx->backend_data); } free(ctx); } +// clang-format off const modbus_backend_t _modbus_rtu_backend = { _MODBUS_BACKEND_TYPE_RTU, _MODBUS_RTU_HEADER_LENGTH, @@ -1226,9 +1223,10 @@ const modbus_backend_t _modbus_rtu_backend = { _modbus_rtu_free }; -modbus_t* modbus_new_rtu(const char *device, - int baud, char parity, int data_bit, - int stop_bit) +// clang-format on + +modbus_t * +modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit) { modbus_t *ctx; modbus_rtu_t *ctx_rtu; @@ -1247,23 +1245,23 @@ modbus_t* modbus_new_rtu(const char *device, return NULL; } - ctx = (modbus_t *)malloc(sizeof(modbus_t)); + ctx = (modbus_t *) malloc(sizeof(modbus_t)); if (ctx == NULL) { return NULL; } _modbus_init_common(ctx); ctx->backend = &_modbus_rtu_backend; - ctx->backend_data = (modbus_rtu_t *)malloc(sizeof(modbus_rtu_t)); + ctx->backend_data = (modbus_rtu_t *) malloc(sizeof(modbus_rtu_t)); if (ctx->backend_data == NULL) { modbus_free(ctx); errno = ENOMEM; return NULL; } - ctx_rtu = (modbus_rtu_t *)ctx->backend_data; + ctx_rtu = (modbus_rtu_t *) ctx->backend_data; /* Device name and \0 */ - ctx_rtu->device = (char *)malloc((strlen(device) + 1) * sizeof(char)); + ctx_rtu->device = (char *) malloc((strlen(device) + 1) * sizeof(char)); if (ctx_rtu->device == NULL) { modbus_free(ctx); errno = ENOMEM; @@ -1292,7 +1290,8 @@ modbus_t* modbus_new_rtu(const char *device, ctx_rtu->rts = MODBUS_RTU_RTS_NONE; /* Calculate estimated time in micro second to send one byte */ - ctx_rtu->onebyte_time = 1000000 * (1 + data_bit + (parity == 'N' ? 0 : 1) + stop_bit) / baud; + ctx_rtu->onebyte_time = + 1000000 * (1 + data_bit + (parity == 'N' ? 0 : 1) + stop_bit) / baud; /* The internal function is used by default to set RTS */ ctx_rtu->set_rts = _modbus_rtu_ioctl_rts; diff --git a/src/modbus-rtu.h b/src/modbus-rtu.h index 9bf4547ca..8e89e7304 100644 --- a/src/modbus-rtu.h +++ b/src/modbus-rtu.h @@ -14,10 +14,10 @@ MODBUS_BEGIN_DECLS /* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5 * RS232 / RS485 ADU = 253 bytes + slave (1 byte) + CRC (2 bytes) = 256 bytes */ -#define MODBUS_RTU_MAX_ADU_LENGTH 256 +#define MODBUS_RTU_MAX_ADU_LENGTH 256 -MODBUS_API modbus_t* modbus_new_rtu(const char *device, int baud, char parity, - int data_bit, int stop_bit); +MODBUS_API modbus_t * +modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit); #define MODBUS_RTU_RS232 0 #define MODBUS_RTU_RS485 1 @@ -25,14 +25,15 @@ MODBUS_API modbus_t* modbus_new_rtu(const char *device, int baud, char parity, MODBUS_API int modbus_rtu_set_serial_mode(modbus_t *ctx, int mode); MODBUS_API int modbus_rtu_get_serial_mode(modbus_t *ctx); -#define MODBUS_RTU_RTS_NONE 0 -#define MODBUS_RTU_RTS_UP 1 -#define MODBUS_RTU_RTS_DOWN 2 +#define MODBUS_RTU_RTS_NONE 0 +#define MODBUS_RTU_RTS_UP 1 +#define MODBUS_RTU_RTS_DOWN 2 MODBUS_API int modbus_rtu_set_rts(modbus_t *ctx, int mode); MODBUS_API int modbus_rtu_get_rts(modbus_t *ctx); -MODBUS_API int modbus_rtu_set_custom_rts(modbus_t *ctx, void (*set_rts) (modbus_t *ctx, int on)); +MODBUS_API int modbus_rtu_set_custom_rts(modbus_t *ctx, + void (*set_rts)(modbus_t *ctx, int on)); MODBUS_API int modbus_rtu_set_rts_delay(modbus_t *ctx, int us); MODBUS_API int modbus_rtu_get_rts_delay(modbus_t *ctx); diff --git a/src/modbus-tcp-private.h b/src/modbus-tcp-private.h index 80ef4708b..faffc21a5 100644 --- a/src/modbus-tcp-private.h +++ b/src/modbus-tcp-private.h @@ -7,11 +7,11 @@ #ifndef MODBUS_TCP_PRIVATE_H #define MODBUS_TCP_PRIVATE_H -#define _MODBUS_TCP_HEADER_LENGTH 7 +#define _MODBUS_TCP_HEADER_LENGTH 7 #define _MODBUS_TCP_PRESET_REQ_LENGTH 12 -#define _MODBUS_TCP_PRESET_RSP_LENGTH 8 +#define _MODBUS_TCP_PRESET_RSP_LENGTH 8 -#define _MODBUS_TCP_CHECKSUM_LENGTH 0 +#define _MODBUS_TCP_CHECKSUM_LENGTH 0 /* In both structures, the transaction ID must be placed on first position to have a quick access not dependent of the TCP backend */ diff --git a/src/modbus-tcp.c b/src/modbus-tcp.c index 171cea604..b084532e4 100644 --- a/src/modbus-tcp.c +++ b/src/modbus-tcp.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: LGPL-2.1-or-later */ +// clang-format off #if defined(_WIN32) # define OS_WIN32 /* ws2_32.dll has getaddrinfo and freeaddrinfo on Windows XP and later. @@ -52,11 +53,12 @@ #if defined(_AIX) && !defined(MSG_DONTWAIT) #define MSG_DONTWAIT MSG_NONBLOCK #endif +// clang-format on #include "modbus-private.h" -#include "modbus-tcp.h" #include "modbus-tcp-private.h" +#include "modbus-tcp.h" #ifdef OS_WIN32 static int _modbus_tcp_init_win32(void) @@ -65,8 +67,9 @@ static int _modbus_tcp_init_win32(void) WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { - fprintf(stderr, "WSAStartup() returned error code %d\n", - (unsigned int)GetLastError()); + fprintf(stderr, + "WSAStartup() returned error code %d\n", + (unsigned int) GetLastError()); errno = EIO; return -1; } @@ -94,9 +97,8 @@ static int _modbus_set_slave(modbus_t *ctx, int slave) } /* Builds a TCP request header */ -static int _modbus_tcp_build_request_basis(modbus_t *ctx, int function, - int addr, int nb, - uint8_t *req) +static int _modbus_tcp_build_request_basis( + modbus_t *ctx, int function, int addr, int nb, uint8_t *req) { modbus_tcp_t *ctx_tcp = ctx->backend_data; @@ -148,7 +150,6 @@ static int _modbus_tcp_build_response_basis(sft_t *sft, uint8_t *rsp) return _MODBUS_TCP_PRESET_RSP_LENGTH; } - static int _modbus_tcp_prepare_response_tid(const uint8_t *req, int *req_length) { return (req[0] << 8) + req[1]; @@ -171,15 +172,17 @@ static ssize_t _modbus_tcp_send(modbus_t *ctx, const uint8_t *req, int req_lengt Requests not to send SIGPIPE on errors on stream oriented sockets when the other end breaks the connection. The EPIPE error is still returned. */ - return send(ctx->s, (const char *)req, req_length, MSG_NOSIGNAL); + return send(ctx->s, (const char *) req, req_length, MSG_NOSIGNAL); } -static int _modbus_tcp_receive(modbus_t *ctx, uint8_t *req) { +static int _modbus_tcp_receive(modbus_t *ctx, uint8_t *req) +{ return _modbus_receive_msg(ctx, req, MSG_INDICATION); } -static ssize_t _modbus_tcp_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length) { - return recv(ctx->s, (char *)rsp, rsp_length, 0); +static ssize_t _modbus_tcp_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length) +{ + return recv(ctx->s, (char *) rsp, rsp_length, 0); } static int _modbus_tcp_check_integrity(modbus_t *ctx, uint8_t *msg, const int msg_length) @@ -187,15 +190,19 @@ static int _modbus_tcp_check_integrity(modbus_t *ctx, uint8_t *msg, const int ms return msg_length; } -static int _modbus_tcp_pre_check_confirmation(modbus_t *ctx, const uint8_t *req, - const uint8_t *rsp, int rsp_length) +static int _modbus_tcp_pre_check_confirmation(modbus_t *ctx, + const uint8_t *req, + const uint8_t *rsp, + int rsp_length) { unsigned int protocol_id; /* Check transaction ID */ if (req[0] != rsp[0] || req[1] != rsp[1]) { if (ctx->debug) { - fprintf(stderr, "Invalid transaction ID received 0x%X (not 0x%X)\n", - (rsp[0] << 8) + rsp[1], (req[0] << 8) + req[1]); + fprintf(stderr, + "Invalid transaction ID received 0x%X (not 0x%X)\n", + (rsp[0] << 8) + rsp[1], + (req[0] << 8) + req[1]); } errno = EMBBADDATA; return -1; @@ -205,8 +212,7 @@ static int _modbus_tcp_pre_check_confirmation(modbus_t *ctx, const uint8_t *req, protocol_id = (rsp[2] << 8) + rsp[3]; if (protocol_id != 0x0) { if (ctx->debug) { - fprintf(stderr, "Invalid protocol ID received 0x%X (not 0x0)\n", - protocol_id); + fprintf(stderr, "Invalid protocol ID received 0x%X (not 0x0)\n", protocol_id); } errno = EMBBADDATA; return -1; @@ -223,8 +229,7 @@ static int _modbus_tcp_set_ipv4_options(int s) /* Set the TCP no delay flag */ /* SOL_TCP = IPPROTO_TCP */ option = 1; - rc = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, - (const void *)&option, sizeof(int)); + rc = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const void *) &option, sizeof(int)); if (rc == -1) { return -1; } @@ -252,8 +257,7 @@ static int _modbus_tcp_set_ipv4_options(int s) **/ /* Set the IP low delay option */ option = IPTOS_LOWDELAY; - rc = setsockopt(s, IPPROTO_IP, IP_TOS, - (const void *)&option, sizeof(int)); + rc = setsockopt(s, IPPROTO_IP, IP_TOS, (const void *) &option, sizeof(int)); if (rc == -1) { return -1; } @@ -262,7 +266,9 @@ static int _modbus_tcp_set_ipv4_options(int s) return 0; } -static int _connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen, +static int _connect(int sockfd, + const struct sockaddr *addr, + socklen_t addrlen, const struct timeval *ro_tv) { int rc = connect(sockfd, addr, addrlen); @@ -292,7 +298,7 @@ static int _connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen, } /* The connection is established if SO_ERROR and optval are set to 0 */ - rc = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&optval, &optlen); + rc = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *) &optval, &optlen); if (rc == 0 && optval == 0) { return 0; } else { @@ -345,7 +351,8 @@ static int _modbus_tcp_connect(modbus_t *ctx) addr.sin_family = AF_INET; addr.sin_port = htons(ctx_tcp->port); addr.sin_addr.s_addr = inet_addr(ctx_tcp->ip); - rc = _connect(ctx->s, (struct sockaddr *)&addr, sizeof(addr), &ctx->response_timeout); + rc = + _connect(ctx->s, (struct sockaddr *) &addr, sizeof(addr), &ctx->response_timeout); if (rc == -1) { close(ctx->s); ctx->s = -1; @@ -381,8 +388,7 @@ static int _modbus_tcp_pi_connect(modbus_t *ctx) ai_hints.ai_next = NULL; ai_list = NULL; - rc = getaddrinfo(ctx_tcp_pi->node, ctx_tcp_pi->service, - &ai_hints, &ai_list); + rc = getaddrinfo(ctx_tcp_pi->node, ctx_tcp_pi->service, &ai_hints, &ai_list); if (rc != 0) { if (ctx->debug) { fprintf(stderr, "Error returned by getaddrinfo: %s\n", gai_strerror(rc)); @@ -462,7 +468,7 @@ static int _modbus_tcp_flush(modbus_t *ctx) tv.tv_usec = 0; FD_ZERO(&rset); FD_SET(ctx->s, &rset); - rc = select(ctx->s+1, &rset, NULL, NULL, &tv); + rc = select(ctx->s + 1, &rset, NULL, NULL, &tv); if (rc == -1) { return -1; } @@ -514,8 +520,8 @@ int modbus_tcp_listen(modbus_t *ctx, int nb_connection) } enable = 1; - if (setsockopt(new_s, SOL_SOCKET, SO_REUSEADDR, - (char *)&enable, sizeof(enable)) == -1) { + if (setsockopt(new_s, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(enable)) == + -1) { close(new_s); return -1; } @@ -531,7 +537,7 @@ int modbus_tcp_listen(modbus_t *ctx, int nb_connection) /* Listen only specified IP address */ addr.sin_addr.s_addr = inet_addr(ctx_tcp->ip); } - if (bind(new_s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + if (bind(new_s, (struct sockaddr *) &addr, sizeof(addr)) == -1) { close(new_s); return -1; } @@ -580,7 +586,7 @@ int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection) service = ctx_tcp_pi->service; } - memset(&ai_hints, 0, sizeof (ai_hints)); + memset(&ai_hints, 0, sizeof(ai_hints)); /* If node is not NULL, than the AI_PASSIVE flag is ignored. */ ai_hints.ai_flags |= AI_PASSIVE; #ifdef AI_ADDRCONFIG @@ -619,8 +625,8 @@ int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection) continue; } else { int enable = 1; - rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, - (void *)&enable, sizeof (enable)); + rc = + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *) &enable, sizeof(enable)); if (rc != 0) { close(s); if (ctx->debug) { @@ -673,9 +679,9 @@ int modbus_tcp_accept(modbus_t *ctx, int *s) addrlen = sizeof(addr); #ifdef HAVE_ACCEPT4 /* Inherit socket flags and use accept4 call */ - ctx->s = accept4(*s, (struct sockaddr *)&addr, &addrlen, SOCK_CLOEXEC); + ctx->s = accept4(*s, (struct sockaddr *) &addr, &addrlen, SOCK_CLOEXEC); #else - ctx->s = accept(*s, (struct sockaddr *)&addr, &addrlen); + ctx->s = accept(*s, (struct sockaddr *) &addr, &addrlen); #endif if (ctx->s < 0) { @@ -683,8 +689,7 @@ int modbus_tcp_accept(modbus_t *ctx, int *s) } if (ctx->debug) { - printf("The client connection from %s is accepted\n", - inet_ntoa(addr.sin_addr)); + printf("The client connection from %s is accepted\n", inet_ntoa(addr.sin_addr)); } return ctx->s; @@ -703,9 +708,9 @@ int modbus_tcp_pi_accept(modbus_t *ctx, int *s) addrlen = sizeof(addr); #ifdef HAVE_ACCEPT4 /* Inherit socket flags and use accept4 call */ - ctx->s = accept4(*s, (struct sockaddr *)&addr, &addrlen, SOCK_CLOEXEC); + ctx->s = accept4(*s, (struct sockaddr *) &addr, &addrlen, SOCK_CLOEXEC); #else - ctx->s = accept(*s, (struct sockaddr *)&addr, &addrlen); + ctx->s = accept(*s, (struct sockaddr *) &addr, &addrlen); #endif if (ctx->s < 0) { @@ -719,10 +724,11 @@ int modbus_tcp_pi_accept(modbus_t *ctx, int *s) return ctx->s; } -static int _modbus_tcp_select(modbus_t *ctx, fd_set *rset, struct timeval *tv, int length_to_read) +static int +_modbus_tcp_select(modbus_t *ctx, fd_set *rset, struct timeval *tv, int length_to_read) { int s_rc; - while ((s_rc = select(ctx->s+1, rset, NULL, NULL, tv)) == -1) { + while ((s_rc = select(ctx->s + 1, rset, NULL, NULL, tv)) == -1) { if (errno == EINTR) { if (ctx->debug) { fprintf(stderr, "A non blocked signal was caught\n"); @@ -743,14 +749,16 @@ static int _modbus_tcp_select(modbus_t *ctx, fd_set *rset, struct timeval *tv, i return s_rc; } -static void _modbus_tcp_free(modbus_t *ctx) { +static void _modbus_tcp_free(modbus_t *ctx) +{ if (ctx->backend_data) { free(ctx->backend_data); } free(ctx); } -static void _modbus_tcp_pi_free(modbus_t *ctx) { +static void _modbus_tcp_pi_free(modbus_t *ctx) +{ if (ctx->backend_data) { modbus_tcp_pi_t *ctx_tcp_pi = ctx->backend_data; free(ctx_tcp_pi->node); @@ -761,6 +769,7 @@ static void _modbus_tcp_pi_free(modbus_t *ctx) { free(ctx); } +// clang-format off const modbus_backend_t _modbus_tcp_backend = { _MODBUS_BACKEND_TYPE_TCP, _MODBUS_TCP_HEADER_LENGTH, @@ -783,7 +792,6 @@ const modbus_backend_t _modbus_tcp_backend = { _modbus_tcp_free }; - const modbus_backend_t _modbus_tcp_pi_backend = { _MODBUS_BACKEND_TYPE_TCP, _MODBUS_TCP_HEADER_LENGTH, @@ -806,7 +814,9 @@ const modbus_backend_t _modbus_tcp_pi_backend = { _modbus_tcp_pi_free }; -modbus_t* modbus_new_tcp(const char *ip, int port) +// clang-format on + +modbus_t *modbus_new_tcp(const char *ip, int port) { modbus_t *ctx; modbus_tcp_t *ctx_tcp; @@ -826,7 +836,7 @@ modbus_t* modbus_new_tcp(const char *ip, int port) } #endif - ctx = (modbus_t *)malloc(sizeof(modbus_t)); + ctx = (modbus_t *) malloc(sizeof(modbus_t)); if (ctx == NULL) { return NULL; } @@ -837,13 +847,13 @@ modbus_t* modbus_new_tcp(const char *ip, int port) ctx->backend = &_modbus_tcp_backend; - ctx->backend_data = (modbus_tcp_t *)malloc(sizeof(modbus_tcp_t)); + ctx->backend_data = (modbus_tcp_t *) malloc(sizeof(modbus_tcp_t)); if (ctx->backend_data == NULL) { modbus_free(ctx); errno = ENOMEM; return NULL; } - ctx_tcp = (modbus_tcp_t *)ctx->backend_data; + ctx_tcp = (modbus_tcp_t *) ctx->backend_data; if (ip != NULL) { dest_size = sizeof(char) * 16; @@ -870,13 +880,12 @@ modbus_t* modbus_new_tcp(const char *ip, int port) return ctx; } - -modbus_t* modbus_new_tcp_pi(const char *node, const char *service) +modbus_t *modbus_new_tcp_pi(const char *node, const char *service) { modbus_t *ctx; modbus_tcp_pi_t *ctx_tcp_pi; - ctx = (modbus_t *)malloc(sizeof(modbus_t)); + ctx = (modbus_t *) malloc(sizeof(modbus_t)); if (ctx == NULL) { return NULL; } @@ -887,13 +896,13 @@ modbus_t* modbus_new_tcp_pi(const char *node, const char *service) ctx->backend = &_modbus_tcp_pi_backend; - ctx->backend_data = (modbus_tcp_pi_t *)malloc(sizeof(modbus_tcp_pi_t)); + ctx->backend_data = (modbus_tcp_pi_t *) malloc(sizeof(modbus_tcp_pi_t)); if (ctx->backend_data == NULL) { modbus_free(ctx); errno = ENOMEM; return NULL; } - ctx_tcp_pi = (modbus_tcp_pi_t *)ctx->backend_data; + ctx_tcp_pi = (modbus_tcp_pi_t *) ctx->backend_data; ctx_tcp_pi->node = NULL; ctx_tcp_pi->service = NULL; diff --git a/src/modbus-tcp.h b/src/modbus-tcp.h index 83cf508fb..768d38c9d 100644 --- a/src/modbus-tcp.h +++ b/src/modbus-tcp.h @@ -15,35 +15,35 @@ MODBUS_BEGIN_DECLS /* Win32 with MinGW, supplement to */ #include #if !defined(ECONNRESET) -#define ECONNRESET WSAECONNRESET +#define ECONNRESET WSAECONNRESET #endif #if !defined(ECONNREFUSED) #define ECONNREFUSED WSAECONNREFUSED #endif #if !defined(ETIMEDOUT) -#define ETIMEDOUT WSAETIMEDOUT +#define ETIMEDOUT WSAETIMEDOUT #endif #if !defined(ENOPROTOOPT) -#define ENOPROTOOPT WSAENOPROTOOPT +#define ENOPROTOOPT WSAENOPROTOOPT #endif #if !defined(EINPROGRESS) -#define EINPROGRESS WSAEINPROGRESS +#define EINPROGRESS WSAEINPROGRESS #endif #endif -#define MODBUS_TCP_DEFAULT_PORT 502 -#define MODBUS_TCP_SLAVE 0xFF +#define MODBUS_TCP_DEFAULT_PORT 502 +#define MODBUS_TCP_SLAVE 0xFF /* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5 * TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes */ -#define MODBUS_TCP_MAX_ADU_LENGTH 260 +#define MODBUS_TCP_MAX_ADU_LENGTH 260 -MODBUS_API modbus_t* modbus_new_tcp(const char *ip_address, int port); +MODBUS_API modbus_t *modbus_new_tcp(const char *ip_address, int port); MODBUS_API int modbus_tcp_listen(modbus_t *ctx, int nb_connection); MODBUS_API int modbus_tcp_accept(modbus_t *ctx, int *s); -MODBUS_API modbus_t* modbus_new_tcp_pi(const char *node, const char *service); +MODBUS_API modbus_t *modbus_new_tcp_pi(const char *node, const char *service); MODBUS_API int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection); MODBUS_API int modbus_tcp_pi_accept(modbus_t *ctx, int *s); diff --git a/src/modbus-version.h.in b/src/modbus-version.h.in index 90c942b34..6b79b51ba 100644 --- a/src/modbus-version.h.in +++ b/src/modbus-version.h.in @@ -29,25 +29,23 @@ #define LIBMODBUS_VERSION_MICRO (@LIBMODBUS_VERSION_MICRO@) /* The full version, like 1.2.3 */ -#define LIBMODBUS_VERSION @LIBMODBUS_VERSION@ +#define LIBMODBUS_VERSION @LIBMODBUS_VERSION@ /* The full version, in string form (suited for string concatenation) */ #define LIBMODBUS_VERSION_STRING "@LIBMODBUS_VERSION@" /* Numerically encoded version, eg. v1.2.3 is 0x010203 */ -#define LIBMODBUS_VERSION_HEX ((LIBMODBUS_VERSION_MAJOR << 16) | \ - (LIBMODBUS_VERSION_MINOR << 8) | \ - (LIBMODBUS_VERSION_MICRO << 0)) +#define LIBMODBUS_VERSION_HEX \ + ((LIBMODBUS_VERSION_MAJOR << 16) | (LIBMODBUS_VERSION_MINOR << 8) | \ + (LIBMODBUS_VERSION_MICRO << 0)) /* Evaluates to True if the version is greater than @major, @minor and @micro */ -#define LIBMODBUS_VERSION_CHECK(major,minor,micro) \ - (LIBMODBUS_VERSION_MAJOR > (major) || \ - (LIBMODBUS_VERSION_MAJOR == (major) && \ - LIBMODBUS_VERSION_MINOR > (minor)) || \ - (LIBMODBUS_VERSION_MAJOR == (major) && \ - LIBMODBUS_VERSION_MINOR == (minor) && \ +#define LIBMODBUS_VERSION_CHECK(major, minor, micro) \ + (LIBMODBUS_VERSION_MAJOR > (major) || \ + (LIBMODBUS_VERSION_MAJOR == (major) && LIBMODBUS_VERSION_MINOR > (minor)) || \ + (LIBMODBUS_VERSION_MAJOR == (major) && LIBMODBUS_VERSION_MINOR == (minor) && \ LIBMODBUS_VERSION_MICRO >= (micro))) #endif /* MODBUS_VERSION_H */ diff --git a/src/modbus.c b/src/modbus.c index e82411c7d..e7379d371 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -7,12 +7,12 @@ * http://libmodbus.org/ */ -#include -#include -#include -#include #include #include +#include +#include +#include +#include #include #ifndef _MSC_VER #include @@ -20,8 +20,8 @@ #include -#include "modbus.h" #include "modbus-private.h" +#include "modbus.h" /* Internal use */ #define MSG_LENGTH_UNDEFINED -1 @@ -41,7 +41,8 @@ typedef enum { _STEP_DATA } _step_t; -const char *modbus_strerror(int errnum) { +const char *modbus_strerror(int errnum) +{ switch (errnum) { case EMBXILFUN: return "Illegal function"; @@ -95,13 +96,12 @@ static void _sleep_response_timeout(modbus_t *ctx) /* Response timeout is always positive */ #ifdef _WIN32 /* usleep doesn't exist on Windows */ - Sleep((ctx->response_timeout.tv_sec * 1000) + - (ctx->response_timeout.tv_usec / 1000)); + Sleep((ctx->response_timeout.tv_sec * 1000) + (ctx->response_timeout.tv_usec / 1000)); #else /* usleep source code */ struct timespec request, remaining; request.tv_sec = ctx->response_timeout.tv_sec; - request.tv_nsec = ((long int)ctx->response_timeout.tv_usec) * 1000; + request.tv_nsec = ((long int) ctx->response_timeout.tv_usec) * 1000; while (nanosleep(&request, &remaining) == -1 && errno == EINTR) { request = remaining; } @@ -137,8 +137,7 @@ static unsigned int compute_response_length_from_request(modbus_t *ctx, uint8_t /* Header + nb values (code from write_bits) */ int nb = (req[offset + 3] << 8) | req[offset + 4]; length = 2 + (nb / 8) + ((nb % 8) ? 1 : 0); - } - break; + } break; case MODBUS_FC_WRITE_AND_READ_REGISTERS: case MODBUS_FC_READ_HOLDING_REGISTERS: case MODBUS_FC_READ_INPUT_REGISTERS: @@ -185,8 +184,9 @@ static int send_msg(modbus_t *ctx, uint8_t *msg, int msg_length) if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) { #ifdef _WIN32 const int wsa_err = WSAGetLastError(); - if (wsa_err == WSAENETRESET || wsa_err == WSAENOTCONN || wsa_err == WSAENOTSOCK || - wsa_err == WSAESHUTDOWN || wsa_err == WSAEHOSTUNREACH || wsa_err == WSAECONNABORTED || + if (wsa_err == WSAENETRESET || wsa_err == WSAENOTCONN || + wsa_err == WSAENOTSOCK || wsa_err == WSAESHUTDOWN || + wsa_err == WSAEHOSTUNREACH || wsa_err == WSAECONNABORTED || wsa_err == WSAECONNRESET || wsa_err == WSAETIMEDOUT) { modbus_close(ctx); _sleep_response_timeout(ctx); @@ -210,8 +210,7 @@ static int send_msg(modbus_t *ctx, uint8_t *msg, int msg_length) #endif } } - } while ((ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) && - rc == -1); + } while ((ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) && rc == -1); if (rc > 0 && rc != msg_length) { errno = EMBBADDATA; @@ -263,8 +262,7 @@ int modbus_send_raw_request(modbus_t *ctx, const uint8_t *raw_req, int raw_req_l */ /* Computes the length to read after the function received */ -static uint8_t compute_meta_length_after_function(int function, - msg_type_t msg_type) +static uint8_t compute_meta_length_after_function(int function, msg_type_t msg_type) { int length; @@ -303,8 +301,8 @@ static uint8_t compute_meta_length_after_function(int function, } /* Computes the length to read after the meta information (address, count, etc) */ -static int compute_data_length_after_meta(modbus_t *ctx, uint8_t *msg, - msg_type_t msg_type) +static int +compute_data_length_after_meta(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type) { int function = msg[ctx->backend->header_length]; int length; @@ -337,7 +335,6 @@ static int compute_data_length_after_meta(modbus_t *ctx, uint8_t *msg, return length; } - /* Waits a response from a modbus server or a request from a modbus client. This function blocks if there is no replies (3 timeouts). @@ -395,7 +392,8 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type) /* By default, the indication timeout isn't set */ p_tv = NULL; } else { - /* Wait for an indication (name of a received request by a server, see schema) */ + /* Wait for an indication (name of a received request by a server, see schema) + */ tv.tv_sec = ctx->indication_timeout.tv_sec; tv.tv_usec = ctx->indication_timeout.tv_usec; p_tv = &tv; @@ -447,17 +445,17 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type) wsa_err = WSAGetLastError(); if ((ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) && (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_TCP) && - (wsa_err == WSAENOTCONN || wsa_err == WSAENETRESET || wsa_err == WSAENOTSOCK || - wsa_err == WSAESHUTDOWN || wsa_err == WSAECONNABORTED || wsa_err == WSAETIMEDOUT || - wsa_err == WSAECONNRESET)) { + (wsa_err == WSAENOTCONN || wsa_err == WSAENETRESET || + wsa_err == WSAENOTSOCK || wsa_err == WSAESHUTDOWN || + wsa_err == WSAECONNABORTED || wsa_err == WSAETIMEDOUT || + wsa_err == WSAECONNRESET)) { modbus_close(ctx); modbus_connect(ctx); } #else if ((ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) && (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_TCP) && - (errno == ECONNRESET || errno == ECONNREFUSED || - errno == EBADF)) { + (errno == ECONNRESET || errno == ECONNREFUSED || errno == EBADF)) { int saved_errno = errno; modbus_close(ctx); modbus_connect(ctx); @@ -471,7 +469,7 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type) /* Display the hex code of each character received */ if (ctx->debug) { int i; - for (i=0; i < rc; i++) + for (i = 0; i < rc; i++) printf("<%.2X>", msg[msg_length + i]); } @@ -485,16 +483,14 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type) case _STEP_FUNCTION: /* Function code position */ length_to_read = compute_meta_length_after_function( - msg[ctx->backend->header_length], - msg_type); + msg[ctx->backend->header_length], msg_type); if (length_to_read != 0) { step = _STEP_META; break; } /* else switches straight to the next step */ case _STEP_META: - length_to_read = compute_data_length_after_meta( - ctx, msg, msg_type); - if ((msg_length + length_to_read) > (int)ctx->backend->max_adu_length) { + length_to_read = compute_data_length_after_meta(ctx, msg, msg_type); + if ((msg_length + length_to_read) > (int) ctx->backend->max_adu_length) { errno = EMBBADDATA; _error_print(ctx, "too many data"); return -1; @@ -554,8 +550,7 @@ int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp) return _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION); } -static int check_confirmation(modbus_t *ctx, uint8_t *req, - uint8_t *rsp, int rsp_length) +static int check_confirmation(modbus_t *ctx, uint8_t *req, uint8_t *rsp, int rsp_length) { int rc; int rsp_length_computed; @@ -577,7 +572,7 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req, /* Exception code */ if (function >= 0x80) { - if (rsp_length == (offset + 2 + (int)ctx->backend->checksum_length) && + if (rsp_length == (offset + 2 + (int) ctx->backend->checksum_length) && req[offset] == (rsp[offset] - 0x80)) { /* Valid exception code received */ @@ -608,9 +603,11 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req, /* Check function code */ if (function != req[offset]) { if (ctx->debug) { - fprintf(stderr, - "Received function not corresponding to the request (0x%X != 0x%X)\n", - function, req[offset]); + fprintf( + stderr, + "Received function not corresponding to the request (0x%X != 0x%X)\n", + function, + req[offset]); } if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) { _sleep_response_timeout(ctx); @@ -641,7 +638,8 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req, case MODBUS_FC_WRITE_MULTIPLE_COILS: case MODBUS_FC_WRITE_MULTIPLE_REGISTERS: /* address in request and response must be equal */ - if ((req[offset + 1] != rsp[offset + 1]) || (req[offset + 2] != rsp[offset + 2])) { + if ((req[offset + 1] != rsp[offset + 1]) || + (req[offset + 2] != rsp[offset + 2])) { resp_addr_ok = FALSE; } /* N Write functions */ @@ -655,11 +653,13 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req, case MODBUS_FC_WRITE_SINGLE_COIL: case MODBUS_FC_WRITE_SINGLE_REGISTER: /* address in request and response must be equal */ - if ((req[offset + 1] != rsp[offset + 1]) || (req[offset + 2] != rsp[offset + 2])) { + if ((req[offset + 1] != rsp[offset + 1]) || + (req[offset + 2] != rsp[offset + 2])) { resp_addr_ok = FALSE; } /* data in request and response must be equal */ - if ((req[offset + 3] != rsp[offset + 3]) || (req[offset + 4] != rsp[offset + 4])) { + if ((req[offset + 3] != rsp[offset + 3]) || + (req[offset + 4] != rsp[offset + 4])) { resp_data_ok = FALSE; } /* 1 Write functions & others */ @@ -671,13 +671,15 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req, break; } - if ((req_nb_value == rsp_nb_value) && (resp_addr_ok == TRUE) && (resp_data_ok == TRUE)) { + if ((req_nb_value == rsp_nb_value) && (resp_addr_ok == TRUE) && + (resp_data_ok == TRUE)) { rc = rsp_nb_value; } else { if (ctx->debug) { fprintf(stderr, "Received data not corresponding to the request (%d != %d)\n", - rsp_nb_value, req_nb_value); + rsp_nb_value, + req_nb_value); } if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) { @@ -690,9 +692,11 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req, } } else { if (ctx->debug) { - fprintf(stderr, - "Message length not corresponding to the computed length (%d != %d)\n", - rsp_length, rsp_length_computed); + fprintf( + stderr, + "Message length not corresponding to the computed length (%d != %d)\n", + rsp_length, + rsp_length_computed); } if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) { _sleep_response_timeout(ctx); @@ -705,9 +709,8 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req, return rc; } -static int response_io_status(uint8_t *tab_io_status, - int address, int nb, - uint8_t *rsp, int offset) +static int +response_io_status(uint8_t *tab_io_status, int address, int nb, uint8_t *rsp, int offset) { int shift = 0; /* Instead of byte (not allowed in Win32) */ @@ -732,10 +735,13 @@ static int response_io_status(uint8_t *tab_io_status, } /* Build the exception response */ -static int response_exception(modbus_t *ctx, sft_t *sft, - int exception_code, uint8_t *rsp, +static int response_exception(modbus_t *ctx, + sft_t *sft, + int exception_code, + uint8_t *rsp, unsigned int to_flush, - const char* template, ...) + const char *template, + ...) { int rsp_length; @@ -768,8 +774,10 @@ static int response_exception(modbus_t *ctx, sft_t *sft, If an error occurs, this function construct the response accordingly. */ -int modbus_reply(modbus_t *ctx, const uint8_t *req, - int req_length, modbus_mapping_t *mb_mapping) +int modbus_reply(modbus_t *ctx, + const uint8_t *req, + int req_length, + modbus_mapping_t *mb_mapping) { int offset; int slave; @@ -801,53 +809,72 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, int start_bits = is_input ? mb_mapping->start_input_bits : mb_mapping->start_bits; int nb_bits = is_input ? mb_mapping->nb_input_bits : mb_mapping->nb_bits; uint8_t *tab_bits = is_input ? mb_mapping->tab_input_bits : mb_mapping->tab_bits; - const char * const name = is_input ? "read_input_bits" : "read_bits"; + const char *const name = is_input ? "read_input_bits" : "read_bits"; int nb = (req[offset + 3] << 8) + req[offset + 4]; /* The mapping can be shifted to reduce memory consumption and it doesn't always start at address zero. */ int mapping_address = address - start_bits; if (nb < 1 || MODBUS_MAX_READ_BITS < nb) { - rsp_length = response_exception( - ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE, - "Illegal nb of values %d in %s (max %d)\n", - nb, name, MODBUS_MAX_READ_BITS); + rsp_length = response_exception(ctx, + &sft, + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, + rsp, + TRUE, + "Illegal nb of values %d in %s (max %d)\n", + nb, + name, + MODBUS_MAX_READ_BITS); } else if (mapping_address < 0 || (mapping_address + nb) > nb_bits) { - rsp_length = response_exception( - ctx, &sft, - MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE, - "Illegal data address 0x%0X in %s\n", - mapping_address < 0 ? address : address + nb, name); + rsp_length = response_exception(ctx, + &sft, + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, + rsp, + FALSE, + "Illegal data address 0x%0X in %s\n", + mapping_address < 0 ? address : address + nb, + name); } else { rsp_length = ctx->backend->build_response_basis(&sft, rsp); rsp[rsp_length++] = (nb / 8) + ((nb % 8) ? 1 : 0); - rsp_length = response_io_status(tab_bits, mapping_address, nb, - rsp, rsp_length); + rsp_length = + response_io_status(tab_bits, mapping_address, nb, rsp, rsp_length); } - } - break; + } break; case MODBUS_FC_READ_HOLDING_REGISTERS: case MODBUS_FC_READ_INPUT_REGISTERS: { unsigned int is_input = (function == MODBUS_FC_READ_INPUT_REGISTERS); - int start_registers = is_input ? mb_mapping->start_input_registers : mb_mapping->start_registers; - int nb_registers = is_input ? mb_mapping->nb_input_registers : mb_mapping->nb_registers; - uint16_t *tab_registers = is_input ? mb_mapping->tab_input_registers : mb_mapping->tab_registers; - const char * const name = is_input ? "read_input_registers" : "read_registers"; + int start_registers = + is_input ? mb_mapping->start_input_registers : mb_mapping->start_registers; + int nb_registers = + is_input ? mb_mapping->nb_input_registers : mb_mapping->nb_registers; + uint16_t *tab_registers = + is_input ? mb_mapping->tab_input_registers : mb_mapping->tab_registers; + const char *const name = is_input ? "read_input_registers" : "read_registers"; int nb = (req[offset + 3] << 8) + req[offset + 4]; /* The mapping can be shifted to reduce memory consumption and it doesn't always start at address zero. */ int mapping_address = address - start_registers; if (nb < 1 || MODBUS_MAX_READ_REGISTERS < nb) { - rsp_length = response_exception( - ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE, - "Illegal nb of values %d in %s (max %d)\n", - nb, name, MODBUS_MAX_READ_REGISTERS); + rsp_length = response_exception(ctx, + &sft, + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, + rsp, + TRUE, + "Illegal nb of values %d in %s (max %d)\n", + nb, + name, + MODBUS_MAX_READ_REGISTERS); } else if (mapping_address < 0 || (mapping_address + nb) > nb_registers) { - rsp_length = response_exception( - ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE, - "Illegal data address 0x%0X in %s\n", - mapping_address < 0 ? address : address + nb, name); + rsp_length = response_exception(ctx, + &sft, + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, + rsp, + FALSE, + "Illegal data address 0x%0X in %s\n", + mapping_address < 0 ? address : address + nb, + name); } else { int i; @@ -858,16 +885,18 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, rsp[rsp_length++] = tab_registers[i] & 0xFF; } } - } - break; + } break; case MODBUS_FC_WRITE_SINGLE_COIL: { int mapping_address = address - mb_mapping->start_bits; if (mapping_address < 0 || mapping_address >= mb_mapping->nb_bits) { - rsp_length = response_exception( - ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE, - "Illegal data address 0x%0X in write_bit\n", - address); + rsp_length = response_exception(ctx, + &sft, + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, + rsp, + FALSE, + "Illegal data address 0x%0X in write_bit\n", + address); } else { int data = (req[offset + 3] << 8) + req[offset + 4]; @@ -877,23 +906,29 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, rsp_length = req_length; } else { rsp_length = response_exception( - ctx, &sft, - MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, FALSE, + ctx, + &sft, + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, + rsp, + FALSE, "Illegal data value 0x%0X in write_bit request at address %0X\n", - data, address); + data, + address); } } - } - break; + } break; case MODBUS_FC_WRITE_SINGLE_REGISTER: { int mapping_address = address - mb_mapping->start_registers; if (mapping_address < 0 || mapping_address >= mb_mapping->nb_registers) { - rsp_length = response_exception( - ctx, &sft, - MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE, - "Illegal data address 0x%0X in write_register\n", - address); + rsp_length = + response_exception(ctx, + &sft, + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, + rsp, + FALSE, + "Illegal data address 0x%0X in write_register\n", + address); } else { int data = (req[offset + 3] << 8) + req[offset + 4]; @@ -901,8 +936,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, memcpy(rsp, req, req_length); rsp_length = req_length; } - } - break; + } break; case MODBUS_FC_WRITE_MULTIPLE_COILS: { int nb = (req[offset + 3] << 8) + req[offset + 4]; int nb_bits = req[offset + 5]; @@ -912,29 +946,34 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, /* May be the indication has been truncated on reading because of * invalid address (eg. nb is 0 but the request contains values to * write) so it's necessary to flush. */ - rsp_length = response_exception( - ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE, - "Illegal number of values %d in write_bits (max %d)\n", - nb, MODBUS_MAX_WRITE_BITS); - } else if (mapping_address < 0 || - (mapping_address + nb) > mb_mapping->nb_bits) { - rsp_length = response_exception( - ctx, &sft, - MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE, - "Illegal data address 0x%0X in write_bits\n", - mapping_address < 0 ? address : address + nb); + rsp_length = + response_exception(ctx, + &sft, + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, + rsp, + TRUE, + "Illegal number of values %d in write_bits (max %d)\n", + nb, + MODBUS_MAX_WRITE_BITS); + } else if (mapping_address < 0 || (mapping_address + nb) > mb_mapping->nb_bits) { + rsp_length = response_exception(ctx, + &sft, + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, + rsp, + FALSE, + "Illegal data address 0x%0X in write_bits\n", + mapping_address < 0 ? address : address + nb); } else { /* 6 = byte count */ - modbus_set_bits_from_bytes(mb_mapping->tab_bits, mapping_address, nb, - &req[offset + 6]); + modbus_set_bits_from_bytes( + mb_mapping->tab_bits, mapping_address, nb, &req[offset + 6]); rsp_length = ctx->backend->build_response_basis(&sft, rsp); /* 4 to copy the bit address (2) and the quantity of bits */ memcpy(rsp + rsp_length, req + rsp_length, 4); rsp_length += 4; } - } - break; + } break; case MODBUS_FC_WRITE_MULTIPLE_REGISTERS: { int nb = (req[offset + 3] << 8) + req[offset + 4]; int nb_bytes = req[offset + 5]; @@ -942,15 +981,24 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, if (nb < 1 || MODBUS_MAX_WRITE_REGISTERS < nb || nb_bytes != nb * 2) { rsp_length = response_exception( - ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE, + ctx, + &sft, + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, + rsp, + TRUE, "Illegal number of values %d in write_registers (max %d)\n", - nb, MODBUS_MAX_WRITE_REGISTERS); + nb, + MODBUS_MAX_WRITE_REGISTERS); } else if (mapping_address < 0 || (mapping_address + nb) > mb_mapping->nb_registers) { - rsp_length = response_exception( - ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE, - "Illegal data address 0x%0X in write_registers\n", - mapping_address < 0 ? address : address + nb); + rsp_length = + response_exception(ctx, + &sft, + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, + rsp, + FALSE, + "Illegal data address 0x%0X in write_registers\n", + mapping_address < 0 ? address : address + nb); } else { int i, j; for (i = mapping_address, j = 6; i < mapping_address + nb; i++, j += 2) { @@ -964,8 +1012,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, memcpy(rsp + rsp_length, req + rsp_length, 4); rsp_length += 4; } - } - break; + } break; case MODBUS_FC_REPORT_SLAVE_ID: { int str_len; int byte_count_pos; @@ -981,8 +1028,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, memcpy(rsp + rsp_length, "LMB" LIBMODBUS_VERSION_STRING, str_len); rsp_length += str_len; rsp[byte_count_pos] = rsp_length - byte_count_pos - 1; - } - break; + } break; case MODBUS_FC_READ_EXCEPTION_STATUS: if (ctx->debug) { fprintf(stderr, "FIXME Not implemented\n"); @@ -994,22 +1040,25 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, int mapping_address = address - mb_mapping->start_registers; if (mapping_address < 0 || mapping_address >= mb_mapping->nb_registers) { - rsp_length = response_exception( - ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE, - "Illegal data address 0x%0X in write_register\n", - address); + rsp_length = + response_exception(ctx, + &sft, + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, + rsp, + FALSE, + "Illegal data address 0x%0X in write_register\n", + address); } else { uint16_t data = mb_mapping->tab_registers[mapping_address]; uint16_t and = (req[offset + 3] << 8) + req[offset + 4]; uint16_t or = (req[offset + 5] << 8) + req[offset + 6]; - data = (data & and) | (or & (~and)); + data = (data & and) | (or &(~and)); mb_mapping->tab_registers[mapping_address] = data; memcpy(rsp, req, req_length); rsp_length = req_length; } - } - break; + } break; case MODBUS_FC_WRITE_AND_READ_REGISTERS: { int nb = (req[offset + 3] << 8) + req[offset + 4]; uint16_t address_write = (req[offset + 5] << 8) + req[offset + 6]; @@ -1018,20 +1067,32 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, int mapping_address = address - mb_mapping->start_registers; int mapping_address_write = address_write - mb_mapping->start_registers; - if (nb_write < 1 || MODBUS_MAX_WR_WRITE_REGISTERS < nb_write || - nb < 1 || MODBUS_MAX_WR_READ_REGISTERS < nb || - nb_write_bytes != nb_write * 2) { + if (nb_write < 1 || MODBUS_MAX_WR_WRITE_REGISTERS < nb_write || nb < 1 || + MODBUS_MAX_WR_READ_REGISTERS < nb || nb_write_bytes != nb_write * 2) { rsp_length = response_exception( - ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE, - "Illegal nb of values (W%d, R%d) in write_and_read_registers (max W%d, R%d)\n", - nb_write, nb, MODBUS_MAX_WR_WRITE_REGISTERS, MODBUS_MAX_WR_READ_REGISTERS); + ctx, + &sft, + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, + rsp, + TRUE, + "Illegal nb of values (W%d, R%d) in write_and_read_registers (max W%d, " + "R%d)\n", + nb_write, + nb, + MODBUS_MAX_WR_WRITE_REGISTERS, + MODBUS_MAX_WR_READ_REGISTERS); } else if (mapping_address < 0 || (mapping_address + nb) > mb_mapping->nb_registers || mapping_address_write < 0 || (mapping_address_write + nb_write) > mb_mapping->nb_registers) { rsp_length = response_exception( - ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE, - "Illegal data read address 0x%0X or write address 0x%0X write_and_read_registers\n", + ctx, + &sft, + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, + rsp, + FALSE, + "Illegal data read address 0x%0X or write address 0x%0X " + "write_and_read_registers\n", mapping_address < 0 ? address : address + nb, mapping_address_write < 0 ? address_write : address_write + nb_write); } else { @@ -1041,8 +1102,8 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, /* Write first. 10 and 11 are the offset of the first values to write */ - for (i = mapping_address_write, j = 10; - i < mapping_address_write + nb_write; i++, j += 2) { + for (i = mapping_address_write, j = 10; i < mapping_address_write + nb_write; + i++, j += 2) { mb_mapping->tab_registers[i] = (req[offset + j] << 8) + req[offset + j + 1]; } @@ -1053,17 +1114,21 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, rsp[rsp_length++] = mb_mapping->tab_registers[i] & 0xFF; } } - } - break; + } break; default: - rsp_length = response_exception( - ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_FUNCTION, rsp, TRUE, - "Unknown Modbus function code: 0x%0X\n", function); + rsp_length = response_exception(ctx, + &sft, + MODBUS_EXCEPTION_ILLEGAL_FUNCTION, + rsp, + TRUE, + "Unknown Modbus function code: 0x%0X\n", + function); break; } - /* Suppress any responses in RTU when the request was a broadcast, excepted when quirk is enabled. */ + /* Suppress any responses in RTU when the request was a broadcast, excepted when quirk + * is enabled. */ if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU && slave == MODBUS_BROADCAST_ADDRESS && !(ctx->quirks & MODBUS_QUIRK_REPLY_TO_BROADCAST)) { @@ -1072,8 +1137,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, return send_msg(ctx, rsp, rsp_length); } -int modbus_reply_exception(modbus_t *ctx, const uint8_t *req, - unsigned int exception_code) +int modbus_reply_exception(modbus_t *ctx, const uint8_t *req, unsigned int exception_code) { int offset; int slave; @@ -1108,8 +1172,7 @@ int modbus_reply_exception(modbus_t *ctx, const uint8_t *req, } /* Reads IO status */ -static int read_io_status(modbus_t *ctx, int function, - int addr, int nb, uint8_t *dest) +static int read_io_status(modbus_t *ctx, int function, int addr, int nb, uint8_t *dest) { int rc; int req_length; @@ -1144,7 +1207,6 @@ static int read_io_status(modbus_t *ctx, int function, dest[pos++] = (temp & bit) ? TRUE : FALSE; bit = bit << 1; } - } } @@ -1166,7 +1228,8 @@ int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest) if (ctx->debug) { fprintf(stderr, "ERROR Too many bits requested (%d > %d)\n", - nb, MODBUS_MAX_READ_BITS); + nb, + MODBUS_MAX_READ_BITS); } errno = EMBMDATA; return -1; @@ -1180,7 +1243,6 @@ int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest) return nb; } - /* Same as modbus_read_bits but reads the remote device input table */ int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest) { @@ -1195,7 +1257,8 @@ int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest) if (ctx->debug) { fprintf(stderr, "ERROR Too many discrete inputs requested (%d > %d)\n", - nb, MODBUS_MAX_READ_BITS); + nb, + MODBUS_MAX_READ_BITS); } errno = EMBMDATA; return -1; @@ -1210,8 +1273,7 @@ int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest) } /* Reads the data from a remote device and put that data into an array */ -static int read_registers(modbus_t *ctx, int function, int addr, int nb, - uint16_t *dest) +static int read_registers(modbus_t *ctx, int function, int addr, int nb, uint16_t *dest) { int rc; int req_length; @@ -1222,7 +1284,8 @@ static int read_registers(modbus_t *ctx, int function, int addr, int nb, if (ctx->debug) { fprintf(stderr, "ERROR Too many registers requested (%d > %d)\n", - nb, MODBUS_MAX_READ_REGISTERS); + nb, + MODBUS_MAX_READ_REGISTERS); } errno = EMBMDATA; return -1; @@ -1247,8 +1310,7 @@ static int read_registers(modbus_t *ctx, int function, int addr, int nb, for (i = 0; i < rc; i++) { /* shift reg hi_byte to temp OR with lo_byte */ - dest[i] = (rsp[offset + 2 + (i << 1)] << 8) | - rsp[offset + 3 + (i << 1)]; + dest[i] = (rsp[offset + 2 + (i << 1)] << 8) | rsp[offset + 3 + (i << 1)]; } } @@ -1270,20 +1332,19 @@ int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest) if (ctx->debug) { fprintf(stderr, "ERROR Too many registers requested (%d > %d)\n", - nb, MODBUS_MAX_READ_REGISTERS); + nb, + MODBUS_MAX_READ_REGISTERS); } errno = EMBMDATA; return -1; } - status = read_registers(ctx, MODBUS_FC_READ_HOLDING_REGISTERS, - addr, nb, dest); + status = read_registers(ctx, MODBUS_FC_READ_HOLDING_REGISTERS, addr, nb, dest); return status; } /* Reads the input registers of remote device and put the data into an array */ -int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, - uint16_t *dest) +int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest) { int status; @@ -1295,13 +1356,13 @@ int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, if (nb > MODBUS_MAX_READ_REGISTERS) { fprintf(stderr, "ERROR Too many input registers requested (%d > %d)\n", - nb, MODBUS_MAX_READ_REGISTERS); + nb, + MODBUS_MAX_READ_REGISTERS); errno = EMBMDATA; return -1; } - status = read_registers(ctx, MODBUS_FC_READ_INPUT_REGISTERS, - addr, nb, dest); + status = read_registers(ctx, MODBUS_FC_READ_INPUT_REGISTERS, addr, nb, dest); return status; } @@ -1344,8 +1405,7 @@ int modbus_write_bit(modbus_t *ctx, int addr, int status) return -1; } - return write_single(ctx, MODBUS_FC_WRITE_SINGLE_COIL, addr, - status ? 0xFF00 : 0); + return write_single(ctx, MODBUS_FC_WRITE_SINGLE_COIL, addr, status ? 0xFF00 : 0); } /* Writes a value in one register of the remote device */ @@ -1377,16 +1437,17 @@ int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src) if (nb > MODBUS_MAX_WRITE_BITS) { if (ctx->debug) { - fprintf(stderr, "ERROR Writing too many bits (%d > %d)\n", - nb, MODBUS_MAX_WRITE_BITS); + fprintf(stderr, + "ERROR Writing too many bits (%d > %d)\n", + nb, + MODBUS_MAX_WRITE_BITS); } errno = EMBMDATA; return -1; } - req_length = ctx->backend->build_request_basis(ctx, - MODBUS_FC_WRITE_MULTIPLE_COILS, - addr, nb, req); + req_length = ctx->backend->build_request_basis( + ctx, MODBUS_FC_WRITE_MULTIPLE_COILS, addr, nb, req); byte_count = (nb / 8) + ((nb % 8) ? 1 : 0); req[req_length++] = byte_count; @@ -1400,7 +1461,7 @@ int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src) if (src[pos++]) req[req_length] |= bit; else - req[req_length] &=~ bit; + req[req_length] &= ~bit; bit = bit << 1; } @@ -1418,7 +1479,6 @@ int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src) rc = check_confirmation(ctx, req, rsp, rc); } - return rc; } @@ -1440,15 +1500,15 @@ int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src) if (ctx->debug) { fprintf(stderr, "ERROR Trying to write to too many registers (%d > %d)\n", - nb, MODBUS_MAX_WRITE_REGISTERS); + nb, + MODBUS_MAX_WRITE_REGISTERS); } errno = EMBMDATA; return -1; } - req_length = ctx->backend->build_request_basis(ctx, - MODBUS_FC_WRITE_MULTIPLE_REGISTERS, - addr, nb, req); + req_length = ctx->backend->build_request_basis( + ctx, MODBUS_FC_WRITE_MULTIPLE_REGISTERS, addr, nb, req); byte_count = nb * 2; req[req_length++] = byte_count; @@ -1471,7 +1531,10 @@ int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src) return rc; } -int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask) +int modbus_mask_write_register(modbus_t *ctx, + int addr, + uint16_t and_mask, + uint16_t or_mask) { int rc; int req_length; @@ -1480,9 +1543,8 @@ int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint1 * (2 bytes) which is not used. */ uint8_t req[_MIN_REQ_LENGTH + 2]; - req_length = ctx->backend->build_request_basis(ctx, - MODBUS_FC_MASK_WRITE_REGISTER, - addr, 0, req); + req_length = ctx->backend->build_request_basis( + ctx, MODBUS_FC_MASK_WRITE_REGISTER, addr, 0, req); /* HACKISH, count is not used */ req_length -= 2; @@ -1510,9 +1572,11 @@ int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint1 /* Write multiple registers from src array to remote device and read multiple registers from remote device to dest array. */ int modbus_write_and_read_registers(modbus_t *ctx, - int write_addr, int write_nb, + int write_addr, + int write_nb, const uint16_t *src, - int read_addr, int read_nb, + int read_addr, + int read_nb, uint16_t *dest) { @@ -1532,7 +1596,8 @@ int modbus_write_and_read_registers(modbus_t *ctx, if (ctx->debug) { fprintf(stderr, "ERROR Too many registers to write (%d > %d)\n", - write_nb, MODBUS_MAX_WR_WRITE_REGISTERS); + write_nb, + MODBUS_MAX_WR_WRITE_REGISTERS); } errno = EMBMDATA; return -1; @@ -1542,14 +1607,14 @@ int modbus_write_and_read_registers(modbus_t *ctx, if (ctx->debug) { fprintf(stderr, "ERROR Too many registers requested (%d > %d)\n", - read_nb, MODBUS_MAX_WR_READ_REGISTERS); + read_nb, + MODBUS_MAX_WR_READ_REGISTERS); } errno = EMBMDATA; return -1; } - req_length = ctx->backend->build_request_basis(ctx, - MODBUS_FC_WRITE_AND_READ_REGISTERS, - read_addr, read_nb, req); + req_length = ctx->backend->build_request_basis( + ctx, MODBUS_FC_WRITE_AND_READ_REGISTERS, read_addr, read_nb, req); req[req_length++] = write_addr >> 8; req[req_length++] = write_addr & 0x00ff; @@ -1578,8 +1643,7 @@ int modbus_write_and_read_registers(modbus_t *ctx, offset = ctx->backend->header_length; for (i = 0; i < rc; i++) { /* shift reg hi_byte to temp OR with lo_byte */ - dest[i] = (rsp[offset + 2 + (i << 1)] << 8) | - rsp[offset + 3 + (i << 1)]; + dest[i] = (rsp[offset + 2 + (i << 1)] << 8) | rsp[offset + 3 + (i << 1)]; } } @@ -1599,8 +1663,8 @@ int modbus_report_slave_id(modbus_t *ctx, int max_dest, uint8_t *dest) return -1; } - req_length = ctx->backend->build_request_basis(ctx, MODBUS_FC_REPORT_SLAVE_ID, - 0, 0, req); + req_length = + ctx->backend->build_request_basis(ctx, MODBUS_FC_REPORT_SLAVE_ID, 0, 0, req); /* HACKISH, addr and count are not used */ req_length -= 4; @@ -1623,7 +1687,7 @@ int modbus_report_slave_id(modbus_t *ctx, int max_dest, uint8_t *dest) /* Byte count, slave id, run indicator status and additional data. Truncate copy to max_dest. */ - for (i=0; i < rc && i < max_dest; i++) { + for (i = 0; i < rc && i < max_dest; i++) { dest[i] = rsp[offset + i]; } } @@ -1672,8 +1736,7 @@ int modbus_get_slave(modbus_t *ctx) return ctx->slave; } -int modbus_set_error_recovery(modbus_t *ctx, - modbus_error_recovery_mode error_recovery) +int modbus_set_error_recovery(modbus_t *ctx, modbus_error_recovery_mode error_recovery) { if (ctx == NULL) { errno = EINVAL; @@ -1721,8 +1784,7 @@ int modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_us int modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec) { - if (ctx == NULL || - (to_sec == 0 && to_usec == 0) || to_usec > 999999) { + if (ctx == NULL || (to_sec == 0 && to_usec == 0) || to_usec > 999999) { errno = EINVAL; return -1; } @@ -1794,7 +1856,8 @@ int modbus_get_header_length(modbus_t *ctx) return ctx->backend->header_length; } -int modbus_enable_quirks(modbus_t *ctx, uint32_t quirks_mask) { +int modbus_enable_quirks(modbus_t *ctx, uint32_t quirks_mask) +{ if (ctx == NULL) { errno = EINVAL; return -1; @@ -1805,7 +1868,8 @@ int modbus_enable_quirks(modbus_t *ctx, uint32_t quirks_mask) { return 0; } -int modbus_disable_quirks(modbus_t *ctx, uint32_t quirks_mask) { +int modbus_disable_quirks(modbus_t *ctx, uint32_t quirks_mask) +{ if (ctx == NULL) { errno = EINVAL; return -1; @@ -1859,15 +1923,18 @@ int modbus_set_debug(modbus_t *ctx, int flag) The modbus_mapping_new_start_address() function shall return the new allocated structure if successful. Otherwise it shall return NULL and set errno to ENOMEM. */ -modbus_mapping_t* modbus_mapping_new_start_address( - unsigned int start_bits, unsigned int nb_bits, - unsigned int start_input_bits, unsigned int nb_input_bits, - unsigned int start_registers, unsigned int nb_registers, - unsigned int start_input_registers, unsigned int nb_input_registers) +modbus_mapping_t *modbus_mapping_new_start_address(unsigned int start_bits, + unsigned int nb_bits, + unsigned int start_input_bits, + unsigned int nb_input_bits, + unsigned int start_registers, + unsigned int nb_registers, + unsigned int start_input_registers, + unsigned int nb_input_registers) { modbus_mapping_t *mb_mapping; - mb_mapping = (modbus_mapping_t *)malloc(sizeof(modbus_mapping_t)); + mb_mapping = (modbus_mapping_t *) malloc(sizeof(modbus_mapping_t)); if (mb_mapping == NULL) { return NULL; } @@ -1879,8 +1946,7 @@ modbus_mapping_t* modbus_mapping_new_start_address( mb_mapping->tab_bits = NULL; } else { /* Negative number raises a POSIX error */ - mb_mapping->tab_bits = - (uint8_t *) malloc(nb_bits * sizeof(uint8_t)); + mb_mapping->tab_bits = (uint8_t *) malloc(nb_bits * sizeof(uint8_t)); if (mb_mapping->tab_bits == NULL) { free(mb_mapping); return NULL; @@ -1894,8 +1960,7 @@ modbus_mapping_t* modbus_mapping_new_start_address( if (nb_input_bits == 0) { mb_mapping->tab_input_bits = NULL; } else { - mb_mapping->tab_input_bits = - (uint8_t *) malloc(nb_input_bits * sizeof(uint8_t)); + mb_mapping->tab_input_bits = (uint8_t *) malloc(nb_input_bits * sizeof(uint8_t)); if (mb_mapping->tab_input_bits == NULL) { free(mb_mapping->tab_bits); free(mb_mapping); @@ -1910,8 +1975,7 @@ modbus_mapping_t* modbus_mapping_new_start_address( if (nb_registers == 0) { mb_mapping->tab_registers = NULL; } else { - mb_mapping->tab_registers = - (uint16_t *) malloc(nb_registers * sizeof(uint16_t)); + mb_mapping->tab_registers = (uint16_t *) malloc(nb_registers * sizeof(uint16_t)); if (mb_mapping->tab_registers == NULL) { free(mb_mapping->tab_input_bits); free(mb_mapping->tab_bits); @@ -1936,15 +2000,16 @@ modbus_mapping_t* modbus_mapping_new_start_address( free(mb_mapping); return NULL; } - memset(mb_mapping->tab_input_registers, 0, - nb_input_registers * sizeof(uint16_t)); + memset(mb_mapping->tab_input_registers, 0, nb_input_registers * sizeof(uint16_t)); } return mb_mapping; } -modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits, - int nb_registers, int nb_input_registers) +modbus_mapping_t *modbus_mapping_new(int nb_bits, + int nb_input_bits, + int nb_registers, + int nb_input_registers) { return modbus_mapping_new_start_address( 0, nb_bits, 0, nb_input_bits, 0, nb_registers, 0, nb_input_registers); diff --git a/src/modbus.h b/src/modbus.h index ee4ad768b..55ef08a0d 100644 --- a/src/modbus.h +++ b/src/modbus.h @@ -7,15 +7,16 @@ #ifndef MODBUS_H #define MODBUS_H +// clang-format off /* Add this for macros that defined unix flavor */ #if (defined(__unix__) || defined(unix)) && !defined(USG) -#include +# include #endif #ifndef _MSC_VER -#include +# include #else -#include "stdint.h" +# include "stdint.h" #endif #include "modbus-version.h" @@ -38,6 +39,7 @@ # define MODBUS_BEGIN_DECLS # define MODBUS_END_DECLS #endif +// clang-format on MODBUS_BEGIN_DECLS @@ -58,28 +60,28 @@ MODBUS_BEGIN_DECLS #endif /* Modbus function codes */ -#define MODBUS_FC_READ_COILS 0x01 -#define MODBUS_FC_READ_DISCRETE_INPUTS 0x02 -#define MODBUS_FC_READ_HOLDING_REGISTERS 0x03 -#define MODBUS_FC_READ_INPUT_REGISTERS 0x04 -#define MODBUS_FC_WRITE_SINGLE_COIL 0x05 -#define MODBUS_FC_WRITE_SINGLE_REGISTER 0x06 -#define MODBUS_FC_READ_EXCEPTION_STATUS 0x07 -#define MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F -#define MODBUS_FC_WRITE_MULTIPLE_REGISTERS 0x10 -#define MODBUS_FC_REPORT_SLAVE_ID 0x11 -#define MODBUS_FC_MASK_WRITE_REGISTER 0x16 -#define MODBUS_FC_WRITE_AND_READ_REGISTERS 0x17 - -#define MODBUS_BROADCAST_ADDRESS 0 +#define MODBUS_FC_READ_COILS 0x01 +#define MODBUS_FC_READ_DISCRETE_INPUTS 0x02 +#define MODBUS_FC_READ_HOLDING_REGISTERS 0x03 +#define MODBUS_FC_READ_INPUT_REGISTERS 0x04 +#define MODBUS_FC_WRITE_SINGLE_COIL 0x05 +#define MODBUS_FC_WRITE_SINGLE_REGISTER 0x06 +#define MODBUS_FC_READ_EXCEPTION_STATUS 0x07 +#define MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F +#define MODBUS_FC_WRITE_MULTIPLE_REGISTERS 0x10 +#define MODBUS_FC_REPORT_SLAVE_ID 0x11 +#define MODBUS_FC_MASK_WRITE_REGISTER 0x16 +#define MODBUS_FC_WRITE_AND_READ_REGISTERS 0x17 + +#define MODBUS_BROADCAST_ADDRESS 0 /* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 1 page 12) * Quantity of Coils to read (2 bytes): 1 to 2000 (0x7D0) * (chapter 6 section 11 page 29) * Quantity of Coils to write (2 bytes): 1 to 1968 (0x7B0) */ -#define MODBUS_MAX_READ_BITS 2000 -#define MODBUS_MAX_WRITE_BITS 1968 +#define MODBUS_MAX_READ_BITS 2000 +#define MODBUS_MAX_WRITE_BITS 1968 /* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 3 page 15) * Quantity of Registers to read (2 bytes): 1 to 125 (0x7D) @@ -88,17 +90,17 @@ MODBUS_BEGIN_DECLS * (chapter 6 section 17 page 38) * Quantity of Registers to write in R/W registers (2 bytes) 1 to 121 (0x79) */ -#define MODBUS_MAX_READ_REGISTERS 125 -#define MODBUS_MAX_WRITE_REGISTERS 123 -#define MODBUS_MAX_WR_WRITE_REGISTERS 121 -#define MODBUS_MAX_WR_READ_REGISTERS 125 +#define MODBUS_MAX_READ_REGISTERS 125 +#define MODBUS_MAX_WRITE_REGISTERS 123 +#define MODBUS_MAX_WR_WRITE_REGISTERS 121 +#define MODBUS_MAX_WR_READ_REGISTERS 125 /* The size of the MODBUS PDU is limited by the size constraint inherited from * the first MODBUS implementation on Serial Line network (max. RS485 ADU = 256 * bytes). Therefore, MODBUS PDU for serial line communication = 256 - Server * address (1 byte) - CRC (2 bytes) = 253 bytes. */ -#define MODBUS_MAX_PDU_LENGTH 253 +#define MODBUS_MAX_PDU_LENGTH 253 /* Consequently: * - RTU MODBUS ADU = 253 bytes + Server address (1 byte) + CRC (2 bytes) = 256 @@ -108,7 +110,7 @@ MODBUS_BEGIN_DECLS * an array of bytes to store responses and it will be compatible with the two * backends. */ -#define MODBUS_MAX_ADU_LENGTH 260 +#define MODBUS_MAX_ADU_LENGTH 260 /* Random number to avoid errno conflicts */ #define MODBUS_ENOBASE 112345678 @@ -141,11 +143,11 @@ enum { #define EMBXGTAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_TARGET) /* Native libmodbus error codes */ -#define EMBBADCRC (EMBXGTAR + 1) -#define EMBBADDATA (EMBXGTAR + 2) -#define EMBBADEXC (EMBXGTAR + 3) -#define EMBUNKEXC (EMBXGTAR + 4) -#define EMBMDATA (EMBXGTAR + 5) +#define EMBBADCRC (EMBXGTAR + 1) +#define EMBBADDATA (EMBXGTAR + 2) +#define EMBBADEXC (EMBXGTAR + 3) +#define EMBUNKEXC (EMBXGTAR + 4) +#define EMBMDATA (EMBXGTAR + 5) #define EMBBADSLAVE (EMBXGTAR + 6) extern const unsigned int libmodbus_version_major; @@ -169,35 +171,39 @@ typedef struct _modbus_mapping_t { uint16_t *tab_registers; } modbus_mapping_t; -typedef enum -{ - MODBUS_ERROR_RECOVERY_NONE = 0, - MODBUS_ERROR_RECOVERY_LINK = (1<<1), - MODBUS_ERROR_RECOVERY_PROTOCOL = (1<<2) +typedef enum { + MODBUS_ERROR_RECOVERY_NONE = 0, + MODBUS_ERROR_RECOVERY_LINK = (1 << 1), + MODBUS_ERROR_RECOVERY_PROTOCOL = (1 << 2) } modbus_error_recovery_mode; -typedef enum -{ - MODBUS_QUIRK_NONE = 0, - MODBUS_QUIRK_MAX_SLAVE = (1<<1), - MODBUS_QUIRK_REPLY_TO_BROADCAST = (1<<2), - MODBUS_QUIRK_ALL = 0xFF +typedef enum { + MODBUS_QUIRK_NONE = 0, + MODBUS_QUIRK_MAX_SLAVE = (1 << 1), + MODBUS_QUIRK_REPLY_TO_BROADCAST = (1 << 2), + MODBUS_QUIRK_ALL = 0xFF } modbus_quirks; -MODBUS_API int modbus_set_slave(modbus_t* ctx, int slave); -MODBUS_API int modbus_get_slave(modbus_t* ctx); -MODBUS_API int modbus_set_error_recovery(modbus_t *ctx, modbus_error_recovery_mode error_recovery); +MODBUS_API int modbus_set_slave(modbus_t *ctx, int slave); +MODBUS_API int modbus_get_slave(modbus_t *ctx); +MODBUS_API int modbus_set_error_recovery(modbus_t *ctx, + modbus_error_recovery_mode error_recovery); MODBUS_API int modbus_set_socket(modbus_t *ctx, int s); MODBUS_API int modbus_get_socket(modbus_t *ctx); -MODBUS_API int modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec); -MODBUS_API int modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec); +MODBUS_API int +modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec); +MODBUS_API int +modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec); -MODBUS_API int modbus_get_byte_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec); +MODBUS_API int +modbus_get_byte_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec); MODBUS_API int modbus_set_byte_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec); -MODBUS_API int modbus_get_indication_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec); -MODBUS_API int modbus_set_indication_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec); +MODBUS_API int +modbus_get_indication_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec); +MODBUS_API int +modbus_set_indication_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec); MODBUS_API int modbus_get_header_length(modbus_t *ctx); @@ -214,37 +220,53 @@ MODBUS_API const char *modbus_strerror(int errnum); MODBUS_API int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest); MODBUS_API int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest); MODBUS_API int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest); -MODBUS_API int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest); +MODBUS_API int +modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest); MODBUS_API int modbus_write_bit(modbus_t *ctx, int coil_addr, int status); MODBUS_API int modbus_write_register(modbus_t *ctx, int reg_addr, const uint16_t value); MODBUS_API int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data); -MODBUS_API int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data); -MODBUS_API int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask); -MODBUS_API int modbus_write_and_read_registers(modbus_t *ctx, int write_addr, int write_nb, - const uint16_t *src, int read_addr, int read_nb, +MODBUS_API int +modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data); +MODBUS_API int +modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask); +MODBUS_API int modbus_write_and_read_registers(modbus_t *ctx, + int write_addr, + int write_nb, + const uint16_t *src, + int read_addr, + int read_nb, uint16_t *dest); MODBUS_API int modbus_report_slave_id(modbus_t *ctx, int max_dest, uint8_t *dest); -MODBUS_API modbus_mapping_t* modbus_mapping_new_start_address( - unsigned int start_bits, unsigned int nb_bits, - unsigned int start_input_bits, unsigned int nb_input_bits, - unsigned int start_registers, unsigned int nb_registers, - unsigned int start_input_registers, unsigned int nb_input_registers); - -MODBUS_API modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits, - int nb_registers, int nb_input_registers); +MODBUS_API modbus_mapping_t * +modbus_mapping_new_start_address(unsigned int start_bits, + unsigned int nb_bits, + unsigned int start_input_bits, + unsigned int nb_input_bits, + unsigned int start_registers, + unsigned int nb_registers, + unsigned int start_input_registers, + unsigned int nb_input_registers); + +MODBUS_API modbus_mapping_t *modbus_mapping_new(int nb_bits, + int nb_input_bits, + int nb_registers, + int nb_input_registers); MODBUS_API void modbus_mapping_free(modbus_mapping_t *mb_mapping); -MODBUS_API int modbus_send_raw_request(modbus_t *ctx, const uint8_t *raw_req, int raw_req_length); +MODBUS_API int +modbus_send_raw_request(modbus_t *ctx, const uint8_t *raw_req, int raw_req_length); MODBUS_API int modbus_receive(modbus_t *ctx, uint8_t *req); MODBUS_API int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp); -MODBUS_API int modbus_reply(modbus_t *ctx, const uint8_t *req, - int req_length, modbus_mapping_t *mb_mapping); -MODBUS_API int modbus_reply_exception(modbus_t *ctx, const uint8_t *req, - unsigned int exception_code); +MODBUS_API int modbus_reply(modbus_t *ctx, + const uint8_t *req, + int req_length, + modbus_mapping_t *mb_mapping); +MODBUS_API int +modbus_reply_exception(modbus_t *ctx, const uint8_t *req, unsigned int exception_code); MODBUS_API int modbus_enable_quirks(modbus_t *ctx, unsigned int quirks_mask); MODBUS_API int modbus_disable_quirks(modbus_t *ctx, unsigned int quirks_mask); @@ -253,40 +275,40 @@ MODBUS_API int modbus_disable_quirks(modbus_t *ctx, unsigned int quirks_mask); **/ #define MODBUS_GET_HIGH_BYTE(data) (((data) >> 8) & 0xFF) -#define MODBUS_GET_LOW_BYTE(data) ((data) & 0xFF) -#define MODBUS_GET_INT64_FROM_INT16(tab_int16, index) \ - (((int64_t)tab_int16[(index) ] << 48) | \ - ((int64_t)tab_int16[(index) + 1] << 32) | \ - ((int64_t)tab_int16[(index) + 2] << 16) | \ - (int64_t)tab_int16[(index) + 3]) +#define MODBUS_GET_LOW_BYTE(data) ((data) &0xFF) +#define MODBUS_GET_INT64_FROM_INT16(tab_int16, index) \ + (((int64_t) tab_int16[(index)] << 48) | ((int64_t) tab_int16[(index) + 1] << 32) | \ + ((int64_t) tab_int16[(index) + 2] << 16) | (int64_t) tab_int16[(index) + 3]) #define MODBUS_GET_INT32_FROM_INT16(tab_int16, index) \ - (((int32_t)tab_int16[(index) ] << 16) | \ - (int32_t)tab_int16[(index) + 1]) + (((int32_t) tab_int16[(index)] << 16) | (int32_t) tab_int16[(index) + 1]) #define MODBUS_GET_INT16_FROM_INT8(tab_int8, index) \ - (((int16_t)tab_int8[(index) ] << 8) | \ - (int16_t)tab_int8[(index) + 1]) -#define MODBUS_SET_INT16_TO_INT8(tab_int8, index, value) \ - do { \ - ((int8_t*)(tab_int8))[(index) ] = (int8_t)((value) >> 8); \ - ((int8_t*)(tab_int8))[(index) + 1] = (int8_t)(value); \ - } while (0) -#define MODBUS_SET_INT32_TO_INT16(tab_int16, index, value) \ - do { \ - ((int16_t*)(tab_int16))[(index) ] = (int16_t)((value) >> 16); \ - ((int16_t*)(tab_int16))[(index) + 1] = (int16_t)(value); \ - } while (0) -#define MODBUS_SET_INT64_TO_INT16(tab_int16, index, value) \ - do { \ - ((int16_t*)(tab_int16))[(index) ] = (int16_t)((value) >> 48); \ - ((int16_t*)(tab_int16))[(index) + 1] = (int16_t)((value) >> 32); \ - ((int16_t*)(tab_int16))[(index) + 2] = (int16_t)((value) >> 16); \ - ((int16_t*)(tab_int16))[(index) + 3] = (int16_t)(value); \ - } while (0) + (((int16_t) tab_int8[(index)] << 8) | (int16_t) tab_int8[(index) + 1]) +#define MODBUS_SET_INT16_TO_INT8(tab_int8, index, value) \ + do { \ + ((int8_t *) (tab_int8))[(index)] = (int8_t) ((value) >> 8); \ + ((int8_t *) (tab_int8))[(index) + 1] = (int8_t) (value); \ + } while (0) +#define MODBUS_SET_INT32_TO_INT16(tab_int16, index, value) \ + do { \ + ((int16_t *) (tab_int16))[(index)] = (int16_t) ((value) >> 16); \ + ((int16_t *) (tab_int16))[(index) + 1] = (int16_t) (value); \ + } while (0) +#define MODBUS_SET_INT64_TO_INT16(tab_int16, index, value) \ + do { \ + ((int16_t *) (tab_int16))[(index)] = (int16_t) ((value) >> 48); \ + ((int16_t *) (tab_int16))[(index) + 1] = (int16_t) ((value) >> 32); \ + ((int16_t *) (tab_int16))[(index) + 2] = (int16_t) ((value) >> 16); \ + ((int16_t *) (tab_int16))[(index) + 3] = (int16_t) (value); \ + } while (0) MODBUS_API void modbus_set_bits_from_byte(uint8_t *dest, int idx, const uint8_t value); -MODBUS_API void modbus_set_bits_from_bytes(uint8_t *dest, int idx, unsigned int nb_bits, - const uint8_t *tab_byte); -MODBUS_API uint8_t modbus_get_byte_from_bits(const uint8_t *src, int idx, unsigned int nb_bits); +MODBUS_API void modbus_set_bits_from_bytes(uint8_t *dest, + int idx, + unsigned int nb_bits, + const uint8_t *tab_byte); +MODBUS_API uint8_t modbus_get_byte_from_bits(const uint8_t *src, + int idx, + unsigned int nb_bits); MODBUS_API float modbus_get_float(const uint16_t *src); MODBUS_API float modbus_get_float_abcd(const uint16_t *src); MODBUS_API float modbus_get_float_dcba(const uint16_t *src); @@ -299,9 +321,9 @@ MODBUS_API void modbus_set_float_dcba(float f, uint16_t *dest); MODBUS_API void modbus_set_float_badc(float f, uint16_t *dest); MODBUS_API void modbus_set_float_cdab(float f, uint16_t *dest); -#include "modbus-tcp.h" #include "modbus-rtu.h" +#include "modbus-tcp.h" MODBUS_END_DECLS -#endif /* MODBUS_H */ +#endif /* MODBUS_H */ diff --git a/src/win32/config.h.win32 b/src/win32/config.h.win32 index 02ac4448b..cc1d9117c 100644 --- a/src/win32/config.h.win32 +++ b/src/win32/config.h.win32 @@ -121,7 +121,7 @@ /* #undef HAVE_WORKING_VFORK */ /* Define to the sub-directory in which libtool stores uninstalled libraries. - */ + */ /* #undef LT_OBJDIR */ /* Name of package */ diff --git a/tests/bandwidth-client.c b/tests/bandwidth-client.c index f579d8891..842462b59 100644 --- a/tests/bandwidth-client.c +++ b/tests/bandwidth-client.c @@ -6,13 +6,13 @@ #include #ifndef _MSC_VER -#include #include +#include #endif -#include +#include #include +#include #include -#include #include @@ -59,7 +59,8 @@ int main(int argc, char *argv[]) use_backend = RTU; n_loop = 100; } else { - printf("Usage:\n %s [tcp|rtu] - Modbus client to measure data bandwidth\n\n", argv[0]); + printf("Usage:\n %s [tcp|rtu] - Modbus client to measure data bandwidth\n\n", + argv[0]); exit(1); } } else { @@ -75,8 +76,7 @@ int main(int argc, char *argv[]) modbus_set_slave(ctx, 1); } if (modbus_connect(ctx) == -1) { - fprintf(stderr, "Connection failed: %s\n", - modbus_strerror(errno)); + fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); modbus_free(ctx); return -1; } @@ -93,7 +93,7 @@ int main(int argc, char *argv[]) nb_points = MODBUS_MAX_READ_BITS; start = gettime_ms(); - for (i=0; i -#include -#include -#include #include #include +#include +#include +#include +#include #include #if defined(_WIN32) #include #else +#include +#include #include #include -#include -#include #endif -#define NB_CONNECTION 5 +#define NB_CONNECTION 5 static modbus_t *ctx = NULL; static modbus_mapping_t *mb_mapping; @@ -52,11 +52,10 @@ int main(void) ctx = modbus_new_tcp("127.0.0.1", 1502); - mb_mapping = modbus_mapping_new(MODBUS_MAX_READ_BITS, 0, - MODBUS_MAX_READ_REGISTERS, 0); + mb_mapping = + modbus_mapping_new(MODBUS_MAX_READ_BITS, 0, MODBUS_MAX_READ_REGISTERS, 0); if (mb_mapping == NULL) { - fprintf(stderr, "Failed to allocate the mapping: %s\n", - modbus_strerror(errno)); + fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno)); modbus_free(ctx); return -1; } @@ -80,7 +79,7 @@ int main(void) for (;;) { rdset = refset; - if (select(fdmax+1, &rdset, NULL, NULL, NULL) == -1) { + if (select(fdmax + 1, &rdset, NULL, NULL, NULL) == -1) { perror("Server select() failure."); close_sigint(1); } @@ -102,7 +101,7 @@ int main(void) /* Handle new connections */ addrlen = sizeof(clientaddr); memset(&clientaddr, 0, sizeof(clientaddr)); - newfd = accept(server_socket, (struct sockaddr *)&clientaddr, &addrlen); + newfd = accept(server_socket, (struct sockaddr *) &clientaddr, &addrlen); if (newfd == -1) { perror("Server accept() error"); } else { @@ -113,7 +112,9 @@ int main(void) fdmax = newfd; } printf("New connection from %s:%d on socket %d\n", - inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port, newfd); + inet_ntoa(clientaddr.sin_addr), + clientaddr.sin_port, + newfd); } } else { modbus_set_socket(ctx, master_socket); diff --git a/tests/bandwidth-server-one.c b/tests/bandwidth-server-one.c index bdb488d58..c7ef1ab20 100644 --- a/tests/bandwidth-server-one.c +++ b/tests/bandwidth-server-one.c @@ -8,9 +8,9 @@ #ifndef _MSC_VER #include #endif -#include -#include #include +#include +#include #include @@ -31,14 +31,15 @@ int main(int argc, char *argv[]) int rc; int use_backend; - /* TCP */ + /* TCP */ if (argc > 1) { if (strcmp(argv[1], "tcp") == 0) { use_backend = TCP; } else if (strcmp(argv[1], "rtu") == 0) { use_backend = RTU; } else { - printf("Usage:\n %s [tcp|rtu] - Modbus client to measure data bandwidth\n\n", argv[0]); + printf("Usage:\n %s [tcp|rtu] - Modbus client to measure data bandwidth\n\n", + argv[0]); exit(1); } } else { @@ -57,22 +58,21 @@ int main(int argc, char *argv[]) modbus_connect(ctx); } - mb_mapping = modbus_mapping_new(MODBUS_MAX_READ_BITS, 0, - MODBUS_MAX_READ_REGISTERS, 0); + mb_mapping = + modbus_mapping_new(MODBUS_MAX_READ_BITS, 0, MODBUS_MAX_READ_REGISTERS, 0); if (mb_mapping == NULL) { - fprintf(stderr, "Failed to allocate the mapping: %s\n", - modbus_strerror(errno)); + fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno)); modbus_free(ctx); return -1; } - for(;;) { + for (;;) { uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH]; rc = modbus_receive(ctx, query); if (rc > 0) { modbus_reply(ctx, query, rc, mb_mapping); - } else if (rc == -1) { + } else if (rc == -1) { /* Connection closed by the client or error */ break; } diff --git a/tests/random-test-client.c b/tests/random-test-client.c index 04da17d16..c2ccf63fd 100644 --- a/tests/random-test-client.c +++ b/tests/random-test-client.c @@ -8,9 +8,9 @@ #ifndef _MSC_VER #include #endif -#include -#include #include +#include +#include #include @@ -27,10 +27,10 @@ All these functions are called with random values on a address range defined by the following defines. */ -#define LOOP 1 -#define SERVER_ID 17 -#define ADDRESS_START 0 -#define ADDRESS_END 99 +#define LOOP 1 +#define SERVER_ID 17 +#define ADDRESS_START 0 +#define ADDRESS_END 99 /* At each loop, the program works in the range ADDRESS_START to * ADDRESS_END then ADDRESS_START + 1 to ADDRESS_END and so on. @@ -50,18 +50,17 @@ int main(void) uint16_t *tab_rp_registers; /* RTU */ -/* - ctx = modbus_new_rtu("/dev/ttyUSB0", 19200, 'N', 8, 1); - modbus_set_slave(ctx, SERVER_ID); -*/ + /* + ctx = modbus_new_rtu("/dev/ttyUSB0", 19200, 'N', 8, 1); + modbus_set_slave(ctx, SERVER_ID); + */ /* TCP */ ctx = modbus_new_tcp("127.0.0.1", 1502); modbus_set_debug(ctx, TRUE); if (modbus_connect(ctx) == -1) { - fprintf(stderr, "Connection failed: %s\n", - modbus_strerror(errno)); + fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); modbus_free(ctx); return -1; } @@ -90,8 +89,8 @@ int main(void) int i; /* Random numbers (short) */ - for (i=0; i #endif -#include #include +#include #include @@ -24,8 +24,7 @@ int main(void) mb_mapping = modbus_mapping_new(500, 500, 500, 500); if (mb_mapping == NULL) { - fprintf(stderr, "Failed to allocate the mapping: %s\n", - modbus_strerror(errno)); + fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno)); modbus_free(ctx); return -1; } diff --git a/tests/unit-test-client.c b/tests/unit-test-client.c index f8c4ab8ce..915a17b79 100644 --- a/tests/unit-test-client.c +++ b/tests/unit-test-client.c @@ -4,12 +4,12 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include -#include -#include -#include #include #include +#include +#include +#include +#include #include "unit-test.h" @@ -22,31 +22,37 @@ enum { }; int test_server(modbus_t *ctx, int use_backend); -int send_crafted_request(modbus_t *ctx, int function, - uint8_t *req, int req_size, - uint16_t max_value, uint16_t bytes, - int backend_length, int backend_offset); +int send_crafted_request(modbus_t *ctx, + int function, + uint8_t *req, + int req_size, + uint16_t max_value, + uint16_t bytes, + int backend_length, + int backend_offset); int equal_dword(uint16_t *tab_reg, const uint32_t value); int is_memory_equal(const void *s1, const void *s2, size_t size); -#define BUG_REPORT(_cond, _format, _args ...) \ - printf("\nLine %d: assertion error for '%s': " _format "\n", __LINE__, # _cond, ## _args) +#define BUG_REPORT(_cond, _format, _args...) \ + printf("\nLine %d: assertion error for '%s': " _format "\n", __LINE__, #_cond, ##_args) -#define ASSERT_TRUE(_cond, _format, __args...) { \ - if (_cond) { \ - printf("OK\n"); \ - } else { \ - BUG_REPORT(_cond, _format, ## __args); \ - goto close; \ - } \ -}; +#define ASSERT_TRUE(_cond, _format, __args...) \ + { \ + if (_cond) { \ + printf("OK\n"); \ + } else { \ + BUG_REPORT(_cond, _format, ##__args); \ + goto close; \ + } \ + }; int is_memory_equal(const void *s1, const void *s2, size_t size) { return (memcmp(s1, s2, size) == 0); } -int equal_dword(uint16_t *tab_reg, const uint32_t value) { +int equal_dword(uint16_t *tab_reg, const uint32_t value) +{ return ((tab_reg[0] == (value >> 16)) && (tab_reg[1] == (value & 0xFFFF))); } @@ -80,7 +86,8 @@ int main(int argc, char *argv[]) } else if (strcmp(argv[1], "rtu") == 0) { use_backend = RTU; } else { - printf("Usage:\n %s [tcp|tcppi|rtu] - Modbus client for unit testing\n\n", argv[0]); + printf("Usage:\n %s [tcp|tcppi|rtu] - Modbus client for unit testing\n\n", + argv[0]); exit(1); } } else { @@ -100,9 +107,8 @@ int main(int argc, char *argv[]) return -1; } modbus_set_debug(ctx, TRUE); - modbus_set_error_recovery(ctx, - MODBUS_ERROR_RECOVERY_LINK | - MODBUS_ERROR_RECOVERY_PROTOCOL); + modbus_set_error_recovery( + ctx, MODBUS_ERROR_RECOVERY_LINK | MODBUS_ERROR_RECOVERY_PROTOCOL); if (use_backend == RTU) { modbus_set_slave(ctx, SERVER_ID); @@ -121,8 +127,8 @@ int main(int argc, char *argv[]) memset(tab_rp_bits, 0, nb_points * sizeof(uint8_t)); /* Allocate and initialize the memory to store the registers */ - nb_points = (UT_REGISTERS_NB > UT_INPUT_REGISTERS_NB) ? - UT_REGISTERS_NB : UT_INPUT_REGISTERS_NB; + nb_points = (UT_REGISTERS_NB > UT_INPUT_REGISTERS_NB) ? UT_REGISTERS_NB + : UT_INPUT_REGISTERS_NB; tab_rp_registers = (uint16_t *) malloc(nb_points * sizeof(uint16_t)); memset(tab_rp_registers, 0, nb_points * sizeof(uint16_t)); @@ -131,7 +137,8 @@ int main(int argc, char *argv[]) printf("1/1 No response timeout modification on connect: "); modbus_get_response_timeout(ctx, &new_response_to_sec, &new_response_to_usec); ASSERT_TRUE(old_response_to_sec == new_response_to_sec && - old_response_to_usec == new_response_to_usec, ""); + old_response_to_usec == new_response_to_usec, + ""); printf("\nTEST WRITE/READ:\n"); @@ -145,8 +152,7 @@ int main(int argc, char *argv[]) rc = modbus_read_bits(ctx, UT_BITS_ADDRESS, 1, tab_rp_bits); printf("2/2 modbus_read_bits: "); ASSERT_TRUE(rc == 1, "FAILED (nb points %d)\n", rc); - ASSERT_TRUE(tab_rp_bits[0] == ON, "FAILED (%0X != %0X)\n", - tab_rp_bits[0], ON); + ASSERT_TRUE(tab_rp_bits[0] == ON, "FAILED (%0X != %0X)\n", tab_rp_bits[0], ON); /* End single */ @@ -169,9 +175,9 @@ int main(int argc, char *argv[]) while (nb_points > 0) { int nb_bits = (nb_points > 8) ? 8 : nb_points; - value = modbus_get_byte_from_bits(tab_rp_bits, i*8, nb_bits); - ASSERT_TRUE(value == UT_BITS_TAB[i], "FAILED (%0X != %0X)\n", - value, UT_BITS_TAB[i]); + value = modbus_get_byte_from_bits(tab_rp_bits, i * 8, nb_bits); + ASSERT_TRUE( + value == UT_BITS_TAB[i], "FAILED (%0X != %0X)\n", value, UT_BITS_TAB[i]); nb_points -= nb_bits; i++; @@ -180,8 +186,8 @@ int main(int argc, char *argv[]) /* End of multiple bits */ /** DISCRETE INPUTS **/ - rc = modbus_read_input_bits(ctx, UT_INPUT_BITS_ADDRESS, - UT_INPUT_BITS_NB, tab_rp_bits); + rc = + modbus_read_input_bits(ctx, UT_INPUT_BITS_ADDRESS, UT_INPUT_BITS_NB, tab_rp_bits); printf("1/1 modbus_read_input_bits: "); ASSERT_TRUE(rc == UT_INPUT_BITS_NB, "FAILED (nb points %d)\n", rc); @@ -189,9 +195,11 @@ int main(int argc, char *argv[]) nb_points = UT_INPUT_BITS_NB; while (nb_points > 0) { int nb_bits = (nb_points > 8) ? 8 : nb_points; - value = modbus_get_byte_from_bits(tab_rp_bits, i*8, nb_bits); - ASSERT_TRUE(value == UT_INPUT_BITS_TAB[i], "FAILED (%0X != %0X)\n", - value, UT_INPUT_BITS_TAB[i]); + value = modbus_get_byte_from_bits(tab_rp_bits, i * 8, nb_bits); + ASSERT_TRUE(value == UT_INPUT_BITS_TAB[i], + "FAILED (%0X != %0X)\n", + value, + UT_INPUT_BITS_TAB[i]); nb_points -= nb_bits; i++; @@ -205,39 +213,39 @@ int main(int argc, char *argv[]) printf("1/2 modbus_write_register: "); ASSERT_TRUE(rc == 1, ""); - rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, - 1, tab_rp_registers); + rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, 1, tab_rp_registers); printf("2/2 modbus_read_registers: "); ASSERT_TRUE(rc == 1, "FAILED (nb points %d)\n", rc); - ASSERT_TRUE(tab_rp_registers[0] == 0x1234, "FAILED (%0X != %0X)\n", - tab_rp_registers[0], 0x1234); + ASSERT_TRUE(tab_rp_registers[0] == 0x1234, + "FAILED (%0X != %0X)\n", + tab_rp_registers[0], + 0x1234); /* End of single register */ /* Many registers */ - rc = modbus_write_registers(ctx, UT_REGISTERS_ADDRESS, - UT_REGISTERS_NB, UT_REGISTERS_TAB); + rc = modbus_write_registers( + ctx, UT_REGISTERS_ADDRESS, UT_REGISTERS_NB, UT_REGISTERS_TAB); printf("1/5 modbus_write_registers: "); ASSERT_TRUE(rc == UT_REGISTERS_NB, ""); - rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, - UT_REGISTERS_NB, tab_rp_registers); + rc = modbus_read_registers( + ctx, UT_REGISTERS_ADDRESS, UT_REGISTERS_NB, tab_rp_registers); printf("2/5 modbus_read_registers: "); ASSERT_TRUE(rc == UT_REGISTERS_NB, "FAILED (nb points %d)\n", rc); - for (i=0; i < UT_REGISTERS_NB; i++) { + for (i = 0; i < UT_REGISTERS_NB; i++) { ASSERT_TRUE(tab_rp_registers[i] == UT_REGISTERS_TAB[i], "FAILED (%0X != %0X)\n", - tab_rp_registers[i], UT_REGISTERS_TAB[i]); + tab_rp_registers[i], + UT_REGISTERS_TAB[i]); } - rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, - 0, tab_rp_registers); + rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, 0, tab_rp_registers); printf("3/5 modbus_read_registers (0): "); ASSERT_TRUE(rc == -1, "FAILED (nb_points %d)\n", rc); - nb_points = (UT_REGISTERS_NB > - UT_INPUT_REGISTERS_NB) ? - UT_REGISTERS_NB : UT_INPUT_REGISTERS_NB; + nb_points = (UT_REGISTERS_NB > UT_INPUT_REGISTERS_NB) ? UT_REGISTERS_NB + : UT_INPUT_REGISTERS_NB; memset(tab_rp_registers, 0, nb_points * sizeof(uint16_t)); /* Write registers to zero from tab_rp_registers and store read registers @@ -251,32 +259,32 @@ int main(int argc, char *argv[]) UT_REGISTERS_NB, tab_rp_registers); printf("4/5 modbus_write_and_read_registers: "); - ASSERT_TRUE(rc == UT_REGISTERS_NB, "FAILED (nb points %d != %d)\n", - rc, UT_REGISTERS_NB); + ASSERT_TRUE( + rc == UT_REGISTERS_NB, "FAILED (nb points %d != %d)\n", rc, UT_REGISTERS_NB); ASSERT_TRUE(tab_rp_registers[0] == UT_REGISTERS_TAB[0], "FAILED (%0X != %0X)\n", - tab_rp_registers[0], UT_REGISTERS_TAB[0]); + tab_rp_registers[0], + UT_REGISTERS_TAB[0]); - for (i=1; i < UT_REGISTERS_NB; i++) { - ASSERT_TRUE(tab_rp_registers[i] == 0, "FAILED (%0X != %0X)\n", - tab_rp_registers[i], 0); + for (i = 1; i < UT_REGISTERS_NB; i++) { + ASSERT_TRUE( + tab_rp_registers[i] == 0, "FAILED (%0X != %0X)\n", tab_rp_registers[i], 0); } /* End of many registers */ - /** INPUT REGISTERS **/ - rc = modbus_read_input_registers(ctx, UT_INPUT_REGISTERS_ADDRESS, - UT_INPUT_REGISTERS_NB, - tab_rp_registers); + rc = modbus_read_input_registers( + ctx, UT_INPUT_REGISTERS_ADDRESS, UT_INPUT_REGISTERS_NB, tab_rp_registers); printf("1/1 modbus_read_input_registers: "); ASSERT_TRUE(rc == UT_INPUT_REGISTERS_NB, "FAILED (nb points %d)\n", rc); - for (i=0; i < UT_INPUT_REGISTERS_NB; i++) { + for (i = 0; i < UT_INPUT_REGISTERS_NB; i++) { ASSERT_TRUE(tab_rp_registers[i] == UT_INPUT_REGISTERS_TAB[i], "FAILED (%0X != %0X)\n", - tab_rp_registers[i], UT_INPUT_REGISTERS_TAB[i]); + tab_rp_registers[i], + UT_INPUT_REGISTERS_TAB[i]); } /* MASKS */ @@ -285,33 +293,36 @@ int main(int argc, char *argv[]) rc = modbus_mask_write_register(ctx, UT_REGISTERS_ADDRESS, 0xF2, 0x25); ASSERT_TRUE(rc != -1, "FAILED (%x == -1)\n", rc); rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, 1, tab_rp_registers); - ASSERT_TRUE(tab_rp_registers[0] == 0x17, - "FAILED (%0X != %0X)\n", - tab_rp_registers[0], 0x17); + ASSERT_TRUE( + tab_rp_registers[0] == 0x17, "FAILED (%0X != %0X)\n", tab_rp_registers[0], 0x17); printf("\nTEST FLOATS\n"); /** FLOAT **/ printf("1/4 Set/get float ABCD: "); modbus_set_float_abcd(UT_REAL, tab_rp_registers); - ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_ABCD_SET, 4), "FAILED Set float ABCD"); + ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_ABCD_SET, 4), + "FAILED Set float ABCD"); real = modbus_get_float_abcd(UT_IREAL_ABCD_GET); ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); printf("2/4 Set/get float DCBA: "); modbus_set_float_dcba(UT_REAL, tab_rp_registers); - ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_DCBA_SET, 4), "FAILED Set float DCBA"); + ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_DCBA_SET, 4), + "FAILED Set float DCBA"); real = modbus_get_float_dcba(UT_IREAL_DCBA_GET); ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); printf("3/4 Set/get float BADC: "); modbus_set_float_badc(UT_REAL, tab_rp_registers); - ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_BADC_SET, 4), "FAILED Set float BADC"); + ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_BADC_SET, 4), + "FAILED Set float BADC"); real = modbus_get_float_badc(UT_IREAL_BADC_GET); ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); printf("4/4 Set/get float CDAB: "); modbus_set_float_cdab(UT_REAL, tab_rp_registers); - ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_CDAB_SET, 4), "FAILED Set float CDAB"); + ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_CDAB_SET, 4), + "FAILED Set float CDAB"); real = modbus_get_float_cdab(UT_IREAL_CDAB_GET); ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); @@ -335,8 +346,8 @@ int main(int argc, char *argv[]) printf("* modbus_read_input_bits (0): "); ASSERT_TRUE(rc == -1 && errno == EMBXILADD, ""); - rc = modbus_read_input_bits(ctx, UT_INPUT_BITS_ADDRESS, - UT_INPUT_BITS_NB + 1, tab_rp_bits); + rc = modbus_read_input_bits( + ctx, UT_INPUT_BITS_ADDRESS, UT_INPUT_BITS_NB + 1, tab_rp_bits); printf("* modbus_read_input_bits (max): "); ASSERT_TRUE(rc == -1 && errno == EMBXILADD, ""); @@ -344,8 +355,8 @@ int main(int argc, char *argv[]) printf("* modbus_read_registers (0): "); ASSERT_TRUE(rc == -1 && errno == EMBXILADD, ""); - rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, - UT_REGISTERS_NB_MAX + 1, tab_rp_registers); + rc = modbus_read_registers( + ctx, UT_REGISTERS_ADDRESS, UT_REGISTERS_NB_MAX + 1, tab_rp_registers); printf("* modbus_read_registers (max): "); ASSERT_TRUE(rc == -1 && errno == EMBXILADD, ""); @@ -353,9 +364,8 @@ int main(int argc, char *argv[]) printf("* modbus_read_input_registers (0): "); ASSERT_TRUE(rc == -1 && errno == EMBXILADD, ""); - rc = modbus_read_input_registers(ctx, UT_INPUT_REGISTERS_ADDRESS, - UT_INPUT_REGISTERS_NB + 1, - tab_rp_registers); + rc = modbus_read_input_registers( + ctx, UT_INPUT_REGISTERS_ADDRESS, UT_INPUT_REGISTERS_NB + 1, tab_rp_registers); printf("* modbus_read_input_registers (max): "); ASSERT_TRUE(rc == -1 && errno == EMBXILADD, ""); @@ -371,8 +381,7 @@ int main(int argc, char *argv[]) printf("* modbus_write_coils (0): "); ASSERT_TRUE(rc == -1 && errno == EMBXILADD, ""); - rc = modbus_write_bits(ctx, UT_BITS_ADDRESS + UT_BITS_NB, - UT_BITS_NB, tab_rp_bits); + rc = modbus_write_bits(ctx, UT_BITS_ADDRESS + UT_BITS_NB, UT_BITS_NB, tab_rp_bits); printf("* modbus_write_coils (max): "); ASSERT_TRUE(rc == -1 && errno == EMBXILADD, ""); @@ -380,8 +389,8 @@ int main(int argc, char *argv[]) printf("* modbus_write_register (0): "); ASSERT_TRUE(rc == -1 && errno == EMBXILADD, ""); - rc = modbus_write_register(ctx, UT_REGISTERS_ADDRESS + UT_REGISTERS_NB_MAX, - tab_rp_registers[0]); + rc = modbus_write_register( + ctx, UT_REGISTERS_ADDRESS + UT_REGISTERS_NB_MAX, tab_rp_registers[0]); printf("* modbus_write_register (max): "); ASSERT_TRUE(rc == -1 && errno == EMBXILADD, ""); @@ -389,8 +398,10 @@ int main(int argc, char *argv[]) printf("* modbus_write_registers (0): "); ASSERT_TRUE(rc == -1 && errno == EMBXILADD, ""); - rc = modbus_write_registers(ctx, UT_REGISTERS_ADDRESS + UT_REGISTERS_NB_MAX, - UT_REGISTERS_NB, tab_rp_registers); + rc = modbus_write_registers(ctx, + UT_REGISTERS_ADDRESS + UT_REGISTERS_NB_MAX, + UT_REGISTERS_NB, + tab_rp_registers); printf("* modbus_write_registers (max): "); ASSERT_TRUE(rc == -1 && errno == EMBXILADD, ""); @@ -398,56 +409,54 @@ int main(int argc, char *argv[]) printf("* modbus_mask_write_registers (0): "); ASSERT_TRUE(rc == -1 && errno == EMBXILADD, ""); - rc = modbus_mask_write_register(ctx, UT_REGISTERS_ADDRESS + UT_REGISTERS_NB_MAX, - 0xF2, 0x25); + rc = modbus_mask_write_register( + ctx, UT_REGISTERS_ADDRESS + UT_REGISTERS_NB_MAX, 0xF2, 0x25); printf("* modbus_mask_write_registers (max): "); ASSERT_TRUE(rc == -1 && errno == EMBXILADD, ""); - rc = modbus_write_and_read_registers(ctx, 0, 1, tab_rp_registers, 0, 1, tab_rp_registers); + rc = modbus_write_and_read_registers( + ctx, 0, 1, tab_rp_registers, 0, 1, tab_rp_registers); printf("* modbus_write_and_read_registers (0): "); ASSERT_TRUE(rc == -1 && errno == EMBXILADD, ""); rc = modbus_write_and_read_registers(ctx, UT_REGISTERS_ADDRESS + UT_REGISTERS_NB_MAX, - UT_REGISTERS_NB, tab_rp_registers, + UT_REGISTERS_NB, + tab_rp_registers, UT_REGISTERS_ADDRESS + UT_REGISTERS_NB_MAX, - UT_REGISTERS_NB, tab_rp_registers); + UT_REGISTERS_NB, + tab_rp_registers); printf("* modbus_write_and_read_registers (max): "); ASSERT_TRUE(rc == -1 && errno == EMBXILADD, ""); /** TOO MANY DATA **/ printf("\nTEST TOO MANY DATA ERROR:\n"); - rc = modbus_read_bits(ctx, UT_BITS_ADDRESS, - MODBUS_MAX_READ_BITS + 1, tab_rp_bits); + rc = modbus_read_bits(ctx, UT_BITS_ADDRESS, MODBUS_MAX_READ_BITS + 1, tab_rp_bits); printf("* modbus_read_bits: "); ASSERT_TRUE(rc == -1 && errno == EMBMDATA, ""); - rc = modbus_read_input_bits(ctx, UT_INPUT_BITS_ADDRESS, - MODBUS_MAX_READ_BITS + 1, tab_rp_bits); + rc = modbus_read_input_bits( + ctx, UT_INPUT_BITS_ADDRESS, MODBUS_MAX_READ_BITS + 1, tab_rp_bits); printf("* modbus_read_input_bits: "); ASSERT_TRUE(rc == -1 && errno == EMBMDATA, ""); - rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, - MODBUS_MAX_READ_REGISTERS + 1, - tab_rp_registers); + rc = modbus_read_registers( + ctx, UT_REGISTERS_ADDRESS, MODBUS_MAX_READ_REGISTERS + 1, tab_rp_registers); printf("* modbus_read_registers: "); ASSERT_TRUE(rc == -1 && errno == EMBMDATA, ""); - rc = modbus_read_input_registers(ctx, UT_INPUT_REGISTERS_ADDRESS, - MODBUS_MAX_READ_REGISTERS + 1, - tab_rp_registers); + rc = modbus_read_input_registers( + ctx, UT_INPUT_REGISTERS_ADDRESS, MODBUS_MAX_READ_REGISTERS + 1, tab_rp_registers); printf("* modbus_read_input_registers: "); ASSERT_TRUE(rc == -1 && errno == EMBMDATA, ""); - rc = modbus_write_bits(ctx, UT_BITS_ADDRESS, - MODBUS_MAX_WRITE_BITS + 1, tab_rp_bits); + rc = modbus_write_bits(ctx, UT_BITS_ADDRESS, MODBUS_MAX_WRITE_BITS + 1, tab_rp_bits); printf("* modbus_write_bits: "); ASSERT_TRUE(rc == -1 && errno == EMBMDATA, ""); - rc = modbus_write_registers(ctx, UT_REGISTERS_ADDRESS, - MODBUS_MAX_WRITE_REGISTERS + 1, - tab_rp_registers); + rc = modbus_write_registers( + ctx, UT_REGISTERS_ADDRESS, MODBUS_MAX_WRITE_REGISTERS + 1, tab_rp_registers); printf("* modbus_write_registers: "); ASSERT_TRUE(rc == -1 && errno == EMBMDATA, ""); @@ -473,15 +482,15 @@ int main(int argc, char *argv[]) printf("\nTEST SLAVE REPLY:\n"); modbus_set_slave(ctx, INVALID_SERVER_ID); - rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, - UT_REGISTERS_NB, tab_rp_registers); + rc = modbus_read_registers( + ctx, UT_REGISTERS_ADDRESS, UT_REGISTERS_NB, tab_rp_registers); if (use_backend == RTU) { const int RAW_REQ_LENGTH = 6; - uint8_t raw_req[] = { INVALID_SERVER_ID, 0x03, 0x00, 0x01, 0x01, 0x01 }; + uint8_t raw_req[] = {INVALID_SERVER_ID, 0x03, 0x00, 0x01, 0x01, 0x01}; /* Too many points */ - uint8_t raw_invalid_req[] = { INVALID_SERVER_ID, 0x03, 0x00, 0x01, 0xFF, 0xFF }; + uint8_t raw_invalid_req[] = {INVALID_SERVER_ID, 0x03, 0x00, 0x01, 0xFF, 0xFF}; const int RAW_RSP_LENGTH = 7; - uint8_t raw_rsp[] = { INVALID_SERVER_ID, 0x03, 0x04, 0, 0, 0, 0 }; + uint8_t raw_rsp[] = {INVALID_SERVER_ID, 0x03, 0x04, 0, 0, 0, 0}; uint8_t rsp[MODBUS_RTU_MAX_ADU_LENGTH]; /* No response in RTU mode */ @@ -491,7 +500,6 @@ int main(int argc, char *argv[]) /* The slave raises a timeout on a confirmation to ignore because if an * indication for another slave is received, a confirmation must follow */ - /* Send a pair of indication/confirmation to the slave with a different * slave ID to simulate a communication on a RS485 bus. At first, the * slave will see the indication message then the confirmation, and it must @@ -515,8 +523,8 @@ int main(int argc, char *argv[]) rc = modbus_set_slave(ctx, MODBUS_BROADCAST_ADDRESS); ASSERT_TRUE(rc == 0, "Invalid broadcast address"); - rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, - UT_REGISTERS_NB, tab_rp_registers); + rc = modbus_read_registers( + ctx, UT_REGISTERS_ADDRESS, UT_REGISTERS_NB, tab_rp_registers); printf("2/3 No reply after a broadcast query: "); ASSERT_TRUE(rc == -1 && errno == ETIMEDOUT, ""); } else { @@ -527,8 +535,8 @@ int main(int argc, char *argv[]) rc = modbus_set_slave(ctx, MODBUS_BROADCAST_ADDRESS); ASSERT_TRUE(rc == 0, "Invalid broacast address"); - rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, - UT_REGISTERS_NB, tab_rp_registers); + rc = modbus_read_registers( + ctx, UT_REGISTERS_ADDRESS, UT_REGISTERS_NB, tab_rp_registers); printf("2/3 Reply after a query with unit id == 0: "); ASSERT_TRUE(rc == UT_REGISTERS_NB, ""); } @@ -537,8 +545,8 @@ int main(int argc, char *argv[]) modbus_set_slave(ctx, old_slave); printf("3/3 Response with an invalid TID or slave: "); - rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS_INVALID_TID_OR_SLAVE, - 1, tab_rp_registers); + rc = modbus_read_registers( + ctx, UT_REGISTERS_ADDRESS_INVALID_TID_OR_SLAVE, 1, tab_rp_registers); ASSERT_TRUE(rc == -1, ""); printf("1/2 Report slave ID truncated: \n"); @@ -546,10 +554,11 @@ int main(int argc, char *argv[]) tab_rp_bits[NB_REPORT_SLAVE_ID - 1] = 42; rc = modbus_report_slave_id(ctx, NB_REPORT_SLAVE_ID - 1, tab_rp_bits); /* Return the size required (response size) but respects the defined limit */ - ASSERT_TRUE(rc == NB_REPORT_SLAVE_ID && - tab_rp_bits[NB_REPORT_SLAVE_ID - 1] == 42, + ASSERT_TRUE(rc == NB_REPORT_SLAVE_ID && tab_rp_bits[NB_REPORT_SLAVE_ID - 1] == 42, "Return is rc %d (%d) and marker is %d (42)", - rc, NB_REPORT_SLAVE_ID, tab_rp_bits[NB_REPORT_SLAVE_ID - 1]); + rc, + NB_REPORT_SLAVE_ID, + tab_rp_bits[NB_REPORT_SLAVE_ID - 1]); printf("2/2 Report slave ID: \n"); /* tab_rp_bits is used to store bytes */ @@ -565,7 +574,7 @@ int main(int argc, char *argv[]) /* Print additional data as string */ if (rc > 2) { printf("Additional data: "); - for (i=2; i < rc; i++) { + for (i = 2; i < rc; i++) { printf("%c", tab_rp_bits[i]); } printf("\n"); @@ -588,8 +597,8 @@ int main(int argc, char *argv[]) ASSERT_TRUE(rc == -1 && errno == EINVAL, ""); modbus_set_response_timeout(ctx, 0, 1); - rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, - UT_REGISTERS_NB, tab_rp_registers); + rc = modbus_read_registers( + ctx, UT_REGISTERS_ADDRESS, UT_REGISTERS_NB, tab_rp_registers); printf("4/6 1us response timeout: "); if (rc == -1 && errno == ETIMEDOUT) { printf("OK\n"); @@ -607,8 +616,8 @@ int main(int argc, char *argv[]) /* Trigger a special behaviour on server to wait for 0.5 second before * replying whereas allowed timeout is 0.2 second */ modbus_set_response_timeout(ctx, 0, 200000); - rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS_SLEEP_500_MS, - 1, tab_rp_registers); + rc = modbus_read_registers( + ctx, UT_REGISTERS_ADDRESS_SLEEP_500_MS, 1, tab_rp_registers); printf("5/6 Too short response timeout (0.2s < 0.5s): "); ASSERT_TRUE(rc == -1 && errno == ETIMEDOUT, ""); @@ -617,22 +626,21 @@ int main(int argc, char *argv[]) modbus_flush(ctx); modbus_set_response_timeout(ctx, 0, 600000); - rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS_SLEEP_500_MS, - 1, tab_rp_registers); + rc = modbus_read_registers( + ctx, UT_REGISTERS_ADDRESS_SLEEP_500_MS, 1, tab_rp_registers); printf("6/6 Adequate response timeout (0.6s > 0.5s): "); ASSERT_TRUE(rc == 1, ""); /* Disable the byte timeout. The full response must be available in the 600ms interval */ modbus_set_byte_timeout(ctx, 0, 0); - rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS_SLEEP_500_MS, - 1, tab_rp_registers); + rc = modbus_read_registers( + ctx, UT_REGISTERS_ADDRESS_SLEEP_500_MS, 1, tab_rp_registers); printf("7/7 Disable byte timeout: "); ASSERT_TRUE(rc == 1, ""); /* Restore original response timeout */ - modbus_set_response_timeout(ctx, old_response_to_sec, - old_response_to_usec); + modbus_set_response_timeout(ctx, old_response_to_sec, old_response_to_usec); if (use_backend == TCP) { /* The test server is only able to test byte timeouts with the TCP @@ -640,8 +648,8 @@ int main(int argc, char *argv[]) /* Timeout of 3ms between bytes */ modbus_set_byte_timeout(ctx, 0, 3000); - rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS_BYTE_SLEEP_5_MS, - 1, tab_rp_registers); + rc = modbus_read_registers( + ctx, UT_REGISTERS_ADDRESS_BYTE_SLEEP_5_MS, 1, tab_rp_registers); printf("1/2 Too small byte timeout (3ms < 5ms): "); ASSERT_TRUE(rc == -1 && errno == ETIMEDOUT, ""); @@ -651,8 +659,8 @@ int main(int argc, char *argv[]) /* Timeout of 7ms between bytes */ modbus_set_byte_timeout(ctx, 0, 7000); - rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS_BYTE_SLEEP_5_MS, - 1, tab_rp_registers); + rc = modbus_read_registers( + ctx, UT_REGISTERS_ADDRESS_BYTE_SLEEP_5_MS, 1, tab_rp_registers); printf("2/2 Adapted byte timeout (7ms > 5ms): "); ASSERT_TRUE(rc == 1, ""); } @@ -664,19 +672,19 @@ int main(int argc, char *argv[]) printf("\nTEST BAD RESPONSE ERROR:\n"); /* Allocate only the required space */ - tab_rp_registers_bad = (uint16_t *) malloc( - UT_REGISTERS_NB_SPECIAL * sizeof(uint16_t)); + tab_rp_registers_bad = + (uint16_t *) malloc(UT_REGISTERS_NB_SPECIAL * sizeof(uint16_t)); - rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, - UT_REGISTERS_NB_SPECIAL, tab_rp_registers_bad); + rc = modbus_read_registers( + ctx, UT_REGISTERS_ADDRESS, UT_REGISTERS_NB_SPECIAL, tab_rp_registers_bad); printf("* modbus_read_registers: "); ASSERT_TRUE(rc == -1 && errno == EMBBADDATA, ""); free(tab_rp_registers_bad); /** MANUAL EXCEPTION **/ printf("\nTEST MANUAL EXCEPTION:\n"); - rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS_SPECIAL, - UT_REGISTERS_NB, tab_rp_registers); + rc = modbus_read_registers( + ctx, UT_REGISTERS_ADDRESS_SPECIAL, UT_REGISTERS_NB, tab_rp_registers); printf("* modbus_read_registers at special address: "); ASSERT_TRUE(rc == -1 && errno == EMBXSBUSY, ""); @@ -722,63 +730,65 @@ int test_server(modbus_t *ctx, int use_backend) /* Read requests */ const int READ_RAW_REQ_LEN = 6; const int slave = (use_backend == RTU) ? SERVER_ID : MODBUS_TCP_SLAVE; - uint8_t read_raw_req[] = { - slave, - /* function, address, 5 values */ - MODBUS_FC_READ_HOLDING_REGISTERS, - UT_REGISTERS_ADDRESS >> 8, UT_REGISTERS_ADDRESS & 0xFF, - 0x0, 0x05 - }; + uint8_t read_raw_req[] = {slave, + /* function, address, 5 values */ + MODBUS_FC_READ_HOLDING_REGISTERS, + UT_REGISTERS_ADDRESS >> 8, + UT_REGISTERS_ADDRESS & 0xFF, + 0x0, + 0x05}; /* Write and read registers request */ const int RW_RAW_REQ_LEN = 13; - uint8_t rw_raw_req[] = { - slave, - /* function, addr to read, nb to read */ - MODBUS_FC_WRITE_AND_READ_REGISTERS, - /* Read */ - UT_REGISTERS_ADDRESS >> 8, UT_REGISTERS_ADDRESS & 0xFF, - (MODBUS_MAX_WR_READ_REGISTERS + 1) >> 8, - (MODBUS_MAX_WR_READ_REGISTERS + 1) & 0xFF, - /* Write */ - 0, 0, - 0, 1, - /* Write byte count */ - 1 * 2, - /* One data to write... */ - 0x12, 0x34 - }; + uint8_t rw_raw_req[] = {slave, + /* function, addr to read, nb to read */ + MODBUS_FC_WRITE_AND_READ_REGISTERS, + /* Read */ + UT_REGISTERS_ADDRESS >> 8, + UT_REGISTERS_ADDRESS & 0xFF, + (MODBUS_MAX_WR_READ_REGISTERS + 1) >> 8, + (MODBUS_MAX_WR_READ_REGISTERS + 1) & 0xFF, + /* Write */ + 0, + 0, + 0, + 1, + /* Write byte count */ + 1 * 2, + /* One data to write... */ + 0x12, + 0x34}; const int WRITE_RAW_REQ_LEN = 13; - uint8_t write_raw_req[] = { - slave, - /* function will be set in the loop */ - MODBUS_FC_WRITE_MULTIPLE_REGISTERS, - /* Address */ - UT_REGISTERS_ADDRESS >> 8, UT_REGISTERS_ADDRESS & 0xFF, - /* 3 values, 6 bytes */ - 0x00, 0x03, 0x06, - /* Dummy data to write */ - 0x02, 0x2B, 0x00, 0x01, 0x00, 0x64 - }; + uint8_t write_raw_req[] = {slave, + /* function will be set in the loop */ + MODBUS_FC_WRITE_MULTIPLE_REGISTERS, + /* Address */ + UT_REGISTERS_ADDRESS >> 8, + UT_REGISTERS_ADDRESS & 0xFF, + /* 3 values, 6 bytes */ + 0x00, + 0x03, + 0x06, + /* Dummy data to write */ + 0x02, + 0x2B, + 0x00, + 0x01, + 0x00, + 0x64}; const int INVALID_FC = 0x42; const int INVALID_FC_REQ_LEN = 6; - uint8_t invalid_fc_raw_req[] = { - slave, 0x42, 0x00, 0x00, 0x00, 0x00 - }; + uint8_t invalid_fc_raw_req[] = {slave, 0x42, 0x00, 0x00, 0x00, 0x00}; int req_length; uint8_t rsp[MODBUS_TCP_MAX_ADU_LENGTH]; - int tab_read_function[] = { - MODBUS_FC_READ_COILS, - MODBUS_FC_READ_DISCRETE_INPUTS, - MODBUS_FC_READ_HOLDING_REGISTERS, - MODBUS_FC_READ_INPUT_REGISTERS - }; - int tab_read_nb_max[] = { - MODBUS_MAX_READ_BITS + 1, - MODBUS_MAX_READ_BITS + 1, - MODBUS_MAX_READ_REGISTERS + 1, - MODBUS_MAX_READ_REGISTERS + 1 - }; + int tab_read_function[] = {MODBUS_FC_READ_COILS, + MODBUS_FC_READ_DISCRETE_INPUTS, + MODBUS_FC_READ_HOLDING_REGISTERS, + MODBUS_FC_READ_INPUT_REGISTERS}; + int tab_read_nb_max[] = {MODBUS_MAX_READ_BITS + 1, + MODBUS_MAX_READ_BITS + 1, + MODBUS_MAX_READ_REGISTERS + 1, + MODBUS_MAX_READ_REGISTERS + 1}; int backend_length; int backend_offset; @@ -820,58 +830,84 @@ int test_server(modbus_t *ctx, int use_backend) /* Try to read more values than a response could hold for all data types. */ - for (i=0; i<4; i++) { - rc = send_crafted_request(ctx, tab_read_function[i], - read_raw_req, READ_RAW_REQ_LEN, - tab_read_nb_max[i], 0, - backend_length, backend_offset); + for (i = 0; i < 4; i++) { + rc = send_crafted_request(ctx, + tab_read_function[i], + read_raw_req, + READ_RAW_REQ_LEN, + tab_read_nb_max[i], + 0, + backend_length, + backend_offset); if (rc == -1) goto close; } - rc = send_crafted_request(ctx, MODBUS_FC_WRITE_AND_READ_REGISTERS, - rw_raw_req, RW_RAW_REQ_LEN, - MODBUS_MAX_WR_READ_REGISTERS + 1, 0, - backend_length, backend_offset); + rc = send_crafted_request(ctx, + MODBUS_FC_WRITE_AND_READ_REGISTERS, + rw_raw_req, + RW_RAW_REQ_LEN, + MODBUS_MAX_WR_READ_REGISTERS + 1, + 0, + backend_length, + backend_offset); if (rc == -1) goto close; - rc = send_crafted_request(ctx, MODBUS_FC_WRITE_MULTIPLE_REGISTERS, - write_raw_req, WRITE_RAW_REQ_LEN, - MODBUS_MAX_WRITE_REGISTERS + 1, 6, - backend_length, backend_offset); + rc = send_crafted_request(ctx, + MODBUS_FC_WRITE_MULTIPLE_REGISTERS, + write_raw_req, + WRITE_RAW_REQ_LEN, + MODBUS_MAX_WRITE_REGISTERS + 1, + 6, + backend_length, + backend_offset); if (rc == -1) goto close; - rc = send_crafted_request(ctx, MODBUS_FC_WRITE_MULTIPLE_COILS, - write_raw_req, WRITE_RAW_REQ_LEN, - MODBUS_MAX_WRITE_BITS + 1, 6, - backend_length, backend_offset); + rc = send_crafted_request(ctx, + MODBUS_FC_WRITE_MULTIPLE_COILS, + write_raw_req, + WRITE_RAW_REQ_LEN, + MODBUS_MAX_WRITE_BITS + 1, + 6, + backend_length, + backend_offset); if (rc == -1) goto close; /* Modbus write multiple registers with large number of values but a set a small number of bytes in requests (not nb * 2 as usual). */ - rc = send_crafted_request(ctx, MODBUS_FC_WRITE_MULTIPLE_REGISTERS, - write_raw_req, WRITE_RAW_REQ_LEN, - MODBUS_MAX_WRITE_REGISTERS, 6, - backend_length, backend_offset); + rc = send_crafted_request(ctx, + MODBUS_FC_WRITE_MULTIPLE_REGISTERS, + write_raw_req, + WRITE_RAW_REQ_LEN, + MODBUS_MAX_WRITE_REGISTERS, + 6, + backend_length, + backend_offset); if (rc == -1) goto close; - rc = send_crafted_request(ctx, MODBUS_FC_WRITE_MULTIPLE_COILS, - write_raw_req, WRITE_RAW_REQ_LEN, - MODBUS_MAX_WRITE_BITS, 6, - backend_length, backend_offset); + rc = send_crafted_request(ctx, + MODBUS_FC_WRITE_MULTIPLE_COILS, + write_raw_req, + WRITE_RAW_REQ_LEN, + MODBUS_MAX_WRITE_BITS, + 6, + backend_length, + backend_offset); if (rc == -1) goto close; /* Test invalid function code */ - modbus_send_raw_request(ctx, invalid_fc_raw_req, INVALID_FC_REQ_LEN * sizeof(uint8_t)); + modbus_send_raw_request( + ctx, invalid_fc_raw_req, INVALID_FC_REQ_LEN * sizeof(uint8_t)); rc = modbus_receive_confirmation(ctx, rsp); printf("Return an exception on unknown function code: "); ASSERT_TRUE(rc == (backend_length + EXCEPTION_RC) && - rsp[backend_offset] == (0x80 + INVALID_FC), "") + rsp[backend_offset] == (0x80 + INVALID_FC), + "") modbus_set_response_timeout(ctx, old_response_to_sec, old_response_to_usec); return 0; @@ -880,16 +916,19 @@ int test_server(modbus_t *ctx, int use_backend) return -1; } - -int send_crafted_request(modbus_t *ctx, int function, - uint8_t *req, int req_len, - uint16_t max_value, uint16_t bytes, - int backend_length, int backend_offset) +int send_crafted_request(modbus_t *ctx, + int function, + uint8_t *req, + int req_len, + uint16_t max_value, + uint16_t bytes, + int backend_length, + int backend_offset) { uint8_t rsp[MODBUS_TCP_MAX_ADU_LENGTH]; int j; - for (j=0; j<2; j++) { + for (j = 0; j < 2; j++) { int rc; req[1] = function; @@ -913,15 +952,19 @@ int send_crafted_request(modbus_t *ctx, int function, modbus_send_raw_request(ctx, req, req_len * sizeof(uint8_t)); if (j == 0) { - printf("* try function 0x%X: %s 0 values: ", function, bytes ? "write": "read"); + printf( + "* try function 0x%X: %s 0 values: ", function, bytes ? "write" : "read"); } else { - printf("* try function 0x%X: %s %d values: ", function, bytes ? "write": "read", + printf("* try function 0x%X: %s %d values: ", + function, + bytes ? "write" : "read", max_value); } rc = modbus_receive_confirmation(ctx, rsp); ASSERT_TRUE(rc == (backend_length + EXCEPTION_RC) && - rsp[backend_offset] == (0x80 + function) && - rsp[backend_offset + 1] == MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, ""); + rsp[backend_offset] == (0x80 + function) && + rsp[backend_offset + 1] == MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, + ""); } return 0; close: diff --git a/tests/unit-test-server.c b/tests/unit-test-server.c index f6e2ebb3e..b5cc3ea54 100644 --- a/tests/unit-test-server.c +++ b/tests/unit-test-server.c @@ -4,12 +4,14 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include -#include -#include -#include #include #include +#include +#include +#include +#include + +// clang-format off #ifdef _WIN32 # include #else @@ -20,6 +22,7 @@ #ifndef MSG_NOSIGNAL # define MSG_NOSIGNAL 0 #endif +// clang-format on #include "unit-test.h" @@ -29,7 +32,7 @@ enum { RTU }; -int main(int argc, char*argv[]) +int main(int argc, char *argv[]) { int s = -1; modbus_t *ctx; @@ -48,7 +51,8 @@ int main(int argc, char*argv[]) } else if (strcmp(argv[1], "rtu") == 0) { use_backend = RTU; } else { - printf("Usage:\n %s [tcp|tcppi|rtu] - Modbus server for unit testing\n\n", argv[0]); + printf("Usage:\n %s [tcp|tcppi|rtu] - Modbus server for unit testing\n\n", + argv[0]); return -1; } } else { @@ -71,14 +75,16 @@ int main(int argc, char*argv[]) modbus_set_debug(ctx, TRUE); - mb_mapping = modbus_mapping_new_start_address( - UT_BITS_ADDRESS, UT_BITS_NB, - UT_INPUT_BITS_ADDRESS, UT_INPUT_BITS_NB, - UT_REGISTERS_ADDRESS, UT_REGISTERS_NB_MAX, - UT_INPUT_REGISTERS_ADDRESS, UT_INPUT_REGISTERS_NB); + mb_mapping = modbus_mapping_new_start_address(UT_BITS_ADDRESS, + UT_BITS_NB, + UT_INPUT_BITS_ADDRESS, + UT_INPUT_BITS_NB, + UT_REGISTERS_ADDRESS, + UT_REGISTERS_NB_MAX, + UT_INPUT_REGISTERS_ADDRESS, + UT_INPUT_REGISTERS_NB); if (mb_mapping == NULL) { - fprintf(stderr, "Failed to allocate the mapping: %s\n", - modbus_strerror(errno)); + fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno)); modbus_free(ctx); return -1; } @@ -87,11 +93,11 @@ int main(int argc, char*argv[]) Only the read-only input values are assigned. */ /* Initialize input values that's can be only done server side. */ - modbus_set_bits_from_bytes(mb_mapping->tab_input_bits, 0, UT_INPUT_BITS_NB, - UT_INPUT_BITS_TAB); + modbus_set_bits_from_bytes( + mb_mapping->tab_input_bits, 0, UT_INPUT_BITS_NB, UT_INPUT_BITS_TAB); /* Initialize values of INPUT REGISTERS */ - for (i=0; i < UT_INPUT_REGISTERS_NB; i++) { + for (i = 0; i < UT_INPUT_REGISTERS_NB; i++) { mb_mapping->tab_input_registers[i] = UT_INPUT_REGISTERS_TAB[i]; } @@ -127,35 +133,34 @@ int main(int argc, char*argv[]) if (query[header_length] == 0x03) { /* Read holding registers */ - if (MODBUS_GET_INT16_FROM_INT8(query, header_length + 3) - == UT_REGISTERS_NB_SPECIAL) { + if (MODBUS_GET_INT16_FROM_INT8(query, header_length + 3) == + UT_REGISTERS_NB_SPECIAL) { printf("Set an incorrect number of values\n"); - MODBUS_SET_INT16_TO_INT8(query, header_length + 3, - UT_REGISTERS_NB_SPECIAL - 1); - } else if (MODBUS_GET_INT16_FROM_INT8(query, header_length + 1) - == UT_REGISTERS_ADDRESS_SPECIAL) { + MODBUS_SET_INT16_TO_INT8( + query, header_length + 3, UT_REGISTERS_NB_SPECIAL - 1); + } else if (MODBUS_GET_INT16_FROM_INT8(query, header_length + 1) == + UT_REGISTERS_ADDRESS_SPECIAL) { printf("Reply to this special register address by an exception\n"); - modbus_reply_exception(ctx, query, - MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY); + modbus_reply_exception(ctx, query, MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY); continue; - } else if (MODBUS_GET_INT16_FROM_INT8(query, header_length + 1) - == UT_REGISTERS_ADDRESS_INVALID_TID_OR_SLAVE) { + } else if (MODBUS_GET_INT16_FROM_INT8(query, header_length + 1) == + UT_REGISTERS_ADDRESS_INVALID_TID_OR_SLAVE) { const int RAW_REQ_LENGTH = 5; - uint8_t raw_req[] = { - (use_backend == RTU) ? INVALID_SERVER_ID : 0xFF, - 0x03, - 0x02, 0x00, 0x00 - }; + uint8_t raw_req[] = {(use_backend == RTU) ? INVALID_SERVER_ID : 0xFF, + 0x03, + 0x02, + 0x00, + 0x00}; printf("Reply with an invalid TID or slave\n"); modbus_send_raw_request(ctx, raw_req, RAW_REQ_LENGTH * sizeof(uint8_t)); continue; - } else if (MODBUS_GET_INT16_FROM_INT8(query, header_length + 1) - == UT_REGISTERS_ADDRESS_SLEEP_500_MS) { + } else if (MODBUS_GET_INT16_FROM_INT8(query, header_length + 1) == + UT_REGISTERS_ADDRESS_SLEEP_500_MS) { printf("Sleep 0.5 s before replying\n"); usleep(500000); - } else if (MODBUS_GET_INT16_FROM_INT8(query, header_length + 1) - == UT_REGISTERS_ADDRESS_BYTE_SLEEP_5_MS) { + } else if (MODBUS_GET_INT16_FROM_INT8(query, header_length + 1) == + UT_REGISTERS_ADDRESS_BYTE_SLEEP_5_MS) { /* Test low level only available in TCP mode */ /* Catch the reply and send reply byte a byte */ uint8_t req[] = "\x00\x1C\x00\x00\x00\x05\xFF\x03\x02\x00\x00"; @@ -168,10 +173,10 @@ int main(int argc, char*argv[]) /* Copy TID */ req[1] = query[1]; - for (i=0; i < req_length; i++) { + for (i = 0; i < req_length; i++) { printf("(%.2X)", req[i]); usleep(5000); - rc = send(w_s, (const char*)(req + i), 1, MSG_NOSIGNAL); + rc = send(w_s, (const char *) (req + i), 1, MSG_NOSIGNAL); if (rc == -1) { break; } diff --git a/tests/unit-test.h.in b/tests/unit-test.h.in index 98c14d698..5e379bb3f 100644 --- a/tests/unit-test.h.in +++ b/tests/unit-test.h.in @@ -11,6 +11,7 @@ #define HAVE_INTTYPES_H @HAVE_INTTYPES_H@ #define HAVE_STDINT_H @HAVE_STDINT_H@ +// clang-format off #ifdef HAVE_INTTYPES_H #include #endif @@ -21,6 +22,7 @@ # include "stdint.h" # endif #endif +// clang-format on #define SERVER_ID 17 #define INVALID_SERVER_ID 18 diff --git a/tests/version.c b/tests/version.c index 0b266d134..8263b2edf 100644 --- a/tests/version.c +++ b/tests/version.c @@ -4,14 +4,18 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include #include +#include int main(void) { - printf("Compiled with libmodbus version %s (%06X)\n", LIBMODBUS_VERSION_STRING, LIBMODBUS_VERSION_HEX); + printf("Compiled with libmodbus version %s (%06X)\n", + LIBMODBUS_VERSION_STRING, + LIBMODBUS_VERSION_HEX); printf("Linked with libmodbus version %d.%d.%d\n", - libmodbus_version_major, libmodbus_version_minor, libmodbus_version_micro); + libmodbus_version_major, + libmodbus_version_minor, + libmodbus_version_micro); if (LIBMODBUS_VERSION_CHECK(2, 1, 0)) { printf("The functions to read/write float values are available (2.1.0).\n"); From fa2079859d82965c5be417cc3985df141d720e0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Sun, 27 Nov 2022 13:03:52 +0100 Subject: [PATCH 067/210] Remove constraints on baud rate values --- src/modbus-rtu.c | 180 +---------------------------------------------- 1 file changed, 3 insertions(+), 177 deletions(-) diff --git a/src/modbus-rtu.c b/src/modbus-rtu.c index 29c6c174d..98b90aa66 100644 --- a/src/modbus-rtu.c +++ b/src/modbus-rtu.c @@ -392,7 +392,6 @@ static int _modbus_rtu_connect(modbus_t *ctx) DCB dcb; #else struct termios tios; - speed_t speed; int flags; #endif modbus_rtu_t *ctx_rtu = ctx->backend_data; @@ -445,74 +444,7 @@ static int _modbus_rtu_connect(modbus_t *ctx) dcb = ctx_rtu->old_dcb; /* Speed setting */ - switch (ctx_rtu->baud) { - case 110: - dcb.BaudRate = CBR_110; - break; - case 300: - dcb.BaudRate = CBR_300; - break; - case 600: - dcb.BaudRate = CBR_600; - break; - case 1200: - dcb.BaudRate = CBR_1200; - break; - case 2400: - dcb.BaudRate = CBR_2400; - break; - case 4800: - dcb.BaudRate = CBR_4800; - break; - case 9600: - dcb.BaudRate = CBR_9600; - break; - case 14400: - dcb.BaudRate = CBR_14400; - break; - case 19200: - dcb.BaudRate = CBR_19200; - break; - case 38400: - dcb.BaudRate = CBR_38400; - break; - case 57600: - dcb.BaudRate = CBR_57600; - break; - case 115200: - dcb.BaudRate = CBR_115200; - break; - case 230400: - /* CBR_230400 - not defined */ - dcb.BaudRate = 230400; - break; - case 250000: - dcb.BaudRate = 250000; - break; - case 256000: - dcb.BaudRate = 256000; - break; - case 460800: - dcb.BaudRate = 460800; - break; - case 500000: - dcb.BaudRate = 500000; - break; - case 921600: - dcb.BaudRate = 921600; - break; - case 1000000: - dcb.BaudRate = 1000000; - break; - default: - dcb.BaudRate = CBR_9600; - if (ctx->debug) { - fprintf(stderr, - "WARNING Unknown baud rate %d for %s (B9600 used)\n", - ctx_rtu->baud, - ctx_rtu->device); - } - } + dcb.BaudRate = ctx_rtu->baud; /* Data bits */ switch (ctx_rtu->data_bit) { @@ -606,116 +538,10 @@ static int _modbus_rtu_connect(modbus_t *ctx) /* C_ISPEED Input baud (new interface) C_OSPEED Output baud (new interface) */ - switch (ctx_rtu->baud) { - case 110: - speed = B110; - break; - case 300: - speed = B300; - break; - case 600: - speed = B600; - break; - case 1200: - speed = B1200; - break; - case 2400: - speed = B2400; - break; - case 4800: - speed = B4800; - break; - case 9600: - speed = B9600; - break; - case 19200: - speed = B19200; - break; - case 38400: - speed = B38400; - break; -#ifdef B57600 - case 57600: - speed = B57600; - break; -#endif -#ifdef B115200 - case 115200: - speed = B115200; - break; -#endif -#ifdef B230400 - case 230400: - speed = B230400; - break; -#endif -#ifdef B460800 - case 460800: - speed = B460800; - break; -#endif -#ifdef B500000 - case 500000: - speed = B500000; - break; -#endif -#ifdef B576000 - case 576000: - speed = B576000; - break; -#endif -#ifdef B921600 - case 921600: - speed = B921600; - break; -#endif -#ifdef B1000000 - case 1000000: - speed = B1000000; - break; -#endif -#ifdef B1152000 - case 1152000: - speed = B1152000; - break; -#endif -#ifdef B1500000 - case 1500000: - speed = B1500000; - break; -#endif -#ifdef B2500000 - case 2500000: - speed = B2500000; - break; -#endif -#ifdef B3000000 - case 3000000: - speed = B3000000; - break; -#endif -#ifdef B3500000 - case 3500000: - speed = B3500000; - break; -#endif -#ifdef B4000000 - case 4000000: - speed = B4000000; - break; -#endif - default: - speed = B9600; - if (ctx->debug) { - fprintf(stderr, - "WARNING Unknown baud rate %d for %s (B9600 used)\n", - ctx_rtu->baud, - ctx_rtu->device); - } - } /* Set the baud rate */ - if ((cfsetispeed(&tios, speed) < 0) || (cfsetospeed(&tios, speed) < 0)) { + if ((cfsetispeed(&tios, ctx_rtu->baud) < 0) || + (cfsetospeed(&tios, ctx_rtu->baud) < 0)) { close(ctx->s); ctx->s = -1; return -1; From 842a0c23554265a96c32c76871dbdbe91fb28c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Sun, 27 Nov 2022 13:30:50 +0100 Subject: [PATCH 068/210] Accept IP or device in arg of unit test progs --- tests/unit-test-client.c | 31 ++++++++++++++++++++++++++----- tests/unit-test-server.c | 31 ++++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/tests/unit-test-client.c b/tests/unit-test-client.c index 915a17b79..e01c65e7b 100644 --- a/tests/unit-test-client.c +++ b/tests/unit-test-client.c @@ -77,6 +77,7 @@ int main(int argc, char *argv[]) int use_backend; int success = FALSE; int old_slave; + char *ip_or_device; if (argc > 1) { if (strcmp(argv[1], "tcp") == 0) { @@ -86,8 +87,9 @@ int main(int argc, char *argv[]) } else if (strcmp(argv[1], "rtu") == 0) { use_backend = RTU; } else { - printf("Usage:\n %s [tcp|tcppi|rtu] - Modbus client for unit testing\n\n", - argv[0]); + printf("Modbus client for unit testing\n"); + printf("Usage:\n %s [tcp|tcppi|rtu]\n", argv[0]); + printf("Eg. tcp 127.0.0.1 or rtu /dev/ttyUSB1\n\n"); exit(1); } } else { @@ -95,17 +97,36 @@ int main(int argc, char *argv[]) use_backend = TCP; } + if (argc > 2) { + ip_or_device = argv[2]; + } else { + switch (use_backend) { + case TCP: + ip_or_device = "127.0.0.1"; + break; + case TCP_PI: + ip_or_device = "::1"; + break; + case RTU: + ip_or_device = "/dev/ttyUSB1"; + break; + default: + break; + } + } + if (use_backend == TCP) { - ctx = modbus_new_tcp("127.0.0.1", 1502); + ctx = modbus_new_tcp(ip_or_device, 1502); } else if (use_backend == TCP_PI) { - ctx = modbus_new_tcp_pi("::1", "1502"); + ctx = modbus_new_tcp_pi(ip_or_device, "1502"); } else { - ctx = modbus_new_rtu("/dev/ttyUSB1", 115200, 'N', 8, 1); + ctx = modbus_new_rtu(ip_or_device, 115200, 'N', 8, 1); } if (ctx == NULL) { fprintf(stderr, "Unable to allocate libmodbus context\n"); return -1; } + modbus_set_debug(ctx, TRUE); modbus_set_error_recovery( ctx, MODBUS_ERROR_RECOVERY_LINK | MODBUS_ERROR_RECOVERY_PROTOCOL); diff --git a/tests/unit-test-server.c b/tests/unit-test-server.c index b5cc3ea54..561d64d01 100644 --- a/tests/unit-test-server.c +++ b/tests/unit-test-server.c @@ -42,6 +42,7 @@ int main(int argc, char *argv[]) int use_backend; uint8_t *query; int header_length; + char *ip_or_device; if (argc > 1) { if (strcmp(argv[1], "tcp") == 0) { @@ -51,8 +52,9 @@ int main(int argc, char *argv[]) } else if (strcmp(argv[1], "rtu") == 0) { use_backend = RTU; } else { - printf("Usage:\n %s [tcp|tcppi|rtu] - Modbus server for unit testing\n\n", - argv[0]); + printf("Modbus server for unit testing.\n"); + printf("Usage:\n %s [tcp|tcppi|rtu] []\n", argv[0]); + printf("Eg. tcp 127.0.0.1 or rtu /dev/ttyUSB0\n\n"); return -1; } } else { @@ -60,17 +62,36 @@ int main(int argc, char *argv[]) use_backend = TCP; } + if (argc > 2) { + ip_or_device = argv[2]; + } else { + switch (use_backend) { + case TCP: + ip_or_device = "127.0.0.1"; + break; + case TCP_PI: + ip_or_device = "::1"; + break; + case RTU: + ip_or_device = "/dev/ttyUSB0"; + break; + default: + break; + } + } + if (use_backend == TCP) { - ctx = modbus_new_tcp("127.0.0.1", 1502); + ctx = modbus_new_tcp(ip_or_device, 1502); query = malloc(MODBUS_TCP_MAX_ADU_LENGTH); } else if (use_backend == TCP_PI) { - ctx = modbus_new_tcp_pi("::0", "1502"); + ctx = modbus_new_tcp_pi(ip_or_device, "1502"); query = malloc(MODBUS_TCP_MAX_ADU_LENGTH); } else { - ctx = modbus_new_rtu("/dev/ttyUSB0", 115200, 'N', 8, 1); + ctx = modbus_new_rtu(ip_or_device, 115200, 'N', 8, 1); modbus_set_slave(ctx, SERVER_ID); query = malloc(MODBUS_RTU_MAX_ADU_LENGTH); } + header_length = modbus_get_header_length(ctx); modbus_set_debug(ctx, TRUE); From aa84a0e0b680c787b62cd6539d153b41d21cc8e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Sun, 27 Nov 2022 21:43:48 +0100 Subject: [PATCH 069/210] Avoid compilation issue with VS2022 with strdup --- src/modbus-tcp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modbus-tcp.c b/src/modbus-tcp.c index b084532e4..081ceee95 100644 --- a/src/modbus-tcp.c +++ b/src/modbus-tcp.c @@ -30,6 +30,7 @@ # include # define SHUT_RDWR 2 # define close closesocket +# define strdup _strdup #else # include # include From 173991115932e219cb3a94fd77970bbfe1a33845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Sun, 27 Nov 2022 22:23:59 +0100 Subject: [PATCH 070/210] Display created files in configure.js --- src/win32/configure.js | 187 ++++++++++++++++++++++------------------- 1 file changed, 99 insertions(+), 88 deletions(-) diff --git a/src/win32/configure.js b/src/win32/configure.js index aa21e49f9..6c96a90a6 100644 --- a/src/win32/configure.js +++ b/src/win32/configure.js @@ -29,135 +29,146 @@ var newFile; /* Displays the details about how to use this script. */ function usage() { - var txt; + var txt; - txt = "Usage:\n"; - txt += " cscript " + WScript.ScriptName + " \n"; - txt += " cscript " + WScript.ScriptName + " help\n\n"; - txt += "Options can be specified in the form