From 6028f89df95b12219d735b277863f83e9f5ee9e9 Mon Sep 17 00:00:00 2001
From: PJ Bostley <pbostley@gmail.com>
Date: Sat, 20 Jan 2018 16:39:36 -0700
Subject: [PATCH 1/2] Adding support for CDAB endian 32-bit modbus polls

---
 src/modbus.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 56 insertions(+), 5 deletions(-)

diff --git a/src/modbus.c b/src/modbus.c
index 31f0c2da81..daa3c028f5 100644
--- a/src/modbus.c
+++ b/src/modbus.c
@@ -76,9 +76,13 @@
 enum mb_register_type_e /* {{{ */
 { REG_TYPE_INT16,
   REG_TYPE_INT32,
+  REG_TYPE_INT32_CDAB,
   REG_TYPE_UINT16,
   REG_TYPE_UINT32,
-  REG_TYPE_FLOAT }; /* }}} */
+  REG_TYPE_UINT32_CDAB,
+  REG_TYPE_FLOAT,
+  REG_TYPE_FLOAT_CDAB }; /* }}} */
+
 enum mb_mreg_type_e /* {{{ */
 { MREG_HOLDING,
   MREG_INPUT }; /* }}} */
@@ -425,7 +429,9 @@ static int mb_read_data(mb_host_t *host, mb_slave_t *slave, /* {{{ */
 
   if ((ds->ds[0].type != DS_TYPE_GAUGE) &&
       (data->register_type != REG_TYPE_INT32) &&
-      (data->register_type != REG_TYPE_UINT32)) {
+      (data->register_type != REG_TYPE_INT32_CDAB) &&
+      (data->register_type != REG_TYPE_UINT32) &&
+      (data->register_type != REG_TYPE_UINT32_CDAB)) {
     NOTICE(
         "Modbus plugin: The data source of type \"%s\" is %s, not gauge. "
         "This will most likely result in problems, because the register type "
@@ -434,8 +440,11 @@ static int mb_read_data(mb_host_t *host, mb_slave_t *slave, /* {{{ */
   }
 
   if ((data->register_type == REG_TYPE_INT32) ||
+      (data->register_type == REG_TYPE_INT32_CDAB) ||
       (data->register_type == REG_TYPE_UINT32) ||
-      (data->register_type == REG_TYPE_FLOAT))
+      (data->register_type == REG_TYPE_UINT32_CDAB) ||
+      (data->register_type == REG_TYPE_FLOAT) ||
+      (data->register_type == REG_TYPE_FLOAT_CDAB))
     values_num = 2;
   else
     values_num = 1;
@@ -496,8 +505,8 @@ static int mb_read_data(mb_host_t *host, mb_slave_t *slave, /* {{{ */
   }
   if (status != values_num) {
     ERROR("Modbus plugin: modbus read function (%s/%s) failed. "
-          " status = %i, values_num = %i. Giving up.",
-          host->host, host->node, status, values_num);
+          " status = %i, start_addr = %i, values_num = %i. Giving up.",
+          host->host, host->node, status, data->register_base, values_num);
 #if LEGACY_LIBMODBUS
     modbus_close(&host->connection);
 #else
@@ -521,6 +530,17 @@ static int mb_read_data(mb_host_t *host, mb_slave_t *slave, /* {{{ */
           "Returned float value is %g",
           (double)float_value);
 
+    CAST_TO_VALUE_T(ds, vt, float_value);
+    mb_submit(host, slave, data, vt);
+  } else if (data->register_type == REG_TYPE_FLOAT_CDAB) {
+    float float_value;
+    value_t vt;
+
+    float_value = mb_register_to_float(values[1], values[0]);
+    DEBUG("Modbus plugin: mb_read_data: "
+          "Returned float value is %g",
+          (double)float_value);
+
     CAST_TO_VALUE_T(ds, vt, float_value);
     mb_submit(host, slave, data, vt);
   } else if (data->register_type == REG_TYPE_INT32) {
@@ -535,6 +555,20 @@ static int mb_read_data(mb_host_t *host, mb_slave_t *slave, /* {{{ */
           "Returned int32 value is %" PRIi32,
           v.i32);
 
+    CAST_TO_VALUE_T(ds, vt, v.i32);
+    mb_submit(host, slave, data, vt);
+  } else if (data->register_type == REG_TYPE_INT32_CDAB) {
+    union {
+      uint32_t u32;
+      int32_t i32;
+    } v;
+    value_t vt;
+
+    v.u32 = (((uint32_t)values[1]) << 16) | ((uint32_t)values[0]);
+    DEBUG("Modbus plugin: mb_read_data: "
+          "Returned int32 value is %" PRIi32,
+          v.i32);
+
     CAST_TO_VALUE_T(ds, vt, v.i32);
     mb_submit(host, slave, data, vt);
   } else if (data->register_type == REG_TYPE_INT16) {
@@ -561,6 +595,17 @@ static int mb_read_data(mb_host_t *host, mb_slave_t *slave, /* {{{ */
           "Returned uint32 value is %" PRIu32,
           v32);
 
+    CAST_TO_VALUE_T(ds, vt, v32);
+    mb_submit(host, slave, data, vt);
+  } else if (data->register_type == REG_TYPE_UINT32_CDAB) {
+    uint32_t v32;
+    value_t vt;
+
+    v32 = (((uint32_t)values[1]) << 16) | ((uint32_t)values[0]);
+    DEBUG("Modbus plugin: mb_read_data: "
+          "Returned uint32 value is %" PRIu32,
+          v32);
+
     CAST_TO_VALUE_T(ds, vt, v32);
     mb_submit(host, slave, data, vt);
   } else /* if (data->register_type == REG_TYPE_UINT16) */
@@ -702,12 +747,18 @@ static int mb_config_add_data(oconfig_item_t *ci) /* {{{ */
         data.register_type = REG_TYPE_INT16;
       else if (strcasecmp("Int32", tmp) == 0)
         data.register_type = REG_TYPE_INT32;
+      else if (strcasecmp("Int32LE", tmp) == 0)
+        data.register_type = REG_TYPE_INT32_CDAB;
       else if (strcasecmp("Uint16", tmp) == 0)
         data.register_type = REG_TYPE_UINT16;
       else if (strcasecmp("Uint32", tmp) == 0)
         data.register_type = REG_TYPE_UINT32;
+      else if (strcasecmp("Uint32LE", tmp) == 0)
+        data.register_type = REG_TYPE_UINT32_CDAB;
       else if (strcasecmp("Float", tmp) == 0)
         data.register_type = REG_TYPE_FLOAT;
+      else if (strcasecmp("FloatLE", tmp) == 0)
+        data.register_type = REG_TYPE_FLOAT_CDAB;
       else {
         ERROR("Modbus plugin: The register type \"%s\" is unknown.", tmp);
         status = -1;

From 67afd2685892e69ababb489f48b9033ab5908f4d Mon Sep 17 00:00:00 2001
From: PJ Bostley <pbostley@gmail.com>
Date: Tue, 23 Jan 2018 15:33:23 -0700
Subject: [PATCH 2/2] Adding documentation for the Modbus little endian modes
 where 32 bit values have thier registers swapped

---
 src/collectd.conf.pod | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod
index dfd785a2c8..e9715126e6 100644
--- a/src/collectd.conf.pod
+++ b/src/collectd.conf.pod
@@ -4139,11 +4139,19 @@ Configures the base register to read from the device. If the option
 B<RegisterType> has been set to B<Uint32> or B<Float>, this and the next
 register will be read (the register number is increased by one).
 
-=item B<RegisterType> B<Int16>|B<Int32>|B<Uint16>|B<Uint32>|B<Float>
-
-Specifies what kind of data is returned by the device. If the type is B<Int32>,
-B<Uint32> or B<Float>, two 16E<nbsp>bit registers will be read and the data is
-combined into one value. Defaults to B<Uint16>.
+=item B<RegisterType> B<Int16>|B<Int32>|B<Uint16>|B<Uint32>|B<Float>|B<Int32LE>|B<Uint32LE>|B<FloatLE>
+
+Specifies what kind of data is returned by the device. This defaults to
+B<Uint16>.  If the type is B<Int32>, B<Int32LE>, B<Uint32>, B<Uint32LE>,
+B<Float> or B<FloatLE>, two 16E<nbsp>bit registers at B<RegisterBase>
+and B<RegisterBase+1> will be read and the data is combined into one
+32E<nbsp>value. For B<Int32>, B<Uint32> and B<Float> the most significant
+16E<nbsp>bits are in the register at B<RegisterBase> and the least
+significant 16E<nbsp>bits are in the register at B<RegisterBase+1>.
+For B<Int32LE>, B<Uint32LE>, or B<Float32LE>, the high and low order
+registers are swapped with the most significant 16E<nbsp>bits in
+the B<RegisterBase+1> and the least significant 16E<nbsp>bits in
+B<RegisterBase>.
 
 =item B<RegisterCmd> B<ReadHolding>|B<ReadInput>
 
