Browse Source

hwxpxx: Initial implementation

This module implements support for the Veris HWXPHTX Hardware Protocol
Humidity and Temperature Sensor family.  It uses MODBUS over an RS485
interface.

This module was developed using libmodbus 3.1.2, and the Veris HWXPHTX.

This sensor supports humidity, and optionally, temperature, slider
switch, and override switch reporting.  The HWXPHTX used to develop
this driver did not include the optional slider or override switches,
however support for them is provided.

Signed-off-by: Jon Trulson <jtrulson@ics.com>
Signed-off-by: Mihai Tudor Panu <mihai.tudor.panu@intel.com>
Jon Trulson 9 years ago
parent
commit
a5fd4a2c10

+ 1
- 0
examples/c++/CMakeLists.txt View File

@@ -235,6 +235,7 @@ add_example (ads1x15)
235 235
 if (MODBUS_FOUND)
236 236
   include_directories(${MODBUS_INCLUDE_DIRS})
237 237
   add_example (t3311)
238
+  add_example (hwxpxx)
238 239
 endif()
239 240
 add_example (hdxxvxta)
240 241
 

+ 102
- 0
examples/c++/hwxpxx.cxx View File

@@ -0,0 +1,102 @@
1
+/*
2
+ * Author: Jon Trulson <jtrulson@ics.com>
3
+ * Copyright (c) 2016 Intel Corporation.
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining
6
+ * a copy of this software and associated documentation files (the
7
+ * "Software"), to deal in the Software without restriction, including
8
+ * without limitation the rights to use, copy, modify, merge, publish,
9
+ * distribute, sublicense, and/or sell copies of the Software, and to
10
+ * permit persons to whom the Software is furnished to do so, subject to
11
+ * the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be
14
+ * included in all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ */
24
+
25
+#include <unistd.h>
26
+#include <iostream>
27
+#include <signal.h>
28
+
29
+#include "hwxpxx.h"
30
+
31
+using namespace std;
32
+
33
+bool shouldRun = true;
34
+
35
+void sig_handler(int signo)
36
+{
37
+  if (signo == SIGINT)
38
+    shouldRun = false;
39
+}
40
+
41
+int main(int argc, char **argv)
42
+{
43
+  signal(SIGINT, sig_handler);
44
+
45
+//! [Interesting]
46
+
47
+  string defaultDev = "/dev/ttyUSB0";
48
+
49
+  // if an argument was specified, use it as the device instead
50
+  if (argc > 1)
51
+    defaultDev = string(argv[1]);
52
+
53
+  cout << "Using device " << defaultDev << endl;
54
+  cout << "Initializing..." << endl;
55
+
56
+  // Instantiate an HWXPXX instance, using MODBUS slave address 3, and
57
+  // default comm parameters (19200, 8, N, 2)
58
+  upm::HWXPXX *sensor = new upm::HWXPXX(defaultDev, 3);
59
+
60
+  // output the Slave ID (manufacturer, model, serno)
61
+  cout << "Slave ID: " << sensor->getSlaveID() << endl;
62
+
63
+  // stored temperature and humidity offsets
64
+  cout << "Temperature Offset: " << sensor->getTemperatureOffset()
65
+       << endl;
66
+  cout << "Humidity Offset: " << sensor->getHumidityOffset()
67
+       << endl;
68
+
69
+  cout << endl;
70
+
71
+  // update and print available values every second
72
+  while (shouldRun)
73
+    {
74
+      // update our values from the sensor
75
+      sensor->update();
76
+
77
+      // we show both C and F for temperature
78
+      cout << "Temperature: " << sensor->getTemperature()
79
+           << " C / " << sensor->getTemperature(true) << " F"
80
+           << endl;
81
+
82
+      cout << "Humidity: " << sensor->getHumidity()
83
+           << " %" << endl;
84
+
85
+      cout << "Slider: " << sensor->getSlider() << " %" << endl;
86
+
87
+      cout << "Override Switch Status: " << sensor->getOverrideSwitchStatus()
88
+           <<  endl;
89
+
90
+      cout << endl;
91
+
92
+      sleep(1);
93
+    }
94
+
95
+  cout << "Exiting..." << endl;
96
+
97
+  delete sensor;
98
+
99
+//! [Interesting]
100
+
101
+  return 0;
102
+}

+ 86
- 0
examples/javascript/hwxpxx.js View File

