diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index a1fcebc9..29e2466b 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -134,6 +134,7 @@ add_example (mma7660) add_example (buzzer) add_example (ppd42ns) add_example (guvas12d) +add_example (otp538u) # Custom examples add_custom_example (nmea_gps_i2c-example-c nmea_gps_i2c.c nmea_gps) diff --git a/examples/c/otp538u.c b/examples/c/otp538u.c new file mode 100644 index 00000000..339a57b4 --- /dev/null +++ b/examples/c/otp538u.c @@ -0,0 +1,81 @@ +/* + * 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 + + +bool shouldRun = true; + +// analog voltage, usually 3.3 or 5.0 +#define OTP538U_AREF 5.0 + +void sig_handler(int signo) +{ + if (signo == SIGINT) + shouldRun = false; +} + +int main() +{ + signal(SIGINT, sig_handler); + +//! [Interesting] + + // Instantiate a OTP538U on analog pins A0 and A1 + // A0 is used for the Ambient Temperature and A1 is used for the + // Object temperature. + otp538u_context temps = otp538u_init(0, 1, OTP538U_AREF); + + // enable debugging if you would like + // otp538u_set_debug(temps, true); + + // Output ambient and object temperatures + while (shouldRun) + { + float ambient = 0, object = 0; + + if (otp538u_get_ambient_temperature(temps, &ambient)) + printf("otp538u_get_ambient_temperature() failed\n"); + else if (otp538u_get_object_temperature(temps, &object)) + printf("otp538u_get_object_temperature() failed\n"); + else + printf("Ambient temp: %f C, Object temp: %f C\n", + ambient, object); + + printf("\n"); + upm_delay(1); + } + + printf("Exiting\n"); + + otp538u_close(temps); + +//! [Interesting] + return 0; +} diff --git a/src/otp538u/CMakeLists.txt b/src/otp538u/CMakeLists.txt index 0fbae056..fbb84967 100644 --- a/src/otp538u/CMakeLists.txt +++ b/src/otp538u/CMakeLists.txt @@ -1,5 +1,9 @@ -set (libname "otp538u") -set (libdescription "Otp538u IR temperature sensor") -set (module_src ${libname}.cxx) -set (module_hpp ${libname}.hpp) -upm_module_init() +upm_mixed_module_init (NAME otp538u + DESCRIPTION "OTP538U Analog IR temperature sensor" + C_HDR otp538u.h + C_SRC otp538u.c + CPP_HDR otp538u.hpp + CPP_SRC otp538u.cxx + FTI_SRC otp538u_fti.c + CPP_WRAPS_C + REQUIRES mraa) diff --git a/src/otp538u/otp538u.c b/src/otp538u/otp538u.c new file mode 100644 index 00000000..acc83415 --- /dev/null +++ b/src/otp538u/otp538u.c @@ -0,0 +1,374 @@ +/* + * 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 "thermopile_vt_table.h" +#include "thermister_rt_table.h" + + +// To save memory space, we disable debugging on non-linux platforms +// by default (Zephyr). For other systems, enable as desired/possible. +#if defined(UPM_PLATFORM_LINUX) +# define OTP538U_DEBUG_ENABLED +#endif // UPM_PLATFORM_LINUX + +otp538u_context otp538u_init(int pinA, int pinO, float aref) +{ + otp538u_context dev = + (otp538u_context)malloc(sizeof(struct _otp538u_context)); + + if (!dev) + return NULL; + + memset((void *)dev, 0, sizeof(struct _otp538u_context)); + + // make sure MRAA is initialized + int mraa_rv; + if ((mraa_rv = mraa_init()) != MRAA_SUCCESS) + { + printf("%s: mraa_init() failed (%d).\n", __FUNCTION__, mraa_rv); + otp538u_close(dev); + return NULL; + } + + // initialize the MRAA contexts + + if (!(dev->aioA = mraa_aio_init(pinA))) + { + printf("%s: mraa_aio_init(pinA) failed.\n", __FUNCTION__); + otp538u_close(dev); + + return NULL; + } + + if (!(dev->aioO = mraa_aio_init(pinO))) + { + printf("%s: mraa_aio_init(pinO) failed.\n", __FUNCTION__); + otp538u_close(dev); + + return NULL; + } + + // for subplatforms like the Arduino 101 with Firmata, we need to + // limit ADC resolution to 10b currently. For this sensor + // unfortunately, this means readings will be less accurate. This + // sensor really does need to measure with about 1mV accuracy. + const int adcHighRes = 4095; + const int adcLowRes = 1023; + + bool isSubplatform = false; + + dev->debug = false; + + if (pinA >= 512 || pinO >= 512) + isSubplatform = true; + + // this is the internal voltage reference on the Grove IR temp + // sensor module for the thermistor. + + dev->internalVRef = 2.5; + + // analog reference in use + dev->aref = aref; + + // This is the value of the output resistor of the Grove IR + // temp sensor's SIG2 output (ambient) + dev->vResistance = 2000000; // 2M ohms + + // This was the default offset voltage in the seeedstudio code. You + // can adjust as neccessary depending on your calibration. + dev->offsetVoltage = 0.014; + + // We need around 1mV resolution (preferred), so use 12 bit + // resolution (4096) if we can. + // + // This logic is over complicated due to the fact that it is + // currently difficult to determine exactly what the capabilities of + // the platform (sub or otherwise) actually are. So for + // subplatforms, we always limit to 1024. Otherwise, we try 12b if + // the mraa_adc_raw_bits() says we can, though this isn't + // particularly accurate either, as it reports that the G2 can do + // 12b, when in reality it can not. We are just lucky that it works + // anyway (ie: will give 12b resolution, though underneath it's just + // scaling the real 10b value.). Sigh. But trying 12b resolution + // on the 101 via firmata will definitely break things, so don't + // even try until whatever the problem it has with 12b is fixed. + if (isSubplatform) + { + dev->adcResolution = adcLowRes; // 10b + } + else + { + if (mraa_adc_raw_bits() == 12) + dev->adcResolution = adcHighRes; // 12b + else + dev->adcResolution = adcLowRes; // 10b + } + + // notify the user + if (dev->adcResolution == adcLowRes) + printf("Using 10 bit ADC resolution. Values will be less accurate.\n"); + + // enable 12 bit resolution, if we can + if (dev->adcResolution == adcHighRes) + mraa_aio_set_bit(dev->aioA, 12); + + if (dev->adcResolution == adcHighRes) + mraa_aio_set_bit(dev->aioO, 12); + + if (isSubplatform) + { + // The first analog read always seems to return 0 on the 101 + // with firmata, so just do a couple of reads here and discard + // them. Then sleep for half a second. THIS IS A HACK. The + // real problem should be fixed elsewhere (Firmata?). + mraa_aio_read(dev->aioA); + mraa_aio_read(dev->aioO); + + upm_delay_ms(500); + } + + return dev; +} + +void otp538u_close(otp538u_context dev) +{ + assert(dev != NULL); + + if (dev->aioA) + mraa_aio_close(dev->aioA); + if (dev->aioO) + mraa_aio_close(dev->aioO); + + free(dev); +} + +upm_result_t otp538u_get_ambient_temperature(const otp538u_context dev, + float *temperature) +{ + assert(dev != NULL); + + const int samples = 5; + int val = 0; + float temp = 0; + float res = 0; + + for (int i=0; iaioA); + if (val == -1) + { + printf("%s: mraa_aio_read() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + temp += (float)val; + upm_delay_ms(10); + } + temp = temp / samples; + + float volts = temp * dev->aref / dev->adcResolution; + +#if defined(OTP538U_DEBUG_ENABLED) + if (dev->debug) + { + printf("\tAMB sample %f aref %f volts %f\n", temp, dev->aref, + volts); + } +#endif // OTP538U_DEBUG_ENABLED + + // compute the resistance of the thermistor + res = dev->vResistance * volts / (dev->internalVRef - volts); + +#if defined(OTP538U_DEBUG_ENABLED) + if (dev->debug) + { + printf("\tAMB computed resistance: %f\n", res); + } +#endif // OTP538U_DEBUG_ENABLED + + // look it up in the thermistor (RT) resistence/temperature table + int rawslot; + int j; + for (j=0; j= otp538u_rt_table_max) + { + printf("%s: Ambient temperature out of range (high)\n", __FUNCTION__); + return UPM_ERROR_OUT_OF_RANGE; + } + + // we need to compensate for the fact that we are supporting + // temperature values less than 0 (-20C), so adjust correspondingly + // so that we obtain the correct temperature 'slot'. This will be + // our base temperature. + int slot = rawslot - 20; + + // too cold + if (slot < 0) + { + printf("%s: Ambient temperature out of range (low)\n", __FUNCTION__); + return UPM_ERROR_OUT_OF_RANGE; + } + + // now compute the ambient temperature + float ambientTemp = slot - 1 + + (otp538u_rt_table[rawslot - 1]-res) / (otp538u_rt_table[rawslot - 1] - + otp538u_rt_table[rawslot]); + + *temperature = ambientTemp; + return UPM_SUCCESS; +} + +upm_result_t otp538u_get_object_temperature(const otp538u_context dev, + float *temperature) +{ + assert(dev != NULL); + + const int samples = 5; + const float reference_vol = 0.5; // what is this value? (from seeedstudio) + const float tempIncrement = 10.0; + int val = 0; + float temp = 0; + + float ambTemp = 0.0; + if (otp538u_get_ambient_temperature(dev, &ambTemp)) + return UPM_ERROR_OPERATION_FAILED; + + for (int i=0; iaioO); + if (val == -1) + { + printf("%s: mraa_aio_read() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + temp += val; + upm_delay_ms(10); + } + + temp = temp / samples; + +#if defined(OTP538U_DEBUG_ENABLED) + if (dev->debug) + printf("\tOBJ sample %f ", temp); +#endif // OTP538U_DEBUG_ENABLED + + float volts = temp * dev->aref / dev->adcResolution; + +#if defined(OTP538U_DEBUG_ENABLED) + if (dev->debug) + printf("VOLTS: %f ", volts); +#endif // OTP538U_DEBUG_ENABLED + + float sensorVolts = volts - (reference_vol + dev->offsetVoltage); + +#if defined(OTP538U_DEBUG_ENABLED) + if (dev->debug) + printf("Sensor Voltage (computed): %f\n", sensorVolts); +#endif // OTP538U_DEBUG_ENABLED + + // search the VT (voltage/temperature) table to find the object + // temperature. + int slot; + // add +2 to compensate for the -20C and -10C slots below zero + int voltOffset = (int)(ambTemp / 10) + 1 + 2; + float voltage = sensorVolts * 10.0; + for (slot=0; slot<(otp538u_vt_table_max - 1); slot++) + { + if ( (voltage > otp538u_vt_table[slot][voltOffset]) && + (voltage < otp538u_vt_table[slot+1][voltOffset]) ) + { + break; + } + } + + if (slot >= (otp538u_vt_table_max - 1)) + { + printf("%s: Object temperature out of range (high)\n", __FUNCTION__); + return UPM_ERROR_OUT_OF_RANGE; + } + + float objTemp = ((float)tempIncrement * voltage) / + ( otp538u_vt_table[slot + 1][voltOffset] - + otp538u_vt_table[slot][voltOffset] ); + +#if defined(OTP538U_DEBUG_ENABLED) + if (dev->debug) + { + printf("\tVoltage (%f): TABLE VALUE [%d][%d] = %f\n", voltage, + slot, voltOffset, otp538u_vt_table[slot][voltOffset]); + } +#endif // OTP538U_DEBUG_ENABLED + + *temperature = ambTemp + objTemp; + return UPM_SUCCESS; +} + +void otp538u_set_voltage_offset(const otp538u_context dev, float offset) +{ + assert(dev != NULL); + + dev->offsetVoltage = offset; +} + +void otp538u_set_output_resistence(const otp538u_context dev, + int resistance) +{ + assert(dev != NULL); + + dev->vResistance = resistance; +} + +void otp538u_set_ivref(const otp538u_context dev, float vref) +{ + assert(dev != NULL); + + dev->internalVRef = vref; +} + +void otp538u_set_debug(const otp538u_context dev, bool enable) +{ + assert(dev != NULL); + + dev->debug = enable; + +#if !defined(UPM_PLATFORM_LINUX) && !defined(OTP538U_DEBUG_ENABLED) + if (enable) + printf("%s: Debugging not enabled at compilation time.\n", + __FUNCTION__); +#endif // !UPM_PLATFORM_LINUX && !OTP538U_DEBUG_ENABLED +} diff --git a/src/otp538u/otp538u.cxx b/src/otp538u/otp538u.cxx index 26966c39..5bf93916 100644 --- a/src/otp538u/otp538u.cxx +++ b/src/otp538u/otp538u.cxx @@ -1,6 +1,6 @@ /* * Author: Jon Trulson - * Copyright (c) 2015 Intel Corporation. + * Copyright (c) 2015-2016 Intel Corporation. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -28,258 +28,40 @@ #include "otp538u.hpp" -#include "thermopile_vt_table.hpp" -#include "thermister_rt_table.hpp" - using namespace upm; using namespace std; -OTP538U::OTP538U(int pinA, int pinO, float aref) +OTP538U::OTP538U(int pinA, int pinO, float aref) : + m_otp538u(otp538u_init(pinA, pinO, aref)) { - const int adcHighRes = 4095; - const int adcLowRes = 1023; - - // for subplatforms like the Arduino 101, we need to limit ADC - // resolution to 10b currently. For this sensor unfortunately, this - // means readings will be less accurate. This sensor really does - // need to measure with about 1mV accuracy. - bool isSubplatform = false; - - m_debug = false; - - if (pinA >= 512 || pinO >= 512) - isSubplatform = true; - - // this is the internal voltage reference on the Grove IR temp - // sensor module for the thermistor. - - m_internalVRef = 2.5; - - // analog reference in use - m_aref = aref; - - // This is the value of the output resistor of the Grove IR - // temp sensor's SIG2 output (ambient) - m_vResistance = 2000000; // 2M ohms - - // This was the default offset voltage in the seeedstudio code. You - // can adjust as neccessary depending on your calibration. - m_offsetVoltage = 0.014; - - // We need around 1mV resolution (preferred), so use 12 bit - // resolution (4096) if we can. - // - // This logic is over complicated due to the fact that it is - // currently difficult to determine exactly what the capabilities of - // the platform (sub or otherwise) actually are. So for - // subplatforms, we always limit to 1024. Otherwise, we try 12b if - // the mraa_adc_raw_bits() says we can, though this isn't - // particularly accurate either, as it reports that the G2 can do - // 12b, when in reality it can not. We are just lucky that it works - // anyway (ie: will give 12b resolution, though underneath it's just - // scaling the real 10b value.). Sigh. But trying 12b resolution - // on the 101 via firmata will definitely break things, so don't - // even try until whatever the problem it has with 12b is fixed. - if (isSubplatform) - { - m_adcResolution = adcLowRes; // 10b - } - else - { - if (mraa_adc_raw_bits() == 12) - m_adcResolution = adcHighRes; // 12b - else - m_adcResolution = adcLowRes; // 10b - } - - // notify the user - if (m_adcResolution == adcLowRes) - cerr << "Using 10 bit ADC resolution. Values will be less accurate." - << endl; - - - if ( !(m_aioA = mraa_aio_init(pinA)) ) - { - throw std::invalid_argument(std::string(__FUNCTION__) + - ": mraa_gpio_init(pinA) failed"); - return; - } - - // enable 12 bit resolution, if we can - if (m_adcResolution == adcHighRes) - mraa_aio_set_bit(m_aioA, 12); - - if ( !(m_aioO = mraa_aio_init(pinO)) ) - { - throw std::invalid_argument(std::string(__FUNCTION__) + - ": mraa_gpio_init(pinO) failed"); - return; - } - - - // enable 12 bit resolution, if we can - if (m_adcResolution == adcHighRes) - mraa_aio_set_bit(m_aioO, 12); - - if (isSubplatform) - { - // The first analog read always seems to return 0 on the 101 - // with firmata, so just do a couple of reads here and discard - // them. Then sleep for half a second. THIS IS A HACK. The - // real problem should be fixed elsewhere (Firmata?). - mraa_aio_read(m_aioA); - mraa_aio_read(m_aioO); - - usleep(500000); - } + if (!m_otp538u) + throw std::runtime_error(std::string(__FUNCTION__) + + ": otp538u_init() failed"); } OTP538U::~OTP538U() { - mraa_aio_close(m_aioA); - mraa_aio_close(m_aioO); + otp538u_close(m_otp538u); } float OTP538U::ambientTemperature() { - const int samples = 5; - int val = 0; - float temp = 0; - float res = 0; + float temp = 0; - for (int i=0; i= otp538u_rt_table_max) - { - throw std::out_of_range(std::string(__FUNCTION__) + - ": ambient temperature out of range (high)."); - return 0; - } - - // we need to compensate for the fact that we are supporting - // temperature values less than 0 (-20C), so adjust correspondingly - // so that we obtain the correct temperature 'slot'. This will be - // our base temperature. - int slot = rawslot - 20; - - // too cold - if (slot < 0) - { - throw std::out_of_range(std::string(__FUNCTION__) + - ": ambient temperature out of range (low)."); - return 0; - } - - // now compute the ambient temperature - float ambientTemp = slot - 1 + - (otp538u_rt_table[rawslot - 1]-res) / (otp538u_rt_table[rawslot - 1] - - otp538u_rt_table[rawslot]); - - return ambientTemp; + return temp; } float OTP538U::objectTemperature() { - const int samples = 5; - const float reference_vol= 0.5; // what is this value? (from seeedstudio) - const float tempIncrement=10; - int val = 0; - float temp = 0; - float ambTemp = ambientTemperature(); - - for (int i=0; i otp538u_vt_table[slot][voltOffset]) && - (voltage < otp538u_vt_table[slot+1][voltOffset]) ) - { - break; - } - } - - if (slot >= (otp538u_vt_table_max - 1)) - { - throw std::out_of_range(std::string(__FUNCTION__) + - ": object temperature out of range."); - return 0; - } - - float objTemp = (float(tempIncrement) * voltage) / - ( otp538u_vt_table[slot + 1][voltOffset] - - otp538u_vt_table[slot][voltOffset] ); - - if (m_debug) - { - cerr << "\tVoltage (" << voltage << "): TABLE VALUE [" << slot << "][" << - voltOffset << "] = " << otp538u_vt_table[slot][voltOffset] << endl; - } - - return (ambTemp + objTemp); + return temp; } diff --git a/src/otp538u/otp538u.h b/src/otp538u/otp538u.h new file mode 100644 index 00000000..92a1d71a --- /dev/null +++ b/src/otp538u/otp538u.h @@ -0,0 +1,153 @@ +/* + * 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 +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @file otp538u.h + * @library otp538u + * @brief C API for the OTP538U IR Temperature Sensor + * + * @include otp538u.c + */ + + /** + * device context + */ + typedef struct _otp538u_context + { + mraa_aio_context aioA; + mraa_aio_context aioO; + + bool debug; + float internalVRef; + float aref; + int vResistance; + float offsetVoltage; + int adcResolution; + } *otp538u_context; + + + /** + * OTP538U constructor + * + * @param pinA Analog pin to use for the ambient temperature + * @param pinO Analog pin to use for the object temperature + * @param aref Analog reference voltage + * @return intialized context, or NULL if error + */ + otp538u_context otp538u_init(int pinA, int pinO, float aref); + + /** + * OTP538U destructor + * + * @param dev Device context + */ + void otp538u_close(otp538u_context dev); + + /** + * Gets the ambient temperature in Celsius + * + * @param dev Device context + * @param temp Ambient temperature + * @return UPM status + */ + upm_result_t otp538u_get_ambient_temperature(const otp538u_context dev, + float *temperature); + + /** + * Gets the object temperature in Celsius + * + * @param dev Device context + * @param temp Object temperature + * @return UPM status + */ + upm_result_t otp538u_get_object_temperature(const otp538u_context dev, + float *temperature); + + /** + * Sets the offset voltage + * + * The Seeed Studio wiki gives an example of calibrating the sensor + * and calculating the offset voltage to apply. Currently, the + * default value is set, but you can use the function to set one + * of your own. + * + * @param dev Device context + * @param vOffset Desired offset voltage + */ + void otp538u_set_voltage_offset(const otp538u_context dev, float offset); + + /** + * Sets the output resistance value + * + * The Seeed Studio wiki example uses a value of 2,000,000 in one of + * the equations used to calculate voltage. The value is the + * resistance of a resistor they use in the output stage of their + * SIG2 output. This was 'decoded' by looking at the EAGLE* files + * containing their schematics for this device. + * + * @param dev Device context + * @param outResistance Value of the output resistor; default is 2M Ohm + */ + void otp538u_set_output_resistence(const otp538u_context dev, + int resistance); + + /** + * Sets the reference voltage of the internal Seeed Studio voltage + * regulator on the sensor board. + * + * The Seeed Studio wiki example uses a value of 2.5 in one of the + * equations used to calculate the resistance of the ambient + * thermistor. The value is the voltage of an internal voltage + * regulator used on the sensor board. This was 'decoded' by + * looking at the EAGLE files containing their schematics for this + * device. + * + * @param dev Device context + * @param vref Reference voltage of the internal sensor; default + * is 2.5 + */ + void otp538u_set_ivref(const otp538u_context dev, float vref); + + /** + * Enable debugging output (linux platforms only). + * + * @param dev Device context + * @param true to enable some debug output, false otherwise + */ + void otp538u_set_debug(const otp538u_context dev, bool enable); + + +#ifdef __cplusplus +} +#endif diff --git a/src/otp538u/otp538u.hpp b/src/otp538u/otp538u.hpp index 82b6094e..1572bdc5 100644 --- a/src/otp538u/otp538u.hpp +++ b/src/otp538u/otp538u.hpp @@ -1,6 +1,6 @@ /* * Author: Jon Trulson - * Copyright (c) 2015 Intel Corporation. + * Copyright (c) 2015-2016 Intel Corporation. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -24,144 +24,148 @@ #pragma once #include -#include +#include namespace upm { - /** - * @brief OTP538U IR Temperature Sensor library - * @defgroup otp538u libupm-otp538u - * @ingroup seeed analog light hak - */ - /** - * @library otp538u - * @sensor otp538u - * @comname OTP538U IR Temperature Sensor - * @altname Grove IR Temperature Sensor - * @type light - * @man generic - * @con analog - * @kit hak - * - * @brief API for the OTP538U IR Temperature Sensor - * - * UPM module for the OTP538U IR temperature sensor - * - * This module was tested with the Grove IR non-contact temperature - * sensor. - * - * The sensor provides 2 analog outputs: one for the thermistor - * that measures the ambient temperature, and the other for the thermopile - * that measures the object temperature. - * - * Much of the code depends on analyzing Seeed Studio* examples - * and the circuit design. As a result, there are several 'magic' - * numbers derived from their circuit design. These values are used - * by default. - * - * The tables used came from the "538U VT - * Table__20_200(v1.3).pdf" and "538RT_table.pdf" datasheets. - * - * These tables assume the object to be measured is 9 cm (3.54 - * inches) from the sensor. - * - * This sensor will not work at 3.3v on the Edsion or the Galileo 2. - * It works fine on both systems at 5v. It will work at 3.3v on the - * Arduino 101 (tested via firmata subplatform on edison). - * - * @image html otp538u.jpg - * @snippet otp538u.cxx Interesting - */ - class OTP538U { - public: /** - * OTP538U constructor - * - * @param pinA Analog pin to use for the ambient temperature - * @param pinO Analog pin to use for the object temperature - * @param aref Analog reference voltage; default is 5.0 V + * @brief OTP538U IR Temperature Sensor library + * @defgroup otp538u libupm-otp538u + * @ingroup seeed analog light hak */ - OTP538U(int pinA, int pinO, float aref = 5.0); - /** - * OTP538U destructor + * @library otp538u + * @sensor otp538u + * @comname OTP538U IR Temperature Sensor + * @altname Grove IR Temperature Sensor + * @type light + * @man generic + * @con analog + * @kit hak + * + * @brief API for the OTP538U IR Temperature Sensor + * + * UPM module for the OTP538U IR temperature sensor + * + * This module was tested with the Grove IR non-contact temperature + * sensor. + * + * The sensor provides 2 analog outputs: one for the thermistor + * that measures the ambient temperature, and the other for the thermopile + * that measures the object temperature. + * + * Much of the code depends on analyzing Seeed Studio* examples + * and the circuit design. As a result, there are several 'magic' + * numbers derived from their circuit design. These values are used + * by default. + * + * The tables used came from the "538U VT + * Table__20_200(v1.3).pdf" and "538RT_table.pdf" datasheets. + * + * These tables assume the object to be measured is 9 cm (3.54 + * inches) from the sensor. + * + * This sensor will not work at 3.3v on the Edsion or the Galileo 2. + * It works fine on both systems at 5v. It will work at 3.3v on the + * Arduino 101 (tested via firmata subplatform on edison). + * + * @image html otp538u.jpg + * @snippet otp538u.cxx Interesting */ - ~OTP538U(); + class OTP538U { + public: + /** + * OTP538U constructor + * + * @param pinA Analog pin to use for the ambient temperature + * @param pinO Analog pin to use for the object temperature + * @param aref Analog reference voltage; default is 5.0 V + */ + OTP538U(int pinA, int pinO, float aref = 5.0); - /** - * Gets the ambient temperature in Celsius - * - * @return Ambient temperature - */ - float ambientTemperature(); + /** + * OTP538U destructor + */ + ~OTP538U(); - /** - * Gets the object temperature in Celsius - * - * @return Object temperature - */ - float objectTemperature(); + /** + * Gets the ambient temperature in Celsius + * + * @return Ambient temperature + */ + float ambientTemperature(); - /** - * Sets the offset voltage - * - * The Seeed Studio wiki gives an example of calibrating the sensor - * and calculating the offset voltage to apply. Currently, the - * default value is set, but you can use the function to set one - * of your own. - * - * @param vOffset Desired offset voltage - */ - void setVoltageOffset(float vOffset) { m_offsetVoltage = vOffset; }; + /** + * Gets the object temperature in Celsius + * + * @return Object temperature + */ + float objectTemperature(); - /** - * Sets the output resistance value - * - * The Seeed Studio wiki example uses a value of 2,000,000 in one of - * the equations used to calculate voltage. The value is the - * resistance of a resistor they use in the output stage of their - * SIG2 output. This was 'decoded' by looking at the EAGLE* files - * containing their schematics for this device. - * - * @param outResistance Value of the output resistor; default is 2M Ohm - */ - void setOutputResistence(int outResistance) { - m_vResistance = outResistance; }; + /** + * Sets the offset voltage + * + * The Seeed Studio wiki gives an example of calibrating the sensor + * and calculating the offset voltage to apply. Currently, the + * default value is set, but you can use the function to set one + * of your own. + * + * @param vOffset Desired offset voltage + */ + void setVoltageOffset(float vOffset) + { + otp538u_set_voltage_offset(m_otp538u, vOffset); + }; - /** - * Sets the reference voltage of the internal Seeed Studio voltage - * regulator on the sensor board. - * - * The Seeed Studio wiki example uses a value of 2.5 in one of the - * equations used to calculate the resistance of the ambient - * thermistor. The value is the voltage of an internal voltage - * regulator used on the sensor board. This was 'decoded' by - * looking at the EAGLE files containing their schematics for this - * device. - * - * @param vref Reference voltage of the internal sensor; default - * is 2.5 - */ - void setVRef(float vref) { m_internalVRef = vref; }; + /** + * Sets the output resistance value + * + * The Seeed Studio wiki example uses a value of 2,000,000 in one of + * the equations used to calculate voltage. The value is the + * resistance of a resistor they use in the output stage of their + * SIG2 output. This was 'decoded' by looking at the EAGLE* files + * containing their schematics for this device. + * + * @param outResistance Value of the output resistor; default is 2M Ohm + */ + void setOutputResistence(int outResistance) + { + otp538u_set_output_resistence(m_otp538u, outResistance); + }; - /** - * Enable debugging output. - * - * @param true to enable some debug output, false otherwise - */ - void setDebug(bool enable) { m_debug = enable; }; + /** + * Sets the reference voltage of the internal Seeed Studio voltage + * regulator on the sensor board. + * + * The Seeed Studio wiki example uses a value of 2.5 in one of the + * equations used to calculate the resistance of the ambient + * thermistor. The value is the voltage of an internal voltage + * regulator used on the sensor board. This was 'decoded' by + * looking at the EAGLE files containing their schematics for this + * device. + * + * @param vref Reference voltage of the internal sensor; default + * is 2.5 + */ + void setVRef(float vref) + { + otp538u_set_ivref(m_otp538u, vref); + }; + + /** + * Enable debugging output. + * + * @param true to enable some debug output, false otherwise + */ + void setDebug(bool enable) + { + otp538u_set_debug(m_otp538u, enable); + }; - private: - bool m_debug; - float m_internalVRef; - float m_aref; - int m_vResistance; - float m_offsetVoltage; - int m_adcResolution; - mraa_aio_context m_aioA; - mraa_aio_context m_aioO; - }; + protected: + otp538u_context m_otp538u; + }; } diff --git a/src/otp538u/otp538u_fti.c b/src/otp538u/otp538u_fti.c new file mode 100644 index 00000000..e79a773e --- /dev/null +++ b/src/otp538u/otp538u_fti.c @@ -0,0 +1,116 @@ +/* + * 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 "otp538u.h" + +#include "upm_fti.h" +#include "upm_sensor.h" + +/** + * This file implements the Function Table Interface (FTI) for this sensor + */ + +const char upm_otp538u_name[] = "OTP538U"; +const char upm_otp538u_description[] = "IR Temperature Sensor"; +const upm_protocol_t upm_otp538u_protocol[] = {UPM_ANALOG, UPM_ANALOG}; +const upm_sensor_t upm_otp538u_category[] = {UPM_TEMPERATURE}; + +// forward declarations +const upm_sensor_descriptor_t upm_otp538u_get_descriptor(); +const void* upm_otp538u_get_ft(upm_sensor_t sensor_type); +upm_result_t upm_otp538u_get_value_temperature(void* dev, float* tempval, + upm_temperature_u unit); +void* upm_otp538u_init_name(); +void upm_otp538u_close(void* dev); + +const upm_sensor_descriptor_t upm_otp538u_get_descriptor() { + upm_sensor_descriptor_t usd; + usd.name = upm_otp538u_name; + usd.description = upm_otp538u_description; + usd.protocol_size = 2; + usd.protocol = upm_otp538u_protocol; + usd.category_size = 1; + usd.category = upm_otp538u_category; + return usd; +} + +static const upm_sensor_ft ft = +{ + .upm_sensor_init_name = &upm_otp538u_init_name, + .upm_sensor_close = &upm_otp538u_close, + .upm_sensor_get_descriptor = &upm_otp538u_get_descriptor +}; + +static const upm_temperature_ft tft = +{ + .upm_temperature_get_value = &upm_otp538u_get_value_temperature +}; + +const void* upm_otp538u_get_ft(upm_sensor_t sensor_type) { + if(sensor_type == UPM_SENSOR) { + return &ft; + } + if(sensor_type == UPM_TEMPERATURE) { + return &tft; + } + return NULL; +} + +void* upm_otp538u_init_name(){ + return NULL; +} + +void upm_otp538u_close(void* dev) +{ + otp538u_close((otp538u_context)dev); +} + +upm_result_t upm_otp538u_get_value_temperature(void* dev, float* tempval, + upm_temperature_u unit) +{ + float temp = 0.0; + upm_result_t rv = otp538u_get_object_temperature((otp538u_context)dev, + &temp); + + if (rv != UPM_SUCCESS) + return rv; + + switch (unit) + { + case CELSIUS: + *tempval = temp; + return UPM_SUCCESS; + + case KELVIN: + *tempval = temp + 273.15; + return UPM_SUCCESS; + + case FAHRENHEIT: + *tempval = temp * (9.0/5.0) + 32.0; + return UPM_SUCCESS; + } + + return UPM_ERROR_INVALID_PARAMETER; +} diff --git a/src/otp538u/thermister_rt_table.hpp b/src/otp538u/thermister_rt_table.h similarity index 96% rename from src/otp538u/thermister_rt_table.hpp rename to src/otp538u/thermister_rt_table.h index bb8d3c95..478a747b 100644 --- a/src/otp538u/thermister_rt_table.hpp +++ b/src/otp538u/thermister_rt_table.h @@ -28,7 +28,7 @@ static const int otp538u_rt_table_max = 121; -static int otp538u_rt_table[otp538u_rt_table_max] = { +static int otp538u_rt_table[121] = { 919730, 869299, 821942, 777454, 735644, 696336, 659365, 624578, 591834, 561002, 531958, 504588, 478788, 454457, 431504, 409843, 389394, 370082, 351839, 334598, 318300, 302903, 288329, 274533, 261471, 249100, 237381, @@ -41,6 +41,5 @@ static int otp538u_rt_table[otp538u_rt_table_max] = { 21202, 20479, 19783, 19115, 18472, 17854, 17260, 16688, 16138, 15608, 15098, 14608, 14135, 13680, 13242, 12819, 12412, 12020, 11642, 11278, 10926, 10587, 10260, 9945, 9641, 9347, 9063, 8789, 8525, 8270, 8023, - 7785, 7555, 7333, 7118, 6911 + 7785, 7555, 7333, 7118, 6911 }; - diff --git a/src/otp538u/thermopile_vt_table.hpp b/src/otp538u/thermopile_vt_table.h similarity index 95% rename from src/otp538u/thermopile_vt_table.hpp rename to src/otp538u/thermopile_vt_table.h index d97d1370..c6b49eee 100644 --- a/src/otp538u/thermopile_vt_table.hpp +++ b/src/otp538u/thermopile_vt_table.h @@ -30,75 +30,74 @@ static const int otp538u_vt_table_max = 23; // Thermistor temperature (C) // { -20 -10 0 10 20 30 40 50 60 70 80 90 100 } -static float otp538u_vt_table[otp538u_vt_table_max][13] = { - // object temp (C) - {0.000, -0.246, -0.523, -0.832, -1.177, -1.559, // -20C +static float otp538u_vt_table[23][13] = { + // object temp (C) + {0.000, -0.246, -0.523, -0.832, -1.177, -1.559, // -20C -1.981, -2.446, -2.957, -3.516, -4.126, -4.791, -5.513}, - + {0.243, 0.000, -0.274, -0.580, -0.922, -1.301, // -10 -1.721, -2.183, -2.691, -3.247, -3.854, -4.516, -5.236}, - + {0.511, 0.271, 0.000, -0.303, -0.642, -1.018, // 0 -1.434, -1.894, -2.398, -2.951, -3.556, -4.215, -4.931}, - + {0.804, 0.567, 0.300, 0.000, -0.335, -0.708, // 10 -1.121, -1.577, -2.078, -2.628, -3.229, -3.884, -4.597}, - + {1.125, 0.891, 0.628, 0.331, 0.000, -0.369, // 20 -0.778, -1.230, -1.728, -2.274, -2.871, -3.523, -4.232}, - + {1.474, 1.244, 0.985, 0.692, 0.365, 0.000, // 30 -0.405, -0.853, -1.347, -1.889, -2.482, -3.130, -3.835}, - + {1.852, 1.628, 1.372, 1.084, 0.761, 0.401, // 40 0.000, -0.444, -0.933, -1.470, -2.059, -2.702, -3.403}, - + {2.263, 2.043, 1.792, 1.509, 1.191, 0.835, // 50 0.439, 0.000, -0.484, -1.017, -1.601, -2.240, -2.936}, - + {2.706, 2.491, 2.246, 1.968, 1.655, 1.304, // 60 0.913, 0.479, 0.000, -0.528, -1.107, -1.740, -2.431}, - + {3.184, 2.975, 2.735, 2.462, 2.155, 1.809, // 70 1.424, 0.996, 0.522, 0.000, -0.573, -1.201, -1.887}, - + {3.698, 3.495, 3.261, 2.994, 2.692, 2.353, // 80 1.974, 1.552, 1.084, 0.568, 0.000, -0.622, -1.301}, - + {4.250, 4.053, 3.825, 3.565, 3.270, 2.937, // 90 2.564, 2.148, 1.687, 1.177, 0.616, 0.000, -0.673}, - + {4.841, 4.651, 4.430, 4.177, 3.888, 3.562, // 100 3.196, 2.787, 2.332, 1.829, 1.275, 0.666, 0.000}, - + {5.473, 5.290, 5.076, 4.830, 4.549, 4.231, // 110 3.872, 3.470, 3.023, 2.527, 1.980, 1.379, 0.720}, - + {6.147, 5.972, 5.767, 5.528, 5.255, 4.944, // 120 4.593, 4.199, 3.760, 3.272, 2.733, 2.139, 1.488}, - + {6.866, 6.699, 6.502, 6.272, 6.007, 5.705, // 130 5.362, 4.976, 4.545, 4.066, 3.535, 2.950, 2.307}, - + {7.631, 7.473, 7.285, 7.064, 6.808, 6.514, // 140 6.180, 5.803, 5.381, 4.910, 4.388, 3.812, 3.178}, - + {8.444, 8.295, 8.116, 7.905, 7.658, 7.373, // 150 7.049, 6.682, 6.269, 5.807, 5.295, 4.728, 4.103}, - + {9.306, 9.167, 8.998, 8.796, 8.560, 8.285, // 160 7.971, 7.613, 7.211, 6.759, 6.257, 5.700, 5.085}, - + {10.219, 10.091, 9.933, 9.741, 9.515, 9.251, // 170 8.947, 8.601, 8.208, 7.768, 7.276, 6.729, 6.125}, - + {11.185, 11.068, 10.921, 10.741, 10.526, 10.274, // 180 9.981, 9.645, 9.264, 8.835, 8.354, 7.818, 7.226}, - + {12.206, 12.101, 11.966, 11.798, 11.595, 11.354, // 190 11.073, 10.749, 10.380, 9.962, 9.493, 8.969, 8.388}, - + {13.284, 13.191, 13.068, 12.913, 12.722, 12.494, // 200 12.225, 11.914, 11.557, 11.152, 10.695, 10.184, 9.616} }; -