Compile and install libmodbus library under CentOS7

Compile and install libmodbus library under CentOS7###

Download libdmodbus source code##

The download page of libmodbus official website is shown below:

You can download the libmodbus source code from the following link

git clone https://github.com/stephane/libmodbus.git

The downloaded source code directory is shown below:

Compile and install lidmodbus

How to install libmodbus has been very clear above:

Run the following commands directly in the root directory of libmodbus to compile and install the libmodbus library

. /autogen.sh 
. /configure
make
make install

The relevant compilation and installation process is shown in the following figure:

You can see that by default the header files of libmodbus are placed in the /usr/local/include directory, and the dynamic library files are placed in the /usr/local/lib directory.

At this point, libmodbus compilation and installation under CentOS7 is complete.

test##

Regarding some tests of libmodbus, Github also describes it more clearly:

Some test examples provided by libmodbus official website are shown below:

/*
 * Copyright © 2008-2014 Stéphane Raimbault <[email protected]>
 *
 * SPDX-License-Identifier: BSD-3-Clause
 * /

# include <stdio.h>
# include <unistd.h>
# include <string.h>
# include <stdlib.h>
# include <errno.h>
# include <modbus.h>
# ifdef _WIN32
# include <winsock2.h>
# else
# include <sys/socket.h>
# endif

/* For MinGW */
# ifndef MSG_NOSIGNAL
# define MSG_NOSIGNAL 0
# endif

# include "unit-test.h"enum{
 TCP,
 TCP_PI,
 RTU
};

int main(int argc, char*argv[]){
 int s =-1;
 modbus_t *ctx;
 modbus_mapping_t *mb_mapping;
 int rc;
 int i;
 int use_backend;
 uint8_t *query;
 int header_length;if(argc >1){if(strcmp(argv[1],"tcp")==0){
   use_backend = TCP;}elseif(strcmp(argv[1],"tcppi")==0){
   use_backend = TCP_PI;}elseif(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]);return-1;}}else{/* By default */
  use_backend = TCP;}if(use_backend == TCP){
  ctx =modbus_new_tcp("127.0.0.1",1502);
  query =malloc(MODBUS_TCP_MAX_ADU_LENGTH);}elseif(use_backend == TCP_PI){
  ctx =modbus_new_tcp_pi("::0","1502");
  query =malloc(MODBUS_TCP_MAX_ADU_LENGTH);}else{
  ctx =modbus_new_rtu("/dev/ttyUSB0",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);

 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));modbus_free(ctx);return-1;}/* Examples from PI_MODBUS_300.pdf.
  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);/* 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];;}if(use_backend == TCP){
  s =modbus_tcp_listen(ctx,1);modbus_tcp_accept(ctx,&s);}elseif(use_backend == TCP_PI){
  s =modbus_tcp_pi_listen(ctx,1);modbus_tcp_pi_accept(ctx,&s);}else{
  rc =modbus_connect(ctx);if(rc ==-1){fprintf(stderr,"Unable to connect %s\n",modbus_strerror(errno));modbus_free(ctx);return-1;}}for(;;){do{
   rc =modbus_receive(ctx, query);/* Filtered queries return 0 */}while(rc ==0);/* The connection is not closed on errors which require on reply such as
   bad CRC in RTU. */if(rc ==-1&& errno != EMBBADCRC){/* Quit */break;}/* Special server behavior to test client */if(query[header_length]==0x03){/* Read holding registers */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);}elseif(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);continue;}elseif(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};printf("Reply with an invalid TID or slave\n");modbus_send_raw_request(ctx, raw_req, RAW_REQ_LENGTH *sizeof(uint8_t));continue;}elseif(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);}elseif(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";
    int req_length =11;
    int w_s =modbus_get_socket(ctx);if(w_s ==-1){fprintf(stderr,"Unable to get a valid socket in special test\n");continue;}/* Copy TID */
    req[1]= query[1];for(i=0; i < req_length; i++){printf("(%.2X)", req[i]);usleep(5000);
     rc =send(w_s,(const char*)(req + i),1, MSG_NOSIGNAL);if(rc ==-1){break;}}continue;}}

  rc =modbus_reply(ctx, query, rc, mb_mapping);if(rc ==-1){break;}}printf("Quit the loop: %s\n",modbus_strerror(errno));if(use_backend == TCP){if(s !=-1){close(s);}}modbus_mapping_free(mb_mapping);free(query);/* For RTU */modbus_close(ctx);modbus_free(ctx);return0;}
/*
 * Copyright © 2008-2014 Stéphane Raimbault <[email protected]>
 *
 * SPDX-License-Identifier: BSD-3-Clause
 * /

# include <stdio.h>
# include <unistd.h>
# include <string.h>
# include <stdlib.h>
# include <errno.h>
# include <modbus.h>

# include "unit-test.h"const int EXCEPTION_RC =2;enum{
 TCP,
 TCP_PI,
 RTU
};

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 equal_dword(uint16_t *tab_reg,const uint32_t value);

# 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;                               \
    }                                             \
};

int equal_dword(uint16_t *tab_reg,const uint32_t value){return((tab_reg[0]==(value >>16))&&(tab_reg[1]==(value &0xFFFF)));}

int main(int argc, char *argv[]){const int NB_REPORT_SLAVE_ID =10;
 uint8_t *tab_rp_bits = NULL;
 uint16_t *tab_rp_registers = NULL;
 uint16_t *tab_rp_registers_bad = NULL;
 modbus_t *ctx = NULL;
 int i;
 uint8_t value;
 int nb_points;
 int rc;
 float real;
 uint32_t old_response_to_sec;
 uint32_t old_response_to_usec;
 uint32_t new_response_to_sec;
 uint32_t new_response_to_usec;
 uint32_t old_byte_to_sec;
 uint32_t old_byte_to_usec;
 int use_backend;
 int success = FALSE;
 int old_slave;if(argc >1){if(strcmp(argv[1],"tcp")==0){
   use_backend = TCP;}elseif(strcmp(argv[1],"tcppi")==0){
   use_backend = TCP_PI;}elseif(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]);exit(1);}}else{/* By default */
  use_backend = TCP;}if(use_backend == TCP){
  ctx =modbus_new_tcp("127.0.0.1",1502);}elseif(use_backend == TCP_PI){
  ctx =modbus_new_tcp_pi("::1","1502");}else{
  ctx =modbus_new_rtu("/dev/ttyUSB1",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);if(use_backend == RTU){modbus_set_slave(ctx, SERVER_ID);}modbus_get_response_timeout(ctx,&old_response_to_sec,&old_response_to_usec);if(modbus_connect(ctx)==-1){fprintf(stderr,"Connection failed: %s\n",modbus_strerror(errno));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;
 tab_rp_bits =(uint8_t *)malloc(nb_points *sizeof(uint8_t));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;
 tab_rp_registers =(uint16_t *)malloc(nb_points *sizeof(uint16_t));memset(tab_rp_registers,0, nb_points *sizeof(uint16_t));printf("\nTEST WRITE/READ:\n");/** COIL BITS **//* Single */
 rc =modbus_write_bit(ctx, UT_BITS_ADDRESS, ON);printf("1/2 modbus_write_bit: ");ASSERT_TRUE(rc ==1,"");

 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);/* End single *//* Multiple bits */{
  uint8_t tab_value[UT_BITS_NB];modbus_set_bits_from_bytes(tab_value,0, UT_BITS_NB, UT_BITS_TAB);
  rc =modbus_write_bits(ctx, UT_BITS_ADDRESS, UT_BITS_NB, tab_value);printf("1/2 modbus_write_bits: ");ASSERT_TRUE(rc == UT_BITS_NB,"");}

 rc =modbus_read_bits(ctx, UT_BITS_ADDRESS, UT_BITS_NB, tab_rp_bits);printf("2/2 modbus_read_bits: ");ASSERT_TRUE(rc == UT_BITS_NB,"FAILED (nb points %d)\n", rc);

 i =0;
 nb_points = UT_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_BITS_TAB[i],"FAILED (%0X != %0X)\n",
     value, UT_BITS_TAB[i]);

  nb_points -= nb_bits;
  i++;}printf("OK\n");/* End of multiple bits *//** DISCRETE INPUTS **/
 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);

 i =0;
 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]);

  nb_points -= nb_bits;
  i++;}printf("OK\n");/** HOLDING REGISTERS **//* Single register */
 rc =modbus_write_register(ctx, UT_REGISTERS_ADDRESS,0x1234);printf("1/2 modbus_write_register: ");ASSERT_TRUE(rc ==1,"");

 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);/* End of single register *//* Many registers */
 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);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++){ASSERT_TRUE(tab_rp_registers[i]== UT_REGISTERS_TAB[i],"FAILED (%0X != %0X)\n",
     tab_rp_registers[i], UT_REGISTERS_TAB[i]);}

 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;memset(tab_rp_registers,0, nb_points *sizeof(uint16_t));/* Write registers to zero from tab_rp_registers and store read registers
  into tab_rp_registers. So the read registers must set to 0, except the
  first one because there is an offset of 1 register on write. */
 rc =modbus_write_and_read_registers(ctx,
           UT_REGISTERS_ADDRESS +1,
           UT_REGISTERS_NB -1,
           tab_rp_registers,
           UT_REGISTERS_ADDRESS,
           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(tab_rp_registers[0]== UT_REGISTERS_TAB[0],"FAILED (%0X != %0X)\n",
    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);}/* End of many registers *//** INPUT 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++){ASSERT_TRUE(tab_rp_registers[i]== UT_INPUT_REGISTERS_TAB[i],"FAILED (%0X != %0X)\n",
     tab_rp_registers[i], UT_INPUT_REGISTERS_TAB[i]);}/* MASKS */printf("1/1 Write mask: ");
 rc =modbus_write_register(ctx, UT_REGISTERS_ADDRESS,0x12);
 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);printf("\nTEST FLOATS\n");/** 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(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(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(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(real == UT_REAL,"FAILED (%f != %f)\n", real, UT_REAL);printf("\nAt this point, error messages doesn't mean the test has failed\n");/** ILLEGAL DATA ADDRESS **/printf("\nTEST ILLEGAL DATA ADDRESS:\n");/* The mapping begins at the defined addresses and ends at address +
  * nb_points so these addresses are not valid. */

 rc =modbus_read_bits(ctx,0,1, tab_rp_bits);printf("* modbus_read_bits (0): ");ASSERT_TRUE(rc ==-1&& errno == EMBXILADD,"");

 rc =modbus_read_bits(ctx, UT_BITS_ADDRESS, UT_BITS_NB +1, tab_rp_bits);printf("* modbus_read_bits (max): ");ASSERT_TRUE(rc ==-1&& errno == EMBXILADD,"");

 rc =modbus_read_input_bits(ctx,0,1, tab_rp_bits);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);printf("* modbus_read_input_bits (max): ");ASSERT_TRUE(rc ==-1&& errno == EMBXILADD,"");

 rc =modbus_read_registers(ctx,0,1, tab_rp_registers);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);printf("* modbus_read_registers (max): ");ASSERT_TRUE(rc ==-1&& errno == EMBXILADD,"");

 rc =modbus_read_input_registers(ctx,0,1, tab_rp_registers);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);printf("* modbus_read_input_registers (max): ");ASSERT_TRUE(rc ==-1&& errno == EMBXILADD,"");

 rc =modbus_write_bit(ctx,0, ON);printf("* modbus_write_bit (0): ");ASSERT_TRUE(rc ==-1&& errno == EMBXILADD,"");

 rc =modbus_write_bit(ctx, UT_BITS_ADDRESS + UT_BITS_NB, ON);printf("* modbus_write_bit (max): ");ASSERT_TRUE(rc ==-1&& errno == EMBXILADD,"");

 rc =modbus_write_bits(ctx,0,1, tab_rp_bits);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);printf("* modbus_write_coils (max): ");ASSERT_TRUE(rc ==-1&& errno == EMBXILADD,"");

 rc =modbus_write_register(ctx,0, tab_rp_registers[0]);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]);printf("* modbus_write_register (max): ");ASSERT_TRUE(rc ==-1&& errno == EMBXILADD,"");

 rc =modbus_write_registers(ctx,0,1, tab_rp_registers);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);printf("* modbus_write_registers (max): ");ASSERT_TRUE(rc ==-1&& errno == EMBXILADD,"");

 rc =modbus_mask_write_register(ctx,0,0xF2,0x25);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);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);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_ADDRESS + UT_REGISTERS_NB_MAX,
           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);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);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);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);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);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);printf("* modbus_write_registers: ");ASSERT_TRUE(rc ==-1&& errno == EMBMDATA,"");/** SLAVE REPLY **/
 old_slave =modbus_get_slave(ctx);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);if(use_backend == RTU){const int RAW_REQ_LENGTH =6;
  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};const int RAW_RSP_LENGTH =7;
  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 */printf("1-A/3 No response from slave %d: ", INVALID_SERVER_ID);ASSERT_TRUE(rc ==-1&& errno == ETIMEDOUT,"");/* 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
   * ignore both. */modbus_send_raw_request(ctx, raw_req, RAW_REQ_LENGTH *sizeof(uint8_t));modbus_send_raw_request(ctx, raw_rsp, RAW_RSP_LENGTH *sizeof(uint8_t));
  rc =modbus_receive_confirmation(ctx, rsp);printf("1-B/3 No response from slave %d on indication/confirmation messages: ",
    INVALID_SERVER_ID);ASSERT_TRUE(rc ==-1&& errno == ETIMEDOUT,"");/* Send an INVALID request for another slave */modbus_send_raw_request(ctx, raw_invalid_req, RAW_REQ_LENGTH *sizeof(uint8_t));
  rc =modbus_receive_confirmation(ctx, rsp);printf("1-C/3 No response from slave %d with invalid request: ",
    INVALID_SERVER_ID);ASSERT_TRUE(rc ==-1&& errno == ETIMEDOUT,"");

  rc =modbus_set_slave(ctx, MODBUS_BROADCAST_ADDRESS);ASSERT_TRUE(rc !=-1,"Invalid broadcast address");

  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{/* Response in TCP mode */printf("1/3 Response from slave %d: ", INVALID_SERVER_ID);ASSERT_TRUE(rc == UT_REGISTERS_NB,"");

  rc =modbus_set_slave(ctx, MODBUS_BROADCAST_ADDRESS);ASSERT_TRUE(rc !=-1,"Invalid broacast address");

  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,"");}/* Restore slave */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);ASSERT_TRUE(rc ==-1,"");printf("1/2 Report slave ID truncated: \n");/* Set a marker to ensure limit is respected */
 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,"Return is rc %d (%d) and marker is %d (42)",
    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 */
 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 */ASSERT_TRUE(rc >0,"");/* Run status indicator is ON */ASSERT_TRUE(rc >1&& tab_rp_bits[1]==0xFF,"");/* Print additional data as string */if(rc >2){printf("Additional data: ");for(i=2; i < rc; i++){printf("%c", tab_rp_bits[i]);}printf("\n");}/* Save original timeout */modbus_get_response_timeout(ctx,&old_response_to_sec,&old_response_to_usec);modbus_get_byte_timeout(ctx,&old_byte_to_sec,&old_byte_to_usec);

 rc =modbus_set_response_timeout(ctx,0,0);printf("1/6 Invalid response timeout (zero): ");ASSERT_TRUE(rc ==-1&& errno == EINVAL,"");

 rc =modbus_set_response_timeout(ctx,0,1000000);printf("2/6 Invalid response timeout (too large us): ");ASSERT_TRUE(rc ==-1&& errno == EINVAL,"");

 rc =modbus_set_byte_timeout(ctx,0,1000000);printf("3/6 Invalid byte timeout (too large us): ");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);printf("4/6 1us response timeout: ");if(rc ==-1&& errno == ETIMEDOUT){printf("OK\n");}else{printf("FAILED (can fail on some platforms)\n");}/* A wait and flush operation is done by the error recovery code of
  * libmodbus but after a sleep of current response timeout
  * so 0 can be too short!
  * /usleep(old_response_to_sec *1000000+ old_response_to_usec);modbus_flush(ctx);/* 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);printf("5/6 Too short response timeout (0.2s < 0.5s): ");ASSERT_TRUE(rc ==-1&& errno == ETIMEDOUT,"");/* Wait for reply (0.2 + 0.4 > 0.5 s) and flush before continue */usleep(400000);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);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);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);if(use_backend == TCP){/* The test server is only able to test byte timeouts with the TCP
   * backend *//* 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);printf("1/2 Too small byte timeout (3ms < 5ms): ");ASSERT_TRUE(rc ==-1&& errno == ETIMEDOUT,"");/* Wait remaing bytes before flushing */usleep(11*5000);modbus_flush(ctx);/* 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);printf("2/2 Adapted byte timeout (7ms > 5ms): ");ASSERT_TRUE(rc ==1,"");}/* Restore original byte timeout */modbus_set_byte_timeout(ctx, old_byte_to_sec, old_byte_to_usec);/** BAD RESPONSE **/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));

 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);printf("* modbus_read_registers at special address: ");ASSERT_TRUE(rc ==-1&& errno == EMBXSBUSY,"");/** Run a few tests to challenge the server code **/if(test_server(ctx, use_backend)==-1){
  goto close;}modbus_close(ctx);modbus_free(ctx);
 ctx = NULL;/* Test init functions */printf("\nTEST INVALID INITIALIZATION:\n");
 ctx =modbus_new_rtu(NULL,1,'A',0,0);ASSERT_TRUE(ctx == NULL && errno == EINVAL,"");

 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;

