[collectd] [PATCH] support for Modbus/RTU

Daniel Golle daniel.golle at gmail.com
Mon Mar 28 20:20:59 CEST 2011


This patch adds Modbus/RTU support to the modbus plugin of collectd 4.10.3.
It's a quick hack and didn't receive any testing yet, but please please review and let me know what you think and help me.

Signed-off-by: Daniel Golle <daniel.golle at gmail.com>

--- a/src/modbus.c	2011-03-28 20:01:58.000000000 +0200
+++ b/src/modbus.c	2011-03-28 20:03:45.000000000 +0200
@@ -91,6 +91,16 @@
 struct mb_host_s /* {{{ */
 {
   char host[DATA_MAX_NAME_LEN];
+  type_com_t type_com;
+#ifdef __APPLE_CC__
+  char device[64];
+#else
+  char device[16];
+#endif
+  int baud;
+  int data_bits;
+  int stop_bits;
+  char parity[5];
   char node[NI_MAXHOST];
   /* char service[NI_MAXSERV]; */
   int port;
@@ -265,7 +275,7 @@
   return (conv.f);
 } /* }}} float mb_register_to_float */
 
-static int mb_init_connection (mb_host_t *host) /* {{{ */
+static inline int mb_init_connection_tcp (mb_host_t *host) /* {{{ */
 {
   int status;
 
@@ -305,6 +315,69 @@
   host->is_connected = 1;
   host->have_reconnected = 1;
   return (0);
+} /* }}} int mb_init_connection_tcp */
+
+static inline int mb_init_connection_rtu (mb_host_t *host) /* {{{ */
+{
+  int status;
+
+  if (host == NULL)
+    return (EINVAL);
+
+  if (host->is_connected)
+    return (0);
+
+  /* Only reconnect once per interval. */
+  if (host->have_reconnected)
+    return (-1);
+
+  modbus_set_debug (&host->connection, 1);
+
+  /* We'll do the error handling ourselves. */
+  modbus_set_error_handling (&host->connection, NOP_ON_ERROR);
+
+  if (host->baud < 300)
+    host->baud = 9600;
+
+  if ((host->data_bits < 7) || (host->data_bits > 16))
+    host->data_bits = 8;
+
+  if ((host->stop_bits < 1) || (host->stop_bits > 16))
+    host->stop_bits = 1;
+
+  if ( ! (strcmp(host->parity, "even") || strcmp(host->parity, "odd") || strcmp(host->parity, "none")))
+    memcpy(host->parity, "none", sizeof("none"));
+
+  DEBUG ("Modbus plugin: Trying to open \"%s\".",
+      host->device);
+
+  modbus_init_rtu (&host->connection,
+      /* device = */ host->device,
+      /* baud = */ host->baud,
+      /* parity = */ host->parity,
+      /* data_bit = */ (uint8_t)host->data_bits,
+      /* stop_bit = */ (uint8_t)host->stop_bits);
+
+  status = modbus_connect (&host->connection);
+  if (status != 0)
+  {
+    ERROR ("Modbus plugin: modbus_connect (%s, %i baud, %i %s %i) failed with status %i.",
+        host->device, host->baud, host->data_bits, host->parity, host->stop_bits, status);
+    return (status);
+  }
+
+  host->is_connected = 1;
+  host->have_reconnected = 1;
+  return (0);
+} /* }}} int mb_init_connection_rtu */
+
+static int mb_init_connection (mb_host_t *host) /* {{{ */
+{
+    if ( host->type_com == TCP )
+	return mb_init_connection_tcp(host);
+    else if ( host->type_com == RTU )
+	return mb_init_connection_rtu(host);
+    else return -1;
 } /* }}} int mb_init_connection */
 
 #define CAST_TO_VALUE_T(ds,vt,raw) do { \
@@ -748,6 +821,7 @@
   if (host->host[0] == 0)
     return (EINVAL);
 
+  host->type_com = TCP;
   for (i = 0; i < ci->children_num; i++)
   {
     oconfig_item_t *child = ci->children + i;
@@ -817,6 +891,114 @@
   return (status);
 } /* }}} int mb_config_add_host */
 
+static int mb_config_add_device (oconfig_item_t *ci) /* {{{ */
+{
+  mb_host_t *host;
+  int status;
+  int i;
+
+  host = malloc (sizeof (*host));
+  if (host == NULL)
+    return (ENOMEM);
+  memset (host, 0, sizeof (*host));
+  host->slaves = NULL;
+
+  status = cf_util_get_string_buffer (ci, host->host, sizeof (host->host));
+  if (status != 0)
+    return (status);
+  if (host->host[0] == 0)
+    return (EINVAL);
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+    status = 0;
+
+    if (strcasecmp ("Device", child->key) == 0)
+    {
+      status = cf_util_get_string_buffer (child, host->device, sizeof (host->device));
+    if (status != 0)
+      return (status);
+    if (host->device[0] == 0)
+      return (EINVAL);
+    }
+    else if (strcasecmp ("Baud", child->key) == 0)
+    {
+      status = cf_util_get_int (child, &host->baud);
+      if (host->baud <= 0)
+        status = -1;
+    }
+    else if (strcasecmp ("DataBits", child->key) == 0)
+    {
+      status = cf_util_get_int (child, &host->data_bits);
+      if (host->data_bits < 7)
+        status = -1;
+    }
+    else if (strcasecmp ("StopBits", child->key) == 0)
+    {
+      status = cf_util_get_int (child, &host->stop_bits);
+      if (host->stop_bits <= 0)
+        status = -1;
+    }
+    else if (strcasecmp ("Parity", child->key) == 0)
+    {
+      status = cf_util_get_string_buffer (child, host->parity, sizeof (host->parity));
+    if (status != 0)
+      return (status);
+    if (host->parity[0] == 0)
+      return (EINVAL);
+
+    }
+    else if (strcasecmp ("Interval", child->key) == 0)
+      status = cf_util_get_int (child, &host->interval);
+    else if (strcasecmp ("Slave", child->key) == 0)
+      /* Don't set status: Gracefully continue if a slave fails. */
+      mb_config_add_slave (host, child);
+    else
+    {
+      ERROR ("Modbus plugin: Unknown configuration option: %s", child->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  } /* for (i = 0; i < ci->children_num; i++) */
+
+  assert (host->host[0] != 0);
+  if (host->host[0] == 0)
+  {
+    ERROR ("Modbus plugin: Data block \"%s\": No type has been specified.",
+        host->host);
+    status = -1;
+  }
+
+  if (status == 0)
+  {
+    user_data_t ud;
+    char name[1024];
+    struct timespec interval;
+
+    ud.data = host;
+    ud.free_func = host_free;
+
+    ssnprintf (name, sizeof (name), "modbus-%s", host->host);
+
+    interval.tv_nsec = 0;
+    if (host->interval > 0)
+      interval.tv_sec = host->interval;
+    else
+      interval.tv_sec = 0;
+
+    plugin_register_complex_read (/* group = */ NULL, name,
+        mb_read, (interval.tv_sec > 0) ? &interval : NULL, &ud);
+  }
+  else
+  {
+    host_free (host);
+  }
+
+  return (status);
+} /* }}} int mb_config_add_device */
+
 static int mb_config (oconfig_item_t *ci) /* {{{ */
 {
   int i;
@@ -832,6 +1014,8 @@
       mb_config_add_data (child);
     else if (strcasecmp ("Host", child->key) == 0)
       mb_config_add_host (child);
+    else if (strcasecmp ("Device", child->key) == 0)
+      mb_config_add_device (child);
     else
       ERROR ("Modbus plugin: Unknown configuration option: %s", child->key);
   }
--- a/src/collectd.conf.in	2011-03-28 20:05:14.000000000 +0200
+++ b/src/collectd.conf.in	2011-03-28 20:12:56.000000000 +0200
@@ -445,6 +445,19 @@
 #			Collect "data_name"
 #		</Slave>
 #	</Host>
+#	<Device "name">
+#		Device "/dev/serial"
+#		Baud 9600
+#		DataBits 8
+#		StopBits 1
+#		Parity "none"
+#		Interval 60
+#
+#		<Slave 1>
+#			Instance "foobar" # optional
+#			Collect "data_name"
+#		</Slave>
+#	</Host>
 #</Plugin>
 
 #<Plugin mysql>
--- a/src/collectd.conf.pod	2011-03-26 18:08:53.000000000 +0200
+++ b/src/collectd.conf.pod	2011-03-28 20:10:59.000000000 +0200
@@ -1731,10 +1731,11 @@
 
 =head2 Plugin C<modbus>
 
-The B<modbus plugin> connects to a Modbus "slave" via Modbus/TCP and reads
-register values. It supports reading single registers (unsigned 16E<nbsp>bit
-values), large integer values (unsigned 32E<nbsp>bit values) and floating point
-values (two registers interpreted as IEEE floats in big endian notation).
+The B<modbus plugin> connects to a Modbus "slave" via Modbus/TCP or Modbus/RTU
+and reads register values.
+It supports reading single registers (unsigned 16E<nbsp>bit values), 
+large integer values (unsigned 32E<nbsp>bit values) and floating point values 
+(two registers interpreted as IEEE floats in big endian notation).
 
 Synopsis:
 
@@ -1758,6 +1759,21 @@
    Interval 60
    
    <Slave 1>
+     Instance "power-supply"
+     Collect  "voltage-input-1"
+     Collect  "voltage-input-2"
+   </Slave>
+ </Host>
+
+ <Device "SerialDevice1">
+   Device "/dev/ttyS1"
+   Baud 9600
+   DataBits 8
+   StopBits 1
+   Parity "none"
+   Interval 60
+   
+   <Slave 1>
      Instance "power-supply"
      Collect  "voltage-input-1"
      Collect  "voltage-input-2"



More information about the collectd mailing list