@@ -0,0 +1,86 @@
1
+/*jslint node:true, vars:true, bitwise:true, unparam:true */
2
+/*jshint unused:true */
3
+
4
+/*
5
+ * Author: Jon Trulson <jtrulson@ics.com>
6
+ * Copyright (c) 2016 Intel Corporation.
7
+ *
8
+ * Permission is hereby granted, free of charge, to any person obtaining
9
+ * a copy of this software and associated documentation files (the
10
+ * "Software"), to deal in the Software without restriction, including
11
+ * without limitation the rights to use, copy, modify, merge, publish,
12
+ * distribute, sublicense, and/or sell copies of the Software, and to
13
+ * permit persons to whom the Software is furnished to do so, subject to
14
+ * the following conditions:
15
+ *
16
+ * The above copyright notice and this permission notice shall be
17
+ * included in all copies or substantial portions of the Software.
18
+ *
19
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
+ */
27
+
28
+
29
+var sensorObj = require('jsupm_hwxpxx');
30
+
31
+
32
+/************** Main code **************/
33
+
34
+var defaultDev = "/dev/ttyUSB0";
35
+
36
+// if an argument was specified, use it as the device instead
37
+if (process.argv.length > 2)
38
+{
39
+    defaultDev = process.argv[2];
40
+}
41
+
42
+console.log("Using device " + defaultDev);
43
+console.log("Initializing...");
44
+
45
+// Instantiate an HWXPXX instance, using MODBUS slave address 3, and
46
+// default comm parameters (19200, 8, N, 2)
47
+var sensor = new sensorObj.HWXPXX(defaultDev, 3);
48
+
49
+// output the Slave ID (manufacturer, model, serno)
50
+console.log("Slave ID:", sensor.getSlaveID());
51
+
52
+// stored temperature and humidity offsets
53
+console.log("Temperature Offset:", sensor.getTemperatureOffset());
54
+console.log("Humidity Offset:", sensor.getHumidityOffset());
55
+
56
+console.log("");
57
+
58
+// update and print available values every second
59
+setInterval(function()
60
+{
61
+    // update our values from the sensor
62
+    sensor.update();
63
+
64
+    // we show both C and F for temperature
65
+    console.log("Temperature:", sensor.getTemperature(),
66
+                "C /", sensor.getTemperature(true), "F");
67
+
68
+    console.log("Humidity:", sensor.getHumidity(), "%");
69
+
70
+    console.log("Slider:", sensor.getSlider(), "%");
71
+
72
+    console.log("Override Switch Status:", sensor.getOverrideSwitchStatus());
73
+
74
+    console.log("");
75
+
76
+}, 1000);
77
+
78
+
79
+process.on('SIGINT', function()
80
+{
81
+    sensor = null;
82
+    sensorObj.cleanUp();
83
+    sensorObj = null;
84
+    console.log("Exiting...");
85
+    process.exit(0);
86
+});

+ 79
- 0
examples/python/hwxpxx.py View File

@@ -0,0 +1,79 @@
1
+#!/usr/bin/python
2
+# Author: Jon Trulson <jtrulson@ics.com>
3
+# Copyright (c) 2016 Intel Corporation.
4
+#
5
+# Permission is hereby granted, free of charge, to any person obtaining
6
+# a copy of this software and associated documentation files (the
7
+# "Software"), to deal in the Software without restriction, including
8
+# without limitation the rights to use, copy, modify, merge, publish,
9
+# distribute, sublicense, and/or sell copies of the Software, and to
10
+# permit persons to whom the Software is furnished to do so, subject to
11
+# the following conditions:
12
+#
13
+# The above copyright notice and this permission notice shall be
14
+# included in all copies or substantial portions of the Software.
15
+#
16
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+import time, sys, signal, atexit
25
+import pyupm_hwxpxx as sensorObj
26
+
27
+## Exit handlers ##
28
+# This function stops python from printing a stacktrace when you hit control-C
29
+def SIGINTHandler(signum, frame):
30
+	raise SystemExit
31
+
32
+# This function lets you run code on exit
33
+def exitHandler():
34
+	print "Exiting..."
35
+	sys.exit(0)
36
+
37
+# Register exit handlers
38
+atexit.register(exitHandler)
39
+signal.signal(signal.SIGINT, SIGINTHandler)
40
+
41
+defaultDev = "/dev/ttyUSB0"
42
+
43
+# if an argument was specified, use it as the device instead
44
+if (len(sys.argv) > 1):
45
+        defaultDev = sys.argv[1]
46
+
47
+print "Using device", defaultDev
48
+print "Initializing..."
49
+
50
+# Instantiate an HWXPXX instance, using MODBUS slave address 3, and
51
+# default comm parameters (19200, 8, N, 2)
52
+sensor = sensorObj.HWXPXX(defaultDev, 3)
53
+
54
+# output the serial number and firmware revision
55
+print "Slave ID:", sensor.getSlaveID()
56
+
57
+# stored temperature and humidity offsets
58
+print "Temperature Offset:", sensor.getTemperatureOffset()
59
+print "Humidity Offset:", sensor.getHumidityOffset()
60
+
61
+print
62
+
63
+# update and print available values every second
64
+while (1):
65
+        # update our values from the sensor
66
+        sensor.update()
67
+
68
+        # we show both C and F for temperature
69
+        print "Temperature:", sensor.getTemperature(), "C /",
70
+        print sensor.getTemperature(True), "F"
71
+
72
+        print "Humidity:", sensor.getHumidity(), "%"
73
+
74
+        print "Slider:", sensor.getSlider(), "%"
75
+
76
+        print "Override Switch Status:", sensor.getOverrideSwitchStatus()
77
+
78
+        print
79
+	time.sleep(1)

