diff --git a/src/configuration.h b/src/configuration.h index 7b7c603ba96..14281334d28 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 0edd5e7a781..ed78de23c93 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 445e9342c66..00ab27d9921 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..77afd38840f 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 and R. Humidity sensor */ + meshtastic_TelemetrySensorType_SHT21 = 46 } meshtastic_TelemetrySensorType; /* Struct definitions */ @@ -461,8 +463,8 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET -#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_BH1750 -#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_BH1750+1)) +#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_SHT21 +#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_SHT21+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..e28f1af1dfb --- /dev/null +++ b/src/modules/Telemetry/Sensor/SHT21Sensor.cpp @@ -0,0 +1,85 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR + +#include "SHT21Sensor.h" +#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; +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); + + // 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; +} + +#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