Changing HTU21D Temperature and Humidity Resolution

By default the HTU21D sets the temperature and humidity sensor resolutions are set at their highest. These default values are, humidity = 12bit and temperature = 14bit. This is all well and good for most situations when you are trying to get the best performance from your device. However, a higher resolution comes at the expense of increased measuring time. Consequently, a longer measuring time will increase the current consumption too.

To demonstrate, I’ve tabulated the maximum measuring times for the different sensor resolution modes from the HTU21D_datasheet.

Resolution Humidity Temperature
14bit NA 50ms
13bit NA 25ms
12bit 16ms 13ms
11bit 8ms 7ms
10bit 5ms NA
08bit 3ms NA

If you do want to change the sensor reading resolutions, this can be achieved by modifying the User Register. The User Register is 8bits in length and can be used to modify the sensor resolutions as well as do some other functions. (I’ve modified the layout of datasheet’s layout of the User Register table for clarity, all the info is still there.)

Bit Function Default
7 Measurement Resolution 0
6 Battery Status
‘0’: VDD > 2.25V
‘1’: VDD < 2.25V
0
5 Reserved 0
4 Reserved 0
3 Reserved 0
2 Enable on-chip heater 0
1 Disable OTP reload 1
0 Measurement Resolution 0

If you want to change the measuring resolution then bits 7 and 0 need to be changed. Since there are only 2bits, then there can only be 4 possible combinations. The sensor resolution combinations available are given in the following table found on page 13 of the  datasheet.

Bit 7 Bit 0 Humidity Temperature
0 0 12bit 14bit
0 1 8bit 12bit
1 0 10bit 13bit
1 1 11bit 11bit

If you do want to change sensor resolutions, all you have to do is write a new byte value to the User Register address. To do this, there is a dedicated command Write User Register available, it has the value of 0xE6. Once, you have issued the Write User Register command, you must send another byte of data containing your new value for the User Register. Once you’ve done that, the sensor will begin using your new values in the User Register.

To confirm that you have correctly written to the User Register, it is possible to read the contents of the User Register. This is done with the command Read User Register  command code 0xE7. Once issued, you must then read 1 byte of data from the I2C bus  which is the contents of the User Register.

To read and write to the User Register, I’ve created two very quick functions to demonstrate how easy it is to modify the User Register. (There is room for improvement with this code. In particular, it would be best to read the value of the User Register and then apply a bit mask to set only the bits you are interested in. This way you can preserve the status of bits you don’t want to affect.) The code, modifies the User Register by changing the sensor resolutions both to 11bits. Once changed, it outputs over serial the sensor readings as well as the the value of the User Register to demonstrate that its value has been changed.

//Code to read the temperature and humidty from a HTU21D temperature sensor
//Code can modify the value of the User Register.
//Modifying the User Register allows for different sensor resolutions to be used.
//Tim Hansen 19/2/15 - www.steelcityelectronics.com
//Protocol: I2C (TWI)
//Specs - Temperature: -40 to 125 degrees Celcius
// - Humidity: 0 to 100% relative humidity
// - Voltage: 3.8VDC max

//What libraries do we need?
#include //The "Wire.h" library is required to use the I2C commands
#include //Use the "math.h" library so we can compute the base 10 logarithm for the dew point

//GLOBAL VARIABLES
//Avoid global variables where practical. Global variables consume SRAM as soon as the micro starts. Whereas a local variable declared inside
//a function only consumes SRAM when the function is called.

void RequestMeas(int Address, int CommandCode)
{
Wire.beginTransmission(Address); // The HTU21D will respond to its address
Wire.write(CommandCode); // request the measurement, ie temperature or humidity
Wire.endTransmission();
return;
}

