168#if defined(MUON_TMP112_I2C_ADDR)
169 const uint8_t addr = (uint8_t)MUON_TMP112_I2C_ADDR;
171 const uint8_t addr = 0x48;
175 const uint8_t tempReg = 0x00;
180 Wire.beginTransmission(addr);
182 int status = Wire.endTransmission(
false);
188 const uint8_t toRead = 2;
189 (void)Wire.requestFrom((
int)addr, (
int)toRead);
190 if (Wire.available() < toRead) {
195 uint8_t msb = (uint8_t)Wire.read();
196 uint8_t lsb = (uint8_t)Wire.read();
201 int16_t raw = (int16_t)((((uint16_t)msb) << 8) | (uint16_t)lsb);
202 raw = (int16_t)(raw >> 4);
205 raw = (int16_t)(raw | 0xF000);
208 tempC = ((float)raw) * 0.0625f;
227#if HAL_PLATFORM_CELLULAR || PLATFORM_ID == PLATFORM_ARGON
231 uint8_t battState = System.batteryState();
232 float soc = System.batteryCharge();
233 int powerSource = System.powerSource();
236 Log.info(
"Battery: state=%s (%d), SoC=%.2f%%, powerSource=%d",
239 current.set_batteryState(battState);
240 current.set_stateOfCharge(soc);
242#if HAL_PLATFORM_CELLULAR && (PLATFORM_ID != PLATFORM_MSOM)
274 static unsigned long lastRemediationAttempt = 0;
275 static uint8_t remediationLevel = 0;
276 static uint8_t consecutiveFaults = 0;
277 const unsigned long REMEDIATION_COOLDOWN = 3600000;
285 byte faultReg = pmic.readFaultRegister();
288 if (faultReg & 0x38) {
289 uint8_t chargeFault = (faultReg >> 3) & 0x07;
292 switch(chargeFault) {
298 Log.info(
"PMIC: Input fault - VBUS out of range (likely solar variation)");
301 Log.error(
"PMIC: Thermal shutdown - charging stopped due to temperature");
305 Log.error(
"PMIC: Charge safety timer expired - charging timeout (common stuck charging indicator)");
309 Log.warn(
"PMIC: Charge fault detected (code=0x%02x)", chargeFault);
317 Log.info(
"PMIC: Fault detected but charging disabled due to temperature (%.1fC) - skipping remediation",
318 (
double)
current.get_internalTempC());
322 unsigned long now = millis();
323 if (now - lastRemediationAttempt > REMEDIATION_COOLDOWN) {
325 if (consecutiveFaults >= 3 && remediationLevel < 2) {
326 remediationLevel = 2;
327 }
else if (consecutiveFaults >= 2 && remediationLevel < 1) {
328 remediationLevel = 1;
332 switch(remediationLevel) {
334 Log.warn(
"PMIC: Attempting soft remediation - cycle charging (level 1)");
335 pmic.disableCharging();
337 pmic.enableCharging();
338 Log.info(
"PMIC: Charging re-enabled after soft reset");
342 Log.error(
"PMIC: Attempting aggressive remediation - power cycle reset (level 2)");
343 pmic.disableCharging();
346 pmic.setWatchdog(0b01);
347 pmic.enableCharging();
348 Log.info(
"PMIC: Charging re-enabled with watchdog supervision");
349 remediationLevel = 0;
353 Log.info(
"PMIC: Fault detected but remediation level 0 - monitoring only");
357 lastRemediationAttempt = now;
359 unsigned long remainingCooldown = (REMEDIATION_COOLDOWN - (now - lastRemediationAttempt)) / 60000;
360 Log.info(
"PMIC: Fault detected but in cooldown period (%lu min remaining)", remainingCooldown);
365 if (consecutiveFaults > 0) {
366 Log.info(
"PMIC: Charging healthy - clearing fault counters");
367 consecutiveFaults = 0;
368 remediationLevel = 0;
371 int8_t currentAlert =
current.get_alertCode();
372 if (currentAlert >= 20 && currentAlert <= 23) {
373 Log.info(
"PMIC: Clearing battery/charging alert %d - charging resumed", currentAlert);
381 byte systemStatus = pmic.readSystemStatusRegister();
382 uint8_t chargeStatus = (systemStatus >> 4) & 0x03;
383 bool vbusGood = (systemStatus & 0x80) != 0;
384 uint8_t thermalStatus = systemStatus & 0x03;
386 const char* chargeStatusStr[] = {
"Not Charging",
"Pre-charge",
"Fast Charging",
"Charge Done"};
387 const char* thermalStr[] = {
"Normal",
"Warm",
"Hot",
"Cold"};
389 Log.info(
"PMIC Status: charge=%s, VBUS=%s, thermal=%s, faultReg=0x%02x",
390 chargeStatusStr[chargeStatus],
391 vbusGood ?
"Good" :
"Fault",
392 thermalStr[thermalStatus],
396 static uint8_t lastChargeStatus = 0xFF;
397 static float lastSoC = -1.0f;
398 static unsigned long chargeStateStartTime = 0;
400 if (chargeStatus == 2) {
401 if (lastChargeStatus == 2) {
403 if (abs(soc - lastSoC) < 1.0f) {
404 if (chargeStateStartTime == 0) {
405 chargeStateStartTime = millis();
406 }
else if (millis() - chargeStateStartTime > 6UL * 3600000UL) {
407 Log.error(
"PMIC: Stuck in Fast Charging for 6+ hours with no SoC increase (%.1f%%) - possible fault", (
double)soc);
411 chargeStateStartTime = 0;
414 chargeStateStartTime = millis();
417 chargeStateStartTime = 0;
420 lastChargeStatus = chargeStatus;
424#elif PLATFORM_ID == 32 || PLATFORM_ID == 34
429 int raw = analogRead(A6);
430 float voltage = raw / 819.2f;
434 float soc = (voltage - 3.0f) * (100.0f / (4.2f - 3.0f));
437 }
else if (soc > 100.0f) {
440 current.set_stateOfCharge(soc);
445 uint8_t battState = 0;
448 Log.info(
"Battery: voltage=%.2fV, state=%s (%d), SoC=%.2f%% (estimated from voltage)",
449 (
double)voltage,
batteryContext[battState], battState, (
double)soc);
451 current.set_batteryState(battState);
468#if defined(MUON_TMP112_I2C_ADDR)
469 const uint8_t tmp112Addr = (uint8_t)MUON_TMP112_I2C_ADDR;
471 const uint8_t tmp112Addr = 0x48;
474 static bool tmp112ProbeDone =
false;
475 static bool tmp112Present =
false;
477#if !defined(DISABLE_TMP112_AUTODETECT)
478 if (!tmp112ProbeDone) {
482 tmp112Present = probeTmp112Present(tmp112Addr);
483 tmp112ProbeDone =
true;
485 Log.info(
"TMP112A probe at 0x%02X: %s", tmp112Addr, tmp112Present ?
"present" :
"not found");
490#if defined(MUON_HAS_TMP112)
491 tmp112Present =
true;
492 tmp112ProbeDone =
true;
498 float prev =
current.get_internalTempC();
499 tempC = (prev > -50.0f && prev < 120.0f) ? prev : 25.0f;
500 Log.warn(
"TMP112A read failed/invalid - falling back to %4.2f C", (
double)tempC);
502 current.set_internalTempC(tempC);
505#if (PLATFORM_ID == 32 || PLATFORM_ID == 34) && !defined(MUON_HAS_TMP36)
512 float tempC =
current.get_internalTempC();
513 if (!(tempC > -50.0f && tempC < 120.0f)) {
518 Log.info(
"P2/Photon2 stub: using internalTempC=%4.2f C (no TMP36 ADC)", (
double)tempC);
521 current.set_internalTempC(tempC);
533 return current.get_stateOfCharge() > 20.0f;
537 const int TMP36_SAMPLES = 8;
538 static int sampleIndex = 0;
539 static int tmpRawSum = 0;
541 if (sampleIndex < TMP36_SAMPLES) {
549 return current.get_stateOfCharge() > 20.0f;
553 int tmpRaw = tmpRawSum / TMP36_SAMPLES;
561 bool sensorOk = (tmpRaw > 50 && tmpRaw < 4000);
569 if (!sensorOk || tempC < -20.0f || tempC > 80.0f) {
570 float prev =
current.get_internalTempC();
571 float fallback = 25.0f;
573 if (prev > -20.0f && prev < 80.0f) {
577 Log.warn(
"TMP36 reading invalid or out of range (tmp36=%4.2f C, raw=%d, sensorOk=%s) - falling back to %4.2f C",
578 (
double)tempC, tmpRaw, sensorOk ?
"true" :
"false", (
double)fallback);
582 current.set_internalTempC(tempC);
586 Log.info(
"Enclosure temperature (effective): %4.2f C (raw=%d)", (
double)tempC, tmpRaw);
597 return current.get_stateOfCharge() > 20.0f;
655#if HAL_PLATFORM_CELLULAR
656 const char *radioTech[10] = {
"Unknown",
"None",
"WiFi",
"GSM",
657 "UMTS",
"CDMA",
"LTE",
"IEEE802154",
658 "LTE_CAT_M1",
"LTE_CAT_NB1"};
661 CellularSignal sig = Cellular.RSSI();
663 auto rat = sig.getAccessTechnology();
666 float strengthPercentage = sig.getStrength();
669 float qualityPercentage = sig.getQuality();
672 radioTech[rat], strengthPercentage, qualityPercentage);
674#elif HAL_PLATFORM_WIFI
675 WiFiSignal sig = WiFi.RSSI();
676 float strengthPercentage = sig.getStrength();
677 float qualityPercentage = sig.getQuality();
680 strengthPercentage, qualityPercentage);