LiFePo4 State of Charge (SoC)

Toelichting bovenstaande afbeelding:

De dashboard meters laten de momentane spanning van de verschillende batterij(sets) zien.
Daaronder staan horizontale barometers die de State of Charge aangeven, respectievelijk van links naar rechts, eerste rij: Save-E noodstroom systeem, Solar Storage (“SolStor”) accu, een extra batterij om vooral in de zomer een extra buffer te hebben, en de status van deze batterijen. Op de tweede rij de HomeWizard Thuisbatterij (HW), met actueel vermogen en SoC, en vervolgens twee EcoFlow River 3 Plus systemen, die als UPS voor mijn PC en mijn servers dienen (en tevens in extreme situaties een extra belasting voor de zonnepanelen kunnen vormen).

Het is altijd fijn om te weten hoeveel energie er nog in je batterij zit.
Enerzijds wil je zien of je nog een tijdje vooruit kunt, en vaak is het ook goed om te weten hoelang je nog kunt (of moet) laden om de batterij vol te krijgen. Commerciële batterijen, zoals die van HomeWizard en EcoFlow laten dat altijd netjes zien in de bijbehorende App, of op het apparaat scherm (bij HomeWizard is dat een apart display met nog meer informatie over je totale energiesysteem). Voor de doe-het-zelver zijn er sets beschikbaar die tussen batterij en lader/verbruikers worden geschakeld. Bekende merken zoals Victron leveren geavanceerde modules met eventuaal Apps en draadloze communicatie, maar die zijn nogal aan de prijs. Bovendien werken die vaak met een heel kleine weerstand in de negatieve stroomleiding, hetgeen een kleine spanningsval veroorzaakt en dito vermogensverlies. Bij Save-E heb ik gekozen voor een Eco-Worthy Battery Monitor. Die meet contactloos met een ringkern om de leiding de stroom van en naar de batterij, en berekent daaruit de State of Charge, kortweg SoC.

De commerciële oplossingen hebben waarschijnlijk nogal complexe algoritmen aan boord. Toch zijn ze niet feilloos. Mij is opgevallen dat men zo nu en dan verlangt dat een batterij helemaal leeg (of juist vol) is om te kunnen kalibreren. Dat betekent waarschijnlijk dat de meetwaarden in de tijd verlopen, en zo nu en dan een reset nodig hebben. Verder verdenk ik de complete systemen ervan al 0% aan te geven als de batterij nog niet echt leeg is, simpelweg om de levensduur van de batterij te verlengen (helemaal ontladen is nooit goed, en het BMS –battery management system– heeft waarschijnlijk al ingegrepen voordat dit punt wordt bereikt). De aangegeven waarden wekken bovendien de indruk van hogere nauwkeurigheid als wat fysisch en technisch mogelijk is. Ondertussen heb ik geleerd om SoC alleen als een ruwe indicatie te interpreteren: ben ik bijna vol of leeg, of hang ik ergens ertussenin.

Een veel gebruikte methode is om de batterijspanning als indicatie te nemen. Daarvoor bestaan verschillende tabellen met onderling minuscule variaties (lees: 0,1V). Nadeel van deze methode is dat deze spanning afhankelijk is van de momentane gebruikstoestand (stand-by, laden of ontladen). Eigenlijk kun je die spanning alleen gebruiken als indicatie voor de SoC als de batterij een half uurtje in rust is geweest (wat prima werkt voor het bewaken van batterijen in opslag). Helaas is dat voor de hobbyist de enige snelle meting die gedaan kan worden.
Nu heb ik ook Home Assistant. Daarmee kunnen best wel complexe berekeningen worden gemaakt, en dus leek het me een leuke uitdaging om zelf een SoC-barometer te maken voor mijn Solar Storage (“SolStor”) batterij.

Via spanningsmeting

Ik gebruik deze methode om een indicatie te hebben van de laadtoestand van een LiFePo4-batterij. Het begint met een tabelletje van waarden die je overal op internet kunt vinden:

Vbatterij (Volt)SoC (%)
14,6100
13,6100
13,490
13,380
13,270
13,160
1350
12,9540
12,930
12,820
1210
100