+ 21
- 0
src/hwxpxx/CMakeLists.txt View File

@@ -0,0 +1,21 @@
1
+set (libname "hwxpxx")
2
+set (libdescription "upm module for the Veris HWXPXX (HWXPHTX)")
3
+set (module_src ${libname}.cxx)
4
+set (module_h ${libname}.h)
5
+
6
+pkg_search_module(MODBUS libmodbus)
7
+if (MODBUS_FOUND)
8
+  set (reqlibname "libmodbus")
9
+  include_directories(${MODBUS_INCLUDE_DIRS})
10
+  upm_module_init()
11
+  add_dependencies(${libname} ${MODBUS_LIBRARIES})
12
+  target_link_libraries(${libname} ${MODBUS_LIBRARIES})
13
+  if (BUILDSWIG)
14
+    if (BUILDSWIGNODE)
15
+      swig_link_libraries (jsupm_${libname} ${MODBUS_LIBRARIES} ${MRAA_LIBRARIES} ${NODE_LIBRARIES})
16
+    endif()
17
+    if (BUILDSWIGPYTHON)
18
+      swig_link_libraries (pyupm_${libname} ${MODBUS_LIBRARIES} ${PYTHON_LIBRARIES} ${MRAA_LIBRARIES})
19
+    endif()
20
+  endif()
21
+endif ()

+ 379
- 0
src/hwxpxx/hwxpxx.cxx View File

