SHT40 with ESP32: Wiring, Complete I2C Code and Valid Readings

Direct answer. Seeing 0x44 in an I2C scan is a useful start, but it is not yet a temperature or humidity reading. Connect the specified SHT40 breakout to a 3.3 V ESP32 bus, send the single-shot command 0xFD, wait for conversion, and read all six response bytes. Check the CRC for both measurements before converting them to °C and %RH. The complete sketch below performs each step and prints a status that tells you where a failure occurred. To judge accuracy in a finished device, compare the assembled sensor with an independent reference.

Illustrative ESP32 development board linked to a small SHT40-class breakout
A representative ESP32 and remote sensor arrangement. Use the wiring map below for the specified breakout pins.

What is the SHT40?

The SHT40 is a compact digital temperature and relative-humidity sensor in Sensirion’s SHT4x family. The bare sensor is a four-contact DFN package with a sensing opening centered on its top face. This guide uses the SHT40 on a breakout board, which brings its power and I²C connections to accessible pins for the ESP32 test.

Check the official SHT4x datasheet for the bare sensor’s package and electrical limits; check the breakout specifications for its board-level voltage rating.

Package-level illustration of a bare four-contact SHT40 sensor with a centered top sensing opening
The bare sensor is much smaller than the breakout board used for the wiring steps below.

1. Know which boards this guide uses

The wiring and code below are written for an ESP32-DevKitC V4 with ESP32-WROOM-32E, an Adafruit SHT40 breakout (PID 4885), and the Arduino-ESP32 Wire library. USB powers the ESP32; its 3V3 pin powers the breakout. This particular breakout uses 7-bit I2C address 0x44. Check the labels on your own boards before copying the wiring: other ESP32 variants and SHT40 carriers can differ.

Voltage is the first distinction to get right. The bare SHT4x sensor is specified for 1.08–3.6 V. Adafruit's PID 4885 breakout accepts 3.3–5 V at VIN because it has a regulator and I2C level shifting. The breakout's 5 V capability does not apply to an unprotected sensor IC.

2. Connect four wires and check the supply

ESP32-DevKitC V4 Adafruit SHT40 PID 4885 Connection
3V3 VIN Supply the breakout at 3.3 V
GND GND Share ground
GPIO21 SDA I2C data
GPIO22 SCL I2C clock

Disconnect USB power before moving jumpers. Connect the breakout's VIN, not its 3V regulator-output pad. Its SDA and SCL already have pull-ups; if several modules share the bus, check their combined pull-up resistance. The sketch uses Wire.begin(21, 22), so changing ESP32 pins also means changing those two numbers in code. If the sensor disappears when connected, measure the supply at the breakout terminals before changing software.

ESP32 GPIO21 to SHT40 SDA, GPIO22 to SCL, 3V3 to VIN and GND to GND
The wiring map matches the pins declared in the sketch. Check the breakout's VIN label and your board revision before connecting power.
Illustrative four-wire connection between an ESP32 development board and an SHT40 breakout
Illustrative four-wire layout. Follow the pin names in the wiring map above and confirm them on your own boards.

3. Scan for 0x44, then verify a measurement

The sketch scans the I2C bus once at startup. An ack_only row means a device acknowledged 0x44 at that moment; it does not identify the device or verify temperature or humidity. The loop then sends 0xFD, waits 10 ms, reads six bytes, and checks the temperature and humidity CRCs separately. It prints crc_ok only after both CRCs and the temperature-range check pass.

If startup prints no_ack, the loop still retries 0x44 at about two-second intervals. While that address remains unreachable, expect command_nack rows. Disconnect USB power before checking VIN, common ground, GPIO21-to-SDA and GPIO22-to-SCL continuity. If the scan reports another address, identify that device and verify the exact SHT40 order code before changing SHT40_ADDR; the Adafruit PID 4885 used here is fixed at 0x44. Reset after rewiring to run the startup scan again.

SHT40 ESP32 I2C check: 0x44 ack_only followed by a 0xFD six-byte response with separate temperature and humidity CRCs
An address ACK confirms only a bus response. A complete six-byte measurement with both CRCs passing produces crc_ok; accuracy of the assembled product still needs a reference check.

4. Upload one complete sketch

In Arduino IDE, select ESP32 Dev Module for this DevKitC-class board and choose its serial port. Copy the entire sketch below, upload it, then open Serial Monitor at 115200 baud. The sketch needs only Arduino.h and Wire.h; it does not depend on a separate SHT40 library.

Complete Arduino-ESP32 sketch · SHT40 single-shot CRC check
#include <Arduino.h>
#include <Wire.h>

// ESP32-DevKitC V4 (ESP32-WROOM-32E) + Adafruit SHT40 breakout PID 4885.
// Both boards use 3.3 V logic in this example; use the breakout VIN pin.
constexpr int SDA_PIN = 21;
constexpr int SCL_PIN = 22;
constexpr uint8_t SHT40_ADDR = 0x44;
constexpr uint32_t SAMPLE_INTERVAL_MS = 2000;

uint8_t crc8(const uint8_t* data, size_t length) {
  uint8_t crc = 0xFF;
  for (size_t i = 0; i < length; ++i) {
    crc ^= data[i];
    for (uint8_t bit = 0; bit < 8; ++bit) {
      crc = (crc & 0x80) ? (uint8_t)((crc << 1) ^ 0x31)
                         : (uint8_t)(crc << 1);
    }
  }
  return crc;
}

void printPrefix(uint8_t level) {
  Serial.print(millis());
  Serial.print(',');
  Serial.print(level);
  Serial.print(F(",0x44,"));
}

void printStatus(uint8_t level, const __FlashStringHelper* status) {
  printPrefix(level);
  Serial.print(F(",,"));
  Serial.println(status);
}

void scanBus() {
  bool foundTarget = false;
  for (uint8_t address = 1; address < 127; ++address) {
    Wire.beginTransmission(address);
    if (Wire.endTransmission() == 0) {
      if (address == SHT40_ADDR) {
        foundTarget = true;
        printStatus(1, F("ack_only"));
      } else {
        Serial.print(F("# Other I2C device at 0x"));
        if (address < 16) Serial.print('0');
        Serial.println(address, HEX);
      }
    }
  }
  if (!foundTarget) printStatus(0, F("no_ack"));
}

void takeSample() {
  Wire.beginTransmission(SHT40_ADDR);
  Wire.write((uint8_t)0xFD);  // High-repeatability, single-shot measurement.
  if (Wire.endTransmission() != 0) {
    printStatus(0, F("command_nack"));
    return;
  }

  delay(10);  // SHT4x datasheet: 8.3 ms maximum for high repeatability.
  const uint8_t count = Wire.requestFrom(SHT40_ADDR, (uint8_t)6);
  if (count != 6) {
    while (Wire.available()) Wire.read();
    printStatus(1, F("short_read"));
    return;
  }

  uint8_t frame[6];
  for (uint8_t i = 0; i < 6; ++i) frame[i] = Wire.read();
  if (crc8(frame, 2) != frame[2] || crc8(frame + 3, 2) != frame[5]) {
    printStatus(2, F("crc_error"));
    return;
  }

  const uint16_t rawT = ((uint16_t)frame[0] << 8) | frame[1];
  const uint16_t rawRH = ((uint16_t)frame[3] << 8) | frame[4];
  const float temperatureC = -45.0f + 175.0f * rawT / 65535.0f;
  float humidityRH = -6.0f + 125.0f * rawRH / 65535.0f;
  if (humidityRH < 0.0f) humidityRH = 0.0f;
  if (humidityRH > 100.0f) humidityRH = 100.0f;

  if (temperatureC < -40.0f || temperatureC > 125.0f) {
    printStatus(2, F("outside_temperature_spec"));
    return;
  }

  printPrefix(3);
  Serial.print(temperatureC, 2);
  Serial.print(',');
  Serial.print(humidityRH, 2);
  Serial.println(F(",crc_ok"));
}

void setup() {
  Serial.begin(115200);
  delay(250);
  Wire.begin(SDA_PIN, SCL_PIN);
  Wire.setClock(100000);
  Serial.println(F("ms,level,address,temperature_c,humidity_rh,status"));
  scanBus();
}

void loop() {
  takeSample();
  delay(SAMPLE_INTERVAL_MS);
}

