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


Log in to reply
 

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