long ReadMeas(int Address)
{
//A measurement from the HTU21D is returned in 3 consecutive bytes. See page 11 of the datasheet
//the order of returend data is: [DATA (Most Sig Byte)], [DATA (Least Sig Byte)], [Checksum]
//We will ignore the checksum to save computation time and complexity. Page 14 of the datasheet details how to calculate the checksum

byte DataHIGH; //The location to store the high measurement byte
byte DataLOW; //The low measurement byte
byte CRC; //If you want to compute the CRC feel free to do so.

long Data; //The temporary memory location we are storing our data in as we receive it from the HTU21D
//Each packet of data is only 8bits in length, but if we do a bit shift we can store the 2 packets of
//measurement data in a variable. Remember you can only return 1 variable when a function finishes.
//Ideally, we'd use an int to store the result, however, at high measurement values in 14bit mode, an int overflows.
//To prevent overflow, we must use a single long (32bits).

Wire.requestFrom(Address, 3); //We are requesting data from the HTU21D and there will be 3x bytes that must be read.
while(Wire.available()) // Check for data from the HTU21D
{
DataHIGH = Wire.read(); // Read high byte
DataLOW = Wire.read(); // Read low byte
CRC = Wire.read(); // Read the CRC byte
}

//OK, so now we are going to 'pack' our 2x bytes of data measuremetns into a single int so only 1 variable is returned.
//To do this, we will use the left bitshift operator '&lt;&lt;'. What this does is push all the bits across to the left.
//FUN FACT: performing a left bit shift is equivalent to multiplication by 2 for each shift. The reverse is true for right
//bitshift which is equivalent to dividing by 2. This is a far more efficient method to perform multiply or divide by 2.

Data = DataHIGH; //The data sits in the low byte of 'int Data'.
//Ie, Data = [00000000][00000000][00000000][8 bits of DataHIGH]
Data = Data &lt;&lt; 8; //Shift all the bits to the left for 8 bits. The old locations fill with zeros.
//Ie, Data = [00000000][00000000][8 bits of DataHIGH][00000000]
Data = Data + DataLOW; //Now simply add the low byte of data to the int.
//Ie, Data = [00000000][00000000][8 bits of DataHIGH][8 bits of DataLOW]

return Data; //return our measurement.
}

byte ReadUserRegister(int Address)
{
byte Data;

Wire.requestFrom(Address, 1); //We are requesting data from the HTU21D User Register - there is only 1 byte.
while(Wire.available()) // Check for data from the HTU21D
{
Data = Wire.read(); // Read byte
}

return Data;
}

void WriteUserRegister(int Address, byte RegData)
{
byte const UserRegWrite = 0xE6;
Wire.beginTransmission(Address); // The HTU21D will respond to its address
Wire.write(UserRegWrite); // We want to write to the User Register
Wire.write(RegData); // request the measurement, ie temperature or humidity
Wire.endTransmission();
return;
}

void setup()
{
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600); // start serial for output
}

