diff --git a/examples/c++/otp538u.cxx b/examples/c++/otp538u.cxx index 914350f0..b32df2d9 100644 --- a/examples/c++/otp538u.cxx +++ b/examples/c++/otp538u.cxx @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "otp538u.hpp" @@ -52,14 +53,24 @@ int main() // Object temperature. upm::OTP538U *temps = new upm::OTP538U(0, 1, OTP538U_AREF); + // enable debugging if you would like + // temps->setDebug(true); + // Output ambient and object temperatures while (shouldRun) { - cout << "Ambient temp: " << std::fixed << setprecision(2) - << temps->ambientTemperature() - << " C, Object temp: " << temps->objectTemperature() - << " C" << endl; - + try { + cout << "Ambient temp: " << std::fixed << setprecision(2) + << temps->ambientTemperature() + << " C, Object temp: " << temps->objectTemperature() + << " C" << endl; + } + catch (std::out_of_range& e) { + cerr << "Temperature(s) are out of range: " << e.what() + << endl; + } + + cout << endl; sleep(1); } //! [Interesting] diff --git a/src/otp538u/otp538u.cxx b/src/otp538u/otp538u.cxx index 8fd830cb..26966c39 100644 --- a/src/otp538u/otp538u.cxx +++ b/src/otp538u/otp538u.cxx @@ -36,9 +36,24 @@ using namespace std; OTP538U::OTP538U(int pinA, int pinO, float 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 - m_vref = 2.5; + // sensor module for the thermistor. + + m_internalVRef = 2.5; // analog reference in use m_aref = aref; @@ -51,29 +66,72 @@ OTP538U::OTP538U(int pinA, int pinO, float aref) // can adjust as neccessary depending on your calibration. m_offsetVoltage = 0.014; - // We need around 1mV resolution, so use 12 bit resolution (4096) - // with a default aref of 5.0. - m_adcResolution = 4096; + // 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, invalid pin?"); + ": mraa_gpio_init(pinA) failed"); return; } - // enable 12 bit resolution - mraa_aio_set_bit(m_aioA, 12); + // 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, invalid pin?"); + ": mraa_gpio_init(pinO) failed"); return; } - // enable 12 bit resolution - mraa_aio_set_bit(m_aioO, 12); + + // 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); + } } OTP538U::~OTP538U() @@ -87,7 +145,7 @@ float OTP538U::ambientTemperature() const int samples = 5; int val = 0; float temp = 0; - float res; + float res = 0; for (int i=0; i= otp538u_rt_table_max) { throw std::out_of_range(std::string(__FUNCTION__) + - ": ambient temperature out of range."); + ": ambient temperature out of range (high)."); return 0; } @@ -133,7 +201,7 @@ float OTP538U::ambientTemperature() if (slot < 0) { throw std::out_of_range(std::string(__FUNCTION__) + - ": ambient temperature out of range."); + ": ambient temperature out of range (low)."); return 0; } @@ -168,9 +236,18 @@ float OTP538U::objectTemperature() temp = temp / samples; - float temp1 = temp * m_aref / m_adcResolution; - float sensorVolts = temp1 - (reference_vol + m_offsetVoltage); - // cout << "Sensor Voltage: " << sensorVolts << endl; + if (m_debug) + cerr << "\tOBJ sample " << temp << " "; + + float volts = temp * m_aref / m_adcResolution; + + if (m_debug) + cerr << "VOLTS: " << volts << " "; + + float sensorVolts = volts - (reference_vol + m_offsetVoltage); + + if (m_debug) + cerr << "Sensor Voltage (computed): " << sensorVolts << endl; // search the VT (voltage/temperature) table to find the object // temperature. @@ -198,8 +275,11 @@ float OTP538U::objectTemperature() ( otp538u_vt_table[slot + 1][voltOffset] - otp538u_vt_table[slot][voltOffset] ); - // cout << "TABLE VALUE [" << slot << "][" << - // voltOffset << "] = " << otp538u_vt_table[slot][voltOffset] << endl; + if (m_debug) + { + cerr << "\tVoltage (" << voltage << "): TABLE VALUE [" << slot << "][" << + voltOffset << "] = " << otp538u_vt_table[slot][voltOffset] << endl; + } return (ambTemp + objTemp); } diff --git a/src/otp538u/otp538u.hpp b/src/otp538u/otp538u.hpp index cc817197..82b6094e 100644 --- a/src/otp538u/otp538u.hpp +++ b/src/otp538u/otp538u.hpp @@ -65,6 +65,10 @@ namespace upm { * 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 */ @@ -135,13 +139,22 @@ namespace upm { * looking at the EAGLE files containing their schematics for this * device. * - * @param vref Reference voltage of the internal sensor; default is 2.5 V + * @param vref Reference voltage of the internal sensor; default + * is 2.5 */ - void setVRef(float vref) { m_vref = vref; }; + void setVRef(float vref) { m_internalVRef = vref; }; + + /** + * Enable debugging output. + * + * @param true to enable some debug output, false otherwise + */ + void setDebug(bool enable) { m_debug = enable; }; private: - float m_vref; + bool m_debug; + float m_internalVRef; float m_aref; int m_vResistance; float m_offsetVoltage;