Ik heb een rc project waarbij ik een 2S (7.4v) lipo accu (of heel misschien 2 parallel) gebruik. Nu wil ik het resterende accu percentage weten maar dit blijkt voor lipo niet handig te zijn om dat puur op voltage te doen. De MAX17044 zou geschikt zijn voor 2S lipo’s maar die is nergens te krijgen. De MAX17043 is voor 1 cel maar wel te krijgen.
Nu heb ik een BMS bordje waarbij ik zowel de GND, 4.2v (aftakking tussen 2 cellen) en 8.4v aansluit. Kan ik daar ook zo’n fuel gauge mee aansluiten? Dus 1 van de 2 cellen meten (of wellicht 2x MAX17043 waarbij ik elke cel los meet en in code het gemiddelde pak?
Of denk ik nu te simpel?
Tussen GND en 4.2v heb ik 1 cell en tussen 4.2v en 8.4v heb ik 1 cel (https://a.aliexpress.com/_EvQCv7a)
Als er betere oplossingen zijn hoor ik dat heel graag
Ohja, en dan nog een vraag, ik heb 2 type accu’s, een accu waarbij ik zo aan de polen van de cel kan, een andere rc accu waar ik plus en min heb en de losse balanceer stekker. De balanceer stekker heeft de GND, 4.2v en 8.4v. Kan ik het daar vanaf halen?
[Bericht gewijzigd door bbuster op (14%)]
paulp
I thought, I thought of everything
Zoek eens op coulomb counter https://www.tinytronics.nl/nl/sensoren/stroom-spanning/seeed-studio-gr…deze bijvoorbeeld.
Succes Paul
Roland van Leusden
It's the rule that you live by and die for It's the one thing you can't deny Even though you don't know what the price is. It is justified.
Je hebt iets nodig als de LTC2944 die meerdere cellen aan kan.
The LTC2944 measures battery charge state, battery voltage, battery current and its own temperature in portable product applications. The wide input voltage range allows use with multicell batteries up to 60V. A precision coulomb counter integrates current through a sensor resistor between the battery’s positive terminal and the load or charger.
https://nl.aliexpress.com/item/1005008849291900.html
Er is een Arduino bibliotheek voor de LTC2944.
Op vrijdag 9 januari 2026 09:09:48 schreef paulp:
Zoek eens op coulomb counter https://www.tinytronics.nl/nl/sensoren/stroom-spanning/seeed-studio-gr…deze bijvoorbeeld.
Succes Paul
Is deze niet voor max 5v?
Hier zie ik bv <8.5v op staan. Is dit dan wat?
https://a.aliexpress.com/_EvXOV1m
Op vrijdag 9 januari 2026 09:48:52 schreef Roland van Leusden:
Je hebt iets nodig als de LTC2944 die meerdere cellen aan kan.[...]
https://nl.aliexpress.com/item/1005008849291900.html
Er is een Arduino bibliotheek voor de LTC2944.
Meet dit niet alleen voltage? Ik las dat dat onnauwkeurig was omdat een lipo in het begin direct voltage verliest en dan lange tijd constant blijft. En dan in een keer weg valt
Roland van Leusden
It's the rule that you live by and die for It's the one thing you can't deny Even though you don't know what the price is. It is justified.
Daarvoor is de Coulomb counter in de LTC2944.
A precision coulomb counter integrates current through a sensor resistor between the battery’s positive terminal and the load or charger.
Uitleg over de werking van een Coulomb counter:
https://mjootech.com/nl/bm-coulomb-counter-for-accurate-battery-monito…
Okay, dat lijkt precies wat ik nodig heb. Ik zie geen aansluitschema’s maar als ik het goed heb zet ik dit in serie met de lipo? Dus al het gevraagde vermogen gaat door deze module?
Roland van Leusden
It's the rule that you live by and die for It's the one thing you can't deny Even though you don't know what the price is. It is justified.
TIP: Zoek voor de modules op Aliexpress altijd het datasheet van de fabrikant erbij. De omschrijvingen op Ali kloppen vaak van geen kanten.
Op de door jouw gelinkte module zit de LTC4150, het datasheet vindt je hier.
Uit dat datasheet:
The LTC4150 is intended for 1-cell or 2-cell Li-Ion and 3-cell to 6-cell NiCd or NiMH applications.
En als je nog niet zo handig ben met programeren en lezen van datasheets is het ook verstandig om te kijken of er al een bibliotheek voor is.
https://learn.sparkfun.com/tutorials/ltc4150-coulomb-counter-hookup-gu…
Roland van Leusden
It's the rule that you live by and die for It's the one thing you can't deny Even though you don't know what the price is. It is justified.
Dus al het gevraagde vermogen gaat door deze module?
Zowel het geleverde vermogen als de stroom van de lader moeten door de module. Je wil immers weten hoeveel mAh er in de cellen gegaan is en eruit om te kunnen bepalen hoeveel mAh er nog in cellen zit.
Dank, ik ga eens proberen of ik de LTC2944 werkend krijg icm de bms en usb lader. Nog even uitzoeken hoe ik het aansluit maar dat moet ik uitzoeken als ik de module heb
Ik heb de LTC2944 module ontvangen en tussen de BMS en lader gezet. Alle stroom uit de batterij loopt dus via de module. Ik kan hem uitlezen maar de SOC blijft altijd op 100% staan. Ik heb nu een waveshare 5" esp32-s3 module erop aangesloten die ongeveer 280mA verbruikt. Dit is de library die ik gebruik: https://github.com/PonyPC/LTC2944-Arduino-Library
Dit is mijn code / resultaat:
#include <LTC2944.h>
const unsigned int fullCapacity = 6000; // Maximum value is 22000 mAh
LTC2944 gauge(50); // Takes R_SENSE value (in milliohms) as constructor argument, can be omitted if using CJMCU-294
void setup() {
Serial.begin(115200);
Serial.println("LTC2944 Battery Readings Example");
Serial.println();
Wire.begin();
while (gauge.begin() == false) {
Serial.println("Failed to detect LTC2944!");
delay(5000);
}
gauge.setBatteryCapacity(fullCapacity);
// gauge.setBatteryToFull(); // Sets accumulated charge registers to the maximum value
gauge.setADCMode(ADC_MODE_SLEEP); // In sleep mode, voltage and temperature measurements will only take place when requested
gauge.startMeasurement();
}
void loop() {
unsigned int raw = gauge.getRawAccumulatedCharge();
Serial.print(F("Raw Accumulated Charge: "));
Serial.println(raw, DEC);
float capacity = gauge.getRemainingCapacity();
Serial.print(F("Battery Capacity: "));
Serial.print(capacity, 3);
Serial.print(F(" / "));
Serial.print(fullCapacity, DEC);
Serial.println(F(" mAh"));
float voltage = gauge.getVoltage();
Serial.print(F("Voltage: "));
Serial.print(voltage, 3);
Serial.println(F(" V"));
float temperature = gauge.getTemperature();
Serial.print(F("Temperature: "));
Serial.print(temperature, 2);
Serial.println(F(" 'C"));
float current = gauge.getCurrent();
Serial.print(F("Current: "));
Serial.print(current, 6);
Serial.println(F("A"));
Serial.println();
delay(5000);
}Resulaat:
Raw Accumulated Charge: 65535
Battery Capacity: 6000.000 / 6000 mAh
Voltage: 8.025 V
Temperature: 21.45 'C
Current: -0.289970ARaw Accumulated Charge: 65535
Battery Capacity: 6000.000 / 6000 mAh
Voltage: 8.024 V
Temperature: 21.45 'C
Current: -0.283720ARaw Accumulated Charge: 65535
Battery Capacity: 6000.000 / 6000 mAh
Voltage: 8.024 V
Temperature: 21.45 'C
Current: -0.269344A
De "Raw Accumulated Charge" blijft na 2 uur nog steeds op 65535. Zo ook de battery capacity. Ik wil een SOC berekenen maar met elke berekening blijf ik dus ook op 100% uitkomen. Wat doe ik fout?
Oh, als ik de accu laat opladen krijg ik bij current 0.5xxA dus dan gaat hij in de plus met 500 of 600mA laden. De aansluiting lijkt me dan correct
Alvast bedankt!
Roland van Leusden
It's the rule that you live by and die for It's the one thing you can't deny Even though you don't know what the price is. It is justified.
Mijn LTC2944 printen zijn nog onderweg, kan nog niets testen dus.
Ik heb (met chatgpt) een aantal aanpassingen gedaan en een andere esp32 gepakt aangezien ik al vaker gemerkt heb dat sommige dingen niet correct werken met de vorige esp32. Ik krijg nu wel waardes maar vraag me af of ze kloppen. Ik hebben waveshare 5" esp32-s3 scherm nu sinds 9 uur vanmorgen aangesloten op de 6000mAh lipo en zie nu deze waardes:
SOC: 95.74% | 5745mAh | V:7.793 I:-294mA | T:19.5 | EMPTY in 19:34:28 | Rint:1.0 mΩ | Health:100%
SOC: 95.74% | 5745mAh | V:7.793 I:-294mA | T:19.5 | EMPTY in 19:33:55 | Rint:1.0 mΩ | Health:100%
SOC: 95.74% | 5745mAh | V:7.793 I:-296mA | T:19.5 | EMPTY in 19:31:37 | Rint:1.0 mΩ | Health:100%
SOC: 95.74% | 5745mAh | V:7.794 I:-296mA | T:19.5 | EMPTY in 19:30:06 | Rint:1.0 mΩ | Health:100%
SOC: 95.74% | 5745mAh | V:7.794 I:-295mA | T:19.5 | EMPTY in 19:29:28 | Rint:1.0 mΩ | Health:100%
SOC: 95.74% | 5745mAh | V:7.793 I:-292mA | T:19.5 | EMPTY in 19:31:59 | Rint:1.0 mΩ | Health:100%
SOC: 95.74% | 5744mAh | V:7.793 I:-289mA | T:19.5 | EMPTY in 19:37:17 | Rint:1.0 mΩ | Health:100%
SOC: 95.74% | 5744mAh | V:7.793 I:-287mA | T:19.5 | EMPTY in 19:43:40 | Rint:1.0 mΩ | Health:100%
SOC: 95.74% | 5744mAh | V:7.793 I:-288mA | T:19.5 | EMPTY in 19:46:42 | Rint:1.0 mΩ | Health:100%
SOC: 95.74% | 5744mAh | V:7.793 I:-289mA | T:19.5 | EMPTY in 19:47:52 | Rint:1.0 mΩ | Health:100%
SOC: 95.74% | 5744mAh | V:7.793 I:-290mA | T:19.5 | EMPTY in 19:48:12 | Rint:1.0 mΩ | Health:100%
SOC: 95.74% | 5744mAh | V:7.793 I:-292mA | T:19.5 | EMPTY in 19:46:26 | Rint:1.0 mΩ | Health:100%
SOC: 95.74% | 5744mAh | V:7.793 I:-293mA | T:19.5 | EMPTY in 19:43:46 | Rint:1.0 mΩ | Health:100%
De SOC klopt niet denk ik. Al 3,5 uur 300mA verbruik zou 1050mA verbruikt moeten hebben, dus ik zou uit moeten komen op 4050mAh resterend en niet 5744mAh. Dus ook de 95% moeten volgens mij eerder rond de 82% moeten liggen. Ook de 7.79v (7.8v) moeten eerder bij de 80% liggen denk ik.
Denk dat mijn code dus iets gefinetuned moet worden
Roland van Leusden
It's the rule that you live by and die for It's the one thing you can't deny Even though you don't know what the price is. It is justified.
Ik kon het niet laten, heb er even in mijn lunch naar gekeken.
Je code ziet er goed uit, maar er mist één belangrijke stap.
setBatteryCapacity() doet alleen twee dingen:
- De library weet dan hoeveel mAh je accu heeft
- De LTC2944 krijgt de juiste “prescaler” instelling
Maar dit zet de teller niet op 100%.
De LTC2944 heeft intern een “Accumulated Charge” register (ACR).
Na opstart staat dat meestal rond 0x7FFF (middenstand).
De library rekent “hoeveel is er verbruikt” vanaf 0xFFFF = volle accu.
Omdat jij nooit zegt “de accu is nu vol”, blijft de library denken dat er bijna niets verbruikt is → SOC blijft 100%.
Probeer dit eens
Uncomment deze regel in je setup:
gauge.setBatteryCapacity(fullCapacity);
gauge.setBatteryToFull(); // <--- deze is belangrijk
Dus:
gauge.setBatteryCapacity(fullCapacity);
gauge.setBatteryToFull();
gauge.setADCMode(ADC_MODE_SLEEP);
gauge.startMeasurement();
Doe dit op een moment dat de accu echt vol is.
Controle stap
Je print al netjes:
unsigned int raw = gauge.getRawAccumulatedCharge();
Let daar eens op:
Als er ~280 mA loopt, moet die waarde langzaam omlaag lopen vanaf 65535.
Blijft hij exact gelijk → meet met een voltmeter in de mV stand de spanning over Rsense
Nog twee kleine checks
Klopt de shunt-waarde?
Jij gebruikt:
LTC2944 gauge(50);
Dat betekent: de library denkt dat je module een 50 mΩ shunt heeft.
Is jouw bordje misschien 10 mΩ of 1 mΩ?
Verkeerde waarde geeft foute mAh/SOC.
Laat hem een aantal minuten draaien met belasting
Coulomb telling is traag; je ziet geen seconde-effect.
Mijn voorbeeld was vanuit een ander stuk code zie ik nu, ik heb nu dit:
#include <EEPROM.h>
#include <LTC2944.h>
#include <math.h> // voor fabs, fmax, fmin
#define EEPROM_ADDR 0
// ===== BATTERIJ SETUP =====
float DESIGN_CAPACITY_MAH = 6000.0;
float learnedCapacity = DESIGN_CAPACITY_MAH;
const float FULL_VOLTAGE = 8.10;
const float EMPTY_VOLTAGE = 6.60;
const float SOC_WRITE_DELTA = 0.5;
const unsigned long SAVE_INTERVAL = 300000; // 5 min
LTC2944 gauge(50);
// ===== EEPROM STRUCT =====
struct GaugeState {
float soc;
uint16_t raw;
float learnedCap;
uint16_t crc;
};
GaugeState state;
uint16_t lastRaw = 0;
int32_t totalRaw = 0;
float lastSavedSOC = -1;
unsigned long lastSave = 0;
bool forceSave = false;
// ===== Kalman filter voor current & voltage =====
float kalmanCurrent = 0;
float kalmanErrorCurrent = 1;
float kalmanQCurrent = 0.02;
float kalmanRCurrent = 0.5;
float kalmanVoltage = 0;
float kalmanErrorVoltage = 1;
float kalmanQVoltage = 0.001;
float kalmanRVoltage = 0.002;
// ===== Kalman filter voor timers =====
float kalmanTimerEmpty = 0;
float kalmanErrorTimerEmpty = 1;
float kalmanTimerFull = 0;
float kalmanErrorTimerFull = 1;
float kalmanQTimer = 0.01; // process noise
float kalmanRTimer = 0.1; // measurement noise
float kalmanUpdateTimerEmpty(float measurement){
kalmanErrorTimerEmpty += kalmanQTimer;
float gain = kalmanErrorTimerEmpty / (kalmanErrorTimerEmpty + kalmanRTimer);
kalmanTimerEmpty += gain * (measurement - kalmanTimerEmpty);
kalmanErrorTimerEmpty *= (1 - gain);
return kalmanTimerEmpty;
}
float kalmanUpdateTimerFull(float measurement){
kalmanErrorTimerFull += kalmanQTimer;
float gain = kalmanErrorTimerFull / (kalmanErrorTimerFull + kalmanRTimer);
kalmanTimerFull += gain * (measurement - kalmanTimerFull);
kalmanErrorTimerFull *= (1 - gain);
return kalmanTimerFull;
}
// ===== Kalman updates voor current & voltage =====
float kalmanUpdateCurrent(float measurement) {
kalmanErrorCurrent += kalmanQCurrent;
float gain = kalmanErrorCurrent / (kalmanErrorCurrent + kalmanRCurrent);
kalmanCurrent += gain * (measurement - kalmanCurrent);
kalmanErrorCurrent *= (1 - gain);
return kalmanCurrent;
}
float kalmanUpdateVoltage(float measurement) {
kalmanErrorVoltage += kalmanQVoltage;
float gain = kalmanErrorVoltage / (kalmanErrorVoltage + kalmanRVoltage);
kalmanVoltage += gain * (measurement - kalmanVoltage);
kalmanErrorVoltage *= (1 - gain);
return kalmanVoltage;
}
// ===== CRC =====
uint16_t calcCRC(GaugeState s) {
return (uint16_t)(s.soc*10) ^ s.raw ^ (uint16_t)s.learnedCap ^ 0xBEEF;
}
bool loadState(GaugeState &out) {
EEPROM.get(EEPROM_ADDR,out);
if(out.crc != calcCRC(out)) return false;
if(out.soc<0||out.soc>100) return false;
if(out.learnedCap<1000||out.learnedCap>20000) return false;
return true;
}
void saveState(float soc,uint16_t raw,float capacity) {
if(lastSavedSOC<0) lastSavedSOC=soc;
if(!forceSave && fabs(soc-lastSavedSOC)<SOC_WRITE_DELTA && millis()-lastSave<SAVE_INTERVAL) return;
GaugeState s;
s.soc=soc;
s.raw=raw;
s.learnedCap=capacity;
s.crc=calcCRC(s);
EEPROM.put(EEPROM_ADDR,s);
EEPROM.commit();
lastSavedSOC=soc;
lastSave=millis();
forceSave=false;
Serial.println("EEPROM saved");
}
// ===== voltage fallback =====
float estimateSOC(float v){
if(v>=8.10) return 100;
if(v>=8.00) return 95;
if(v>=7.80) return 80;
if(v>=7.60) return 65;
if(v>=7.40) return 50;
if(v>=7.20) return 35;
if(v>=7.00) return 20;
if(v>=6.80) return 10;
return 5;
}
// ===== print time functie =====
void printTime(float hours){
if(hours < 0){
Serial.print("--:--:--");
return;
}
int h = (int)hours;
float remainder = hours - h;
int m = (int)(remainder * 60);
int s = (int)(((remainder * 60) - m) * 60 + 0.5);
if(s >= 60){ s -= 60; m += 1; }
if(m >= 60){ m -= 60; h += 1; }
if(h < 10) Serial.print('0');
Serial.print(h); Serial.print(':');
if(m < 10) Serial.print('0');
Serial.print(m); Serial.print(':');
if(s < 10) Serial.print('0');
Serial.print(s);
}
// ===== SETUP =====
void setup(){
Serial.begin(115200);
Wire.begin();
EEPROM.begin(128);
Serial.println("\n=== SMART FUEL GAUGE + HEALTH ===");
while(!gauge.begin()){
Serial.println("LTC2944 niet gevonden");
delay(2000);
}
gauge.setBatteryCapacity(DESIGN_CAPACITY_MAH);
gauge.setADCMode(ADC_MODE_SLEEP);
gauge.startMeasurement();
delay(300);
float voltage=gauge.getVoltage();
float current=gauge.getCurrent();
GaugeState saved;
bool ok=loadState(saved);
if(ok){
totalRaw=saved.raw;
lastRaw=saved.raw;
learnedCapacity=saved.learnedCap;
kalmanCurrent=current*1000;
kalmanVoltage=voltage;
Serial.println("EEPROM SOC + capaciteit geladen");
} else {
float soc;
if(voltage>=FULL_VOLTAGE){
gauge.setBatteryToFull(); totalRaw=65535; lastRaw=65535; soc=100; kalmanCurrent=current*1000;
} else if(voltage<=EMPTY_VOLTAGE){
gauge.setRawAccumulatedCharge(0); totalRaw=0; lastRaw=0; soc=0; kalmanCurrent=current*1000;
} else {
soc=estimateSOC(voltage); uint16_t raw=(soc/100.0)*65535.0;
gauge.setRawAccumulatedCharge(raw); totalRaw=raw; lastRaw=raw; kalmanCurrent=current*1000;
}
Serial.println("Geen EEPROM → voltage fallback");
}
}
// ===== LOOP =====
void loop(){
uint16_t raw = gauge.getRawAccumulatedCharge();
int32_t delta = raw - lastRaw;
if(delta > 32767) delta -= 65536;
if(delta < -32767) delta += 65536;
totalRaw += delta;
lastRaw = raw;
if(totalRaw<0) totalRaw=0;
if(totalRaw>65535) totalRaw=65535;
float soc = (totalRaw/65535.0)*100.0;
float remaining = (soc/100.0)*learnedCapacity;
float voltage = gauge.getVoltage();
float current = gauge.getCurrent()*1000; // mA
float temp = gauge.getTemperature();
float filteredCurrent = kalmanUpdateCurrent(current);
float filteredVoltage = kalmanUpdateVoltage(voltage);
// ===== TIMER BEREKENING MET SMOOTH =====
const float CURRENT_THRESHOLD = 20.0; // mA drempel voor idle
static float smoothEmpty = -1;
static float smoothFull = -1;
float timeToEmpty = -1;
float timeToFull = -1;
if(filteredCurrent < -CURRENT_THRESHOLD){
timeToEmpty = remaining / -filteredCurrent;
smoothEmpty = kalmanUpdateTimerEmpty(timeToEmpty);
smoothFull = -1; // reset full timer
} else if(filteredCurrent > CURRENT_THRESHOLD){
timeToFull = (learnedCapacity - remaining) / filteredCurrent;
// timer mag niet oplopen, alleen afnemen
if(smoothFull < 0 || timeToFull < smoothFull){
smoothFull = kalmanUpdateTimerFull(timeToFull);
} else {
smoothFull = smoothFull*0.9 + timeToFull*0.1;
}
smoothEmpty = -1; // reset empty timer
} else {
smoothEmpty = -1;
smoothFull = -1;
}
// ===== interne weerstand =====
static float prevVoltage = voltage;
static float prevCurrent = current;
float dV = prevVoltage - voltage;
float dI = prevCurrent - current;
float internalRes = (dI!=0) ? (dV/dI*1000.0f) : 0; // mΩ
if(fabs(internalRes)<1.0f) internalRes=1.0f;
prevVoltage = voltage;
prevCurrent = current;
static float rintSmooth = internalRes;
rintSmooth = rintSmooth*0.9 + internalRes*0.1;
if(fabs(rintSmooth)<0.5) rintSmooth=0;
// ===== battery health =====
float batteryHealth = 100.0f - fmax(0.0f, fmin(100.0f, (rintSmooth-10.0f)*100.0f/40.0f));
// ===== print =====
Serial.print("SOC: "); Serial.print(soc,2); Serial.print("% | ");
Serial.print(remaining,0); Serial.print("mAh | ");
Serial.print("V:"); Serial.print(filteredVoltage,3);
Serial.print(" I:"); Serial.print(filteredCurrent,0); Serial.print("mA | ");
Serial.print("T:"); Serial.print(temp,1);
Serial.print(" | ");
if(smoothFull > 0){ Serial.print("FULL in "); printTime(smoothFull); }
else if(smoothEmpty > 0){ Serial.print("EMPTY in "); printTime(smoothEmpty); }
else { Serial.print("Idle"); }
Serial.print(" | Rint:"); Serial.print(rintSmooth,1);
Serial.print(" mΩ | Health:"); Serial.print(batteryHealth,0); Serial.println("%");
// ===== recalibratie full/empty + capaciteit leren =====
static bool wasLow=false;
if(soc<15) wasLow=true;
if(voltage>FULL_VOLTAGE && fabs(filteredCurrent)<120){
gauge.setBatteryToFull(); totalRaw=65535; forceSave=true;
if(wasLow){
float used=learnedCapacity*(100.0-soc)/100.0;
learnedCapacity=learnedCapacity*0.9 + used*0.1;
Serial.print("Nieuwe capaciteit geleerd: "); Serial.println(learnedCapacity);
wasLow=false;
}
}
if(voltage<EMPTY_VOLTAGE && fabs(filteredCurrent)<50){
gauge.setRawAccumulatedCharge(0); totalRaw=0; forceSave=true;
}
saveState(soc,lastRaw,learnedCapacity);
delay(1000);
}
Ik gebruik een CJMCU-294 met R050 weerstand
Roland van Leusden
It's the rule that you live by and die for It's the one thing you can't deny Even though you don't know what the price is. It is justified.
Ik vertouw die LTC2944 libary niet, hieronder testcode deze libary niet gebruikt en rechtstreeks de registers uitleest.
Bij opstart verwacht je ongeveer:
- Status ≈ 0x00
- Control ≈ 0x3F (ADC sleep, prescaler 4096)
- ACR ≈ rond 0x7FFF
#include <Wire.h>
#define ADDR 0x64 // LTC2944 7-bit address
uint16_t readWord(uint8_t reg) {
Wire.beginTransmission(ADDR);
Wire.write(reg);
Wire.endTransmission(false);
Wire.requestFrom(ADDR, 2);
uint16_t msb = Wire.read();
uint16_t lsb = Wire.read();
return (msb << 8) | lsb;
}
uint8_t readByte(uint8_t reg) {
Wire.beginTransmission(ADDR);
Wire.write(reg);
Wire.endTransmission(false);
Wire.requestFrom(ADDR, 1);
return Wire.read();
}
void setup() {
Serial.begin(115200);
Wire.begin();
Serial.println("\n--- LTC2944 RAW REGISTER TEST ---");
// Lees status en control register
uint8_t status = readByte(0x00);
uint8_t control = readByte(0x01);
Serial.print("Status (0x00): 0x"); Serial.println(status, HEX);
Serial.print("Control (0x01): 0x"); Serial.println(control, HEX);
// ACR (coulomb teller)
uint16_t acr = readWord(0x02);
Serial.print("ACR (0x02): 0x"); Serial.println(acr, HEX);
// Voltage, Current, Temp
uint16_t volt = readWord(0x08);
uint16_t curr = readWord(0x0E);
uint16_t temp = readWord(0x14);
Serial.print("Voltage raw (0x08): 0x"); Serial.println(volt, HEX);
Serial.print("Current raw (0x0E): 0x"); Serial.println(curr, HEX);
Serial.print("Temp raw (0x14): 0x"); Serial.println(temp, HEX);
Serial.println("\nHerhaal dit terwijl er stroom loopt.");
}
void loop() {
// elke 5 sec opnieuw alleen ACR en current
delay(5000);
uint16_t acr = readWord(0x02);
uint16_t curr = readWord(0x0E);
Serial.print("ACR: 0x"); Serial.print(acr, HEX);
Serial.print(" Current: 0x"); Serial.println(curr, HEX);
}
19:56:31.012 -> --- LTC2944 RAW REGISTER TEST ---
19:56:31.012 -> Status (0x00): 0x4
19:56:31.012 -> Control (0x01): 0x28
19:56:31.012 -> ACR (0x02): 0xDBD2
19:56:31.012 -> Voltage raw (0x08): 0x1B01
19:56:31.012 -> Current raw (0x0E): 0x6120
19:56:31.012 -> Temp raw (0x14): 0x92A0
19:56:31.012 ->
19:56:31.012 -> Herhaal dit terwijl er stroom loopt.
19:56:36.009 -> ACR: 0xDBCD Current: 0x6120
19:56:41.016 -> ACR: 0xDBC8 Current: 0x6120
19:56:46.020 -> ACR: 0xDBC3 Current: 0x6120
19:56:51.016 -> ACR: 0xDBBF Current: 0x6120Mijn "load" is ongeveer 280mA (gemeten met de labvoeding)
Roland van Leusden
It's the rule that you live by and die for It's the one thing you can't deny Even though you don't know what the price is. It is justified.
Je RAW test laat zien:
- ACR loopt netjes terug
- ACR: 0xDBD2 → 0xDBCD → 0xDBC8 → 0xDBC3 → 0xDBBF
Dat is precies wat de LTC2944 hoort te doen bij ontladen:
- De accumulator telt naar beneden
- Elke ~5 sec daalt hij met ~5 counts
Dat is consistent gedrag.
Current raw: 0x6120 (constant)
Omgerekend volgens datasheet:
Exact jouw gemeten ~280 mA!
Kortom het door PonyPc "omgebouwde" van LTC2941 naar LTC2944 libary werkt niet correct.
Misschien doe ik iets fout maar als ik de formule uitvoer kom ik anders uit.
value = 0x6120 (24864)
24864 / 32768 =0,759
0,759 - 1 =-0,241
0,241 * 1280 =308,48
Wat doe ik fout?
Roland van Leusden
It's the rule that you live by and die for It's the one thing you can't deny Even though you don't know what the price is. It is justified.
Wat is de spanning over je shunt ? Dan kunnen we berekenen wat de exacte waarde van Rsense is. Bij een 5% tolerantie voor Rsense ligt deze tussen de 47,5…52,5 mΩ, dat geeft een afwijking van +/- 15mA bij 300mA. Uiteraard speelt de nauwkeurigheid van je meters ook nog een rol.
Ik meet tussen S1 en S+ bij load van 280mA ongeveer -16mV
Zonder load gemiddeld 950uV
Of bedoel je iets anders?
[Bericht gewijzigd door bbuster op (12%)]
Roland van Leusden
It's the rule that you live by and die for It's the one thing you can't deny Even though you don't know what the price is. It is justified.
Rsense = V/I => -16mV / 280mA = 0.057Ω => 57mΩ ipv 50mΩ
Het kan zijn dat jouw waveshare 5" esp32-s3 scherm geen constante stroom trekt. kun je bv een lampje of weerstand aansluiten en opnieuw de spanning tussen S- en S+ meten.
Nee ik heb hier helaas niks van weerstanden of lampen liggen die op 8v iets van vermogen trekken
Heb net een heel klein 12v lampje gevonden wat in een schakelaar zit. Op 7.49v (zelfde wat mijn accu nu heeft) meet ik via de labvoeding 0.035A.
De scope geeft over de S1 / S+ gemiddeld -1.11mV aan
De LTC2944 library komt uit op 0.036A. Dat lijkt dan redelijk te kloppen. Ik vraag me alleen af of de SOC klopt
Ik ben nu weer even met de fuel gauge aan het stoeien maar weet eigenlijk niet zo goed hoe ik de logica moet maken.
Als ik het goed heb meet de LTC2944 hoeveel "coulumb?" er gebruikt is t.o.v. de vorige meting? Mijn accu van 6000mA heeft initeel 0 coulomb verbruikt en per x mA wordt er dus 1 coulomb opgeteld. Zodra de 6000mA verbruikt zijn staat de waarde op 65536(?)
Nu zal mijn accu niet precies 6000mA zijn maar nog 5492mA of 6182mA (willekeurige gok).
Hoe kan ik de logica nu goed implementeren?
De LTC2944 heeft geen idee van initiële state of charge dus dit moet ik zelf bepalen. Dat kan door de accu volledig op te laden en de firmware de waarde dan op 65536 te zetten. Daarna wordt het voor mij onduidelijk. Stel de accu wordt een jaar in de kast gelegd. Ik zet de microcontroller dan aan en deze haalt uit de software dat de accu 100% vol was. Uiteraard is de accu niet meer volledig vol dus hier is een mismatch. Een alternatief is aan de hand van het voltage te meten wat het zou moeten zijn. Stel de accu is geen 8.1v maar 7.9, dan kan ik bepalen dat de soc ongeveer 95% is ipv 100%.
Ook dat geeft onduidelijkheden, want stel ik heb de lader eraan hangen, dan meet ik idd 7.9v dus zeg ik 95%, maar zodra de lader eruit getrokken wordt valt de spanning terug naar 7.7v en klopt mijn waarde niet meer.
Wat is hier de juiste logica voor?
Roland van Leusden
It's the rule that you live by and die for It's the one thing you can't deny Even though you don't know what the price is. It is justified.
De LTC2944 meet gewoon coulomb (lading) via de shunt. Hij weet niets van je SOC, dat moet je zelf in je microcontroller bijhouden.
Wat je moet doen:
Een keer leeg → vol en vol → leeg meten voor kalibratie.
Dan weet je hoeveel ACR-tikken overeenkomen met de bruikbare capaciteit van jouw accu.
Die waarde sla je op in EEPROM.
Tijdens normaal gebruik:
ACR uitlezen
delta berekenen
SOC in je MCU verhogen/verlagen
begrenzen tussen 0% en 100%
Daarnaast heb je ankerpunten nodig om drift te corrigeren:
Vol detecteren via CV + lage laadstroom (taper current) → SOC = 100%
Leeg detecteren via onderspanning onder belasting → SOC = 0%
Stel de accu wordt een jaar in de kast gelegd.
Dan klopt je opgeslagen SOC niet meer door zelfontlading.
Oplossing: bij opstart een OCV-meting doen (dus meten als er géén stroom loopt en de accu even heeft gerust).
Die rustspanning kun je vergelijken met de OCV-curve van je accuchemie om een nieuwe start-SOC te bepalen.
Belangrijk: spanning tijdens laden of ontladen zegt weinig door IR-drop en surface charge. Alleen bij rust gebruiken.
Kort gezegd:
Coulomb counting voor het dagelijkse werk, full/empty events als harde referentie, en OCV alleen voor herinitialisatie na lange stilstand.