void loop()
{

//Set the address of the HTU21D. This detail is hidden at the bototm of page 10 of the datasheet.
const int I2C_address = 0x40; // I2C write address. Note that 'const' has been declared. If you try to write to this variable
// anywhere else in your code you'll get a compilation error. This prevents you from doing silly things.

const int TempCode = 0xE3; // A temperature measurement is executed with this command code. See page 11 of datasheet.
const int HumidCode = 0xE5; // A humidity measurement is executed with this command code. See page 11 of datasheet.
const int UserRegRead = 0xE7; //Read teh contents of the user register
//const int UserRegWrite = 0xE6; //Write to the user register
const int SoftReset = 0xFE; //Perform a soft reset

const float TempCoefficient = -0.15; //The temperature compensation coefficient value to guaratnee RH accuracy between 20-80%RH

const float DewConstA = 8.1332; //Constants required to calclulate the partial pressure and dew point. See datasheet page 16
const float DewConstB = 1762.39;
const float DewConstC = 235.66;

long Temperature; //Perform our calculation using LONG to prevent overflow at high measuremetn values when using 14bit mode.
long Humidity;

float TemperatureFL; //Variable to store our final calculated temperature measurement
float HumidityFL;
float HumidityCompFL; //%RH value that has been temperature compensated to gurantee performance of +-3% between 20-80%RH
float ParitalPressureFL; //Calculated partial pressure in mmHg. used to calculate Dew Point.
float DewPointFL; //calculated Dew Point in degrees Celcius

byte UserRegister; //The value of the HTU21D User Register

delay(100); // a short delay to let everything stabilise

while(true) // execute all the instructions in this loop forever
{
delay(10); //add a small delay to slow things down

RequestMeas(I2C_address, TempCode); //Request the current temperature
Temperature = ReadMeas(I2C_address); //read the result

//We must now convert our temp measurement into an actual proper temperature value
//According to page 15, the conversion equation is: Temp = -46.85 + 175.72 x TeampMeas / 2^16
//Becasue the result will have decimal places in the result we must use a Float datatype.
TemperatureFL = -46.85 + 175.72*(Temperature/pow(2, 16));

delay(10); //add a small delay to slow things down

RequestMeas(I2C_address, HumidCode); //Request the current humidity
Humidity = ReadMeas(I2C_address); //read the result

delay(10); //add a small delay to slow things down

//We are going to change the Resolutions to RH=11bit, Temp = 11bit. Therefore set bits 7 and 0 to 1.
//Important, we must preserve the 'OTP Reload - bit 1' default value of '1'.
WriteUserRegister(I2C_address, 0b10000011); //0b10000011
delay(10); //add a small delay to slow things down

RequestMeas(I2C_address, UserRegRead); //Request the contents of the User Register
UserRegister = ReadUserRegister(I2C_address); //read the result

//We must now convert our humidity measurement into an actual proper humidity value
//According to page 15, the conversion equation is: Humidity = 6 + 125 x TeampMeas / 2^16
//Because the result will have decimal places in the result we must use a Float datatype.
HumidityFL = -6 + 125*(Humidity/pow(2, 16));

//The relative humidty value read from directly from the chip is not the optimised value.
//We must perfrom the 'temperature Coefficient Compensation Equation" specified on page 4 of the datasheet
//RHcompensated = RHactual + (25 - TEMPactual) * coefficient
//The coefficient value is -0.15%RH/C - from page 3 of datasheet
HumidityCompFL = HumidityFL +(25 - TemperatureFL) * TempCoefficient;

//To calculate the dew point, the partial pressure must be determined first. See datasheet page 16 for details.
//ParitalPress = 10 ^ (A - (B / (Temp + C)))
ParitalPressureFL = (DewConstA - (DewConstB / (TemperatureFL + DewConstC)));
ParitalPressureFL = pow(10, ParitalPressureFL);

//Dew point is calculated using the partial pressure, humidity and temperature.
//The datasheet on page 16 is doesn't say to use teh temperature compensated
//RH value. It says "Ambient humidity in %RH, computed from HTU21D(F) sensor". Therefore, I am going to use
//the raw RH value straight from the sensor.
//DewPoint = -(C + B/(log(RH * PartialPress /100) - A ))
//Arduino doesn't have a LOG base 10 function. But Arduino can use the AVRLibC libraries, so we'll use the "math.h".
DewPointFL = (HumidityFL * ParitalPressureFL / 100); //do the innermost brackets
DewPointFL = log10(DewPointFL) - DewConstA; //we have calculated the denominator of the equation
DewPointFL = DewConstB / DewPointFL; //the whole fraction of the equation has been calculated
DewPointFL = -(DewPointFL + DewConstC); //The whole dew point calculation has been performed

//All the calculations are complete, lets now display our values for temperature and humidity on the serial port.
Serial.print("Temperature: ");
Serial.print(TemperatureFL);
Serial.print("C");

Serial.print("\t");
Serial.print("Humidity (raw): ");
Serial.print(HumidityFL);
Serial.print("%RH");

Serial.print("\t");
Serial.print("Humidity (Comp): ");
Serial.print(HumidityCompFL);
Serial.print("%RH");

Serial.print("\t");
Serial.print("Partial Press: ");
Serial.print(ParitalPressureFL);
Serial.print("mmHg");

Serial.print("\t");
Serial.print("Dew Point: ");
Serial.print(DewPointFL);
Serial.print("C");

Serial.print("\t");
Serial.print("User Register: ");
Serial.println(UserRegister, BIN);
}

return;
}

Share your thoughts

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s