From ad334e37d18aa68a314994e3c836aa5568cbdd8d Mon Sep 17 00:00:00 2001 From: Tomas Kuchta Date: Tue, 3 Feb 2026 12:05:47 +0100 Subject: [PATCH 1/3] Add SHT21 sensor support and related configurations --- src/configuration.h | 4 + src/detect/ScanI2C.h | 1 + src/detect/ScanI2CTwoWire.cpp | 10 +++ src/mesh/generated/meshtastic/telemetry.pb.h | 6 +- .../Telemetry/EnvironmentTelemetry.cpp | 7 ++ src/modules/Telemetry/Sensor/SHT21Sensor.cpp | 83 +++++++++++++++++++ src/modules/Telemetry/Sensor/SHT21Sensor.h | 22 +++++ 7 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 src/modules/Telemetry/Sensor/SHT21Sensor.cpp create mode 100644 src/modules/Telemetry/Sensor/SHT21Sensor.h diff --git a/src/configuration.h b/src/configuration.h index 66fa4492dba..018c43b3a0d 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -219,6 +219,10 @@ along with this program. If not, see . #define LPS22HB_ADDR_ALT 0x5D #define SHT31_4x_ADDR 0x44 #define SHT31_4x_ADDR_ALT 0x45 +#define SHT21_ADDR 0x40 // same address as INA_ADDR +#ifndef HAS_SHT21 +#define HAS_SHT21 0 // set to one to override detection of INA219/260 devices at this address +#endif #define PMSA003I_ADDR 0x12 #define QMA6100P_ADDR 0x12 #define AHT10_ADDR 0x38 diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h index dffcd8fb65a..6d9da08a748 100644 --- a/src/detect/ScanI2C.h +++ b/src/detect/ScanI2C.h @@ -32,6 +32,7 @@ class ScanI2C MAX17048, MCP9808, SHT31, + SHT21, SHT4X, SHTC3, LPS22HB, diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index c6ef3484679..2a9ab4a3f36 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -308,7 +308,9 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) break; #endif #if !defined(M5STACK_UNITC6L) +#if !HAS_SHT21 case INA_ADDR: +#endif case INA_ADDR_ALTERNATE: case INA_ADDR_WAVESHARE_UPS: registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFE), 2); @@ -401,6 +403,14 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) break; +#if HAS_SHT21 + case SHT21_ADDR: + // SHT21 / HTU21D family commonly at 0x40 + logFoundDevice("SHT21/HTU21D", (uint8_t)addr.address); + type = SHT21; + break; +#endif + SCAN_SIMPLE_CASE(SHTC3_ADDR, SHTC3, "SHTC3", (uint8_t)addr.address) case RCWL9620_ADDR: // get MAX30102 PARTID diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h index dc9d876dcc6..c452b6bda18 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.h +++ b/src/mesh/generated/meshtastic/telemetry.pb.h @@ -103,7 +103,9 @@ typedef enum _meshtastic_TelemetrySensorType { /* TSL2561 light sensor */ meshtastic_TelemetrySensorType_TSL2561 = 44, /* BH1750 light sensor */ - meshtastic_TelemetrySensorType_BH1750 = 45 + meshtastic_TelemetrySensorType_BH1750 = 45, + /* STH21 temperature, R. humidity sensor */ + meshtastic_TelemetrySensorType_SHT21 = 46 } meshtastic_TelemetrySensorType; /* Struct definitions */ @@ -461,7 +463,7 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET -#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_BH1750 +#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_SHT21 #define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_BH1750+1)) diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 140c2c17e7b..dea9276e054 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -69,6 +69,10 @@ extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const c #include "Sensor/SHT31Sensor.h" #endif +#if HAS_SHT21 +#include "Sensor/SHT21Sensor.h" +#endif + #if __has_include() #include "Sensor/LPS22HBSensor.h" #endif @@ -202,6 +206,9 @@ void EnvironmentTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner) #if __has_include() addSensor(i2cScanner, ScanI2C::DeviceType::SHT31); #endif +#if HAS_SHT21 + addSensor(i2cScanner, ScanI2C::DeviceType::SHT21); +#endif #if __has_include() addSensor(i2cScanner, ScanI2C::DeviceType::LPS22HB); #endif diff --git a/src/modules/Telemetry/Sensor/SHT21Sensor.cpp b/src/modules/Telemetry/Sensor/SHT21Sensor.cpp new file mode 100644 index 00000000000..e9feee0da35 --- /dev/null +++ b/src/modules/Telemetry/Sensor/SHT21Sensor.cpp @@ -0,0 +1,83 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR + +#include "SHT21Sensor.h" +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "TelemetrySensor.h" +#include + +// SHT21 I2C address and commands (use no-hold commands to avoid clock-stretch issues) +static const uint8_t CMD_TEMP_NOHOLD = 0xF3; +static const uint8_t CMD_HUM_NOHOLD = 0xF5; + +static bool read_sht21_raw(TwoWire *bus, uint8_t addr, uint8_t cmd, uint16_t &raw, uint16_t waitMs) +{ + bus->beginTransmission(addr); + bus->write(cmd); + if (bus->endTransmission() != 0) + return false; + // Wait for measurement to complete (no-hold mode) + delay(waitMs); + int toRead = 3; // two data bytes + CRC + bus->requestFrom((int)addr, toRead); + if (bus->available() < 2) + return false; + uint8_t msb = bus->read(); + uint8_t lsb = bus->read(); + if (bus->available()) + (void)bus->read(); + + raw = ((uint16_t)msb << 8) | lsb; + // clear status bits per datasheet + raw &= ~0x0003; + return true; +} + +SHT21Sensor::SHT21Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SHT21, "SHT21") {} + +bool SHT21Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) +{ + LOG_INFO("Init sensor: %s", sensorName); + i2c = bus; + i2c_addr = dev->address.address ? dev->address.address : SHT21_ADDR; + + uint16_t raw; + // use no-hold command and wait for conversion (max ~85ms for temp) + if (!read_sht21_raw(i2c, i2c_addr, CMD_TEMP_NOHOLD, raw, 85)) { + return false; + } + float temp = -46.85f + 175.72f * ((float)raw / 65536.0f); + // basic sanity check + if (temp < -50.0f || temp > 150.0f) + return false; + + status = true; + initI2CSensor(); + return true; +} + +bool SHT21Sensor::getMetrics(meshtastic_Telemetry *measurement) +{ + measurement->variant.environment_metrics.has_temperature = true; + measurement->variant.environment_metrics.has_relative_humidity = true; + + LOG_DEBUG("SHT21 getMetrics"); + + uint16_t raw_t = 0, raw_h = 0; + if (!read_sht21_raw(i2c, i2c_addr, CMD_TEMP_NOHOLD, raw_t, 85)) + return false; + // humidity max conversion time ~29ms + if (!read_sht21_raw(i2c, i2c_addr, CMD_HUM_NOHOLD, raw_h, 30)) + return false; + + float temp = -46.85f + 175.72f * ((float)raw_t / 65536.0f); + float rh = -6.0f + 125.0f * ((float)raw_h / 65536.0f); + + measurement->variant.environment_metrics.temperature = temp; + measurement->variant.environment_metrics.relative_humidity = rh; + + return true; +} + +#endif diff --git a/src/modules/Telemetry/Sensor/SHT21Sensor.h b/src/modules/Telemetry/Sensor/SHT21Sensor.h new file mode 100644 index 00000000000..80760dc6deb --- /dev/null +++ b/src/modules/Telemetry/Sensor/SHT21Sensor.h @@ -0,0 +1,22 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "TelemetrySensor.h" +#include + +class SHT21Sensor : public TelemetrySensor +{ + private: + TwoWire *i2c = nullptr; + uint8_t i2c_addr = 0; + bool status = false; + + public: + SHT21Sensor(); + virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; +}; + +#endif From 75abbaf6896591ae4e728087a566665b189a3b39 Mon Sep 17 00:00:00 2001 From: Tomas Kuchta Date: Wed, 4 Feb 2026 15:34:58 +0100 Subject: [PATCH 2/3] regenerated protobufs --- src/mesh/generated/meshtastic/telemetry.pb.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h index c452b6bda18..77afd38840f 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.h +++ b/src/mesh/generated/meshtastic/telemetry.pb.h @@ -104,7 +104,7 @@ typedef enum _meshtastic_TelemetrySensorType { meshtastic_TelemetrySensorType_TSL2561 = 44, /* BH1750 light sensor */ meshtastic_TelemetrySensorType_BH1750 = 45, - /* STH21 temperature, R. humidity sensor */ + /* STH21 Temperature and R. Humidity sensor */ meshtastic_TelemetrySensorType_SHT21 = 46 } meshtastic_TelemetrySensorType; @@ -464,7 +464,7 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET #define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_SHT21 -#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_BH1750+1)) +#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_SHT21+1)) From ec0c2f637492b97d1f910bbee85f3a01566202f0 Mon Sep 17 00:00:00 2001 From: Tomas Kuchta Date: Thu, 5 Feb 2026 15:21:10 +0100 Subject: [PATCH 3/3] SHT21: add rounding --- src/modules/Telemetry/Sensor/SHT21Sensor.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/modules/Telemetry/Sensor/SHT21Sensor.cpp b/src/modules/Telemetry/Sensor/SHT21Sensor.cpp index e9feee0da35..e28f1af1dfb 100644 --- a/src/modules/Telemetry/Sensor/SHT21Sensor.cpp +++ b/src/modules/Telemetry/Sensor/SHT21Sensor.cpp @@ -6,6 +6,7 @@ #include "../mesh/generated/meshtastic/telemetry.pb.h" #include "TelemetrySensor.h" #include +#include // SHT21 I2C address and commands (use no-hold commands to avoid clock-stretch issues) static const uint8_t CMD_TEMP_NOHOLD = 0xF3; @@ -74,8 +75,9 @@ bool SHT21Sensor::getMetrics(meshtastic_Telemetry *measurement) float temp = -46.85f + 175.72f * ((float)raw_t / 65536.0f); float rh = -6.0f + 125.0f * ((float)raw_h / 65536.0f); - measurement->variant.environment_metrics.temperature = temp; - measurement->variant.environment_metrics.relative_humidity = rh; + // Round to 2 decimal places for telemetry + measurement->variant.environment_metrics.temperature = roundf(temp * 100.0f) / 100.0f; + measurement->variant.environment_metrics.relative_humidity = roundf(rh * 100.0f) / 100.0f; return true; }