Deze heb ik in Excel ingevoegd, en vervolgens grafiekjes laten maken. Deze grafiekjes kunnen ook de bijbehorende formule tonen. Omdat er geen continu verloop van de curve is, heb ik de grafiek gesplitst in verschillende segmenten, met name onder de 30% (waar de afname sneller verloopt dan daarboven).

Dat moet vervolgens in Home Assistant verwerkt worden. Voor mijn extra batterij “SolStor” wordt de waarde van de klemspanning weergegeven met “sensor.voltage“. Eerst moeten we per segment de juiste SoC berekenen (interpoleren). Daartoe maak ik eenvoudige helpers aan, template sensors met de volgende inhoud (Meeteenheid: %, Apparaatklasse: Batterij, Statusklasse: Meting), precies zoals de Excel-formules aangeven:

SoC_10:

{{ 5 * states('sensor.voltage') | float(0) - 50}}

SoC_20:

{{ 12.5 * states('sensor.voltage') | float(0) - 140}}

SoC_30:

{{ 100 * states('sensor.voltage') | float(0) - 1260}}

SoC_100:

{{ -85.244 * (states('sensor.voltage') | float(0) **2) + 2358.6 * states('sensor.voltage') | float(0) - 16192 }}

Vervolgens nog een template sensor op basis van deze segmenten, die de juiste formule inschakelt bij verandering van de spanning:

SolStor-SoC:

{% if (states('sensor.voltage') | float(0) > 13.6) %} 100
  {% elif (states('sensor.voltage') | float(0) > 12.9) and (states('sensor.voltage') | float(0) < 13.6) %} {{states('sensor.soc_100') | round(0)}}
    {% elif (states('sensor.voltage') | float(0) > 12.8) and (states('sensor.voltage') | float(0) < 12.9) %} {{states('sensor.soc_30') | round(0)}}
      {% elif (states('sensor.voltage') | float(0) > 12) and (states('sensor.voltage') | float(0) < 12.8) %} {{states('sensor.soc_20') | round(0)}}
        {% elif (states('sensor.voltage') | float(0) > 10) and (states('sensor.voltage') | float(0) < 12) %} {{states('sensor.soc_10') | round(0)}}
  {% else %} 0
{% endif %}

Het ziet er wellicht wat ingewikkeld uit, maar het is niet meer dan een uitgebreid “IF-THEN-ELSE” statement, die checkt in welk segment de spanning zit, en de overeenkomstige SoC-waarde presenteert. Wat extra is zijn de uiterste waarden: onder 10 Volt en boven 13,6 Volt, resp. SoC=0% en SoC=100%. Het is natuurlijk ook mogelijk om de SoC-berekeningen in deze formule in te voeren, maar dan wordt het onleesbaar, en (nog belangrijker) lastig te modificeren. Nu kun je de grenswaarden of een losse formule snel aanpassen zonder grote kans op tikfouten en dergelijke. Deze oplossing werkt voor monitoring van passieve accu’s prima, maar bedenk wel dat bij een actieve batterij de waarden niet erg nauwkeurig zijn. Door SolStor-SoC als “Apparaatklasse: Batterij” te definiëren, is het eenvoudig om op je dashboard een horizontale barometer op te nemen die de laadtoestand weergeeft in procenten en in kleuren (rood, geel, groen, afhankelijk van de laadtoestand).

Via berekening lading

Omdat SolStor altijd in bedrijf is, kies ik een methode waarbij de hoeveelheid lading wordt ingeschat die in en uit de batterij gaat. SolStor betreft een batterij van 140Ah, en LiFePo4 accu’s staan erom bekend dat je redelijk nauwkeurig (ont-)laad stromen kunt berekenen aan de hand van de tijd. In dit geval: een lege batterij 140 uur laden met 1A zorgt voor een volle batterij (en bij ontladen dus een lege batterij). Gebruik je 10A, dan is de tijd evenredig korter: 14 uur. Bij 20A dus 7 uur en bij 100A (het maximum voor dit exemplaar) zou het 1,4 uur zijn. Nou wijst de praktijk uit dat dit niet helemaal klopt. Er zijn altijd wel ergens (stroomsterkte-afhankelijke) verliezen, dus je moet een marge van 10% extra nemen. Ook dat is maar een vuistregel; het is deze onnauwkeurigheid die ervoor zorgt dat deze meetmethode regelmatig gekalibreerd moet worden omdat anders de schaal langzaam maar zeker verloopt. Dat is mogelijk met softwarematige resets van de SoC, maar op den duur ontstonden toch vreemde gedragingen die handmatig moesten worden opgelost door de sensorstatistieken binnen Home Assistant bij te werken. Desondanks is het een leuke exercitie die ik hier voor geïnteresseerden vermeld.

