The C<onewire> plugin uses the B<owcapi> library from the B<owfs> project
L<http://owfs.org/> to read sensors connected via the onewire bus.
-Currently only temperature sensors (sensors with the family code C<10>,
-e.E<nbsp>g. DS1820, DS18S20, DS1920) can be read. If you have other sensors you
-would like to have included, please send a sort request to the mailing list.
+It can be used in two possible modes - standard or advanced.
+
+In the standard mode only temperature sensors (sensors with the family code
+C<10>, C<22> and C<28> - e.g. DS1820, DS18S20, DS1920) can be read. If you have
+other sensors you would like to have included, please send a sort request to
+the mailing list. You can select sensors to be read or to be ignored depending
+on the option B<IgnoreSelected>). When no list is provided the whole bus is
+walked and all sensors are read.
Hubs (the DS2409 chips) are working, but read the note, why this plugin is
experimental, below.
+In the advanced mode you can configure any sensor to be read (only numerical
+value) using full OWFS path (e.g. "/uncached/10.F10FCA000800/temperature").
+In this mode you have to list all the sensors. Neither default bus walk nor
+B<IgnoreSelected> are used here. Address and type (file) is extracted from
+the path automatically and should produce compatible structure with the "standard"
+mode (basically the path is expected as for example
+"/uncached/10.F10FCA000800/temperature" where it would extract address part
+"F10FCA000800" and the rest after the slash is considered the type - here
+"temperature").
+There are two advantages to this mode - you can access virtually any sensor
+(not just temperature), select whether to use cached or directly read values
+and it is slighlty faster. The downside is more complex configuration.
+
+The two modes are distinguished automatically by the format of the address.
+It is not possible to mix the two modes. Once a full path is detected in any
+B<Sensor> then the whole addressing (all sensors) is considered to be this way
+(and as standard addresses will fail parsing they will be ignored).
+
=over 4
=item B<Device> I<Device>
=item B<Sensor> I<Sensor>
-Selects sensors to collect or to ignore, depending on B<IgnoreSelected>, see
-below. Sensors are specified without the family byte at the beginning, to you'd
-use C<F10FCA000800>, and B<not> include the leading C<10.> family byte and
-point.
+In the standard mode selects sensors to collect or to ignore
+(depending on B<IgnoreSelected>, see below). Sensors are specified without
+the family byte at the beginning, so you have to use for example C<F10FCA000800>,
+and B<not> include the leading C<10.> family byte and point.
+When no B<Sensor> is configured the whole Onewire bus is walked and all supported
+sensors (see above) are read.
+
+In the advanced mode the B<Sensor> specifies full OWFS path - e.g.
+C</uncached/10.F10FCA000800/temperature> (or when cached values are OK
+C</10.F10FCA000800/temperature>). B<IgnoreSelected> is not used.
+
+As there can be multiple devices on the bus you can list multiple sensor (use
+multiple B<Sensor> elements).
=item B<IgnoreSelected> I<true>|I<false>
-If no configuration if given, the B<onewire> plugin will collect data from all
+If no configuration is given, the B<onewire> plugin will collect data from all
sensors found. This may not be practical, especially if sensors are added and
removed regularly. Sometimes, however, it's easier/preferred to collect only
specific sensors or all sensors I<except> a few specified ones. This option
B<Sensor> is inverted: All selected interfaces are ignored and all other
interfaces are collected.
+Used only in the standard mode - see above.
+
=item B<Interval> I<Seconds>
Sets the interval in which all sensors should be read. If not specified, the
#include "plugin.h"
#include "utils_ignorelist.h"
+#include <sys/time.h>
+#include <sys/types.h>
+#include <regex.h>
#include <owcapi.h>
#define OW_FAMILY_LENGTH 8
};
typedef struct ow_family_features_s ow_family_features_t;
+/* internal timing info collected in debug version only */
+#if COLLECT_DEBUG
+static struct timeval tv_begin, tv_end, tv_diff;
+#endif /* COLLECT_DEBUG */
+
+/* regexp to extract address (without family) and file from the owfs path */
+static const char *regexp_to_match = "[A-Fa-f0-9]{2}\\.([A-Fa-f0-9]{12})/([[:alnum:]]+)$";
+
/* see http://owfs.sourceforge.net/ow_table.html for a list of families */
static ow_family_features_t ow_family_features[] =
{
static char *device_g = NULL;
static cdtime_t ow_interval = 0;
+static _Bool direct_access = 0;
static const char *config_keys[] =
{
static ignorelist_t *sensor_list;
+static _Bool regex_direct_initialized = 0;
+static regex_t regex_direct;
+
+/**
+ * List of onewire owfs "files" to be directly read
+ */
+typedef struct direct_access_element_s
+{
+ char *path; /**< The whole owfs path */
+ char *address; /**< 1-wire address without family */
+ char *file; /**< owfs file - e.g. temperature */
+ struct direct_access_element_s *next; /**< Next in the list */
+} direct_access_element_t;
+
+static direct_access_element_t * direct_list = NULL;
+
+/* =================================================================================== */
+
+#if COLLECT_DEBUG
+/* Return 1 if the difference is negative, otherwise 0. */
+static int timeval_subtract(struct timeval *result, struct timeval *t2, struct timeval *t1)
+{
+ long int diff = (t2->tv_usec + 1000000 * t2->tv_sec) - (t1->tv_usec + 1000000 * t1->tv_sec);
+ result->tv_sec = diff / 1000000;
+ result->tv_usec = diff % 1000000;
+
+ return (diff<0);
+}
+#endif /* COLLECT_DEBUG */
+
+/* =================================================================================== */
+
+static void direct_list_element_free(direct_access_element_t *el)
+{
+ if (el != NULL)
+ {
+ DEBUG ("onewire plugin: direct_list_element_free - deleting <%s>", el->path);
+ sfree (el->path);
+ sfree (el->address);
+ sfree (el->file);
+ free (el);
+ }
+}
+
+static int direct_list_insert(const char * config)
+{
+ regmatch_t pmatch[3];
+ size_t nmatch = 3;
+ direct_access_element_t *element = NULL;
+
+ DEBUG ("onewire plugin: direct_list_insert <%s>", config);
+
+ element = (direct_access_element_t *) malloc (sizeof(*element));
+ if (element == NULL)
+ {
+ ERROR ("onewire plugin: direct_list_insert - cannot allocate element");
+ return 1;
+ }
+ element->path = NULL;
+ element->address = NULL;
+ element->file = NULL;
+
+ element->path = strdup (config);
+ if (element->path == NULL)
+ {
+ ERROR ("onewire plugin: direct_list_insert - cannot allocate path");
+ direct_list_element_free (element);
+ return 1;
+ }
+
+ DEBUG ("onewire plugin: direct_list_insert - about to match %s", config);
+
+ if (!regex_direct_initialized)
+ {
+ if (regcomp (®ex_direct, regexp_to_match, REG_EXTENDED))
+ {
+ ERROR ("onewire plugin: Cannot compile regex");
+ direct_list_element_free (element);
+ return (1);
+ }
+ regex_direct_initialized = 1;
+ DEBUG ("onewire plugin: Compiled regex!!");
+ }
+
+ if (regexec (®ex_direct, config, nmatch, pmatch, 0))
+ {
+ ERROR ("onewire plugin: direct_list_insert - no regex match");
+ direct_list_element_free (element);
+ return 1;
+ }
+
+ if (pmatch[1].rm_so<0)
+ {
+ ERROR ("onewire plugin: direct_list_insert - no address regex match");
+ direct_list_element_free (element);
+ return 1;
+ }
+ element->address = strndup (config+pmatch[1].rm_so,
+ pmatch[1].rm_eo - pmatch[1].rm_so);
+ if (element->address == NULL)
+ {
+ ERROR ("onewire plugin: direct_list_insert - cannot allocate address");
+ direct_list_element_free (element);
+ return 1;
+ }
+ DEBUG ("onewire plugin: direct_list_insert - found address <%s>",
+ element->address);
+
+ if (pmatch[2].rm_so<0)
+ {
+ ERROR ("onewire plugin: direct_list_insert - no file regex match");
+ direct_list_element_free (element);
+ return 1;
+ }
+ element->file = strndup (config+pmatch[2].rm_so,
+ pmatch[2].rm_eo - pmatch[2].rm_so);
+ if (element->file == NULL)
+ {
+ ERROR ("onewire plugin: direct_list_insert - cannot allocate file");
+ direct_list_element_free (element);
+ return 1;
+ }
+ DEBUG ("onewire plugin: direct_list_insert - found file <%s>", element->file);
+
+ element->next = direct_list;
+ direct_list = element;
+
+ return 0;
+}
+
+static void direct_list_free(void)
+{
+ direct_access_element_t *traverse = direct_list;
+ direct_access_element_t *tmp = NULL;;
+
+ while(traverse != NULL)
+ {
+ tmp = traverse;
+ traverse = traverse->next;
+ direct_list_element_free (tmp);
+ tmp = NULL;
+ }
+}
+
+/* =================================================================================== */
+
static int cow_load_config (const char *key, const char *value)
{
if (sensor_list == NULL)
if (strcasecmp (key, "Sensor") == 0)
{
- if (ignorelist_add (sensor_list, value))
+ if (direct_list_insert (value))
{
- ERROR ("sensors plugin: "
- "Cannot add value to ignorelist.");
- return (1);
+ DEBUG ("onewire plugin: Cannot add %s to direct_list_insert.", value);
+
+ if (ignorelist_add (sensor_list, value))
+ {
+ ERROR ("onewire plugin: Cannot add value to ignorelist.");
+ return (1);
+ }
+ }
+ else
+ {
+ DEBUG ("onewire plugin: %s is a direct access", value);
+ direct_access = 1;
}
}
else if (strcasecmp (key, "IgnoreSelected") == 0)
buffer = NULL;
buffer_size = 0;
+ DEBUG ("Start reading onewire device %s", file);
status = OW_get (file, &buffer, &buffer_size);
if (status < 0)
{
path, family_info->features[i].filename, status);
return (-1);
}
+ DEBUG ("Read onewire device %s as %s", file, buffer);
endptr = NULL;
values[0].gauge = strtod (buffer, &endptr);
return (0);
} /* int cow_read_bus */
+
+/* =================================================================================== */
+
+static int cow_simple_read (void)
+{
+ value_t values[1];
+ value_list_t vl = VALUE_LIST_INIT;
+ char *buffer;
+ size_t buffer_size;
+ int status;
+ char *endptr;
+ direct_access_element_t *traverse;
+
+ /* traverse list and check entries */
+ for (traverse = direct_list; traverse != NULL; traverse = traverse->next)
+ {
+ vl.values = values;
+ vl.values_len = 1;
+
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "onewire", sizeof (vl.plugin));
+ sstrncpy (vl.plugin_instance, traverse->address, sizeof (vl.plugin_instance));
+
+ status = OW_get (traverse->path, &buffer, &buffer_size);
+ if (status < 0)
+ {
+ ERROR ("onewire plugin: OW_get (%s) failed. status = %#x;",
+ traverse->path,
+ status);
+ return (-1);
+ }
+ DEBUG ("onewire plugin: Read onewire device %s as %s", traverse->path, buffer);
+
+
+ endptr = NULL;
+ values[0].gauge = strtod (buffer, &endptr);
+ if (endptr == NULL)
+ {
+ ERROR ("onewire plugin: Buffer is not a number: %s", buffer);
+ status = -1;
+ continue;
+ }
+
+ sstrncpy (vl.type, traverse->file, sizeof (vl.type));
+ sstrncpy (vl.type_instance, "", sizeof (""));
+
+ plugin_dispatch_values (&vl);
+ free (buffer);
+ } /* for (traverse) */
+
+ return 0;
+} /* int cow_simple_read */
+
+/* =================================================================================== */
+
static int cow_read (user_data_t *ud __attribute__((unused)))
{
- return (cow_read_bus ("/"));
+ int result=0;
+
+#if COLLECT_DEBUG
+ gettimeofday (&tv_begin, NULL);
+#endif /* COLLECT_DEBUG */
+
+ if (direct_access)
+ {
+ DEBUG ("onewire plugin: Direct access read");
+ result = cow_simple_read ();
+ }
+ else
+ {
+ DEBUG ("onewire plugin: Standard access read");
+ result = cow_read_bus ("/");
+ }
+
+#if COLLECT_DEBUG
+ gettimeofday (&tv_end, NULL);
+ timeval_subtract (&tv_diff, &tv_end, &tv_begin);
+ DEBUG ("onewire plugin: Onewire read took us %ld.%06ld s",
+ tv_diff.tv_sec,
+ tv_diff.tv_usec);
+#endif /* COLLECT_DEBUG */
+
+ return result;
} /* int cow_read */
static int cow_shutdown (void)
{
- OW_finish ();
- ignorelist_free (sensor_list);
- return (0);
+ OW_finish ();
+ ignorelist_free (sensor_list);
+
+ direct_list_free ();
+
+ if (regex_direct_initialized)
+ {
+ regfree(®ex_direct);
+ }
+
+ return (0);
} /* int cow_shutdown */
static int cow_init (void)
return (-1);
}
+ DEBUG ("onewire plugin: about to init device <%s>.", device_g);
status = (int) OW_init (device_g);
if (status != 0)
{
{
plugin_register_init ("onewire", cow_init);
plugin_register_config ("onewire", cow_load_config,
- config_keys, config_keys_num);
+ config_keys, config_keys_num);
}
/* vim: set sw=2 sts=2 ts=8 et fdm=marker cindent : */