@@ -0,0 +1,379 @@
1
+/*
2
+ * Author: Jon Trulson <jtrulson@ics.com>
3
+ * Copyright (c) 2016 Intel Corporation.
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining
6
+ * a copy of this software and associated documentation files (the
7
+ * "Software"), to deal in the Software without restriction, including
8
+ * without limitation the rights to use, copy, modify, merge, publish,
9
+ * distribute, sublicense, and/or sell copies of the Software, and to
10
+ * permit persons to whom the Software is furnished to do so, subject to
11
+ * the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be
14
+ * included in all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ */
24
+
25
+#include <unistd.h>
26
+#include <iostream>
27
+#include <stdexcept>
28
+#include <string>
29
+
30
+#include "hwxpxx.h"
31
+
32
+using namespace upm;
33
+using namespace std;
34
+
35
+// conversion from fahrenheit to celcius and back
36
+
37
+static float f2c(float f)
38
+{
39
+  return ((f - 32.0) / (9.0 / 5.0));
40
+}
41
+
42
+static float c2f(float c)
43
+{
44
+  return (c * (9.0 / 5.0) + 32.0);
45
+}
46
+
47
+HWXPXX::HWXPXX(std::string device, int address, int baud, int bits, char parity,
48
+               int stopBits) :
49
+  m_mbContext(0)
50
+{
51
+  // check some of the parameters
52
+  if (!(bits == 7 || bits == 8))
53
+    {
54
+      throw std::out_of_range(std::string(__FUNCTION__) +
55
+                              ": bits must be 7 or 8");
56
+    }
57
+
58
+  if (!(parity == 'N' || parity == 'E' || parity == 'O'))
59
+    {
60
+      throw std::out_of_range(std::string(__FUNCTION__) +
61
+                              ": parity must be 'N', 'O', or 'E'");
62
+    }
63
+
64
+  if (!(stopBits == 1 || stopBits == 2))
65
+    {
66
+      throw std::out_of_range(std::string(__FUNCTION__) +
67
+                              ": stopBits must be 1 or 2");
68
+    }
69
+
70
+  m_temperature = 0.0;
71
+  m_humidity = 0.0;
72
+  m_slider = 0;
73
+
74
+  // now, open/init the device and modbus context
75
+
76
+  if (!(m_mbContext = modbus_new_rtu(device.c_str(), baud, parity, bits,
77
+                                     stopBits)))
78
+    {
79
+      throw std::runtime_error(std::string(__FUNCTION__) +
80
+                               ": modbus_new_rtu() failed");
81
+    }
82
+
83
+  // set the slave address of the device we want to talk to
84
+
85
+  // addresses are only 8bits wide
86
+  address &= 0xff;
87
+  if (modbus_set_slave(m_mbContext, address))
88
+    {
89
+      throw std::runtime_error(std::string(__FUNCTION__) +
90
+                               ": modbus_set_slave() failed");
91
+    }
92
+
93
+  // set the serial mode
94
+  modbus_rtu_set_serial_mode(m_mbContext, MODBUS_RTU_RS232);
95
+
96
+  // now connect..
97
+  if (modbus_connect(m_mbContext))
98
+    {
99
+      throw std::runtime_error(std::string(__FUNCTION__) +
100
+                               ": modbus_connect() failed");
101
+    }
102
+
103
+  // read the 2 coils to determine temperature scale and current status
104
+  // of (optional) override switch
105
+  uint8_t coils[2];
106
+  readCoils(COIL_TEMP_SCALE, 2, coils);
107
+
108
+  // temp scale
109
+  if (coils[0])
110
+    m_isCelcius = false;
111
+  else
112
+    m_isCelcius = true;
113
+
114
+  // current override switch status
115
+  m_override = ((coils[1]) ? true : false);
116
+
117
+  // turn off debugging
118
+  setDebug(false);
119
+}
120
+
121
+HWXPXX::~HWXPXX()
122
+{
123
+  if (m_mbContext)
124
+    {
125
+      modbus_close(m_mbContext);
126
+      modbus_free(m_mbContext);
127
+    }
128
+}
129
+
130
+int HWXPXX::readInputRegs(INPUT_REGS_T reg, int len, uint16_t *buf)
131
+{
132
+  int rv;
133
+
134
+  if ((rv = modbus_read_input_registers(m_mbContext, reg, len, buf)) < 0)
135
+    {
136
+      throw std::runtime_error(std::string(__FUNCTION__) +
137
+                               ": modbus_read_input_registers() failed");
138
+    }
139
+
140
+  return rv;
141
+}
142
+
143
+uint16_t HWXPXX::readInputReg(INPUT_REGS_T reg)
144
+{
145
+  uint16_t val;
146
+
147
+  if (readInputRegs(reg, 1, &val) != 1)
148
+    {
149
+      throw std::runtime_error(std::string(__FUNCTION__) +
150
+                               ": readInputRegs() returned bad data");
151
+    }
152
+
153
+  return val;
154
+}
155
+
156
+int HWXPXX::readHoldingRegs(HOLDING_REGS_T reg, int len, uint16_t *buf)
157
+{
158
+  int rv;
159
+
160
+  if ((rv = modbus_read_registers(m_mbContext, reg, len, buf)) < 0)
161
+    {
162
+      throw std::runtime_error(std::string(__FUNCTION__) +
163
+                               ": modbus_read_registers() failed");
164
+    }
165
+
166
+  return rv;
167
+}
168
+
169
+uint16_t HWXPXX::readHoldingReg(HOLDING_REGS_T reg)
170
+{
171
+  uint16_t val;
172
+
173
+  if (readHoldingRegs(reg, 1, &val) != 1)
174
+    {
175
+      throw std::runtime_error(std::string(__FUNCTION__) +
176
+                               ": readInputRegs() returned bad data");
177
+    }
178
+
179
+  return val;
180
+}
181
+
182
+void HWXPXX::writeHoldingReg(HOLDING_REGS_T reg, int value)
183
+{
184
+  if (modbus_write_register(m_mbContext, reg, value) != 1)
185
+    {
186
+      throw std::runtime_error(std::string(__FUNCTION__) +
187
+                               ": modbus_write_register() failed");
188
+    }
189
+}
190
+
191
+int HWXPXX::readCoils(COIL_REGS_T reg, int numBits, uint8_t *buf)
192
+{
193
+  int rv;
194
+
195
+  if ((rv = modbus_read_bits(m_mbContext, reg, numBits, buf)) < 0)
196
+    {
197
+      throw std::runtime_error(std::string(__FUNCTION__) +
198
+                               ": modbus_read_bits() failed");
199
+    }
200
+
201
+  return rv;
202
+}
203
+
204
+bool HWXPXX::readCoil(COIL_REGS_T reg)
205
+{
206
+  uint8_t buf;
207
+
208
+  if (readCoils(reg, 1, &buf) != 1)
209
+    {
210
+      throw std::runtime_error(std::string(__FUNCTION__) +
211
+                               ": readCoils() returned bad data");
212
+    }
213
+
214
+  return ((buf) ? true : false);
215
+}
216
+
217
+void HWXPXX::writeCoil(COIL_REGS_T reg, bool val)
218
+{
219
+  int value = (val) ? TRUE : FALSE;
220
+
221
+  if (modbus_write_bit(m_mbContext, reg, value) != 1)
222
+    {
223
+      throw std::runtime_error(std::string(__FUNCTION__) +
224
+                               ": modbus_write_bit() failed");
225
+    }
226
+}
227
+
228
+void HWXPXX::update()
229
+{
230
+  static const int dataLen = 3;
231
+  uint16_t data[dataLen];
232
+
233
+  // we read 3 input registers starting at humidity
234
+  if (readInputRegs(INPUT_HUMIDITY, dataLen, data) != dataLen)
235
+    {
236
+      throw std::runtime_error(std::string(__FUNCTION__) +
237
+                               ": readInputRegs() failed to read 3 registers");
238
+    }
239
+
240
+  // humidity
241
+  m_humidity = float((int16_t)data[0]) / 10.0;
242
+
243
+  // temperature, we always store as C
244
+  float tmpF = float((int16_t)data[1]) / 10.0;
245
+
246
+  if (m_isCelcius)
247
+    m_temperature = tmpF;
248
+  else
249
+    m_temperature = f2c(tmpF);
250
+
251
+  // optional slider level
252
+  m_slider = int(data[2]);
253
+
254
+  // optional override switch status
255
+  m_override = readCoil(COIL_OVERRIDE);
256
+}
257
+
258
+float HWXPXX::getTemperature(bool fahrenheit)
259
+{
260
+  if (fahrenheit)
261
+    return c2f(m_temperature);
262
+  else
263
+    return m_temperature;
264
+}
265
+
266
+float HWXPXX::getHumidity()
267
+{
268
+  return m_humidity;
269
+}
270
+
271
+int HWXPXX::getSlider()
272
+{
273
+  return m_slider;
274
+}
275
+
276
+bool HWXPXX::getOverrideSwitchStatus()
277
+{
278
+  return m_override;
279
+}
280
+
281
+int HWXPXX::getTemperatureOffset()
282
+{
283
+  return int((int16_t)readHoldingReg(HOLDING_TEMP_OFFSET));
284
+}
285
+
286
+int HWXPXX::getHumidityOffset()
287
+{
288
+  return int((int16_t)readHoldingReg(HOLDING_HUM_OFFSET));
289
+}
290
+
291
+void HWXPXX::setTemperatureOffset(int offset)
292
+{
293
+  if (offset < -50 || offset > 50)
294
+    {
295
+      throw std::out_of_range(std::string(__FUNCTION__) +
296
+                              ": offset must be between -50 to 50");
297
+    }
298
+
299
+  writeHoldingReg(HOLDING_TEMP_OFFSET, offset);
300
+}
301
+
302
+void HWXPXX::setHumidityOffset(int offset)
303
+{
304
+  if (offset < -100 || offset > 100)
305
+    {
306
+      throw std::out_of_range(std::string(__FUNCTION__) +
307
+                              ": offset must be between -100 to 100");
308
+    }
309
+
310
+  writeHoldingReg(HOLDING_HUM_OFFSET, offset);
311
+}
312
+
313
+void HWXPXX::clearOverrideSwitch()
314
+{
315
+  writeCoil(COIL_OVERRIDE, false);
316
+}
317
+
318
+void HWXPXX::setTemperatureScale(bool fahrenheit)
319
+{
320
+  writeCoil(COIL_TEMP_SCALE, fahrenheit);
321
+
322
+  // now re-read and set m_isCelcius properly
323
+  if (readCoil(COIL_TEMP_SCALE))
324
+    m_isCelcius = false;
325
+  else
326
+    m_isCelcius = true;
327
+}
328
+
329
+string HWXPXX::getSlaveID()
330
+{
331
+  uint8_t id[MODBUS_MAX_PDU_LENGTH];
332
+  int rv;
333
+
334
+  if ((rv = modbus_report_slave_id(m_mbContext, MODBUS_MAX_PDU_LENGTH, id)) < 0)
335
+    {
336
+      throw std::runtime_error(std::string(__FUNCTION__) +
337
+                               ": modbus_report_slave_id() failed");
338
+    }
339
+
340
+  // the first byte is the number of bytes in the response, the second
341
+  // byte is the active indicator (00 = off, ff = on), and the rest
342
+  // are ascii identification (company, model, and serial number) data.
343
+
344
+  if (rv > 2)
345
+    {
346
+      string retID((char *)&id[2], rv - 2);
347
+      return retID;
348
+    }
349
+  else
350
+    return "";
351
+}
352
+
353
+void HWXPXX::setSlaveAddress(int addr)
354
+{
355
+  // addresses are only 8bits wide
356
+  addr &= 0xff;
357
+
358
+  if (modbus_set_slave(m_mbContext, addr))
359
+    {
360
+      throw std::runtime_error(std::string(__FUNCTION__) +
361
+                               ": modbus_set_slave() failed");
362
+    }
363
+
364
+  // now re-read and set m_isCelcius properly
365
+  if (readCoil(COIL_TEMP_SCALE))
366
+    m_isCelcius = false;
367
+  else
368
+    m_isCelcius = true;
369
+}
370
+
371
+void HWXPXX::setDebug(bool enable)
372
+{
373
+  m_debugging = enable;
374
+
375
+  if (enable)
376
+    modbus_set_debug(m_mbContext, 1);
377
+  else
378
+    modbus_set_debug(m_mbContext, 0);
379
+}

