diff --git a/BUILDS.md b/BUILDS.md index e760e3cba1c7..cb6e3de96d79 100644 --- a/BUILDS.md +++ b/BUILDS.md @@ -112,6 +112,7 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up | USE_ADS1115 | - | - / x | - | x | - | - | | USE_INA219 | - | - / x | - | x | - | - | | USE_INA226 | - | - / - | - | - | - | - | +| USE_INA3221 | - | - / - | - | - | - | - | | USE_SHT3X | - | - / x | - | x | - | - | | USE_TSL2561 | - | - / - | - | - | - | - | | USE_TSL2591 | - | - / - | - | - | - | - | @@ -147,6 +148,7 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up | USE_PMSA003I | - | - / - | - | - | - | - | | USE_LOX_O2 | - | - / x | - | x | - | - | | USE_GDK101 | - | - / - | - | - | - | - | +| USE_TC74 | - | - / - | - | - | - | - | | | | | | | | | | Feature or Sensor | l | t | k | s | i | d | Remarks | USE_HIH6 | - | - / x | - | x | - | - | diff --git a/CHANGELOG.md b/CHANGELOG.md index cc77eec5aff7..67f37e0ee20b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. - Matter allow `Matter#Initialized` rule once the device is configured (#18451) - Matter UI to change endpoints configuration (#18498) - Matter support for Shutters (without Tilt) (#18509) +- Support for TC74 temperature sensor by Michael Loftis (#18042) ### Breaking Changed diff --git a/CODE_OWNERS.md b/CODE_OWNERS.md index 2e9bcfc20314..49d7f6f53668 100644 --- a/CODE_OWNERS.md +++ b/CODE_OWNERS.md @@ -206,6 +206,8 @@ In addition to @arendst the following code is mainly owned by: | xsns_104_pmsa003i | Jean-Pierre Deschamps | xsns_105_lox_o2 | @ACE1046 | xsns_106_gdk101 | @Szewcson +| xsns_107_gm861 | @arendst +| xsns_108_tc74 | Michael Loftis | | | Libraries | | | diff --git a/I2CDEVICES.md b/I2CDEVICES.md index d90ff4e44eca..9faaea61c299 100644 --- a/I2CDEVICES.md +++ b/I2CDEVICES.md @@ -114,4 +114,5 @@ Index | Define | Driver | Device | Address(es) | Description 76 | USE_SEN5X | xsns_103 | SEN5X | 0x69 | Gas (VOC/NOx index) and air quality (PPM <1,<2.5,<4,<10) 77 | USE_MCP23XXX_DRV | xdrv_67 | MCP23x17 | 0x20 - 0x26 | 16-bit I/O expander as virtual button/switch/relay 78 | USE_PMSA003I | xsns_104 | PMSA003I | 0x12 | PM2.5 Air Quality Sensor with I2C Interface - 79 | USE_GDK101 | xsns_106 | GDK101 | 0x18 - 0x1B | Gamma Radiation Sensor \ No newline at end of file + 79 | USE_GDK101 | xsns_106 | GDK101 | 0x18 - 0x1B | Gamma Radiation Sensor + 80 | USE_TC74 | xsns_108 | TC74 | 0x48 - 0x4F | Temperature sensor \ No newline at end of file diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c30e4cfa64bf..df5c0d388b3f 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -113,6 +113,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm ## Changelog v12.5.0.1 ### Added - Command ``SetOption152 0/1`` to select two (0 = default) pin bistable or one (1) pin latching relay control [#18386](https://github.com/arendst/Tasmota/issues/18386) +- Support for TC74 temperature sensor by Michael Loftis [#18042](https://github.com/arendst/Tasmota/issues/18042) - Matter sensors Humidity, Pressure, Illuminance [#18441](https://github.com/arendst/Tasmota/issues/18441) - Matter allow `Matter#Initialized` rule once the device is configured [#18451](https://github.com/arendst/Tasmota/issues/18451) - Matter UI to change endpoints configuration [#18498](https://github.com/arendst/Tasmota/issues/18498) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 83c450db6ad2..0d4b8088a626 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -706,7 +706,10 @@ // #define GDK101_SHOW_STATUS // #define GDK101_SHOW_VIBRATION_STATUS // #define GDK101_SHOW_MEAS_TIME - +// #define USE_TC74 // [I2cDriver80] Enable TC74 sensor (I2C addresses 0x48 - 0x4F) (+1k code) +// #define TC74_MAX_SENSORS 8 // Support non-default/multiple I2C addresses +// #define TC74_I2C_PROBE_ADDRESSES { 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F } // Addresses to probe/support +// #define TC74_MAX_FAILCOUNT 8 // Maximum failed polls before it's marked inactive until reprobing later // #define USE_RTC_CHIPS // Enable RTC chip support and NTP server - Select only one // #define USE_DS3231 // [I2cDriver26] Enable DS3231 RTC (I2C address 0x68) (+1k2 code) diff --git a/tasmota/tasmota_support/support_features.ino b/tasmota/tasmota_support/support_features.ino index 7bdb9fb3444b..e19a74c86691 100644 --- a/tasmota/tasmota_support/support_features.ino +++ b/tasmota/tasmota_support/support_features.ino @@ -888,9 +888,12 @@ void ResponseAppendFeatures(void) #if defined(USE_I2C) && defined(USE_GDK101) feature9 |= 0x00100000; // xsns_106_gdk101.ino #endif - -// feature9 |= 0x00200000; -// feature9 |= 0x00400000; +#ifdef USE_GM861 + feature9 |= 0x00200000; // xsns_107_gm861.ino +#endif +#if defined(USE_I2C) && defined(USE_TC74) + feature9 |= 0x00400000; // xsns_108_tc74.ino +#endif // feature9 |= 0x00800000; // feature9 |= 0x01000000; diff --git a/tasmota/tasmota_xsns_sensor/xsns_108_tc74.ino b/tasmota/tasmota_xsns_sensor/xsns_108_tc74.ino new file mode 100644 index 000000000000..ccc4a98ad5f3 --- /dev/null +++ b/tasmota/tasmota_xsns_sensor/xsns_108_tc74.ino @@ -0,0 +1,287 @@ +/* + xsns_108_tc74.ino - TC74 I2C temperature sensor support for Tasmota + + Copyright (C) 2023 Michael Loftis + + 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +#ifdef USE_I2C +#ifdef USE_TC74 + +/*********************************************************************************************\ + * TC74 - Temperature Sensor + * + * TC74 I2C Address: 0x4D + * Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/21462D.pdf + * + * Eight I2C address variant parts are available. The part numbers are TC74An where N is + * one of the eight addresses noted in the (unused) #defines below. Be sure to check your + * part number for the correct address before building your firmware image! Due to the + * limited RAM in ESP8266's the default configuration does not try to work with all eight. + * + * For I2C 0x4D, TC74A5 part, is the standard address, other addresses are available as a + * custom order, eight total addresses. This driver thus supports up to + * eight sensors, sensors beyond/other than the default 0x4D require setting + * of defines, there is also no good way to ID these sensors as there's no MSR + * and nothing unique about their information. + * + * These sensors only have a nominal 1C resolution. The git devices are sold + * calibrated to a specific supply voltage but will work from 2.7V-5.5V, but + * will suffer 1C per Volt deviation from the rating of accuracy loss. They're + * only accurate to about +/-2C to begin with. + \*********************************************************************************************/ + +#define XSNS_108 108 +#define XI2C_80 80 + +//#define TC74_MAX_SENSORS 8 // Support non-default/multiple I2C addresses +//#define TC74_I2C_PROBE_ADDRESSES { 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F } // Addresses to probe/support +//#define TC74_MAX_FAILCOUNT 8 // maximum failed polls before it's marked inactive until reprobing later +//#define TC74_EXTRA_DEBUG // Compile in some extra debug logging statements around failures and measurements + +#define TC74_ADDR0 0x48 +#define TC74_ADDR1 0x49 +#define TC74_ADDR2 0x4A +#define TC74_ADDR3 0x4B +#define TC74_ADDR4 0x4C +#define TC74_ADDR5 0x4D +#define TC74_ADDR6 0x4E +#define TC74_ADDR7 0x4F +#define TC74_ADDR_BASE TC74_ADDR0 +#define TC74_ADDR_DEFAULT TC74_ADDR5 + +#ifndef TC74_MAX_SENSORS +#define TC74_MAX_SENSORS 1 +uint8_t tc74_probes[TC74_MAX_SENSORS] PROGMEM = { TC74_ADDR_DEFAULT }; +#else +uint8_t tc74_probes[TC74_MAX_SENSORS] PROGMEM = TC74_I2C_PROBE_ADDRESSES; +#endif + +#ifndef TC74_MAX_FAILCOUNT +#define TC74_MAX_FAILCOUNT 8 +#endif + +const uint8_t TC74_CMD_RTR = 0x00; // Read Temperature +const uint8_t TC74_CMD_RWCR = 0x01; // Read/Write CONFIG + +/* CONFIG register + * D7 = STANDBY (R/W) + * D6 = READY (R/O) + * D5-D0 = Reserved - always zero + * POR state is all zeroes. + */ +const uint8_t TC74_CONFIG_DRDY = 0b00000010; + +// only used once... +#define TC74_CONFIG_MASK 0b11111100 + +struct { + uint8_t found_count = 0; + uint8_t tcnt = 0; +} tc74_status; + +struct { + // Last read temperature + float temperature = NAN; + // flag if we could/couldn't read in most recent poll + bool is_active; + // failure count, if this goes above N we set active to false and + // the sensor will be retried later + uint8_t failed_count; + uint8_t address; +} tc74_sensors[TC74_MAX_SENSORS]; + + +void TC74InitState() { +#ifdef TC74_EXTRA_DEBUG + AddLog(LOG_LEVEL_DEBUG, PSTR("TC7: InitState")); +#endif + for (uint32_t i = 0; i < TC74_MAX_SENSORS; i++) { + tc74_sensors[i].is_active = false; + tc74_sensors[i].failed_count = 0; + tc74_sensors[i].address = pgm_read_byte(tc74_probes + i); + } +} + +void TC74Detect(bool forced) { + for (uint32_t i = 0; i < TC74_MAX_SENSORS; i++) { + uint8_t config_reg; + uint8_t addr = tc74_sensors[i].address; +#ifdef TC74_EXTRA_DEBUG + AddLog(LOG_LEVEL_DEBUG, PSTR("TC7: Addr %X probing"), addr); +#endif + + // if we failed more than N times, unless we're being forced to, skip it. + if (!forced && tc74_sensors[i].failed_count >= TC74_MAX_FAILCOUNT ) { + tc74_sensors[i].is_active = false; + continue; + } + + // I2cSetDevice ALWAYs returns false if a device is marked active already... + // So if we have the is_active flag for this device SKIP this check + if (!tc74_sensors[i].is_active && !I2cSetDevice(addr)) { + if (tc74_sensors[i].failed_count < 253) { tc74_sensors[i].failed_count++; } +#ifdef TC74_EXTRA_DEBUG + AddLog(LOG_LEVEL_DEBUG, PSTR("TC7: Addr %X failed I2cSetDevice"), addr); +#endif + continue; + } + + // Pull CONFIG and check it, best we can do to keep away from other I2C devices + if (!I2cValidRead8(&config_reg, addr, TC74_CMD_RWCR)) { + tc74_sensors[i].is_active = false; + tc74_sensors[i].failed_count++; + if(I2cActive(addr)) { I2cResetActive(addr); } +#ifdef TC74_EXTRA_DEBUG + AddLog(LOG_LEVEL_ERROR, PSTR("TC7: Addr %X failed CONFIG read, deactivated"), addr); +#endif + continue; + } + + // if any reserved bits are set, not our device + if (config_reg & TC74_CONFIG_MASK != 0x00 ) { + tc74_sensors[i].is_active = false; + tc74_sensors[i].failed_count++; + if(I2cActive(addr)) { I2cResetActive(addr); } +#ifdef TC74_EXTRA_DEBUG + AddLog(LOG_LEVEL_DEBUG, PSTR("TC7: Addr %X found reserved bits set [%x]"), addr, config_reg); +#endif + + continue; + } + + // Make sure STANDBY is not set, POR should be clear, but, if another I2C driver toggled it.... + I2cWrite8(addr, TC74_CMD_RWCR, 0x0); + + if (!tc74_sensors[i].is_active) { + tc74_sensors[i].is_active = true; +#ifdef TC74_EXTRA_DEBUG + AddLog(LOG_LEVEL_DEBUG, PSTR("TC7: Addr %X set active"), addr); +#endif + I2cSetActiveFound(addr, "TC74"); + } + tc74_sensors[i].failed_count = 0; + } // for sensors... +} + +void TC74PollActive() { + for (uint8_t i = 0; i < TC74_MAX_SENSORS; i++) { + uint8_t addr = tc74_sensors[i].address; + uint8_t config_reg; + int8_t temperature_register; + + if (!tc74_sensors[i].is_active) { continue; } + + // if timing were a problem we could read both bytes from the RTR, then + // do mask/shift to recover the parts we want. + if (!I2cValidRead8(&config_reg, addr, TC74_CMD_RWCR)) { + tc74_sensors[i].failed_count++; + continue; + } + + // check if a measurement is ready + if (config_reg & TC74_CONFIG_DRDY != 0b00000010) { continue; } + + // grab most recent reading + if (!I2cValidRead8((uint8_t*)&temperature_register, addr, TC74_CMD_RTR)) { + tc74_sensors[i].failed_count++; + continue; + } + + tc74_sensors[i].temperature = ConvertTemp(temperature_register); + tc74_sensors[i].failed_count = 0; +#ifdef TC74_EXTRA_DEBUG + AddLog(LOG_LEVEL_DEBUG, PSTR("TC7: Addr %X register temperature was %hhi or [%i]"), addr, temperature_register, temperature_register); + AddLog(LOG_LEVEL_DEBUG, PSTR("TC7: Addr %X stored temperature %*_f"), addr, Settings->flag2.temperature_resolution, &tc74_sensors[i].temperature); +#endif + + } // for sensors... +} + +void TC74Show(bool json) { + bool once = true; + // loop over sensors, only output if active + for (uint8_t i = 0; i < TC74_MAX_SENSORS; i++) { + if (tc74_sensors[i].is_active) { + char sname[10]; + snprintf_P(sname, sizeof(sname), PSTR("TC74%c%02X"), IndexSeparator(), tc74_sensors[i].address); + if (json) { + ResponseAppend_P(JSON_SNS_F_TEMP, sname, Settings->flag2.temperature_resolution, &tc74_sensors[i].temperature); + // also send KNX and Domoticz if enabled...and first sensor reporting + // might beed guarded by some sort of enable (compile time or otherwise) + if ((0 == TasmotaGlobal.tele_period) && once) { +#ifdef USE_DOMOTICZ + DomoticzFloatSensor(DZ_TEMP, tc74_sensors[i].temperature); +#endif // USE_DOMOTICZ +#ifdef USE_KNX + KnxSensor(KNX_TEMPERATURE, tc74_sensors[i].temperature); +#endif // USE_KNX + once = false; + } // tele_period, first reporter +#ifdef USE_WEBSERVER + } else { + WSContentSend_Temp(sname, tc74_sensors[i].temperature); +#endif // USE_WEBSERVER + } // if(json) + } // if(is_active) + } // for sensors... +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns108(uint32_t function) { + if (!I2cEnabled(XI2C_80)) { return false; } + + bool result = false; + + // Unlike some other drivers we DO support sensors disappearing and reappearing later + // or simply not available during initial startup + + switch (function) { + case FUNC_INIT: + TC74InitState(); + TC74Detect(false); + break; + case FUNC_HOTPLUG_SCAN: + TC74Detect(true); + tc74_status.tcnt = 1; + break; + case FUNC_EVERY_SECOND: + /* if(tc74_status.tcnt == 0 || tc74_status.tcnt == 60) { + TC74Detect(true); + } */ + tc74_status.tcnt++; + if(tc74_status.tcnt > 60) { + tc74_status.tcnt = 1; + } + TC74PollActive(); + break; + case FUNC_JSON_APPEND: + TC74Show(true); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + TC74Show(false); + break; +#endif + } // switch(function) + return result; +} + +#endif // USE_TC74 +#endif // USE_I2C \ No newline at end of file diff --git a/tools/decode-status.py b/tools/decode-status.py index ebe18e460c23..8ff87b7bfbee 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -295,7 +295,7 @@ "USE_DINGTIAN_RELAY","USE_HMC5883L","USE_LD2410","USE_ME007", "USE_DISPLAY_TM1650","USE_PCA9632","USE_TUYAMCUBR","USE_SEN5X", "USE_BIOPDU","USE_MCP23XXX_DRV","USE_PMSA003I","USE_LOX_O2", - "USE_GDK101","","","", + "USE_GDK101","USE_GM861","USE_TC74","", "","","","", "","","","" ]]