Add support for Sensirion SHT4X using define USE_SHT3X (#15349)

This commit is contained in:
Theo Arends 2022-06-04 16:52:52 +02:00
parent d7ffd01f1f
commit dae1b32995
4 changed files with 108 additions and 51 deletions

View File

@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
## [11.1.0.4]
### Added
- Support for HYTxxx temperature and humidity sensor (#15715)
- Support for Sensirion SHT4X using define USE_SHT3X (#15349)
### Changed
- Restructured tasmota source directories taking benefit from PlatformIO Core v6.0.2

View File

@ -31,7 +31,8 @@ Index | Define | Driver | Device | Address(es) | Description
13 | USE_ADS1115 | xsns_12 | ADS1115 | 0x48 - 0x4B | 4-channel 16-bit A/D converter
14 | USE_INA219 | xsns_13 | INA219 | 0x40 - 0x41, 0x44 - 0x45 | Low voltage current sensor
15 | USE_SHT3X | xsns_14 | SHT3X | 0x44 - 0x45 | Temperature and Humidity sensor
15 | USE_SHT3X | xsns_14 | SHTC3 | 0x70 | Temperature and Humidity sensor
15 | USE_SHT3X | xsns_14 | SHT4X | 0x44 - 0x45 | Temperature and Humidity sensor
15 | USE_SHT3X | xsns_14 | SHTCX | 0x70 | Temperature and Humidity sensor
16 | USE_TSL2561 | xsns_16 | TSL2561 | 0x29, 0x39, 0x49 | Light intensity sensor
17 | USE_MGS | xsns_19 | Grove | 0x04 | Multichannel gas sensor
18 | USE_SGP30 | xsns_21 | SGP30 | 0x58 | Gas (TVOC) and air quality sensor
@ -102,4 +103,3 @@ Index | Define | Driver | Device | Address(es) | Description
66 | USE_PCF85363 | xsns_99 | PCF85363 | 0x51 | Real time clock
67 | USE_DS3502 | xdrv_61 | DS3502 | 0x28 - 0x2B | Digital potentiometer
68 | USE_HYT | xsns_97 | HYTxxx | 0x28 | Temperature and Humidity sensor

View File

@ -114,6 +114,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo
- Command ``SspmDisplay 2`` to display Sonoff SPM energy data in GUI for user tab-selected relay modules [#13447](https://github.com/arendst/Tasmota/issues/13447)
- Support for Sonoff MS01 soil moisture sensor [#15335](https://github.com/arendst/Tasmota/issues/15335)
- Support for daisy chaining MAX7219 displays [#15345](https://github.com/arendst/Tasmota/issues/15345)
- Support for Sensirion SHT4X using define USE_SHT3X [#15349](https://github.com/arendst/Tasmota/issues/15349)
- Sonoff SPM delayed SetPowerOnState [#13447](https://github.com/arendst/Tasmota/issues/13447)
- Support for Sonoff SPM v1.2.0
- Support for Sonoff Zigbee Bridge Pro by Stephan Hadinger [#15701](https://github.com/arendst/Tasmota/issues/15701)

View File

@ -1,7 +1,7 @@
/*
xsns_14_sht3x.ino - SHT3X temperature and humidity sensor support for Tasmota
xsns_14_sht3x.ino - SHT3X, SHT4X and SHTCX temperature and humidity sensor support for Tasmota
Copyright (C) 2021 Theo Arends
Copyright (C) 2022 Theo Arends, Stefan Tibus
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -20,85 +20,141 @@
#ifdef USE_I2C
#ifdef USE_SHT3X
/*********************************************************************************************\
* SHT3X and SHTC3 - Temperature and Humidity
* Sensirion I2C temperature and humidity sensors
*
* I2C Address: 0x44, 0x45 or 0x70 (SHTC3)
* This driver supports the following sensors:
* - SHT3x series: SHT30, SHT31, SHT35 (addresses: A: 0x44, B: 0x45)
* - SHTC series: SHTC1, SHTC3 (address: 0x70)
* - SHT4x series: SHT40, SHT41, SHT45 (addresses: A: 0x44, B: 0x45)
\*********************************************************************************************/
#define XSNS_14 14
#define XI2C_15 15 // See I2CDEVICES.md
#define SHT3X_ADDR_GND 0x44 // address pin low (GND)
#define SHT3X_ADDR_VDD 0x45 // address pin high (VDD)
#define SHTC3_ADDR 0x70 // address for shtc3 sensor
#define SHT3X_ADDRESSES 2 // 2 addresses for SHT3x
#define SHT3X_ADDR_GND 0x44 // Address A pin low (GND)
#define SHT3X_ADDR_VDD 0x45 // Address B pin high (VDD)
#define SHTCX_ADDRESSES 1 // 1 address for SHTCx
#define SHTCX_ADDR 0x70 // Address for SHTCx sensors
#define SHT4X_ADDRESSES 2 // 2 addresses for SHT4x
#define SHT4X_ADDR_A 0x44 // Address SHT4x A
#define SHT4X_ADDR_B 0x45 // Address SHT4x B
#define SHT3X_MAX_SENSORS 3
#define SHT3X_MAX_SENSORS 3 // Only one of 0x44, 0x45 and 0x70
const char kShtTypes[] PROGMEM = "SHT3X|SHT3X|SHTC3";
uint8_t sht3x_addresses[] = { SHT3X_ADDR_GND, SHT3X_ADDR_VDD, SHTC3_ADDR };
enum SHT3X_Types {
SHT3X_TYPE_SHT3X,
SHT3X_TYPE_SHTCX,
SHT3X_TYPE_SHT4X
};
const char kSht3xTypes[] PROGMEM = "SHT3X|SHTC3|SHT4X";
uint8_t sht3x_addresses[] = { SHT3X_ADDR_GND, SHT3X_ADDR_VDD, SHTCX_ADDR };
uint8_t sht3x_count = 0;
struct SHT3XSTRUCT {
uint8_t address; // I2C bus address
char types[6]; // Sensor type name and address - "SHT3X-0xXX"
uint8_t type; // Sensor type
uint8_t address; // I2C bus address
char types[6]; // Sensor type name and address, e.g. "SHT3X"
} sht3x_sensors[SHT3X_MAX_SENSORS];
bool Sht3xRead(float &t, float &h, uint8_t sht3x_address)
{
unsigned int data[6];
uint8_t Sht3xComputeCrc(uint8_t data[], uint8_t len) {
// Compute CRC as per datasheet
uint8_t crc = 0xFF;
for (uint8_t x = 0; x < len; x++) {
crc ^= data[x];
for (uint8_t i = 0; i < 8; i++) {
if (crc & 0x80) {
crc = (crc << 1) ^ 0x31;
} else {
crc <<= 1;
}
}
}
return crc;
}
bool Sht3xRead(uint32_t type, float &t, float &h, uint8_t i2c_address) {
uint8_t data[6];
t = NAN;
h = NAN;
Wire.beginTransmission(sht3x_address);
if (SHTC3_ADDR == sht3x_address) {
Wire.write(0x35); // Wake from
Wire.write(0x17); // sleep
Wire.endTransmission();
Wire.beginTransmission(sht3x_address);
Wire.write(0x78); // Disable clock stretching ( I don't think that wire library support clock stretching )
Wire.write(0x66); // High resolution
} else {
Wire.write(0x2C); // Enable clock stretching
Wire.write(0x06); // High repeatability
Wire.beginTransmission(i2c_address);
switch (type) {
case SHT3X_TYPE_SHT3X:
// TODO: Clock stretching is used for SHT3x but not for SHTC3. Why?
Wire.write(0x2C); // Enable clock stretching
Wire.write(0x06); // High repeatability measurement
break;
case SHT3X_TYPE_SHTCX:
Wire.write(0x35); // Wake from
Wire.write(0x17); // sleep
Wire.endTransmission();
Wire.beginTransmission(i2c_address);
// TODO: Clock stretching is used for SHT3x but not for SHTC3. Why?
Wire.write(0x78); // Disable clock stretching
Wire.write(0x66); // Normal mode measurement
break;
case SHT3X_TYPE_SHT4X:
Wire.write(0xFD); // High repeatability measurement
break;
}
if (Wire.endTransmission() != 0) { // Stop I2C transmission
if (Wire.endTransmission() != 0) { // Stop I2C transmission
return false;
}
delay(30); // Timing verified with logic analyzer (10 is to short)
Wire.requestFrom(sht3x_address, (uint8_t)6); // Request 6 bytes of data
delay(30); // Timing verified with logic analyzer (10 is to short)
Wire.requestFrom(i2c_address, (uint8_t)6); // Request 6 bytes of data
for (uint32_t i = 0; i < 6; i++) {
data[i] = Wire.read(); // cTemp msb, cTemp lsb, cTemp crc, humidity msb, humidity lsb, humidity crc
data[i] = Wire.read(); // temperature (MSB, LSB, CRC), humidity (MSB, LSB, CRC)
};
if ((Sht3xComputeCrc(&data[0], 2) != data[2]) || (Sht3xComputeCrc(&data[3], 2) != data[5])) {
return false;
}
t = ConvertTemp((float)((((data[0] << 8) | data[1]) * 175) / 65535.0) - 45);
h = ConvertHumidity((float)((((data[3] << 8) | data[4]) * 100) / 65535.0));
return (!isnan(t) && !isnan(h) && (h != 0));
return (!isnan(t) && !isnan(h));
}
/********************************************************************************************/
void Sht3xDetect(void)
{
void Sht3xDetect(void) {
float t;
float h;
for (uint32_t i = 0; i < SHT3X_MAX_SENSORS; i++) {
if (!I2cSetDevice(sht3x_addresses[i])) { continue; }
float t;
float h;
if (Sht3xRead(t, h, sht3x_addresses[i])) {
sht3x_sensors[sht3x_count].address = sht3x_addresses[i];
GetTextIndexed(sht3x_sensors[sht3x_count].types, sizeof(sht3x_sensors[sht3x_count].types), i, kShtTypes);
I2cSetActiveFound(sht3x_sensors[sht3x_count].address, sht3x_sensors[sht3x_count].types);
sht3x_count++;
if (i < 2) { // 0x44 and 0x45
sht3x_sensors[sht3x_count].type = SHT3X_TYPE_SHT3X;
if (!Sht3xRead(sht3x_sensors[sht3x_count].type, t, h, sht3x_addresses[i])) {
sht3x_sensors[sht3x_count].type = SHT3X_TYPE_SHT4X;
if (!Sht3xRead(sht3x_sensors[sht3x_count].type, t, h, sht3x_addresses[i])) {
continue;
}
}
} else { // 0x70
sht3x_sensors[sht3x_count].type = SHT3X_TYPE_SHTCX;
if (!Sht3xRead(sht3x_sensors[sht3x_count].type, t, h, sht3x_addresses[i])) {
continue;
}
}
sht3x_sensors[sht3x_count].address = sht3x_addresses[i];
GetTextIndexed(sht3x_sensors[sht3x_count].types, sizeof(sht3x_sensors[sht3x_count].types), SHT3X_TYPE_SHT3X, kSht3xTypes);
I2cSetActiveFound(sht3x_sensors[sht3x_count].address, sht3x_sensors[sht3x_count].types);
sht3x_count++;
}
}
void Sht3xShow(bool json)
{
void Sht3xShow(bool json) {
float t;
float h;
char types[11];
for (uint32_t i = 0; i < sht3x_count; i++) {
float t;
float h;
if (Sht3xRead(t, h, sht3x_sensors[i].address)) {
char types[11];
if (Sht3xRead(sht3x_sensors[i].type, t, h, sht3x_sensors[i].address)) {
t = ConvertTemp(t);
h = ConvertHumidity(h);
strlcpy(types, sht3x_sensors[i].types, sizeof(types));
if (sht3x_count > 1) {
snprintf_P(types, sizeof(types), PSTR("%s%c%02X"), sht3x_sensors[i].types, IndexSeparator(), sht3x_sensors[i].address); // "SHT3X-0xXX"
@ -112,8 +168,7 @@ void Sht3xShow(bool json)
* Interface
\*********************************************************************************************/
bool Xsns14(uint8_t function)
{
bool Xsns14(uint8_t function) {
if (!I2cEnabled(XI2C_15)) { return false; }
bool result = false;
@ -137,4 +192,4 @@ bool Xsns14(uint8_t function)
}
#endif // USE_SHT3X
#endif // USE_I2C
#endif // USE_I2C