mirror of
https://github.com/eclipse/upm.git
synced 2025-03-15 04:57:30 +03:00
otp538u: C implementation; FTI; C++ wraps C
Signed-off-by: Jon Trulson <jtrulson@ics.com>
This commit is contained in:
parent
1aa748e3d6
commit
1630ebfca4
@ -134,6 +134,7 @@ add_example (mma7660)
|
|||||||
add_example (buzzer)
|
add_example (buzzer)
|
||||||
add_example (ppd42ns)
|
add_example (ppd42ns)
|
||||||
add_example (guvas12d)
|
add_example (guvas12d)
|
||||||
|
add_example (otp538u)
|
||||||
|
|
||||||
# Custom examples
|
# Custom examples
|
||||||
add_custom_example (nmea_gps_i2c-example-c nmea_gps_i2c.c nmea_gps)
|
add_custom_example (nmea_gps_i2c-example-c nmea_gps_i2c.c nmea_gps)
|
||||||
|
81
examples/c/otp538u.c
Normal file
81
examples/c/otp538u.c
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Author: Jon Trulson <jtrulson@ics.com>
|
||||||
|
* 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 <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <upm_utilities.h>
|
||||||
|
#include <otp538u.h>
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
@ -1,5 +1,9 @@
|
|||||||
set (libname "otp538u")
|
upm_mixed_module_init (NAME otp538u
|
||||||
set (libdescription "Otp538u IR temperature sensor")
|
DESCRIPTION "OTP538U Analog IR temperature sensor"
|
||||||
set (module_src ${libname}.cxx)
|
C_HDR otp538u.h
|
||||||
set (module_hpp ${libname}.hpp)
|
C_SRC otp538u.c
|
||||||
upm_module_init()
|
CPP_HDR otp538u.hpp
|
||||||
|
CPP_SRC otp538u.cxx
|
||||||
|
FTI_SRC otp538u_fti.c
|
||||||
|
CPP_WRAPS_C
|
||||||
|
REQUIRES mraa)
|
||||||
|
374
src/otp538u/otp538u.c
Normal file
374
src/otp538u/otp538u.c
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
/*
|
||||||
|
* Author: Jon Trulson <jtrulson@ics.com>
|
||||||
|
* 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 <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <upm_utilities.h>
|
||||||
|
#include <upm_platform.h>
|
||||||
|
|
||||||
|
#include <otp538u.h>
|
||||||
|
|
||||||
|
#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; i<samples; i++)
|
||||||
|
{
|
||||||
|
val = mraa_aio_read(dev->aioA);
|
||||||
|
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; j++)
|
||||||
|
if (otp538u_rt_table[j] < res)
|
||||||
|
{
|
||||||
|
rawslot = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (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; i<samples; i++)
|
||||||
|
{
|
||||||
|
val = mraa_aio_read(dev->aioO);
|
||||||
|
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
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Author: Jon Trulson <jtrulson@ics.com>
|
* 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
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@ -28,258 +28,40 @@
|
|||||||
|
|
||||||
#include "otp538u.hpp"
|
#include "otp538u.hpp"
|
||||||
|
|
||||||
#include "thermopile_vt_table.hpp"
|
|
||||||
#include "thermister_rt_table.hpp"
|
|
||||||
|
|
||||||
using namespace upm;
|
using namespace upm;
|
||||||
using namespace std;
|
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;
|
if (!m_otp538u)
|
||||||
const int adcLowRes = 1023;
|
throw std::runtime_error(std::string(__FUNCTION__) +
|
||||||
|
": otp538u_init() failed");
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OTP538U::~OTP538U()
|
OTP538U::~OTP538U()
|
||||||
{
|
{
|
||||||
mraa_aio_close(m_aioA);
|
otp538u_close(m_otp538u);
|
||||||
mraa_aio_close(m_aioO);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float OTP538U::ambientTemperature()
|
float OTP538U::ambientTemperature()
|
||||||
{
|
{
|
||||||
const int samples = 5;
|
float temp = 0;
|
||||||
int val = 0;
|
|
||||||
float temp = 0;
|
|
||||||
float res = 0;
|
|
||||||
|
|
||||||
for (int i=0; i<samples; i++)
|
if (otp538u_get_ambient_temperature(m_otp538u, &temp))
|
||||||
{
|
throw std::runtime_error(std::string(__FUNCTION__) +
|
||||||
val = mraa_aio_read(m_aioA);
|
": otp538u_get_ambient_temperature() failed");
|
||||||
if (val == -1) {
|
|
||||||
throw std::runtime_error(std::string(__FUNCTION__) +
|
|
||||||
": failed to do aio read");
|
|
||||||
}
|
|
||||||
temp += (float)val;
|
|
||||||
usleep(10000);
|
|
||||||
}
|
|
||||||
temp = temp / samples;
|
|
||||||
|
|
||||||
float volts = temp * m_aref / m_adcResolution;
|
return temp;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float OTP538U::objectTemperature()
|
float OTP538U::objectTemperature()
|
||||||
{
|
{
|
||||||
const int samples = 5;
|
float temp = 0;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
return temp;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
153
src/otp538u/otp538u.h
Normal file
153
src/otp538u/otp538u.h
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* Author: Jon Trulson <jtrulson@ics.com>
|
||||||
|
* 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 <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <upm.h>
|
||||||
|
#include <mraa/aio.h>
|
||||||
|
|
||||||
|
#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
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Author: Jon Trulson <jtrulson@ics.com>
|
* 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
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@ -24,144 +24,148 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <mraa/aio.h>
|
#include <otp538u.h>
|
||||||
|
|
||||||
namespace upm {
|
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
|
* @brief OTP538U IR Temperature Sensor library
|
||||||
*
|
* @defgroup otp538u libupm-otp538u
|
||||||
* @param pinA Analog pin to use for the ambient temperature
|
* @ingroup seeed analog light hak
|
||||||
* @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);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
* OTP538U destructor
|
||||||
*
|
*/
|
||||||
* @return Ambient temperature
|
~OTP538U();
|
||||||
*/
|
|
||||||
float ambientTemperature();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the object temperature in Celsius
|
* Gets the ambient temperature in Celsius
|
||||||
*
|
*
|
||||||
* @return Object temperature
|
* @return Ambient temperature
|
||||||
*/
|
*/
|
||||||
float objectTemperature();
|
float ambientTemperature();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the offset voltage
|
* Gets the object temperature in Celsius
|
||||||
*
|
*
|
||||||
* The Seeed Studio wiki gives an example of calibrating the sensor
|
* @return Object temperature
|
||||||
* and calculating the offset voltage to apply. Currently, the
|
*/
|
||||||
* default value is set, but you can use the function to set one
|
float objectTemperature();
|
||||||
* of your own.
|
|
||||||
*
|
|
||||||
* @param vOffset Desired offset voltage
|
|
||||||
*/
|
|
||||||
void setVoltageOffset(float vOffset) { m_offsetVoltage = vOffset; };
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the output resistance value
|
* Sets the offset voltage
|
||||||
*
|
*
|
||||||
* The Seeed Studio wiki example uses a value of 2,000,000 in one of
|
* The Seeed Studio wiki gives an example of calibrating the sensor
|
||||||
* the equations used to calculate voltage. The value is the
|
* and calculating the offset voltage to apply. Currently, the
|
||||||
* resistance of a resistor they use in the output stage of their
|
* default value is set, but you can use the function to set one
|
||||||
* SIG2 output. This was 'decoded' by looking at the EAGLE* files
|
* of your own.
|
||||||
* containing their schematics for this device.
|
*
|
||||||
*
|
* @param vOffset Desired offset voltage
|
||||||
* @param outResistance Value of the output resistor; default is 2M Ohm
|
*/
|
||||||
*/
|
void setVoltageOffset(float vOffset)
|
||||||
void setOutputResistence(int outResistance) {
|
{
|
||||||
m_vResistance = outResistance; };
|
otp538u_set_voltage_offset(m_otp538u, vOffset);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the reference voltage of the internal Seeed Studio voltage
|
* Sets the output resistance value
|
||||||
* regulator on the sensor board.
|
*
|
||||||
*
|
* The Seeed Studio wiki example uses a value of 2,000,000 in one of
|
||||||
* The Seeed Studio wiki example uses a value of 2.5 in one of the
|
* the equations used to calculate voltage. The value is the
|
||||||
* equations used to calculate the resistance of the ambient
|
* resistance of a resistor they use in the output stage of their
|
||||||
* thermistor. The value is the voltage of an internal voltage
|
* SIG2 output. This was 'decoded' by looking at the EAGLE* files
|
||||||
* regulator used on the sensor board. This was 'decoded' by
|
* containing their schematics for this device.
|
||||||
* looking at the EAGLE files containing their schematics for this
|
*
|
||||||
* device.
|
* @param outResistance Value of the output resistor; default is 2M Ohm
|
||||||
*
|
*/
|
||||||
* @param vref Reference voltage of the internal sensor; default
|
void setOutputResistence(int outResistance)
|
||||||
* is 2.5
|
{
|
||||||
*/
|
otp538u_set_output_resistence(m_otp538u, outResistance);
|
||||||
void setVRef(float vref) { m_internalVRef = vref; };
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable debugging output.
|
* Sets the reference voltage of the internal Seeed Studio voltage
|
||||||
*
|
* regulator on the sensor board.
|
||||||
* @param true to enable some debug output, false otherwise
|
*
|
||||||
*/
|
* The Seeed Studio wiki example uses a value of 2.5 in one of the
|
||||||
void setDebug(bool enable) { m_debug = enable; };
|
* 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:
|
protected:
|
||||||
bool m_debug;
|
otp538u_context m_otp538u;
|
||||||
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;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
116
src/otp538u/otp538u_fti.c
Normal file
116
src/otp538u/otp538u_fti.c
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Author: Jon Trulson <jtrulson@ics.com>
|
||||||
|
* 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;
|
||||||
|
}
|
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
static const int otp538u_rt_table_max = 121;
|
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,
|
919730, 869299, 821942, 777454, 735644, 696336, 659365, 624578, 591834,
|
||||||
561002, 531958, 504588, 478788, 454457, 431504, 409843, 389394, 370082,
|
561002, 531958, 504588, 478788, 454457, 431504, 409843, 389394, 370082,
|
||||||
351839, 334598, 318300, 302903, 288329, 274533, 261471, 249100, 237381,
|
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,
|
21202, 20479, 19783, 19115, 18472, 17854, 17260, 16688, 16138, 15608,
|
||||||
15098, 14608, 14135, 13680, 13242, 12819, 12412, 12020, 11642, 11278,
|
15098, 14608, 14135, 13680, 13242, 12819, 12412, 12020, 11642, 11278,
|
||||||
10926, 10587, 10260, 9945, 9641, 9347, 9063, 8789, 8525, 8270, 8023,
|
10926, 10587, 10260, 9945, 9641, 9347, 9063, 8789, 8525, 8270, 8023,
|
||||||
7785, 7555, 7333, 7118, 6911
|
7785, 7555, 7333, 7118, 6911
|
||||||
};
|
};
|
||||||
|
|
@ -30,75 +30,74 @@ static const int otp538u_vt_table_max = 23;
|
|||||||
// Thermistor temperature (C)
|
// Thermistor temperature (C)
|
||||||
// { -20 -10 0 10 20 30 40 50 60 70 80 90 100 }
|
// { -20 -10 0 10 20 30 40 50 60 70 80 90 100 }
|
||||||
|
|
||||||
static float otp538u_vt_table[otp538u_vt_table_max][13] = {
|
static float otp538u_vt_table[23][13] = {
|
||||||
// object temp (C)
|
// object temp (C)
|
||||||
{0.000, -0.246, -0.523, -0.832, -1.177, -1.559, // -20C
|
{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},
|
-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
|
{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},
|
-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
|
{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},
|
-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
|
{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.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
|
{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},
|
-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
|
{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},
|
-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
|
{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},
|
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
|
{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},
|
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
|
{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},
|
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
|
{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},
|
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
|
{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},
|
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
|
{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},
|
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
|
{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},
|
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
|
{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},
|
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
|
{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},
|
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
|
{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},
|
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
|
{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},
|
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
|
{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},
|
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
|
{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},
|
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
|
{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},
|
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
|
{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},
|
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
|
{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},
|
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
|
{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}
|
12.225, 11.914, 11.557, 11.152, 10.695, 10.184, 9.616}
|
||||||
};
|
};
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user