STM32F407 SHT41 I2C Guide: Wiring, HAL Code and CRC

Read temperature and humidity from an SHT41 using STM32F407 I2C1. This guide connects PB6 and PB7, configures STM32CubeF4 HAL, and builds a driver that accepts a sample only after both CRC checks pass.

Start with the wiring and protocol below, or jump to the complete driver in Section 6. Sections 2–5 explain individual steps; use the complete driver when integrating the example to avoid duplicate definitions.

Quick answer

An STM32F407 reads an SHT41 in four stages: write the high-precision command 0xFD to the sensor's 7-bit address 0x44, wait about 10 ms, read six bytes, then verify the separate CRC bytes for temperature and humidity. With the STM32F4 HAL master APIs, pass the device address as (0x44 << 1).

ItemCorrect valueCommon mistake
SHT41 7-bit address0x44Passing 0x44 directly to the STM32F4 HAL master API
HAL address argument0x44 << 1Manually setting the read/write bit as well
High-precision command0xFDTreating the command as a register address
High-precision wait10 ms in this exampleReading immediately after the write
Response lengthSix bytesReading only four bytes
CRC polynomial and initial value0x31 and 0xFFReusing CRC settings from another sensor
Temperature and humidity monitor product concept with front display, controller PCB and separate vented sensor board
Figure 1. Temperature and humidity monitor concept with a separate vented sensor board.

1 How should the STM32F407 and SHT41 be wired

This example connects the STM32F407 I2C1 peripheral to the SHT41. SDA and SCL are open-drain lines, so each needs a pull-up to the sensor's supply rail. If you use a breakout board, check its schematic before adding resistors; the board may already include them.

SHT41 pinFunctionSTM32F407 connectionNote
SDAI2C dataPB7 / I2C1_SDAPull up to the sensor supply
SCLI2C clockPB6 / I2C1_SCLPull up to the sensor supply
VDDSupply3.3 VConfirm the breakout board's supply requirements
GNDGroundGNDShare ground with the microcontroller
STM32F407 I2C1 wiring with PB6 SCL, PB7 SDA, 3.3 V supply and pull-up resistors
Figure 2. STM32F407 I2C1 to SHT41 functional wiring. The 10 kΩ pull-ups are examples, not universal component values. Check existing breakout-board resistors and size the pull-ups for bus capacitance and speed.

First bench test

  • Initialize HAL, system clock, GPIO, I²C and UART.
  • Trigger one SHT41 measurement each second and print the result.
  • After readings are stable, add the display, controller or data logger.

This isolates sensor faults before application logic is added.

2 How should STM32F407 I2C1 be configured

Start at 100 kHz to simplify debugging. In master mode, the MCU's own address has no practical role here and can remain zero. Select 7-bit addressing and disable dual addressing and general call. The following initialization follows the common STM32CubeF4 HAL project structure.

C / STM32CubeF4 HAL
I2C_HandleTypeDef hi2c1;

static void MX_I2C1_Init(void)
{
    hi2c1.Instance = I2C1;
    hi2c1.Init.ClockSpeed = 100000;
    hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
    hi2c1.Init.OwnAddress1 = 0;
    hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
    hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
    hi2c1.Init.OwnAddress2 = 0;
    hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
    hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;

    if (HAL_I2C_Init(&hi2c1) != HAL_OK) {
        Error_Handler();
    }
}

CubeMX normally generates the GPIO alternate-function setup in HAL_I2C_MspInit(). Configure PB6 and PB7 as alternate-function open-drain pins with GPIO_AF4_I2C1. The code below shows a common mapping; if your board routes I2C1 to other pins, follow its schematic and CubeMX configuration.

C / STM32CubeF4 HAL
void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    if (hi2c->Instance == I2C1) {
        __HAL_RCC_GPIOB_CLK_ENABLE();
        __HAL_RCC_I2C1_CLK_ENABLE();

        GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    }
}

Before increasing bus speed

  • Pull-ups: Internal MCU pull-ups do not replace external resistors sized for the actual supply, bus capacitance and any pull-ups already on the module.
  • Rising edges: Longer wiring and higher capacitance slow the bus; choose resistor values for the wiring and target clock rate.
  • NoStretchMode: This configures STM32 slave-mode behavior. It does not set the SHT41 conversion delay in this master-mode example.

3 What are the SHT41 commands and measurement sequence

The SHT41 does not use a conventional register address for these operations. The host writes a one-byte command, waits for the operation, and then reads the result. The relevant commands are:

CommandOperationMaximum time in datasheetResponse
0xFDHigh-precision measurementAbout 8.3 msSix bytes
0xF6Medium-precision measurementAbout 4.5 msSix bytes
0xE0Low-precision measurementAbout 1.6 msSix bytes
0x89Read serial numberNot applicableSix bytes
0x94Soft resetUp to 1 msNo data bytes

