Subversion Repositories modbus_client.modbus_client

Rev

Blame | Last modification | View Log | Download | RSS feed

  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. }
  274.