Skip to content

Commit

Permalink
LifePo4 and PB battery table - added voltage filter
Browse files Browse the repository at this point in the history
removed delay from adc reading, added a software filter to smooth out voltage readings. In those applications battery would last hours to days, no sudden change should be expected so a less frequent voltage reading or a more aggressive filtering could be done.
Note: to speed up convergence i initiliazied the last value to the minimum voltage, there are other and better ways to init the filter.

Added LiFePO4 and PB  open circuit volta battery tables,
  • Loading branch information
Gabrielerusso committed Feb 12, 2024
1 parent 4cfd927 commit 6f50d8b
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 40 deletions.
61 changes: 27 additions & 34 deletions src/Power.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
if (v < noBatVolt)
return -1; // If voltage is super low assume no battery installed

#ifdef ARCH_ESP32
#ifdef NO_BATTERY_LEVEL_ON_CHARGE
// This does not work on a RAK4631 with battery connected
if (v > chargingVolt)
return 0; // While charging we can't report % full on the battery
Expand All @@ -147,23 +147,20 @@ class AnalogBatteryLevel : public HasBatteryLevel
* @date 06/02/2024
*/
float battery_SOC = 0.0;
uint16_t voltage = v/NUM_CELLS;
const uint16_t OCV[NUM_OCV_POINTS] = OCV_ARRAY;
for (int i = 0; i < NUM_OCV_POINTS; i++){
if (OCV[i] <= voltage){
uint16_t voltage = v / NUM_CELLS; // single cell voltage (average)
for (int i = 0; i < NUM_OCV_POINTS; i++) {
if (OCV[i] <= voltage) {
if (i == 0) {
battery_SOC = 100.0; // 100% full
}
else {
battery_SOC = 100.0; // 100% full
} else {
// interpolate between OCV[i] and OCV[i-1]
battery_SOC = (float) 100.0 / (NUM_OCV_POINTS - 1.0) *
(NUM_OCV_POINTS - 1.0 - i + ((float)voltage - OCV[i]) /
(OCV[i-1] - OCV[i]));
battery_SOC = (float)100.0 / (NUM_OCV_POINTS - 1.0) *
(NUM_OCV_POINTS - 1.0 - i + ((float)voltage - OCV[i]) / (OCV[i - 1] - OCV[i]));
}
break;
}
}
return clamp((int)(battery_SOC),0,100);
return clamp((int)(battery_SOC), 0, 100);
}

/**
Expand Down Expand Up @@ -212,7 +209,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
scaled = operativeAdcMultiplier * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * raw;
#endif
// LOG_DEBUG("battery gpio %d raw val=%u scaled=%u\n", BATTERY_PIN, raw, (uint32_t)(scaled));
last_read_value = scaled;
last_read_value += (scaled - last_read_value) * 0.5; // Virtual LPF
return scaled;
} else {
return last_read_value;
Expand All @@ -232,19 +229,16 @@ class AnalogBatteryLevel : public HasBatteryLevel

#ifndef BAT_MEASURE_ADC_UNIT // ADC1
#ifdef ADC_CTRL
if (heltec_version == 5) {
pinMode(ADC_CTRL, OUTPUT);
digitalWrite(ADC_CTRL, HIGH);
delay(10);
}
pinMode(ADC_CTRL, OUTPUT);
digitalWrite(ADC_CTRL, HIGH);
delay(10);
#endif
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
raw += adc1_get_raw(adc_channel);
// delayMicroseconds(100);
}
#ifdef ADC_CTRL
if (heltec_version == 5) {
digitalWrite(ADC_CTRL, LOW);
}
digitalWrite(ADC_CTRL, LOW);
#endif
#else // ADC2
int32_t adc_buf = 0;
Expand Down Expand Up @@ -293,14 +287,13 @@ class AnalogBatteryLevel : public HasBatteryLevel
/// in power

/// For heltecs with no battery connected, the measured voltage is 2204, so
//need to be higher than that, in this case is 2500mV
const uint16_t OCV[NUM_OCV_POINTS] = OCV_ARRAY;
const float fullVolt = OCV[0]*NUM_CELLS;
const float emptyVolt = OCV[NUM_OCV_POINTS-1]*NUM_CELLS;
const float chargingVolt = (OCV[0]+10)*NUM_CELLS;
const float noBatVolt = (OCV[NUM_OCV_POINTS-1]-500)*NUM_CELLS;

float last_read_value = 0.0;
// need to be higher than that, in this case is 2500mV (3000-500)
const uint16_t OCV[NUM_OCV_POINTS] = {OCV_ARRAY};
const float chargingVolt = (OCV[0] + 10) * NUM_CELLS;
const float noBatVolt = (OCV[NUM_OCV_POINTS - 1] - 500) * NUM_CELLS;
// Start value from minimum voltage for the filter to not start from 0
// that could trigger some events.
float last_read_value = (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS);
uint32_t last_read_time_ms = 0;

#if defined(HAS_TELEMETRY) && !defined(ARCH_PORTDUINO)
Expand Down Expand Up @@ -476,9 +469,9 @@ void Power::readPowerStatus()
// If the AXP192 returns a percentage less than 0, the feature is either not supported or there is an error
// In that case, we compute an estimate of the charge percent based on open circuite voltage table defined
// in power.h
batteryChargePercent =
clamp((int)(((batteryVoltageMv - (OCV[NUM_OCV_POINTS-1]*NUM_CELLS)) * 1e2) / ((OCV[0]*NUM_CELLS) - (OCV[NUM_OCV_POINTS-1]*NUM_CELLS))),
0, 100);
batteryChargePercent = clamp((int)(((batteryVoltageMv - (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS)) * 1e2) /
((OCV[0] * NUM_CELLS) - (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS))),
0, 100);
}
}

Expand Down Expand Up @@ -545,9 +538,9 @@ void Power::readPowerStatus()

// If we have a battery at all and it is less than 0%, force deep sleep if we have more than 10 low readings in
// a row. NOTE: min LiIon/LiPo voltage is 2.0 to 2.5V, current OCV min is set to 3100 that is large enough.
//
//
if (powerStatus2.getHasBattery() && !powerStatus2.getHasUSB()) {
if (batteryLevel->getBattVoltage() < OCV[NUM_OCV_POINTS-1]) {
if (batteryLevel->getBattVoltage() < OCV[NUM_OCV_POINTS - 1]) {
low_voltage_counter++;
LOG_DEBUG("Low voltage counter: %d/10\n", low_voltage_counter);
if (low_voltage_counter > 10) {
Expand Down
22 changes: 16 additions & 6 deletions src/power.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,22 @@
#define NUM_OCV_POINTS 11
#endif

// 3400,3350,3320,3300,3270,3260,3250,3220,3200,3120,3000 //3.4 to 3.0 LiFePO4
// 2120,2090,2070,2050,2030,2010,1990,1980,1970,1960,1950 //2.12 to 1.95 Lead Acid
// 4200,4050,3990,3890,3790,3700,3650,3550,3450,3300,3200 //4.2 to 3.2 LiIon/LiPo
// 4200,4050,3990,3890,3790,3700,3650,3550,3400,3300,3000 //4.2 to 3.0 LiIon/LiPo
// 4150,4050,3990,3890,3790,3690,3620,3520,3420,3300,3100 //4.15 to 3.1 LiIon/LiPo
#ifndef OCV_ARRAY
//{4200,4050,3990,3890,3790,3700,3650,3550,3450,3300,3200} //4.2 to 3.2
//{4200,4050,3990,3890,3790,3700,3650,3550,3400,3300,3000} //4.2 to 3.0
//{4150,4050,3990,3890,3790,3690,3620,3520,3420,3300,3100} //4.15 to 3.1
#define OCV_ARRAY {4150,4050,3990,3890,3790,3690,3620,3520,3420,3300,3100}
#ifdef CELL_TYPE_LIFEPO4
#define OCV_ARRAY 3400, 3350, 3320, 3300, 3270, 3260, 3250, 3220, 3200, 3120, 3000
#elif defined(CELL_TYPE_LEADACID)
#define OCV_ARRAY 2120, 2090, 2070, 2050, 2030, 2010, 1990, 1980, 1970, 1960, 1950
#else // LiIon
#define OCV_ARRAY 4190, 4050, 3990, 3890, 3790, 3690, 3620, 3520, 3420, 3300, 3100
#endif
#endif

/*Note: 12V lead acid is 6 cells, most board accept only 1 cell LiIon/LiPo*/
#ifndef NUM_CELLS
#define NUM_CELLS 1
#endif
Expand Down Expand Up @@ -48,7 +57,8 @@ class Power : private concurrency::OSThread
virtual bool setup();
virtual int32_t runOnce() override;
void setStatusHandler(meshtastic::PowerStatus *handler) { statusHandler = handler; }
const uint16_t OCV[11] = OCV_ARRAY;
const uint16_t OCV[11] = {OCV_ARRAY};

protected:
meshtastic::PowerStatus *statusHandler;

Expand All @@ -58,7 +68,7 @@ class Power : private concurrency::OSThread
bool analogInit();

private:
//open circuit voltage lookup table
// open circuit voltage lookup table
uint8_t low_voltage_counter;
#ifdef DEBUG_HEAP
uint32_t lastheap;
Expand Down

0 comments on commit 6f50d8b

Please sign in to comment.