enable Charging Indicator

This commit is contained in:
Thomas Göttgens
2026-05-18 23:41:41 +02:00
parent 8a8e4b841a
commit 8fa7ae40a7
4 changed files with 235 additions and 0 deletions

View File

@@ -23,6 +23,7 @@
#include "main.h"
#include "meshUtils.h"
#include "power/PowerHAL.h"
#include "power/SGM41562.h"
#include "sleep.h"
#if defined(ARCH_PORTDUINO)
@@ -453,6 +454,10 @@ class AnalogBatteryLevel : public HasBatteryLevel
/// source
virtual bool isVbusIn() override
{
#ifdef HAS_SGM41562
if (sgm41562 && sgm41562->refresh())
return sgm41562->isInputPowerGood();
#endif
#ifdef EXT_PWR_DETECT
#if defined(HELTEC_CAPSULE_SENSOR_V3) || defined(HELTEC_SENSOR_HUB)
// if external powered that pin will be pulled down
@@ -483,6 +488,10 @@ class AnalogBatteryLevel : public HasBatteryLevel
/// we can't be smart enough to say 'full'?
virtual bool isCharging() override
{
#ifdef HAS_SGM41562
if (sgm41562 && sgm41562->refresh())
return sgm41562->isCharging();
#endif
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(HAS_RAKPROT) && !defined(HAS_PMU)
if (hasRAK()) {
return (rak9154Sensor.isCharging()) ? OptTrue : OptFalse;
@@ -697,6 +706,12 @@ bool Power::analogInit()
*/
bool Power::setup()
{
#ifdef HAS_SGM41562
// Initialize the charger early so AnalogBatteryLevel can read charging
// state from it. The charger does not provide battery voltage / percent —
// those still come from the platform ADC via analogInit() below.
initSGM41562(SGM41562_WIRE);
#endif
bool found = false;
if (axpChipInit()) {
found = true;

109
src/power/SGM41562.cpp Normal file
View File

@@ -0,0 +1,109 @@
#include "SGM41562.h"
#ifdef HAS_SGM41562
#include <Arduino.h>
SGM41562 *sgm41562 = nullptr;
bool initSGM41562(TwoWire &wire)
{
if (sgm41562)
return true;
sgm41562 = new SGM41562();
if (!sgm41562->begin(wire)) {
delete sgm41562;
sgm41562 = nullptr;
return false;
}
return true;
}
bool SGM41562::readReg(uint8_t reg, uint8_t &value)
{
wire_->beginTransmission(address_);
wire_->write(reg);
if (wire_->endTransmission(false) != 0)
return false;
if (wire_->requestFrom((int)address_, 1) != 1)
return false;
value = wire_->read();
return true;
}
bool SGM41562::writeReg(uint8_t reg, uint8_t value)
{
wire_->beginTransmission(address_);
wire_->write(reg);
wire_->write(value);
return wire_->endTransmission() == 0;
}
bool SGM41562::updateReg(uint8_t reg, uint8_t mask, uint8_t value)
{
uint8_t cur;
if (!readReg(reg, cur))
return false;
cur = (cur & ~mask) | (value & mask);
return writeReg(reg, cur);
}
bool SGM41562::begin(TwoWire &wire, uint8_t address)
{
wire_ = &wire;
address_ = address;
uint8_t id;
if (!readReg(REG_DEVICE_ID, id)) {
LOG_WARN("SGM41562: I2C read failed at 0x%02X", address_);
return false;
}
if (id != DEVICE_ID_EXPECTED) {
LOG_WARN("SGM41562: unexpected device ID 0x%02X (expected 0x%02X)", id, DEVICE_ID_EXPECTED);
return false;
}
LOG_INFO("SGM41562: detected at 0x%02X (id 0x%02X)", address_, id);
// Mirror the vendor reference init sequence: PCB OTP off, NTC off,
// watchdog off, charger enabled. These match LilyGo's stock firmware
// for the T-Impulse Plus.
delay(120);
writeReg(REG_SYS_VOLTAGE_REG, 0xB7);
writeReg(REG_MISC_OP_CONTROL, 0x40);
writeReg(REG_CHARGE_TERM_TIMER, 0x1A);
writeReg(REG_POWER_ON_CFG, 0xA4);
return refresh();
}
bool SGM41562::refresh()
{
uint32_t now = millis();
if (lastRefreshMs_ != 0 && (now - lastRefreshMs_) < 250)
return true; // cached
lastRefreshMs_ = now == 0 ? 1 : now;
uint8_t status, fault;
if (!readReg(REG_SYSTEM_STATUS, status))
return false;
if (!readReg(REG_FAULT, fault))
return false;
chargeStatus_ = static_cast<ChargeStatus>((status >> SYS_STATUS_CHRG_SHIFT) & SYS_STATUS_CHRG_MASK);
inputPowerGood_ = (status & SYS_STATUS_PG) != 0;
thermalReg_ = (status & SYS_STATUS_THERM_REG) != 0;
faultMask_ = fault & 0x3F; // bits [7:6] are enter_ship_time config, not faults
return true;
}
bool SGM41562::setChargeEnable(bool enable)
{
return updateReg(REG_POWER_ON_CFG, POWER_ON_CFG_CHG_DISABLE, enable ? 0x00 : POWER_ON_CFG_CHG_DISABLE);
}
bool SGM41562::setShippingModeEnable(bool enable)
{
return updateReg(REG_MISC_OP_CONTROL, MISC_OP_SHIPPING_MODE, enable ? MISC_OP_SHIPPING_MODE : 0x00);
}
#endif // HAS_SGM41562

105
src/power/SGM41562.h Normal file
View File

@@ -0,0 +1,105 @@
#pragma once
#include "configuration.h"
#ifdef HAS_SGM41562
#include <Wire.h>
#include <stdint.h>
// SG Micro SGM41562 — single-cell Li-ion buck charger, I²C-controlled, no
// fuel gauge. This driver exposes status (charging / input good / fault),
// charge enable, and shipping-mode control. Battery voltage/percent still
// come from the platform ADC path; the charger is plumbed in as a
// side-channel for isCharging()/isVbusIn() in AnalogBatteryLevel.
//
// Reference: SGM41562 datasheet (Cmd map + bit fields cross-verified against
// LilyGo's `Cpp_Bus_Driver::Sgm41562xx` driver, which is what their vendor
// example for this board uses).
#ifndef SGM41562_ADDR
#define SGM41562_ADDR 0x03 // Per datasheet — unusual but correct
#endif
#ifndef SGM41562_WIRE
#define SGM41562_WIRE Wire1 // Most boards put the PMU on the secondary bus
#endif
class SGM41562
{
public:
enum class ChargeStatus : uint8_t {
NotCharging = 0b00,
Precharge = 0b01,
FastCharge = 0b10,
ChargeDone = 0b11,
};
bool begin(TwoWire &wire, uint8_t address = SGM41562_ADDR);
// Re-read the system status + fault registers. Throttled internally to
// at most one I²C transaction per 250 ms — call as often as you like.
bool refresh();
// Status — cached from the most recent refresh().
ChargeStatus chargeStatus() const { return chargeStatus_; }
bool isCharging() const
{
return chargeStatus_ == ChargeStatus::Precharge || chargeStatus_ == ChargeStatus::FastCharge;
}
bool isChargeDone() const { return chargeStatus_ == ChargeStatus::ChargeDone; }
bool isInputPowerGood() const { return inputPowerGood_; }
bool isThermalRegulation() const { return thermalReg_; }
uint8_t faultMask() const { return faultMask_; }
// Control.
bool setChargeEnable(bool enable);
bool setShippingModeEnable(bool enable);
private:
TwoWire *wire_ = nullptr;
uint8_t address_ = SGM41562_ADDR;
uint32_t lastRefreshMs_ = 0;
ChargeStatus chargeStatus_ = ChargeStatus::NotCharging;
bool inputPowerGood_ = false;
bool thermalReg_ = false;
uint8_t faultMask_ = 0;
bool readReg(uint8_t reg, uint8_t &value);
bool writeReg(uint8_t reg, uint8_t value);
bool updateReg(uint8_t reg, uint8_t mask, uint8_t value);
// SGM41562 register addresses
static constexpr uint8_t REG_INPUT_SOURCE = 0x00;
static constexpr uint8_t REG_POWER_ON_CFG = 0x01;
static constexpr uint8_t REG_CHARGE_CURRENT = 0x02;
static constexpr uint8_t REG_DISCHARGE_TERM_CURRENT = 0x03;
static constexpr uint8_t REG_CHARGE_VOLTAGE = 0x04;
static constexpr uint8_t REG_CHARGE_TERM_TIMER = 0x05;
static constexpr uint8_t REG_MISC_OP_CONTROL = 0x06;
static constexpr uint8_t REG_SYS_VOLTAGE_REG = 0x07;
static constexpr uint8_t REG_SYSTEM_STATUS = 0x08;
static constexpr uint8_t REG_FAULT = 0x09;
static constexpr uint8_t REG_I2C_ADDR_MISC = 0x0A;
static constexpr uint8_t REG_DEVICE_ID = 0x0B;
// Bit positions in REG_POWER_ON_CFG.
static constexpr uint8_t POWER_ON_CFG_CHG_DISABLE = 0x08; // bit 3: 1 = charging disabled
// Bit positions in REG_MISC_OP_CONTROL.
static constexpr uint8_t MISC_OP_SHIPPING_MODE = 0x20; // bit 5: 1 = enter shipping mode
// Bit positions in REG_SYSTEM_STATUS.
static constexpr uint8_t SYS_STATUS_CHRG_SHIFT = 3;
static constexpr uint8_t SYS_STATUS_CHRG_MASK = 0x03;
static constexpr uint8_t SYS_STATUS_PG = 0x02; // bit 1: input power good
static constexpr uint8_t SYS_STATUS_THERM_REG = 0x01; // bit 0: thermal regulation
static constexpr uint8_t DEVICE_ID_EXPECTED = 0x04;
};
extern SGM41562 *sgm41562;
// Lazy-instantiate the global on the supplied wire. Returns true on success.
bool initSGM41562(TwoWire &wire);
#endif // HAS_SGM41562

View File

@@ -157,6 +157,12 @@ static const uint8_t SCL = PIN_WIRE_SCL;
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
#define HAS_ICM20948
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Charger (SGM41562 on Wire1 @ 0x03)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
#define HAS_SGM41562
#define SGM41562_WIRE Wire1
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Compatibility Definitions
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━