Each measurement returns temperature MSB, temperature LSB, temperature CRC, humidity MSB, humidity LSB, humidity CRC. The sketch sends 0xFD, waits 10 ms against the datasheet's 8.3 ms maximum high-repeatability conversion time, and checks the two CRC-8 bytes separately (polynomial 0x31, initial value 0xFF). It rejects a short or corrupted response instead of repeating an old number. Calculated RH is clipped to 0–100 %RH for display. If the log shows exactly 0 or 100 %RH, the raw value may have been clipped; crc_ok does not establish that ambient humidity was exactly at that boundary. Retain the raw humidity word when debugging boundary readings. The two-second pause between attempts is only a convenient bring-up interval.

5. Read the log: from address to usable data

The first line is ms,level,address,temperature_c,humidity_rh,status. The following shows the format, not measured test data:

ms,level,address,temperature_c,humidity_rh,status
<elapsed_ms>,1,0x44,,,ack_only
<elapsed_ms>,3,0x44,<temperature>,<humidity>,crc_ok

ms is elapsed ESP32 time, not a wall-clock timestamp. Keep these four questions separate:

  1. Did the address answer? ack_only establishes an I2C response, not sensor identity.
  2. Did six bytes arrive? short_read means the frame was incomplete. A complete frame can still be wrong.
  3. Did both CRCs pass? crc_ok marks a usable bring-up sample after conversion and a temperature-range check; crc_error must be discarded. CRC checks transmission integrity, not calibration.
  4. Does the assembled product measure correctly? Compare stable, time-aligned temperature and RH readings with a suitable independent reference under representative conditions. Firmware cannot award itself this level.

The numeric level is a quick progress marker: 0 means no usable response, 1 means an address answered or a read was incomplete, 2 means a complete frame failed integrity or range checks, and 3 means a CRC-checked measurement was printed. The status text gives the precise reason. The fourth question—accuracy of the finished product—cannot be answered by this sketch. Add a synchronized host timestamp if you need to compare longer logs with a reference instrument.

6. Troubleshoot the first failed step

What you see What it tells you Check next
no_ack at startup Nothing answered at 0x44 Breakout voltage, common ground, SDA/SCL continuity, actual part address and other bus devices.
command_nack after an earlier ACK Communication changed or the sensor did not accept the command Supply and bus stability, wiring, address and whether the device is still busy.
short_read Fewer than six response bytes arrived Conversion delay, wiring length, pull-ups and I2C signal quality.
crc_error Six bytes arrived but failed integrity checking Ground path, pull-ups, leads and how often the error recurs. Do not use that sample.
crc_ok but implausible T or RH The transfer passed; the environment or device may still be wrong Heat from the host, blocked airflow, moisture exposure, wrong device identity and a reference comparison.

The SHT4x does not clock-stretch. A read attempted before conversion completes can receive a NACK. A successful read also consumes that measurement, so send a new command for the next sample. If failures are intermittent, retain failed rows in the log instead of graphing only the successful values.

7. When a plausible reading is still wrong

Suppose temperature rises and RH falls after you close the enclosure. That pattern can occur when the sensor sees air warmed by the ESP32 regulator, a charger or a display rather than the air you intend to measure. Relative humidity depends on temperature; a sound I2C transaction cannot correct a placement error.

Keep the sensing opening in representative airflow, away from warm copper, trapped pockets and handling. Allow the assembly to settle after power-up or an enclosure change, then record temperature and RH together. When comparing two locations, swap the sensor assemblies while keeping each electrical channel and code identity. If the difference follows the location, investigate airflow or heating. If it follows the device, investigate the assembly and sensor. One believable room reading is not an accuracy test.

Illustrative vented enclosure with a small humidity sensor PCB separated from the ESP32 host board
Place the sensing opening in representative airflow, away from host power heat, then check the assembled enclosure against a reference.

8. From a bench reading to a product decision

Once crc_ok is repeatable, test the finished assembly over its intended supply range, I2C loading, enclosure airflow, temperature and humidity conditions. Compare time-aligned data with a suitable reference, define an error budget, and retain failure counts alongside good samples. Recheck after production steps or exposure that can affect the sensing opening. This evidence determines whether the design meets its own limits; an address scan and a room-temperature screenshot do not.

If the project needs another sensor source, NYFEA FHT40 can be evaluated at this stage. Compare its controlled specification with the original design's supply, footprint, pinout, commands, CRC, timing and accuracy conditions before changing hardware or firmware. Do not assume that the SHT40 sketch or breakout wiring also applies to a bare FHT40. A shared address alone is not a replacement decision. If condensation or heater recovery is part of the requirement, use the SHT40 heater and recovery guide as a separate test path.

9. Optional: pair SHT40 readings with GPS coordinates

An ESP32 can pair SHT40 temperature and relative-humidity readings with a GPS location for a moving environmental logger. The SHT40 stays on I2C; a UART GPS receiver, such as a NEO-6M-based module, uses a separate serial port. The four SHT40 connections above remain the same.

Example connections for the ESP32-DevKitC V4 with an ESP32-WROOM module
DeviceDevice pinESP32 connectionWhat it does
Adafruit SHT40 breakoutVIN3V3Breakout supply for this setup
Adafruit SHT40 breakoutGNDGNDCommon ground
Adafruit SHT40 breakoutSDAGPIO21I2C data
Adafruit SHT40 breakoutSCLGPIO22I2C clock
UART GPS receiverVCCSupply permitted by the exact GPS boardCheck the carrier board's input-voltage rating
UART GPS receiverGNDGNDCommon ground
UART GPS receiverTXGPIO16 (ESP32 RX2)Position messages from GPS to ESP32
UART GPS receiverRXGPIO17 (ESP32 TX2), optionalCommands from ESP32 to GPS, if needed

Check the GPS carrier's supply and UART logic levels before connecting it. A bare u-blox NEO-6M has a 2.7–3.6 V supply range; a carrier board may have its own regulator and a different input rating, so the label “NEO-6M” alone is not permission to apply 5 V. Espressif lists GPIO16 and GPIO17 as available on ESP32-DevKitC boards with WROOM or SOLO modules; they are reserved on WROVER versions. Confirm the exact ESP32 module before using those pins.

A GPS data stream is not yet a geotagged measurement. The sketch above reports SHT40 status only; it does not parse GPS messages or output coordinates. For a location-tagged record, read the GPS receiver on a separate UART at its configured baud rate, require a valid position fix, and associate its timestamp and coordinates with a SHT40 sample that passed both CRC checks. If no valid fix is available, retain the sensor sample with a no_fix location state rather than treating missing or stale coordinates as current.

Evaluate NYFEA FHT40 for the finished design

Check the current product data before starting a side-by-side evaluation.

Review the FHT40 specification

10. Short FAQ

Can I power the SHT40 from the ESP32 5 V pin?

Not as wired here. A bare SHT40 is rated for 1.08–3.6 V. Adafruit PID 4885 accepts 3.3–5 V at its breakout VIN, but this example uses the ESP32's 3V3 pin.

Does 0x44 in an I2C scan mean the sensor works?

No. It means an address answered. Send a measurement command, read six bytes and validate both CRCs.

Why is Serial Monitor unreadable?

Set it to 115200 baud to match Serial.begin(115200). If the header is readable but samples fail, investigate I2C rather than the serial setting.

Does crc_ok prove accurate RH?

No. It proves that the received measurement frame passed its integrity checks. Accuracy in your product needs a controlled comparison with an independent reference.

Can the UART GPS receiver share the SHT40's SDA and SCL pins?

Not in this wiring. The SHT40 uses I2C on GPIO21 and GPIO22; the UART GPS receiver sends data to ESP32 RX2 on GPIO16. GPS RX on GPIO17 is optional when the ESP32 must send commands.

Can I connect a NEO-6M GPS module to 5 V?

Only if the exact carrier board specifies a 5 V input. The bare u-blox NEO-6M supply is rated for 2.7–3.6 V. Check the carrier's regulator and UART logic levels before wiring.

Why do I see GPS serial data but no coordinates?

Receiving GPS messages does not prove a valid position fix. Check the receiver's fix indication and antenna conditions; mark the location no_fix until a valid fix is available. Do not substitute an old position for the current sensor sample.

Are GPIO16 and GPIO17 available on every ESP32-DevKitC V4?

No. Espressif lists them as available on WROOM and SOLO versions, but reserved for internal use on WROVER versions. Check the module on your board before assigning the GPS UART.

SHT40 and Sensirion are identifiers or trademarks of their respective owners. NYFEA FHT40 is an independent product; this article does not claim an affiliation or an approved replacement relationship.

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.

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.

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.

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.

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.

NYFEA FT18B20 digital temperature and humidity 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.

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