-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for TC74 temperature sensor by Michael Loftis (#18042)
- Loading branch information
Showing
9 changed files
with
306 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters