Having both temperature and humidity sensor capabilities in a single package makes the HTU21D an ideal sensor for a home weather station. As such, if you’re a person who is interested in knowing the current weather conditions, you’ll probably want to know what the Dew Point is too. But before we can get carried away, we need to learn how to use an additional library.
Thankfully, the manufacturers of the HTU21D provide details on how you can calculate Dew Point. At first glance, the provided equations on how to calculate the Dew Point might be daunting, but as we’ll see this is not complicated if we break up the steps.
Firstly, we’ll declare the provided constants as constants in our code. Using the variable qualifier const is a good idea. It makes the variable “read only” and is a good way of preventing you from changing its value accidentally. If you do try to modify the value of a variable declared as const you will get compilation errors telling you that you are doing something wrong. In the example below, “DewConstC” has been declared as a const and assigned the value of 235.66, on the following line, DewConstC has been assigned a new value of 0. When this code is compiled the error “assignment of read-only variable ‘DewConstC'” is raised alerting you of your mistake. If you did not declare DewConstC as a const, then if you compiled this code you would not get an error and you could spend a long time trying to understand why your code was buggy.
Next we’ll declare two new float variables to store our dew point calculations, one for the partial pressure and the other for the dew point. We’ll call them something recognisable, PartialPressureFL and DewPointFL. They have been declared as float datatype because our results will contain a decimal point.
To calculate the partial pressure, we’ll follow the provided formula. To make reading the code easier, we’ll break it up into two steps. This makes diagnosing your code simpler, and if you ever revisit your code in the future, you’ll find it easier to read and understand.
Next we’ll calculate the Dew Point. If you take a look at the formula provided in the datasheet you’ll see that we need to take the base 10 logarithm. The Arduino library does not have a function to perform the calculation of logarithms. However, if you scroll to the bottom of the Arduino language reference page, you’ll see the following clue.
What this means is, we can include any of the AVR LibC libraries in our code and therefore use the functions in that library. There are many additional libraries available with many useful functions. Check out the ARV LibC library reference page and look for <math.h>. You’ll see that there a many mathematics functions available, what we are interested in though is calculating the base 10 logarithm.
If you scroll down through the list of functions you’ll find, ‘double log10(double __x)‘. This function will accept a variable of datatype double and calculate its base 10 logarithm. Once calculated, its result will also be of datatype double. This suits us just fine as we will only be using a float datatype.
To use our new log10 function, we must tell the Arduino to use the library otherwise when we compile we’ll get an error. This is a simple step of inserting a new #include line of code.
The equation to calculate the dew point appears complicated, but if you take it one step at a time, it is very simple to calculate. For this example, we’ll take four lines to calculate the result. You can also see the use of our new function log10.
Now, you can add some extra lines to output our freshly calculated dew Point values to the serial terminal.
The code should compile with no errors and if you open the serial monitor, you will see something similar to the following.
For the complete code listing, copy and paste the following into a new Arduino sketch and compile.
//Code to read the temperature and humidty from a HTU21D temperature sensor //Tim Hansen 12/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 //REVISION RECORD //12/2/15 - Rev1 - Tim Hansen //11/6/15 - Rev2 - Changed Dew Point Calulation to use Compensated RH% value instead of raw RH%. // Change suggested by Bjoern Kasper, refer to Moliere h-x-diagram. // Similar concept when calculating gas flow, you should use the temp compensated differential pressure. //What libraries do we need? #include <Wire.h> //The "Wire.h" library is required to use the I2C commands #include <math.h> //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 '<<'. 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 << 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. } 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 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 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 //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 doesn't say to use the temperature compensated //RH value and is ambiguous. It says "Ambient humidity in %RH, computed from HTU21D(F) sensor". //However, as Bjoern Kasper pointed out, when considering a Moliere h-x-diagram, temperature compensated RH% should be used. //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 = (HumidityCompFL * 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.println("C"); } return; }
For a practical application for your HTU21D sensor – make a 3D printed Stevenson Screen weather station for your sensor.
Hello,
thank you for your great tutorial.
One thing I have to note: you said in your comments “The datasheet on page 16 is doesn’t say to use the 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.”
Doing like that, you are going to make a mistake. Comparing the values temperature, humidity and dew point temperature with h-x-diagram (Molliere) it’s clear that you have to use the compensated humidity value instead. Whether the datasheet doesn’t say which Molliere-diagram to use (i. e. which ambient pressure) …
Cheers,
Bjoern.
LikeLike
Thanks for the feedback Bjoern.
The code has been modified to use temperature compensated RH values instead of raw RH as you suggested.
Cheers,
Tim
LikeLike
Hello Tim,
very interesting read. Very much in line with a project I am working on…and got stuck in the process. Unfortunately I am not much of a programmer and could very much need your help, to put me back on track.
If you are up for a new challenge, I would be glad to hear from you via email.
LikeLike
Very usefull script. A sugestion:
insert degree character in serial.print
…
Serial.print(“Temperature: “);
Serial.print(TemperatureFL);
Serial.print((char)176); //Degrees character
Serial.print(“C”);
…
LikeLike