From e1c66c351af67784d714ba6cf5fd941a0eddf73c Mon Sep 17 00:00:00 2001 From: Jon Trulson Date: Tue, 19 Jan 2016 17:13:37 -0700 Subject: [PATCH] rhusb: Initial implementation The driver implements support for the Omega RH-USB Humidity Probe with Temperature sensor. It connects via an integrated USB cable, and appears as a serial port. It does not currently work with Edison (as of this date) due to missing ftdi_sio and usbserial kernel support. It was implemented and tested on the Galileo 2. Signed-off-by: Jon Trulson Signed-off-by: Mihai Tudor Panu --- examples/c++/CMakeLists.txt | 1 + examples/c++/rhusb.cxx | 88 +++++++++++++++++ examples/javascript/rhusb.js | 76 +++++++++++++++ examples/python/rhusb.py | 69 ++++++++++++++ src/rhusb/CMakeLists.txt | 6 ++ src/rhusb/javaupm_rhusb.i | 23 +++++ src/rhusb/jsupm_rhusb.i | 8 ++ src/rhusb/pyupm_rhusb.i | 12 +++ src/rhusb/rhusb.cxx | 179 +++++++++++++++++++++++++++++++++++ src/rhusb/rhusb.h | 118 +++++++++++++++++++++++ src/upm.h | 6 ++ 11 files changed, 586 insertions(+) create mode 100644 examples/c++/rhusb.cxx create mode 100644 examples/javascript/rhusb.js create mode 100644 examples/python/rhusb.py create mode 100644 src/rhusb/CMakeLists.txt create mode 100644 src/rhusb/javaupm_rhusb.i create mode 100644 src/rhusb/jsupm_rhusb.i create mode 100644 src/rhusb/pyupm_rhusb.i create mode 100644 src/rhusb/rhusb.cxx create mode 100644 src/rhusb/rhusb.h diff --git a/examples/c++/CMakeLists.txt b/examples/c++/CMakeLists.txt index 7b7f50cf..3fbfad22 100644 --- a/examples/c++/CMakeLists.txt +++ b/examples/c++/CMakeLists.txt @@ -238,6 +238,7 @@ if (MODBUS_FOUND) add_example (hwxpxx) endif() add_example (hdxxvxta) +add_example (rhusb) # These are special cases where you specify example binary, source file and module(s) include_directories (${PROJECT_SOURCE_DIR}/src) diff --git a/examples/c++/rhusb.cxx b/examples/c++/rhusb.cxx new file mode 100644 index 00000000..20f12ed2 --- /dev/null +++ b/examples/c++/rhusb.cxx @@ -0,0 +1,88 @@ +/* + * 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 "rhusb.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 RHUSB instance on defaultDev + upm::RHUSB sensor(defaultDev); + + // output the firmware ID + cout << "Firmware ID: " << sensor.getFirmwareID() << 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 << endl; + + sleep(1); + } + + + cout << "Exiting..." << endl; + +//! [Interesting] + + return 0; +} diff --git a/examples/javascript/rhusb.js b/examples/javascript/rhusb.js new file mode 100644 index 00000000..bbd5a16f --- /dev/null +++ b/examples/javascript/rhusb.js @@ -0,0 +1,76 @@ +/*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_rhusb'); + + +/************** 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 RHUSB instance on defaultDev +var sensor = new sensorObj.RHUSB(defaultDev); + +// output the firmware ID +console.log("Firmware ID:", sensor.getFirmwareID()); +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(""); + +}, 1000); + + +process.on('SIGINT', function() +{ + sensor = null; + sensorObj.cleanUp(); + sensorObj = null; + console.log("Exiting..."); + process.exit(0); +}); diff --git a/examples/python/rhusb.py b/examples/python/rhusb.py new file mode 100644 index 00000000..70198e09 --- /dev/null +++ b/examples/python/rhusb.py @@ -0,0 +1,69 @@ +#!/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_rhusb 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 RHUSB instance on defaultDev +sensor = sensorObj.RHUSB(defaultDev) + +# output the firmware ID +print "Firmware ID:", sensor.getFirmwareID() +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 + time.sleep(1) diff --git a/src/rhusb/CMakeLists.txt b/src/rhusb/CMakeLists.txt new file mode 100644 index 00000000..64f2aff0 --- /dev/null +++ b/src/rhusb/CMakeLists.txt @@ -0,0 +1,6 @@ +set (libname "rhusb") +set (libdescription "upm module for the Omega RH-USB sensor") +set (module_src ${libname}.cxx) +set (module_h ${libname}.h) +upm_module_init() + diff --git a/src/rhusb/javaupm_rhusb.i b/src/rhusb/javaupm_rhusb.i new file mode 100644 index 00000000..e72f423d --- /dev/null +++ b/src/rhusb/javaupm_rhusb.i @@ -0,0 +1,23 @@ +%module javaupm_rhusb +%include "../upm.i" +%include "typemaps.i" +%include "cpointer.i" +%include "arrays_java.i"; +%include "../java_buffer.i" + +%{ + #include "rhusb.h" +%} + +%include "rhusb.h" + +%pragma(java) jniclasscode=%{ + static { + try { + System.loadLibrary("javaupm_rhusb"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. \n" + e); + System.exit(1); + } + } +%} diff --git a/src/rhusb/jsupm_rhusb.i b/src/rhusb/jsupm_rhusb.i new file mode 100644 index 00000000..3786cb7a --- /dev/null +++ b/src/rhusb/jsupm_rhusb.i @@ -0,0 +1,8 @@ +%module jsupm_rhusb +%include "../upm.i" +%include "stdint.i" + +%include "rhusb.h" +%{ + #include "rhusb.h" +%} diff --git a/src/rhusb/pyupm_rhusb.i b/src/rhusb/pyupm_rhusb.i new file mode 100644 index 00000000..83322af3 --- /dev/null +++ b/src/rhusb/pyupm_rhusb.i @@ -0,0 +1,12 @@ +// Include doxygen-generated documentation +%include "pyupm_doxy2swig.i" +%module pyupm_rhusb +%include "../upm.i" +%include "stdint.i" + +%feature("autodoc", "3"); + +%include "rhusb.h" +%{ + #include "rhusb.h" +%} diff --git a/src/rhusb/rhusb.cxx b/src/rhusb/rhusb.cxx new file mode 100644 index 00000000..6b6b88ca --- /dev/null +++ b/src/rhusb/rhusb.cxx @@ -0,0 +1,179 @@ +/* + * 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 + +#include "rhusb.h" + +using namespace upm; +using namespace std; + +static const int maxBuffer = 1024; +// baud rate is always 9600 +static const int baudRate = 9600; + +// conversion from celcius to fahrenheit + +static float c2f(float c) +{ + return (c * (9.0 / 5.0) + 32.0); +} + +RHUSB::RHUSB(std::string device) : + m_uart(device) +{ + m_uart.setBaudRate(baudRate); + + m_temperature = 0.0; + m_humidity = 0.0; +} + +RHUSB::~RHUSB() +{ +} + +void RHUSB::update() +{ + char *endptr; + float value; + + // first get the temp in C + string resp = sendCommand("C"); + + // convert + value = strtof(resp.c_str(), &endptr); + + // check for conversion failure + if (value == 0.0 && resp.c_str() == endptr) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": strtof() temperature conversion failed"); + } + m_temperature = value; + + // now humidity + resp = sendCommand("H"); + + // convert + value = strtof(resp.c_str(), &endptr); + + // check for conversion failure + if (value == 0.0 && resp.c_str() == endptr) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": strtof() humidity conversion failed"); + } + m_humidity = value; +} + +float RHUSB::getTemperature(bool fahrenheit) +{ + if (fahrenheit) + return c2f(m_temperature); + else + return m_temperature; +} + +float RHUSB::getHumidity() +{ + return m_humidity; +} + +bool RHUSB::dataAvailable(unsigned int millis) +{ + return m_uart.dataAvailable(millis); +} + +string RHUSB::readStr(int len) +{ + return m_uart.readStr(len); +} + +int RHUSB::writeStr(string data) +{ + m_uart.flush(); + return m_uart.writeStr(data); +} + +string RHUSB::sendCommand(string cmd) +{ + // make sure we got a command + if (cmd.empty()) + { + throw std::invalid_argument(std::string(__FUNCTION__) + + ": cmd is empty!"); + return ""; + } + + // make sure string is CR terminated + if (cmd.at(cmd.size() - 1) != '\r') + cmd.append("\r"); + + writeStr(cmd); + + string resp; + // I see random timeouts with wait values below 250ms + while (dataAvailable(250)) + { + resp += readStr(maxBuffer); + } + + if (resp.empty()) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": timed out waiting for response"); + return ""; + } + + // check that the last character is the prompt + if (resp.at(resp.size() - 1) != '>') + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": read from device corrupted"); + return ""; + } + + // delete the last 3 characters, which should be '\r\n>' + resp.erase(resp.size() - 3, 3); + + return resp; +} + +string RHUSB::getFirmwareID() +{ + string resp = sendCommand("ENQ"); + + // For readability, replace the intermediate \r\n with a space if found. + + size_t pos = resp.find("\r\n"); + + if (pos != string::npos) + resp.replace(pos, 2, " "); + + return resp; +} diff --git a/src/rhusb/rhusb.h b/src/rhusb/rhusb.h new file mode 100644 index 00000000..4fd45485 --- /dev/null +++ b/src/rhusb/rhusb.h @@ -0,0 +1,118 @@ +/* + * 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 Omega RH-USB Temperature and Humidity Sensor + * @defgroup rhusb libupm-rhusb + * @ingroup uart temp + */ + + /** + * @library rhusb + * @sensor rhusb + * @comname UPM API for the Omega RH-USB Temperature and Humidity Sensor + * @type temp + * @man omega + * @con uart + * @web http://www.omega.com/pptst/RH-USB.html + * + * @brief UPM API for the Omega RH-USB Temperature and Humidity Sensor + * + * This module implements support for the Omega RH-USB Temperature + * and Humidity Sensor Probe. It connects via an integrated USB + * cable, and is accessed via a serial port. It is suitable for + * wall or duct mounting. + * + * @snippet rhusb.cxx Interesting + */ + + class RHUSB { + public: + /** + * RHUSB constructor + * + * @param device Path to the serial device + */ + RHUSB(std::string device); + + /** + * RHUSB Destructor + */ + ~RHUSB(); + + /** + * Read current values from the sensor and update internal stored + * values. This method must be called prior to querying any + * values, such as temperature or humidity. + */ + void update(); + + /** + * Get the current temperature. update() must have been called + * prior to calling this method. + * + * @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 firmware identification string. + * + * @return The firmware identification + */ + std::string getFirmwareID(); + + + protected: + // serial i/o support + bool dataAvailable(unsigned int millis); + int writeStr(std::string data); + std::string readStr(int len); + std::string sendCommand(std::string cmd); + + mraa::Uart m_uart; + + private: + // data + float m_temperature; + float m_humidity; + }; +} diff --git a/src/upm.h b/src/upm.h index 4c2cbc0c..eaecb3fd 100644 --- a/src/upm.h +++ b/src/upm.h @@ -326,6 +326,12 @@ * @ingroup byman */ +/** + * @brief Omega + * @defgroup omega Omega + * @ingroup byman + */ + /** * @brief SeeedStudio - Grove Sensors * @defgroup seeed SeeedStudio