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.


Log in to reply
 

Looks like your connection to MySensors Forum was lost, please wait while we try to reconnect.