close:/* Free the memory */free(tab_rp_bits);free(tab_rp_registers);/* Close the connection */modbus_close(ctx);modbus_free(ctx);return(success)?0:-1;}/* Send crafted requests to test server resilience
 and ensure proper exceptions are returned. */
int test_server(modbus_t *ctx, int use_backend){
 int rc;
 int i;/* 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};/* 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};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};const int INVALID_FC =0x42;const int INVALID_FC_REQ_LEN =6;
 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 backend_length;
 int backend_offset;if(use_backend == RTU){
  backend_length =3;
  backend_offset =1;}else{
  backend_length =7;
  backend_offset =7;}printf("\nTEST RAW REQUESTS:\n");

 uint32_t old_response_to_sec;
 uint32_t old_response_to_usec;/* This requests can generate flushes server side so we need a higher
  * response timeout than the server. The server uses the defined response
  * timeout to sleep before flushing.
  * The old timeouts are restored at the end.
  * /modbus_get_response_timeout(ctx,&old_response_to_sec,&old_response_to_usec);modbus_set_response_timeout(ctx,0,600000);

 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);printf("* modbus_receive_confirmation: ");
 rc =modbus_receive_confirmation(ctx, rsp);ASSERT_TRUE(rc ==(backend_length +12),"FAILED (%d)\n", rc);/* 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);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);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);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);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);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);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));
 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),"")modbus_set_response_timeout(ctx, old_response_to_sec, old_response_to_usec);return0;
close:modbus_set_response_timeout(ctx, old_response_to_sec, old_response_to_usec);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){
 uint8_t rsp[MODBUS_TCP_MAX_ADU_LENGTH];
 int j;for(j=0; j<2; j++){
  int rc;

  req[1]=function;if(j ==0){/* Try to read or write zero values on first iteration */
   req[4]=0x00;
   req[5]=0x00;if(bytes){/* Write query */
    req[6]=0x00;}}else{/* Try to read or write max values + 1 on second iteration */
   req[4]=(max_value >>8)&0xFF;
   req[5]= max_value &0xFF;if(bytes){/* Write query (nb values * 2 to convert in bytes for registers) */
    req[6]= bytes;}}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");}else{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,"");}return0;
