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