+ 283
- 0
src/hwxpxx/hwxpxx.h View File

@@ -0,0 +1,283 @@
1
+/*
2
+ * Author: Jon Trulson <jtrulson@ics.com>
3
+ * Copyright (c) 2016 Intel Corporation.
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining
6
+ * a copy of this software and associated documentation files (the
7
+ * "Software"), to deal in the Software without restriction, including
8
+ * without limitation the rights to use, copy, modify, merge, publish,
9
+ * distribute, sublicense, and/or sell copies of the Software, and to
10
+ * permit persons to whom the Software is furnished to do so, subject to
11
+ * the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be
14
+ * included in all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ */
24
+#pragma once
25
+
26
+#include <string>
27
+
28
+#include <modbus/modbus.h>
29
+
30
+namespace upm {
31
+
32
+  /**
33
+   * @brief HWXPXX Hardware Protocol Humidity and Temperature Sensor
34
+   * @defgroup hwxpxx libupm-hwxpxx
35
+   * @ingroup uart temp
36
+   */
37
+
38
+  /**
39
+   * @library hwxpxx
40
+   * @sensor hwxpxx
41
+   * @comname UPM API for the Veris HWXPXX Hardware Protocol Humidity and Temperature Sensor
42
+   * @type temp
43
+   * @man veris
44
+   * @con uart
45
+   * @web http://cpengineerscorp.net/veris-industries-hwxphtx.html
46
+   *
47
+   * @brief UPM API for the Veris HWXPXX Hardware Protocol Humidity and Temperature Sensor
48
+   *
49
+   * This module implements support for the Veris HWXPHTX Hardware
50
+   * Protocol Humidity and Temperature Sensor family.  It uses MODBUS
51
+   * over an RS485 interface.  You must have libmodbus v3.1.2 (or
52
+   * greater) installed to compile and use this driver.
53
+   *
54
+   * This module was developed using libmodbus 3.1.2, and the HWXPHTX.
55
+   * This sensor supports humidity, and optionally, temperature,
56
+   * slider switch, and override switch reporting.  The HWXPHTX used to
57
+   * develop this driver did not include the optional slider or
58
+   * override switches, however support for them is provided.
59
+   *
60
+   * It was developed using an RS232->RS485 inteface.  You cannot use
61
+   * the built in MCU TTL UART pins for accessing this device -- you
62
+   * must use a full serial RS232->RS485 interface connected via USB.
63
+   *
64
+   * @snippet hwxpxx.cxx Interesting
65
+   */
66
+
67
+  class HWXPXX {
68
+  public:
69
+    // MODBUS input registers
70
+    typedef enum {
71
+      INPUT_HUMIDITY                        = 0x0000,
72
+      // optional temp sensor
73
+      INPUT_TEMPERATURE                     = 0x0001,
74
+      // optional slider input
75
+      INPUT_SLIDER                          = 0x0002
76
+    } INPUT_REGS_T;
77
+
78
+    // MODBUS coil registers
79
+    typedef enum {
80
+      // device reports in C or F?
81
+      COIL_TEMP_SCALE                       = 0x0000,
82
+
83
+      // optional override button status
84
+      COIL_OVERRIDE                         = 0x0001
85
+    } COIL_REGS_T;
86
+
87
+    // MODBUS holding registers
88
+    typedef enum {
89
+      HOLDING_TEMP_OFFSET                   = 0x0000,
90
+      HOLDING_HUM_OFFSET                    = 0x0001
91
+    } HOLDING_REGS_T;
92
+
93
+    /**
94
+     * HWXPXX constructor
95
+     *
96
+     * @param device Path to the serial device
97
+     * @param address The MODBUS slave address
98
+     * @param baud The baudrate of the device.  Default: 19200
99
+     * @param bits The number of bits per byte.  Default: 8
100
+     * @param parity The parity of the connection, 'N' for None, 'E'
101
+     * for Even, 'O' for Odd.  Default: 'N'
102
+     * @param stopBits The number of stop bits.  Default: 2
103
+     */
104
+    HWXPXX(std::string device, int address, int baud=19200, int bits=8,
105
+          char parity='N', int stopBits=2);
106
+
107
+    /**
108
+     * HWXPXX Destructor
109
+     */
110
+    ~HWXPXX();
111
+
112
+    /**
113
+     * Read current values from the sensor and update internal stored
114
+     * values.  This method must be called prior to querying any
115
+     * values, such as temperature, humidity, override switch status,
116
+     * or slider switch status.
117
+     */
118
+    void update();
119
+
120
+    /**
121
+     * Get the current temperature.  update() must have been called
122
+     * prior to calling this method.  If this option was not
123
+     * installed, this method will always return 0C/0F, depending on
124
+     * the scale the device is operating in natively.
125
+     *
126
+     * @param fahrenheit true to return the temperature in degrees
127
+     * fahrenheit, false to return the temperature in degrees celcius.
128
+     * The default is false (degrees Celcius).
129
+     * @return The last temperature reading in Celcius or Fahrenheit
130
+     */
131
+    float getTemperature(bool fahrenheit=false);
132
+
133
+    /**
134
+     * Get the current relative humidity.  update() must have been called
135
+     * prior to calling this method.
136
+     *
137
+     * @return The last humidity reading
138
+     */
139
+    float getHumidity();
140
+
141
+    /**
142
+     * Get the current slider switch position.  update() must have
143
+     * been called prior to calling this method.  This returns a value
144
+     * between 0-100 corresponding to the position of the slider
145
+     * switch.  If this option is not installed, this method will
146
+     * always return 0.
147
+     *
148
+     * @return The last slider switch reading
149
+     */
150
+    int getSlider();
151
+
152
+    /**
153
+     * Get the current override switch status.  update() must have
154
+     * been called prior to calling this method.  This returns true if
155
+     * the override switch was pressed.  Use clearOverrideSwitch() to
156
+     * reset this value to false.  If this option is not installed,
157
+     * then this method will always return false.  It is not possible
158
+     * to programatically set this value to true - that can only be
159
+     * done by physically pressing the override switch.
160
+     *
161
+     * @return The last overide switch status reading
162
+     */
163
+    bool getOverrideSwitchStatus();
164
+
165
+    /**
166
+     * Clear the override switch status (set it to false).  If this
167
+     * option is not installed, then this method will have no effect
168
+     * (the overide switch status will always be false).
169
+     *
170
+     */
171
+    void clearOverrideSwitch();
172
+
173
+    /**
174
+     * Return the current temperature offset stored on the device.
175
+     * This is a value between -50 and +50, specified in tenths of a
176
+     * degree in whatever scale (Celcius or Fahrenheit) is in use.
177
+     * This offset is applied to the returned temperature reading by the
178
+     * device.
179
+     *
180
+     * @return The current temperature offset in tenths of a degree
181
+     */
182
+    int getTemperatureOffset();
183
+
184
+    /**
185
+     * Return the current humidity offset stored on the device.  This
186
+     * is a value between -100 and +100, specified in tenths of a
187
+     * percent.  This offset is applied to the returned humidity
188
+     * reading by the device.
189
+     *
190
+     * @return The current humidity offset in tenths of a percent
191
+     */
192
+    int getHumidityOffset();
193
+
194
+    /**
195
+     * Set the stored temperature offset on the device.  This is a
196
+     * value between -50 and +50, specified in tenths of a degree in
197
+     * what ever scale (Celcius or Fahrenheit) is in use.  This offset
198
+     * is applied to the returned temperature reading by the device.
199
+     *
200
+     * @param offset Offset in tenths of a degree with a range of -50 to +50
201
+     */
202
+    void setTemperatureOffset(int offset);
203
+
204
+    /**
205
+     * Set the stored humidity offset on the device.  This is a value
206
+     * between -100 and +100, specified in tenths of a percent.  This
207
+     * offset is applied to the returned humidity reading by the
208
+     * device.
209
+     *
210
+     * @param offset Offset in tenths of a percent with a range of -100 to +100
211
+     */
212
+    void setHumidityOffset(int offset);
213
+
214
+    /**
215
+     * Set the temperature scale used by the device.  This driver
216
+     * detects this setting automatically and adjusts itself
217
+     * accordingly, so this is generally never needed.  It is used to
218
+     * set the native reporting scale of the temperature either in
219
+     * degrees Celcius or Fahrenheit.  Its setting will not affect
220
+     * the operation of getTemperature().
221
+     *
222
+     * @param fahrenheit true to set Fahrenheit, false to set Celcius
223
+     */
224
+    void setTemperatureScale(bool fahrenheit);
225
+
226
+    /**
227
+     * Return a string corresponding the the device's MODBUS slave ID.
228
+     * This includes information such as the manufacturer, device
229
+     * model number and serial number.
230
+     *
231
+     * @return string represnting the MODBUS slave ID
232
+     */
233
+    std::string getSlaveID();
234
+
235
+    /**
236
+     * Set a new MODBUS slave address.  This is useful if you have
237
+     * multiple HWXPXX devices on a single bus.  When this method is
238
+     * called, the current temperature scale is re-read so that
239
+     * further update() calls can work correctly.
240
+     *
241
+     * @param addr The new slave address to set
242
+     */
243
+    void setSlaveAddress(int addr);
244
+
245
+    /**
246
+     * Enable or disable debugging output.  This primarily enables and
247
+     * disables libmodbus debugging output.
248
+     *
249
+     * @param enable true to enable debugging, false otherwise
250
+     */
251
+    void setDebug(bool enable);
252
+
253
+  protected:
254
+    // input registers
255
+    int readInputRegs(INPUT_REGS_T reg, int len, uint16_t *buf);
256
+    uint16_t readInputReg(INPUT_REGS_T reg);
257
+
258
+    // coils
259
+    int readCoils(COIL_REGS_T reg, int numBits, uint8_t *buf);
260
+    bool readCoil(COIL_REGS_T reg);
261
+    void writeCoil(COIL_REGS_T reg, bool val);
262
+
263
+    // holding registers
264
+    int readHoldingRegs(HOLDING_REGS_T reg, int len, uint16_t *buf);
265
+    uint16_t readHoldingReg(HOLDING_REGS_T reg);
266
+    void writeHoldingReg(HOLDING_REGS_T reg, int value);
267
+
268
+    // MODBUS context
269
+    modbus_t *m_mbContext;
270
+
271
+    // is the device reporting in C or F?
272
+    bool m_isCelcius;
273
+
274
+  private:
275
+    bool m_debugging;
276
+
277
+    // data
278
+    float m_temperature;
279
+    float m_humidity; // relative
280
+    int m_slider; // optional slider value
281
+    bool m_override; // status of override switch
282
+  };
283
+}

