Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions examples/companion_radio/ui-new/UITask.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "UITask.h"
#include <helpers/TxtDataHelpers.h>
#include <helpers/Battery.h>
#include "../MyMesh.h"
#include "target.h"
#ifdef WIFI_SSID
Expand Down Expand Up @@ -102,12 +103,7 @@ class HomeScreen : public UIScreen {


void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts) {
// Convert millivolts to percentage
const int minMilliVolts = 3000; // Minimum voltage (e.g., 3.0V)
const int maxMilliVolts = 4200; // Maximum voltage (e.g., 4.2V)
int batteryPercentage = ((batteryMilliVolts - minMilliVolts) * 100) / (maxMilliVolts - minMilliVolts);
if (batteryPercentage < 0) batteryPercentage = 0; // Clamp to 0%
if (batteryPercentage > 100) batteryPercentage = 100; // Clamp to 100%
int batteryPercentage = batteryPercentFromMilliVolts(batteryMilliVolts);

// battery icon
int iconWidth = 24;
Expand Down Expand Up @@ -336,8 +332,20 @@ class HomeScreen : public UIScreen {
strcpy(name, "gps"); sprintf(buf, "%.4f %.4f", lat, lon);
break;
case LPP_VOLTAGE:
r.readVoltage(v);
strcpy(name, "voltage"); sprintf(buf, "%6.2f", v);
r.readVoltage(v); // v is in volts

if (channel == TELEM_CHANNEL_SELF) {
// This is our own battery voltage
uint16_t batteryMilliVolts = (uint16_t)(v * 1000.0f + 0.5f); // convert V -> mV
int pct = batteryPercentFromMilliVolts(batteryMilliVolts);

strcpy(name, "battery");
sprintf(buf, "%4.2fV %3d%%", v, pct);
} else {
// Other voltage sensor
strcpy(name, "voltage");
sprintf(buf, "%6.2f", v);
}
break;
case LPP_CURRENT:
r.readCurrent(v);
Expand Down
70 changes: 70 additions & 0 deletions src/helpers/Battery.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include <stddef.h>
#include "Battery.h"

// Build the OCV table from the configured macro.
// 11 entries: 100%, 90%, ..., 0%.
static const uint16_t kOcvTable[] = { OCV_ARRAY };
static const size_t kOcvTableSize = sizeof(kOcvTable) / sizeof(kOcvTable[0]);

#if defined(__cplusplus) && __cplusplus >= 201103L
static_assert(kOcvTableSize == 11,
"OCV_ARRAY must contain exactly 11 entries: 100%, 90%, ..., 0%.");
#endif

int batteryPercentFromMilliVolts(uint16_t batteryMilliVolts) {
const uint8_t stepPct = 10; // distance between table entries (100 → 90 → ... → 0)
const size_t n = kOcvTableSize; // should be 11

if (NUM_CELLS_IN_SERIES > 1) {
// Adjust the input voltage to per-cell basis
batteryMilliVolts /= NUM_CELLS_IN_SERIES;
}

if (n != 11 || batteryMilliVolts <= 0) {
// Error: invalid OCV_ARRAY table size or voltage
return -1;
}

// Above or equal to "full" voltage → clamp to 100%
if (batteryMilliVolts >= kOcvTable[0]) {
return 100;
}

// Below or equal to "empty" voltage → clamp to 0%
if (batteryMilliVolts <= kOcvTable[n - 1]) {
return 0;
}

// Find the segment [i, i+1] where:
// vHigh >= batteryMilliVolts >= vLow
// and map that to [pctHigh, pctLow] = [100 - 10*i, 100 - 10*(i+1)]
for (size_t i = 0; i < n - 1; ++i) {
uint16_t vHigh = kOcvTable[i]; // higher voltage, higher %
uint16_t vLow = kOcvTable[i + 1]; // lower voltage, lower %

if (batteryMilliVolts <= vHigh && batteryMilliVolts >= vLow) {
uint8_t pctHigh = 100 - i * stepPct;
uint8_t pctLow = 100 - (i + 1) * stepPct;

uint16_t dv = vHigh - vLow;
if (dv == 0) {
return pctLow;
}

// How far are we from the low voltage towards the high, as a fraction of the segment?
uint16_t pv = batteryMilliVolts - vLow; // in [0, dv]

// Interpolate percentage within this 10% band.
uint8_t deltaPct = (uint32_t)pv * stepPct / dv;
int pct = pctLow + deltaPct;

// Clamp to [0, 100]
if (pct < 0) pct = 0;
if (pct > 100) pct = 100;
return pct;
}
}

// Should be unreachable if the table is monotonic and cases above are handled.
return -1;
}
54 changes: 54 additions & 0 deletions src/helpers/Battery.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#pragma once

#include <stdint.h>
#include <stddef.h>

// -----------------------------------------------------------------------------
// Open Circuit Voltage (OCV) map configuration
//
// OCV_ARRAY must expand to 11 integer millivolt values, corresponding to:
//
// 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 0%
//
// in *descending* voltage order.
//
// -----------------------------------------------------------------------------

#ifndef OCV_ARRAY
#ifdef CELL_TYPE_LIFEPO4
#define OCV_ARRAY 3400, 3350, 3320, 3290, 3270, 3260, 3250, 3230, 3200, 3120, 3000
#elif defined(CELL_TYPE_LEADACID)
#define OCV_ARRAY 2120, 2090, 2070, 2050, 2030, 2010, 1990, 1980, 1970, 1960, 1950
#elif defined(CELL_TYPE_ALKALINE)
#define OCV_ARRAY 1580, 1400, 1350, 1300, 1280, 1250, 1230, 1190, 1150, 1100, 1000
#elif defined(CELL_TYPE_NIMH)
#define OCV_ARRAY 1400, 1300, 1280, 1270, 1260, 1250, 1240, 1230, 1210, 1150, 1000
#elif defined(CELL_TYPE_LTO)
#define OCV_ARRAY 2700, 2560, 2540, 2520, 2500, 2460, 2420, 2400, 2380, 2320, 1500
#else
// Default Li-Ion / Li-Po
#define OCV_ARRAY 4190, 4050, 3990, 3890, 3800, 3720, 3630, 3530, 3420, 3300, 3100
#endif
#endif

/**
* Number of cells in series (for multi-cell battery packs)
* Default to 1 if not defined.
*/
#ifndef NUM_CELLS_IN_SERIES
#define NUM_CELLS_IN_SERIES 1
#endif


/**
* Convert a battery voltage (in millivolts) to approximate state-of-charge (%),
* using the OCV curve defined by OCV_ARRAY.
*
* Assumes:
* - OCV_ARRAY has 11 entries, in descending order.
* - These correspond to 100, 90, 80, ..., 0 percent.
* - The input batteryMilliVolts is in the same scale as the OCV values.
*
* Returns an integer percentage in [0, 100] or -1 on error.
*/
int batteryPercentFromMilliVolts(uint16_t batteryMilliVolts);