close:return-1;}

After libmodbus is compiled and installed under CentOS7, there will be more executable files such as unit-test-server and unit-test-client in the project root directory, as shown in the following figure:

Use terminal tools such as xshell to open two terminals respectively, and run ./unit-test-server and ./unit-test-client respectively, as shown in the following figure:

References##

Recommended Posts

Compile and install libmodbus library under CentOS7
Compile and install LAMP under Centos 5.2
CentOs7.3 compile and install Nginx 1.9.9
Centos compile and install Git
Centos7 compile and install ntp-4.2.8p11
CentOS 6.9 compile and install python
CentOS 6 compile and install python 3
Compile and install Lnmp shell script under Linux centos
Install and configure keepalived under CentOS 5.9
CentOS Yum compile and install MySQL 5.6
CentOS 6.x compile and install Nginx
CentOS7 compile and install L(A|N)MP environment
Install Python3 and ansible under CentOS8
Install and use docker under CentOS 6.8
Install Python3 and Py under CentOS7
Linux CentOS6 compile and install Pyt
Compile and install QEMU under Ubuntu
Install Mono 3.2 and Jexus 5.4 under CentOS 6.3
CentOS7.5 source code compile and install mysql5.7.29
Centos7 compile and install MySQL8 problem record
Install Mono 2.10.8 and Jexus 5.0 under 32- and 64-bit CentOS 6.0
Compile and install nodejs and yum in Centos8
CentOS7.4 source code compile and install MySQL8.0
Install centos7 and connect
Compile Hadoop-2.7.6 under CentOS7.4
Install mysql5.7 under CentOS7
Install ActiveMQ under Centos7
Install PostgreSQL12 under CentOS7
Install CentOS under VMware
Install mysql under Centos 7
Install Jenkins under Centos 7
Install MariaDB under MariaDB Centos7
Install mysql5.1 under CentOS6.5
Install svn and configuration through yum under CentOS
Centos6.5 compile and install LNMP architecture web environment
Centos7.2 compile and install way to build phpMyAdmin
centos7 install python3 and ipython
Know Linux and install CentOS
CentOS 7 install Mono and MonoDevelop
Ubuntu 16.04 compile and install PHP 7.2
ubuntu18.04 compile and install python3.8
CentOS6.5 install Java 8 and Tomcat8
Install Oracle11gR2 database under CentOS6.9
Install MySQL under Linux (CentOS 7)
Centos6.5 install and configure mongodb
Compile and install OpenJDK8 from source code under Ubuntu 18.04.1
CentOS7 install python3 and pip3
Install Java JDK8 under CentOS6
CentOS7 install OracleJDK and JRE
CentOS6.5 install Java 8 and Tomcat8
CentOS6 install and crack Jira 7
Centos compile and install LAMP (apache-2.4.7 + mysql-5.5.35 + php 5.5.8) + Redis
CentOS6.5 install Java 8 and Tomcat8
CentOS6 install and crack confluence
Install MongoDB database under CentOS7
CentOS6 install and crack Jira 7
CentOS 6.8 under linux install mongodb
Install Mesos tutorial under CentOS7
Compile and install the open source EDA tool-Surelog on CentOS8
Centos 7 install jdk and package service service
CentOS7 yum install and start mysql