HTU21D: подключение к ESP32 и Arduino

Цифровой датчик температуры и влажности (I2C)

Точный датчик температуры и влажности на шине I2C. Модуль GY-21 и его аналоги (Si7021, SHT21) понимают те же команды, поэтому один скетч подходит им всем.

Как работает

HTU21D отвечает по I2C на адресе 0x40, и поменять его нельзя. Регистров, как у BME280, здесь нет: датчик понимает несколько однобайтовых команд. После команды измерения (0xF3 — температура, 0xF5 — влажность) нужно подождать до 50 мс и прочитать три байта: старший и младший байт результата и контрольную сумму CRC-8.

Пересчёт из даташита: T = −46,85 + 175,72 · S / 65536 и RH = −6 + 125 · S / 65536, где два младших бита результата обнуляются. Есть и готовые библиотеки, но по такому скетчу видно, что происходит на шине.

Выводы

ВыводНазначение
SCLтактовый сигнал I2C
SDAданные I2C
VCCпитание модуля
GNDобщий провод

Подключение к ESP32

HTU21DESP32
SCLGPIO22
SDAGPIO21
VCC3V3
GNDGND

Схема из примера «HTU21D: температура и влажность по I2C без библиотеки».

HTU21D: температура и влажность по I2C без библиотеки

Открыть пример целиком
Код скетча
#include <Wire.h>

// HTU21D (модуль GY-21) на I2C. Библиотека не нужна: у датчика всего
// несколько команд, и вся работа видна в скетче.
//
// Алгоритм один на оба измерения:
//   1. послать команду «измерь» (0xF3 — температура, 0xF5 — влажность);
//   2. подождать: измерение занимает до 50 мс, и раньше времени датчик
//      не отвечает;
//   3. прочитать три байта: старший, младший и контрольную сумму CRC-8;
//   4. сверить сумму и пересчитать число по формуле из даташита.

const uint8_t HTU21D = 0x40;
const uint8_t CMD_TEMPERATURE = 0xF3;
const uint8_t CMD_HUMIDITY = 0xF5;

uint8_t crc8(uint8_t msb, uint8_t lsb) {
  // Многочлен датчика x^8 + x^5 + x^4 + 1 (0x131), начальное значение 0.
  uint16_t crc = 0;
  uint8_t data[2] = {msb, lsb};
  for (int i = 0; i < 2; i++) {
    crc ^= data[i];
    for (int bit = 0; bit < 8; bit++) {
      crc = (crc & 0x80) ? ((crc << 1) ^ 0x131) : (crc << 1);
    }
  }
  return crc & 0xFF;
}

// Возвращает false, если датчик не ответил или сумма не сошлась.
bool measure(uint8_t command, uint16_t &raw) {
  Wire.beginTransmission(HTU21D);
  Wire.write(command);
  if (Wire.endTransmission() != 0) return false;

  delay(60);

  if (Wire.requestFrom((int)HTU21D, 3) != 3) return false;
  uint8_t msb = Wire.read();
  uint8_t lsb = Wire.read();
  uint8_t crc = Wire.read();
  if (crc8(msb, lsb) != crc) return false;

  // Два младших бита — служебные, перед пересчётом их обнуляют.
  raw = ((uint16_t)msb << 8) | (lsb & 0xFC);
  return true;
}

void setup() {
  Serial.begin(115200);
  Wire.begin(21, 22);   // SDA, SCL
}

void loop() {
  uint16_t rawT, rawH;
  if (!measure(CMD_TEMPERATURE, rawT) || !measure(CMD_HUMIDITY, rawH)) {
    Serial.println("датчик не ответил: проверьте SDA, SCL и питание");
    delay(2000);
    return;
  }

  float t = -46.85 + 175.72 * rawT / 65536.0;
  float h = -6.0 + 125.0 * rawH / 65536.0;
  h = constrain(h, 0.0, 100.0);   // у границ формула может чуть выйти за 0..100

  Serial.printf("%.2f °C   %.1f %%\n", t, h);
  delay(2000);
}

Подключение к Arduino Uno

HTU21DArduino Uno
SCLA5
SDAA4
VCC5V
GNDGND

Схема из примера «Arduino Uno: HTU21D по I2C без библиотеки».

Arduino Uno: HTU21D по I2C без библиотеки

Открыть пример целиком
Код скетча
#include <Wire.h>

// HTU21D (модуль GY-21) на I2C. Библиотека не нужна: у датчика всего
// несколько команд, и вся работа видна в скетче.
//
// Алгоритм один на оба измерения:
//   1. послать команду «измерь» (0xF3 — температура, 0xF5 — влажность);
//   2. подождать: измерение занимает до 50 мс, и раньше времени датчик
//      не отвечает;
//   3. прочитать три байта: старший, младший и контрольную сумму CRC-8;
//   4. сверить сумму и пересчитать число по формуле из даташита.

const uint8_t HTU21D = 0x40;
const uint8_t CMD_TEMPERATURE = 0xF3;
const uint8_t CMD_HUMIDITY = 0xF5;

uint8_t crc8(uint8_t msb, uint8_t lsb) {
  // Многочлен датчика x^8 + x^5 + x^4 + 1 (0x131), начальное значение 0.
  uint16_t crc = 0;
  uint8_t data[2] = {msb, lsb};
  for (int i = 0; i < 2; i++) {
    crc ^= data[i];
    for (int bit = 0; bit < 8; bit++) {
      crc = (crc & 0x80) ? ((crc << 1) ^ 0x131) : (crc << 1);
    }
  }
  return crc & 0xFF;
}

// Возвращает false, если датчик не ответил или сумма не сошлась.
bool measure(uint8_t command, uint16_t &raw) {
  Wire.beginTransmission(HTU21D);
  Wire.write(command);
  if (Wire.endTransmission() != 0) return false;

  delay(60);

  if (Wire.requestFrom((int)HTU21D, 3) != 3) return false;
  uint8_t msb = Wire.read();
  uint8_t lsb = Wire.read();
  uint8_t crc = Wire.read();
  if (crc8(msb, lsb) != crc) return false;

  // Два младших бита — служебные, перед пересчётом их обнуляют.
  raw = ((uint16_t)msb << 8) | (lsb & 0xFC);
  return true;
}

void setup() {
  Serial.begin(9600);
  Wire.begin();
}

void loop() {
  uint16_t rawT, rawH;
  if (!measure(CMD_TEMPERATURE, rawT) || !measure(CMD_HUMIDITY, rawH)) {
    Serial.println(F("датчик не ответил: проверьте A4, A5 и питание"));
    delay(2000);
    return;
  }

  float t = -46.85 + 175.72 * rawT / 65536.0;
  float h = -6.0 + 125.0 * rawH / 65536.0;
  h = constrain(h, 0.0, 100.0);   // у границ формула может чуть выйти за 0..100

  Serial.print("t:");
  Serial.print(t, 2);
  Serial.print(" h:");
  Serial.println(h, 1);
  delay(2000);
}

В железе

Сама микросхема HTU21D рассчитана на питание до 3,6 В. На готовых модулях типа GY-21 стоит стабилизатор и подтяжки шины, и их можно питать от 5 В, а на «голой» микросхеме — нельзя. Адрес один и менять его нельзя, поэтому два таких датчика на одной шине не работают без мультиплексора.

Частые ошибки

Другие детали