Het begint met het systeem:

Het bestaat, behalve uit de accu, uit twee laders van resp. 20A en 40A, en een omvormer die 250 Watt naar het net kan voeden (ruimschoots voldoende voor mijn stand-by verbruik gedurende de nacht). De laders zijn specifiek bedoeld voor LiFePo4 batterijen (met een eenvoudiger laadkarakteristiek dan voor een lood accu), en hebben een maximum uitgangsspanning van 14,6 Volt. Omdat de accu door de super-lage inwendige impedantie de werkspanning van de laders bepaalt, kunnen deze gerust parallel worden verbonden: de interne regeling zorgt dan voor de gespecificeerde laadstroom. Een losse spanningsmeter, gemaakt met een veelzijdige Shelly Plus Uni module, communiceert via WiFi de klemspanning naar Home Assistant. Deze module wordt ook gevoed vanuit de te meten batterij, wat niet de beste oplossing is (extra stroomverbruik en wegvallen sensor als de BMS van de batterij uitschakelt). Optimaal zou zijn om de unit apart te voeden: de voltmeter aansluiting vraagt nauwelijks stroom. Gezien de grote stromen is het belangrijk de juiste draaddiameters te gebruiken (de vaste aansluitingen op de laders zijn goed), en alles zo dicht mogelijk bij de accuklemmen te bevestigen; ik gebruik daarvoor 2 stevige busbars die via korte, dikke kabels met de accupolen verbonden zijn.
Wat niet in de tekening is aangegeven zijn de Zigbee-schakelaars die laders en omvormer op het net aansluiten. Deze modules hebben ook een vermogensmeting aan boord. Vanuit Home Assistant kan ik dus elke lader of omvormer aan- of uitschakelen, en tevens het opgenomen of afgegeven vermogen meten. De automatiseringen voor het schakelen laat ik hier achterwege: ik beschrijf alleen de meetmethode om een SoC te bepalen.

Om de stroomsterkte te meten zou je direct met een stroomtang of meet-shunt aan de gang kunnen gaan. Alleen wordt het systeem daardoor wel erg duur, en SolStor was bedoeld als een goedkope overloopbuffer in plaats van een extra commerciële thuisbatterij. De enige meetgegevens die mij ter beschikking staan zijn dus de vermogens naar de laders en van de omvormer, en de klemspanning van de accu. Zowel laders als omvormer hebben een bepaald rendement: niet al het vermogen dat je erin stopt, komt er ook uit. Om die rendementen te bepalen hebben we een losse stroomtang-multimeter nodig (voor de hobbyist sowieso een nuttige aanschaf; als je een nieuwe multimeter overweegt, kies er dan een met een stroomtang voor gelijkstromen, zodat je geen draden hoeft los te maken om de stroom te meten). Schakel een voor een de apparaten in, en kijk welke stroom er gaat lopen en welke spanning op dat moment op de klemmen staat. Lees tevens tegelijkertijd het AC-vermogen af bij de schakelaars van elk apparaat (dus niet èèn gezamenlijke stekker die alles tegelijk meet). Ik heb dat als proef bij verschillende laadtoestanden van de accu gedaan, en eigenlijk vallen alle afwijkingen binnen de meetnauwkeurigheid. Dat geeft tevens een indicatie waarom na verloop van tijd (6 tot 12 maanden) toch verschuivingen ontstaan…
Mijn metingen heb ik in onderstaande tabel verzameld:

ApparaatPacVdcIdcPdcη
Lader 20A31013,3420,126886,49%
Lader 40A61913,4840,855088,58%
Omvormer 250W-19713,0917,8-233118,28%

Het rendement “η” laat zien dat je minder gelijkstroomvermogen uit de lader haalt als dat je er via de netkabel in stopt, en dat je bijna 20% vermogen kwijtraakt in de omvormer. Dat laatste vergt een gedachtesprong: er loopt mèèr stroom uit de batterij als dat je op grond van het geleverde netvermogen zou verwachten!

De gelijkstroom in of uit de batterij is dan:
Idc = η * Pac / sensor.voltage

In Home Assistant werk ik weer stapsgewijs (niet alles in èèn formule proberen te dwingen). Ik maak voor elk van de apparaten een template sensor voor stroom (Meeteenheid: A; Apparaatklasse: Stroom; Statusklasse: Meting) als helper aan:

I-20: (let niet op de lange sensor definitie; dat is toevallig in mijn systeem zo)

{{ 0.8649 * states('sensor.solstor_laders_electric_consumption_w') | float(0) / states('sensor.voltage') | float(1) }}

I-40:

{{ 0.8858 * states('sensor.solstor_laders_electric_consumption_w_2') | float(0) / states('sensor.voltage') |float(1)}}

I-omv (sensor levert negatief vermogen):

{{ 1.1828 * states('sensor.zb_i15_vermogen') | float(0) / states('sensor.voltage')|float(1)}}

De “float”-waarde is wat de formule gebruikt als de sensor om welke reden dan ook niet beschikbaar is. Daarom is bij sensor.voltage gebruikt “float(1)”, zodat nooit door 0 (nul) gedeeld wordt.

De netto stroom voor de accu is dan I-SolStor (let op dat de laadstromen optellen en de ontlaadstroom daarvan af gaat):

{{states('sensor.i_20') | float(0) + states('sensor.i_40') | float(0) + states('sensor.i_omv') | float(0)}}

Om de lading (Ah) te weten, moet de netto stroom worden geïntegreerd. Daartoe maken we de volgende helper aan, genaamd Coulomb. Kies daarvoor een Integraal met de ‘Linker Riemann som’ en als tijdseenheid ‘Uren’. De precisie kan op 0 worden gezet (het gaat niet om decimalen achter de komma).

Om de lading te tellen, maken we ten slotte een Nutsmeter (SS-SoC) aan met ‘Coulomb’ als inputsensor, dagelijkse reset, netto verbruik ingeschakeld. Dit kan weer via een Helper gedaan worden, maar dan is achteraf wijzigen lastig (meestal moet je de oude helper weggooien en opnieuw een nutsmeter aanmaken met dezelfde naam, maar andere settings). Bovendien wil ik mijn nutsmeter elke dag resetten om 06:00 uur ’s ochtends, als ik zeker weet dat de batterij leeg is. Hierdoor wordt verloop na op- en ontladen gecorrigeerd. In mijn geval weet ik zeker dat de batterij elke ochtend helemaal leeg is; als in jouw oplossing dit anders ligt moet je òf andere cycli en/of tijden kiezen, òf een automatiseren maken die kijkt naar bepaalde waarden en besluit of een reset nodig is, òf handmatig resetten wanneer je de tijd rijp acht. Ik maak de nutsmeter in YAML, in de configuration.yaml file (oppassen dat je hier verder niets verstoort, anders kan Home Assistant in de problemen komen).

# SS-SoC Utility Meter SolStor
utility_meter:
  ss_soc:
    name: "SS-SoC"
    unique_id: ss_soc
    source: sensor.coulomb
    cycle: daily
    offset:
      hours: 06
    net_consumption: true
    periodically_resetting: true
    always_available: true

State of Charge in procenten volgt dan uit (waarbij 1,4 overeenkomt met de opgegeven capaciteit van de accu (100%/140Ah):

{{states('sensor.ss_soc') | float(0) / 1.4}}

Dit is uiteraard een helper (meeteenheid: %, Apparaatklasse: Batterij, Statusklasse: Meting). Deze kan weer eenvoudig worden weergegeven in het Home Assistant dashboard.