+ 20
- 0
src/hwxpxx/javaupm_hwxpxx.i View File

@@ -0,0 +1,20 @@
1
+%module javaupm_hwxpxx
2
+%include "../upm.i"
3
+%include "typemaps.i"
4
+
5
+%{
6
+    #include "hwxpxx.h"
7
+%}
8
+
9
+%include "hwxpxx.h"
10
+
11
+%pragma(java) jniclasscode=%{
12
+    static {
13
+        try {
14
+            System.loadLibrary("javaupm_hwxpxx");
15
+        } catch (UnsatisfiedLinkError e) {
16
+            System.err.println("Native code library failed to load. \n" + e);
17
+            System.exit(1);
18
+        }
19
+    }
20
+%}

+ 8
- 0
src/hwxpxx/jsupm_hwxpxx.i View File

@@ -0,0 +1,8 @@
1
+%module jsupm_hwxpxx
2
+%include "../upm.i"
3
+%include "stdint.i"
4
+
5
+%include "hwxpxx.h"
6
+%{
7
+    #include "hwxpxx.h"
8
+%}

+ 12
- 0
src/hwxpxx/pyupm_hwxpxx.i View File

@@ -0,0 +1,12 @@
1
+// Include doxygen-generated documentation
2
+%include "pyupm_doxy2swig.i"
3
+%module pyupm_hwxpxx
4
+%include "../upm.i"
5
+%include "stdint.i"
6
+
7
+%feature("autodoc", "3");
8
+
9
+%include "hwxpxx.h"
10
+%{
11
+    #include "hwxpxx.h"
12
+%}