mirror of
https://github.com/eclipse/upm.git
synced 2025-03-26 02:10:02 +03:00
314 lines
7.8 KiB
C++
314 lines
7.8 KiB
C++
/*
|
|
* 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 <iostream>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <sstream>
|
|
|
|
#include "t3311.hpp"
|
|
|
|
using namespace upm;
|
|
using namespace std;
|
|
|
|
// conversion from fahrenheit to celsius and back
|
|
|
|
static float f2c(float f)
|
|
{
|
|
return ((f - 32.0) / (9.0 / 5.0));
|
|
}
|
|
|
|
static float c2f(float c)
|
|
{
|
|
return (c * (9.0 / 5.0) + 32.0);
|
|
}
|
|
|
|
// conversion from 1-byte BCD to decimal
|
|
static uint8_t bcd2dec(uint8_t bcd)
|
|
{
|
|
return (bcd / 16 * 10) + (bcd % 16);
|
|
}
|
|
|
|
T3311::T3311(std::string device, int address, int baud, int bits, char parity,
|
|
int stopBits) :
|
|
m_mbContext(0)
|
|
{
|
|
// check some of the parameters
|
|
if (!(bits == 7 || bits == 8))
|
|
{
|
|
throw std::out_of_range(std::string(__FUNCTION__) +
|
|
": bits must be 7 or 8");
|
|
}
|
|
|
|
if (!(parity == 'N' || parity == 'E' || parity == 'O'))
|
|
{
|
|
throw std::out_of_range(std::string(__FUNCTION__) +
|
|
": parity must be 'N', 'O', or 'E'");
|
|
}
|
|
|
|
if (!(stopBits == 1 || stopBits == 2))
|
|
{
|
|
throw std::out_of_range(std::string(__FUNCTION__) +
|
|
": stopBits must be 1 or 2");
|
|
}
|
|
|
|
m_temperature = 0.0;
|
|
m_humidity = 0.0;
|
|
m_computedValue = 0.0;
|
|
m_dewPointTemperature = 0.0;
|
|
m_absoluteHumidity = 0.0;
|
|
m_specificHumidity = 0.0;
|
|
m_mixingRatio = 0.0;
|
|
m_specificEnthalpy = 0.0;
|
|
|
|
// addresses are only 8bits wide
|
|
address &= 0xff;
|
|
|
|
// now, open/init the device and modbus context
|
|
|
|
if (!(m_mbContext = modbus_new_rtu(device.c_str(), baud, parity, bits,
|
|
stopBits)))
|
|
{
|
|
throw std::runtime_error(std::string(__FUNCTION__) +
|
|
": modbus_new_rtu() failed");
|
|
}
|
|
|
|
// set the slave address of the device we want to talk to
|
|
if (modbus_set_slave(m_mbContext, address))
|
|
{
|
|
throw std::runtime_error(std::string(__FUNCTION__) +
|
|
": modbus_set_slave() failed");
|
|
}
|
|
|
|
// set the serial mode
|
|
modbus_rtu_set_serial_mode(m_mbContext, MODBUS_RTU_RS232);
|
|
|
|
// now connect..
|
|
if (modbus_connect(m_mbContext))
|
|
{
|
|
throw std::runtime_error(std::string(__FUNCTION__) +
|
|
": modbus_connect() failed");
|
|
}
|
|
|
|
// This is a bit of a hack. The device uses bus power, which isn't
|
|
// provided unless the device has been opened and accessed. As a
|
|
// result, register reads will usually fail the first time the
|
|
// device is accessed after a power cycle. Here, we read the
|
|
// temperature value (which will most likely fail), then sleep,
|
|
// allowing the sensor to "boot". The datasheet says it takes at
|
|
// about 2 seconds to boot, we will wait for 5.
|
|
uint16_t tmp;
|
|
modbus_read_input_registers(m_mbContext, REG_TEMPERATURE, 1, &tmp);
|
|
|
|
// sleep for 5 seconds to give time for device to powerup and boot
|
|
sleep(5);
|
|
|
|
// turn off debugging
|
|
setDebug(false);
|
|
|
|
// now read the UNIT_SETTING reg to see what units we are getting
|
|
// our temperature data in.
|
|
tmp = readInputReg(REG_UNIT_SETTINGS);
|
|
if (tmp & 0x0001)
|
|
m_isCelsius = false;
|
|
else
|
|
m_isCelsius = true;
|
|
|
|
// read in the the FW_LO register (BCD encoded) and convert
|
|
tmp = readInputReg(REG_FW_LO);
|
|
|
|
// HI byte (major)
|
|
m_fwRevHi = (tmp >> 8) & 0xff;
|
|
m_fwRevHi = bcd2dec(m_fwRevHi);
|
|
|
|
// LO byte (minor)
|
|
m_fwRevLo = (tmp & 0xff);
|
|
m_fwRevLo = bcd2dec(m_fwRevLo);
|
|
|
|
if (m_fwRevHi >= 2 && m_fwRevLo >= 44)
|
|
m_isExtendedDataAvailable = true;
|
|
else
|
|
m_isExtendedDataAvailable = false;
|
|
|
|
// now get the serial number (BCD encoded 4-byte value, which we
|
|
// will pack into a string)
|
|
stringstream preformat;
|
|
uint8_t b;
|
|
// LO (but really HI)
|
|
tmp = readInputReg(REG_SERIAL_LO);
|
|
b = bcd2dec((tmp & 0xff00) >> 8);
|
|
preformat << int(b);
|
|
|
|
b = bcd2dec(tmp & 0x00ff);
|
|
preformat << int(b);
|
|
|
|
// HI (but really LO)
|
|
tmp = readInputReg(REG_SERIAL_HI);
|
|
|
|
b = bcd2dec((tmp & 0xff00) >> 8);
|
|
preformat << int(b);
|
|
b = bcd2dec(tmp & 0x00ff);
|
|
preformat << int(b);
|
|
|
|
m_serialNumber = preformat.str();
|
|
}
|
|
|
|
T3311::~T3311()
|
|
{
|
|
if (m_mbContext)
|
|
{
|
|
modbus_close(m_mbContext);
|
|
modbus_free(m_mbContext);
|
|
}
|
|
}
|
|
|
|
uint16_t T3311::readInputReg(int reg)
|
|
{
|
|
uint16_t val;
|
|
|
|
if (modbus_read_input_registers(m_mbContext, reg, 1, &val) <= 0)
|
|
{
|
|
throw std::runtime_error(std::string(__FUNCTION__) +
|
|
": modbus_read_input_registers() failed");
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
int T3311::readInputRegs(int reg, int len, uint16_t *buf)
|
|
{
|
|
int rv;
|
|
|
|
if ((rv = modbus_read_input_registers(m_mbContext, reg, len, buf)) < 0)
|
|
{
|
|
throw std::runtime_error(std::string(__FUNCTION__) +
|
|
": modbus_read_input_registers() failed");
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
void T3311::update()
|
|
{
|
|
static const int dataLen = 9;
|
|
uint16_t data[dataLen];
|
|
|
|
// we read the 9 registers starting at the temperature
|
|
if (readInputRegs(REG_TEMPERATURE, dataLen, data) != dataLen)
|
|
{
|
|
throw std::runtime_error(std::string(__FUNCTION__) +
|
|
": read less than the expected 9 registers");
|
|
}
|
|
|
|
// temperature first, we always store as C
|
|
float tmpF = float((int16_t)data[0]) / 10.0;
|
|
if (m_isCelsius)
|
|
m_temperature = tmpF;
|
|
else
|
|
m_temperature = f2c(tmpF);
|
|
|
|
m_humidity = float((int16_t)data[1]) / 10.0;
|
|
m_computedValue = float((int16_t)data[2]) / 10.0;
|
|
|
|
// skip data[3], (pressure) as this device does not support it
|
|
|
|
// if extended info is supported, grab those too
|
|
if (extendedDataAvailable())
|
|
{
|
|
// we always store temps in C
|
|
tmpF = float((int16_t)data[4]) / 10.0;
|
|
if (m_isCelsius)
|
|
m_dewPointTemperature = tmpF;
|
|
else
|
|
m_dewPointTemperature = f2c(tmpF);
|
|
|
|
m_absoluteHumidity = float((int16_t)data[5]) / 10.0;
|
|
m_specificHumidity = float((int16_t)data[6]) / 10.0;
|
|
m_mixingRatio = float((int16_t)data[7]) / 10.0;
|
|
m_specificEnthalpy = float((int16_t)data[8]) / 10.0;
|
|
}
|
|
}
|
|
|
|
float T3311::getTemperature(bool fahrenheit)
|
|
{
|
|
if (fahrenheit)
|
|
return c2f(m_temperature);
|
|
else
|
|
return m_temperature;
|
|
}
|
|
|
|
float T3311::getTemperature()
|
|
{
|
|
return getTemperature(false);
|
|
}
|
|
|
|
float T3311::getHumidity()
|
|
{
|
|
return m_humidity;
|
|
}
|
|
|
|
float T3311::getComputedValue()
|
|
{
|
|
return m_computedValue;
|
|
}
|
|
|
|
float T3311::getDewPointTemperature(bool fahrenheit)
|
|
{
|
|
if (fahrenheit)
|
|
return c2f(m_dewPointTemperature);
|
|
else
|
|
return m_dewPointTemperature;
|
|
}
|
|
|
|
float T3311::getAbsoluteHumidity()
|
|
{
|
|
return m_absoluteHumidity;
|
|
}
|
|
|
|
float T3311::getSpecificHumidity()
|
|
{
|
|
return m_specificHumidity;
|
|
}
|
|
|
|
float T3311::getMixingRatio()
|
|
{
|
|
return m_mixingRatio;
|
|
}
|
|
|
|
float T3311::getSpecificEnthalpy()
|
|
{
|
|
return m_specificEnthalpy;
|
|
}
|
|
|
|
void T3311::setDebug(bool enable)
|
|
{
|
|
m_debugging = enable;
|
|
|
|
if (enable)
|
|
modbus_set_debug(m_mbContext, 1);
|
|
else
|
|
modbus_set_debug(m_mbContext, 0);
|
|
}
|