├── .gitignore ├── .vs └── modbus │ └── v14 │ └── .suo ├── README.md ├── bin └── x64 │ ├── modbus.dll │ ├── modbus.lib │ ├── modbusD.dll │ └── modbusD.lib ├── clear.bat ├── modbus.sln └── modbus ├── config.h ├── modbus-data.c ├── modbus-private.h ├── modbus-rtu-private.h ├── modbus-rtu.c ├── modbus-rtu.h ├── modbus-tcp-private.h ├── modbus-tcp.c ├── modbus-tcp.h ├── modbus-version.h ├── modbus-version.h.in ├── modbus.c ├── modbus.h ├── modbus.rc ├── modbus.vcxproj ├── modbus.vcxproj.filters └── modbus.vcxproj.user /.gitignore: -------------------------------------------------------------------------------- 1 | /modbus.VC.db 2 | /modbus.VC.VC.opendb 3 | /x64 4 | -------------------------------------------------------------------------------- /.vs/modbus/v14/.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chliang/libmodbus_windows_vs2015/92848c41a0112d97769c4e94e9f00fc6b3372f08/.vs/modbus/v14/.suo -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libmodbus_windows_vs2015 2 | 3 | libmodbus(https://github.com/stephane/libmodbus) Visual Studio 2015 project. 4 | 5 | -------------------------------------------------------------------------------- /bin/x64/modbus.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chliang/libmodbus_windows_vs2015/92848c41a0112d97769c4e94e9f00fc6b3372f08/bin/x64/modbus.dll -------------------------------------------------------------------------------- /bin/x64/modbus.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chliang/libmodbus_windows_vs2015/92848c41a0112d97769c4e94e9f00fc6b3372f08/bin/x64/modbus.lib -------------------------------------------------------------------------------- /bin/x64/modbusD.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chliang/libmodbus_windows_vs2015/92848c41a0112d97769c4e94e9f00fc6b3372f08/bin/x64/modbusD.dll -------------------------------------------------------------------------------- /bin/x64/modbusD.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chliang/libmodbus_windows_vs2015/92848c41a0112d97769c4e94e9f00fc6b3372f08/bin/x64/modbusD.lib -------------------------------------------------------------------------------- /clear.bat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chliang/libmodbus_windows_vs2015/92848c41a0112d97769c4e94e9f00fc6b3372f08/clear.bat -------------------------------------------------------------------------------- /modbus.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "modbus", "modbus\modbus.vcxproj", "{FB50B4A3-A44F-4741-AFBD-842E6520E044}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {FB50B4A3-A44F-4741-AFBD-842E6520E044}.Debug|x64.ActiveCfg = Debug|x64 15 | {FB50B4A3-A44F-4741-AFBD-842E6520E044}.Debug|x64.Build.0 = Debug|x64 16 | {FB50B4A3-A44F-4741-AFBD-842E6520E044}.Release|x64.ActiveCfg = Release|x64 17 | {FB50B4A3-A44F-4741-AFBD-842E6520E044}.Release|x64.Build.0 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /modbus/config.h: -------------------------------------------------------------------------------- 1 | /* config.h. Generated from config.h.in by configure. */ 2 | /* config.h.in. Generated from configure.ac by autoheader. */ 3 | 4 | /* Define to 1 if you have the header file. */ 5 | /* #undef HAVE_ARPA_INET_H */ 6 | 7 | /* Define to 1 if you have the declaration of `TIOCSRS485', and to 0 if you 8 | don't. */ 9 | /* #undef HAVE_DECL_TIOCSRS485 */ 10 | 11 | /* Define to 1 if you have the declaration of `__CYGWIN__', and to 0 if you 12 | don't. */ 13 | /* #undef HAVE_DECL___CYGWIN__ */ 14 | 15 | /* Define to 1 if you have the header file. */ 16 | /* #undef HAVE_DLFCN_H */ 17 | 18 | /* Define to 1 if you have the header file. */ 19 | #define HAVE_ERRNO_H 1 20 | 21 | /* Define to 1 if you have the header file. */ 22 | #define HAVE_FCNTL_H 1 23 | 24 | /* Define to 1 if you have the `fork' function. */ 25 | /* #undef HAVE_FORK */ 26 | 27 | /* Define to 1 if you have the `getaddrinfo' function. */ 28 | /* #undef HAVE_GETADDRINFO */ 29 | 30 | /* Define to 1 if you have the `gettimeofday' function. */ 31 | /* #undef HAVE_GETTIMEOFDAY */ 32 | 33 | /* Define to 1 if you have the `inet_ntoa' function. */ 34 | /* #undef HAVE_INET_NTOA */ 35 | 36 | /* Define to 1 if you have the header file. */ 37 | #define HAVE_INTTYPES_H 1 38 | 39 | /* Define to 1 if you have the header file. */ 40 | #define HAVE_LIMITS_H 1 41 | 42 | /* Define to 1 if you have the header file. */ 43 | /* #undef HAVE_LINUX_SERIAL_H */ 44 | 45 | /* Define to 1 if you have the header file. */ 46 | #define HAVE_MEMORY_H 1 47 | 48 | /* Define to 1 if you have the `memset' function. */ 49 | #define HAVE_MEMSET 1 50 | 51 | /* Define to 1 if you have the header file. */ 52 | /* #undef HAVE_NETDB_H */ 53 | 54 | /* Define to 1 if you have the header file. */ 55 | /* #undef HAVE_NETINET_IN_H */ 56 | 57 | /* Define to 1 if you have the header file. */ 58 | /* #undef HAVE_NETINET_TCP_H */ 59 | 60 | /* Define to 1 if you have the `select' function. */ 61 | /* #undef HAVE_SELECT */ 62 | 63 | /* Define to 1 if you have the `socket' function. */ 64 | /* #undef HAVE_SOCKET */ 65 | 66 | /* Define to 1 if you have the header file. */ 67 | #define HAVE_STDINT_H 1 68 | 69 | /* Define to 1 if you have the header file. */ 70 | #define HAVE_STDLIB_H 1 71 | 72 | /* Define to 1 if you have the `strerror' function. */ 73 | #define HAVE_STRERROR 1 74 | 75 | /* Define to 1 if you have the header file. */ 76 | /* #undef HAVE_STRINGS_H */ 77 | 78 | /* Define to 1 if you have the header file. */ 79 | #define HAVE_STRING_H 1 80 | 81 | /* Define to 1 if you have the `strlcpy' function. */ 82 | /* #undef HAVE_STRLCPY */ 83 | 84 | /* Define to 1 if you have the header file. */ 85 | /* #undef HAVE_SYS_IOCTL_H */ 86 | 87 | /* Define to 1 if you have the header file. */ 88 | /* #undef HAVE_SYS_SOCKET_H */ 89 | 90 | /* Define to 1 if you have the header file. */ 91 | #define HAVE_SYS_STAT_H 1 92 | 93 | /* Define to 1 if you have the header file. */ 94 | /* #undef HAVE_SYS_TIME_H */ 95 | 96 | /* Define to 1 if you have the header file. */ 97 | #define HAVE_SYS_TYPES_H 1 98 | 99 | /* Define to 1 if you have the header file. */ 100 | /* #undef HAVE_TERMIOS_H */ 101 | 102 | /* Define to 1 if you have the header file. */ 103 | #define HAVE_TIME_H 1 104 | 105 | /* Define to 1 if you have the header file. */ 106 | /* #undef HAVE_UNISTD_H */ 107 | 108 | /* Define to 1 if you have the `vfork' function. */ 109 | /* #undef HAVE_VFORK */ 110 | 111 | /* Define to 1 if you have the header file. */ 112 | /* #undef HAVE_VFORK_H */ 113 | 114 | /* Define to 1 if you have the header file. */ 115 | #define HAVE_WINSOCK2_H 1 116 | 117 | /* Define to 1 if `fork' works. */ 118 | /* #undef HAVE_WORKING_FORK */ 119 | 120 | /* Define to 1 if `vfork' works. */ 121 | /* #undef HAVE_WORKING_VFORK */ 122 | 123 | /* Define to the sub-directory in which libtool stores uninstalled libraries. 124 | */ 125 | /* #undef LT_OBJDIR */ 126 | 127 | /* Name of package */ 128 | #define PACKAGE "libmodbus" 129 | 130 | /* Define to the address where bug reports for this package should be sent. */ 131 | #define PACKAGE_BUGREPORT "https://github.com/stephane/libmodbus/issues" 132 | 133 | /* Define to the full name of this package. */ 134 | #define PACKAGE_NAME "libmodbus" 135 | 136 | /* Define to the full name and version of this package. */ 137 | #define PACKAGE_STRING "libmodbus 3.1.7" 138 | 139 | /* Define to the one symbol short name of this package. */ 140 | #define PACKAGE_TARNAME "libmodbus" 141 | 142 | /* Define to the home page for this package. */ 143 | #define PACKAGE_URL "" 144 | 145 | /* Define to the version of this package. */ 146 | #define PACKAGE_VERSION "3.1.7" 147 | 148 | /* Define to 1 if you have the ANSI C header files. */ 149 | #define STDC_HEADERS 1 150 | 151 | /* Define to 1 if you can safely include both and . */ 152 | /* #undef TIME_WITH_SYS_TIME */ 153 | 154 | /* Version number of package */ 155 | #define VERSION "3.1.7" 156 | 157 | /* Define to empty if `const' does not conform to ANSI C. */ 158 | /* #undef const */ 159 | 160 | /* Define to `int' if does not define. */ 161 | /* #undef pid_t */ 162 | 163 | /* Define to `unsigned int' if does not define. */ 164 | /* #undef size_t */ 165 | 166 | /* Define as `fork' if `vfork' does not work. */ 167 | #define vfork fork 168 | -------------------------------------------------------------------------------- /modbus/modbus-data.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © Stéphane Raimbault 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | 7 | #include 8 | 9 | // clang-format off 10 | #ifndef _MSC_VER 11 | # include 12 | #else 13 | # include "stdint.h" 14 | #endif 15 | 16 | #include 17 | #include 18 | 19 | #if defined(_WIN32) 20 | # include 21 | #else 22 | # include 23 | #endif 24 | 25 | #include "config.h" 26 | 27 | #include "modbus.h" 28 | 29 | #if defined(HAVE_BYTESWAP_H) 30 | # include 31 | #endif 32 | 33 | #if defined(__APPLE__) 34 | # include 35 | # define bswap_16 OSSwapInt16 36 | # define bswap_32 OSSwapInt32 37 | # define bswap_64 OSSwapInt64 38 | #endif 39 | 40 | #if defined(__GNUC__) 41 | # define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__ * 10) 42 | # if GCC_VERSION >= 430 43 | // Since GCC >= 4.30, GCC provides __builtin_bswapXX() alternatives so we switch to them 44 | # undef bswap_32 45 | # define bswap_32 __builtin_bswap32 46 | # endif 47 | # if GCC_VERSION >= 480 48 | # undef bswap_16 49 | # define bswap_16 __builtin_bswap16 50 | # endif 51 | #endif 52 | 53 | #if defined(_MSC_VER) && (_MSC_VER >= 1400) 54 | # define bswap_32 _byteswap_ulong 55 | # define bswap_16 _byteswap_ushort 56 | #endif 57 | 58 | #if !defined(bswap_16) 59 | # warning "Fallback on C functions for bswap_16" 60 | static inline uint16_t bswap_16(uint16_t x) 61 | { 62 | return (x >> 8) | (x << 8); 63 | } 64 | #endif 65 | 66 | #if !defined(bswap_32) 67 | # warning "Fallback on C functions for bswap_32" 68 | static inline uint32_t bswap_32(uint32_t x) 69 | { 70 | return (bswap_16(x & 0xffff) << 16) | (bswap_16(x >> 16)); 71 | } 72 | #endif 73 | // clang-format on 74 | 75 | /* Sets many bits from a single byte value (all 8 bits of the byte value are 76 | set) */ 77 | void modbus_set_bits_from_byte(uint8_t *dest, int idx, const uint8_t value) 78 | { 79 | int i; 80 | 81 | for (i = 0; i < 8; i++) { 82 | dest[idx + i] = (value & (1 << i)) ? 1 : 0; 83 | } 84 | } 85 | 86 | /* Sets many bits from a table of bytes (only the bits between idx and 87 | idx + nb_bits are set) */ 88 | void modbus_set_bits_from_bytes(uint8_t *dest, 89 | int idx, 90 | unsigned int nb_bits, 91 | const uint8_t *tab_byte) 92 | { 93 | unsigned int i; 94 | int shift = 0; 95 | 96 | for (i = idx; i < idx + nb_bits; i++) { 97 | dest[i] = tab_byte[(i - idx) / 8] & (1 << shift) ? 1 : 0; 98 | /* gcc doesn't like: shift = (++shift) % 8; */ 99 | shift++; 100 | shift %= 8; 101 | } 102 | } 103 | 104 | /* Gets the byte value from many bits. 105 | To obtain a full byte, set nb_bits to 8. */ 106 | uint8_t modbus_get_byte_from_bits(const uint8_t *src, int idx, unsigned int nb_bits) 107 | { 108 | unsigned int i; 109 | uint8_t value = 0; 110 | 111 | if (nb_bits > 8) { 112 | /* Assert is ignored if NDEBUG is set */ 113 | assert(nb_bits < 8); 114 | nb_bits = 8; 115 | } 116 | 117 | for (i = 0; i < nb_bits; i++) { 118 | value |= (src[idx + i] << i); 119 | } 120 | 121 | return value; 122 | } 123 | 124 | /* Get a float from 4 bytes (Modbus) without any conversion (ABCD) */ 125 | float modbus_get_float_abcd(const uint16_t *src) 126 | { 127 | float f; 128 | uint32_t i; 129 | uint8_t a, b, c, d; 130 | 131 | a = (src[0] >> 8) & 0xFF; 132 | b = (src[0] >> 0) & 0xFF; 133 | c = (src[1] >> 8) & 0xFF; 134 | d = (src[1] >> 0) & 0xFF; 135 | 136 | i = (a << 24) | (b << 16) | (c << 8) | (d << 0); 137 | memcpy(&f, &i, 4); 138 | 139 | return f; 140 | } 141 | 142 | /* Get a float from 4 bytes (Modbus) in inversed format (DCBA) */ 143 | float modbus_get_float_dcba(const uint16_t *src) 144 | { 145 | float f; 146 | uint32_t i; 147 | uint8_t a, b, c, d; 148 | 149 | a = (src[0] >> 8) & 0xFF; 150 | b = (src[0] >> 0) & 0xFF; 151 | c = (src[1] >> 8) & 0xFF; 152 | d = (src[1] >> 0) & 0xFF; 153 | 154 | i = (d << 24) | (c << 16) | (b << 8) | (a << 0); 155 | memcpy(&f, &i, 4); 156 | 157 | return f; 158 | } 159 | 160 | /* Get a float from 4 bytes (Modbus) with swapped bytes (BADC) */ 161 | float modbus_get_float_badc(const uint16_t *src) 162 | { 163 | float f; 164 | uint32_t i; 165 | uint8_t a, b, c, d; 166 | 167 | a = (src[0] >> 8) & 0xFF; 168 | b = (src[0] >> 0) & 0xFF; 169 | c = (src[1] >> 8) & 0xFF; 170 | d = (src[1] >> 0) & 0xFF; 171 | 172 | i = (b << 24) | (a << 16) | (d << 8) | (c << 0); 173 | memcpy(&f, &i, 4); 174 | 175 | return f; 176 | } 177 | 178 | /* Get a float from 4 bytes (Modbus) with swapped words (CDAB) */ 179 | float modbus_get_float_cdab(const uint16_t *src) 180 | { 181 | float f; 182 | uint32_t i; 183 | uint8_t a, b, c, d; 184 | 185 | a = (src[0] >> 8) & 0xFF; 186 | b = (src[0] >> 0) & 0xFF; 187 | c = (src[1] >> 8) & 0xFF; 188 | d = (src[1] >> 0) & 0xFF; 189 | 190 | i = (c << 24) | (d << 16) | (a << 8) | (b << 0); 191 | memcpy(&f, &i, 4); 192 | 193 | return f; 194 | } 195 | 196 | /* DEPRECATED - Get a float from 4 bytes in sort of Modbus format */ 197 | float modbus_get_float(const uint16_t *src) 198 | { 199 | float f; 200 | uint32_t i; 201 | 202 | i = (((uint32_t) src[1]) << 16) + src[0]; 203 | memcpy(&f, &i, sizeof(float)); 204 | 205 | return f; 206 | } 207 | 208 | /* Set a float to 4 bytes for Modbus w/o any conversion (ABCD) */ 209 | void modbus_set_float_abcd(float f, uint16_t *dest) 210 | { 211 | uint32_t i; 212 | uint8_t *out = (uint8_t *) dest; 213 | uint8_t a, b, c, d; 214 | 215 | memcpy(&i, &f, sizeof(uint32_t)); 216 | a = (i >> 24) & 0xFF; 217 | b = (i >> 16) & 0xFF; 218 | c = (i >> 8) & 0xFF; 219 | d = (i >> 0) & 0xFF; 220 | 221 | out[0] = a; 222 | out[1] = b; 223 | out[2] = c; 224 | out[3] = d; 225 | } 226 | 227 | /* Set a float to 4 bytes for Modbus with byte and word swap conversion (DCBA) */ 228 | void modbus_set_float_dcba(float f, uint16_t *dest) 229 | { 230 | uint32_t i; 231 | uint8_t *out = (uint8_t *) dest; 232 | uint8_t a, b, c, d; 233 | 234 | memcpy(&i, &f, sizeof(uint32_t)); 235 | a = (i >> 24) & 0xFF; 236 | b = (i >> 16) & 0xFF; 237 | c = (i >> 8) & 0xFF; 238 | d = (i >> 0) & 0xFF; 239 | 240 | out[0] = d; 241 | out[1] = c; 242 | out[2] = b; 243 | out[3] = a; 244 | } 245 | 246 | /* Set a float to 4 bytes for Modbus with byte swap conversion (BADC) */ 247 | void modbus_set_float_badc(float f, uint16_t *dest) 248 | { 249 | uint32_t i; 250 | uint8_t *out = (uint8_t *) dest; 251 | uint8_t a, b, c, d; 252 | 253 | memcpy(&i, &f, sizeof(uint32_t)); 254 | a = (i >> 24) & 0xFF; 255 | b = (i >> 16) & 0xFF; 256 | c = (i >> 8) & 0xFF; 257 | d = (i >> 0) & 0xFF; 258 | 259 | out[0] = b; 260 | out[1] = a; 261 | out[2] = d; 262 | out[3] = c; 263 | } 264 | 265 | /* Set a float to 4 bytes for Modbus with word swap conversion (CDAB) */ 266 | void modbus_set_float_cdab(float f, uint16_t *dest) 267 | { 268 | uint32_t i; 269 | uint8_t *out = (uint8_t *) dest; 270 | uint8_t a, b, c, d; 271 | 272 | memcpy(&i, &f, sizeof(uint32_t)); 273 | a = (i >> 24) & 0xFF; 274 | b = (i >> 16) & 0xFF; 275 | c = (i >> 8) & 0xFF; 276 | d = (i >> 0) & 0xFF; 277 | 278 | out[0] = c; 279 | out[1] = d; 280 | out[2] = a; 281 | out[3] = b; 282 | } 283 | 284 | /* DEPRECATED - Set a float to 4 bytes in a sort of Modbus format! */ 285 | void modbus_set_float(float f, uint16_t *dest) 286 | { 287 | uint32_t i; 288 | 289 | memcpy(&i, &f, sizeof(uint32_t)); 290 | dest[0] = (uint16_t) i; 291 | dest[1] = (uint16_t) (i >> 16); 292 | } 293 | -------------------------------------------------------------------------------- /modbus/modbus-private.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © Stéphane Raimbault 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | 7 | #ifndef MODBUS_PRIVATE_H 8 | #define MODBUS_PRIVATE_H 9 | 10 | // clang-format off 11 | #ifndef _MSC_VER 12 | # include 13 | # include 14 | #else 15 | # include "stdint.h" 16 | # include 17 | typedef int ssize_t; 18 | #endif 19 | // clang-format on 20 | #include "config.h" 21 | #include 22 | 23 | #include "modbus.h" 24 | 25 | MODBUS_BEGIN_DECLS 26 | 27 | /* It's not really the minimal length (the real one is report slave ID 28 | * in RTU (4 bytes)) but it's a convenient size to use in RTU or TCP 29 | * communications to read many values or write a single one. 30 | * Maximum between : 31 | * - HEADER_LENGTH_TCP (7) + function (1) + address (2) + number (2) 32 | * - HEADER_LENGTH_RTU (1) + function (1) + address (2) + number (2) + CRC (2) 33 | */ 34 | #define _MIN_REQ_LENGTH 12 35 | 36 | #define _REPORT_SLAVE_ID 180 37 | 38 | #define _MODBUS_EXCEPTION_RSP_LENGTH 5 39 | 40 | /* Timeouts in microsecond (0.5 s) */ 41 | #define _RESPONSE_TIMEOUT 500000 42 | #define _BYTE_TIMEOUT 500000 43 | 44 | typedef enum { 45 | _MODBUS_BACKEND_TYPE_RTU = 0, 46 | _MODBUS_BACKEND_TYPE_TCP 47 | } modbus_backend_type_t; 48 | 49 | /* 50 | * ---------- Request Indication ---------- 51 | * | Client | ---------------------->| Server | 52 | * ---------- Confirmation Response ---------- 53 | */ 54 | typedef enum { 55 | /* Request message on the server side */ 56 | MSG_INDICATION, 57 | /* Request message on the client side */ 58 | MSG_CONFIRMATION 59 | } msg_type_t; 60 | 61 | /* This structure reduces the number of params in functions and so 62 | * optimizes the speed of execution (~ 37%). */ 63 | typedef struct _sft { 64 | int slave; 65 | int function; 66 | int t_id; 67 | } sft_t; 68 | 69 | typedef struct _modbus_backend { 70 | unsigned int backend_type; 71 | unsigned int header_length; 72 | unsigned int checksum_length; 73 | unsigned int max_adu_length; 74 | int (*set_slave)(modbus_t *ctx, int slave); 75 | int (*build_request_basis)( 76 | modbus_t *ctx, int function, int addr, int nb, uint8_t *req); 77 | int (*build_response_basis)(sft_t *sft, uint8_t *rsp); 78 | int (*prepare_response_tid)(const uint8_t *req, int *req_length); 79 | int (*send_msg_pre)(uint8_t *req, int req_length); 80 | ssize_t (*send)(modbus_t *ctx, const uint8_t *req, int req_length); 81 | int (*receive)(modbus_t *ctx, uint8_t *req); 82 | ssize_t (*recv)(modbus_t *ctx, uint8_t *rsp, int rsp_length); 83 | int (*check_integrity)(modbus_t *ctx, uint8_t *msg, const int msg_length); 84 | int (*pre_check_confirmation)(modbus_t *ctx, 85 | const uint8_t *req, 86 | const uint8_t *rsp, 87 | int rsp_length); 88 | int (*connect)(modbus_t *ctx); 89 | unsigned int (*is_connected)(modbus_t *ctx); 90 | void (*close)(modbus_t *ctx); 91 | int (*flush)(modbus_t *ctx); 92 | int (*select)(modbus_t *ctx, fd_set *rset, struct timeval *tv, int msg_length); 93 | void (*free)(modbus_t *ctx); 94 | } modbus_backend_t; 95 | 96 | struct _modbus { 97 | /* Slave address */ 98 | int slave; 99 | /* Socket or file descriptor */ 100 | int s; 101 | int debug; 102 | int error_recovery; 103 | int quirks; 104 | struct timeval response_timeout; 105 | struct timeval byte_timeout; 106 | struct timeval indication_timeout; 107 | const modbus_backend_t *backend; 108 | void *backend_data; 109 | }; 110 | 111 | void _modbus_init_common(modbus_t *ctx); 112 | void _error_print(modbus_t *ctx, const char *context); 113 | int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type); 114 | 115 | #ifndef HAVE_STRLCPY 116 | size_t strlcpy(char *dest, const char *src, size_t dest_size); 117 | #endif 118 | 119 | MODBUS_END_DECLS 120 | 121 | #endif /* MODBUS_PRIVATE_H */ 122 | -------------------------------------------------------------------------------- /modbus/modbus-rtu-private.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © Stéphane Raimbault 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | 7 | #ifndef MODBUS_RTU_PRIVATE_H 8 | #define MODBUS_RTU_PRIVATE_H 9 | 10 | #ifndef _MSC_VER 11 | #include 12 | #else 13 | #include "stdint.h" 14 | #endif 15 | 16 | #if defined(_WIN32) 17 | #include 18 | #else 19 | #include 20 | #endif 21 | 22 | #define _MODBUS_RTU_HEADER_LENGTH 1 23 | #define _MODBUS_RTU_PRESET_REQ_LENGTH 6 24 | #define _MODBUS_RTU_PRESET_RSP_LENGTH 2 25 | 26 | #define _MODBUS_RTU_CHECKSUM_LENGTH 2 27 | 28 | #if defined(_WIN32) 29 | #if !defined(ENOTSUP) 30 | #define ENOTSUP WSAEOPNOTSUPP 31 | #endif 32 | 33 | /* WIN32: struct containing serial handle and a receive buffer */ 34 | #define PY_BUF_SIZE 512 35 | 36 | struct win32_ser { 37 | /* File handle */ 38 | HANDLE fd; 39 | /* Receive buffer */ 40 | uint8_t buf[PY_BUF_SIZE]; 41 | /* Received chars */ 42 | DWORD n_bytes; 43 | }; 44 | #endif /* _WIN32 */ 45 | 46 | typedef struct _modbus_rtu { 47 | /* Device: "/dev/ttyS0", "/dev/ttyUSB0" or "/dev/tty.USA19*" on Mac OS X. */ 48 | char *device; 49 | /* Bauds: 9600, 19200, 57600, 115200, etc */ 50 | int baud; 51 | /* Data bit */ 52 | uint8_t data_bit; 53 | /* Stop bit */ 54 | uint8_t stop_bit; 55 | /* Parity: 'N', 'O', 'E' */ 56 | char parity; 57 | #if defined(_WIN32) 58 | struct win32_ser w_ser; 59 | DCB old_dcb; 60 | #else 61 | /* Save old termios settings */ 62 | struct termios old_tios; 63 | #endif 64 | #if HAVE_DECL_TIOCSRS485 65 | int serial_mode; 66 | #endif 67 | #if HAVE_DECL_TIOCM_RTS 68 | int rts; 69 | int rts_delay; 70 | int onebyte_time; 71 | void (*set_rts)(modbus_t *ctx, int on); 72 | #endif 73 | /* To handle many slaves on the same link */ 74 | int confirmation_to_ignore; 75 | } modbus_rtu_t; 76 | 77 | #endif /* MODBUS_RTU_PRIVATE_H */ 78 | -------------------------------------------------------------------------------- /modbus/modbus-rtu.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © Stéphane Raimbault 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #ifndef _MSC_VER 13 | #include 14 | #endif 15 | #include "modbus-private.h" 16 | #include 17 | 18 | #include "modbus-rtu-private.h" 19 | #include "modbus-rtu.h" 20 | 21 | #if HAVE_DECL_TIOCSRS485 || HAVE_DECL_TIOCM_RTS 22 | #include 23 | #endif 24 | 25 | #if HAVE_DECL_TIOCSRS485 26 | #include 27 | #endif 28 | 29 | /* Table of CRC values for high-order byte */ 30 | static const uint8_t table_crc_hi[] = { 31 | 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 32 | 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 33 | 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 34 | 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 35 | 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 36 | 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 37 | 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 38 | 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 39 | 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 40 | 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 41 | 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 42 | 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 43 | 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 44 | 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 45 | 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 46 | 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 47 | 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 48 | 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 49 | 0x00, 0xC1, 0x81, 0x40}; 50 | 51 | /* Table of CRC values for low-order byte */ 52 | static const uint8_t table_crc_lo[] = { 53 | 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 54 | 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 55 | 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 56 | 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 57 | 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 58 | 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 59 | 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 60 | 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 61 | 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 62 | 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 63 | 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 64 | 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 65 | 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 66 | 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 67 | 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 68 | 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 69 | 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 70 | 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 71 | 0x41, 0x81, 0x80, 0x40}; 72 | 73 | /* Define the slave ID of the remote device to talk in master mode or set the 74 | * internal slave ID in slave mode */ 75 | static int _modbus_set_slave(modbus_t *ctx, int slave) 76 | { 77 | int max_slave = (ctx->quirks & MODBUS_QUIRK_MAX_SLAVE) ? 255 : 247; 78 | 79 | /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */ 80 | if (slave >= 0 && slave <= max_slave) { 81 | ctx->slave = slave; 82 | } else { 83 | errno = EINVAL; 84 | return -1; 85 | } 86 | 87 | return 0; 88 | } 89 | 90 | /* Builds a RTU request header */ 91 | static int _modbus_rtu_build_request_basis( 92 | modbus_t *ctx, int function, int addr, int nb, uint8_t *req) 93 | { 94 | assert(ctx->slave != -1); 95 | req[0] = ctx->slave; 96 | req[1] = function; 97 | req[2] = addr >> 8; 98 | req[3] = addr & 0x00ff; 99 | req[4] = nb >> 8; 100 | req[5] = nb & 0x00ff; 101 | 102 | return _MODBUS_RTU_PRESET_REQ_LENGTH; 103 | } 104 | 105 | /* Builds a RTU response header */ 106 | static int _modbus_rtu_build_response_basis(sft_t *sft, uint8_t *rsp) 107 | { 108 | /* In this case, the slave is certainly valid because a check is already 109 | * done in _modbus_rtu_listen */ 110 | rsp[0] = sft->slave; 111 | rsp[1] = sft->function; 112 | 113 | return _MODBUS_RTU_PRESET_RSP_LENGTH; 114 | } 115 | 116 | static uint16_t crc16(uint8_t *buffer, uint16_t buffer_length) 117 | { 118 | uint8_t crc_hi = 0xFF; /* high CRC byte initialized */ 119 | uint8_t crc_lo = 0xFF; /* low CRC byte initialized */ 120 | unsigned int i; /* will index into CRC lookup */ 121 | 122 | /* pass through message buffer */ 123 | while (buffer_length--) { 124 | i = crc_lo ^ *buffer++; /* calculate the CRC */ 125 | crc_lo = crc_hi ^ table_crc_hi[i]; 126 | crc_hi = table_crc_lo[i]; 127 | } 128 | 129 | return (crc_hi << 8 | crc_lo); 130 | } 131 | 132 | static int _modbus_rtu_prepare_response_tid(const uint8_t *req, int *req_length) 133 | { 134 | (*req_length) -= _MODBUS_RTU_CHECKSUM_LENGTH; 135 | /* No TID */ 136 | return 0; 137 | } 138 | 139 | static int _modbus_rtu_send_msg_pre(uint8_t *req, int req_length) 140 | { 141 | uint16_t crc = crc16(req, req_length); 142 | 143 | /* According to the MODBUS specs (p. 14), the low order byte of the CRC comes 144 | * first in the RTU message */ 145 | req[req_length++] = crc & 0x00FF; 146 | req[req_length++] = crc >> 8; 147 | 148 | return req_length; 149 | } 150 | 151 | #if defined(_WIN32) 152 | 153 | /* This simple implementation is sort of a substitute of the select() call, 154 | * working this way: the win32_ser_select() call tries to read some data from 155 | * the serial port, setting the timeout as the select() call would. Data read is 156 | * stored into the receive buffer, that is then consumed by the win32_ser_read() 157 | * call. So win32_ser_select() does both the event waiting and the reading, 158 | * while win32_ser_read() only consumes the receive buffer. 159 | */ 160 | 161 | static void win32_ser_init(struct win32_ser *ws) 162 | { 163 | /* Clear everything */ 164 | memset(ws, 0x00, sizeof(struct win32_ser)); 165 | 166 | /* Set file handle to invalid */ 167 | ws->fd = INVALID_HANDLE_VALUE; 168 | } 169 | 170 | /* FIXME Try to remove length_to_read -> max_len argument, only used by win32 */ 171 | static int win32_ser_select(struct win32_ser *ws, int max_len, const struct timeval *tv) 172 | { 173 | COMMTIMEOUTS comm_to; 174 | unsigned int msec = 0; 175 | 176 | /* Check if some data still in the buffer to be consumed */ 177 | if (ws->n_bytes > 0) { 178 | return 1; 179 | } 180 | 181 | /* Setup timeouts like select() would do. 182 | FIXME Please someone on Windows can look at this? 183 | Does it possible to use WaitCommEvent? 184 | When tv is NULL, MAXDWORD isn't infinite! 185 | */ 186 | if (tv == NULL) { 187 | msec = MAXDWORD; 188 | } else { 189 | msec = tv->tv_sec * 1000 + tv->tv_usec / 1000; 190 | if (msec < 1) 191 | msec = 1; 192 | } 193 | 194 | comm_to.ReadIntervalTimeout = msec; 195 | comm_to.ReadTotalTimeoutMultiplier = 0; 196 | comm_to.ReadTotalTimeoutConstant = msec; 197 | comm_to.WriteTotalTimeoutMultiplier = 0; 198 | comm_to.WriteTotalTimeoutConstant = 1000; 199 | SetCommTimeouts(ws->fd, &comm_to); 200 | 201 | /* Read some bytes */ 202 | if ((max_len > PY_BUF_SIZE) || (max_len < 0)) { 203 | max_len = PY_BUF_SIZE; 204 | } 205 | 206 | if (ReadFile(ws->fd, &ws->buf, max_len, &ws->n_bytes, NULL)) { 207 | /* Check if some bytes available */ 208 | if (ws->n_bytes > 0) { 209 | /* Some bytes read */ 210 | return 1; 211 | } else { 212 | /* Just timed out */ 213 | return 0; 214 | } 215 | } else { 216 | /* Some kind of error */ 217 | return -1; 218 | } 219 | } 220 | 221 | static int win32_ser_read(struct win32_ser *ws, uint8_t *p_msg, unsigned int max_len) 222 | { 223 | unsigned int n = ws->n_bytes; 224 | 225 | if (max_len < n) { 226 | n = max_len; 227 | } 228 | 229 | if (n > 0) { 230 | memcpy(p_msg, ws->buf, n); 231 | } 232 | 233 | ws->n_bytes -= n; 234 | 235 | return n; 236 | } 237 | #endif 238 | 239 | #if HAVE_DECL_TIOCM_RTS 240 | static void _modbus_rtu_ioctl_rts(modbus_t *ctx, int on) 241 | { 242 | int fd = ctx->s; 243 | int flags; 244 | 245 | ioctl(fd, TIOCMGET, &flags); 246 | if (on) { 247 | flags |= TIOCM_RTS; 248 | } else { 249 | flags &= ~TIOCM_RTS; 250 | } 251 | ioctl(fd, TIOCMSET, &flags); 252 | } 253 | #endif 254 | 255 | static ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_length) 256 | { 257 | #if defined(_WIN32) 258 | modbus_rtu_t *ctx_rtu = ctx->backend_data; 259 | DWORD n_bytes = 0; 260 | return (WriteFile(ctx_rtu->w_ser.fd, req, req_length, &n_bytes, NULL)) 261 | ? (ssize_t) n_bytes 262 | : -1; 263 | #else 264 | #if HAVE_DECL_TIOCM_RTS 265 | modbus_rtu_t *ctx_rtu = ctx->backend_data; 266 | if (ctx_rtu->rts != MODBUS_RTU_RTS_NONE) { 267 | ssize_t size; 268 | 269 | if (ctx->debug) { 270 | fprintf(stderr, "Sending request using RTS signal\n"); 271 | } 272 | 273 | ctx_rtu->set_rts(ctx, ctx_rtu->rts == MODBUS_RTU_RTS_UP); 274 | usleep(ctx_rtu->rts_delay); 275 | 276 | size = write(ctx->s, req, req_length); 277 | 278 | usleep(ctx_rtu->onebyte_time * req_length + ctx_rtu->rts_delay); 279 | ctx_rtu->set_rts(ctx, ctx_rtu->rts != MODBUS_RTU_RTS_UP); 280 | 281 | return size; 282 | } else { 283 | #endif 284 | return write(ctx->s, req, req_length); 285 | #if HAVE_DECL_TIOCM_RTS 286 | } 287 | #endif 288 | #endif 289 | } 290 | 291 | static int _modbus_rtu_receive(modbus_t *ctx, uint8_t *req) 292 | { 293 | int rc; 294 | modbus_rtu_t *ctx_rtu = ctx->backend_data; 295 | 296 | if (ctx_rtu->confirmation_to_ignore) { 297 | _modbus_receive_msg(ctx, req, MSG_CONFIRMATION); 298 | /* Ignore errors and reset the flag */ 299 | ctx_rtu->confirmation_to_ignore = FALSE; 300 | rc = 0; 301 | if (ctx->debug) { 302 | printf("Confirmation to ignore\n"); 303 | } 304 | } else { 305 | rc = _modbus_receive_msg(ctx, req, MSG_INDICATION); 306 | if (rc == 0) { 307 | /* The next expected message is a confirmation to ignore */ 308 | ctx_rtu->confirmation_to_ignore = TRUE; 309 | } 310 | } 311 | return rc; 312 | } 313 | 314 | static ssize_t _modbus_rtu_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length) 315 | { 316 | #if defined(_WIN32) 317 | return win32_ser_read(&((modbus_rtu_t *) ctx->backend_data)->w_ser, rsp, rsp_length); 318 | #else 319 | return read(ctx->s, rsp, rsp_length); 320 | #endif 321 | } 322 | 323 | static int _modbus_rtu_flush(modbus_t *); 324 | 325 | static int _modbus_rtu_pre_check_confirmation(modbus_t *ctx, 326 | const uint8_t *req, 327 | const uint8_t *rsp, 328 | int rsp_length) 329 | { 330 | /* Check responding slave is the slave we requested (except for broacast 331 | * request) */ 332 | if (req[0] != rsp[0] && req[0] != MODBUS_BROADCAST_ADDRESS) { 333 | if (ctx->debug) { 334 | fprintf(stderr, 335 | "The responding slave %d isn't the requested slave %d\n", 336 | rsp[0], 337 | req[0]); 338 | } 339 | errno = EMBBADSLAVE; 340 | return -1; 341 | } else { 342 | return 0; 343 | } 344 | } 345 | 346 | /* The check_crc16 function shall return 0 if the message is ignored and the 347 | message length if the CRC is valid. Otherwise it shall return -1 and set 348 | errno to EMBBADCRC. */ 349 | static int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg, const int msg_length) 350 | { 351 | uint16_t crc_calculated; 352 | uint16_t crc_received; 353 | int slave = msg[0]; 354 | 355 | /* Filter on the Modbus unit identifier (slave) in RTU mode to avoid useless 356 | * CRC computing. */ 357 | if (slave != ctx->slave && slave != MODBUS_BROADCAST_ADDRESS) { 358 | if (ctx->debug) { 359 | printf("Request for slave %d ignored (not %d)\n", slave, ctx->slave); 360 | } 361 | /* Following call to check_confirmation handles this error */ 362 | return 0; 363 | } 364 | 365 | crc_calculated = crc16(msg, msg_length - 2); 366 | crc_received = (msg[msg_length - 1] << 8) | msg[msg_length - 2]; 367 | 368 | /* Check CRC of msg */ 369 | if (crc_calculated == crc_received) { 370 | return msg_length; 371 | } else { 372 | if (ctx->debug) { 373 | fprintf(stderr, 374 | "ERROR CRC received 0x%0X != CRC calculated 0x%0X\n", 375 | crc_received, 376 | crc_calculated); 377 | } 378 | 379 | if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) { 380 | _modbus_rtu_flush(ctx); 381 | } 382 | errno = EMBBADCRC; 383 | return -1; 384 | } 385 | } 386 | 387 | /* Sets up a serial port for RTU communications */ 388 | #if defined(_WIN32) 389 | static int _modbus_rtu_connect(modbus_t *ctx) 390 | { 391 | DCB dcb; 392 | modbus_rtu_t *ctx_rtu = ctx->backend_data; 393 | 394 | if (ctx->debug) { 395 | printf("Opening %s at %d bauds (%c, %d, %d)\n", 396 | ctx_rtu->device, 397 | ctx_rtu->baud, 398 | ctx_rtu->parity, 399 | ctx_rtu->data_bit, 400 | ctx_rtu->stop_bit); 401 | } 402 | 403 | /* Some references here: 404 | * http://msdn.microsoft.com/en-us/library/aa450602.aspx 405 | */ 406 | win32_ser_init(&ctx_rtu->w_ser); 407 | 408 | /* ctx_rtu->device should contain a string like "COMxx:" xx being a decimal 409 | * number */ 410 | ctx_rtu->w_ser.fd = CreateFileA( 411 | ctx_rtu->device, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); 412 | 413 | /* Error checking */ 414 | if (ctx_rtu->w_ser.fd == INVALID_HANDLE_VALUE) { 415 | if (ctx->debug) { 416 | fprintf(stderr, 417 | "ERROR Can't open the device %s (LastError %d)\n", 418 | ctx_rtu->device, 419 | (int) GetLastError()); 420 | } 421 | return -1; 422 | } 423 | 424 | /* Save params */ 425 | ctx_rtu->old_dcb.DCBlength = sizeof(DCB); 426 | if (!GetCommState(ctx_rtu->w_ser.fd, &ctx_rtu->old_dcb)) { 427 | if (ctx->debug) { 428 | fprintf(stderr, 429 | "ERROR Error getting configuration (LastError %d)\n", 430 | (int) GetLastError()); 431 | } 432 | CloseHandle(ctx_rtu->w_ser.fd); 433 | ctx_rtu->w_ser.fd = INVALID_HANDLE_VALUE; 434 | return -1; 435 | } 436 | 437 | /* Build new configuration (starting from current settings) */ 438 | dcb = ctx_rtu->old_dcb; 439 | 440 | /* Speed setting */ 441 | dcb.BaudRate = ctx_rtu->baud; 442 | 443 | /* Data bits */ 444 | switch (ctx_rtu->data_bit) { 445 | case 5: 446 | dcb.ByteSize = 5; 447 | break; 448 | case 6: 449 | dcb.ByteSize = 6; 450 | break; 451 | case 7: 452 | dcb.ByteSize = 7; 453 | break; 454 | case 8: 455 | default: 456 | dcb.ByteSize = 8; 457 | break; 458 | } 459 | 460 | /* Stop bits */ 461 | if (ctx_rtu->stop_bit == 1) 462 | dcb.StopBits = ONESTOPBIT; 463 | else /* 2 */ 464 | dcb.StopBits = TWOSTOPBITS; 465 | 466 | /* Parity */ 467 | if (ctx_rtu->parity == 'N') { 468 | dcb.Parity = NOPARITY; 469 | dcb.fParity = FALSE; 470 | } else if (ctx_rtu->parity == 'E') { 471 | dcb.Parity = EVENPARITY; 472 | dcb.fParity = TRUE; 473 | } else { 474 | /* odd */ 475 | dcb.Parity = ODDPARITY; 476 | dcb.fParity = TRUE; 477 | } 478 | 479 | /* Hardware handshaking left as default settings retrieved */ 480 | 481 | /* No software handshaking */ 482 | dcb.fTXContinueOnXoff = TRUE; 483 | dcb.fOutX = FALSE; 484 | dcb.fInX = FALSE; 485 | 486 | /* Binary mode (it's the only supported on Windows anyway) */ 487 | dcb.fBinary = TRUE; 488 | 489 | /* Don't want errors to be blocking */ 490 | dcb.fAbortOnError = FALSE; 491 | 492 | /* Setup port */ 493 | if (!SetCommState(ctx_rtu->w_ser.fd, &dcb)) { 494 | if (ctx->debug) { 495 | fprintf(stderr, 496 | "ERROR Error setting new configuration (LastError %d)\n", 497 | (int) GetLastError()); 498 | } 499 | CloseHandle(ctx_rtu->w_ser.fd); 500 | ctx_rtu->w_ser.fd = INVALID_HANDLE_VALUE; 501 | return -1; 502 | } 503 | 504 | return 0; 505 | } 506 | #else 507 | 508 | static speed_t _get_termios_speed(int baud, int debug) 509 | { 510 | speed_t speed; 511 | 512 | switch (baud) { 513 | case 110: 514 | speed = B110; 515 | break; 516 | case 300: 517 | speed = B300; 518 | break; 519 | case 600: 520 | speed = B600; 521 | break; 522 | case 1200: 523 | speed = B1200; 524 | break; 525 | case 2400: 526 | speed = B2400; 527 | break; 528 | case 4800: 529 | speed = B4800; 530 | break; 531 | case 9600: 532 | speed = B9600; 533 | break; 534 | case 19200: 535 | speed = B19200; 536 | break; 537 | case 38400: 538 | speed = B38400; 539 | break; 540 | #ifdef B57600 541 | case 57600: 542 | speed = B57600; 543 | break; 544 | #endif 545 | #ifdef B115200 546 | case 115200: 547 | speed = B115200; 548 | break; 549 | #endif 550 | #ifdef B230400 551 | case 230400: 552 | speed = B230400; 553 | break; 554 | #endif 555 | #ifdef B460800 556 | case 460800: 557 | speed = B460800; 558 | break; 559 | #endif 560 | #ifdef B500000 561 | case 500000: 562 | speed = B500000; 563 | break; 564 | #endif 565 | #ifdef B576000 566 | case 576000: 567 | speed = B576000; 568 | break; 569 | #endif 570 | #ifdef B921600 571 | case 921600: 572 | speed = B921600; 573 | break; 574 | #endif 575 | #ifdef B1000000 576 | case 1000000: 577 | speed = B1000000; 578 | break; 579 | #endif 580 | #ifdef B1152000 581 | case 1152000: 582 | speed = B1152000; 583 | break; 584 | #endif 585 | #ifdef B1500000 586 | case 1500000: 587 | speed = B1500000; 588 | break; 589 | #endif 590 | #ifdef B2500000 591 | case 2500000: 592 | speed = B2500000; 593 | break; 594 | #endif 595 | #ifdef B3000000 596 | case 3000000: 597 | speed = B3000000; 598 | break; 599 | #endif 600 | #ifdef B3500000 601 | case 3500000: 602 | speed = B3500000; 603 | break; 604 | #endif 605 | #ifdef B4000000 606 | case 4000000: 607 | speed = B4000000; 608 | break; 609 | #endif 610 | default: 611 | speed = B9600; 612 | if (debug) { 613 | fprintf(stderr, "WARNING Unknown baud rate %d (B9600 used)\n", baud); 614 | } 615 | } 616 | 617 | return speed; 618 | } 619 | 620 | /* POSIX */ 621 | static int _modbus_rtu_connect(modbus_t *ctx) 622 | { 623 | struct termios tios; 624 | int flags; 625 | speed_t speed; 626 | modbus_rtu_t *ctx_rtu = ctx->backend_data; 627 | 628 | if (ctx->debug) { 629 | printf("Opening %s at %d bauds (%c, %d, %d)\n", 630 | ctx_rtu->device, 631 | ctx_rtu->baud, 632 | ctx_rtu->parity, 633 | ctx_rtu->data_bit, 634 | ctx_rtu->stop_bit); 635 | } 636 | 637 | /* The O_NOCTTY flag tells UNIX that this program doesn't want 638 | to be the "controlling terminal" for that port. If you 639 | don't specify this then any input (such as keyboard abort 640 | signals and so forth) will affect your process 641 | 642 | Timeouts are ignored in canonical input mode or when the 643 | NDELAY option is set on the file via open or fcntl */ 644 | flags = O_RDWR | O_NOCTTY | O_NDELAY | O_EXCL; 645 | #ifdef O_CLOEXEC 646 | flags |= O_CLOEXEC; 647 | #endif 648 | 649 | ctx->s = open(ctx_rtu->device, flags); 650 | if (ctx->s < 0) { 651 | if (ctx->debug) { 652 | fprintf(stderr, 653 | "ERROR Can't open the device %s (%s)\n", 654 | ctx_rtu->device, 655 | strerror(errno)); 656 | } 657 | return -1; 658 | } 659 | 660 | /* Save */ 661 | tcgetattr(ctx->s, &ctx_rtu->old_tios); 662 | 663 | memset(&tios, 0, sizeof(struct termios)); 664 | 665 | /* C_ISPEED Input baud (new interface) 666 | C_OSPEED Output baud (new interface) 667 | */ 668 | 669 | /* Set the baud rate */ 670 | 671 | /* 672 | On MacOS, constants of baud rates are equal to the integer in argument but 673 | that's not the case under Linux so we have to find the corresponding 674 | constant. Until the code is upgraded to termios2, the list of possible 675 | values is limited (no 14400 for example). 676 | */ 677 | if (9600 == B9600) { 678 | speed = ctx_rtu->baud; 679 | } else { 680 | speed = _get_termios_speed(ctx_rtu->baud, ctx->debug); 681 | } 682 | 683 | if ((cfsetispeed(&tios, speed) < 0) || (cfsetospeed(&tios, speed) < 0)) { 684 | close(ctx->s); 685 | ctx->s = -1; 686 | return -1; 687 | } 688 | 689 | /* C_CFLAG Control options 690 | CLOCAL Local line - do not change "owner" of port 691 | CREAD Enable receiver 692 | */ 693 | tios.c_cflag |= (CREAD | CLOCAL); 694 | /* CSIZE, HUPCL, CRTSCTS (hardware flow control) */ 695 | 696 | /* Set data bits (5, 6, 7, 8 bits) 697 | CSIZE Bit mask for data bits 698 | */ 699 | tios.c_cflag &= ~CSIZE; 700 | switch (ctx_rtu->data_bit) { 701 | case 5: 702 | tios.c_cflag |= CS5; 703 | break; 704 | case 6: 705 | tios.c_cflag |= CS6; 706 | break; 707 | case 7: 708 | tios.c_cflag |= CS7; 709 | break; 710 | case 8: 711 | default: 712 | tios.c_cflag |= CS8; 713 | break; 714 | } 715 | 716 | /* Stop bit (1 or 2) */ 717 | if (ctx_rtu->stop_bit == 1) 718 | tios.c_cflag &= ~CSTOPB; 719 | else /* 2 */ 720 | tios.c_cflag |= CSTOPB; 721 | 722 | /* PARENB Enable parity bit 723 | PARODD Use odd parity instead of even */ 724 | if (ctx_rtu->parity == 'N') { 725 | /* None */ 726 | tios.c_cflag &= ~PARENB; 727 | } else if (ctx_rtu->parity == 'E') { 728 | /* Even */ 729 | tios.c_cflag |= PARENB; 730 | tios.c_cflag &= ~PARODD; 731 | } else { 732 | /* Odd */ 733 | tios.c_cflag |= PARENB; 734 | tios.c_cflag |= PARODD; 735 | } 736 | 737 | /* Read the man page of termios if you need more information. */ 738 | 739 | /* This field isn't used on POSIX systems 740 | tios.c_line = 0; 741 | */ 742 | 743 | /* C_LFLAG Line options 744 | 745 | ISIG Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals 746 | ICANON Enable canonical input (else raw) 747 | XCASE Map uppercase \lowercase (obsolete) 748 | ECHO Enable echoing of input characters 749 | ECHOE Echo erase character as BS-SP-BS 750 | ECHOK Echo NL after kill character 751 | ECHONL Echo NL 752 | NOFLSH Disable flushing of input buffers after 753 | interrupt or quit characters 754 | IEXTEN Enable extended functions 755 | ECHOCTL Echo control characters as ^char and delete as ~? 756 | ECHOPRT Echo erased character as character erased 757 | ECHOKE BS-SP-BS entire line on line kill 758 | FLUSHO Output being flushed 759 | PENDIN Retype pending input at next read or input char 760 | TOSTOP Send SIGTTOU for background output 761 | 762 | Canonical input is line-oriented. Input characters are put 763 | into a buffer which can be edited interactively by the user 764 | until a CR (carriage return) or LF (line feed) character is 765 | received. 766 | 767 | Raw input is unprocessed. Input characters are passed 768 | through exactly as they are received, when they are 769 | received. Generally you'll deselect the ICANON, ECHO, 770 | ECHOE, and ISIG options when using raw input 771 | */ 772 | 773 | /* Raw input */ 774 | tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); 775 | 776 | /* C_IFLAG Input options 777 | 778 | Constant Description 779 | INPCK Enable parity check 780 | IGNPAR Ignore parity errors 781 | PARMRK Mark parity errors 782 | ISTRIP Strip parity bits 783 | IXON Enable software flow control (outgoing) 784 | IXOFF Enable software flow control (incoming) 785 | IXANY Allow any character to start flow again 786 | IGNBRK Ignore break condition 787 | BRKINT Send a SIGINT when a break condition is detected 788 | INLCR Map NL to CR 789 | IGNCR Ignore CR 790 | ICRNL Map CR to NL 791 | IUCLC Map uppercase to lowercase 792 | IMAXBEL Echo BEL on input line too long 793 | */ 794 | if (ctx_rtu->parity == 'N') { 795 | /* None */ 796 | tios.c_iflag &= ~INPCK; 797 | } else { 798 | tios.c_iflag |= INPCK; 799 | } 800 | 801 | /* Software flow control is disabled */ 802 | tios.c_iflag &= ~(IXON | IXOFF | IXANY); 803 | 804 | /* C_OFLAG Output options 805 | OPOST Postprocess output (not set = raw output) 806 | ONLCR Map NL to CR-NL 807 | 808 | ONCLR ant others needs OPOST to be enabled 809 | */ 810 | 811 | /* Raw output */ 812 | tios.c_oflag &= ~OPOST; 813 | 814 | /* C_CC Control characters 815 | VMIN Minimum number of characters to read 816 | VTIME Time to wait for data (tenths of seconds) 817 | 818 | UNIX serial interface drivers provide the ability to 819 | specify character and packet timeouts. Two elements of the 820 | c_cc array are used for timeouts: VMIN and VTIME. Timeouts 821 | are ignored in canonical input mode or when the NDELAY 822 | option is set on the file via open or fcntl. 823 | 824 | VMIN specifies the minimum number of characters to read. If 825 | it is set to 0, then the VTIME value specifies the time to 826 | wait for every character read. Note that this does not mean 827 | that a read call for N bytes will wait for N characters to 828 | come in. Rather, the timeout will apply to the first 829 | character and the read call will return the number of 830 | characters immediately available (up to the number you 831 | request). 832 | 833 | If VMIN is non-zero, VTIME specifies the time to wait for 834 | the first character read. If a character is read within the 835 | time given, any read will block (wait) until all VMIN 836 | characters are read. That is, once the first character is 837 | read, the serial interface driver expects to receive an 838 | entire packet of characters (VMIN bytes total). If no 839 | character is read within the time allowed, then the call to 840 | read returns 0. This method allows you to tell the serial 841 | driver you need exactly N bytes and any read call will 842 | return 0 or N bytes. However, the timeout only applies to 843 | the first character read, so if for some reason the driver 844 | misses one character inside the N byte packet then the read 845 | call could block forever waiting for additional input 846 | characters. 847 | 848 | VTIME specifies the amount of time to wait for incoming 849 | characters in tenths of seconds. If VTIME is set to 0 (the 850 | default), reads will block (wait) indefinitely unless the 851 | NDELAY option is set on the port with open or fcntl. 852 | */ 853 | /* Unused because we use open with the NDELAY option */ 854 | tios.c_cc[VMIN] = 0; 855 | tios.c_cc[VTIME] = 0; 856 | 857 | if (tcsetattr(ctx->s, TCSANOW, &tios) < 0) { 858 | close(ctx->s); 859 | ctx->s = -1; 860 | return -1; 861 | } 862 | 863 | return 0; 864 | } 865 | #endif 866 | 867 | // FIXME Temporary solution before rewriting Windows RTU backend 868 | static unsigned int _modbus_rtu_is_connected(modbus_t *ctx) 869 | { 870 | #if defined(_WIN32) 871 | modbus_rtu_t *ctx_rtu = ctx->backend_data; 872 | 873 | /* Check if file handle is valid */ 874 | return ctx_rtu->w_ser.fd != INVALID_HANDLE_VALUE; 875 | #else 876 | return ctx->s >= 0; 877 | #endif 878 | } 879 | 880 | int modbus_rtu_set_serial_mode(modbus_t *ctx, int mode) 881 | { 882 | if (ctx == NULL) { 883 | errno = EINVAL; 884 | return -1; 885 | } 886 | 887 | if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) { 888 | #if HAVE_DECL_TIOCSRS485 889 | modbus_rtu_t *ctx_rtu = ctx->backend_data; 890 | struct serial_rs485 rs485conf; 891 | 892 | if (mode == MODBUS_RTU_RS485) { 893 | // Get 894 | if (ioctl(ctx->s, TIOCGRS485, &rs485conf) < 0) { 895 | return -1; 896 | } 897 | // Set 898 | rs485conf.flags |= SER_RS485_ENABLED; 899 | if (ioctl(ctx->s, TIOCSRS485, &rs485conf) < 0) { 900 | return -1; 901 | } 902 | 903 | ctx_rtu->serial_mode = MODBUS_RTU_RS485; 904 | return 0; 905 | } else if (mode == MODBUS_RTU_RS232) { 906 | /* Turn off RS485 mode only if required */ 907 | if (ctx_rtu->serial_mode == MODBUS_RTU_RS485) { 908 | /* The ioctl call is avoided because it can fail on some RS232 ports */ 909 | if (ioctl(ctx->s, TIOCGRS485, &rs485conf) < 0) { 910 | return -1; 911 | } 912 | rs485conf.flags &= ~SER_RS485_ENABLED; 913 | if (ioctl(ctx->s, TIOCSRS485, &rs485conf) < 0) { 914 | return -1; 915 | } 916 | } 917 | ctx_rtu->serial_mode = MODBUS_RTU_RS232; 918 | return 0; 919 | } 920 | #else 921 | if (ctx->debug) { 922 | fprintf(stderr, "This function isn't supported on your platform\n"); 923 | } 924 | errno = ENOTSUP; 925 | return -1; 926 | #endif 927 | } 928 | 929 | /* Wrong backend and invalid mode specified */ 930 | errno = EINVAL; 931 | return -1; 932 | } 933 | 934 | int modbus_rtu_get_serial_mode(modbus_t *ctx) 935 | { 936 | if (ctx == NULL) { 937 | errno = EINVAL; 938 | return -1; 939 | } 940 | 941 | if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) { 942 | #if HAVE_DECL_TIOCSRS485 943 | modbus_rtu_t *ctx_rtu = ctx->backend_data; 944 | return ctx_rtu->serial_mode; 945 | #else 946 | if (ctx->debug) { 947 | fprintf(stderr, "This function isn't supported on your platform\n"); 948 | } 949 | errno = ENOTSUP; 950 | return -1; 951 | #endif 952 | } else { 953 | errno = EINVAL; 954 | return -1; 955 | } 956 | } 957 | 958 | int modbus_rtu_get_rts(modbus_t *ctx) 959 | { 960 | if (ctx == NULL) { 961 | errno = EINVAL; 962 | return -1; 963 | } 964 | 965 | if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) { 966 | #if HAVE_DECL_TIOCM_RTS 967 | modbus_rtu_t *ctx_rtu = ctx->backend_data; 968 | return ctx_rtu->rts; 969 | #else 970 | if (ctx->debug) { 971 | fprintf(stderr, "This function isn't supported on your platform\n"); 972 | } 973 | errno = ENOTSUP; 974 | return -1; 975 | #endif 976 | } else { 977 | errno = EINVAL; 978 | return -1; 979 | } 980 | } 981 | 982 | int modbus_rtu_set_rts(modbus_t *ctx, int mode) 983 | { 984 | if (ctx == NULL) { 985 | errno = EINVAL; 986 | return -1; 987 | } 988 | 989 | if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) { 990 | #if HAVE_DECL_TIOCM_RTS 991 | modbus_rtu_t *ctx_rtu = ctx->backend_data; 992 | 993 | if (mode == MODBUS_RTU_RTS_NONE || mode == MODBUS_RTU_RTS_UP || 994 | mode == MODBUS_RTU_RTS_DOWN) { 995 | ctx_rtu->rts = mode; 996 | 997 | /* Set the RTS bit in order to not reserve the RS485 bus */ 998 | ctx_rtu->set_rts(ctx, ctx_rtu->rts != MODBUS_RTU_RTS_UP); 999 | 1000 | return 0; 1001 | } else { 1002 | errno = EINVAL; 1003 | return -1; 1004 | } 1005 | #else 1006 | if (ctx->debug) { 1007 | fprintf(stderr, "This function isn't supported on your platform\n"); 1008 | } 1009 | errno = ENOTSUP; 1010 | return -1; 1011 | #endif 1012 | } 1013 | /* Wrong backend or invalid mode specified */ 1014 | errno = EINVAL; 1015 | return -1; 1016 | } 1017 | 1018 | int modbus_rtu_set_custom_rts(modbus_t *ctx, void (*set_rts)(modbus_t *ctx, int on)) 1019 | { 1020 | if (ctx == NULL) { 1021 | errno = EINVAL; 1022 | return -1; 1023 | } 1024 | 1025 | if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) { 1026 | #if HAVE_DECL_TIOCM_RTS 1027 | modbus_rtu_t *ctx_rtu = ctx->backend_data; 1028 | ctx_rtu->set_rts = set_rts; 1029 | return 0; 1030 | #else 1031 | if (ctx->debug) { 1032 | fprintf(stderr, "This function isn't supported on your platform\n"); 1033 | } 1034 | errno = ENOTSUP; 1035 | return -1; 1036 | #endif 1037 | } else { 1038 | errno = EINVAL; 1039 | return -1; 1040 | } 1041 | } 1042 | 1043 | int modbus_rtu_get_rts_delay(modbus_t *ctx) 1044 | { 1045 | if (ctx == NULL) { 1046 | errno = EINVAL; 1047 | return -1; 1048 | } 1049 | 1050 | if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) { 1051 | #if HAVE_DECL_TIOCM_RTS 1052 | modbus_rtu_t *ctx_rtu; 1053 | ctx_rtu = (modbus_rtu_t *) ctx->backend_data; 1054 | return ctx_rtu->rts_delay; 1055 | #else 1056 | if (ctx->debug) { 1057 | fprintf(stderr, "This function isn't supported on your platform\n"); 1058 | } 1059 | errno = ENOTSUP; 1060 | return -1; 1061 | #endif 1062 | } else { 1063 | errno = EINVAL; 1064 | return -1; 1065 | } 1066 | } 1067 | 1068 | int modbus_rtu_set_rts_delay(modbus_t *ctx, int us) 1069 | { 1070 | if (ctx == NULL || us < 0) { 1071 | errno = EINVAL; 1072 | return -1; 1073 | } 1074 | 1075 | if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) { 1076 | #if HAVE_DECL_TIOCM_RTS 1077 | modbus_rtu_t *ctx_rtu; 1078 | ctx_rtu = (modbus_rtu_t *) ctx->backend_data; 1079 | ctx_rtu->rts_delay = us; 1080 | return 0; 1081 | #else 1082 | if (ctx->debug) { 1083 | fprintf(stderr, "This function isn't supported on your platform\n"); 1084 | } 1085 | errno = ENOTSUP; 1086 | return -1; 1087 | #endif 1088 | } else { 1089 | errno = EINVAL; 1090 | return -1; 1091 | } 1092 | } 1093 | 1094 | static void _modbus_rtu_close(modbus_t *ctx) 1095 | { 1096 | /* Restore line settings and close file descriptor in RTU mode */ 1097 | modbus_rtu_t *ctx_rtu = ctx->backend_data; 1098 | 1099 | #if defined(_WIN32) 1100 | /* Revert settings */ 1101 | if (!SetCommState(ctx_rtu->w_ser.fd, &ctx_rtu->old_dcb) && ctx->debug) { 1102 | fprintf(stderr, 1103 | "ERROR Couldn't revert to configuration (LastError %d)\n", 1104 | (int) GetLastError()); 1105 | } 1106 | 1107 | if (!CloseHandle(ctx_rtu->w_ser.fd) && ctx->debug) { 1108 | fprintf(stderr, 1109 | "ERROR Error while closing handle (LastError %d)\n", 1110 | (int) GetLastError()); 1111 | } 1112 | #else 1113 | if (ctx->s >= 0) { 1114 | tcsetattr(ctx->s, TCSANOW, &ctx_rtu->old_tios); 1115 | close(ctx->s); 1116 | ctx->s = -1; 1117 | } 1118 | #endif 1119 | } 1120 | 1121 | static int _modbus_rtu_flush(modbus_t *ctx) 1122 | { 1123 | #if defined(_WIN32) 1124 | modbus_rtu_t *ctx_rtu = ctx->backend_data; 1125 | ctx_rtu->w_ser.n_bytes = 0; 1126 | return (PurgeComm(ctx_rtu->w_ser.fd, PURGE_RXCLEAR) == FALSE); 1127 | #else 1128 | return tcflush(ctx->s, TCIOFLUSH); 1129 | #endif 1130 | } 1131 | 1132 | static int 1133 | _modbus_rtu_select(modbus_t *ctx, fd_set *rset, struct timeval *tv, int length_to_read) 1134 | { 1135 | int s_rc; 1136 | #if defined(_WIN32) 1137 | s_rc = win32_ser_select( 1138 | &((modbus_rtu_t *) ctx->backend_data)->w_ser, length_to_read, tv); 1139 | if (s_rc == 0) { 1140 | errno = ETIMEDOUT; 1141 | return -1; 1142 | } 1143 | 1144 | if (s_rc < 0) { 1145 | return -1; 1146 | } 1147 | #else 1148 | while ((s_rc = select(ctx->s + 1, rset, NULL, NULL, tv)) == -1) { 1149 | if (errno == EINTR) { 1150 | if (ctx->debug) { 1151 | fprintf(stderr, "A non blocked signal was caught\n"); 1152 | } 1153 | /* Necessary after an error */ 1154 | FD_ZERO(rset); 1155 | FD_SET(ctx->s, rset); 1156 | } else { 1157 | return -1; 1158 | } 1159 | } 1160 | 1161 | if (s_rc == 0) { 1162 | /* Timeout */ 1163 | errno = ETIMEDOUT; 1164 | return -1; 1165 | } 1166 | #endif 1167 | 1168 | return s_rc; 1169 | } 1170 | 1171 | static void _modbus_rtu_free(modbus_t *ctx) 1172 | { 1173 | if (ctx->backend_data) { 1174 | free(((modbus_rtu_t *) ctx->backend_data)->device); 1175 | free(ctx->backend_data); 1176 | } 1177 | 1178 | free(ctx); 1179 | } 1180 | 1181 | // clang-format off 1182 | const modbus_backend_t _modbus_rtu_backend = { 1183 | _MODBUS_BACKEND_TYPE_RTU, 1184 | _MODBUS_RTU_HEADER_LENGTH, 1185 | _MODBUS_RTU_CHECKSUM_LENGTH, 1186 | MODBUS_RTU_MAX_ADU_LENGTH, 1187 | _modbus_set_slave, 1188 | _modbus_rtu_build_request_basis, 1189 | _modbus_rtu_build_response_basis, 1190 | _modbus_rtu_prepare_response_tid, 1191 | _modbus_rtu_send_msg_pre, 1192 | _modbus_rtu_send, 1193 | _modbus_rtu_receive, 1194 | _modbus_rtu_recv, 1195 | _modbus_rtu_check_integrity, 1196 | _modbus_rtu_pre_check_confirmation, 1197 | _modbus_rtu_connect, 1198 | _modbus_rtu_is_connected, 1199 | _modbus_rtu_close, 1200 | _modbus_rtu_flush, 1201 | _modbus_rtu_select, 1202 | _modbus_rtu_free 1203 | }; 1204 | 1205 | // clang-format on 1206 | 1207 | modbus_t * 1208 | modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit) 1209 | { 1210 | modbus_t *ctx; 1211 | modbus_rtu_t *ctx_rtu; 1212 | 1213 | /* Check device argument */ 1214 | if (device == NULL || *device == 0) { 1215 | fprintf(stderr, "The device string is empty\n"); 1216 | errno = EINVAL; 1217 | return NULL; 1218 | } 1219 | 1220 | /* Check baud argument */ 1221 | if (baud == 0) { 1222 | fprintf(stderr, "The baud rate value must not be zero\n"); 1223 | errno = EINVAL; 1224 | return NULL; 1225 | } 1226 | 1227 | ctx = (modbus_t *) malloc(sizeof(modbus_t)); 1228 | if (ctx == NULL) { 1229 | return NULL; 1230 | } 1231 | 1232 | _modbus_init_common(ctx); 1233 | ctx->backend = &_modbus_rtu_backend; 1234 | ctx->backend_data = (modbus_rtu_t *) malloc(sizeof(modbus_rtu_t)); 1235 | if (ctx->backend_data == NULL) { 1236 | modbus_free(ctx); 1237 | errno = ENOMEM; 1238 | return NULL; 1239 | } 1240 | ctx_rtu = (modbus_rtu_t *) ctx->backend_data; 1241 | 1242 | /* Device name and \0 */ 1243 | ctx_rtu->device = (char *) malloc((strlen(device) + 1) * sizeof(char)); 1244 | if (ctx_rtu->device == NULL) { 1245 | modbus_free(ctx); 1246 | errno = ENOMEM; 1247 | return NULL; 1248 | } 1249 | 1250 | #if defined(_WIN32) 1251 | strcpy_s(ctx_rtu->device, strlen(device) + 1, device); 1252 | #else 1253 | strcpy(ctx_rtu->device, device); 1254 | #endif 1255 | 1256 | ctx_rtu->baud = baud; 1257 | if (parity == 'N' || parity == 'E' || parity == 'O') { 1258 | ctx_rtu->parity = parity; 1259 | } else { 1260 | modbus_free(ctx); 1261 | errno = EINVAL; 1262 | return NULL; 1263 | } 1264 | ctx_rtu->data_bit = data_bit; 1265 | ctx_rtu->stop_bit = stop_bit; 1266 | 1267 | #if HAVE_DECL_TIOCSRS485 1268 | /* The RS232 mode has been set by default */ 1269 | ctx_rtu->serial_mode = MODBUS_RTU_RS232; 1270 | #endif 1271 | 1272 | #if HAVE_DECL_TIOCM_RTS 1273 | /* The RTS use has been set by default */ 1274 | ctx_rtu->rts = MODBUS_RTU_RTS_NONE; 1275 | 1276 | /* Calculate estimated time in micro second to send one byte */ 1277 | ctx_rtu->onebyte_time = 1278 | 1000000 * (1 + data_bit + (parity == 'N' ? 0 : 1) + stop_bit) / baud; 1279 | 1280 | /* The internal function is used by default to set RTS */ 1281 | ctx_rtu->set_rts = _modbus_rtu_ioctl_rts; 1282 | 1283 | /* The delay before and after transmission when toggling the RTS pin */ 1284 | ctx_rtu->rts_delay = ctx_rtu->onebyte_time; 1285 | #endif 1286 | 1287 | ctx_rtu->confirmation_to_ignore = FALSE; 1288 | 1289 | return ctx; 1290 | } 1291 | -------------------------------------------------------------------------------- /modbus/modbus-rtu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © Stéphane Raimbault 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | 7 | #ifndef MODBUS_RTU_H 8 | #define MODBUS_RTU_H 9 | 10 | #include "modbus.h" 11 | 12 | MODBUS_BEGIN_DECLS 13 | 14 | /* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5 15 | * RS232 / RS485 ADU = 253 bytes + slave (1 byte) + CRC (2 bytes) = 256 bytes 16 | */ 17 | #define MODBUS_RTU_MAX_ADU_LENGTH 256 18 | 19 | MODBUS_API modbus_t * 20 | modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit); 21 | 22 | #define MODBUS_RTU_RS232 0 23 | #define MODBUS_RTU_RS485 1 24 | 25 | MODBUS_API int modbus_rtu_set_serial_mode(modbus_t *ctx, int mode); 26 | MODBUS_API int modbus_rtu_get_serial_mode(modbus_t *ctx); 27 | 28 | #define MODBUS_RTU_RTS_NONE 0 29 | #define MODBUS_RTU_RTS_UP 1 30 | #define MODBUS_RTU_RTS_DOWN 2 31 | 32 | MODBUS_API int modbus_rtu_set_rts(modbus_t *ctx, int mode); 33 | MODBUS_API int modbus_rtu_get_rts(modbus_t *ctx); 34 | 35 | MODBUS_API int modbus_rtu_set_custom_rts(modbus_t *ctx, 36 | void (*set_rts)(modbus_t *ctx, int on)); 37 | 38 | MODBUS_API int modbus_rtu_set_rts_delay(modbus_t *ctx, int us); 39 | MODBUS_API int modbus_rtu_get_rts_delay(modbus_t *ctx); 40 | 41 | MODBUS_END_DECLS 42 | 43 | #endif /* MODBUS_RTU_H */ 44 | -------------------------------------------------------------------------------- /modbus/modbus-tcp-private.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © Stéphane Raimbault 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | 7 | #ifndef MODBUS_TCP_PRIVATE_H 8 | #define MODBUS_TCP_PRIVATE_H 9 | 10 | #define _MODBUS_TCP_HEADER_LENGTH 7 11 | #define _MODBUS_TCP_PRESET_REQ_LENGTH 12 12 | #define _MODBUS_TCP_PRESET_RSP_LENGTH 8 13 | 14 | #define _MODBUS_TCP_CHECKSUM_LENGTH 0 15 | 16 | /* In both structures, the transaction ID must be placed on first position 17 | to have a quick access not dependent of the TCP backend */ 18 | typedef struct _modbus_tcp { 19 | /* Extract from MODBUS Messaging on TCP/IP Implementation Guide V1.0b 20 | (page 23/46): 21 | The transaction identifier is used to associate the future response 22 | with the request. This identifier is unique on each TCP connection. */ 23 | uint16_t t_id; 24 | /* TCP port */ 25 | int port; 26 | /* IP address */ 27 | char ip[16]; 28 | } modbus_tcp_t; 29 | 30 | typedef struct _modbus_tcp_pi { 31 | /* Transaction ID */ 32 | uint16_t t_id; 33 | /* TCP port */ 34 | int port; 35 | /* Node */ 36 | char *node; 37 | /* Service */ 38 | char *service; 39 | } modbus_tcp_pi_t; 40 | 41 | #endif /* MODBUS_TCP_PRIVATE_H */ 42 | -------------------------------------------------------------------------------- /modbus/modbus-tcp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © Stéphane Raimbault 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | 7 | // clang-format off 8 | #if defined(_WIN32) 9 | # define OS_WIN32 10 | /* ws2_32.dll has getaddrinfo and freeaddrinfo on Windows XP and later. 11 | * minwg32 headers check WINVER before allowing the use of these */ 12 | # ifndef WINVER 13 | # define WINVER 0x0501 14 | # endif 15 | #endif 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #ifndef _MSC_VER 22 | #include 23 | #endif 24 | #include 25 | #include 26 | 27 | #if defined(_WIN32) 28 | /* Already set in modbus-tcp.h but it seems order matters in VS2005 */ 29 | # include 30 | # include 31 | # define SHUT_RDWR 2 32 | # define close closesocket 33 | # define strdup _strdup 34 | #else 35 | # include 36 | # include 37 | 38 | #if defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD__ < 5) 39 | # define OS_BSD 40 | # include 41 | #endif 42 | 43 | # include 44 | # include 45 | # include 46 | # include 47 | # include 48 | #endif 49 | 50 | #if !defined(MSG_NOSIGNAL) 51 | #define MSG_NOSIGNAL 0 52 | #endif 53 | 54 | #if defined(_AIX) && !defined(MSG_DONTWAIT) 55 | #define MSG_DONTWAIT MSG_NONBLOCK 56 | #endif 57 | // clang-format on 58 | 59 | #include "modbus-private.h" 60 | 61 | #include "modbus-tcp-private.h" 62 | #include "modbus-tcp.h" 63 | 64 | #ifdef OS_WIN32 65 | static int _modbus_tcp_init_win32(void) 66 | { 67 | /* Initialise Windows Socket API */ 68 | WSADATA wsaData; 69 | 70 | if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { 71 | fprintf(stderr, 72 | "WSAStartup() returned error code %d\n", 73 | (unsigned int) GetLastError()); 74 | errno = EIO; 75 | return -1; 76 | } 77 | return 0; 78 | } 79 | #endif 80 | 81 | static int _modbus_set_slave(modbus_t *ctx, int slave) 82 | { 83 | int max_slave = (ctx->quirks & MODBUS_QUIRK_MAX_SLAVE) ? 255 : 247; 84 | 85 | /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */ 86 | if (slave >= 0 && slave <= max_slave) { 87 | ctx->slave = slave; 88 | } else if (slave == MODBUS_TCP_SLAVE) { 89 | /* The special value MODBUS_TCP_SLAVE (0xFF) can be used in TCP mode to 90 | * restore the default value. */ 91 | ctx->slave = slave; 92 | } else { 93 | errno = EINVAL; 94 | return -1; 95 | } 96 | 97 | return 0; 98 | } 99 | 100 | /* Builds a TCP request header */ 101 | static int _modbus_tcp_build_request_basis( 102 | modbus_t *ctx, int function, int addr, int nb, uint8_t *req) 103 | { 104 | modbus_tcp_t *ctx_tcp = ctx->backend_data; 105 | 106 | /* Increase transaction ID */ 107 | if (ctx_tcp->t_id < UINT16_MAX) 108 | ctx_tcp->t_id++; 109 | else 110 | ctx_tcp->t_id = 0; 111 | req[0] = ctx_tcp->t_id >> 8; 112 | req[1] = ctx_tcp->t_id & 0x00ff; 113 | 114 | /* Protocol Modbus */ 115 | req[2] = 0; 116 | req[3] = 0; 117 | 118 | /* Length will be defined later by set_req_length_tcp at offsets 4 119 | and 5 */ 120 | 121 | req[6] = ctx->slave; 122 | req[7] = function; 123 | req[8] = addr >> 8; 124 | req[9] = addr & 0x00ff; 125 | req[10] = nb >> 8; 126 | req[11] = nb & 0x00ff; 127 | 128 | return _MODBUS_TCP_PRESET_REQ_LENGTH; 129 | } 130 | 131 | /* Builds a TCP response header */ 132 | static int _modbus_tcp_build_response_basis(sft_t *sft, uint8_t *rsp) 133 | { 134 | /* Extract from MODBUS Messaging on TCP/IP Implementation 135 | Guide V1.0b (page 23/46): 136 | The transaction identifier is used to associate the future 137 | response with the request. */ 138 | rsp[0] = sft->t_id >> 8; 139 | rsp[1] = sft->t_id & 0x00ff; 140 | 141 | /* Protocol Modbus */ 142 | rsp[2] = 0; 143 | rsp[3] = 0; 144 | 145 | /* Length will be set later by send_msg (4 and 5) */ 146 | 147 | /* The slave ID is copied from the indication */ 148 | rsp[6] = sft->slave; 149 | rsp[7] = sft->function; 150 | 151 | return _MODBUS_TCP_PRESET_RSP_LENGTH; 152 | } 153 | 154 | static int _modbus_tcp_prepare_response_tid(const uint8_t *req, int *req_length) 155 | { 156 | return (req[0] << 8) + req[1]; 157 | } 158 | 159 | static int _modbus_tcp_send_msg_pre(uint8_t *req, int req_length) 160 | { 161 | /* Subtract the header length to the message length */ 162 | int mbap_length = req_length - 6; 163 | 164 | req[4] = mbap_length >> 8; 165 | req[5] = mbap_length & 0x00FF; 166 | 167 | return req_length; 168 | } 169 | 170 | static ssize_t _modbus_tcp_send(modbus_t *ctx, const uint8_t *req, int req_length) 171 | { 172 | /* MSG_NOSIGNAL 173 | Requests not to send SIGPIPE on errors on stream oriented 174 | sockets when the other end breaks the connection. The EPIPE 175 | error is still returned. */ 176 | return send(ctx->s, (const char *) req, req_length, MSG_NOSIGNAL); 177 | } 178 | 179 | static int _modbus_tcp_receive(modbus_t *ctx, uint8_t *req) 180 | { 181 | return _modbus_receive_msg(ctx, req, MSG_INDICATION); 182 | } 183 | 184 | static ssize_t _modbus_tcp_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length) 185 | { 186 | return recv(ctx->s, (char *) rsp, rsp_length, 0); 187 | } 188 | 189 | static int _modbus_tcp_check_integrity(modbus_t *ctx, uint8_t *msg, const int msg_length) 190 | { 191 | return msg_length; 192 | } 193 | 194 | static int _modbus_tcp_pre_check_confirmation(modbus_t *ctx, 195 | const uint8_t *req, 196 | const uint8_t *rsp, 197 | int rsp_length) 198 | { 199 | unsigned int protocol_id; 200 | /* Check transaction ID */ 201 | if (req[0] != rsp[0] || req[1] != rsp[1]) { 202 | if (ctx->debug) { 203 | fprintf(stderr, 204 | "Invalid transaction ID received 0x%X (not 0x%X)\n", 205 | (rsp[0] << 8) + rsp[1], 206 | (req[0] << 8) + req[1]); 207 | } 208 | errno = EMBBADDATA; 209 | return -1; 210 | } 211 | 212 | /* Check protocol ID */ 213 | protocol_id = (rsp[2] << 8) + rsp[3]; 214 | if (protocol_id != 0x0) { 215 | if (ctx->debug) { 216 | fprintf(stderr, "Invalid protocol ID received 0x%X (not 0x0)\n", protocol_id); 217 | } 218 | errno = EMBBADDATA; 219 | return -1; 220 | } 221 | 222 | return 0; 223 | } 224 | 225 | static int _modbus_tcp_set_ipv4_options(int s) 226 | { 227 | int rc; 228 | int option; 229 | 230 | /* Set the TCP no delay flag */ 231 | /* SOL_TCP = IPPROTO_TCP */ 232 | option = 1; 233 | rc = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const void *) &option, sizeof(int)); 234 | if (rc == -1) { 235 | return -1; 236 | } 237 | 238 | /* If the OS does not offer SOCK_NONBLOCK, fall back to setting FIONBIO to 239 | * make sockets non-blocking */ 240 | /* Do not care about the return value, this is optional */ 241 | #if !defined(SOCK_NONBLOCK) && defined(FIONBIO) 242 | #ifdef OS_WIN32 243 | { 244 | /* Setting FIONBIO expects an unsigned long according to MSDN */ 245 | u_long loption = 1; 246 | ioctlsocket(s, FIONBIO, &loption); 247 | } 248 | #else 249 | option = 1; 250 | ioctl(s, FIONBIO, &option); 251 | #endif 252 | #endif 253 | 254 | #ifndef OS_WIN32 255 | /** 256 | * Cygwin defines IPTOS_LOWDELAY but can't handle that flag so it's 257 | * necessary to workaround that problem. 258 | **/ 259 | /* Set the IP low delay option */ 260 | option = IPTOS_LOWDELAY; 261 | rc = setsockopt(s, IPPROTO_IP, IP_TOS, (const void *) &option, sizeof(int)); 262 | if (rc == -1) { 263 | return -1; 264 | } 265 | #endif 266 | 267 | return 0; 268 | } 269 | 270 | static int _connect(int sockfd, 271 | const struct sockaddr *addr, 272 | socklen_t addrlen, 273 | const struct timeval *ro_tv) 274 | { 275 | int rc = connect(sockfd, addr, addrlen); 276 | 277 | #ifdef OS_WIN32 278 | int wsaError = 0; 279 | if (rc == -1) { 280 | wsaError = WSAGetLastError(); 281 | } 282 | 283 | if (wsaError == WSAEWOULDBLOCK || wsaError == WSAEINPROGRESS) { 284 | #else 285 | if (rc == -1 && errno == EINPROGRESS) { 286 | #endif 287 | fd_set wset; 288 | int optval; 289 | socklen_t optlen = sizeof(optval); 290 | struct timeval tv = *ro_tv; 291 | 292 | /* Wait to be available in writing */ 293 | FD_ZERO(&wset); 294 | FD_SET(sockfd, &wset); 295 | rc = select(sockfd + 1, NULL, &wset, NULL, &tv); 296 | if (rc <= 0) { 297 | /* Timeout or fail */ 298 | return -1; 299 | } 300 | 301 | /* The connection is established if SO_ERROR and optval are set to 0 */ 302 | rc = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *) &optval, &optlen); 303 | if (rc == 0 && optval == 0) { 304 | return 0; 305 | } else { 306 | errno = ECONNREFUSED; 307 | return -1; 308 | } 309 | } 310 | return rc; 311 | } 312 | 313 | /* Establishes a modbus TCP connection with a Modbus server. */ 314 | static int _modbus_tcp_connect(modbus_t *ctx) 315 | { 316 | int rc; 317 | /* Specialized version of sockaddr for Internet socket address (same size) */ 318 | struct sockaddr_in addr; 319 | modbus_tcp_t *ctx_tcp = ctx->backend_data; 320 | int flags = SOCK_STREAM; 321 | 322 | #ifdef OS_WIN32 323 | if (_modbus_tcp_init_win32() == -1) { 324 | return -1; 325 | } 326 | #endif 327 | 328 | #ifdef SOCK_CLOEXEC 329 | flags |= SOCK_CLOEXEC; 330 | #endif 331 | 332 | #ifdef SOCK_NONBLOCK 333 | flags |= SOCK_NONBLOCK; 334 | #endif 335 | 336 | ctx->s = socket(PF_INET, flags, 0); 337 | if (ctx->s < 0) { 338 | return -1; 339 | } 340 | 341 | rc = _modbus_tcp_set_ipv4_options(ctx->s); 342 | if (rc == -1) { 343 | close(ctx->s); 344 | ctx->s = -1; 345 | return -1; 346 | } 347 | 348 | if (ctx->debug) { 349 | printf("Connecting to %s:%d\n", ctx_tcp->ip, ctx_tcp->port); 350 | } 351 | 352 | addr.sin_family = AF_INET; 353 | addr.sin_port = htons(ctx_tcp->port); 354 | rc = inet_pton(addr.sin_family, ctx_tcp->ip, &(addr.sin_addr)); 355 | if (rc <= 0) { 356 | if (ctx->debug) { 357 | fprintf(stderr, "Invalid IP address: %s\n", ctx_tcp->ip); 358 | } 359 | close(ctx->s); 360 | ctx->s = -1; 361 | return -1; 362 | } 363 | 364 | rc = 365 | _connect(ctx->s, (struct sockaddr *) &addr, sizeof(addr), &ctx->response_timeout); 366 | if (rc == -1) { 367 | close(ctx->s); 368 | ctx->s = -1; 369 | return -1; 370 | } 371 | 372 | return 0; 373 | } 374 | 375 | /* Establishes a modbus TCP PI connection with a Modbus server. */ 376 | static int _modbus_tcp_pi_connect(modbus_t *ctx) 377 | { 378 | int rc; 379 | struct addrinfo *ai_list; 380 | struct addrinfo *ai_ptr; 381 | struct addrinfo ai_hints; 382 | modbus_tcp_pi_t *ctx_tcp_pi = ctx->backend_data; 383 | 384 | #ifdef OS_WIN32 385 | if (_modbus_tcp_init_win32() == -1) { 386 | return -1; 387 | } 388 | #endif 389 | 390 | memset(&ai_hints, 0, sizeof(ai_hints)); 391 | #ifdef AI_ADDRCONFIG 392 | ai_hints.ai_flags |= AI_ADDRCONFIG; 393 | #endif 394 | ai_hints.ai_family = AF_UNSPEC; 395 | ai_hints.ai_socktype = SOCK_STREAM; 396 | ai_hints.ai_addr = NULL; 397 | ai_hints.ai_canonname = NULL; 398 | ai_hints.ai_next = NULL; 399 | 400 | ai_list = NULL; 401 | rc = getaddrinfo(ctx_tcp_pi->node, ctx_tcp_pi->service, &ai_hints, &ai_list); 402 | if (rc != 0) { 403 | if (ctx->debug) { 404 | fprintf(stderr, "Error returned by getaddrinfo: %s\n", gai_strerror(rc)); 405 | } 406 | errno = ECONNREFUSED; 407 | return -1; 408 | } 409 | 410 | for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) { 411 | int flags = ai_ptr->ai_socktype; 412 | int s; 413 | 414 | #ifdef SOCK_CLOEXEC 415 | flags |= SOCK_CLOEXEC; 416 | #endif 417 | 418 | #ifdef SOCK_NONBLOCK 419 | flags |= SOCK_NONBLOCK; 420 | #endif 421 | 422 | s = socket(ai_ptr->ai_family, flags, ai_ptr->ai_protocol); 423 | if (s < 0) 424 | continue; 425 | 426 | if (ai_ptr->ai_family == AF_INET) 427 | _modbus_tcp_set_ipv4_options(s); 428 | 429 | if (ctx->debug) { 430 | printf("Connecting to [%s]:%s\n", ctx_tcp_pi->node, ctx_tcp_pi->service); 431 | } 432 | 433 | rc = _connect(s, ai_ptr->ai_addr, ai_ptr->ai_addrlen, &ctx->response_timeout); 434 | if (rc == -1) { 435 | close(s); 436 | continue; 437 | } 438 | 439 | ctx->s = s; 440 | break; 441 | } 442 | 443 | freeaddrinfo(ai_list); 444 | 445 | if (ctx->s < 0) { 446 | return -1; 447 | } 448 | 449 | return 0; 450 | } 451 | 452 | static unsigned int _modbus_tcp_is_connected(modbus_t *ctx) 453 | { 454 | return ctx->s >= 0; 455 | } 456 | 457 | /* Closes the network connection and socket in TCP mode */ 458 | static void _modbus_tcp_close(modbus_t *ctx) 459 | { 460 | if (ctx->s >= 0) { 461 | shutdown(ctx->s, SHUT_RDWR); 462 | close(ctx->s); 463 | ctx->s = -1; 464 | } 465 | } 466 | 467 | static int _modbus_tcp_flush(modbus_t *ctx) 468 | { 469 | int rc; 470 | int rc_sum = 0; 471 | 472 | do { 473 | /* Extract the garbage from the socket */ 474 | char devnull[MODBUS_TCP_MAX_ADU_LENGTH]; 475 | #ifndef OS_WIN32 476 | rc = recv(ctx->s, devnull, MODBUS_TCP_MAX_ADU_LENGTH, MSG_DONTWAIT); 477 | #else 478 | /* On Win32, it's a bit more complicated to not wait */ 479 | fd_set rset; 480 | struct timeval tv; 481 | 482 | tv.tv_sec = 0; 483 | tv.tv_usec = 0; 484 | FD_ZERO(&rset); 485 | FD_SET(ctx->s, &rset); 486 | rc = select(ctx->s + 1, &rset, NULL, NULL, &tv); 487 | if (rc == -1) { 488 | return -1; 489 | } 490 | 491 | if (rc == 1) { 492 | /* There is data to flush */ 493 | rc = recv(ctx->s, devnull, MODBUS_TCP_MAX_ADU_LENGTH, 0); 494 | } 495 | #endif 496 | if (rc > 0) { 497 | rc_sum += rc; 498 | } 499 | } while (rc == MODBUS_TCP_MAX_ADU_LENGTH); 500 | 501 | return rc_sum; 502 | } 503 | 504 | /* Listens for any request from one or many modbus masters in TCP */ 505 | int modbus_tcp_listen(modbus_t *ctx, int nb_connection) 506 | { 507 | int new_s; 508 | int enable; 509 | int flags; 510 | struct sockaddr_in addr; 511 | modbus_tcp_t *ctx_tcp; 512 | int rc; 513 | 514 | if (ctx == NULL) { 515 | errno = EINVAL; 516 | return -1; 517 | } 518 | 519 | ctx_tcp = ctx->backend_data; 520 | 521 | #ifdef OS_WIN32 522 | if (_modbus_tcp_init_win32() == -1) { 523 | return -1; 524 | } 525 | #endif 526 | 527 | flags = SOCK_STREAM; 528 | 529 | #ifdef SOCK_CLOEXEC 530 | flags |= SOCK_CLOEXEC; 531 | #endif 532 | 533 | new_s = socket(PF_INET, flags, IPPROTO_TCP); 534 | if (new_s == -1) { 535 | return -1; 536 | } 537 | 538 | enable = 1; 539 | if (setsockopt(new_s, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(enable)) == 540 | -1) { 541 | close(new_s); 542 | return -1; 543 | } 544 | 545 | memset(&addr, 0, sizeof(addr)); 546 | addr.sin_family = AF_INET; 547 | /* If the modbus port is < to 1024, we need the setuid root. */ 548 | addr.sin_port = htons(ctx_tcp->port); 549 | if (ctx_tcp->ip[0] == '0') { 550 | /* Listen any addresses */ 551 | addr.sin_addr.s_addr = htonl(INADDR_ANY); 552 | } else { 553 | /* Listen only specified IP address */ 554 | rc = inet_pton(addr.sin_family, ctx_tcp->ip, &(addr.sin_addr)); 555 | if (rc <= 0) { 556 | if (ctx->debug) { 557 | fprintf(stderr, "Invalid IP address: %s\n", ctx_tcp->ip); 558 | } 559 | close(new_s); 560 | return -1; 561 | } 562 | } 563 | 564 | if (bind(new_s, (struct sockaddr *) &addr, sizeof(addr)) == -1) { 565 | close(new_s); 566 | return -1; 567 | } 568 | 569 | if (listen(new_s, nb_connection) == -1) { 570 | close(new_s); 571 | return -1; 572 | } 573 | 574 | return new_s; 575 | } 576 | 577 | int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection) 578 | { 579 | int rc; 580 | struct addrinfo *ai_list; 581 | struct addrinfo *ai_ptr; 582 | struct addrinfo ai_hints; 583 | const char *node; 584 | const char *service; 585 | int new_s; 586 | modbus_tcp_pi_t *ctx_tcp_pi; 587 | 588 | if (ctx == NULL) { 589 | errno = EINVAL; 590 | return -1; 591 | } 592 | 593 | ctx_tcp_pi = ctx->backend_data; 594 | 595 | #ifdef OS_WIN32 596 | if (_modbus_tcp_init_win32() == -1) { 597 | return -1; 598 | } 599 | #endif 600 | 601 | if (ctx_tcp_pi->node[0] == 0) { 602 | node = NULL; /* == any */ 603 | } else { 604 | node = ctx_tcp_pi->node; 605 | } 606 | 607 | if (ctx_tcp_pi->service[0] == 0) { 608 | service = "502"; 609 | } else { 610 | service = ctx_tcp_pi->service; 611 | } 612 | 613 | memset(&ai_hints, 0, sizeof(ai_hints)); 614 | /* If node is not NULL, than the AI_PASSIVE flag is ignored. */ 615 | ai_hints.ai_flags |= AI_PASSIVE; 616 | #ifdef AI_ADDRCONFIG 617 | ai_hints.ai_flags |= AI_ADDRCONFIG; 618 | #endif 619 | ai_hints.ai_family = AF_UNSPEC; 620 | ai_hints.ai_socktype = SOCK_STREAM; 621 | ai_hints.ai_addr = NULL; 622 | ai_hints.ai_canonname = NULL; 623 | ai_hints.ai_next = NULL; 624 | 625 | ai_list = NULL; 626 | rc = getaddrinfo(node, service, &ai_hints, &ai_list); 627 | if (rc != 0) { 628 | if (ctx->debug) { 629 | fprintf(stderr, "Error returned by getaddrinfo: %s\n", gai_strerror(rc)); 630 | } 631 | errno = ECONNREFUSED; 632 | return -1; 633 | } 634 | 635 | new_s = -1; 636 | for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) { 637 | int flags = ai_ptr->ai_socktype; 638 | int s; 639 | 640 | #ifdef SOCK_CLOEXEC 641 | flags |= SOCK_CLOEXEC; 642 | #endif 643 | 644 | s = socket(ai_ptr->ai_family, flags, ai_ptr->ai_protocol); 645 | if (s < 0) { 646 | if (ctx->debug) { 647 | perror("socket"); 648 | } 649 | continue; 650 | } else { 651 | int enable = 1; 652 | rc = 653 | setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *) &enable, sizeof(enable)); 654 | if (rc != 0) { 655 | close(s); 656 | if (ctx->debug) { 657 | perror("setsockopt"); 658 | } 659 | continue; 660 | } 661 | } 662 | 663 | rc = bind(s, ai_ptr->ai_addr, ai_ptr->ai_addrlen); 664 | if (rc != 0) { 665 | close(s); 666 | if (ctx->debug) { 667 | perror("bind"); 668 | } 669 | continue; 670 | } 671 | 672 | rc = listen(s, nb_connection); 673 | if (rc != 0) { 674 | close(s); 675 | if (ctx->debug) { 676 | perror("listen"); 677 | } 678 | continue; 679 | } 680 | 681 | new_s = s; 682 | break; 683 | } 684 | freeaddrinfo(ai_list); 685 | 686 | if (new_s < 0) { 687 | return -1; 688 | } 689 | 690 | return new_s; 691 | } 692 | 693 | int modbus_tcp_accept(modbus_t *ctx, int *s) 694 | { 695 | struct sockaddr_in addr; 696 | socklen_t addrlen; 697 | 698 | if (ctx == NULL) { 699 | errno = EINVAL; 700 | return -1; 701 | } 702 | 703 | addrlen = sizeof(addr); 704 | #ifdef HAVE_ACCEPT4 705 | /* Inherit socket flags and use accept4 call */ 706 | ctx->s = accept4(*s, (struct sockaddr *) &addr, &addrlen, SOCK_CLOEXEC); 707 | #else 708 | ctx->s = accept(*s, (struct sockaddr *) &addr, &addrlen); 709 | #endif 710 | 711 | if (ctx->s < 0) { 712 | return -1; 713 | } 714 | 715 | if (ctx->debug) { 716 | char buf[INET_ADDRSTRLEN]; 717 | if (inet_ntop(AF_INET, &(addr.sin_addr), buf, INET_ADDRSTRLEN) == NULL) { 718 | fprintf(stderr, "Client connection accepted from unparsable IP.\n"); 719 | } else { 720 | printf("Client connection accepted from %s.\n", buf); 721 | } 722 | } 723 | 724 | return ctx->s; 725 | } 726 | 727 | int modbus_tcp_pi_accept(modbus_t *ctx, int *s) 728 | { 729 | struct sockaddr_in6 addr; 730 | socklen_t addrlen; 731 | 732 | if (ctx == NULL) { 733 | errno = EINVAL; 734 | return -1; 735 | } 736 | 737 | addrlen = sizeof(addr); 738 | #ifdef HAVE_ACCEPT4 739 | /* Inherit socket flags and use accept4 call */ 740 | ctx->s = accept4(*s, (struct sockaddr *) &addr, &addrlen, SOCK_CLOEXEC); 741 | #else 742 | ctx->s = accept(*s, (struct sockaddr *) &addr, &addrlen); 743 | #endif 744 | 745 | if (ctx->s < 0) { 746 | return -1; 747 | } 748 | 749 | if (ctx->debug) { 750 | char buf[INET6_ADDRSTRLEN]; 751 | if (inet_ntop(AF_INET6, &(addr.sin6_addr), buf, INET6_ADDRSTRLEN) == NULL) { 752 | fprintf(stderr, "Client connection accepted from unparsable IP.\n"); 753 | } else { 754 | printf("Client connection accepted from %s.\n", buf); 755 | } 756 | } 757 | 758 | return ctx->s; 759 | } 760 | 761 | static int 762 | _modbus_tcp_select(modbus_t *ctx, fd_set *rset, struct timeval *tv, int length_to_read) 763 | { 764 | int s_rc; 765 | while ((s_rc = select(ctx->s + 1, rset, NULL, NULL, tv)) == -1) { 766 | if (errno == EINTR) { 767 | if (ctx->debug) { 768 | fprintf(stderr, "A non blocked signal was caught\n"); 769 | } 770 | /* Necessary after an error */ 771 | FD_ZERO(rset); 772 | FD_SET(ctx->s, rset); 773 | } else { 774 | return -1; 775 | } 776 | } 777 | 778 | if (s_rc == 0) { 779 | errno = ETIMEDOUT; 780 | return -1; 781 | } 782 | 783 | return s_rc; 784 | } 785 | 786 | static void _modbus_tcp_free(modbus_t *ctx) 787 | { 788 | if (ctx->backend_data) { 789 | free(ctx->backend_data); 790 | } 791 | free(ctx); 792 | } 793 | 794 | static void _modbus_tcp_pi_free(modbus_t *ctx) 795 | { 796 | if (ctx->backend_data) { 797 | modbus_tcp_pi_t *ctx_tcp_pi = ctx->backend_data; 798 | free(ctx_tcp_pi->node); 799 | free(ctx_tcp_pi->service); 800 | free(ctx->backend_data); 801 | } 802 | 803 | free(ctx); 804 | } 805 | 806 | // clang-format off 807 | const modbus_backend_t _modbus_tcp_backend = { 808 | _MODBUS_BACKEND_TYPE_TCP, 809 | _MODBUS_TCP_HEADER_LENGTH, 810 | _MODBUS_TCP_CHECKSUM_LENGTH, 811 | MODBUS_TCP_MAX_ADU_LENGTH, 812 | _modbus_set_slave, 813 | _modbus_tcp_build_request_basis, 814 | _modbus_tcp_build_response_basis, 815 | _modbus_tcp_prepare_response_tid, 816 | _modbus_tcp_send_msg_pre, 817 | _modbus_tcp_send, 818 | _modbus_tcp_receive, 819 | _modbus_tcp_recv, 820 | _modbus_tcp_check_integrity, 821 | _modbus_tcp_pre_check_confirmation, 822 | _modbus_tcp_connect, 823 | _modbus_tcp_is_connected, 824 | _modbus_tcp_close, 825 | _modbus_tcp_flush, 826 | _modbus_tcp_select, 827 | _modbus_tcp_free 828 | }; 829 | 830 | const modbus_backend_t _modbus_tcp_pi_backend = { 831 | _MODBUS_BACKEND_TYPE_TCP, 832 | _MODBUS_TCP_HEADER_LENGTH, 833 | _MODBUS_TCP_CHECKSUM_LENGTH, 834 | MODBUS_TCP_MAX_ADU_LENGTH, 835 | _modbus_set_slave, 836 | _modbus_tcp_build_request_basis, 837 | _modbus_tcp_build_response_basis, 838 | _modbus_tcp_prepare_response_tid, 839 | _modbus_tcp_send_msg_pre, 840 | _modbus_tcp_send, 841 | _modbus_tcp_receive, 842 | _modbus_tcp_recv, 843 | _modbus_tcp_check_integrity, 844 | _modbus_tcp_pre_check_confirmation, 845 | _modbus_tcp_pi_connect, 846 | _modbus_tcp_is_connected, 847 | _modbus_tcp_close, 848 | _modbus_tcp_flush, 849 | _modbus_tcp_select, 850 | _modbus_tcp_pi_free 851 | }; 852 | 853 | // clang-format on 854 | 855 | modbus_t *modbus_new_tcp(const char *ip, int port) 856 | { 857 | modbus_t *ctx; 858 | modbus_tcp_t *ctx_tcp; 859 | size_t dest_size; 860 | size_t ret_size; 861 | 862 | #if defined(OS_BSD) 863 | /* MSG_NOSIGNAL is unsupported on *BSD so we install an ignore 864 | handler for SIGPIPE. */ 865 | struct sigaction sa; 866 | 867 | sa.sa_handler = SIG_IGN; 868 | if (sigaction(SIGPIPE, &sa, NULL) < 0) { 869 | /* The debug flag can't be set here... */ 870 | fprintf(stderr, "Could not install SIGPIPE handler.\n"); 871 | return NULL; 872 | } 873 | #endif 874 | 875 | ctx = (modbus_t *) malloc(sizeof(modbus_t)); 876 | if (ctx == NULL) { 877 | return NULL; 878 | } 879 | _modbus_init_common(ctx); 880 | 881 | /* Could be changed after to reach a remote serial Modbus device */ 882 | ctx->slave = MODBUS_TCP_SLAVE; 883 | 884 | ctx->backend = &_modbus_tcp_backend; 885 | 886 | ctx->backend_data = (modbus_tcp_t *) malloc(sizeof(modbus_tcp_t)); 887 | if (ctx->backend_data == NULL) { 888 | modbus_free(ctx); 889 | errno = ENOMEM; 890 | return NULL; 891 | } 892 | ctx_tcp = (modbus_tcp_t *) ctx->backend_data; 893 | 894 | if (ip != NULL) { 895 | dest_size = sizeof(char) * 16; 896 | ret_size = strlcpy(ctx_tcp->ip, ip, dest_size); 897 | if (ret_size == 0) { 898 | fprintf(stderr, "The IP string is empty\n"); 899 | modbus_free(ctx); 900 | errno = EINVAL; 901 | return NULL; 902 | } 903 | 904 | if (ret_size >= dest_size) { 905 | fprintf(stderr, "The IP string has been truncated\n"); 906 | modbus_free(ctx); 907 | errno = EINVAL; 908 | return NULL; 909 | } 910 | } else { 911 | ctx_tcp->ip[0] = '0'; 912 | } 913 | ctx_tcp->port = port; 914 | ctx_tcp->t_id = 0; 915 | 916 | return ctx; 917 | } 918 | 919 | modbus_t *modbus_new_tcp_pi(const char *node, const char *service) 920 | { 921 | modbus_t *ctx; 922 | modbus_tcp_pi_t *ctx_tcp_pi; 923 | 924 | ctx = (modbus_t *) malloc(sizeof(modbus_t)); 925 | if (ctx == NULL) { 926 | return NULL; 927 | } 928 | _modbus_init_common(ctx); 929 | 930 | /* Could be changed after to reach a remote serial Modbus device */ 931 | ctx->slave = MODBUS_TCP_SLAVE; 932 | 933 | ctx->backend = &_modbus_tcp_pi_backend; 934 | 935 | ctx->backend_data = (modbus_tcp_pi_t *) malloc(sizeof(modbus_tcp_pi_t)); 936 | if (ctx->backend_data == NULL) { 937 | modbus_free(ctx); 938 | errno = ENOMEM; 939 | return NULL; 940 | } 941 | ctx_tcp_pi = (modbus_tcp_pi_t *) ctx->backend_data; 942 | ctx_tcp_pi->node = NULL; 943 | ctx_tcp_pi->service = NULL; 944 | 945 | if (node != NULL) { 946 | ctx_tcp_pi->node = strdup(node); 947 | } else { 948 | /* The node argument can be empty to indicate any hosts */ 949 | ctx_tcp_pi->node = strdup(""); 950 | } 951 | 952 | if (ctx_tcp_pi->node == NULL) { 953 | modbus_free(ctx); 954 | errno = ENOMEM; 955 | return NULL; 956 | } 957 | 958 | if (service != NULL && service[0] != '\0') { 959 | ctx_tcp_pi->service = strdup(service); 960 | } else { 961 | /* Default Modbus port number */ 962 | ctx_tcp_pi->service = strdup("502"); 963 | } 964 | 965 | if (ctx_tcp_pi->service == NULL) { 966 | modbus_free(ctx); 967 | errno = ENOMEM; 968 | return NULL; 969 | } 970 | 971 | ctx_tcp_pi->t_id = 0; 972 | 973 | return ctx; 974 | } 975 | -------------------------------------------------------------------------------- /modbus/modbus-tcp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © Stéphane Raimbault 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | 7 | #ifndef MODBUS_TCP_H 8 | #define MODBUS_TCP_H 9 | 10 | #include "modbus.h" 11 | 12 | MODBUS_BEGIN_DECLS 13 | 14 | #if defined(_WIN32) && !defined(__CYGWIN__) 15 | /* Win32 with MinGW, supplement to */ 16 | #include 17 | #if !defined(ECONNRESET) 18 | #define ECONNRESET WSAECONNRESET 19 | #endif 20 | #if !defined(ECONNREFUSED) 21 | #define ECONNREFUSED WSAECONNREFUSED 22 | #endif 23 | #if !defined(ETIMEDOUT) 24 | #define ETIMEDOUT WSAETIMEDOUT 25 | #endif 26 | #if !defined(ENOPROTOOPT) 27 | #define ENOPROTOOPT WSAENOPROTOOPT 28 | #endif 29 | #if !defined(EINPROGRESS) 30 | #define EINPROGRESS WSAEINPROGRESS 31 | #endif 32 | #endif 33 | 34 | #define MODBUS_TCP_DEFAULT_PORT 502 35 | #define MODBUS_TCP_SLAVE 0xFF 36 | 37 | /* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5 38 | * TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes 39 | */ 40 | #define MODBUS_TCP_MAX_ADU_LENGTH 260 41 | 42 | MODBUS_API modbus_t *modbus_new_tcp(const char *ip_address, int port); 43 | MODBUS_API int modbus_tcp_listen(modbus_t *ctx, int nb_connection); 44 | MODBUS_API int modbus_tcp_accept(modbus_t *ctx, int *s); 45 | 46 | MODBUS_API modbus_t *modbus_new_tcp_pi(const char *node, const char *service); 47 | MODBUS_API int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection); 48 | MODBUS_API int modbus_tcp_pi_accept(modbus_t *ctx, int *s); 49 | 50 | MODBUS_END_DECLS 51 | 52 | #endif /* MODBUS_TCP_H */ 53 | -------------------------------------------------------------------------------- /modbus/modbus-version.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2010-2014 Stéphane Raimbault 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #ifndef MODBUS_VERSION_H 20 | #define MODBUS_VERSION_H 21 | 22 | /* The major version, (1, if %LIBMODBUS_VERSION is 1.2.3) */ 23 | #define LIBMODBUS_VERSION_MAJOR (3) 24 | 25 | /* The minor version (2, if %LIBMODBUS_VERSION is 1.2.3) */ 26 | #define LIBMODBUS_VERSION_MINOR (1) 27 | 28 | /* The micro version (3, if %LIBMODBUS_VERSION is 1.2.3) */ 29 | #define LIBMODBUS_VERSION_MICRO (10) 30 | 31 | /* The full version, like 1.2.3 */ 32 | #define LIBMODBUS_VERSION 3.1.10 33 | 34 | /* The full version, in string form (suited for string concatenation) 35 | */ 36 | #define LIBMODBUS_VERSION_STRING "3.1.10" 37 | 38 | /* Numerically encoded version, eg. v1.2.3 is 0x010203 */ 39 | #define LIBMODBUS_VERSION_HEX ((LIBMODBUS_VERSION_MAJOR << 16) | \ 40 | (LIBMODBUS_VERSION_MINOR << 8) | \ 41 | (LIBMODBUS_VERSION_MICRO << 0)) 42 | 43 | /* Evaluates to True if the version is greater than @major, @minor and @micro 44 | */ 45 | #define LIBMODBUS_VERSION_CHECK(major,minor,micro) \ 46 | (LIBMODBUS_VERSION_MAJOR > (major) || \ 47 | (LIBMODBUS_VERSION_MAJOR == (major) && \ 48 | LIBMODBUS_VERSION_MINOR > (minor)) || \ 49 | (LIBMODBUS_VERSION_MAJOR == (major) && \ 50 | LIBMODBUS_VERSION_MINOR == (minor) && \ 51 | LIBMODBUS_VERSION_MICRO >= (micro))) 52 | 53 | #endif /* MODBUS_VERSION_H */ 54 | -------------------------------------------------------------------------------- /modbus/modbus-version.h.in: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © Stéphane Raimbault 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #ifndef MODBUS_VERSION_H 20 | #define MODBUS_VERSION_H 21 | 22 | /* The major version, (1, if %LIBMODBUS_VERSION is 1.2.3) */ 23 | #define LIBMODBUS_VERSION_MAJOR (@LIBMODBUS_VERSION_MAJOR@) 24 | 25 | /* The minor version (2, if %LIBMODBUS_VERSION is 1.2.3) */ 26 | #define LIBMODBUS_VERSION_MINOR (@LIBMODBUS_VERSION_MINOR@) 27 | 28 | /* The micro version (3, if %LIBMODBUS_VERSION is 1.2.3) */ 29 | #define LIBMODBUS_VERSION_MICRO (@LIBMODBUS_VERSION_MICRO@) 30 | 31 | /* The full version, like 1.2.3 */ 32 | #define LIBMODBUS_VERSION @LIBMODBUS_VERSION@ 33 | 34 | /* The full version, in string form (suited for string concatenation) 35 | */ 36 | #define LIBMODBUS_VERSION_STRING "@LIBMODBUS_VERSION@" 37 | 38 | /* Numerically encoded version, eg. v1.2.3 is 0x010203 */ 39 | #define LIBMODBUS_VERSION_HEX \ 40 | ((LIBMODBUS_VERSION_MAJOR << 16) | (LIBMODBUS_VERSION_MINOR << 8) | \ 41 | (LIBMODBUS_VERSION_MICRO << 0)) 42 | 43 | /* Evaluates to True if the version is greater than @major, @minor and @micro 44 | */ 45 | #define LIBMODBUS_VERSION_CHECK(major, minor, micro) \ 46 | (LIBMODBUS_VERSION_MAJOR > (major) || \ 47 | (LIBMODBUS_VERSION_MAJOR == (major) && LIBMODBUS_VERSION_MINOR > (minor)) || \ 48 | (LIBMODBUS_VERSION_MAJOR == (major) && LIBMODBUS_VERSION_MINOR == (minor) && \ 49 | LIBMODBUS_VERSION_MICRO >= (micro))) 50 | 51 | #endif /* MODBUS_VERSION_H */ 52 | -------------------------------------------------------------------------------- /modbus/modbus.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © Stéphane Raimbault 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | * 6 | * This library implements the Modbus protocol. 7 | * http://libmodbus.org/ 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #ifndef _MSC_VER 18 | #include 19 | #endif 20 | 21 | #include "config.h" 22 | #include "modbus-private.h" 23 | #include "modbus.h" 24 | 25 | /* Internal use */ 26 | #define MSG_LENGTH_UNDEFINED -1 27 | 28 | /* Exported version */ 29 | const unsigned int libmodbus_version_major = LIBMODBUS_VERSION_MAJOR; 30 | const unsigned int libmodbus_version_minor = LIBMODBUS_VERSION_MINOR; 31 | const unsigned int libmodbus_version_micro = LIBMODBUS_VERSION_MICRO; 32 | 33 | /* Max between RTU and TCP max adu length (so TCP) */ 34 | #define MAX_MESSAGE_LENGTH 260 35 | 36 | /* 3 steps are used to parse the query */ 37 | typedef enum { 38 | _STEP_FUNCTION, 39 | _STEP_META, 40 | _STEP_DATA 41 | } _step_t; 42 | 43 | const char *modbus_strerror(int errnum) 44 | { 45 | switch (errnum) { 46 | case EMBXILFUN: 47 | return "Illegal function"; 48 | case EMBXILADD: 49 | return "Illegal data address"; 50 | case EMBXILVAL: 51 | return "Illegal data value"; 52 | case EMBXSFAIL: 53 | return "Slave device or server failure"; 54 | case EMBXACK: 55 | return "Acknowledge"; 56 | case EMBXSBUSY: 57 | return "Slave device or server is busy"; 58 | case EMBXNACK: 59 | return "Negative acknowledge"; 60 | case EMBXMEMPAR: 61 | return "Memory parity error"; 62 | case EMBXGPATH: 63 | return "Gateway path unavailable"; 64 | case EMBXGTAR: 65 | return "Target device failed to respond"; 66 | case EMBBADCRC: 67 | return "Invalid CRC"; 68 | case EMBBADDATA: 69 | return "Invalid data"; 70 | case EMBBADEXC: 71 | return "Invalid exception code"; 72 | case EMBMDATA: 73 | return "Too many data"; 74 | case EMBBADSLAVE: 75 | return "Response not from requested slave"; 76 | default: 77 | return strerror(errnum); 78 | } 79 | } 80 | 81 | void _error_print(modbus_t *ctx, const char *context) 82 | { 83 | if (ctx->debug) { 84 | fprintf(stderr, "ERROR %s", modbus_strerror(errno)); 85 | if (context != NULL) { 86 | fprintf(stderr, ": %s\n", context); 87 | } else { 88 | fprintf(stderr, "\n"); 89 | } 90 | } 91 | } 92 | 93 | static void _sleep_response_timeout(modbus_t *ctx) 94 | { 95 | /* Response timeout is always positive */ 96 | #ifdef _WIN32 97 | /* usleep doesn't exist on Windows */ 98 | Sleep((ctx->response_timeout.tv_sec * 1000) + (ctx->response_timeout.tv_usec / 1000)); 99 | #else 100 | /* usleep source code */ 101 | struct timespec request, remaining; 102 | request.tv_sec = ctx->response_timeout.tv_sec; 103 | request.tv_nsec = ((long int) ctx->response_timeout.tv_usec) * 1000; 104 | while (nanosleep(&request, &remaining) == -1 && errno == EINTR) { 105 | request = remaining; 106 | } 107 | #endif 108 | } 109 | 110 | int modbus_flush(modbus_t *ctx) 111 | { 112 | int rc; 113 | 114 | if (ctx == NULL) { 115 | errno = EINVAL; 116 | return -1; 117 | } 118 | 119 | rc = ctx->backend->flush(ctx); 120 | if (rc != -1 && ctx->debug) { 121 | /* Not all backends are able to return the number of bytes flushed */ 122 | printf("Bytes flushed (%d)\n", rc); 123 | } 124 | return rc; 125 | } 126 | 127 | /* Computes the length of the expected response */ 128 | static unsigned int compute_response_length_from_request(modbus_t *ctx, uint8_t *req) 129 | { 130 | int length; 131 | const int offset = ctx->backend->header_length; 132 | 133 | switch (req[offset]) { 134 | case MODBUS_FC_READ_COILS: 135 | case MODBUS_FC_READ_DISCRETE_INPUTS: { 136 | /* Header + nb values (code from write_bits) */ 137 | int nb = (req[offset + 3] << 8) | req[offset + 4]; 138 | length = 2 + (nb / 8) + ((nb % 8) ? 1 : 0); 139 | } break; 140 | case MODBUS_FC_WRITE_AND_READ_REGISTERS: 141 | case MODBUS_FC_READ_HOLDING_REGISTERS: 142 | case MODBUS_FC_READ_INPUT_REGISTERS: 143 | /* Header + 2 * nb values */ 144 | length = 2 + 2 * (req[offset + 3] << 8 | req[offset + 4]); 145 | break; 146 | case MODBUS_FC_READ_EXCEPTION_STATUS: 147 | length = 3; 148 | break; 149 | case MODBUS_FC_REPORT_SLAVE_ID: 150 | /* The response is device specific (the header provides the 151 | length) */ 152 | return MSG_LENGTH_UNDEFINED; 153 | case MODBUS_FC_MASK_WRITE_REGISTER: 154 | length = 7; 155 | break; 156 | default: 157 | length = 5; 158 | } 159 | 160 | return offset + length + ctx->backend->checksum_length; 161 | } 162 | 163 | /* Sends a request/response */ 164 | static int send_msg(modbus_t *ctx, uint8_t *msg, int msg_length) 165 | { 166 | int rc; 167 | int i; 168 | 169 | msg_length = ctx->backend->send_msg_pre(msg, msg_length); 170 | 171 | if (ctx->debug) { 172 | for (i = 0; i < msg_length; i++) 173 | printf("[%.2X]", msg[i]); 174 | printf("\n"); 175 | } 176 | 177 | /* In recovery mode, the write command will be issued until to be 178 | successful! Disabled by default. */ 179 | do { 180 | rc = ctx->backend->send(ctx, msg, msg_length); 181 | if (rc == -1) { 182 | _error_print(ctx, NULL); 183 | if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) { 184 | #ifdef _WIN32 185 | const int wsa_err = WSAGetLastError(); 186 | if (wsa_err == WSAENETRESET || wsa_err == WSAENOTCONN || 187 | wsa_err == WSAENOTSOCK || wsa_err == WSAESHUTDOWN || 188 | wsa_err == WSAEHOSTUNREACH || wsa_err == WSAECONNABORTED || 189 | wsa_err == WSAECONNRESET || wsa_err == WSAETIMEDOUT) { 190 | modbus_close(ctx); 191 | _sleep_response_timeout(ctx); 192 | modbus_connect(ctx); 193 | } else { 194 | _sleep_response_timeout(ctx); 195 | modbus_flush(ctx); 196 | } 197 | #else 198 | int saved_errno = errno; 199 | 200 | if ((errno == EBADF || errno == ECONNRESET || errno == EPIPE)) { 201 | modbus_close(ctx); 202 | _sleep_response_timeout(ctx); 203 | modbus_connect(ctx); 204 | } else { 205 | _sleep_response_timeout(ctx); 206 | modbus_flush(ctx); 207 | } 208 | errno = saved_errno; 209 | #endif 210 | } 211 | } 212 | } while ((ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) && rc == -1); 213 | 214 | if (rc > 0 && rc != msg_length) { 215 | errno = EMBBADDATA; 216 | return -1; 217 | } 218 | 219 | return rc; 220 | } 221 | 222 | int modbus_send_raw_request(modbus_t *ctx, const uint8_t *raw_req, int raw_req_length) 223 | { 224 | sft_t sft; 225 | uint8_t req[MAX_MESSAGE_LENGTH]; 226 | int req_length; 227 | 228 | if (ctx == NULL) { 229 | errno = EINVAL; 230 | return -1; 231 | } 232 | 233 | if (raw_req_length < 2 || raw_req_length > (MODBUS_MAX_PDU_LENGTH + 1)) { 234 | /* The raw request must contain function and slave at least and 235 | must not be longer than the maximum pdu length plus the slave 236 | address. */ 237 | errno = EINVAL; 238 | return -1; 239 | } 240 | 241 | sft.slave = raw_req[0]; 242 | sft.function = raw_req[1]; 243 | /* The t_id is left to zero */ 244 | sft.t_id = 0; 245 | /* This response function only set the header so it's convenient here */ 246 | req_length = ctx->backend->build_response_basis(&sft, req); 247 | 248 | if (raw_req_length > 2) { 249 | /* Copy data after function code */ 250 | memcpy(req + req_length, raw_req + 2, raw_req_length - 2); 251 | req_length += raw_req_length - 2; 252 | } 253 | 254 | return send_msg(ctx, req, req_length); 255 | } 256 | 257 | /* 258 | * ---------- Request Indication ---------- 259 | * | Client | ---------------------->| Server | 260 | * ---------- Confirmation Response ---------- 261 | */ 262 | 263 | /* Computes the length to read after the function received */ 264 | static uint8_t compute_meta_length_after_function(int function, msg_type_t msg_type) 265 | { 266 | int length; 267 | 268 | if (msg_type == MSG_INDICATION) { 269 | if (function <= MODBUS_FC_WRITE_SINGLE_REGISTER) { 270 | length = 4; 271 | } else if (function == MODBUS_FC_WRITE_MULTIPLE_COILS || 272 | function == MODBUS_FC_WRITE_MULTIPLE_REGISTERS) { 273 | length = 5; 274 | } else if (function == MODBUS_FC_MASK_WRITE_REGISTER) { 275 | length = 6; 276 | } else if (function == MODBUS_FC_WRITE_AND_READ_REGISTERS) { 277 | length = 9; 278 | } else { 279 | /* MODBUS_FC_READ_EXCEPTION_STATUS, MODBUS_FC_REPORT_SLAVE_ID */ 280 | length = 0; 281 | } 282 | } else { 283 | /* MSG_CONFIRMATION */ 284 | switch (function) { 285 | case MODBUS_FC_WRITE_SINGLE_COIL: 286 | case MODBUS_FC_WRITE_SINGLE_REGISTER: 287 | case MODBUS_FC_WRITE_MULTIPLE_COILS: 288 | case MODBUS_FC_WRITE_MULTIPLE_REGISTERS: 289 | length = 4; 290 | break; 291 | case MODBUS_FC_MASK_WRITE_REGISTER: 292 | length = 6; 293 | break; 294 | default: 295 | length = 1; 296 | } 297 | } 298 | 299 | return length; 300 | } 301 | 302 | /* Computes the length to read after the meta information (address, count, etc) */ 303 | static int 304 | compute_data_length_after_meta(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type) 305 | { 306 | int function = msg[ctx->backend->header_length]; 307 | int length; 308 | 309 | if (msg_type == MSG_INDICATION) { 310 | switch (function) { 311 | case MODBUS_FC_WRITE_MULTIPLE_COILS: 312 | case MODBUS_FC_WRITE_MULTIPLE_REGISTERS: 313 | length = msg[ctx->backend->header_length + 5]; 314 | break; 315 | case MODBUS_FC_WRITE_AND_READ_REGISTERS: 316 | length = msg[ctx->backend->header_length + 9]; 317 | break; 318 | default: 319 | length = 0; 320 | } 321 | } else { 322 | /* MSG_CONFIRMATION */ 323 | if (function <= MODBUS_FC_READ_INPUT_REGISTERS || 324 | function == MODBUS_FC_REPORT_SLAVE_ID || 325 | function == MODBUS_FC_WRITE_AND_READ_REGISTERS) { 326 | length = msg[ctx->backend->header_length + 1]; 327 | } else { 328 | length = 0; 329 | } 330 | } 331 | 332 | length += ctx->backend->checksum_length; 333 | 334 | return length; 335 | } 336 | 337 | /* Waits a response from a modbus server or a request from a modbus client. 338 | This function blocks if there is no replies (3 timeouts). 339 | 340 | The function shall return the number of received characters and the received 341 | message in an array of uint8_t if successful. Otherwise it shall return -1 342 | and errno is set to one of the values defined below: 343 | - ECONNRESET 344 | - EMBBADDATA 345 | - ETIMEDOUT 346 | - read() or recv() error codes 347 | */ 348 | 349 | int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type) 350 | { 351 | int rc; 352 | fd_set rset; 353 | struct timeval tv; 354 | struct timeval *p_tv; 355 | unsigned int length_to_read; 356 | int msg_length = 0; 357 | _step_t step; 358 | #ifdef _WIN32 359 | int wsa_err; 360 | #endif 361 | 362 | if (ctx->debug) { 363 | if (msg_type == MSG_INDICATION) { 364 | printf("Waiting for an indication...\n"); 365 | } else { 366 | printf("Waiting for a confirmation...\n"); 367 | } 368 | } 369 | 370 | if (!ctx->backend->is_connected(ctx)) { 371 | if (ctx->debug) { 372 | fprintf(stderr, "ERROR The connection is not established.\n"); 373 | } 374 | return -1; 375 | } 376 | 377 | /* Add a file descriptor to the set */ 378 | FD_ZERO(&rset); 379 | FD_SET(ctx->s, &rset); 380 | 381 | /* We need to analyse the message step by step. At the first step, we want 382 | * to reach the function code because all packets contain this 383 | * information. */ 384 | step = _STEP_FUNCTION; 385 | length_to_read = ctx->backend->header_length + 1; 386 | 387 | if (msg_type == MSG_INDICATION) { 388 | /* Wait for a message, we don't know when the message will be 389 | * received */ 390 | if (ctx->indication_timeout.tv_sec == 0 && ctx->indication_timeout.tv_usec == 0) { 391 | /* By default, the indication timeout isn't set */ 392 | p_tv = NULL; 393 | } else { 394 | /* Wait for an indication (name of a received request by a server, see schema) 395 | */ 396 | tv.tv_sec = ctx->indication_timeout.tv_sec; 397 | tv.tv_usec = ctx->indication_timeout.tv_usec; 398 | p_tv = &tv; 399 | } 400 | } else { 401 | tv.tv_sec = ctx->response_timeout.tv_sec; 402 | tv.tv_usec = ctx->response_timeout.tv_usec; 403 | p_tv = &tv; 404 | } 405 | 406 | while (length_to_read != 0) { 407 | rc = ctx->backend->select(ctx, &rset, p_tv, length_to_read); 408 | if (rc == -1) { 409 | _error_print(ctx, "select"); 410 | if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) { 411 | #ifdef _WIN32 412 | wsa_err = WSAGetLastError(); 413 | 414 | // no equivalent to ETIMEDOUT when select fails on Windows 415 | if (wsa_err == WSAENETDOWN || wsa_err == WSAENOTSOCK) { 416 | modbus_close(ctx); 417 | modbus_connect(ctx); 418 | } 419 | #else 420 | int saved_errno = errno; 421 | 422 | if (errno == ETIMEDOUT) { 423 | _sleep_response_timeout(ctx); 424 | modbus_flush(ctx); 425 | } else if (errno == EBADF) { 426 | modbus_close(ctx); 427 | modbus_connect(ctx); 428 | } 429 | errno = saved_errno; 430 | #endif 431 | } 432 | return -1; 433 | } 434 | 435 | rc = ctx->backend->recv(ctx, msg + msg_length, length_to_read); 436 | if (rc == 0) { 437 | errno = ECONNRESET; 438 | rc = -1; 439 | } 440 | 441 | if (rc == -1) { 442 | _error_print(ctx, "read"); 443 | #ifdef _WIN32 444 | wsa_err = WSAGetLastError(); 445 | if ((ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) && 446 | (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_TCP) && 447 | (wsa_err == WSAENOTCONN || wsa_err == WSAENETRESET || 448 | wsa_err == WSAENOTSOCK || wsa_err == WSAESHUTDOWN || 449 | wsa_err == WSAECONNABORTED || wsa_err == WSAETIMEDOUT || 450 | wsa_err == WSAECONNRESET)) { 451 | modbus_close(ctx); 452 | modbus_connect(ctx); 453 | } 454 | #else 455 | if ((ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) && 456 | (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_TCP) && 457 | (errno == ECONNRESET || errno == ECONNREFUSED || errno == EBADF)) { 458 | int saved_errno = errno; 459 | modbus_close(ctx); 460 | modbus_connect(ctx); 461 | /* Could be removed by previous calls */ 462 | errno = saved_errno; 463 | } 464 | #endif 465 | return -1; 466 | } 467 | 468 | /* Display the hex code of each character received */ 469 | if (ctx->debug) { 470 | int i; 471 | for (i = 0; i < rc; i++) 472 | printf("<%.2X>", msg[msg_length + i]); 473 | } 474 | 475 | /* Sums bytes received */ 476 | msg_length += rc; 477 | /* Computes remaining bytes */ 478 | length_to_read -= rc; 479 | 480 | if (length_to_read == 0) { 481 | switch (step) { 482 | case _STEP_FUNCTION: 483 | /* Function code position */ 484 | length_to_read = compute_meta_length_after_function( 485 | msg[ctx->backend->header_length], msg_type); 486 | if (length_to_read != 0) { 487 | step = _STEP_META; 488 | break; 489 | } /* else switches straight to the next step */ 490 | case _STEP_META: 491 | length_to_read = compute_data_length_after_meta(ctx, msg, msg_type); 492 | if ((msg_length + length_to_read) > ctx->backend->max_adu_length) { 493 | errno = EMBBADDATA; 494 | _error_print(ctx, "too many data"); 495 | return -1; 496 | } 497 | step = _STEP_DATA; 498 | break; 499 | default: 500 | break; 501 | } 502 | } 503 | 504 | if (length_to_read > 0 && 505 | (ctx->byte_timeout.tv_sec > 0 || ctx->byte_timeout.tv_usec > 0)) { 506 | /* If there is no character in the buffer, the allowed timeout 507 | interval between two consecutive bytes is defined by 508 | byte_timeout */ 509 | tv.tv_sec = ctx->byte_timeout.tv_sec; 510 | tv.tv_usec = ctx->byte_timeout.tv_usec; 511 | p_tv = &tv; 512 | } 513 | /* else timeout isn't set again, the full response must be read before 514 | expiration of response timeout (for CONFIRMATION only) */ 515 | } 516 | 517 | if (ctx->debug) 518 | printf("\n"); 519 | 520 | return ctx->backend->check_integrity(ctx, msg, msg_length); 521 | } 522 | 523 | /* Receive the request from a modbus master */ 524 | int modbus_receive(modbus_t *ctx, uint8_t *req) 525 | { 526 | if (ctx == NULL) { 527 | errno = EINVAL; 528 | return -1; 529 | } 530 | 531 | return ctx->backend->receive(ctx, req); 532 | } 533 | 534 | /* Receives the confirmation. 535 | 536 | The function shall store the read response in rsp and return the number of 537 | values (bits or words). Otherwise, its shall return -1 and errno is set. 538 | 539 | The function doesn't check the confirmation is the expected response to the 540 | initial request. 541 | */ 542 | int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp) 543 | { 544 | if (ctx == NULL) { 545 | errno = EINVAL; 546 | return -1; 547 | } 548 | 549 | return _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION); 550 | } 551 | 552 | static int check_confirmation(modbus_t *ctx, uint8_t *req, uint8_t *rsp, int rsp_length) 553 | { 554 | int rc; 555 | int rsp_length_computed; 556 | const unsigned int offset = ctx->backend->header_length; 557 | const int function = rsp[offset]; 558 | 559 | if (ctx->backend->pre_check_confirmation) { 560 | rc = ctx->backend->pre_check_confirmation(ctx, req, rsp, rsp_length); 561 | if (rc == -1) { 562 | if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) { 563 | _sleep_response_timeout(ctx); 564 | modbus_flush(ctx); 565 | } 566 | return -1; 567 | } 568 | } 569 | 570 | rsp_length_computed = compute_response_length_from_request(ctx, req); 571 | 572 | /* Exception code */ 573 | if (function >= 0x80) { 574 | if (rsp_length == (int) (offset + 2 + ctx->backend->checksum_length) && 575 | req[offset] == (rsp[offset] - 0x80)) { 576 | /* Valid exception code received */ 577 | 578 | int exception_code = rsp[offset + 1]; 579 | if (exception_code < MODBUS_EXCEPTION_MAX) { 580 | errno = MODBUS_ENOBASE + exception_code; 581 | } else { 582 | errno = EMBBADEXC; 583 | } 584 | _error_print(ctx, NULL); 585 | return -1; 586 | } else { 587 | errno = EMBBADEXC; 588 | _error_print(ctx, NULL); 589 | return -1; 590 | } 591 | } 592 | 593 | /* Check length */ 594 | if ((rsp_length == rsp_length_computed || 595 | rsp_length_computed == MSG_LENGTH_UNDEFINED) && 596 | function < 0x80) { 597 | int req_nb_value; 598 | int rsp_nb_value; 599 | int resp_addr_ok = TRUE; 600 | int resp_data_ok = TRUE; 601 | 602 | /* Check function code */ 603 | if (function != req[offset]) { 604 | if (ctx->debug) { 605 | fprintf( 606 | stderr, 607 | "Received function not corresponding to the request (0x%X != 0x%X)\n", 608 | function, 609 | req[offset]); 610 | } 611 | if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) { 612 | _sleep_response_timeout(ctx); 613 | modbus_flush(ctx); 614 | } 615 | errno = EMBBADDATA; 616 | return -1; 617 | } 618 | 619 | /* Check the number of values is corresponding to the request */ 620 | switch (function) { 621 | case MODBUS_FC_READ_COILS: 622 | case MODBUS_FC_READ_DISCRETE_INPUTS: 623 | /* Read functions, 8 values in a byte (nb 624 | * of values in the request and byte count in 625 | * the response. */ 626 | req_nb_value = (req[offset + 3] << 8) + req[offset + 4]; 627 | req_nb_value = (req_nb_value / 8) + ((req_nb_value % 8) ? 1 : 0); 628 | rsp_nb_value = rsp[offset + 1]; 629 | break; 630 | case MODBUS_FC_WRITE_AND_READ_REGISTERS: 631 | case MODBUS_FC_READ_HOLDING_REGISTERS: 632 | case MODBUS_FC_READ_INPUT_REGISTERS: 633 | /* Read functions 1 value = 2 bytes */ 634 | req_nb_value = (req[offset + 3] << 8) + req[offset + 4]; 635 | rsp_nb_value = (rsp[offset + 1] / 2); 636 | break; 637 | case MODBUS_FC_WRITE_MULTIPLE_COILS: 638 | case MODBUS_FC_WRITE_MULTIPLE_REGISTERS: 639 | /* address in request and response must be equal */ 640 | if ((req[offset + 1] != rsp[offset + 1]) || 641 | (req[offset + 2] != rsp[offset + 2])) { 642 | resp_addr_ok = FALSE; 643 | } 644 | /* N Write functions */ 645 | req_nb_value = (req[offset + 3] << 8) + req[offset + 4]; 646 | rsp_nb_value = (rsp[offset + 3] << 8) | rsp[offset + 4]; 647 | break; 648 | case MODBUS_FC_REPORT_SLAVE_ID: 649 | /* Report slave ID (bytes received) */ 650 | req_nb_value = rsp_nb_value = rsp[offset + 1]; 651 | break; 652 | case MODBUS_FC_WRITE_SINGLE_COIL: 653 | case MODBUS_FC_WRITE_SINGLE_REGISTER: 654 | /* address in request and response must be equal */ 655 | if ((req[offset + 1] != rsp[offset + 1]) || 656 | (req[offset + 2] != rsp[offset + 2])) { 657 | resp_addr_ok = FALSE; 658 | } 659 | /* data in request and response must be equal */ 660 | if ((req[offset + 3] != rsp[offset + 3]) || 661 | (req[offset + 4] != rsp[offset + 4])) { 662 | resp_data_ok = FALSE; 663 | } 664 | /* 1 Write functions & others */ 665 | req_nb_value = rsp_nb_value = 1; 666 | break; 667 | default: 668 | /* 1 Write functions & others */ 669 | req_nb_value = rsp_nb_value = 1; 670 | break; 671 | } 672 | 673 | if ((req_nb_value == rsp_nb_value) && (resp_addr_ok == TRUE) && 674 | (resp_data_ok == TRUE)) { 675 | rc = rsp_nb_value; 676 | } else { 677 | if (ctx->debug) { 678 | fprintf(stderr, 679 | "Received data not corresponding to the request (%d != %d)\n", 680 | rsp_nb_value, 681 | req_nb_value); 682 | } 683 | 684 | if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) { 685 | _sleep_response_timeout(ctx); 686 | modbus_flush(ctx); 687 | } 688 | 689 | errno = EMBBADDATA; 690 | rc = -1; 691 | } 692 | } else { 693 | if (ctx->debug) { 694 | fprintf( 695 | stderr, 696 | "Message length not corresponding to the computed length (%d != %d)\n", 697 | rsp_length, 698 | rsp_length_computed); 699 | } 700 | if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) { 701 | _sleep_response_timeout(ctx); 702 | modbus_flush(ctx); 703 | } 704 | errno = EMBBADDATA; 705 | rc = -1; 706 | } 707 | 708 | return rc; 709 | } 710 | 711 | static int 712 | response_io_status(uint8_t *tab_io_status, int address, int nb, uint8_t *rsp, int offset) 713 | { 714 | int shift = 0; 715 | /* Instead of byte (not allowed in Win32) */ 716 | int one_byte = 0; 717 | int i; 718 | 719 | for (i = address; i < address + nb; i++) { 720 | one_byte |= tab_io_status[i] << shift; 721 | if (shift == 7) { 722 | /* Byte is full */ 723 | rsp[offset++] = one_byte; 724 | one_byte = shift = 0; 725 | } else { 726 | shift++; 727 | } 728 | } 729 | 730 | if (shift != 0) 731 | rsp[offset++] = one_byte; 732 | 733 | return offset; 734 | } 735 | 736 | /* Build the exception response */ 737 | static int response_exception(modbus_t *ctx, 738 | sft_t *sft, 739 | int exception_code, 740 | uint8_t *rsp, 741 | unsigned int to_flush, 742 | const char *template, 743 | ...) 744 | { 745 | int rsp_length; 746 | 747 | /* Print debug message */ 748 | if (ctx->debug) { 749 | va_list ap; 750 | 751 | va_start(ap, template); 752 | vfprintf(stderr, template, ap); 753 | va_end(ap); 754 | } 755 | 756 | /* Flush if required */ 757 | if (to_flush) { 758 | _sleep_response_timeout(ctx); 759 | modbus_flush(ctx); 760 | } 761 | 762 | /* Build exception response */ 763 | sft->function = sft->function + 0x80; 764 | rsp_length = ctx->backend->build_response_basis(sft, rsp); 765 | rsp[rsp_length++] = exception_code; 766 | 767 | return rsp_length; 768 | } 769 | 770 | /* Send a response to the received request. 771 | Analyses the request and constructs a response. 772 | 773 | If an error occurs, this function construct the response 774 | accordingly. 775 | */ 776 | int modbus_reply(modbus_t *ctx, 777 | const uint8_t *req, 778 | int req_length, 779 | modbus_mapping_t *mb_mapping) 780 | { 781 | unsigned int offset; 782 | int slave; 783 | int function; 784 | uint16_t address; 785 | uint8_t rsp[MAX_MESSAGE_LENGTH]; 786 | int rsp_length = 0; 787 | sft_t sft; 788 | 789 | if (ctx == NULL) { 790 | errno = EINVAL; 791 | return -1; 792 | } 793 | 794 | offset = ctx->backend->header_length; 795 | slave = req[offset - 1]; 796 | function = req[offset]; 797 | address = (req[offset + 1] << 8) + req[offset + 2]; 798 | 799 | sft.slave = slave; 800 | sft.function = function; 801 | sft.t_id = ctx->backend->prepare_response_tid(req, &req_length); 802 | 803 | /* Data are flushed on illegal number of values errors. */ 804 | switch (function) { 805 | case MODBUS_FC_READ_COILS: 806 | case MODBUS_FC_READ_DISCRETE_INPUTS: { 807 | unsigned int is_input = (function == MODBUS_FC_READ_DISCRETE_INPUTS); 808 | int start_bits = is_input ? mb_mapping->start_input_bits : mb_mapping->start_bits; 809 | int nb_bits = is_input ? mb_mapping->nb_input_bits : mb_mapping->nb_bits; 810 | uint8_t *tab_bits = is_input ? mb_mapping->tab_input_bits : mb_mapping->tab_bits; 811 | const char *const name = is_input ? "read_input_bits" : "read_bits"; 812 | int nb = (req[offset + 3] << 8) + req[offset + 4]; 813 | /* The mapping can be shifted to reduce memory consumption and it 814 | doesn't always start at address zero. */ 815 | int mapping_address = address - start_bits; 816 | 817 | if (nb < 1 || MODBUS_MAX_READ_BITS < nb) { 818 | rsp_length = response_exception(ctx, 819 | &sft, 820 | MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, 821 | rsp, 822 | TRUE, 823 | "Illegal nb of values %d in %s (max %d)\n", 824 | nb, 825 | name, 826 | MODBUS_MAX_READ_BITS); 827 | } else if (mapping_address < 0 || (mapping_address + nb) > nb_bits) { 828 | rsp_length = response_exception(ctx, 829 | &sft, 830 | MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, 831 | rsp, 832 | FALSE, 833 | "Illegal data address 0x%0X in %s\n", 834 | mapping_address < 0 ? address : address + nb, 835 | name); 836 | } else { 837 | rsp_length = ctx->backend->build_response_basis(&sft, rsp); 838 | rsp[rsp_length++] = (nb / 8) + ((nb % 8) ? 1 : 0); 839 | rsp_length = 840 | response_io_status(tab_bits, mapping_address, nb, rsp, rsp_length); 841 | } 842 | } break; 843 | case MODBUS_FC_READ_HOLDING_REGISTERS: 844 | case MODBUS_FC_READ_INPUT_REGISTERS: { 845 | unsigned int is_input = (function == MODBUS_FC_READ_INPUT_REGISTERS); 846 | int start_registers = 847 | is_input ? mb_mapping->start_input_registers : mb_mapping->start_registers; 848 | int nb_registers = 849 | is_input ? mb_mapping->nb_input_registers : mb_mapping->nb_registers; 850 | uint16_t *tab_registers = 851 | is_input ? mb_mapping->tab_input_registers : mb_mapping->tab_registers; 852 | const char *const name = is_input ? "read_input_registers" : "read_registers"; 853 | int nb = (req[offset + 3] << 8) + req[offset + 4]; 854 | /* The mapping can be shifted to reduce memory consumption and it 855 | doesn't always start at address zero. */ 856 | int mapping_address = address - start_registers; 857 | 858 | if (nb < 1 || MODBUS_MAX_READ_REGISTERS < nb) { 859 | rsp_length = response_exception(ctx, 860 | &sft, 861 | MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, 862 | rsp, 863 | TRUE, 864 | "Illegal nb of values %d in %s (max %d)\n", 865 | nb, 866 | name, 867 | MODBUS_MAX_READ_REGISTERS); 868 | } else if (mapping_address < 0 || (mapping_address + nb) > nb_registers) { 869 | rsp_length = response_exception(ctx, 870 | &sft, 871 | MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, 872 | rsp, 873 | FALSE, 874 | "Illegal data address 0x%0X in %s\n", 875 | mapping_address < 0 ? address : address + nb, 876 | name); 877 | } else { 878 | int i; 879 | 880 | rsp_length = ctx->backend->build_response_basis(&sft, rsp); 881 | rsp[rsp_length++] = nb << 1; 882 | for (i = mapping_address; i < mapping_address + nb; i++) { 883 | rsp[rsp_length++] = tab_registers[i] >> 8; 884 | rsp[rsp_length++] = tab_registers[i] & 0xFF; 885 | } 886 | } 887 | } break; 888 | case MODBUS_FC_WRITE_SINGLE_COIL: { 889 | int mapping_address = address - mb_mapping->start_bits; 890 | 891 | if (mapping_address < 0 || mapping_address >= mb_mapping->nb_bits) { 892 | rsp_length = response_exception(ctx, 893 | &sft, 894 | MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, 895 | rsp, 896 | FALSE, 897 | "Illegal data address 0x%0X in write_bit\n", 898 | address); 899 | } else { 900 | int data = (req[offset + 3] << 8) + req[offset + 4]; 901 | 902 | if (data == 0xFF00 || data == 0x0) { 903 | mb_mapping->tab_bits[mapping_address] = data ? ON : OFF; 904 | memcpy(rsp, req, req_length); 905 | rsp_length = req_length; 906 | } else { 907 | rsp_length = response_exception( 908 | ctx, 909 | &sft, 910 | MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, 911 | rsp, 912 | FALSE, 913 | "Illegal data value 0x%0X in write_bit request at address %0X\n", 914 | data, 915 | address); 916 | } 917 | } 918 | } break; 919 | case MODBUS_FC_WRITE_SINGLE_REGISTER: { 920 | int mapping_address = address - mb_mapping->start_registers; 921 | 922 | if (mapping_address < 0 || mapping_address >= mb_mapping->nb_registers) { 923 | rsp_length = 924 | response_exception(ctx, 925 | &sft, 926 | MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, 927 | rsp, 928 | FALSE, 929 | "Illegal data address 0x%0X in write_register\n", 930 | address); 931 | } else { 932 | int data = (req[offset + 3] << 8) + req[offset + 4]; 933 | 934 | mb_mapping->tab_registers[mapping_address] = data; 935 | memcpy(rsp, req, req_length); 936 | rsp_length = req_length; 937 | } 938 | } break; 939 | case MODBUS_FC_WRITE_MULTIPLE_COILS: { 940 | int nb = (req[offset + 3] << 8) + req[offset + 4]; 941 | int nb_bits = req[offset + 5]; 942 | int mapping_address = address - mb_mapping->start_bits; 943 | 944 | if (nb < 1 || MODBUS_MAX_WRITE_BITS < nb || nb_bits * 8 < nb) { 945 | /* May be the indication has been truncated on reading because of 946 | * invalid address (eg. nb is 0 but the request contains values to 947 | * write) so it's necessary to flush. */ 948 | rsp_length = 949 | response_exception(ctx, 950 | &sft, 951 | MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, 952 | rsp, 953 | TRUE, 954 | "Illegal number of values %d in write_bits (max %d)\n", 955 | nb, 956 | MODBUS_MAX_WRITE_BITS); 957 | } else if (mapping_address < 0 || (mapping_address + nb) > mb_mapping->nb_bits) { 958 | rsp_length = response_exception(ctx, 959 | &sft, 960 | MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, 961 | rsp, 962 | FALSE, 963 | "Illegal data address 0x%0X in write_bits\n", 964 | mapping_address < 0 ? address : address + nb); 965 | } else { 966 | /* 6 = byte count */ 967 | modbus_set_bits_from_bytes( 968 | mb_mapping->tab_bits, mapping_address, nb, &req[offset + 6]); 969 | 970 | rsp_length = ctx->backend->build_response_basis(&sft, rsp); 971 | /* 4 to copy the bit address (2) and the quantity of bits */ 972 | memcpy(rsp + rsp_length, req + rsp_length, 4); 973 | rsp_length += 4; 974 | } 975 | } break; 976 | case MODBUS_FC_WRITE_MULTIPLE_REGISTERS: { 977 | int nb = (req[offset + 3] << 8) + req[offset + 4]; 978 | int nb_bytes = req[offset + 5]; 979 | int mapping_address = address - mb_mapping->start_registers; 980 | 981 | if (nb < 1 || MODBUS_MAX_WRITE_REGISTERS < nb || nb_bytes != nb * 2) { 982 | rsp_length = response_exception( 983 | ctx, 984 | &sft, 985 | MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, 986 | rsp, 987 | TRUE, 988 | "Illegal number of values %d in write_registers (max %d)\n", 989 | nb, 990 | MODBUS_MAX_WRITE_REGISTERS); 991 | } else if (mapping_address < 0 || 992 | (mapping_address + nb) > mb_mapping->nb_registers) { 993 | rsp_length = 994 | response_exception(ctx, 995 | &sft, 996 | MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, 997 | rsp, 998 | FALSE, 999 | "Illegal data address 0x%0X in write_registers\n", 1000 | mapping_address < 0 ? address : address + nb); 1001 | } else { 1002 | int i, j; 1003 | for (i = mapping_address, j = 6; i < mapping_address + nb; i++, j += 2) { 1004 | /* 6 and 7 = first value */ 1005 | mb_mapping->tab_registers[i] = 1006 | (req[offset + j] << 8) + req[offset + j + 1]; 1007 | } 1008 | 1009 | rsp_length = ctx->backend->build_response_basis(&sft, rsp); 1010 | /* 4 to copy the address (2) and the no. of registers */ 1011 | memcpy(rsp + rsp_length, req + rsp_length, 4); 1012 | rsp_length += 4; 1013 | } 1014 | } break; 1015 | case MODBUS_FC_REPORT_SLAVE_ID: { 1016 | int str_len; 1017 | int byte_count_pos; 1018 | 1019 | rsp_length = ctx->backend->build_response_basis(&sft, rsp); 1020 | /* Skip byte count for now */ 1021 | byte_count_pos = rsp_length++; 1022 | rsp[rsp_length++] = _REPORT_SLAVE_ID; 1023 | /* Run indicator status to ON */ 1024 | rsp[rsp_length++] = 0xFF; 1025 | /* LMB + length of LIBMODBUS_VERSION_STRING */ 1026 | str_len = 3 + strlen(LIBMODBUS_VERSION_STRING); 1027 | memcpy(rsp + rsp_length, "LMB" LIBMODBUS_VERSION_STRING, str_len); 1028 | rsp_length += str_len; 1029 | rsp[byte_count_pos] = rsp_length - byte_count_pos - 1; 1030 | } break; 1031 | case MODBUS_FC_READ_EXCEPTION_STATUS: 1032 | if (ctx->debug) { 1033 | fprintf(stderr, "FIXME Not implemented\n"); 1034 | } 1035 | errno = ENOPROTOOPT; 1036 | return -1; 1037 | break; 1038 | case MODBUS_FC_MASK_WRITE_REGISTER: { 1039 | int mapping_address = address - mb_mapping->start_registers; 1040 | 1041 | if (mapping_address < 0 || mapping_address >= mb_mapping->nb_registers) { 1042 | rsp_length = 1043 | response_exception(ctx, 1044 | &sft, 1045 | MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, 1046 | rsp, 1047 | FALSE, 1048 | "Illegal data address 0x%0X in write_register\n", 1049 | address); 1050 | } else { 1051 | uint16_t data = mb_mapping->tab_registers[mapping_address]; 1052 | uint16_t and = (req[offset + 3] << 8) + req[offset + 4]; 1053 | uint16_t or = (req[offset + 5] << 8) + req[offset + 6]; 1054 | 1055 | data = (data & and) | (or &(~and)); 1056 | mb_mapping->tab_registers[mapping_address] = data; 1057 | memcpy(rsp, req, req_length); 1058 | rsp_length = req_length; 1059 | } 1060 | } break; 1061 | case MODBUS_FC_WRITE_AND_READ_REGISTERS: { 1062 | int nb = (req[offset + 3] << 8) + req[offset + 4]; 1063 | uint16_t address_write = (req[offset + 5] << 8) + req[offset + 6]; 1064 | int nb_write = (req[offset + 7] << 8) + req[offset + 8]; 1065 | int nb_write_bytes = req[offset + 9]; 1066 | int mapping_address = address - mb_mapping->start_registers; 1067 | int mapping_address_write = address_write - mb_mapping->start_registers; 1068 | 1069 | if (nb_write < 1 || MODBUS_MAX_WR_WRITE_REGISTERS < nb_write || nb < 1 || 1070 | MODBUS_MAX_WR_READ_REGISTERS < nb || nb_write_bytes != nb_write * 2) { 1071 | rsp_length = response_exception( 1072 | ctx, 1073 | &sft, 1074 | MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, 1075 | rsp, 1076 | TRUE, 1077 | "Illegal nb of values (W%d, R%d) in write_and_read_registers (max W%d, " 1078 | "R%d)\n", 1079 | nb_write, 1080 | nb, 1081 | MODBUS_MAX_WR_WRITE_REGISTERS, 1082 | MODBUS_MAX_WR_READ_REGISTERS); 1083 | } else if (mapping_address < 0 || 1084 | (mapping_address + nb) > mb_mapping->nb_registers || 1085 | mapping_address_write < 0 || 1086 | (mapping_address_write + nb_write) > mb_mapping->nb_registers) { 1087 | rsp_length = response_exception( 1088 | ctx, 1089 | &sft, 1090 | MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, 1091 | rsp, 1092 | FALSE, 1093 | "Illegal data read address 0x%0X or write address 0x%0X " 1094 | "write_and_read_registers\n", 1095 | mapping_address < 0 ? address : address + nb, 1096 | mapping_address_write < 0 ? address_write : address_write + nb_write); 1097 | } else { 1098 | int i, j; 1099 | rsp_length = ctx->backend->build_response_basis(&sft, rsp); 1100 | rsp[rsp_length++] = nb << 1; 1101 | 1102 | /* Write first. 1103 | 10 and 11 are the offset of the first values to write */ 1104 | for (i = mapping_address_write, j = 10; i < mapping_address_write + nb_write; 1105 | i++, j += 2) { 1106 | mb_mapping->tab_registers[i] = 1107 | (req[offset + j] << 8) + req[offset + j + 1]; 1108 | } 1109 | 1110 | /* and read the data for the response */ 1111 | for (i = mapping_address; i < mapping_address + nb; i++) { 1112 | rsp[rsp_length++] = mb_mapping->tab_registers[i] >> 8; 1113 | rsp[rsp_length++] = mb_mapping->tab_registers[i] & 0xFF; 1114 | } 1115 | } 1116 | } break; 1117 | 1118 | default: 1119 | rsp_length = response_exception(ctx, 1120 | &sft, 1121 | MODBUS_EXCEPTION_ILLEGAL_FUNCTION, 1122 | rsp, 1123 | TRUE, 1124 | "Unknown Modbus function code: 0x%0X\n", 1125 | function); 1126 | break; 1127 | } 1128 | 1129 | /* Suppress any responses in RTU when the request was a broadcast, excepted when quirk 1130 | * is enabled. */ 1131 | if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU && 1132 | slave == MODBUS_BROADCAST_ADDRESS && 1133 | !(ctx->quirks & MODBUS_QUIRK_REPLY_TO_BROADCAST)) { 1134 | return 0; 1135 | } 1136 | return send_msg(ctx, rsp, rsp_length); 1137 | } 1138 | 1139 | int modbus_reply_exception(modbus_t *ctx, const uint8_t *req, unsigned int exception_code) 1140 | { 1141 | unsigned int offset; 1142 | int slave; 1143 | int function; 1144 | uint8_t rsp[MAX_MESSAGE_LENGTH]; 1145 | int rsp_length; 1146 | int dummy_length = 99; 1147 | sft_t sft; 1148 | 1149 | if (ctx == NULL) { 1150 | errno = EINVAL; 1151 | return -1; 1152 | } 1153 | 1154 | offset = ctx->backend->header_length; 1155 | slave = req[offset - 1]; 1156 | function = req[offset]; 1157 | 1158 | sft.slave = slave; 1159 | sft.function = function + 0x80; 1160 | sft.t_id = ctx->backend->prepare_response_tid(req, &dummy_length); 1161 | rsp_length = ctx->backend->build_response_basis(&sft, rsp); 1162 | 1163 | /* Positive exception code */ 1164 | if (exception_code < MODBUS_EXCEPTION_MAX) { 1165 | rsp[rsp_length++] = exception_code; 1166 | return send_msg(ctx, rsp, rsp_length); 1167 | } else { 1168 | errno = EINVAL; 1169 | return -1; 1170 | } 1171 | } 1172 | 1173 | /* Reads IO status */ 1174 | static int read_io_status(modbus_t *ctx, int function, int addr, int nb, uint8_t *dest) 1175 | { 1176 | int rc; 1177 | int req_length; 1178 | 1179 | uint8_t req[_MIN_REQ_LENGTH]; 1180 | uint8_t rsp[MAX_MESSAGE_LENGTH]; 1181 | 1182 | req_length = ctx->backend->build_request_basis(ctx, function, addr, nb, req); 1183 | 1184 | rc = send_msg(ctx, req, req_length); 1185 | if (rc > 0) { 1186 | int temp, bit; 1187 | int pos = 0; 1188 | unsigned int offset; 1189 | unsigned int offset_end; 1190 | 1191 | rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION); 1192 | if (rc == -1) 1193 | return -1; 1194 | 1195 | rc = check_confirmation(ctx, req, rsp, rc); 1196 | if (rc == -1) 1197 | return -1; 1198 | 1199 | offset = ctx->backend->header_length + 2; 1200 | offset_end = offset + rc; 1201 | for (unsigned int i = offset; i < offset_end; i++) { 1202 | /* Shift reg hi_byte to temp */ 1203 | temp = rsp[i]; 1204 | 1205 | for (bit = 0x01; (bit & 0xff) && (pos < nb);) { 1206 | dest[pos++] = (temp & bit) ? TRUE : FALSE; 1207 | bit = bit << 1; 1208 | } 1209 | } 1210 | } 1211 | 1212 | return rc; 1213 | } 1214 | 1215 | /* Reads the boolean status of bits and sets the array elements 1216 | in the destination to TRUE or FALSE (single bits). */ 1217 | int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest) 1218 | { 1219 | int rc; 1220 | 1221 | if (ctx == NULL) { 1222 | errno = EINVAL; 1223 | return -1; 1224 | } 1225 | 1226 | if (nb > MODBUS_MAX_READ_BITS) { 1227 | if (ctx->debug) { 1228 | fprintf(stderr, 1229 | "ERROR Too many bits requested (%d > %d)\n", 1230 | nb, 1231 | MODBUS_MAX_READ_BITS); 1232 | } 1233 | errno = EMBMDATA; 1234 | return -1; 1235 | } 1236 | 1237 | rc = read_io_status(ctx, MODBUS_FC_READ_COILS, addr, nb, dest); 1238 | 1239 | if (rc == -1) 1240 | return -1; 1241 | else 1242 | return nb; 1243 | } 1244 | 1245 | /* Same as modbus_read_bits but reads the remote device input table */ 1246 | int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest) 1247 | { 1248 | int rc; 1249 | 1250 | if (ctx == NULL) { 1251 | errno = EINVAL; 1252 | return -1; 1253 | } 1254 | 1255 | if (nb > MODBUS_MAX_READ_BITS) { 1256 | if (ctx->debug) { 1257 | fprintf(stderr, 1258 | "ERROR Too many discrete inputs requested (%d > %d)\n", 1259 | nb, 1260 | MODBUS_MAX_READ_BITS); 1261 | } 1262 | errno = EMBMDATA; 1263 | return -1; 1264 | } 1265 | 1266 | rc = read_io_status(ctx, MODBUS_FC_READ_DISCRETE_INPUTS, addr, nb, dest); 1267 | 1268 | if (rc == -1) 1269 | return -1; 1270 | else 1271 | return nb; 1272 | } 1273 | 1274 | /* Reads the data from a remote device and put that data into an array */ 1275 | static int read_registers(modbus_t *ctx, int function, int addr, int nb, uint16_t *dest) 1276 | { 1277 | int rc; 1278 | int req_length; 1279 | uint8_t req[_MIN_REQ_LENGTH]; 1280 | uint8_t rsp[MAX_MESSAGE_LENGTH]; 1281 | 1282 | if (nb > MODBUS_MAX_READ_REGISTERS) { 1283 | if (ctx->debug) { 1284 | fprintf(stderr, 1285 | "ERROR Too many registers requested (%d > %d)\n", 1286 | nb, 1287 | MODBUS_MAX_READ_REGISTERS); 1288 | } 1289 | errno = EMBMDATA; 1290 | return -1; 1291 | } 1292 | 1293 | req_length = ctx->backend->build_request_basis(ctx, function, addr, nb, req); 1294 | 1295 | rc = send_msg(ctx, req, req_length); 1296 | if (rc > 0) { 1297 | unsigned int offset; 1298 | int i; 1299 | 1300 | rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION); 1301 | if (rc == -1) 1302 | return -1; 1303 | 1304 | rc = check_confirmation(ctx, req, rsp, rc); 1305 | if (rc == -1) 1306 | return -1; 1307 | 1308 | offset = ctx->backend->header_length; 1309 | 1310 | for (i = 0; i < rc; i++) { 1311 | /* shift reg hi_byte to temp OR with lo_byte */ 1312 | dest[i] = (rsp[offset + 2 + (i << 1)] << 8) | rsp[offset + 3 + (i << 1)]; 1313 | } 1314 | } 1315 | 1316 | return rc; 1317 | } 1318 | 1319 | /* Reads the holding registers of remote device and put the data into an 1320 | array */ 1321 | int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest) 1322 | { 1323 | int status; 1324 | 1325 | if (ctx == NULL) { 1326 | errno = EINVAL; 1327 | return -1; 1328 | } 1329 | 1330 | if (nb > MODBUS_MAX_READ_REGISTERS) { 1331 | if (ctx->debug) { 1332 | fprintf(stderr, 1333 | "ERROR Too many registers requested (%d > %d)\n", 1334 | nb, 1335 | MODBUS_MAX_READ_REGISTERS); 1336 | } 1337 | errno = EMBMDATA; 1338 | return -1; 1339 | } 1340 | 1341 | status = read_registers(ctx, MODBUS_FC_READ_HOLDING_REGISTERS, addr, nb, dest); 1342 | return status; 1343 | } 1344 | 1345 | /* Reads the input registers of remote device and put the data into an array */ 1346 | int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest) 1347 | { 1348 | int status; 1349 | 1350 | if (ctx == NULL) { 1351 | errno = EINVAL; 1352 | return -1; 1353 | } 1354 | 1355 | if (nb > MODBUS_MAX_READ_REGISTERS) { 1356 | fprintf(stderr, 1357 | "ERROR Too many input registers requested (%d > %d)\n", 1358 | nb, 1359 | MODBUS_MAX_READ_REGISTERS); 1360 | errno = EMBMDATA; 1361 | return -1; 1362 | } 1363 | 1364 | status = read_registers(ctx, MODBUS_FC_READ_INPUT_REGISTERS, addr, nb, dest); 1365 | 1366 | return status; 1367 | } 1368 | 1369 | /* Write a value to the specified register of the remote device. 1370 | Used by write_bit and write_register */ 1371 | static int write_single(modbus_t *ctx, int function, int addr, const uint16_t value) 1372 | { 1373 | int rc; 1374 | int req_length; 1375 | uint8_t req[_MIN_REQ_LENGTH]; 1376 | 1377 | if (ctx == NULL) { 1378 | errno = EINVAL; 1379 | return -1; 1380 | } 1381 | 1382 | req_length = ctx->backend->build_request_basis(ctx, function, addr, (int) value, req); 1383 | 1384 | rc = send_msg(ctx, req, req_length); 1385 | if (rc > 0) { 1386 | /* Used by write_bit and write_register */ 1387 | uint8_t rsp[MAX_MESSAGE_LENGTH]; 1388 | 1389 | rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION); 1390 | if (rc == -1) 1391 | return -1; 1392 | 1393 | rc = check_confirmation(ctx, req, rsp, rc); 1394 | } 1395 | 1396 | return rc; 1397 | } 1398 | 1399 | /* Turns ON or OFF a single bit of the remote device */ 1400 | int modbus_write_bit(modbus_t *ctx, int addr, int status) 1401 | { 1402 | if (ctx == NULL) { 1403 | errno = EINVAL; 1404 | return -1; 1405 | } 1406 | 1407 | return write_single(ctx, MODBUS_FC_WRITE_SINGLE_COIL, addr, status ? 0xFF00 : 0); 1408 | } 1409 | 1410 | /* Writes a value in one register of the remote device */ 1411 | int modbus_write_register(modbus_t *ctx, int addr, const uint16_t value) 1412 | { 1413 | if (ctx == NULL) { 1414 | errno = EINVAL; 1415 | return -1; 1416 | } 1417 | 1418 | return write_single(ctx, MODBUS_FC_WRITE_SINGLE_REGISTER, addr, value); 1419 | } 1420 | 1421 | /* Write the bits of the array in the remote device */ 1422 | int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src) 1423 | { 1424 | int rc; 1425 | int i; 1426 | int byte_count; 1427 | int req_length; 1428 | int bit_check = 0; 1429 | int pos = 0; 1430 | uint8_t req[MAX_MESSAGE_LENGTH]; 1431 | 1432 | if (ctx == NULL) { 1433 | errno = EINVAL; 1434 | return -1; 1435 | } 1436 | 1437 | if (nb > MODBUS_MAX_WRITE_BITS) { 1438 | if (ctx->debug) { 1439 | fprintf(stderr, 1440 | "ERROR Writing too many bits (%d > %d)\n", 1441 | nb, 1442 | MODBUS_MAX_WRITE_BITS); 1443 | } 1444 | errno = EMBMDATA; 1445 | return -1; 1446 | } 1447 | 1448 | req_length = ctx->backend->build_request_basis( 1449 | ctx, MODBUS_FC_WRITE_MULTIPLE_COILS, addr, nb, req); 1450 | byte_count = (nb / 8) + ((nb % 8) ? 1 : 0); 1451 | req[req_length++] = byte_count; 1452 | 1453 | for (i = 0; i < byte_count; i++) { 1454 | int bit; 1455 | 1456 | bit = 0x01; 1457 | req[req_length] = 0; 1458 | 1459 | while ((bit & 0xFF) && (bit_check++ < nb)) { 1460 | if (src[pos++]) 1461 | req[req_length] |= bit; 1462 | else 1463 | req[req_length] &= ~bit; 1464 | 1465 | bit = bit << 1; 1466 | } 1467 | req_length++; 1468 | } 1469 | 1470 | rc = send_msg(ctx, req, req_length); 1471 | if (rc > 0) { 1472 | uint8_t rsp[MAX_MESSAGE_LENGTH]; 1473 | 1474 | rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION); 1475 | if (rc == -1) 1476 | return -1; 1477 | 1478 | rc = check_confirmation(ctx, req, rsp, rc); 1479 | } 1480 | 1481 | return rc; 1482 | } 1483 | 1484 | /* Write the values from the array to the registers of the remote device */ 1485 | int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src) 1486 | { 1487 | int rc; 1488 | int i; 1489 | int req_length; 1490 | int byte_count; 1491 | uint8_t req[MAX_MESSAGE_LENGTH]; 1492 | 1493 | if (ctx == NULL) { 1494 | errno = EINVAL; 1495 | return -1; 1496 | } 1497 | 1498 | if (nb > MODBUS_MAX_WRITE_REGISTERS) { 1499 | if (ctx->debug) { 1500 | fprintf(stderr, 1501 | "ERROR Trying to write to too many registers (%d > %d)\n", 1502 | nb, 1503 | MODBUS_MAX_WRITE_REGISTERS); 1504 | } 1505 | errno = EMBMDATA; 1506 | return -1; 1507 | } 1508 | 1509 | req_length = ctx->backend->build_request_basis( 1510 | ctx, MODBUS_FC_WRITE_MULTIPLE_REGISTERS, addr, nb, req); 1511 | byte_count = nb * 2; 1512 | req[req_length++] = byte_count; 1513 | 1514 | for (i = 0; i < nb; i++) { 1515 | req[req_length++] = src[i] >> 8; 1516 | req[req_length++] = src[i] & 0x00FF; 1517 | } 1518 | 1519 | rc = send_msg(ctx, req, req_length); 1520 | if (rc > 0) { 1521 | uint8_t rsp[MAX_MESSAGE_LENGTH]; 1522 | 1523 | rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION); 1524 | if (rc == -1) 1525 | return -1; 1526 | 1527 | rc = check_confirmation(ctx, req, rsp, rc); 1528 | } 1529 | 1530 | return rc; 1531 | } 1532 | 1533 | int modbus_mask_write_register(modbus_t *ctx, 1534 | int addr, 1535 | uint16_t and_mask, 1536 | uint16_t or_mask) 1537 | { 1538 | int rc; 1539 | int req_length; 1540 | /* The request length can not exceed _MIN_REQ_LENGTH - 2 and 4 bytes to 1541 | * store the masks. The ugly substraction is there to remove the 'nb' value 1542 | * (2 bytes) which is not used. */ 1543 | uint8_t req[_MIN_REQ_LENGTH + 2]; 1544 | 1545 | req_length = ctx->backend->build_request_basis( 1546 | ctx, MODBUS_FC_MASK_WRITE_REGISTER, addr, 0, req); 1547 | 1548 | /* HACKISH, count is not used */ 1549 | req_length -= 2; 1550 | 1551 | req[req_length++] = and_mask >> 8; 1552 | req[req_length++] = and_mask & 0x00ff; 1553 | req[req_length++] = or_mask >> 8; 1554 | req[req_length++] = or_mask & 0x00ff; 1555 | 1556 | rc = send_msg(ctx, req, req_length); 1557 | if (rc > 0) { 1558 | /* Used by write_bit and write_register */ 1559 | uint8_t rsp[MAX_MESSAGE_LENGTH]; 1560 | 1561 | rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION); 1562 | if (rc == -1) 1563 | return -1; 1564 | 1565 | rc = check_confirmation(ctx, req, rsp, rc); 1566 | } 1567 | 1568 | return rc; 1569 | } 1570 | 1571 | /* Write multiple registers from src array to remote device and read multiple 1572 | registers from remote device to dest array. */ 1573 | int modbus_write_and_read_registers(modbus_t *ctx, 1574 | int write_addr, 1575 | int write_nb, 1576 | const uint16_t *src, 1577 | int read_addr, 1578 | int read_nb, 1579 | uint16_t *dest) 1580 | 1581 | { 1582 | int rc; 1583 | int req_length; 1584 | int i; 1585 | int byte_count; 1586 | uint8_t req[MAX_MESSAGE_LENGTH]; 1587 | uint8_t rsp[MAX_MESSAGE_LENGTH]; 1588 | 1589 | if (ctx == NULL) { 1590 | errno = EINVAL; 1591 | return -1; 1592 | } 1593 | 1594 | if (write_nb > MODBUS_MAX_WR_WRITE_REGISTERS) { 1595 | if (ctx->debug) { 1596 | fprintf(stderr, 1597 | "ERROR Too many registers to write (%d > %d)\n", 1598 | write_nb, 1599 | MODBUS_MAX_WR_WRITE_REGISTERS); 1600 | } 1601 | errno = EMBMDATA; 1602 | return -1; 1603 | } 1604 | 1605 | if (read_nb > MODBUS_MAX_WR_READ_REGISTERS) { 1606 | if (ctx->debug) { 1607 | fprintf(stderr, 1608 | "ERROR Too many registers requested (%d > %d)\n", 1609 | read_nb, 1610 | MODBUS_MAX_WR_READ_REGISTERS); 1611 | } 1612 | errno = EMBMDATA; 1613 | return -1; 1614 | } 1615 | req_length = ctx->backend->build_request_basis( 1616 | ctx, MODBUS_FC_WRITE_AND_READ_REGISTERS, read_addr, read_nb, req); 1617 | 1618 | req[req_length++] = write_addr >> 8; 1619 | req[req_length++] = write_addr & 0x00ff; 1620 | req[req_length++] = write_nb >> 8; 1621 | req[req_length++] = write_nb & 0x00ff; 1622 | byte_count = write_nb * 2; 1623 | req[req_length++] = byte_count; 1624 | 1625 | for (i = 0; i < write_nb; i++) { 1626 | req[req_length++] = src[i] >> 8; 1627 | req[req_length++] = src[i] & 0x00FF; 1628 | } 1629 | 1630 | rc = send_msg(ctx, req, req_length); 1631 | if (rc > 0) { 1632 | unsigned int offset; 1633 | 1634 | rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION); 1635 | if (rc == -1) 1636 | return -1; 1637 | 1638 | rc = check_confirmation(ctx, req, rsp, rc); 1639 | if (rc == -1) 1640 | return -1; 1641 | 1642 | offset = ctx->backend->header_length; 1643 | for (i = 0; i < rc; i++) { 1644 | /* shift reg hi_byte to temp OR with lo_byte */ 1645 | dest[i] = (rsp[offset + 2 + (i << 1)] << 8) | rsp[offset + 3 + (i << 1)]; 1646 | } 1647 | } 1648 | 1649 | return rc; 1650 | } 1651 | 1652 | /* Send a request to get the slave ID of the device (only available in serial 1653 | communication). */ 1654 | int modbus_report_slave_id(modbus_t *ctx, int max_dest, uint8_t *dest) 1655 | { 1656 | int rc; 1657 | int req_length; 1658 | uint8_t req[_MIN_REQ_LENGTH]; 1659 | 1660 | if (ctx == NULL || max_dest <= 0) { 1661 | errno = EINVAL; 1662 | return -1; 1663 | } 1664 | 1665 | req_length = 1666 | ctx->backend->build_request_basis(ctx, MODBUS_FC_REPORT_SLAVE_ID, 0, 0, req); 1667 | 1668 | /* HACKISH, addr and count are not used */ 1669 | req_length -= 4; 1670 | 1671 | rc = send_msg(ctx, req, req_length); 1672 | if (rc > 0) { 1673 | int i; 1674 | unsigned int offset; 1675 | uint8_t rsp[MAX_MESSAGE_LENGTH]; 1676 | 1677 | rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION); 1678 | if (rc == -1) 1679 | return -1; 1680 | 1681 | rc = check_confirmation(ctx, req, rsp, rc); 1682 | if (rc == -1) 1683 | return -1; 1684 | 1685 | offset = ctx->backend->header_length + 2; 1686 | 1687 | /* Byte count, slave id, run indicator status and 1688 | additional data. Truncate copy to max_dest. */ 1689 | for (i = 0; i < rc && i < max_dest; i++) { 1690 | dest[i] = rsp[offset + i]; 1691 | } 1692 | } 1693 | 1694 | return rc; 1695 | } 1696 | 1697 | void _modbus_init_common(modbus_t *ctx) 1698 | { 1699 | /* Slave and socket are initialized to -1 */ 1700 | ctx->slave = -1; 1701 | ctx->s = -1; 1702 | 1703 | ctx->debug = FALSE; 1704 | ctx->error_recovery = MODBUS_ERROR_RECOVERY_NONE; 1705 | ctx->quirks = MODBUS_QUIRK_NONE; 1706 | 1707 | ctx->response_timeout.tv_sec = 0; 1708 | ctx->response_timeout.tv_usec = _RESPONSE_TIMEOUT; 1709 | 1710 | ctx->byte_timeout.tv_sec = 0; 1711 | ctx->byte_timeout.tv_usec = _BYTE_TIMEOUT; 1712 | 1713 | ctx->indication_timeout.tv_sec = 0; 1714 | ctx->indication_timeout.tv_usec = 0; 1715 | } 1716 | 1717 | /* Define the slave number */ 1718 | int modbus_set_slave(modbus_t *ctx, int slave) 1719 | { 1720 | if (ctx == NULL) { 1721 | errno = EINVAL; 1722 | return -1; 1723 | } 1724 | 1725 | return ctx->backend->set_slave(ctx, slave); 1726 | } 1727 | 1728 | int modbus_get_slave(modbus_t *ctx) 1729 | { 1730 | if (ctx == NULL) { 1731 | errno = EINVAL; 1732 | return -1; 1733 | } 1734 | 1735 | return ctx->slave; 1736 | } 1737 | 1738 | int modbus_set_error_recovery(modbus_t *ctx, modbus_error_recovery_mode error_recovery) 1739 | { 1740 | if (ctx == NULL) { 1741 | errno = EINVAL; 1742 | return -1; 1743 | } 1744 | 1745 | /* The type of modbus_error_recovery_mode is unsigned enum */ 1746 | ctx->error_recovery = (uint8_t) error_recovery; 1747 | return 0; 1748 | } 1749 | 1750 | // FIXME Doesn't work under Windows RTU 1751 | int modbus_set_socket(modbus_t *ctx, int s) 1752 | { 1753 | if (ctx == NULL) { 1754 | errno = EINVAL; 1755 | return -1; 1756 | } 1757 | 1758 | ctx->s = s; 1759 | return 0; 1760 | } 1761 | 1762 | int modbus_get_socket(modbus_t *ctx) 1763 | { 1764 | if (ctx == NULL) { 1765 | errno = EINVAL; 1766 | return -1; 1767 | } 1768 | 1769 | return ctx->s; 1770 | } 1771 | 1772 | /* Get the timeout interval used to wait for a response */ 1773 | int modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec) 1774 | { 1775 | if (ctx == NULL) { 1776 | errno = EINVAL; 1777 | return -1; 1778 | } 1779 | 1780 | *to_sec = ctx->response_timeout.tv_sec; 1781 | *to_usec = ctx->response_timeout.tv_usec; 1782 | return 0; 1783 | } 1784 | 1785 | int modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec) 1786 | { 1787 | if (ctx == NULL || (to_sec == 0 && to_usec == 0) || to_usec > 999999) { 1788 | errno = EINVAL; 1789 | return -1; 1790 | } 1791 | 1792 | ctx->response_timeout.tv_sec = to_sec; 1793 | ctx->response_timeout.tv_usec = to_usec; 1794 | return 0; 1795 | } 1796 | 1797 | /* Get the timeout interval between two consecutive bytes of a message */ 1798 | int modbus_get_byte_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec) 1799 | { 1800 | if (ctx == NULL) { 1801 | errno = EINVAL; 1802 | return -1; 1803 | } 1804 | 1805 | *to_sec = ctx->byte_timeout.tv_sec; 1806 | *to_usec = ctx->byte_timeout.tv_usec; 1807 | return 0; 1808 | } 1809 | 1810 | int modbus_set_byte_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec) 1811 | { 1812 | /* Byte timeout can be disabled when both values are zero */ 1813 | if (ctx == NULL || to_usec > 999999) { 1814 | errno = EINVAL; 1815 | return -1; 1816 | } 1817 | 1818 | ctx->byte_timeout.tv_sec = to_sec; 1819 | ctx->byte_timeout.tv_usec = to_usec; 1820 | return 0; 1821 | } 1822 | 1823 | /* Get the timeout interval used by the server to wait for an indication from a client */ 1824 | int modbus_get_indication_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec) 1825 | { 1826 | if (ctx == NULL) { 1827 | errno = EINVAL; 1828 | return -1; 1829 | } 1830 | 1831 | *to_sec = ctx->indication_timeout.tv_sec; 1832 | *to_usec = ctx->indication_timeout.tv_usec; 1833 | return 0; 1834 | } 1835 | 1836 | int modbus_set_indication_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec) 1837 | { 1838 | /* Indication timeout can be disabled when both values are zero */ 1839 | if (ctx == NULL || to_usec > 999999) { 1840 | errno = EINVAL; 1841 | return -1; 1842 | } 1843 | 1844 | ctx->indication_timeout.tv_sec = to_sec; 1845 | ctx->indication_timeout.tv_usec = to_usec; 1846 | return 0; 1847 | } 1848 | 1849 | int modbus_get_header_length(modbus_t *ctx) 1850 | { 1851 | if (ctx == NULL) { 1852 | errno = EINVAL; 1853 | return -1; 1854 | } 1855 | 1856 | return ctx->backend->header_length; 1857 | } 1858 | 1859 | int modbus_enable_quirks(modbus_t *ctx, unsigned int quirks_mask) 1860 | { 1861 | if (ctx == NULL) { 1862 | errno = EINVAL; 1863 | return -1; 1864 | } 1865 | 1866 | /* Enable quirks that have a true value at their index in the mask */ 1867 | ctx->quirks |= quirks_mask; 1868 | return 0; 1869 | } 1870 | 1871 | int modbus_disable_quirks(modbus_t *ctx, unsigned int quirks_mask) 1872 | { 1873 | if (ctx == NULL) { 1874 | errno = EINVAL; 1875 | return -1; 1876 | } 1877 | 1878 | /* Disable quirks that have a true value at ther index in the mask */ 1879 | ctx->quirks &= ~quirks_mask; 1880 | return 0; 1881 | } 1882 | 1883 | int modbus_connect(modbus_t *ctx) 1884 | { 1885 | if (ctx == NULL) { 1886 | errno = EINVAL; 1887 | return -1; 1888 | } 1889 | 1890 | return ctx->backend->connect(ctx); 1891 | } 1892 | 1893 | void modbus_close(modbus_t *ctx) 1894 | { 1895 | if (ctx == NULL) 1896 | return; 1897 | 1898 | ctx->backend->close(ctx); 1899 | } 1900 | 1901 | void modbus_free(modbus_t *ctx) 1902 | { 1903 | if (ctx == NULL) 1904 | return; 1905 | 1906 | ctx->backend->free(ctx); 1907 | } 1908 | 1909 | int modbus_set_debug(modbus_t *ctx, int flag) 1910 | { 1911 | if (ctx == NULL) { 1912 | errno = EINVAL; 1913 | return -1; 1914 | } 1915 | 1916 | ctx->debug = flag; 1917 | return 0; 1918 | } 1919 | 1920 | /* Allocates 4 arrays to store bits, input bits, registers and inputs 1921 | registers. The pointers are stored in modbus_mapping structure. 1922 | 1923 | The modbus_mapping_new_start_address() function shall return the new allocated 1924 | structure if successful. Otherwise it shall return NULL and set errno to 1925 | ENOMEM. */ 1926 | modbus_mapping_t *modbus_mapping_new_start_address(unsigned int start_bits, 1927 | unsigned int nb_bits, 1928 | unsigned int start_input_bits, 1929 | unsigned int nb_input_bits, 1930 | unsigned int start_registers, 1931 | unsigned int nb_registers, 1932 | unsigned int start_input_registers, 1933 | unsigned int nb_input_registers) 1934 | { 1935 | modbus_mapping_t *mb_mapping; 1936 | 1937 | mb_mapping = (modbus_mapping_t *) malloc(sizeof(modbus_mapping_t)); 1938 | if (mb_mapping == NULL) { 1939 | return NULL; 1940 | } 1941 | 1942 | /* 0X */ 1943 | mb_mapping->nb_bits = nb_bits; 1944 | mb_mapping->start_bits = start_bits; 1945 | if (nb_bits == 0) { 1946 | mb_mapping->tab_bits = NULL; 1947 | } else { 1948 | /* Negative number raises a POSIX error */ 1949 | mb_mapping->tab_bits = (uint8_t *) malloc(nb_bits * sizeof(uint8_t)); 1950 | if (mb_mapping->tab_bits == NULL) { 1951 | free(mb_mapping); 1952 | return NULL; 1953 | } 1954 | memset(mb_mapping->tab_bits, 0, nb_bits * sizeof(uint8_t)); 1955 | } 1956 | 1957 | /* 1X */ 1958 | mb_mapping->nb_input_bits = nb_input_bits; 1959 | mb_mapping->start_input_bits = start_input_bits; 1960 | if (nb_input_bits == 0) { 1961 | mb_mapping->tab_input_bits = NULL; 1962 | } else { 1963 | mb_mapping->tab_input_bits = (uint8_t *) malloc(nb_input_bits * sizeof(uint8_t)); 1964 | if (mb_mapping->tab_input_bits == NULL) { 1965 | free(mb_mapping->tab_bits); 1966 | free(mb_mapping); 1967 | return NULL; 1968 | } 1969 | memset(mb_mapping->tab_input_bits, 0, nb_input_bits * sizeof(uint8_t)); 1970 | } 1971 | 1972 | /* 4X */ 1973 | mb_mapping->nb_registers = nb_registers; 1974 | mb_mapping->start_registers = start_registers; 1975 | if (nb_registers == 0) { 1976 | mb_mapping->tab_registers = NULL; 1977 | } else { 1978 | mb_mapping->tab_registers = (uint16_t *) malloc(nb_registers * sizeof(uint16_t)); 1979 | if (mb_mapping->tab_registers == NULL) { 1980 | free(mb_mapping->tab_input_bits); 1981 | free(mb_mapping->tab_bits); 1982 | free(mb_mapping); 1983 | return NULL; 1984 | } 1985 | memset(mb_mapping->tab_registers, 0, nb_registers * sizeof(uint16_t)); 1986 | } 1987 | 1988 | /* 3X */ 1989 | mb_mapping->nb_input_registers = nb_input_registers; 1990 | mb_mapping->start_input_registers = start_input_registers; 1991 | if (nb_input_registers == 0) { 1992 | mb_mapping->tab_input_registers = NULL; 1993 | } else { 1994 | mb_mapping->tab_input_registers = 1995 | (uint16_t *) malloc(nb_input_registers * sizeof(uint16_t)); 1996 | if (mb_mapping->tab_input_registers == NULL) { 1997 | free(mb_mapping->tab_registers); 1998 | free(mb_mapping->tab_input_bits); 1999 | free(mb_mapping->tab_bits); 2000 | free(mb_mapping); 2001 | return NULL; 2002 | } 2003 | memset(mb_mapping->tab_input_registers, 0, nb_input_registers * sizeof(uint16_t)); 2004 | } 2005 | 2006 | return mb_mapping; 2007 | } 2008 | 2009 | modbus_mapping_t *modbus_mapping_new(int nb_bits, 2010 | int nb_input_bits, 2011 | int nb_registers, 2012 | int nb_input_registers) 2013 | { 2014 | return modbus_mapping_new_start_address( 2015 | 0, nb_bits, 0, nb_input_bits, 0, nb_registers, 0, nb_input_registers); 2016 | } 2017 | 2018 | /* Frees the 4 arrays */ 2019 | void modbus_mapping_free(modbus_mapping_t *mb_mapping) 2020 | { 2021 | if (mb_mapping == NULL) { 2022 | return; 2023 | } 2024 | 2025 | free(mb_mapping->tab_input_registers); 2026 | free(mb_mapping->tab_registers); 2027 | free(mb_mapping->tab_input_bits); 2028 | free(mb_mapping->tab_bits); 2029 | free(mb_mapping); 2030 | } 2031 | 2032 | #ifndef HAVE_STRLCPY 2033 | /* 2034 | * Function strlcpy was originally developed by 2035 | * Todd C. Miller to simplify writing secure code. 2036 | * See ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/strlcpy.3 2037 | * for more information. 2038 | * 2039 | * Thank you Ulrich Drepper... not! 2040 | * 2041 | * Copy src to string dest of size dest_size. At most dest_size-1 characters 2042 | * will be copied. Always NUL terminates (unless dest_size == 0). Returns 2043 | * strlen(src); if retval >= dest_size, truncation occurred. 2044 | */ 2045 | size_t strlcpy(char *dest, const char *src, size_t dest_size) 2046 | { 2047 | register char *d = dest; 2048 | register const char *s = src; 2049 | register size_t n = dest_size; 2050 | 2051 | /* Copy as many bytes as will fit */ 2052 | if (n != 0 && --n != 0) { 2053 | do { 2054 | if ((*d++ = *s++) == 0) 2055 | break; 2056 | } while (--n != 0); 2057 | } 2058 | 2059 | /* Not enough room in dest, add NUL and traverse rest of src */ 2060 | if (n == 0) { 2061 | if (dest_size != 0) 2062 | *d = '\0'; /* NUL-terminate dest */ 2063 | while (*s++) 2064 | ; 2065 | } 2066 | 2067 | return (s - src - 1); /* count does not include NUL */ 2068 | } 2069 | #endif 2070 | -------------------------------------------------------------------------------- /modbus/modbus.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © Stéphane Raimbault 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | 7 | #ifndef MODBUS_H 8 | #define MODBUS_H 9 | 10 | // clang-format off 11 | /* Add this for macros that defined unix flavor */ 12 | #if (defined(__unix__) || defined(unix)) && !defined(USG) 13 | # include 14 | #endif 15 | 16 | #ifndef _MSC_VER 17 | # include 18 | #else 19 | # include "stdint.h" 20 | #endif 21 | 22 | #include "modbus-version.h" 23 | 24 | #if defined(_MSC_VER) 25 | # if defined(DLLBUILD) 26 | /* define DLLBUILD when building the DLL */ 27 | # define MODBUS_API __declspec(dllexport) 28 | # else 29 | # define MODBUS_API __declspec(dllimport) 30 | # endif 31 | #else 32 | # define MODBUS_API 33 | #endif 34 | 35 | #ifdef __cplusplus 36 | # define MODBUS_BEGIN_DECLS extern "C" { 37 | # define MODBUS_END_DECLS } 38 | #else 39 | # define MODBUS_BEGIN_DECLS 40 | # define MODBUS_END_DECLS 41 | #endif 42 | // clang-format on 43 | 44 | MODBUS_BEGIN_DECLS 45 | 46 | #ifndef FALSE 47 | #define FALSE 0 48 | #endif 49 | 50 | #ifndef TRUE 51 | #define TRUE 1 52 | #endif 53 | 54 | #ifndef OFF 55 | #define OFF 0 56 | #endif 57 | 58 | #ifndef ON 59 | #define ON 1 60 | #endif 61 | 62 | /* Modbus function codes */ 63 | #define MODBUS_FC_READ_COILS 0x01 64 | #define MODBUS_FC_READ_DISCRETE_INPUTS 0x02 65 | #define MODBUS_FC_READ_HOLDING_REGISTERS 0x03 66 | #define MODBUS_FC_READ_INPUT_REGISTERS 0x04 67 | #define MODBUS_FC_WRITE_SINGLE_COIL 0x05 68 | #define MODBUS_FC_WRITE_SINGLE_REGISTER 0x06 69 | #define MODBUS_FC_READ_EXCEPTION_STATUS 0x07 70 | #define MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F 71 | #define MODBUS_FC_WRITE_MULTIPLE_REGISTERS 0x10 72 | #define MODBUS_FC_REPORT_SLAVE_ID 0x11 73 | #define MODBUS_FC_MASK_WRITE_REGISTER 0x16 74 | #define MODBUS_FC_WRITE_AND_READ_REGISTERS 0x17 75 | 76 | #define MODBUS_BROADCAST_ADDRESS 0 77 | 78 | /* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 1 page 12) 79 | * Quantity of Coils to read (2 bytes): 1 to 2000 (0x7D0) 80 | * (chapter 6 section 11 page 29) 81 | * Quantity of Coils to write (2 bytes): 1 to 1968 (0x7B0) 82 | */ 83 | #define MODBUS_MAX_READ_BITS 2000 84 | #define MODBUS_MAX_WRITE_BITS 1968 85 | 86 | /* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 3 page 15) 87 | * Quantity of Registers to read (2 bytes): 1 to 125 (0x7D) 88 | * (chapter 6 section 12 page 31) 89 | * Quantity of Registers to write (2 bytes) 1 to 123 (0x7B) 90 | * (chapter 6 section 17 page 38) 91 | * Quantity of Registers to write in R/W registers (2 bytes) 1 to 121 (0x79) 92 | */ 93 | #define MODBUS_MAX_READ_REGISTERS 125 94 | #define MODBUS_MAX_WRITE_REGISTERS 123 95 | #define MODBUS_MAX_WR_WRITE_REGISTERS 121 96 | #define MODBUS_MAX_WR_READ_REGISTERS 125 97 | 98 | /* The size of the MODBUS PDU is limited by the size constraint inherited from 99 | * the first MODBUS implementation on Serial Line network (max. RS485 ADU = 256 100 | * bytes). Therefore, MODBUS PDU for serial line communication = 256 - Server 101 | * address (1 byte) - CRC (2 bytes) = 253 bytes. 102 | */ 103 | #define MODBUS_MAX_PDU_LENGTH 253 104 | 105 | /* Consequently: 106 | * - RTU MODBUS ADU = 253 bytes + Server address (1 byte) + CRC (2 bytes) = 256 107 | * bytes. 108 | * - TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes. 109 | * so the maximum of both backend in 260 bytes. This size can used to allocate 110 | * an array of bytes to store responses and it will be compatible with the two 111 | * backends. 112 | */ 113 | #define MODBUS_MAX_ADU_LENGTH 260 114 | 115 | /* Random number to avoid errno conflicts */ 116 | #define MODBUS_ENOBASE 112345678 117 | 118 | /* Protocol exceptions */ 119 | enum { 120 | MODBUS_EXCEPTION_ILLEGAL_FUNCTION = 0x01, 121 | MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, 122 | MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, 123 | MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE, 124 | MODBUS_EXCEPTION_ACKNOWLEDGE, 125 | MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY, 126 | MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE, 127 | MODBUS_EXCEPTION_MEMORY_PARITY, 128 | MODBUS_EXCEPTION_NOT_DEFINED, 129 | MODBUS_EXCEPTION_GATEWAY_PATH, 130 | MODBUS_EXCEPTION_GATEWAY_TARGET, 131 | MODBUS_EXCEPTION_MAX 132 | }; 133 | 134 | #define EMBXILFUN (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_FUNCTION) 135 | #define EMBXILADD (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS) 136 | #define EMBXILVAL (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE) 137 | #define EMBXSFAIL (MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE) 138 | #define EMBXACK (MODBUS_ENOBASE + MODBUS_EXCEPTION_ACKNOWLEDGE) 139 | #define EMBXSBUSY (MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY) 140 | #define EMBXNACK (MODBUS_ENOBASE + MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE) 141 | #define EMBXMEMPAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_MEMORY_PARITY) 142 | #define EMBXGPATH (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_PATH) 143 | #define EMBXGTAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_TARGET) 144 | 145 | /* Native libmodbus error codes */ 146 | #define EMBBADCRC (EMBXGTAR + 1) 147 | #define EMBBADDATA (EMBXGTAR + 2) 148 | #define EMBBADEXC (EMBXGTAR + 3) 149 | #define EMBUNKEXC (EMBXGTAR + 4) 150 | #define EMBMDATA (EMBXGTAR + 5) 151 | #define EMBBADSLAVE (EMBXGTAR + 6) 152 | 153 | extern const unsigned int libmodbus_version_major; 154 | extern const unsigned int libmodbus_version_minor; 155 | extern const unsigned int libmodbus_version_micro; 156 | 157 | typedef struct _modbus modbus_t; 158 | 159 | typedef struct _modbus_mapping_t { 160 | int nb_bits; 161 | int start_bits; 162 | int nb_input_bits; 163 | int start_input_bits; 164 | int nb_input_registers; 165 | int start_input_registers; 166 | int nb_registers; 167 | int start_registers; 168 | uint8_t *tab_bits; 169 | uint8_t *tab_input_bits; 170 | uint16_t *tab_input_registers; 171 | uint16_t *tab_registers; 172 | } modbus_mapping_t; 173 | 174 | typedef enum { 175 | MODBUS_ERROR_RECOVERY_NONE = 0, 176 | MODBUS_ERROR_RECOVERY_LINK = (1 << 1), 177 | MODBUS_ERROR_RECOVERY_PROTOCOL = (1 << 2) 178 | } modbus_error_recovery_mode; 179 | 180 | typedef enum { 181 | MODBUS_QUIRK_NONE = 0, 182 | MODBUS_QUIRK_MAX_SLAVE = (1 << 1), 183 | MODBUS_QUIRK_REPLY_TO_BROADCAST = (1 << 2), 184 | MODBUS_QUIRK_ALL = 0xFF 185 | } modbus_quirks; 186 | 187 | MODBUS_API int modbus_set_slave(modbus_t *ctx, int slave); 188 | MODBUS_API int modbus_get_slave(modbus_t *ctx); 189 | MODBUS_API int modbus_set_error_recovery(modbus_t *ctx, 190 | modbus_error_recovery_mode error_recovery); 191 | MODBUS_API int modbus_set_socket(modbus_t *ctx, int s); 192 | MODBUS_API int modbus_get_socket(modbus_t *ctx); 193 | 194 | MODBUS_API int 195 | modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec); 196 | MODBUS_API int 197 | modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec); 198 | 199 | MODBUS_API int 200 | modbus_get_byte_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec); 201 | MODBUS_API int modbus_set_byte_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec); 202 | 203 | MODBUS_API int 204 | modbus_get_indication_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec); 205 | MODBUS_API int 206 | modbus_set_indication_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec); 207 | 208 | MODBUS_API int modbus_get_header_length(modbus_t *ctx); 209 | 210 | MODBUS_API int modbus_connect(modbus_t *ctx); 211 | MODBUS_API void modbus_close(modbus_t *ctx); 212 | 213 | MODBUS_API void modbus_free(modbus_t *ctx); 214 | 215 | MODBUS_API int modbus_flush(modbus_t *ctx); 216 | MODBUS_API int modbus_set_debug(modbus_t *ctx, int flag); 217 | 218 | MODBUS_API const char *modbus_strerror(int errnum); 219 | 220 | MODBUS_API int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest); 221 | MODBUS_API int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest); 222 | MODBUS_API int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest); 223 | MODBUS_API int 224 | modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest); 225 | MODBUS_API int modbus_write_bit(modbus_t *ctx, int coil_addr, int status); 226 | MODBUS_API int modbus_write_register(modbus_t *ctx, int reg_addr, const uint16_t value); 227 | MODBUS_API int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data); 228 | MODBUS_API int 229 | modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data); 230 | MODBUS_API int 231 | modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask); 232 | MODBUS_API int modbus_write_and_read_registers(modbus_t *ctx, 233 | int write_addr, 234 | int write_nb, 235 | const uint16_t *src, 236 | int read_addr, 237 | int read_nb, 238 | uint16_t *dest); 239 | MODBUS_API int modbus_report_slave_id(modbus_t *ctx, int max_dest, uint8_t *dest); 240 | 241 | MODBUS_API modbus_mapping_t * 242 | modbus_mapping_new_start_address(unsigned int start_bits, 243 | unsigned int nb_bits, 244 | unsigned int start_input_bits, 245 | unsigned int nb_input_bits, 246 | unsigned int start_registers, 247 | unsigned int nb_registers, 248 | unsigned int start_input_registers, 249 | unsigned int nb_input_registers); 250 | 251 | MODBUS_API modbus_mapping_t *modbus_mapping_new(int nb_bits, 252 | int nb_input_bits, 253 | int nb_registers, 254 | int nb_input_registers); 255 | MODBUS_API void modbus_mapping_free(modbus_mapping_t *mb_mapping); 256 | 257 | MODBUS_API int 258 | modbus_send_raw_request(modbus_t *ctx, const uint8_t *raw_req, int raw_req_length); 259 | 260 | MODBUS_API int modbus_receive(modbus_t *ctx, uint8_t *req); 261 | 262 | MODBUS_API int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp); 263 | 264 | MODBUS_API int modbus_reply(modbus_t *ctx, 265 | const uint8_t *req, 266 | int req_length, 267 | modbus_mapping_t *mb_mapping); 268 | MODBUS_API int 269 | modbus_reply_exception(modbus_t *ctx, const uint8_t *req, unsigned int exception_code); 270 | MODBUS_API int modbus_enable_quirks(modbus_t *ctx, unsigned int quirks_mask); 271 | MODBUS_API int modbus_disable_quirks(modbus_t *ctx, unsigned int quirks_mask); 272 | 273 | /** 274 | * UTILS FUNCTIONS 275 | **/ 276 | 277 | #define MODBUS_GET_HIGH_BYTE(data) (((data) >> 8) & 0xFF) 278 | #define MODBUS_GET_LOW_BYTE(data) ((data) &0xFF) 279 | #define MODBUS_GET_INT64_FROM_INT16(tab_int16, index) \ 280 | (((int64_t) tab_int16[(index)] << 48) | ((int64_t) tab_int16[(index) + 1] << 32) | \ 281 | ((int64_t) tab_int16[(index) + 2] << 16) | (int64_t) tab_int16[(index) + 3]) 282 | #define MODBUS_GET_INT32_FROM_INT16(tab_int16, index) \ 283 | (((int32_t) tab_int16[(index)] << 16) | (int32_t) tab_int16[(index) + 1]) 284 | #define MODBUS_GET_INT16_FROM_INT8(tab_int8, index) \ 285 | (((int16_t) tab_int8[(index)] << 8) | (int16_t) tab_int8[(index) + 1]) 286 | #define MODBUS_SET_INT16_TO_INT8(tab_int8, index, value) \ 287 | do { \ 288 | ((int8_t *) (tab_int8))[(index)] = (int8_t) ((value) >> 8); \ 289 | ((int8_t *) (tab_int8))[(index) + 1] = (int8_t) (value); \ 290 | } while (0) 291 | #define MODBUS_SET_INT32_TO_INT16(tab_int16, index, value) \ 292 | do { \ 293 | ((int16_t *) (tab_int16))[(index)] = (int16_t) ((value) >> 16); \ 294 | ((int16_t *) (tab_int16))[(index) + 1] = (int16_t) (value); \ 295 | } while (0) 296 | #define MODBUS_SET_INT64_TO_INT16(tab_int16, index, value) \ 297 | do { \ 298 | ((int16_t *) (tab_int16))[(index)] = (int16_t) ((value) >> 48); \ 299 | ((int16_t *) (tab_int16))[(index) + 1] = (int16_t) ((value) >> 32); \ 300 | ((int16_t *) (tab_int16))[(index) + 2] = (int16_t) ((value) >> 16); \ 301 | ((int16_t *) (tab_int16))[(index) + 3] = (int16_t) (value); \ 302 | } while (0) 303 | 304 | MODBUS_API void modbus_set_bits_from_byte(uint8_t *dest, int idx, const uint8_t value); 305 | MODBUS_API void modbus_set_bits_from_bytes(uint8_t *dest, 306 | int idx, 307 | unsigned int nb_bits, 308 | const uint8_t *tab_byte); 309 | MODBUS_API uint8_t modbus_get_byte_from_bits(const uint8_t *src, 310 | int idx, 311 | unsigned int nb_bits); 312 | MODBUS_API float modbus_get_float(const uint16_t *src); 313 | MODBUS_API float modbus_get_float_abcd(const uint16_t *src); 314 | MODBUS_API float modbus_get_float_dcba(const uint16_t *src); 315 | MODBUS_API float modbus_get_float_badc(const uint16_t *src); 316 | MODBUS_API float modbus_get_float_cdab(const uint16_t *src); 317 | 318 | MODBUS_API void modbus_set_float(float f, uint16_t *dest); 319 | MODBUS_API void modbus_set_float_abcd(float f, uint16_t *dest); 320 | MODBUS_API void modbus_set_float_dcba(float f, uint16_t *dest); 321 | MODBUS_API void modbus_set_float_badc(float f, uint16_t *dest); 322 | MODBUS_API void modbus_set_float_cdab(float f, uint16_t *dest); 323 | 324 | #include "modbus-rtu.h" 325 | #include "modbus-tcp.h" 326 | 327 | MODBUS_END_DECLS 328 | 329 | #endif /* MODBUS_H */ 330 | -------------------------------------------------------------------------------- /modbus/modbus.rc: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #include 3 | #include "config.h" 4 | #include "modbus-version.h" 5 | 6 | #define VERSTRING PACKAGE_VERSION 7 | 8 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 9 | VS_VERSION_INFO VERSIONINFO 10 | FILEVERSION LIBMODBUS_VERSION_MAJOR, LIBMODBUS_VERSION_MINOR, LIBMODBUS_VERSION_MICRO, 2 11 | PRODUCTVERSION LIBMODBUS_VERSION_MAJOR, LIBMODBUS_VERSION_MINOR, LIBMODBUS_VERSION_MICRO, 2 12 | #if defined(DEBUG) || defined(W32DEBUG) 13 | FILEFLAGS 0x1L 14 | #else 15 | FILEFLAGS 0x0L 16 | #endif 17 | FILEOS VOS_NT_WINDOWS32 18 | FILETYPE VFT_DLL 19 | { 20 | BLOCK "StringFileInfo" 21 | { 22 | BLOCK "000004E4" 23 | { 24 | VALUE "CompanyName", "\x0" 25 | VALUE "FileDescription", "libmodbus DLL\x0" 26 | #if defined(__MINGW32__) && !defined(__MINGW64__) 27 | VALUE "FileVersion", VERSTRING " (gcc)" 28 | #endif 29 | #if defined(__MINGW64__) 30 | VALUE "FileVersion", VERSTRING " (gcc64)" 31 | #endif 32 | #if defined(_MSC_VER) 33 | # if defined(MSC64) 34 | VALUE "FileVersion", VERSTRING " (cl64)" 35 | # else 36 | VALUE "FileVersion", VERSTRING " (cl)" 37 | # endif 38 | #endif 39 | VALUE "InternalName", "modbus.dll" 40 | VALUE "LegalCopyright", "?See libmodbus.org" 41 | VALUE "OriginalFilename", "modbus.dll" 42 | VALUE "ProductName", "libmodbus" 43 | } 44 | } 45 | BLOCK "VarFileInfo" 46 | { 47 | VALUE "Translation", 0x0, 1252 48 | } 49 | } 50 | 51 | // Manifest 52 | #if (_MSC_VER >= 1400) 53 | // CAVEAT: the manifest has a version string THAT MUST MATCH the DLL version 54 | CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "modbus.dll.manifest" 55 | #endif 56 | -------------------------------------------------------------------------------- /modbus/modbus.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {FB50B4A3-A44F-4741-AFBD-842E6520E044} 23 | Win32Proj 24 | modbus 25 | 10.0.22000.0 26 | 27 | 28 | 29 | DynamicLibrary 30 | true 31 | v140 32 | Unicode 33 | 34 | 35 | DynamicLibrary 36 | false 37 | v140 38 | true 39 | Unicode 40 | 41 | 42 | DynamicLibrary 43 | true 44 | v140 45 | Unicode 46 | 47 | 48 | DynamicLibrary 49 | false 50 | v140 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | 75 | 76 | true 77 | $(ProjectName)D 78 | $(SolutionDir)\bin\$(Platform)\ 79 | 80 | 81 | false 82 | 83 | 84 | false 85 | $(SolutionDir)\bin\$(Platform)\ 86 | 87 | 88 | 89 | 90 | 91 | Level3 92 | Disabled 93 | WIN32;_DEBUG;_WINDOWS;_USRDLL;MODBUS_EXPORTS;%(PreprocessorDefinitions) 94 | true 95 | 96 | 97 | Windows 98 | true 99 | 100 | 101 | 102 | 103 | 104 | 105 | Level3 106 | Disabled 107 | _DEBUG;_WINDOWS;_USRDLL;MODBUS_EXPORTS;_CRT_SECURE_NO_DEPRECATE=1;_CRT_NONSTDC_NO_DEPRECATE=1;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) 108 | true 109 | 110 | 111 | Windows 112 | true 113 | ws2_32.lib;%(AdditionalDependencies) 114 | 115 | 116 | 117 | 118 | Level3 119 | 120 | 121 | MaxSpeed 122 | true 123 | true 124 | WIN32;NDEBUG;_WINDOWS;_USRDLL;MODBUS_EXPORTS;%(PreprocessorDefinitions) 125 | true 126 | 127 | 128 | Windows 129 | true 130 | true 131 | true 132 | 133 | 134 | 135 | 136 | Level3 137 | 138 | 139 | MaxSpeed 140 | true 141 | true 142 | NDEBUG;_WINDOWS;_USRDLL;MODBUS_EXPORTS;_CRT_SECURE_NO_DEPRECATE=1;_CRT_NONSTDC_NO_DEPRECATE=1;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) 143 | true 144 | 145 | 146 | Windows 147 | true 148 | true 149 | true 150 | ws2_32.lib;%(AdditionalDependencies) 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /modbus/modbus.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 头文件 20 | 21 | 22 | 头文件 23 | 24 | 25 | 头文件 26 | 27 | 28 | 头文件 29 | 30 | 31 | 头文件 32 | 33 | 34 | 头文件 35 | 36 | 37 | 38 | 39 | 源文件 40 | 41 | 42 | 源文件 43 | 44 | 45 | 源文件 46 | 47 | 48 | 49 | 50 | 资源文件 51 | 52 | 53 | -------------------------------------------------------------------------------- /modbus/modbus.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | --------------------------------------------------------------------------------