548 lines
10 KiB
C++
548 lines
10 KiB
C++
#include "SHT3x.h"
|
|
SHT3x::SHT3x(int Address, ValueIfError Value, uint8_t HardResetPin, SHT3xSensor SensorType, SHT3xMode Mode)
|
|
{
|
|
_Error = noError;
|
|
SetAddress((uint8_t)Address);
|
|
_ValueIfError = Value;
|
|
SetMode(Mode);
|
|
_SensorType = SensorType;
|
|
_HardResetPin = HardResetPin ;
|
|
|
|
}
|
|
|
|
void SHT3x::Begin()
|
|
{
|
|
Wire.begin();
|
|
_OperationEnabled = true;
|
|
}
|
|
|
|
void SHT3x::UpdateData()
|
|
{
|
|
_Error = noError;
|
|
if ((_LastUpdateMillisec == 0) || ((millis()-_LastUpdateMillisec)>=_UpdateIntervalMillisec))
|
|
{
|
|
SendCommand(_MeasMSB,_MeasLSB);
|
|
if (_Error == noError)
|
|
{
|
|
Wire.requestFrom(_Address, (uint8_t)6);
|
|
uint32_t WaitingBeginTime = millis();
|
|
while ((Wire.available()<6) && ((millis() - WaitingBeginTime) < _TimeoutMillisec))
|
|
{
|
|
//Do nothing, just wait
|
|
}
|
|
if ((millis() - WaitingBeginTime) < _TimeoutMillisec)
|
|
{
|
|
_LastUpdateMillisec = WaitingBeginTime;
|
|
uint8_t data[6];
|
|
for (uint8_t i = 0; i<6; i++)
|
|
{
|
|
data[i] = Wire.read();
|
|
}
|
|
|
|
if ((CRC8(data[0],data[1],data[2])) && (CRC8(data[3],data[4],data[5])))
|
|
{
|
|
uint16_t TemperatureRaw = (data[0]<<8)+(data[1]<<0);
|
|
uint16_t RelHumidityRaw = (data[3]<<8)+(data[4]<<0);
|
|
_TemperatureCeil = ((float) TemperatureRaw) * 0.00267033 - 45.;
|
|
_TemperatureCeil = _TemperatureCeil*_TemperatureCalibration.Factor +
|
|
_TemperatureCalibration.Shift;
|
|
_RelHumidity = ((float) RelHumidityRaw) * 0.0015259;
|
|
_RelHumidity = _RelHumidity * _RelHumidityCalibration.Factor +
|
|
_RelHumidityCalibration.Shift;
|
|
|
|
_Error = noError;
|
|
}
|
|
else
|
|
{
|
|
_Error = DataCorrupted;
|
|
}
|
|
}
|
|
else //Timeout
|
|
{
|
|
_Error = Timeout;
|
|
}
|
|
}
|
|
else //Error after message send
|
|
{
|
|
// Nothing to do, measurement commands will return NULL because of Error != noError
|
|
}
|
|
}
|
|
else //LastUpdate was too recently
|
|
{
|
|
//Nothing to do, wait for next call
|
|
}
|
|
}
|
|
|
|
float SHT3x::GetTemperature(TemperatureScale Degree)
|
|
{
|
|
//default scale is Celsius
|
|
//At first, calculate in Celsius, than, if need, recalculate to Farenheit or Kelvin and than adjust according to calibration;
|
|
float Temperature = _TemperatureCeil;
|
|
if (Degree == Kel)
|
|
{
|
|
Temperature+=273.15;
|
|
}
|
|
else if (Degree == Far)
|
|
{
|
|
Temperature = Temperature*1.8+32.;
|
|
}
|
|
|
|
return ReturnValueIfError(Temperature);
|
|
}
|
|
|
|
float SHT3x::GetRelHumidity()
|
|
{
|
|
return ReturnValueIfError(_RelHumidity);
|
|
}
|
|
|
|
float SHT3x::GetAbsHumidity(AbsHumidityScale Scale)
|
|
{
|
|
float millikelvins = GetTemperature(Kel) / 1000.;
|
|
float Pressure = 0.;
|
|
for (uint8_t i = 0; i<6; i++)
|
|
{
|
|
float term = 1.;
|
|
for (uint8_t j = 0; j<i; j++)
|
|
{
|
|
term *= millikelvins;
|
|
}
|
|
Pressure += term*_AbsHumPoly[i];
|
|
}
|
|
Pressure *= GetRelHumidity();
|
|
switch (Scale)
|
|
{
|
|
case mmHg:
|
|
{
|
|
//Already in mm Hg
|
|
break;
|
|
}
|
|
case Torr:
|
|
{
|
|
//Already in Torr
|
|
break;
|
|
}
|
|
case Pa:
|
|
{
|
|
Pressure *= 133.322;
|
|
break;
|
|
}
|
|
case Bar:
|
|
{
|
|
Pressure *= 0.0013332;
|
|
break;
|
|
}
|
|
case At:
|
|
{
|
|
Pressure *= 0.0013595;
|
|
break;
|
|
}
|
|
case Atm:
|
|
{
|
|
Pressure *= 0.0013158;
|
|
break;
|
|
}
|
|
case mH2O:
|
|
{
|
|
Pressure *= 0.013595;
|
|
break;
|
|
}
|
|
case psi:
|
|
{
|
|
Pressure *= 0.019337;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return ReturnValueIfError(Pressure);
|
|
}
|
|
|
|
float SHT3x::GetTempTolerance(TemperatureScale Degree, SHT3xSensor SensorType)
|
|
{
|
|
//Temperature tolerance is similar for both SHT30 and SHT31
|
|
//At first, calculate at Celsius (similar to Kelvins), than, if need, recalculate to Farenheit
|
|
float Temperature = GetTemperature();
|
|
float Tolerance;
|
|
switch (SensorType)
|
|
{
|
|
case SHT30:
|
|
{
|
|
if ((0. <= Temperature) && (Temperature <= 65.))
|
|
{
|
|
Tolerance = 0.2;
|
|
}
|
|
else if (Temperature > 65.)
|
|
{
|
|
//Linear from 0.2 at 65 C to 0.6 at 125 C.
|
|
Tolerance = 0.0067 * Temperature - 0.2333;
|
|
}
|
|
else //if (Temperature < 0.)
|
|
{
|
|
//Linear from 0.6 at -40 C to 0.2 at 0 C.
|
|
Tolerance = -0.01 * Temperature + 0.2;
|
|
}
|
|
break;
|
|
}
|
|
case SHT31:
|
|
{
|
|
if ((0. <= Temperature) && (Temperature <= 90.))
|
|
{
|
|
Tolerance = 0.2;
|
|
}
|
|
else if (Temperature > 65.)
|
|
{
|
|
//Linear from 0.2 at 90 C to 0.5 at 125 C.
|
|
Tolerance = 0.0086 * Temperature - 0.5714;
|
|
}
|
|
else //if (Temperature < 0.)
|
|
{
|
|
//Linear from 0.3 at -40 C to 0.2 at 0 C.
|
|
Tolerance = -0.0025 * Temperature + 0.2;
|
|
}
|
|
break;
|
|
}
|
|
case SHT35:
|
|
{
|
|
if (Temperature <= 0.)
|
|
{
|
|
Tolerance = 0.2;
|
|
}
|
|
else if ((0. < Temperature) && (Temperature <= 20.))
|
|
{
|
|
//Linear from 0.2 at 0 C to 0.1 at 20 C.
|
|
Tolerance = -0.005 * Temperature + 0.2;
|
|
}
|
|
else if ((20. < Temperature) && (Temperature <= 60.))
|
|
{
|
|
Tolerance = 0.1;
|
|
}
|
|
else if ((60. < Temperature) && (Temperature <= 90.))
|
|
{
|
|
//Linear from 0.1 at 60 C to 0.2 at 90 C.
|
|
Tolerance = -0.0033 * Temperature - 0.1;
|
|
}
|
|
else //if (90. < Temperature)
|
|
{
|
|
//Linear from 0.2 at 90 C to 0.4 at 125 C.
|
|
Tolerance = 0.0057 * Temperature - 0.3143;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (Degree == Far)
|
|
{
|
|
Tolerance *= 1.8;
|
|
}
|
|
|
|
|
|
return ReturnValueIfError(Tolerance);
|
|
}
|
|
|
|
float SHT3x::GetRelHumTolerance(SHT3xSensor SensorType)
|
|
{
|
|
float RelHumidity = GetRelHumidity();
|
|
float Tolerance;
|
|
switch (SensorType)
|
|
{
|
|
case SHT30:
|
|
{
|
|
if ((10.<= RelHumidity) && (RelHumidity <= 90.))
|
|
{
|
|
Tolerance = 2.;
|
|
}
|
|
else if (RelHumidity < 10.)
|
|
{
|
|
//Linear from 4 at 0% to 2 at 10%
|
|
Tolerance = -0.2 * RelHumidity + 4.;
|
|
}
|
|
else
|
|
{
|
|
//Linear from 2 at 90% to 4 at 100%
|
|
Tolerance = 0.2 * RelHumidity - 16.;
|
|
}
|
|
break;
|
|
}
|
|
case SHT31:
|
|
{
|
|
Tolerance = 2.;
|
|
break;
|
|
}
|
|
case SHT35:
|
|
{
|
|
if (RelHumidity <= 80.)
|
|
{
|
|
Tolerance = 1.5;
|
|
}
|
|
else //if (80 < RelHumidity)
|
|
{
|
|
//Linear from 0.5 at 80% to 2 at 100%
|
|
Tolerance = 0.025 * RelHumidity - 0.5;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return ReturnValueIfError(Tolerance);
|
|
|
|
}
|
|
|
|
float SHT3x::GetAbsHumTolerance(AbsHumidityScale Scale, SHT3xSensor SensorType)
|
|
{
|
|
/* Dependence of absolute humidity is similar (from 0 to 80C) to P = H*a*exp(b*T),
|
|
* where P is absolute humidity, H is relative humidity, T is temperature (Celsius),
|
|
* a ~= 0.0396, b~=0.0575.
|
|
* So its relative tolerance dP/P = square root [ (dH/H)^2 + (b*dT)^2 ].
|
|
*/
|
|
|
|
float RelHumidityRelTolerance = GetRelHumTolerance(SensorType)/GetRelHumidity();
|
|
float TemperatureRelTolerance = 0.0575*GetTempTolerance(Cel, SensorType);
|
|
RelHumidityRelTolerance*=RelHumidityRelTolerance;
|
|
TemperatureRelTolerance*=TemperatureRelTolerance;
|
|
return ReturnValueIfError(
|
|
GetAbsHumidity(Scale) * sqrt(RelHumidityRelTolerance+TemperatureRelTolerance)
|
|
);
|
|
}
|
|
|
|
void SHT3x::SetMode(SHT3xMode Mode)
|
|
{
|
|
switch (Mode)
|
|
{
|
|
case Single_HighRep_ClockStretch:
|
|
{
|
|
_MeasMSB=0x2C;
|
|
_MeasLSB=0x06;
|
|
break;
|
|
}
|
|
case Single_MediumRep_ClockStretch:
|
|
{
|
|
_MeasMSB=0x2C;
|
|
_MeasLSB=0x0D;
|
|
break;
|
|
}
|
|
case Single_LowRep_ClockStretch:
|
|
{
|
|
_MeasMSB=0x2C;
|
|
_MeasLSB=0x10;
|
|
break;
|
|
}
|
|
case Single_HighRep_NoClockStretch:
|
|
{
|
|
_MeasMSB=0x24;
|
|
_MeasLSB=0x00;
|
|
break;
|
|
}
|
|
case Single_MediumRep_NoClockStretch:
|
|
{
|
|
_MeasMSB=0x24;
|
|
_MeasLSB=0x0B;
|
|
break;
|
|
}
|
|
case Single_LowRep_NoClockStretch:
|
|
{
|
|
_MeasMSB=0x24;
|
|
_MeasLSB=0x16;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
_MeasMSB=0x2C;
|
|
_MeasLSB=0x06;
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void SHT3x::SetTemperatureCalibrationFactors(CalibrationFactors TemperatureCalibration)
|
|
{
|
|
_TemperatureCalibration = TemperatureCalibration;
|
|
}
|
|
|
|
void SHT3x::SetRelHumidityCalibrationFactors(CalibrationFactors RelHumidityCalibration)
|
|
{
|
|
_RelHumidityCalibration = RelHumidityCalibration;
|
|
}
|
|
|
|
void SHT3x::SetTemperatureCalibrationPoints(CalibrationPoints SensorValues, CalibrationPoints Reference)
|
|
{
|
|
_TemperatureCalibration.Factor = (Reference.Second - Reference.First) / (SensorValues.Second - SensorValues.First);
|
|
_TemperatureCalibration.Shift = Reference.First - _TemperatureCalibration.Factor * SensorValues.First;
|
|
}
|
|
|
|
void SHT3x::SetRelHumidityCalibrationPoints(CalibrationPoints SensorValues, CalibrationPoints Reference)
|
|
{
|
|
_RelHumidityCalibration.Factor = (Reference.Second - Reference.First) / (SensorValues.Second - SensorValues.First);
|
|
_RelHumidityCalibration.Shift = Reference.First - _RelHumidityCalibration.Factor * SensorValues.First;
|
|
}
|
|
|
|
void SHT3x::SoftReset()
|
|
{
|
|
SendCommand(0x30, 0xA2);
|
|
}
|
|
|
|
void SHT3x::HardReset()
|
|
{
|
|
if (_HardResetPin<100) //MEGA2560 have only 68 pins
|
|
{
|
|
pinMode(_HardResetPin,OUTPUT);
|
|
digitalWrite(_HardResetPin,LOW);
|
|
delayMicroseconds(1); //Reset pin have to be LOW at least 350ns, so 1 usec is enough. I think, there is no need for micros() for 1 usec.
|
|
digitalWrite(_HardResetPin,HIGH);
|
|
}
|
|
}
|
|
|
|
void SHT3x::HeaterOn()
|
|
{
|
|
SendCommand(0x30, 0x6D);
|
|
}
|
|
|
|
void SHT3x::HeaterOff()
|
|
{
|
|
SendCommand(0x30, 0x66);
|
|
}
|
|
|
|
void SHT3x::SetAddress(uint8_t Address)
|
|
{
|
|
if ((Address == 0x44) || (Address == 0x45))
|
|
{
|
|
_Address = Address;
|
|
}
|
|
else
|
|
{
|
|
_Error = WrongAddress;
|
|
}
|
|
}
|
|
|
|
void SHT3x::SetUpdateInterval(uint32_t UpdateIntervalMillisec)
|
|
{
|
|
if (UpdateIntervalMillisec > 0)
|
|
{
|
|
_UpdateIntervalMillisec = UpdateIntervalMillisec;
|
|
}
|
|
}
|
|
|
|
void SHT3x::SetTimeout(uint32_t TimeoutMillisec)
|
|
{
|
|
if (TimeoutMillisec > 0)
|
|
{
|
|
_TimeoutMillisec = _TimeoutMillisec;
|
|
}
|
|
}
|
|
|
|
void SHT3x::I2CError(uint8_t I2Canswer)
|
|
{
|
|
switch (I2Canswer)
|
|
{
|
|
case 0:
|
|
{
|
|
_Error = noError;
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
_Error = TooMuchData;
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
_Error = AddressNACK;
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
_Error = DataNACK;
|
|
break;
|
|
}
|
|
case 4:
|
|
{
|
|
_Error = OtherI2CError;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
_Error = UnexpectedError;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SHT3x::SendCommand(uint8_t MSB, uint8_t LSB)
|
|
{
|
|
if (_OperationEnabled)
|
|
{
|
|
//Everything is OK, nothing to do
|
|
}
|
|
else
|
|
{
|
|
Wire.begin();
|
|
_OperationEnabled = true;
|
|
}
|
|
Wire.beginTransmission(_Address);
|
|
// Send Soft Reset command
|
|
Wire.write(MSB);
|
|
Wire.write(LSB);
|
|
// Stop I2C transmission
|
|
uint8_t success = Wire.endTransmission();
|
|
}
|
|
|
|
bool SHT3x::CRC8(uint8_t MSB, uint8_t LSB, uint8_t CRC)
|
|
{
|
|
/*
|
|
* Name : CRC-8
|
|
* Poly : 0x31 x^8 + x^5 + x^4 + 1
|
|
* Init : 0xFF
|
|
* Revert: false
|
|
* XorOut: 0x00
|
|
* Check : for 0xBE,0xEF CRC is 0x92
|
|
*/
|
|
uint8_t crc = 0xFF;
|
|
uint8_t i;
|
|
crc ^=MSB;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
crc = crc & 0x80 ? (crc << 1) ^ 0x31 : crc << 1;
|
|
|
|
crc ^= LSB;
|
|
for (i = 0; i < 8; i++)
|
|
crc = crc & 0x80 ? (crc << 1) ^ 0x31 : crc << 1;
|
|
|
|
if (crc==CRC)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
uint8_t SHT3x::GetError()
|
|
{
|
|
return _Error;
|
|
}
|
|
|
|
float SHT3x::ReturnValueIfError(float Value)
|
|
{
|
|
if (_Error == noError)
|
|
{
|
|
return Value;
|
|
}
|
|
else
|
|
{
|
|
if (_ValueIfError == PrevValue)
|
|
{
|
|
return Value;
|
|
}
|
|
else
|
|
{
|
|
return 0.;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SHT3x::ToReturnIfError(ValueIfError Value)
|
|
{
|
|
_ValueIfError = Value;
|
|
}
|