This allows access to a local RS-485 serial port via the modbus plugin by specifying i.e.
Device "/dev/ttyUSB0" Baudrate 38400 in a <Host> block. For now it assumes 8N1; adding another config option to support other configurations could be done later. Lightly tested on my local setup. Signed-off-by: Eric Sandeen <[email protected]> --- (FWIW I tested the previous modbus/TCP patch here too, and it all works fine) V2: Rebase, update docs diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 1c5d110..283fc62 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -2880,8 +2880,8 @@ which the sizes of physical memory vary. =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 +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). @@ -2903,6 +2903,14 @@ B<Synopsis:> Instance "input-2" </Data> + <Data "supply-temperature-1"> + RegisterBase 0 + RegisterType Int16 + ModbusRegisterType holding + Type temperature + Instance "temp-1" + </Data> + <Host "modbus.example.com"> Address "192.168.0.42" Port "502" @@ -2915,6 +2923,17 @@ B<Synopsis:> </Slave> </Host> + <Host "localhost"> + Device "/dev/ttyUSB0" + Baudrate 38400 + Interval 20 + + <Slave 1> + Instance "temperature" + Collect "supply-temperature-1" + </Slave> + </Host> + =over 4 =item E<lt>B<Data> I<Name>E<gt> blocks @@ -2968,15 +2987,25 @@ Within E<lt>HostE<nbsp>/E<gt> blocks, the following options are allowed: =item B<Address> I<Hostname> -Specifies the node name (the actual network address) used to connect to the -host. This may be an IP address or a hostname. Please note that the used -I<libmodbus> library only supports IPv4 at the moment. +For Modbus/TCP, specifies the node name (the actual network address) used to +connect to the host. This may be an IP address or a hostname. Please note that +the used I<libmodbus> library only supports IPv4 at the moment. =item B<Port> I<Service> -Specifies the port used to connect to the host. The port can either be given as -a number or as a service name. Please note that the I<Service> argument must be -a string, even if ports are given in their numerical form. Defaults to "502". +for Modbus/TCP, specifies the port used to connect to the host. The port can +either be given as a number or as a service name. Please note that the +I<Service> argument must be a string, even if ports are given in their numerical +form. Defaults to "502". + +=item B<Device> I<Devicenode> + +For Modbus/RTU, specifies the path to the serial device being used. + +=item B<Baudrate> I<Baudrate> + +For Modbus/RTU, specifies the baud rate of the serial device. +Note, connections currently support only 8/N/1. =item B<Interval> I<Interval> @@ -2985,7 +3014,7 @@ host. By default the global B<Interval> setting will be used. =item E<lt>B<Slave> I<ID>E<gt> -Over each TCP connection, multiple Modbus devices may be reached. The slave ID +Over each connection, multiple Modbus devices may be reached. The slave ID is used to specify which device should be addressed. For each device you want to query, one B<Slave> block must be given. diff --git a/src/modbus.c b/src/modbus.c index 795465f..e4459c7 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -56,6 +56,10 @@ * <Host "name"> * Address "addr" * Port "1234" + * # Or: + * # Device "/dev/ttyUSB0" + * # Baudrate 38400 + * # (Assumes 8N1) * Interval 60 * * <Slave 1> @@ -84,6 +88,14 @@ enum mb_mreg_type_e /* {{{ */ typedef enum mb_register_type_e mb_register_type_t; typedef enum mb_mreg_type_e mb_mreg_type_t; +/* TCP or RTU depending on what is specified in host config block */ +enum mb_conntype_e /* {{{ */ +{ + MBCONN_TCP, + MBCONN_RTU +}; /* }}} */ +typedef enum mb_conntype_e mb_conntype_t; + struct mb_data_s; typedef struct mb_data_s mb_data_t; struct mb_data_s /* {{{ */ @@ -109,9 +121,11 @@ typedef struct mb_slave_s mb_slave_t; struct mb_host_s /* {{{ */ { char host[DATA_MAX_NAME_LEN]; - char node[NI_MAXHOST]; + char node[NI_MAXHOST]; /* TCP hostname or RTU serial device */ /* char service[NI_MAXSERV]; */ - int port; + int port; /* for Modbus/TCP */ + int baudrate; /* for Modbus/RTU */ + mb_conntype_t conntype; cdtime_t interval; mb_slave_t *slaves; @@ -301,21 +315,33 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */ /* We'll do the error handling ourselves. */ modbus_set_error_handling (&host->connection, NOP_ON_ERROR); - if ((host->port < 1) || (host->port > 65535)) - host->port = MODBUS_TCP_DEFAULT_PORT; + if (host->conntype == MBCONN_TCP) + { + if ((host->port < 1) || (host->port > 65535)) + host->port = MODBUS_TCP_DEFAULT_PORT; - DEBUG ("Modbus plugin: Trying to connect to \"%s\", port %i.", - host->node, host->port); + DEBUG ("Modbus plugin: Trying to connect to \"%s\", port %i.", + host->node, host->port); - modbus_init_tcp (&host->connection, - /* host = */ host->node, - /* port = */ host->port); + modbus_init_tcp (&host->connection, + /* host = */ host->node, + /* port = */ host->port); + } + else /* MBCONN_RTU */ + { + DEBUG ("Modbus plugin: Trying to connect to \"%s\".", host->node); + + modbus_init_rtu (&host->connection, + /* device = */ host->node, + /* baudrate = */ host->baudrate, + 'N', 8, 1, 0); + } status = modbus_connect (&host->connection); if (status != 0) { ERROR ("Modbus plugin: modbus_connect (%s, %i) failed with status %i.", - host->node, host->port, status); + host->node, host->port ? host->port : host->baudrate, status); return (status); } @@ -336,17 +362,32 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */ if (host->connection != NULL) return (0); - if ((host->port < 1) || (host->port > 65535)) - host->port = MODBUS_TCP_DEFAULT_PORT; + if (host->conntype == MBCONN_TCP) + { + if ((host->port < 1) || (host->port > 65535)) + host->port = MODBUS_TCP_DEFAULT_PORT; - DEBUG ("Modbus plugin: Trying to connect to \"%s\", port %i.", - host->node, host->port); + DEBUG ("Modbus plugin: Trying to connect to \"%s\", port %i.", + host->node, host->port); - host->connection = modbus_new_tcp (host->node, host->port); - if (host->connection == NULL) + host->connection = modbus_new_tcp (host->node, host->port); + if (host->connection == NULL) + { + ERROR ("Modbus plugin: Creating new Modbus/TCP object failed."); + return (-1); + } + } + else { - ERROR ("Modbus plugin: Creating new Modbus/TCP object failed."); - return (-1); + DEBUG ("Modbus plugin: Trying to connect to \"%s\", baudrate %i.", + host->node, host->baudrate); + + host->connection = modbus_new_rtu (host->node, host->baudrate, 'N', 8, 1); + if (host->connection == NULL) + { + ERROR ("Modbus plugin: Creating new Modbus/RTU object failed."); + return (-1); + } } modbus_set_debug (host->connection, 1); @@ -358,7 +399,7 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */ if (status != 0) { ERROR ("Modbus plugin: modbus_connect (%s, %i) failed with status %i.", - host->node, host->port, status); + host->node, host->port ? host->port : host->baudrate, status); modbus_free (host->connection); host->connection = NULL; return (status); @@ -427,7 +468,7 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */ { status = EBADF; } - else + else if (host->conntype == MBCONN_TCP) { struct sockaddr sockaddr; socklen_t saddrlen = sizeof (sockaddr); @@ -919,6 +960,8 @@ static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */ status = cf_util_get_string_buffer (child, buffer, sizeof (buffer)); if (status == 0) status = mb_config_set_host_address (host, buffer); + if (status == 0) + host->conntype = MBCONN_TCP; } else if (strcasecmp ("Port", child->key) == 0) { @@ -926,6 +969,14 @@ static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */ if (host->port <= 0) status = -1; } + else if (strcasecmp ("Device", child->key) == 0) + { + status = cf_util_get_string_buffer (child, host->node, sizeof (host->node)); + if (status == 0) + host->conntype = MBCONN_RTU; + } + else if (strcasecmp ("Baudrate", child->key) == 0) + status = cf_util_get_int(child, &host->baudrate); else if (strcasecmp ("Interval", child->key) == 0) status = cf_util_get_cdtime (child, &host->interval); else if (strcasecmp ("Slave", child->key) == 0) @@ -942,9 +993,22 @@ static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */ } /* for (i = 0; i < ci->children_num; i++) */ assert (host->host[0] != 0); - if (host->host[0] == 0) + if (host->node[0] == 0) { - ERROR ("Modbus plugin: Data block \"%s\": No type has been specified.", + ERROR ("Modbus plugin: Data block \"%s\": No address or device has been specified.", + host->host); + status = -1; + } + if (host->conntype == MBCONN_RTU && !host->baudrate) + { + ERROR ("Modbus plugin: Data block \"%s\": No serial baudrate has been specified.", + host->host); + status = -1; + } + if ((host->conntype == MBCONN_TCP && host->baudrate) || + (host->conntype == MBCONN_RTU && host->port)) + { + ERROR ("Modbus plugin: Data block \"%s\": You've mixed up RTU and TCP options.", host->host); status = -1; } _______________________________________________ collectd mailing list [email protected] http://mailman.verplant.org/listinfo/collectd
