diff --git a/examples/c++/CMakeLists.txt b/examples/c++/CMakeLists.txt index 2b63115f..7b7f50cf 100644 --- a/examples/c++/CMakeLists.txt +++ b/examples/c++/CMakeLists.txt @@ -235,6 +235,7 @@ add_example (ads1x15) if (MODBUS_FOUND) include_directories(${MODBUS_INCLUDE_DIRS}) add_example (t3311) + add_example (hwxpxx) endif() add_example (hdxxvxta) diff --git a/examples/c++/hwxpxx.cxx b/examples/c++/hwxpxx.cxx new file mode 100644 index 00000000..1cfdf543 --- /dev/null +++ b/examples/c++/hwxpxx.cxx @@ -0,0 +1,102 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include "hwxpxx.h" + +using namespace std; + +bool shouldRun = true; + +void sig_handler(int signo) +{ + if (signo == SIGINT) + shouldRun = false; +} + +int main(int argc, char **argv) +{ + signal(SIGINT, sig_handler); + +//! [Interesting] + + string defaultDev = "/dev/ttyUSB0"; + + // if an argument was specified, use it as the device instead + if (argc > 1) + defaultDev = string(argv[1]); + + cout << "Using device " << defaultDev << endl; + cout << "Initializing..." << endl; + + // Instantiate an HWXPXX instance, using MODBUS slave address 3, and + // default comm parameters (19200, 8, N, 2) + upm::HWXPXX *sensor = new upm::HWXPXX(defaultDev, 3); + + // output the Slave ID (manufacturer, model, serno) + cout << "Slave ID: " << sensor->getSlaveID() << endl; + + // stored temperature and humidity offsets + cout << "Temperature Offset: " << sensor->getTemperatureOffset() + << endl; + cout << "Humidity Offset: " << sensor->getHumidityOffset() + << endl; + + cout << endl; + + // update and print available values every second + while (shouldRun) + { + // update our values from the sensor + sensor->update(); + + // we show both C and F for temperature + cout << "Temperature: " << sensor->getTemperature() + << " C / " << sensor->getTemperature(true) << " F" + << endl; + + cout << "Humidity: " << sensor->getHumidity() + << " %" << endl; + + cout << "Slider: " << sensor->getSlider() << " %" << endl; + + cout << "Override Switch Status: " << sensor->getOverrideSwitchStatus() + << endl; + + cout << endl; + + sleep(1); + } + + cout << "Exiting..." << endl; + + delete sensor; + +//! [Interesting] + + return 0; +} diff --git a/examples/javascript/hwxpxx.js b/examples/javascript/hwxpxx.js new file mode 100644 index 00000000..08269c71 --- /dev/null +++ b/examples/javascript/hwxpxx.js @@ -0,0 +1,86 @@ +/*jslint node:true, vars:true, bitwise:true, unparam:true */ +/*jshint unused:true */ + +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +var sensorObj = require('jsupm_hwxpxx'); + + +/************** Main code **************/ + +var defaultDev = "/dev/ttyUSB0"; + +// if an argument was specified, use it as the device instead +if (process.argv.length > 2) +{ + defaultDev = process.argv[2]; +} + +console.log("Using device " + defaultDev); +console.log("Initializing..."); + +// Instantiate an HWXPXX instance, using MODBUS slave address 3, and +// default comm parameters (19200, 8, N, 2) +var sensor = new sensorObj.HWXPXX(defaultDev, 3); + +// output the Slave ID (manufacturer, model, serno) +console.log("Slave ID:", sensor.getSlaveID()); + +// stored temperature and humidity offsets +console.log("Temperature Offset:", sensor.getTemperatureOffset()); +console.log("Humidity Offset:", sensor.getHumidityOffset()); + +console.log(""); + +// update and print available values every second +setInterval(function() +{ + // update our values from the sensor + sensor.update(); + + // we show both C and F for temperature + console.log("Temperature:", sensor.getTemperature(), + "C /", sensor.getTemperature(true), "F"); + + console.log("Humidity:", sensor.getHumidity(), "%"); + + console.log("Slider:", sensor.getSlider(), "%"); + + console.log("Override Switch Status:", sensor.getOverrideSwitchStatus()); + + console.log(""); + +}, 1000); + + +process.on('SIGINT', function() +{ + sensor = null; + sensorObj.cleanUp(); + sensorObj = null; + console.log("Exiting..."); + process.exit(0); +}); diff --git a/examples/python/hwxpxx.py b/examples/python/hwxpxx.py new file mode 100644 index 00000000..0def9953 --- /dev/null +++ b/examples/python/hwxpxx.py @@ -0,0 +1,79 @@ +#!/usr/bin/python +# Author: Jon Trulson +# Copyright (c) 2016 Intel Corporation. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import time, sys, signal, atexit +import pyupm_hwxpxx as sensorObj + +## Exit handlers ## +# This function stops python from printing a stacktrace when you hit control-C +def SIGINTHandler(signum, frame): + raise SystemExit + +# This function lets you run code on exit +def exitHandler(): + print "Exiting..." + sys.exit(0) + +# Register exit handlers +atexit.register(exitHandler) +signal.signal(signal.SIGINT, SIGINTHandler) + +defaultDev = "/dev/ttyUSB0" + +# if an argument was specified, use it as the device instead +if (len(sys.argv) > 1): + defaultDev = sys.argv[1] + +print "Using device", defaultDev +print "Initializing..." + +# Instantiate an HWXPXX instance, using MODBUS slave address 3, and +# default comm parameters (19200, 8, N, 2) +sensor = sensorObj.HWXPXX(defaultDev, 3) + +# output the serial number and firmware revision +print "Slave ID:", sensor.getSlaveID() + +# stored temperature and humidity offsets +print "Temperature Offset:", sensor.getTemperatureOffset() +print "Humidity Offset:", sensor.getHumidityOffset() + +print + +# update and print available values every second +while (1): + # update our values from the sensor + sensor.update() + + # we show both C and F for temperature + print "Temperature:", sensor.getTemperature(), "C /", + print sensor.getTemperature(True), "F" + + print "Humidity:", sensor.getHumidity(), "%" + + print "Slider:", sensor.getSlider(), "%" + + print "Override Switch Status:", sensor.getOverrideSwitchStatus() + + print + time.sleep(1) diff --git a/src/hwxpxx/CMakeLists.txt b/src/hwxpxx/CMakeLists.txt new file mode 100644 index 00000000..47bee1af --- /dev/null +++ b/src/hwxpxx/CMakeLists.txt @@ -0,0 +1,21 @@ +set (libname "hwxpxx") +set (libdescription "upm module for the Veris HWXPXX (HWXPHTX)") +set (module_src ${libname}.cxx) +set (module_h ${libname}.h) + +pkg_search_module(MODBUS libmodbus) +if (MODBUS_FOUND) + set (reqlibname "libmodbus") + include_directories(${MODBUS_INCLUDE_DIRS}) + upm_module_init() + add_dependencies(${libname} ${MODBUS_LIBRARIES}) + target_link_libraries(${libname} ${MODBUS_LIBRARIES}) + if (BUILDSWIG) + if (BUILDSWIGNODE) + swig_link_libraries (jsupm_${libname} ${MODBUS_LIBRARIES} ${MRAA_LIBRARIES} ${NODE_LIBRARIES}) + endif() + if (BUILDSWIGPYTHON) + swig_link_libraries (pyupm_${libname} ${MODBUS_LIBRARIES} ${PYTHON_LIBRARIES} ${MRAA_LIBRARIES}) + endif() + endif() +endif () diff --git a/src/hwxpxx/hwxpxx.cxx b/src/hwxpxx/hwxpxx.cxx new file mode 100644 index 00000000..78753ce9 --- /dev/null +++ b/src/hwxpxx/hwxpxx.cxx @@ -0,0 +1,379 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "hwxpxx.h" + +using namespace upm; +using namespace std; + +// conversion from fahrenheit to celcius and back + +static float f2c(float f) +{ + return ((f - 32.0) / (9.0 / 5.0)); +} + +static float c2f(float c) +{ + return (c * (9.0 / 5.0) + 32.0); +} + +HWXPXX::HWXPXX(std::string device, int address, int baud, int bits, char parity, + int stopBits) : + m_mbContext(0) +{ + // check some of the parameters + if (!(bits == 7 || bits == 8)) + { + throw std::out_of_range(std::string(__FUNCTION__) + + ": bits must be 7 or 8"); + } + + if (!(parity == 'N' || parity == 'E' || parity == 'O')) + { + throw std::out_of_range(std::string(__FUNCTION__) + + ": parity must be 'N', 'O', or 'E'"); + } + + if (!(stopBits == 1 || stopBits == 2)) + { + throw std::out_of_range(std::string(__FUNCTION__) + + ": stopBits must be 1 or 2"); + } + + m_temperature = 0.0; + m_humidity = 0.0; + m_slider = 0; + + // now, open/init the device and modbus context + + if (!(m_mbContext = modbus_new_rtu(device.c_str(), baud, parity, bits, + stopBits))) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": modbus_new_rtu() failed"); + } + + // set the slave address of the device we want to talk to + + // addresses are only 8bits wide + address &= 0xff; + if (modbus_set_slave(m_mbContext, address)) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": modbus_set_slave() failed"); + } + + // set the serial mode + modbus_rtu_set_serial_mode(m_mbContext, MODBUS_RTU_RS232); + + // now connect.. + if (modbus_connect(m_mbContext)) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": modbus_connect() failed"); + } + + // read the 2 coils to determine temperature scale and current status + // of (optional) override switch + uint8_t coils[2]; + readCoils(COIL_TEMP_SCALE, 2, coils); + + // temp scale + if (coils[0]) + m_isCelcius = false; + else + m_isCelcius = true; + + // current override switch status + m_override = ((coils[1]) ? true : false); + + // turn off debugging + setDebug(false); +} + +HWXPXX::~HWXPXX() +{ + if (m_mbContext) + { + modbus_close(m_mbContext); + modbus_free(m_mbContext); + } +} + +int HWXPXX::readInputRegs(INPUT_REGS_T reg, int len, uint16_t *buf) +{ + int rv; + + if ((rv = modbus_read_input_registers(m_mbContext, reg, len, buf)) < 0) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": modbus_read_input_registers() failed"); + } + + return rv; +} + +uint16_t HWXPXX::readInputReg(INPUT_REGS_T reg) +{ + uint16_t val; + + if (readInputRegs(reg, 1, &val) != 1) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": readInputRegs() returned bad data"); + } + + return val; +} + +int HWXPXX::readHoldingRegs(HOLDING_REGS_T reg, int len, uint16_t *buf) +{ + int rv; + + if ((rv = modbus_read_registers(m_mbContext, reg, len, buf)) < 0) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": modbus_read_registers() failed"); + } + + return rv; +} + +uint16_t HWXPXX::readHoldingReg(HOLDING_REGS_T reg) +{ + uint16_t val; + + if (readHoldingRegs(reg, 1, &val) != 1) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": readInputRegs() returned bad data"); + } + + return val; +} + +void HWXPXX::writeHoldingReg(HOLDING_REGS_T reg, int value) +{ + if (modbus_write_register(m_mbContext, reg, value) != 1) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": modbus_write_register() failed"); + } +} + +int HWXPXX::readCoils(COIL_REGS_T reg, int numBits, uint8_t *buf) +{ + int rv; + + if ((rv = modbus_read_bits(m_mbContext, reg, numBits, buf)) < 0) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": modbus_read_bits() failed"); + } + + return rv; +} + +bool HWXPXX::readCoil(COIL_REGS_T reg) +{ + uint8_t buf; + + if (readCoils(reg, 1, &buf) != 1) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": readCoils() returned bad data"); + } + + return ((buf) ? true : false); +} + +void HWXPXX::writeCoil(COIL_REGS_T reg, bool val) +{ + int value = (val) ? TRUE : FALSE; + + if (modbus_write_bit(m_mbContext, reg, value) != 1) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": modbus_write_bit() failed"); + } +} + +void HWXPXX::update() +{ + static const int dataLen = 3; + uint16_t data[dataLen]; + + // we read 3 input registers starting at humidity + if (readInputRegs(INPUT_HUMIDITY, dataLen, data) != dataLen) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": readInputRegs() failed to read 3 registers"); + } + + // humidity + m_humidity = float((int16_t)data[0]) / 10.0; + + // temperature, we always store as C + float tmpF = float((int16_t)data[1]) / 10.0; + + if (m_isCelcius) + m_temperature = tmpF; + else + m_temperature = f2c(tmpF); + + // optional slider level + m_slider = int(data[2]); + + // optional override switch status + m_override = readCoil(COIL_OVERRIDE); +} + +float HWXPXX::getTemperature(bool fahrenheit) +{ + if (fahrenheit) + return c2f(m_temperature); + else + return m_temperature; +} + +float HWXPXX::getHumidity() +{ + return m_humidity; +} + +int HWXPXX::getSlider() +{ + return m_slider; +} + +bool HWXPXX::getOverrideSwitchStatus() +{ + return m_override; +} + +int HWXPXX::getTemperatureOffset() +{ + return int((int16_t)readHoldingReg(HOLDING_TEMP_OFFSET)); +} + +int HWXPXX::getHumidityOffset() +{ + return int((int16_t)readHoldingReg(HOLDING_HUM_OFFSET)); +} + +void HWXPXX::setTemperatureOffset(int offset) +{ + if (offset < -50 || offset > 50) + { + throw std::out_of_range(std::string(__FUNCTION__) + + ": offset must be between -50 to 50"); + } + + writeHoldingReg(HOLDING_TEMP_OFFSET, offset); +} + +void HWXPXX::setHumidityOffset(int offset) +{ + if (offset < -100 || offset > 100) + { + throw std::out_of_range(std::string(__FUNCTION__) + + ": offset must be between -100 to 100"); + } + + writeHoldingReg(HOLDING_HUM_OFFSET, offset); +} + +void HWXPXX::clearOverrideSwitch() +{ + writeCoil(COIL_OVERRIDE, false); +} + +void HWXPXX::setTemperatureScale(bool fahrenheit) +{ + writeCoil(COIL_TEMP_SCALE, fahrenheit); + + // now re-read and set m_isCelcius properly + if (readCoil(COIL_TEMP_SCALE)) + m_isCelcius = false; + else + m_isCelcius = true; +} + +string HWXPXX::getSlaveID() +{ + uint8_t id[MODBUS_MAX_PDU_LENGTH]; + int rv; + + if ((rv = modbus_report_slave_id(m_mbContext, MODBUS_MAX_PDU_LENGTH, id)) < 0) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": modbus_report_slave_id() failed"); + } + + // the first byte is the number of bytes in the response, the second + // byte is the active indicator (00 = off, ff = on), and the rest + // are ascii identification (company, model, and serial number) data. + + if (rv > 2) + { + string retID((char *)&id[2], rv - 2); + return retID; + } + else + return ""; +} + +void HWXPXX::setSlaveAddress(int addr) +{ + // addresses are only 8bits wide + addr &= 0xff; + + if (modbus_set_slave(m_mbContext, addr)) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": modbus_set_slave() failed"); + } + + // now re-read and set m_isCelcius properly + if (readCoil(COIL_TEMP_SCALE)) + m_isCelcius = false; + else + m_isCelcius = true; +} + +void HWXPXX::setDebug(bool enable) +{ + m_debugging = enable; + + if (enable) + modbus_set_debug(m_mbContext, 1); + else + modbus_set_debug(m_mbContext, 0); +} diff --git a/src/hwxpxx/hwxpxx.h b/src/hwxpxx/hwxpxx.h new file mode 100644 index 00000000..1d2da733 --- /dev/null +++ b/src/hwxpxx/hwxpxx.h @@ -0,0 +1,283 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#pragma once + +#include + +#include + +namespace upm { + + /** + * @brief HWXPXX Hardware Protocol Humidity and Temperature Sensor + * @defgroup hwxpxx libupm-hwxpxx + * @ingroup uart temp + */ + + /** + * @library hwxpxx + * @sensor hwxpxx + * @comname UPM API for the Veris HWXPXX Hardware Protocol Humidity and Temperature Sensor + * @type temp + * @man veris + * @con uart + * @web http://cpengineerscorp.net/veris-industries-hwxphtx.html + * + * @brief UPM API for the Veris HWXPXX Hardware Protocol Humidity and Temperature Sensor + * + * This module implements support for the Veris HWXPHTX Hardware + * Protocol Humidity and Temperature Sensor family. It uses MODBUS + * over an RS485 interface. You must have libmodbus v3.1.2 (or + * greater) installed to compile and use this driver. + * + * This module was developed using libmodbus 3.1.2, and the 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. + * + * It was developed using an RS232->RS485 inteface. You cannot use + * the built in MCU TTL UART pins for accessing this device -- you + * must use a full serial RS232->RS485 interface connected via USB. + * + * @snippet hwxpxx.cxx Interesting + */ + + class HWXPXX { + public: + // MODBUS input registers + typedef enum { + INPUT_HUMIDITY = 0x0000, + // optional temp sensor + INPUT_TEMPERATURE = 0x0001, + // optional slider input + INPUT_SLIDER = 0x0002 + } INPUT_REGS_T; + + // MODBUS coil registers + typedef enum { + // device reports in C or F? + COIL_TEMP_SCALE = 0x0000, + + // optional override button status + COIL_OVERRIDE = 0x0001 + } COIL_REGS_T; + + // MODBUS holding registers + typedef enum { + HOLDING_TEMP_OFFSET = 0x0000, + HOLDING_HUM_OFFSET = 0x0001 + } HOLDING_REGS_T; + + /** + * HWXPXX constructor + * + * @param device Path to the serial device + * @param address The MODBUS slave address + * @param baud The baudrate of the device. Default: 19200 + * @param bits The number of bits per byte. Default: 8 + * @param parity The parity of the connection, 'N' for None, 'E' + * for Even, 'O' for Odd. Default: 'N' + * @param stopBits The number of stop bits. Default: 2 + */ + HWXPXX(std::string device, int address, int baud=19200, int bits=8, + char parity='N', int stopBits=2); + + /** + * HWXPXX Destructor + */ + ~HWXPXX(); + + /** + * Read current values from the sensor and update internal stored + * values. This method must be called prior to querying any + * values, such as temperature, humidity, override switch status, + * or slider switch status. + */ + void update(); + + /** + * Get the current temperature. update() must have been called + * prior to calling this method. If this option was not + * installed, this method will always return 0C/0F, depending on + * the scale the device is operating in natively. + * + * @param fahrenheit true to return the temperature in degrees + * fahrenheit, false to return the temperature in degrees celcius. + * The default is false (degrees Celcius). + * @return The last temperature reading in Celcius or Fahrenheit + */ + float getTemperature(bool fahrenheit=false); + + /** + * Get the current relative humidity. update() must have been called + * prior to calling this method. + * + * @return The last humidity reading + */ + float getHumidity(); + + /** + * Get the current slider switch position. update() must have + * been called prior to calling this method. This returns a value + * between 0-100 corresponding to the position of the slider + * switch. If this option is not installed, this method will + * always return 0. + * + * @return The last slider switch reading + */ + int getSlider(); + + /** + * Get the current override switch status. update() must have + * been called prior to calling this method. This returns true if + * the override switch was pressed. Use clearOverrideSwitch() to + * reset this value to false. If this option is not installed, + * then this method will always return false. It is not possible + * to programatically set this value to true - that can only be + * done by physically pressing the override switch. + * + * @return The last overide switch status reading + */ + bool getOverrideSwitchStatus(); + + /** + * Clear the override switch status (set it to false). If this + * option is not installed, then this method will have no effect + * (the overide switch status will always be false). + * + */ + void clearOverrideSwitch(); + + /** + * Return the current temperature offset stored on the device. + * This is a value between -50 and +50, specified in tenths of a + * degree in whatever scale (Celcius or Fahrenheit) is in use. + * This offset is applied to the returned temperature reading by the + * device. + * + * @return The current temperature offset in tenths of a degree + */ + int getTemperatureOffset(); + + /** + * Return the current humidity offset stored on the device. This + * is a value between -100 and +100, specified in tenths of a + * percent. This offset is applied to the returned humidity + * reading by the device. + * + * @return The current humidity offset in tenths of a percent + */ + int getHumidityOffset(); + + /** + * Set the stored temperature offset on the device. This is a + * value between -50 and +50, specified in tenths of a degree in + * what ever scale (Celcius or Fahrenheit) is in use. This offset + * is applied to the returned temperature reading by the device. + * + * @param offset Offset in tenths of a degree with a range of -50 to +50 + */ + void setTemperatureOffset(int offset); + + /** + * Set the stored humidity offset on the device. This is a value + * between -100 and +100, specified in tenths of a percent. This + * offset is applied to the returned humidity reading by the + * device. + * + * @param offset Offset in tenths of a percent with a range of -100 to +100 + */ + void setHumidityOffset(int offset); + + /** + * Set the temperature scale used by the device. This driver + * detects this setting automatically and adjusts itself + * accordingly, so this is generally never needed. It is used to + * set the native reporting scale of the temperature either in + * degrees Celcius or Fahrenheit. Its setting will not affect + * the operation of getTemperature(). + * + * @param fahrenheit true to set Fahrenheit, false to set Celcius + */ + void setTemperatureScale(bool fahrenheit); + + /** + * Return a string corresponding the the device's MODBUS slave ID. + * This includes information such as the manufacturer, device + * model number and serial number. + * + * @return string represnting the MODBUS slave ID + */ + std::string getSlaveID(); + + /** + * Set a new MODBUS slave address. This is useful if you have + * multiple HWXPXX devices on a single bus. When this method is + * called, the current temperature scale is re-read so that + * further update() calls can work correctly. + * + * @param addr The new slave address to set + */ + void setSlaveAddress(int addr); + + /** + * Enable or disable debugging output. This primarily enables and + * disables libmodbus debugging output. + * + * @param enable true to enable debugging, false otherwise + */ + void setDebug(bool enable); + + protected: + // input registers + int readInputRegs(INPUT_REGS_T reg, int len, uint16_t *buf); + uint16_t readInputReg(INPUT_REGS_T reg); + + // coils + int readCoils(COIL_REGS_T reg, int numBits, uint8_t *buf); + bool readCoil(COIL_REGS_T reg); + void writeCoil(COIL_REGS_T reg, bool val); + + // holding registers + int readHoldingRegs(HOLDING_REGS_T reg, int len, uint16_t *buf); + uint16_t readHoldingReg(HOLDING_REGS_T reg); + void writeHoldingReg(HOLDING_REGS_T reg, int value); + + // MODBUS context + modbus_t *m_mbContext; + + // is the device reporting in C or F? + bool m_isCelcius; + + private: + bool m_debugging; + + // data + float m_temperature; + float m_humidity; // relative + int m_slider; // optional slider value + bool m_override; // status of override switch + }; +} diff --git a/src/hwxpxx/javaupm_hwxpxx.i b/src/hwxpxx/javaupm_hwxpxx.i new file mode 100644 index 00000000..f6f849bd --- /dev/null +++ b/src/hwxpxx/javaupm_hwxpxx.i @@ -0,0 +1,20 @@ +%module javaupm_hwxpxx +%include "../upm.i" +%include "typemaps.i" + +%{ + #include "hwxpxx.h" +%} + +%include "hwxpxx.h" + +%pragma(java) jniclasscode=%{ + static { + try { + System.loadLibrary("javaupm_hwxpxx"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. \n" + e); + System.exit(1); + } + } +%} diff --git a/src/hwxpxx/jsupm_hwxpxx.i b/src/hwxpxx/jsupm_hwxpxx.i new file mode 100644 index 00000000..26200005 --- /dev/null +++ b/src/hwxpxx/jsupm_hwxpxx.i @@ -0,0 +1,8 @@ +%module jsupm_hwxpxx +%include "../upm.i" +%include "stdint.i" + +%include "hwxpxx.h" +%{ + #include "hwxpxx.h" +%} diff --git a/src/hwxpxx/pyupm_hwxpxx.i b/src/hwxpxx/pyupm_hwxpxx.i new file mode 100644 index 00000000..fcfec090 --- /dev/null +++ b/src/hwxpxx/pyupm_hwxpxx.i @@ -0,0 +1,12 @@ +// Include doxygen-generated documentation +%include "pyupm_doxy2swig.i" +%module pyupm_hwxpxx +%include "../upm.i" +%include "stdint.i" + +%feature("autodoc", "3"); + +%include "hwxpxx.h" +%{ + #include "hwxpxx.h" +%}