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; }; #endifcpp
#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; } -
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; }; #endifcpp
#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.
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better 💗
Register Login