Subversion Repositories modbus_client.modbus_client

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
// Modbus TCP client by Pierre-Marie Baty <pm@pmbaty.com>
2
 
3
#include <stdio.h>
4
#include <stdlib.h>
5
#include <stdint.h>
6
#include <limits.h>
7
#include <string.h>
8
#include <errno.h>
9
#include <time.h>
10
#ifdef _WIN32 // POSIX shims for that Microsoft joke pretending to be an OS
11
typedef ptrdiff_t ssize_t;
12
static const char *basename (const char *pathname)
13
{
14
        for (const char *p = pathname + strlen (pathname); p != pathname; p--)
15
                if ((*p == '\\') || (*p == '/'))
16
                        return (p + 1);
17
        return (pathname);
18
}
19
#define fputc SetConsoleOutputCP(CP_UTF8), fputc
20
#define fputs SetConsoleOutputCP(CP_UTF8), fputs
21
#define printf SetConsoleOutputCP(CP_UTF8), printf
22
#define fprintf SetConsoleOutputCP(CP_UTF8), fprintf
23
#include <winsock2.h>
24
static int _socket (int domain, int type, int protocol)
25
{
26
        static WSADATA wsa_data = { 0 };
27
        if (wsa_data.wVersion == 0)
28
                WSAStartup (0x0202, &wsa_data);
29
        return (socket (domain, type, protocol));
30
}
31
#define socket(domain,type,protocol) _socket ((domain), (type), (protocol))
32
#ifdef _MSC_VER
33
#pragma comment(lib,"ws2_32.lib")
34
#endif // _MSC_VER
35
#else // !_WIN32
36
#include <libgen.h>
37
#include <sys/socket.h>
38
#include <netinet/in.h>
39
#include <arpa/inet.h>
40
#endif // _WIN32
41
 
42
 
