Battery charge calculation

Hello together,

sorry in advance for my non-existent knowledge of English. Everything runs through the translator. I have built a battery monitor with ESP8266 and INA226. So far everything works great. I would like to get the ESP to calculate the Ah and the percentages. Unfortunately I am not so fit in ESPHome to be able to do that. Actually, the INA226 provides everything necessary for the calculation. No plan how I can implement this. I have found no examples on the net. I have really long searched.

Personally I did not use this yet, but the Total Daily Energy Sensor might do what you want.

Thank you for the answer. Should it really be that simple? I had already thought about a complicated lambda calculation :rofl:
The total daily energy sensor returns me a Wh or kWh value if I understand correctly? I would then only have to convert correct?

I see two potential issues with that solution:

  1. it will reset daily, definitely an issue for tracking battery capacity

  2. I don’t know if it can handle negative power (discharging), it probably can.

This would be better done in home assistant.

You can use the Riemann Sum helper to track Ah directly by feeding it your current sensor. Be sure to use method: left.

If you need to you can then import this back into ESPHome.

In that case, would the Integration Sensor (being the more generic version of the Total Daily Energy Sensor) be a better choice?

Ohh. Yes it would. I looked for that earlier today and only found the daily one.

Much better idea. Just feed it the current sensor with a time unit of an hour to get Ah.

Super thanks for the suggestions. I will sit down and try it out. I had not thought of these solutions at all.

I too have been looking at solutions for this, here is what i currently use to track my 12v SLA battery thats charged by solar

  - platform: integration
    name: ${disp_name} Coulomb Counter
    id: coulomb_counter
    sensor: coulomb_count_sensor
    accuracy_decimals: 0
    time_unit: s
    entity_category: diagnostic
    restore: true
    #min_save_interval: 300s
    filters:
      - throttle: 60s

  - platform: template
    name: ${disp_name} Battery Level
    lambda: return(id(coulomb_counter).state);
    update_interval: 60s
    unit_of_measurement: '%'
    accuracy_decimals: 1
    device_class: battery
    state_class: measurement
    filters:
      - calibrate_linear:
          - -32400 -> 0
          - 0 -> 100

the issue is the charge isn’t 100% efficient and i should really have a fudge factor so that current into the battery is reduced by the charging efficiency of about 80%.

as i couldn’t figure this out i made a crude hack with an automation that resets the integration when i determine the battery is fully charged

binary_sensor:
  - platform: template
    name: ${disp_name} Fully Charged
    lambda: |-
      if ((id(charge_current).state <= 0.120) 
        and (id(buck_pwm).state <= 65) 
        and (id(coulomb_counter).state >= 0) 
        and (abs((id(batt_voltage).state) - (id(chg_tgt_voltage).state)) <= 0.05 ) 
        and (id(status).state == 6) 
        and (id(uptime_sensor).state >= 600)) {
        return true;
      } else {
        return false;
      }

this part will be very specific to my charger, basically it looks to see if the battery has reduced it current consumption, and the solar is still actually producing enough to provide that minimum consumption, the charger had reached its float charge state, the voltage is right for the current temperature and the charger has been running for at least 10min.
edit:
the automation in HA to do the reset

alias: Battery_charged
description: ""
trigger:
  - platform: state
    entity_id:
      - binary_sensor.makerpower_mppt_fully_charged
    to: "on"
    for:
      hours: 0
      minutes: 0
      seconds: 10
condition: []
action:
  - service: esphome.makerpower_mppt_makerpower_mppt_reset_coulomb
    data: {}
mode: single

This should help get you started with the AH and % you are looking for

Thanks for the addition, although it doesn’t look quite so simple :wink:
I’ll have to sit down to figure that one out.

if you are using the ina226 you should have something like

sensor:
  - platform: ina226
    address: 0x40
    shunt_resistance: 0.1 ohm
    current:
      name: "INA226 Current"
    power:
      name: "INA226 Power"
    bus_voltage:
      name: "INA226 Bus Voltage"
    shunt_voltage:
      name: "INA226 Shunt Voltage"
    max_current: 3.2A
    update_interval: 60s

now a coulomb is amps x seconds, so i update the current every second for the integrator… but make it internal so it doesnt spam your DB.
and if you want voltages but dont want to spam your db then set filters, this just throttles back to once every 60sec.

i2c:
  sda: D2
  scl: D1
  scan: true
  id: bus_a

sensor:
  - platform: ina226
    address: 0x40
    shunt_resistance: 0.1 ohm
    current:
      name: "INA226 Current"
      id: coulomb_counter
    power:
      name: "INA226 Power"
      filters:
      - throttle: 60s
    bus_voltage:
      name: "INA226 Bus Voltage"
      filters:
      - throttle: 60s
    shunt_voltage:
      name: "INA226 Shunt Voltage"
      filters:
      - throttle: 60s
    max_current: 3.2A
    update_interval: 1s

  - platform: integration
    name: Coulomb Counter
    id: coulomb_counter
    sensor: coulomb_count_sensor
    accuracy_decimals: 0
    time_unit: s
    entity_category: diagnostic
    restore: true
    #min_save_interval: 300s
    filters:
      - throttle: 60s

  - platform: template
    name: Battery Level
    lambda: return(id(coulomb_counter).state);
    update_interval: 60s
    unit_of_measurement: '%'
    accuracy_decimals: 1
    device_class: battery
    state_class: measurement
    filters:
      - calibrate_linear:
          - -32400 -> 0
          - 0 -> 100

if you still want current after its been set to internal, create a template sensor and have it publish at your desired rate

  - platform: ina226
    address: 0x40
    shunt_resistance: 0.00148 ohm
    current:
      name: "INA226 Current"
      id: current
      accuracy_decimals: 2
    power:
      name: "INA226 Power"
      id: power
    bus_voltage:
      name: "INA226 Bus Voltage"
      id: voltage  
    shunt_voltage:
      name: "INA226 Shunt Voltage"
    max_current: 50A
    update_interval: 2s

That’s how it looks to me. I have installed an external shunt. So this is about right. Thanks for the detailed explanation. I have to deal with it to implement it.

I’m a relative newbie to battery monitoring so more asking this out of interest.

Can’t you just do this all from the voltage (only) and some knowledge of the battery capacity? I recall discharge profiles are non-linear (so Ah would be approximated?)

Or is the idea with the suggested solutions to do it more accurately?

For my 18650 battery monitoring I just measure voltage and calibrate it to a %battery.

Can see id: batt_level here for details.

I always thought that the voltage curve is too inaccurate to derive the capacity. At least for LiFePo4 batteries, which is what this topic is about.

1 Like

The voltage curve is load dependent, it is also very flat for lithium batteries, so no it is not a good choice. Integrating the current in and out gives a much better indication of the state of the battery.

1 Like

I thought so. Thanks for the info :+1: