Don't misdetect the ES7210 audio codec as an INA219 (#11120)

The T-Deck's ES7210 audio ADC straps to 0x40, which is INA_ADDR. It
answers none of the INA226/INA260/SHT2X checks, so it fell into the bare
"assume INA219" fallback and PowerTelemetry then drove an audio codec as
a power monitor, panicking the board into a boot loop.

The INA219 has no manufacturer or die ID register, so it can only ever be
inferred. Positively identify the ES7210 instead, via its chip ID
registers 0x3D/0x3E (0x72/0x10), and leave the device unclaimed.

Also guard the INA219 fallback on the type rather than chaining it to the
SHT2X check: that else bound to the SHT2X if, so a positively identified
INA226/INA260 was overwritten with INA219 right after being found.

Fixes #11115
This commit is contained in:
Ben Meadors
2026-07-21 07:24:01 -05:00
committed by GitHub
co-authored by GitHub
parent d587f08481
commit a58daf015d
+49 -1
View File
@@ -220,6 +220,44 @@ bool detectSHT21SerialNumber(TwoWire *i2cBus, uint8_t address)
}
#endif
// Everest Semiconductor ES7210 4-channel audio ADC, as found on the T-Deck. Its AD1/AD0 straps
// select 0x40..0x43, so it lands squarely on the INA/SHT2X addresses. Chip ID registers and their
// reset values, per the ES7210 datasheet rev 2.0.
static constexpr uint8_t ES7210_CHIP_ID1_REG = 0x3D;
static constexpr uint8_t ES7210_CHIP_ID1 = 0x72;
static constexpr uint8_t ES7210_CHIP_ID0_REG = 0x3E;
static constexpr uint8_t ES7210_CHIP_ID0 = 0x10;
static bool readByteRegister(TwoWire *i2cBus, uint8_t address, uint8_t reg, uint8_t &value)
{
i2cBus->beginTransmission(address);
i2cBus->write(reg);
if (i2cBus->endTransmission() != 0)
return false;
if (i2cBus->requestFrom(address, (uint8_t)1) != 1 || !i2cBus->available())
return false;
value = i2cBus->read();
return true;
}
// The INA219 has no manufacturer or die ID register to check, so it is inferred from "something
// answered here and it wasn't anything else we know". That makes positively identifying the other
// occupants of this address the only way to keep them out of the fallback.
static bool detectES7210(TwoWire *i2cBus, uint8_t address)
{
uint8_t id = 0;
// Read the high ID byte first so anything that clearly isn't an ES7210 costs a single
// transaction, then confirm with the low byte.
if (!readByteRegister(i2cBus, address, ES7210_CHIP_ID1_REG, id) || id != ES7210_CHIP_ID1)
return false;
return readByteRegister(i2cBus, address, ES7210_CHIP_ID0_REG, id) && id == ES7210_CHIP_ID0;
}
#define SCAN_SIMPLE_CASE(ADDR, T, ...) \
case ADDR: \
logFoundDevice(__VA_ARGS__); \
@@ -472,13 +510,23 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
type = INA260;
}
}
// The ES7210 audio ADC shares this address on some boards (T-Deck). It answers
// none of the checks above, so identify it here and leave the type unset - driving
// an audio codec as if it were a power monitor crashes the device. See #11115.
if (type == NONE && detectES7210(i2cBus, (uint8_t)addr.address)) {
LOG_INFO("ES7210 audio codec at 0x%x, not a power sensor", (uint8_t)addr.address);
break;
}
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
if (type == NONE && detectSHT21SerialNumber(i2cBus, (uint8_t)addr.address)) {
logFoundDevice("SHTXX (SHT2X)", (uint8_t)addr.address);
type = SHTXX;
}
#endif
else { // Assume INA219 if none of the above ones are found
// Guarded on the type rather than chained to the SHT2X check above, so that a
// positively identified INA226/INA260 isn't overwritten right after being found.
if (type == NONE) { // Assume INA219 if none of the above ones are found
logFoundDevice("INA219", (uint8_t)addr.address);
type = INA219;
}