arduino2/libraries/SHT3x-master/SHT3x.cpp
2020-11-06 13:17:55 +01:00

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;
}