ESP32 (24) – I2C a pratical example with HTU21D sensor

luca 13/10/2017 0

In my previous tutorial I explained how to use the I2C driver included in the esp-idf framework to communicate with I2C devices. Today I’ll show you a pratical example: the use of a temperature/humidity sensor.

The sensor

For this tutorial I chose the HTU21D sensor by Te Connectivity. This sensor offers a good level of accuracy and it’s available, already soldered on a breakboard, for few euros:

htu21d-00a htu21d-00b


Sparkfun was the first manufacturer to sell a board based on this sensor… even if now this board is retired, other websites (for example Banggood) sell similar products.

To connect the sensor to your esp32 development board, first you have to choose which pins will be used for SDA and SCL signals. Besides these, you have also to connect VDD (3.3V) and GND:

htu21d-00d htu21d-00e

htu21d-00f htu21d-00g

Datasheet and commands

After having completed the physical connection between the devboard and the sensor, you have to understand how to write a program that communicates with it. The first thing to do is certainly to read the datasheet of the sensor (here in PDF format). Do not be scared if you realize that it contains 21 pages of technical details, in the following paragraphs I’ll explain what are the information you absolutely have to know!

On page 10, it starts the chapter about the communication protocol (COMMUNICATION PROTOCOL WITH HTU21D(F) SENSOR). Immediately you read that the sensor offers an I2C slave interface with address 0x40; just below you can also find the table with the supported commands:


The sensor offers two different modes when measuring temperature and humidity:

  • hold master
  • no hold master

In the first mode, the sensor blocks the clock signal (SCK) during measurement process: this means that the master can send the read command only when the measure is completed. In the second mode instead the master can operate on the bus (for example send commands to other devices) during the measurement process.

In no hold mode the master, after having sent the “trigger maesurement” command, must wait for the sensor to complete the measurement before reading the value. You can verify if the measuring process is complete sending to the sensor a read command and wait for the ACK: if you receive it, it means that the value is ready.

The sensor returns a raw value of 16bits (= 2 bytes). In addition to the value, it also returns a checksum byte; this byte allows the master to verify that no errors occurred when data was transmitted on the bus.

In the previous post you’ve already learned how to query a slave device; therefore the instructions to read the temperature value from a HTU21D sensor in no hold mode are:

// constants
#define HTU21D_ADDR			0x40
// send the command
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_write_byte(cmd, (HTU21D_ADDR << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, TRIGGER_TEMP_MEASURE_NOHOLD, true);
ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_RATE_MS);
// wait for the sensor (50ms)
vTaskDelay(50 / portTICK_RATE_MS);
// receive the answer
uint8_t msb, lsb, crc;
cmd = i2c_cmd_link_create();
i2c_master_write_byte(cmd, (HTU21D_ADDR << 1) | I2C_MASTER_READ, true);
i2c_master_read_byte(cmd, &msb, 0x00);
i2c_master_read_byte(cmd, &lsb, 0x00);
i2c_master_read_byte(cmd, &crc, 0x01);
ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_RATE_MS);

The code above waits 50ms before fetching the data; from the datasheet you can indeed read that the maximum measuring time is exactly 50ms for the temperature value at the maximum resolution:


To convert the raw value into the “real” temperature (in °C) or humidity (in %) you can use the formulas in the datasheet:

uint16_t raw_value = ((uint16_t) msb << 8) | (uint16_t) lsb;
float temperature = (raw_value * 175.72 / 65536.0) - 46.85;
float humidity = (raw_value * 125.0 / 65536.0) - 6.0;


The sensor offers 4 different resolution combinations for temperature and humidity:

  • humidity 12bit, temperature 14bit
  • humidity 8bit, temperature 12bit
  • humidity 10bit, temperature 13bit
  • humidity 11bit, temperature 11bit

You can select the resolution changing the value of a configuration register with the command write user register. Specifically, the registry bits to be modified are bit 0 and bit 7:



The algorithm for verifying the CRC value is explained in the datasheet.

The polynomial used is x^8 + x^5 + x^4 + 1, in binary:


First add to the polynomial as many zeroes as the bit length of the CRC:


The resulting value, in hexadecimal, is 0x98800.

Add, at the right of the value you want to verify, the CRC value received from the sensor as third byte:

uint32_t row = (uint32_t)value << 8;
row |= crc;

Then check the input bit above the leftmost divisor bit: if the bit is 1, the input value is XORed to the divisor. Finally, the divisor is shifted one bit right:

for (int i = 0 ; i < 16 ; i++) {
 if (row & (uint32_t)1 << (23 - i)) row ^= divisor;  divisor >>= 1;

If, at the end of the process, the row value equals zero, the CRC is verified.


I developed a component for the esp-idf framework that implements what explained in this post. The component is available on Github and the documentation about its use is published in a dedicated page.

Here’s a video about it:

Leave A Response »

This website uses cookies to ensure you get the best experience on our website maggiori informazioni

Questo sito utilizza i cookie per fonire la migliore esperienza di navigazione possibile. Continuando a utilizzare questo sito senza modificare le impostazioni dei cookie o clicchi su "Accetta" permetti al loro utilizzo.