[max17048] Add MAX17048 Li+ Cell Fuel Gauge IC (#13585)

Co-authored-by: Rodrigo Martín <contact@rodrigomartin.dev>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
B48D81EFCC
2026-10-12 12:53:16 -10:00
committed by GitHub
co-authored by Rodrigo Martín J. Nick Koston
parent c5baae35d5
commit 88668f448e
9 changed files with 183 additions and 0 deletions
+1
View File
@@ -343,6 +343,7 @@ esphome/components/m5stack_8angle/* @rnauber
esphome/components/mapping/* @clydebarrow
esphome/components/matrix_keypad/* @ssieb
esphome/components/max17043/* @blacknell
esphome/components/max17048/* @B48D81EFCC
esphome/components/max31865/* @DAVe3283
esphome/components/max31888/* @JiriPrchal
esphome/components/max44009/* @berfenger
+2
View File
@@ -0,0 +1,2 @@
CODEOWNERS = ["@B48D81EFCC"]
DOMAIN = "max17048"
+77
View File
@@ -0,0 +1,77 @@
#include "max17048.h"
#include "esphome/core/log.h"
namespace esphome::max17048 {
// MAX17048/MAX17049 1 and 2 cell fuel gauges with ModelGauge
// Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX17048-MAX17049.pdf
ESPHOME_LOG_TAG(TAG, "max17048");
static constexpr uint8_t REG_VCELL = 0x02; // cell voltage
static constexpr uint8_t REG_SOC = 0x04; // state of charge
static constexpr uint8_t REG_VERSION = 0x08; // 0x001x
static constexpr uint8_t REG_CRATE = 0x16; // charge rate, signed
static constexpr uint16_t VERSION_MASK = 0xFFF0;
static constexpr uint16_t VERSION_FAMILY = 0x0010;
static constexpr float VCELL_VOLTS_PER_LSB = 78.125e-6f;
static constexpr float SOC_PERCENT_PER_LSB = 1.0f / 256.0f;
static constexpr float CRATE_PERCENT_PER_HOUR_PER_LSB = 0.208f;
void MAX17048Component::setup() {
uint16_t version;
if (!this->read_byte_16(REG_VERSION, &version)) {
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
return;
}
if ((version & VERSION_MASK) != VERSION_FAMILY) {
ESP_LOGW(TAG, "Unexpected version 0x%04X", version);
}
}
void MAX17048Component::update() {
bool ok = true;
uint16_t raw;
if (this->battery_voltage_sensor_ != nullptr) {
if (this->read_byte_16(REG_VCELL, &raw)) {
this->battery_voltage_sensor_->publish_state(raw * VCELL_VOLTS_PER_LSB);
} else {
ok = false;
}
}
if (this->battery_level_sensor_ != nullptr) {
if (this->read_byte_16(REG_SOC, &raw)) {
this->battery_level_sensor_->publish_state(raw * SOC_PERCENT_PER_LSB);
} else {
ok = false;
}
}
if (this->rate_sensor_ != nullptr) {
if (this->read_byte_16(REG_CRATE, &raw)) {
this->rate_sensor_->publish_state(static_cast<int16_t>(raw) * CRATE_PERCENT_PER_HOUR_PER_LSB);
} else {
ok = false;
}
}
if (ok) {
this->status_clear_warning();
} else {
this->status_set_warning(LOG_STR("Register read failed"));
}
}
void MAX17048Component::dump_config() {
ESP_LOGCONFIG(TAG, "MAX17048:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Battery Voltage", this->battery_voltage_sensor_);
LOG_SENSOR(" ", "Battery Level", this->battery_level_sensor_);
LOG_SENSOR(" ", "Rate", this->rate_sensor_);
}
} // namespace esphome::max17048
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome::max17048 {
class MAX17048Component final : public PollingComponent, public i2c::I2CDevice {
SUB_SENSOR(battery_voltage)
SUB_SENSOR(battery_level)
SUB_SENSOR(rate)
public:
void setup() override;
void dump_config() override;
void update() override;
};
} // namespace esphome::max17048
+66
View File
@@ -0,0 +1,66 @@
import esphome.codegen as cg
from esphome.components import i2c, sensor
import esphome.config_validation as cv
from esphome.const import (
CONF_BATTERY_LEVEL,
CONF_BATTERY_VOLTAGE,
CONF_ID,
CONF_RATE,
DEVICE_CLASS_BATTERY,
DEVICE_CLASS_VOLTAGE,
ENTITY_CATEGORY_DIAGNOSTIC,
STATE_CLASS_MEASUREMENT,
UNIT_PERCENT,
UNIT_VOLT,
)
from esphome.types import ConfigType
DEPENDENCIES = ["i2c"]
UNIT_PERCENT_PER_HOUR = "%/h"
max17048_ns = cg.esphome_ns.namespace("max17048")
MAX17048Component = max17048_ns.class_(
"MAX17048Component", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(MAX17048Component),
cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=3,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_BATTERY,
state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
cv.Optional(CONF_RATE): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT_PER_HOUR,
accuracy_decimals=1,
state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x36))
)
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
sensors = sensor.sub_sensors(config)
await sensors(CONF_BATTERY_VOLTAGE, var.set_battery_voltage_sensor)
await sensors(CONF_BATTERY_LEVEL, var.set_battery_level_sensor)
await sensors(CONF_RATE, var.set_rate_sensor)
+8
View File
@@ -0,0 +1,8 @@
sensor:
- platform: max17048
battery_voltage:
name: Battery Voltage
battery_level:
name: Battery Level
rate:
name: Battery Rate
@@ -0,0 +1,3 @@
packages:
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
max17048: !include common.yaml
@@ -0,0 +1,3 @@
packages:
i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml
max17048: !include common.yaml
@@ -0,0 +1,3 @@
packages:
i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml
max17048: !include common.yaml