A high-precision measurement has five steps:

  1. Send command: Write 0xFD to 7-bit address 0x44.
  2. Wait: Allow 10 ms for this blocking high-precision example.
  3. Read: Request T_MSB, T_LSB, T_CRC, RH_MSB, RH_LSB, RH_CRC.
  4. Validate: Check the CRC after each two-byte measurement word.
  5. Convert: Calculate °C and %RH only after both CRC checks pass.
SHT41 measurement sequence from 0xFD command through conversion delay and six-byte read
Figure 3. A write transaction sends 0xFD; a later read transaction collects six bytes. The 10 ms wait provides margin over the specified 8.3 ms high-precision maximum.

If the host requests data before conversion finishes, the SHT41 may NACK the read header. That can look like an address problem. First confirm that the command write succeeded, then check the delay before the read.

Measurement data can be received only once. The sensor deletes it after the first acknowledged I2C read header, so a bus error during that read calls for a new measurement command rather than repeated attempts to fetch the old result. A design that needs less fixed waiting can retry when a busy sensor NACKs. This example uses 10 ms to cover the 8.3 ms high-precision maximum with a little margin.

4 How is the SHT41 CRC 8 calculated

The SHT41 appends a CRC byte after each two-byte measurement value. Temperature and humidity have independent checksums. Use the sample only after both checks pass.

CRC parameterValue
NameCRC-8
Polynomial0x31, representing x^8 + x^5 + x^4 + 1
Initial value0xFF
Reflected input / outputfalse / false
Final XOR0x00
Check exampleCRC(0xBE, 0xEF) = 0x92

For each data byte, XOR it into the current CRC and process eight bits from the most significant bit. If the top bit is set, shift left and XOR with 0x31; otherwise just shift left. This implementation uses an 8-bit accumulator, so every assignment retains only the low eight bits.

C / SHT41 data processing
static uint8_t SHT41_CalcCRC8(const uint8_t *data, uint8_t len)
{
    uint8_t crc = 0xFF;

    for (uint8_t i = 0; i < len; ++i) {
        crc ^= data[i];
        for (uint8_t bit = 0; bit < 8; ++bit) {
            crc = (crc & 0x80U)
                ? (uint8_t)((crc << 1) ^ 0x31U)
                : (uint8_t)(crc << 1);
        }
    }
    return crc;
}

5 How are raw SHT41 readings converted

The six response bytes appear in this order:

SHT41 six-byte response with separate temperature and humidity CRC bytes and conversion formulas
Figure 4. Byte positions, separate CRC checks, and the datasheet conversion formulas. Assemble each 16-bit value from its most significant byte first.
C / SHT41 data processing
rx[0]  Temperature raw value, most significant byte
rx[1]  Temperature raw value, least significant byte
rx[2]  Temperature CRC
rx[3]  Humidity raw value, most significant byte
rx[4]  Humidity raw value, least significant byte
rx[5]  Humidity CRC

The sensor sends the most significant byte first, so assemble each unsigned 16-bit value in big-endian order:

C / SHT41 data processing
uint16_t raw_t  = ((uint16_t)rx[0] << 8) | rx[1];
uint16_t raw_rh = ((uint16_t)rx[3] << 8) | rx[4];

Apply the datasheet's linear conversion formulas:

C / SHT41 data processing
float temperature_c = -45.0f + 175.0f * (float)raw_t / 65535.0f;
float humidity_rh   =  -6.0f + 125.0f * (float)raw_rh / 65535.0f;

For user-facing relative humidity, clamp the result to the physical range of 0 to 100 %RH:

C / SHT41 data processing
if (humidity_rh > 100.0f) humidity_rh = 100.0f;
if (humidity_rh <   0.0f) humidity_rh =   0.0f;

The datasheet formulas divide by 65535.0f, or 2^16 - 1. Sensirion also permits division by 65536.0f as a computational simplification; it introduces deviations below 0.002 %RH and 0.003 °C. This tutorial retains the original denominator. Floating-point constants prevent integer-division truncation. Clamping is useful for display, while an analysis of boundary behavior may need the unmodified value.

6 How can the SHT41 driver be packaged for reuse

The following driver puts the address, commands, CRC, and measurement flow in one place. SHT41_Read() returns true only when both the bus transaction and CRC checks succeed. The caller should use the output values only after a successful return.

C / STM32CubeF4 HAL
#include "stm32f4xx_hal.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#define SHT41_ADDR_7BIT          0x44U
#define SHT41_ADDR_HAL           (SHT41_ADDR_7BIT << 1)
#define SHT41_CMD_MEASURE_HIGH   0xFDU
#define SHT41_CMD_SOFT_RESET     0x94U
#define SHT41_I2C_TIMEOUT_MS     100U

extern I2C_HandleTypeDef hi2c1;

