Skip to content

Commit

Permalink
Add support for TC74
Browse files Browse the repository at this point in the history
Add support for TC74 temperature sensor by Michael Loftis (#18042)
  • Loading branch information
arendst committed May 1, 2023
1 parent 616652e commit cda2bf1
Show file tree
Hide file tree
Showing 9 changed files with 306 additions and 6 deletions.
2 changes: 2 additions & 0 deletions BUILDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 | - | - / - | - | - | - | - |
Expand Down Expand Up @@ -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 | - | - |
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions CODE_OWNERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
| |
Expand Down
3 changes: 2 additions & 1 deletion I2CDEVICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
79 | USE_GDK101 | xsns_106 | GDK101 | 0x18 - 0x1B | Gamma Radiation Sensor
80 | USE_TC74 | xsns_108 | TC74 | 0x48 - 0x4F | Temperature sensor
1 change: 1 addition & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 4 additions & 1 deletion tasmota/my_user_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 6 additions & 3 deletions tasmota/tasmota_support/support_features.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
287 changes: 287 additions & 0 deletions tasmota/tasmota_xsns_sensor/xsns_108_tc74.ino
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

#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
2 changes: 1 addition & 1 deletion tools/decode-status.py
Original file line number Diff line number Diff line change
Expand Up @@ -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","",
"","","","",
"","","",""
]]
Expand Down

0 comments on commit cda2bf1

Please sign in to comment.