Skip to content

Instantly share code, notes, and snippets.

@tekk
Created November 26, 2025 17:24
Show Gist options
  • Select an option

  • Save tekk/c36151c1bf0a6e1f927caef40f1614ea to your computer and use it in GitHub Desktop.

Select an option

Save tekk/c36151c1bf0a6e1f927caef40f1614ea to your computer and use it in GitHub Desktop.
Li-Ion / Li-Pol battery percentage interpolation - better algorithm with lookup table
#include <array>
#include <cstddef>
#include <algorithm>
struct LipoPoint {
float voltage;
float percent;
};
static constexpr std::array<LipoPoint, 21> kLipoCurve = {{
{3.27f, 0.0f}, {3.61f, 5.0f}, {3.69f, 10.0f}, {3.71f, 15.0f}, {3.73f, 20.0f},
{3.75f, 25.0f}, {3.77f, 30.0f}, {3.79f, 35.0f}, {3.80f, 40.0f}, {3.82f, 45.0f},
{3.84f, 50.0f}, {3.85f, 55.0f}, {3.87f, 60.0f}, {3.91f, 65.0f}, {3.95f, 70.0f},
{3.98f, 75.0f}, {4.02f, 80.0f}, {4.08f, 85.0f}, {4.11f, 90.0f}, {4.15f, 95.0f},
{4.20f, 100.0f}
}};
static float lerp(float a, float b, float t) {
return a + (b - a) * t;
}
float estimateLipoPercent(float packVoltage, int cells) {
if (cells <= 0 || packVoltage <= 0.0f) return 0.0f;
float vCell = packVoltage / static_cast<float>(cells);
if (vCell <= kLipoCurve.front().voltage) return 0.0f;
if (vCell >= kLipoCurve.back().voltage) return 100.0f;
for (std::size_t i = 1; i < kLipoCurve.size(); ++i) {
const auto& p0 = kLipoCurve[i - 1];
const auto& p1 = kLipoCurve[i];
if (vCell <= p1.voltage) {
float t = (p1.voltage - p0.voltage) > 0.0f
? (vCell - p0.voltage) / (p1.voltage - p0.voltage)
: 0.0f;
float percent = lerp(p0.percent, p1.percent, t);
return std::clamp(percent, 0.0f, 100.0f);
}
}
return 100.0f;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment