Arduino library · Sensors

Adafruit SHT31: temperature and humidity from the SHT31-D

The SHT31-D is Sensirion’s I2C temperature and humidity sensor. Each answer it sends carries a CRC byte after every data word, so a garbled transfer is caught and reported as NaN instead of being printed as a believable number. Adafruit’s library for it needs only Wire: there are no events to declare, just a call for each value that returns a float.

Built in on Mokxi: #include <Adafruit_SHT31.h> and it builds, with nothing to install. Free, and no account needed.

What Adafruit SHT31 Library does

The module answers at 0x44, or 0x45 when its ADR pin is tied high. begin() sends a soft reset and reads the status register; when nothing replies it returns false. Each readTemperature() or readHumidity() runs its own single-shot measurement at high repeatability and waits 20 ms for it, so a sketch that wants both values from the same moment should use readBoth(), which takes one measurement for the pair.

In Mokxi the sht31 part has a slider for each value. The Mokxi build adds measure(), getTemperatureCentis() and getHumidityCentis(), which return whole hundredths and build no floats, to save flash on an Uno. The air quality monitor below relies on them to give the SGP30 its humidity correction and to show the climate on a four-digit display. The upstream library lacks those three calls, so code meant for hardware should stay with the float ones.

Library Manager nameAdafruit SHT31 Library
AuthorAdafruit
Include#include <Adafruit_SHT31.h>
Boardsevery board Mokxi compiles for but ATtiny85
In the simulatorSimulated: it runs against the part the way it does on the bench.
Parts it drivesTemperature and humidity, SHT31

Install it

It builds on every board Mokxi compiles for but ATtiny85.

In Mokxi it is built in: #include <Adafruit_SHT31.h> and the sketch compiles, with no download, no zip and no Library Manager.

On a real board, install "Adafruit SHT31 Library" by Adafruit from the Arduino IDE’s Library Manager (Sketch, Include Library, Manage Libraries), or add Adafruit SHT31 Library to lib_deps in PlatformIO. Mokxi follows the calls of release 2.2.2, so a sketch written here builds there too.

The calls you will use

In the order a sketch meets them.

Adafruit_SHT31 sht31 = Adafruit_SHT31()

Creates the driver on the default Wire bus. Pass &Wire1 to the constructor on a board with a second bus.

sht31.begin(0x44)

Resets the sensor and checks that its status register answers. Returns false when nothing is at that address; use 0x45 for a module with ADR high.

float t = sht31.readTemperature()

Degrees Celsius from a fresh measurement, or NaN when the read failed or a CRC did not match.

float t = sht31.readTemperature();
if (isnan(t)) {
  Serial.println("SHT31 read failed");
}

float h = sht31.readHumidity()

Relative humidity in percent, from a measurement of its own, with the same NaN rule.

sht31.readBoth(&t, &h)

One measurement for both values, half the bus time of two separate calls. Returns false, and writes NaN to both, when it failed.

sht31.heater(true)

Switches on the heater built into the sensor, which a real SHT31 uses to drive off condensation. isHeaterEnabled() reads the status bit back.

sht31.getTemperatureCentis()

Mokxi’s own: after measure(), the temperature in hundredths of a degree as an int32_t, so 2345 means 23.45 °C. getHumidityCentis() is the humidity twin.

Example: Air quality monitor

On the Arduino Uno R3. It runs in the browser exactly as printed.

sketch.ino · Arduino Uno R3
// Air quality monitor: an SGP30 gas sensor, an SHT31 thermometer and an
// HT16K33 four-digit LED display share the I2C bus, the way a home-built CO2
// monitor is made.
//
//   SGP30, SHT31, display   SDA -> A4   SCL -> A5   VIN or VCC -> 5 V   GND -> GND
//   red LED                 pin 8 through 220 ohms
//
// The SGP30 reads 400 ppm and 0 ppb for its first 15 seconds while its hot
// plate warms up, so the display counts that down after "--". Then it takes
// turns: the CO2eq in ppm for four seconds, the temperature ("23*C") and the
// humidity ("45rh") for one each. Above 1000 ppm, where a room wants a
// window opening, the red LED comes on and the display blinks.
//
// The SHT31's humidity makes the SGP30 more accurate: the sensor wants it as
// milligrams of water in a cubic meter of air, which is worked out here from
// a table of how much water air can hold, because this board has no exp().
//
// Drag the sliders on the two sensors and watch the display follow.

#include <Wire.h>
#include "Adafruit_LEDBackpack.h"
#include "Adafruit_SHT31.h"
#include "Adafruit_SGP30.h"

const int ALERT_LED = 8;
const uint16_t ALERT_PPM = 1000;

// Segments for the units: A is bit 0, G is bit 6.
const uint8_t DEGREE = 0x63;
const uint8_t LETTER_C = 0x39;
const uint8_t LETTER_R = 0x50;
const uint8_t LETTER_H = 0x74;

Adafruit_7segment display = Adafruit_7segment();
Adafruit_SHT31 sht31 = Adafruit_SHT31();
Adafruit_SGP30 sgp;

// How much water saturated air holds, in mg per cubic meter, at 0, 5, 10 ...
// 50 degrees Celsius.
const uint32_t SATURATION[] = {4847, 6797, 9399, 12830, 17300, 23050,
                               30380, 39630, 51120, 65330, 82770};

uint32_t absoluteHumidity(int32_t centiC, int32_t centiRh) {
  if (centiC < 0) centiC = 0;
  if (centiC > 4999) centiC = 4999;
  uint8_t i = centiC / 500;
  uint32_t frac = centiC % 500;
  uint32_t sat = SATURATION[i] + (SATURATION[i + 1] - SATURATION[i]) * frac / 500;
  return sat * (uint32_t)centiRh / 10000;
}

// Two digits on the left, two unit segments on the right.
void showWithUnit(int32_t value, uint8_t left, uint8_t right) {
  display.print(value * 100, DEC);
  display.writeDigitRaw(3, left);
  display.writeDigitRaw(4, right);
}

unsigned long started;
uint8_t tick = 0;
bool alarm = false;

void setup() {
  pinMode(ALERT_LED, OUTPUT);
  display.begin(0x70);
  display.setBrightness(10);
  if (!sht31.begin(0x44) || !sgp.begin()) {
    // "Err": a sensor did not answer.
    display.writeDigitRaw(0, 0x79);
    display.writeDigitRaw(1, LETTER_R);
    display.writeDigitRaw(3, LETTER_R);
    display.writeDisplay();
    while (1) delay(10);
  }
  started = millis();
}

void loop() {
  bool climate = sht31.measure();
  int32_t centiC = sht31.getTemperatureCentis();
  int32_t centiRh = sht31.getHumidityCentis();
  if (climate) sgp.setHumidity(absoluteHumidity(centiC, centiRh));
  if (!sgp.IAQmeasure()) {
    delay(1000);
    return;
  }

  unsigned long seconds = (millis() - started) / 1000;
  tick = (tick + 1) % 6;
  if (seconds < 15) {
    display.print(15 - seconds, DEC);
    display.writeDigitRaw(0, 0x40);
    display.writeDigitRaw(1, 0x40);
  } else if (tick == 4) {
    showWithUnit((centiC + 50) / 100, DEGREE, LETTER_C);
  } else if (tick == 5) {
    showWithUnit((centiRh + 50) / 100, LETTER_R, LETTER_H);
  } else {
    display.print(sgp.eCO2, DEC);
  }
  display.writeDisplay();

  bool high = seconds >= 15 && sgp.eCO2 >= ALERT_PPM;
  if (high != alarm) {
    alarm = high;
    display.blinkRate(alarm ? HT16K33_BLINK_1HZ : HT16K33_BLINK_OFF);
  }
  digitalWrite(ALERT_LED, alarm ? HIGH : LOW);

  // One measurement a second, as the SGP30's baseline algorithm wants.
  delay(1000 - 70);
}

Open it in the editor, wired and ready to run · Air quality monitor: the project page

Common errors and how to fix them

Couldn't find SHT31

begin() heard no reply at the address given. On an Uno, SDA goes to A4 and SCL to A5, with VIN powered. If the ADR pin is high, call begin(0x45); an I2C scanner shows which of the two answers.

Temperature or humidity prints nan

The measurement failed or its checksum was wrong, usually from a loose jumper, a long cable without pull-ups, or two devices sharing one address. Test with isnan() before using the value and try again on the next pass.

Readings run warm and dry compared with another thermometer nearby

The heater may have been left on, or the module sits beside a regulator or a busy chip. Call heater(false), then mount the sensor where nothing on the board warms it.

Questions and answers

Can I read the SHT31 without floating point on an Uno?

In Mokxi, yes: call measure(), then getTemperatureCentis() and getHumidityCentis() for whole hundredths, which is how the air quality monitor stays inside the Uno’s flash.

How is this different from the Adafruit SHT4x library?

The SHT4x driver returns sensors_event_t structs from getEvent() and has precision and heater settings per measurement. This one returns plain floats from separate calls.

Try Adafruit SHT31 Library without installing anything

Open the example, change the sketch, and keep your version in a free account.