43
int main (int argc, char **argv)
44
{
45
        const char *modbus_function_strings[] =
46
        {
47
                "", // not defined
48
                "Read Coils",
49
                "Read Discrete Inputs",
50
                "Read Multiple Holding Registers",
51
                "Read Input Registers",
52
                "Write Single Coil",
53
                "Write Single Holding Register",
54
                "Read Exception Status [serial only]", // serial only
55
                "Diagnostic [serial only]", // serial only
56
                "", // not defined
57
                "", // not defined
58
                "Get Com Event Counter [serial only]", // serial only
59
                "Get Com Event Log [serial only]", // serial only
60
                "", // not defined
61
                "", // not defined
62
                "Write Multiple Coils",
63
                "Write Multiple Holding Registers",
64
        };
65
        const char *modbus_exception_strings[] =
66
        {
67
                "undefined exception", // not defined
68
                "Illegal Function",
69
                "Illegal Data Address",
70
                "Illegal Data Value",
71
                "Server Device Failure",
72
                "Acknowledge",
73
                "Server Device Busy",
74
                "Negative Acknowledge",
75
                "Memory Parity Error",
76
                "undefined exception", // not defined
77
                "Gateway Path Unavailable",
78
                "Gateway Target Device Failed to Respond",
79
        };
80
 
81
        struct sockaddr_in server = { 0 };
82
        unsigned int address_bytes[4] = { 0 };
83
        unsigned int port = 502;
84
#ifdef _MSC_VER
85
#pragma pack(push,1)
86
#define __attribute__(...)
87
#endif // _MSC_VER
88
        struct __attribute__((packed))
89
        {
90
                struct __attribute__((packed))
91
                {
92
                        uint16_t message_id; // big endian
93
                        uint16_t protocol_id; // big endian
94
                        uint16_t payload_len; // big endian
95
                        uint8_t unit_id;
96
                } header;
97
                union
98
                {
99
                        struct __attribute__((packed))
100
                        {
101
                                uint8_t function_code;
102
                                uint16_t start_address; // big endian
103
                                uint16_t count_or_val; // big endian
104
                                uint8_t byte_len; // optional
105
                                uint8_t bytes[UINT8_MAX]; // optional
106
                        } as_query;
107
                        struct __attribute__((packed))
108
                        {
109
                                uint8_t function_code;
110
                                union
111
                                {
112
                                        struct __attribute__((packed))
113
                                        {
114
                                                uint8_t byte_len;
115
                                                uint8_t bytes[UINT8_MAX];
116
                                        } as_bytelen_plus_data; // function codes 1-4
117
                                        struct __attribute__((packed))
118
                                        {
119
                                                uint16_t start_address;
120
                                                uint16_t count_or_val;
121
                                        } as_address_plus_value; // function codes 5-6
122
                                        struct __attribute__((packed))
123
                                        {
124
                                                uint8_t bytes[0xffff];
125
                                        } as_data; // unknown function codes
126
                                } u;
127
                        } as_reply;
128
                        struct __attribute__((packed))
129
                        {
130
                                uint8_t function_code;
131
                                uint8_t exception_code;
132
                        } as_error;
133
                } u;
134
        } modbus_message = { 0 };
135
#ifdef _MSC_VER
136
#undef __attribute__
137
#pragma pack(pop)
138
#endif // _MSC_VER
139
        ssize_t message_len;
140
        size_t array_index;
141
        int client_socket;
142
 
143
        if (argc < 6)
144
        {
145
                fputs ("usage:\n", stderr);
146
                fprintf (stderr, "   %s <ip address[:port]> <unit ID> <function code> <start address> <count|value> [<hexbyte> [...]]\n", basename (argv[0]));
147
                fputs ("Modbus function codes:\n", stderr);
148
                for (array_index = 0; array_index < sizeof (modbus_function_strings) / sizeof (modbus_function_strings[0]); array_index++)
149
                        if ((modbus_function_strings[array_index][0] != 0) && (strstr (modbus_function_strings[array_index], " [serial only]") == NULL))
150
                                fprintf (stderr, "   %zd\t%s\n", array_index, modbus_function_strings[array_index]);
151
                exit (1);
152
        }
153
 
154
        if ((sscanf (argv[1], "%u.%u.%u.%u:%u", &address_bytes[0], &address_bytes[1], &address_bytes[2], &address_bytes[3], &port) < 4)
155
            || ((address_bytes[0] | address_bytes[1] | address_bytes[2] | address_bytes[3]) > 255))
156
        {
157
                fprintf (stderr, "error: invalid IPv4 address\n");
158
                exit (1);
159
        }
160
        server.sin_family = AF_INET;
161
        server.sin_addr.s_addr = htonl ((address_bytes[0] << 24) | (address_bytes[1] << 16) | (address_bytes[2] << 8) | (address_bytes[3] << 0));
162
        server.sin_port = htons ((uint16_t) port);
163
 
164
        client_socket = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
165
        if (client_socket == -1)
166
        {
167
                fprintf (stderr, "error: socket() call failed with errno %d: %s\n", errno, strerror (errno));
168
                exit (1);
169
        }
170
        else if (connect (client_socket, (struct sockaddr *) &server, sizeof (server)) == -1)
171
        {
172
                fprintf (stderr, "error: connect() call failed with errno %d: %s\n", errno, strerror (errno));
173
                exit (1);
174
        }
175
        printf ("Connected to %u.%u.%u.%u on port %u\n", address_bytes[0], address_bytes[1], address_bytes[2], address_bytes[3], port);
176
 
177
        srand (time (NULL));
178
        modbus_message.header.message_id  = ntohs ((uint16_t) rand ());
179
        modbus_message.header.protocol_id = ntohs (0);
180
        modbus_message.header.unit_id     = (uint8_t) strtol (argv[2], NULL, 0);
181
        modbus_message.u.as_query.function_code = (uint8_t) strtol (argv[3], NULL, 0);
182
        modbus_message.u.as_query.start_address = ntohs ((uint16_t) strtol (argv[4], NULL, 0));
183
        modbus_message.u.as_query.count_or_val  = ntohs ((uint16_t) strtol (argv[5], NULL, 0));
184
        modbus_message.u.as_query.byte_len = 0;
185
        for (array_index = 6; array_index < argc; array_index++)
186
                modbus_message.u.as_query.bytes[modbus_message.u.as_query.byte_len++] = (uint8_t) strtol (argv[array_index], NULL, 0);
187
 
188
        message_len = sizeof (modbus_message.header) + sizeof (modbus_message.u.as_query.function_code);
189
        if (strchr ("\x01\x02\x03\x04\x05\x06", (char) modbus_message.u.as_query.function_code) != NULL)
190
                message_len += sizeof (modbus_message.u.as_query.start_address) + sizeof (modbus_message.u.as_query.count_or_val);
191
        else if (modbus_message.u.as_query.byte_len > 0)
192
                message_len += sizeof (modbus_message.u.as_query.start_address) + sizeof (modbus_message.u.as_query.count_or_val) + sizeof (modbus_message.u.as_query.byte_len) + modbus_message.u.as_query.byte_len;
193
        modbus_message.header.payload_len = ntohs (message_len - ((size_t) &modbus_message.header.unit_id - (size_t) &modbus_message));
194
 
195
        fputs ("Modbus query:", stdout);
196
        for (array_index = 0; array_index < message_len; array_index++)
197
            printf (" %02x", ((uint8_t *) &modbus_message)[array_index]);
198
        fputc ('\n', stdout);
199
        fputs  ("├ MBAP header\n", stdout);
200
        printf ("│ ├ Message ID:     %d\n", ntohs (modbus_message.header.message_id));
201
        printf ("│ ├ Protocol ID:    %d\n", ntohs (modbus_message.header.protocol_id));
202
        printf ("│ ├ Payload length: %d bytes\n", ntohs (modbus_message.header.payload_len));
203
        printf ("│ └ Unit ID:        %d\n",        modbus_message.header.unit_id);
204
        fputs  ("└ Payload\n", stdout);
205
        if (message_len == sizeof (modbus_message.header) + sizeof (modbus_message.u.as_query.function_code))
206
                printf ("  └ Function code:  %d (%s)\n", modbus_message.u.as_query.function_code, (modbus_message.u.as_query.function_code < sizeof (modbus_function_strings) / sizeof (modbus_function_strings[0]) ? modbus_function_strings[modbus_message.u.as_query.function_code] : "unknown function code"));
207
        else
208
        {
209
                printf ("  ├ Function code:  %d (%s)\n", modbus_message.u.as_query.function_code, (modbus_message.u.as_query.function_code < sizeof (modbus_function_strings) / sizeof (modbus_function_strings[0]) ? modbus_function_strings[modbus_message.u.as_query.function_code] : "unknown function code"));
210
                printf ("  ├ Start address:  %d\n", ntohs (modbus_message.u.as_query.start_address));
211
                if (message_len == sizeof (modbus_message.header) + sizeof (modbus_message.u.as_query.function_code) + sizeof (modbus_message.u.as_query.start_address) + sizeof (modbus_message.u.as_query.count_or_val))
212
                        printf ("  └ Count / value:  %d\n", ntohs (modbus_message.u.as_query.count_or_val));
213
                else
214
                {
215
                        printf ("  ├ Count / value:  %d\n", ntohs (modbus_message.u.as_query.count_or_val));
216
                        printf ("  ├ Data length:    %d bytes\n", modbus_message.u.as_query.byte_len);
217
                        fputs  ("  └ Data:          ", stdout);
218
                        for (array_index = 0; array_index < modbus_message.u.as_query.byte_len; array_index++)
219
                            printf (" %02x", modbus_message.u.as_query.bytes[array_index]);
220
                        fputc ('\n', stdout);
221
                }
222
        }
223
 
224
        if (send (client_socket, (void *) &modbus_message, message_len, 0) != message_len)
225
        {
226
                fprintf (stderr, "error: send() call failed with errno %d: %s\n", errno, strerror (errno));
227
                exit (1);
228
        }
229
 
230
        message_len = recv (client_socket, (void *) &modbus_message, sizeof (modbus_message), 0);
231
        if (message_len <= 0)
232
        {
233
                fprintf (stderr, "error: recv() call failed with return value %zd and errno %d: %s\n", message_len, errno, strerror (errno));
234
                exit (1);
235
        }
236
 
237
        fputs ("Modbus reply:", stdout);
238
        for (array_index = 0; array_index < message_len; array_index++)
239
            printf (" %02x", ((uint8_t *) &modbus_message)[array_index]);
240
        fputc ('\n', stdout);
241
        printf ("├ MBAP header\n");
242
        printf ("│ ├ Message ID:     %d\n", ntohs (modbus_message.header.message_id));
243
        printf ("│ ├ Protocol ID:    %d\n", ntohs (modbus_message.header.protocol_id));
244
        printf ("│ ├ Payload length: %d bytes\n", ntohs (modbus_message.header.payload_len));
245
        printf ("│ └ Unit ID:        %d\n",        modbus_message.header.unit_id);
246
        printf ("└ Payload\n");
247
                printf ("  ├ Function code:  %d (%s)\n", modbus_message.u.as_reply.function_code & 0x7f, (modbus_message.u.as_reply.function_code & 0x80 ? "error bit set" : "ok"));
248
        if (modbus_message.u.as_reply.function_code & 0x80)
249
                printf ("  └ Exception code: %d (%s)\n", modbus_message.u.as_error.exception_code, (modbus_message.u.as_error.exception_code < sizeof (modbus_exception_strings) / sizeof (modbus_exception_strings[0]) ? modbus_exception_strings[modbus_message.u.as_error.exception_code] : "unknown exception"));
250
        else if ((modbus_message.u.as_reply.function_code >= 1) && (modbus_message.u.as_reply.function_code <= 4))
251
        {
252
                printf ("  ├ Data length:    %d bytes\n", modbus_message.u.as_reply.u.as_bytelen_plus_data.byte_len);
253
                fputs  ("  └ Data:          ", stdout);
254
                for (array_index = 0; array_index < modbus_message.u.as_reply.u.as_bytelen_plus_data.byte_len; array_index++)
255
                    printf (" %02x", modbus_message.u.as_reply.u.as_bytelen_plus_data.bytes[array_index]);
256
                fputc ('\n', stdout);
257
        }
258
        else if (   ((modbus_message.u.as_reply.function_code >=  4) && (modbus_message.u.as_reply.function_code <=  5))
259
                 || ((modbus_message.u.as_reply.function_code >= 15) && (modbus_message.u.as_reply.function_code <= 16)))
260
        {
261
                printf ("  ├ Start address:  %d\n", modbus_message.u.as_reply.u.as_address_plus_value.start_address);
262
                printf ("  └ Count / value:  %d\n", modbus_message.u.as_reply.u.as_address_plus_value.count_or_val);
263
        }
264
        else
265
        {
266
                fputs  ("  └ Data:          ", stdout);
267
                for (array_index = 0; array_index < message_len - ((size_t) modbus_message.u.as_reply.u.as_data.bytes - (size_t) &modbus_message); array_index++)
268
                    printf (" %02x", modbus_message.u.as_reply.u.as_data.bytes[array_index]);
269
                fputc ('\n', stdout);
270
        }
271
 
272
        exit (modbus_message.u.as_reply.function_code & 0x80 ? 1 : 0);
273
}