Better battery reporting
-
Hello,
A modification I implemented starting from the "battery sensor" sketch, in order to improve battery % reporting; wrapped up in a small library. Please be kind with my code, I'm merely a hobbyist.
Why:
1- LiPo cells can't get under 3.3v without damage risk, AA cells are leak proof down to 0.7v, I wanted my battery reporting to implement this: LiPo: 100% = 4.15v, 0% = 3.3v // AA: 100% = 1.5v, 0% = 0.8v
2 - Voltage divider precision relies on resistors accuracy, I wanted a way to "calibrate" my divider in order to get proper battery reporting
3 - For fun
Please note that the voltage divider is not the same for LiPo and AA battery (explained in cpp's comments)Feel free to copy, paste, modify, criticize!
Test sketch
#include <BatteryManager.h> const int BatteryType = 1; // 1 for 1S LiPo, 2 for 2xAA const uint8_t BatterySensorPin = A0; const float VoltageDividerCalibration = 1; // default 1, can be calibrated by measurement BatteryManager bms; void setup() { // put your setup code here, to run once: Serial.begin(115200); bms.begin(BatteryType, BatterySensorPin,VoltageDividerCalibration); } void loop() { // put your main code here, to run repeatedly: Serial.print("Voltage: "); Serial.println(bms.reportVoltage()); Serial.print("Percent: "); Serial.println(bms.reportPercent()); delay(3000); }
header:
/* * Battery Manager */ #ifndef BatteryManager_h #define BatteryManager_h #include <Arduino.h> class BatteryManager { public: BatteryManager(); void begin(int BatteryType, uint8_t SensePin, float Compensation); int reportPercent(); float reportVoltage(); private: int _BatteryType; //1 for 1S LiPO, 2 for 2x AA uint8_t _SensePin; float _Compensation; float Battery_Min_V; float Battery_Max_V; float Battery_Sense_Pin_Resolution; }; #endif
cpp
#include <Arduino.h> #include <BatteryManager.h> BatteryManager::BatteryManager() { } void BatteryManager::begin(int BatteryType, uint8_t SensePin, float Compensation) { _SensePin = SensePin; _Compensation = Compensation; pinMode(_SensePin, INPUT); // Battery use the 1.1 V internal reference #if defined(__AVR_ATmega2560__) analogReference(INTERNAL1V1); #else analogReference(INTERNAL); #endif int firstRead = analogRead(_SensePin); // check battery type and feed related variables if (BatteryType == 1) { /* LiPo voltage need to be measured up to 4.2V hence using internal ADC ref of 1.1V, * divider across battery needs to be 1M, 330K, * which allows a range of 0 to ((1e6+330e3)/330e3)*1.1 = 4.433 Volts * resolution: 4.4333/1023 = Volts per bit = 0.004333659 * sense point can be bypassed with 0.1 uF cap to reduce noise at that point * LiPo is full @ 4.2V and risks damage below 3.3v * => LiPo usable range is 3.3 to 4.2 volts so the goal is to report * 100% @ 4.2 volts * 0% @ 3.3 volts */ Battery_Min_V = 3.3; Battery_Max_V = 4.15; Battery_Sense_Pin_Resolution = 4.333659; //mv per bit } if (BatteryType == 2) { /* 2 x AA Alkaline voltage need to be measured up to 3V hence using internal ADC ref of 1.1V, * divider across battery needs to be 1M, 470K, * which allows a range of 0 to ((1e6+470e3)/470e3)*1.1 = 3.44 Volts * resolution: 3.44/1023 = Volts per bit = 0.003363075 * sense point can be bypassed with 0.1 uF cap to reduce noise at that point * AA is normally leak proof down to 0.7V, I add an extra 0.1V to avoid any issue * => AA usable range is 1.5 to 0.8 volts so the goal is to report * 100% @ 2x1.5 = 3 volts * 0% @ 2x0.8 = 1.6 volts */ Battery_Min_V = 1.6; Battery_Max_V = 3; Battery_Sense_Pin_Resolution = 3.363075; //mv per bit } } int BatteryManager::reportPercent() { // get the battery Voltage int sensorValue = analogRead(_SensePin); Serial.println(sensorValue); float batteryV = sensorValue * Battery_Sense_Pin_Resolution * _Compensation / 1000; // map to 0 -- 100 according to boundaries // need to multiply voltages by 10 as map function processes long variables return map(10*batteryV,10*Battery_Min_V, 10*Battery_Max_V, 0, 100);; } float BatteryManager::reportVoltage() { // get the battery Voltage int sensorValue = analogRead(_SensePin); Serial.println(sensorValue); return sensorValue * Battery_Sense_Pin_Resolution * _Compensation / 1000; }
-
@thierryd Nice idea.
Two remarks:
You write in the cpp file:
if (BatteryType == 2) {
/* LiPo voltage ...You probably mean here "AA voltage"
The AA batteries you talk about are Alkaline (not rechargeable)?
The values are different for NiMH rechargeable (which I always use).
-
Thank you for your answer, indeed there's a mistake in the comment, I'll edit that right away!
Yes for AA i mean Alkaline, I implemented the "battery type" feature even if I'm not yet using it, as I currently only power my gadget with LiPo cells. But as it is explained well enough (I hope), anybody can add his battery type in the code! I guess for NiMH boundaries would be 0.9v to 1.3v
-
I chose a different approach to this problem (but only with 1 power supply).
That was to replace the resistor pair of 1M0 and 470K with 9M1 and 0.5M preset (trimmer). This reduces the current flow by about 90%+ and by adjusting the preset I can 'calibrate' it to 100%. Changing batteries doesn't seem to require retweeking if the same type are used.
I even did a little sketch to load to the arduino to calibrate the battery reading before loading the main sketch.
So far it is working well for me.