static uint8_t SHT41_CalcCRC8(const uint8_t *data, uint8_t len)
{
    uint8_t crc = 0xFF;

    for (uint8_t i = 0; i < len; ++i) {
        crc ^= data[i];
        for (uint8_t bit = 0; bit < 8; ++bit) {
            crc = (crc & 0x80U)
                ? (uint8_t)((crc << 1) ^ 0x31U)
                : (uint8_t)(crc << 1);
        }
    }
    return crc;
}

HAL_StatusTypeDef SHT41_SoftReset(void)
{
    uint8_t cmd = SHT41_CMD_SOFT_RESET;
    HAL_StatusTypeDef status = HAL_I2C_Master_Transmit(
        &hi2c1, SHT41_ADDR_HAL, &cmd, 1, SHT41_I2C_TIMEOUT_MS);

    if (status == HAL_OK) {
        HAL_Delay(2); /* Allow margin beyond the 1 ms soft-reset maximum. */
    }
    return status;
}

bool SHT41_Read(float *temperature_c, float *humidity_rh)
{
    uint8_t cmd = SHT41_CMD_MEASURE_HIGH;
    uint8_t rx[6];

    if ((temperature_c == NULL) || (humidity_rh == NULL)) {
        return false;
    }

    if (HAL_I2C_Master_Transmit(&hi2c1, SHT41_ADDR_HAL,
                                &cmd, 1, SHT41_I2C_TIMEOUT_MS) != HAL_OK) {
        return false;
    }

    HAL_Delay(10);

    if (HAL_I2C_Master_Receive(&hi2c1, SHT41_ADDR_HAL,
                               rx, sizeof(rx), SHT41_I2C_TIMEOUT_MS) != HAL_OK) {
        return false;
    }

    if ((SHT41_CalcCRC8(&rx[0], 2) != rx[2]) ||
        (SHT41_CalcCRC8(&rx[3], 2) != rx[5])) {
        return false;
    }

    uint16_t raw_t  = ((uint16_t)rx[0] << 8) | rx[1];
    uint16_t raw_rh = ((uint16_t)rx[3] << 8) | rx[4];

    *temperature_c = -45.0f + 175.0f * (float)raw_t / 65535.0f;
    *humidity_rh   =  -6.0f + 125.0f * (float)raw_rh / 65535.0f;

    if (*humidity_rh > 100.0f) *humidity_rh = 100.0f;
    if (*humidity_rh <   0.0f) *humidity_rh =   0.0f;

    return true;
}

7 How do you read the SHT41 in the main loop

During validation, trigger one read per second and print it over UART. In a product, use a scheduler or timer to avoid long main-loop blocking. This main() shows the call site. In a single main.c, place the Section 6 driver above main(); if you split it into .c and .h files, declare both driver APIs in the header. CubeMX clock, GPIO, and I2C initialization must exist. UART setup and printf redirection are project-specific.

C / STM32CubeF4 HAL
#include <stdio.h>

int main(void)
{
    float temperature = 0.0f;
    float humidity = 0.0f;

    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_I2C1_Init();
    /* Initialize the project's UART and redirect printf here. */

    if (HAL_I2C_IsDeviceReady(&hi2c1, SHT41_ADDR_HAL,
                              3, SHT41_I2C_TIMEOUT_MS) != HAL_OK) {
        printf("SHT41 not found\r\n");
    }

    for (;;) {
        if (SHT41_Read(&temperature, &humidity)) {
            printf("Temperature: %.2f C, Humidity: %.2f %%RH\r\n",
                   temperature, humidity);
        } else {
            printf("SHT41 read failed\r\n");
        }
        HAL_Delay(1000);
    }
}

8 How should an SHT41 read failure be diagnosed

  1. Wrong address format. 0x44 is a 7-bit address; pass (0x44 << 1) to the STM32F4 HAL master API.
  2. Insufficient conversion time. A high-precision 0xFD measurement can take up to about 8.3 ms; this example waits 10 ms.
  3. Incomplete read. Read all six bytes and verify both CRCs. The datasheet permits stopping after a 16-bit word, but this omits checksum protection.
  4. Wrong CRC settings. Use polynomial 0x31 and initial value 0xFF, not settings copied from another sensor.
  5. Unclear humidity-range handling. Clamp display values to 0–100 %RH; keep unclamped values when studying range boundaries.
  6. Treating the SHT41 as a register device. 0xFD is a one-byte command, not a register address for HAL_I2C_Mem_Read(). Use master transmit and receive.
  7. Ignoring pull-ups or supply. SDA and SCL need appropriate external pull-ups. Check whether the module already provides them.

Troubleshoot in this order: device ACK, command write, conversion wait, response length, and CRC. It moves from the bus layer to the data layer, avoiding premature work on conversion formulas or serial-output formatting.

SymptomCheck firstEvidence to look for
HAL_I2C_IsDeviceReady() failsShifted address, wiring, supply, pull-upsAddress and ACK on the bus
Command succeeds but read NACKsMeasurement delayRead succeeds after waiting 10 ms
Read succeeds but CRC failsByte order, length, CRC settingsEach two-byte word has its own CRC
Temperature looks right, humidity does notHumidity indexes and conversionHumidity comes from rx[3] and rx[4]
Values never changeRepeated measurement triggerSend 0xFD before every new sample

9 What should be checked before porting the example

Illustrative STM32F407 and SHT41 development bench with connected sensor module
Figure 5. Illustrative development setup for the STM32F407 and SHT41 tutorial. Use the wiring diagram below and your board schematic to identify the actual connections.
  • Confirm that your board actually uses PB6 and PB7 rather than other I2C1 alternate-function pins.
  • Confirm the module supply voltage and whether SDA and SCL pull-ups are already fitted.
  • Replace the UART instance, system clock setup, and printf redirection with the versions used by your project.

10 Frequently asked questions

Can the SHT41 CRC check be omitted

The protocol permits a master to stop reading before a CRC when it does not need verification. This driver reads all six bytes and checks both CRCs. That keeps corrupted transfers out of control and logging paths and helps distinguish bus faults from conversion mistakes.

Why can calculated humidity exceed 100 percent RH

Near the measurement-range boundaries, the SHT41 conversion formula can produce a value slightly below 0 or above 100 %RH. Clamp values shown to users; retain the unclamped result if you are studying boundary distributions. Such a value alone does not prove that the sensor is damaged.

Why are floating-point values missing from UART output

First confirm that printf is redirected to the intended UART. Some small embedded C libraries do not enable floating-point formatting by default, so %.2f may not print as expected. Enable float formatting for your toolchain or scale the value and print an integer. This symptom is separate from the SHT41 I2C transaction.

11 From a failed read to stable sampling

Return to the opening failure: test the address and wiring with HAL_I2C_IsDeviceReady(), confirm 0xFD and the conversion delay, then verify byte order and both CRCs. These checks isolate bus, timing, and parsing faults.

This small driver can validate a bare-metal project before being adapted to a nonblocking state machine or RTOS task. The address format, timing, response layout, and CRC parameters remain the same.

12 Technical references

Sensirion SHT4x datasheet covers timing, response format, CRC, and conversion; ST STM32F407 datasheet covers pin mapping; ST STM32F4 HAL I2C driver covers the master APIs.

From a working driver to a reliable sensor assembly

Before integrating the STM32F407 driver into your application, confirm the board pin mapping, supply and pull-up rails, conversion delay, and both CRC checks. Then verify repeated sampling and error recovery with the final wiring, power supply and enclosure.

Comparing sensors for a new design? Include NYFEA FHT41 as a candidate in your selection review. Compare its electrical limits, I²C commands, CRC behavior, accuracy across your operating range, power consumption, package and pinout with the SHT41 requirements. Validate the selected device on your STM32F407 board before reusing the driver or approving the assembly.

More Product Selection

NYFEA FHT30 digital temperature and humidity sensor

FHT30

FHT30 targets digital temperature and relative-humidity measurement where dual I²C addresses, periodic acquisition, an alert output and a compact open-cavity package are useful.

Learn More
NYFEA FHT31 digital temperature and humidity sensor

FHT31

The ±2%RH and ±0.2 °C figures are typical values, not guaranteed maximum limits across every temperature and humidity combination. Use the tolerance plots, response time, drift, chemical limits and assembled-product tests when setting a system accuracy claim.

Learn More
NYFEA FHT40 digital temperature and humidity sensor

FHT40

Typical accuracy is not a universal maximum. Review the humidity/temperature tolerance maps, response time, drift, chemical exposure, self-heating and mechanical placement before setting an end-product accuracy claim.

Learn More
NYFEA FHT41 digital temperature and humidity sensor

FHT41

FHT41 is a high-precision digital temperature and humidity sensor for low-power systems. Its compact package, selectable repeatability and integrated heater support space-constrained consumer, cabin and industrial designs.

Learn More
NYFEA FHTC3 digital temperature and humidity sensor

FHTC3

FHTC3 is a factory-calibrated digital sensor for simultaneous temperature and relative-humidity measurement. Its low standby current, two measurement-power modes and small leadless package suit compact connected products.

Learn More
NYFEA FT18B20 digital temperature sensor

FT18B20

FT18B20 is intended for direct digital temperature measurement on a single-drop or multidrop 1-Wire bus. Its ROM identity, alarm search, configurable conversion time and optional two-wire parasite-power connection suit distributed temperature sensing.

Learn More
© 2023 All Rights Reserved. www.nyfea.com Terms of Use | Privacy Policy