How to automatically stop charging at 80%

TLDR; It’s a bit of work, and you’d like to have some experience with electronics and Arduino. Basically I monitor charging current (or speed if you like) to determine when the charge on my iPhone battery has reached about 80%, and then cut power to the charger.

Note that on Android there are apps that will do this for you.

Also note that on iPhone you can use Automation in the Shortcuts app to send you a notification at a certain charge, and then take the phone off manually.

That said the method I use here could in theory be used to modify any device that uses lithium batteries. I must admit I did it mostly for the challenge, however I also find it useful.

1st: Why stop charging before it has reached full capacity?

TLDR; To prolong battery life it is a good practice to avoid fully charging or discharging your battery.

In an interview at Norwegian TV Eirik Børsheim, then with NEK (the Norwegian branch of IEC) said that the more you avoid charging fully, and discharging fully, the more you preserve battery capacity over time. According to him the rate is exponential, and the ideal charge rate would be between 30% and 55%. I can’t find other sources that these exact numbers are true. But it seem to be a general understanding that you should try to keep the charge between 20% and 80%, or even 25% and 75%. It also seems true that you should avoid charging to full capacity, especially when the battery is at a high temperature at the same time. read more here

Now if you ask me, keeping the charge between 30% and 55% all the time gives a very limited window of use. In fact just 25% of the battery’s full capacity. It does not make sense in my opinion to only utilise 25% percentage of the battery to keep it from decaying for 9 years. Who needs a 9 year old phone with a pristine battery? That said I wouldn’t mind having a 4 year old phone with 80% battery capacity left.

My typical usage window would be to take the phone off the charger in the morning, bring it down to around 60% trough the day, and put it back on the charger over the night. If I were to change that and top it up to 60%/70% twice a day with a quick charger, that would significantly prolong battery life. Then I could bring it to a 100% charge whenever I’d need, for traveling and the likes.

2nd: How?

TLDR; Multiple options. I decided to monitor the amperage on my wireless charger, and then stop charging at a certain amperage threshold. Using an arduino and some module boards. 

There are at least two ways to stop the charging, with apple certainly not letting you do it in software on the phone (you’d might be able too with Android). On the iPhone you could use a shortcut to send a notification to a sever on a specific charge, then have a raspberry pi or something cut power to the charger. That to me sounds more expensive and less reliable, cause you always seem to end up with lag somewhere. It might be possible that you could read some information off of the data-line of the charger as well, and that might be the most sophisticated, but also the most difficult way. The seemingly easiest would be to monitor the current of the charger, and then when it reaches a certain threshold, cut the power. The reason you might be able to do this is because of lithium cells charging profile. I dont know much more about this then that it charges slower the higher the charge.

3rd: Schematics, data and code:

I decided to modify a Wireless charger to do this. However it should be possible with any charging wire as long as the current is high enough for the bottle neck to be the battery. I.e. if you charge with 500mA, you’d never be above what the battery can take, and thus not see a difference. So generally the “faster” the charger, the better luck you have.

Here i briefly explain how i did it in this case. You’ll have to do slightly different things depending on what kind of charger you’ve got.

The charger i have will give 7.5W and draws about 1.5A over 9V. This seem to be enough to detect a difference in current draw at about 65-70% charge, which too me is ideal.

This charger has gets power off a Qualcomm Quick Charge charger at 9V. Sometimes the handshake goes wrong and, it only gets 5V. This has to be accounted for, but it is not the biggest problem.

So it has a 9V rail that goes to the circuit for the wireless charger, and it has a 5V rail for the watch and airpods. The last is nice cause I can just power the arduino straight from that. I needed the ground for the voltage sensor anyway, so its just one extra lead.

All in all this is four wires to get out of the charger. The ground, the 5V rail, and the 9V in and out.

The ground and 5V is easy enough to probe for and find, and then its just a matter scraping a bit of solder mask off, and solder the leads onto it.

9v rail is almost just as simple. But since we are measuring current, we need to cut the rail, literally. With a utility knife cut into the rail and make two grooves a millimetre or two apart. Cut into the grooves a few times so you have the best possible chance of getting trough the copper. Then scrape away the copper between the grooves. Now scrape some solder mask off of each side and solder your 9V in and out leads to them. Out (+ in the schematic) on the side that connects to the usb-c socket, and in (-) on the side that connects to the wireless charger part. Be careful to cut after the point where the 5V regulator is supplied from. Also be careful to not cut anything else than the rail or too deep into the board, or yourself.

The cheapo e-bay charger that i used:

Wiring diagram, should be pretty self explanatory:

Script:

