otp538u: C implementation; FTI; C++ wraps C

Signed-off-by: Jon Trulson <jtrulson@ics.com>
This commit is contained in:
Jon Trulson
2016-11-07 13:53:11 -07:00
parent 1aa748e3d6
commit 1630ebfca4
10 changed files with 906 additions and 393 deletions

View File

@ -1,6 +1,6 @@
/*
* Author: Jon Trulson <jtrulson@ics.com>
* 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<samples; i++)
{
val = mraa_aio_read(m_aioA);
if (val == -1) {
throw std::runtime_error(std::string(__FUNCTION__) +
": failed to do aio read");
}
temp += (float)val;
usleep(10000);
}
temp = temp / samples;
if (otp538u_get_ambient_temperature(m_otp538u, &temp))
throw std::runtime_error(std::string(__FUNCTION__) +
": otp538u_get_ambient_temperature() failed");
float volts = temp * m_aref / m_adcResolution;
if (m_debug)
{
cerr << "\tAMB sample " << temp << " m_aref " << m_aref
<< " VOLTS " << volts << endl;
}
// compute the resistance of the thermistor
res = m_vResistance * volts / (m_internalVRef - volts);
if (m_debug)
{
cerr << "\tAMB computed resistance: " << res << endl;
}
// look it up in the thermistor (RT) resistence/temperature table
int rawslot;
int j;
for (j=0; j<otp538u_rt_table_max; j++)
if (otp538u_rt_table[j] < res)
{
rawslot = j;
break;
}
if (j >= 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<samples; i++)
{
val = mraa_aio_read(m_aioO);
if (val == -1) {
throw std::runtime_error(std::string(__FUNCTION__) +
": failed to do aio read.");
return 0;
}
temp += val;
usleep(10000);
}
float temp = 0;
temp = temp / samples;
if (otp538u_get_object_temperature(m_otp538u, &temp))
throw std::runtime_error(std::string(__FUNCTION__) +
": otp538u_get_object_temperature() failed");
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.
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))
{
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;
}