//main loop timing
unsigned long lastRun = 0;
unsigned long currentMillis = 0;
const unsigned long interval = 2000;  //loop time in millis

bool threshold = false; //boolean set when threshold is met
const float thresholdValue = 0.9;
//when threshold value is met, program checks for the next time the
//charging rate goes below, then terminates charging

//pin assignment
const int yellowLed = 5;
const int redLed = 4;
const int supplyRelay = 2;
const int chargeRelay = 3;

//current sensor vars
const int currentPin = A2;
int adcValue = 0;
double adcVoltage = 0;
double currentValue = 0;
unsigned int RawValue = 0;

//volt sensor vars
int analogInput = A1;
float volt = 0.0;

void setup() 
{
  pinMode(currentPin, INPUT);
  pinMode(analogInput, INPUT);
  pinMode(yellowLed, OUTPUT);
  pinMode(redLed, OUTPUT);
  pinMode(supplyRelay, OUTPUT);
  pinMode(chargeRelay, OUTPUT);

  Serial.begin(9600);
  while (!Serial) {
      // will pause Zero, Leonardo, etc until serial console opens
      delay(1);
  }

  //turn relays on, not needed
  //digitalWrite(chargeRelay, LOW);
  //digitalWrite(supplyRelay, LOW);
  
  delay(1000);

  lastRun = millis();  //initial start time
}
 
void loop()
{
  currentMillis = millis();
  if (currentMillis - lastRun >= interval){

    //amp
    //blink if charging, ie threshold met
    if (threshold){ digitalWrite(yellowLed, HIGH); }

    //take 64 amp readings and add them together
    for (int x = 0; x < 64; x++){
      adcValue = analogRead(currentPin);
      RawValue = RawValue + adcValue; // add each adc reading to a total
      delay (10);
    }
    //calculate average and convert from pin readout to amp
    adcVoltage = ((RawValue / 64) / 1024.0) * 5000;
    currentValue = ((adcVoltage - 2500) / 185);

    //"unblink" if charging, ie threshold met
    if (threshold){ digitalWrite(yellowLed, LOW); }


    //sense threshold
    if (threshold == false){
      if (currentValue > thresholdValue){
          threshold = true;
      }
      //blink both once when threshold set
      digitalWrite(redLed, HIGH);
      digitalWrite(yellowLed, HIGH);
      delay(500);
      digitalWrite(redLed, LOW);
      digitalWrite(yellowLed, LOW);
    }
    
    //terminate if threshold set and amperage too low
    if(threshold && (currentValue < thresholdValue)){
        digitalWrite(chargeRelay, HIGH);
        //blink one at the time: charging done
        for (int y = 0; y < 75; y++){
          digitalWrite(yellowLed, HIGH);
          digitalWrite(redLed, LOW);
          delay(200);
          digitalWrite(yellowLed, LOW);
          digitalWrite(redLed, HIGH);
          delay(200); 
        }
        digitalWrite(supplyRelay, HIGH);
    }

    //calculate voltage
    volt = (((analogRead(analogInput)) * 5.0) / 1024.0) / 0.2;

    //halt charging if voltage too low
    if (volt < 8){
        digitalWrite(chargeRelay, HIGH);
        //blink red, no charging!!
        while (volt < 8){
          digitalWrite(redLed, HIGH);
          delay(500);
          digitalWrite(redLed, LOW);
          delay(500); 
        }
    }

    Serial.print(currentValue,3);
    Serial.print(", " );
    Serial.println(volt,2);

    RawValue = 0;
    lastRun = currentMillis;
  }
}Code language: Arduino (arduino)

Data:

This is from the testing phase, and you can see how noisy the signal is: Clearly it has to be filtered, but you can see a clear trend. Shows about the actual current times 2, since i didn’t pay enough attention with the code.

 

Averaging test:

The green line is a single sample from the INA216 sensor.
The blue line is the average of 64 samples with 10ms in between each.
The orange line is the highest single sample out of the same 64 as used for average.

I decided to go for the blue line, cause there is multiple lower points after the 49 min mark, and if i set a threshold just above them, i can use this threshold to terminate the charging. I.e. as soon as i get a value below 1.02 mA, stop charging.

 

After taking everything off the breadboard and soldering it together all values read slightly different, however seemingly consistent. I had to collect the data again to set a new threshold, which wasn’t the biggest problem. I set it too 0.9, and charging seem to consistently stop at 65-70%

There are a few changes before the new data. The relay module hadn’t been connected before, and i swapped the arduino nano for one without pin headers. After that voltage value seem to read higher than it actually was, and current lower. But as stated, seemingly consistently, so i couldn’t be assed debugging. 

52 – 92%: