Safe Charging for LiPO batteries - deviceOS@2.x LTS Approach

First, let me start by saying that I am very happy about Particle's decision to pursue a long term support approach to deviceOS. If you want to read more, please see @vikram's excellent post here:

For devices in remote locations, like mine, having a focus on stability and reliability is a much apprecaited improvement. I plan to use the LTS approach for my devices as much as possible.

I am starting this thread as a continuation of this one given the move from 2nd Generation to 3rd Generation devices and the new LTS context:

If are you are using the Boron or Argon for outdoor applications and the temperature could go below freezing or above 113F (45C), you need to take care to disable charging based on temperature. As the disable charging feature did not make it into the LTS 2.x line, you have another year managing this in your firmware (If you are going to use the 3.x release, this feature is in the current beta).

I wanted to share my approach and see if anyone had ideas for improvement. I think this is worth discussion for two reasons which make this a little more challenging than simply calling pmic.disableCharging():

  1. Using the PMIC API is not sticky and there may be times where your settings will be overwritten

  2. Changes you make in the PMIC commands are not correctly reflected in the System.batteryState() and you can get incorrect reporting on what is going on with the battery.

Here is how I am currently handling this. I call the following function each hour when the device awakes to report and from Setup(). The context of the battery is passed in a WebHook to Ubidots so I can see what the devices' battery is doing. I also increment an "alerts" flag so I can get automated reporting across the fleet.

  if (!isItSafeToCharge()) current.alertCount++;                      // Increment the alert count

calls

bool isItSafeToCharge()                                               // Returns a true or false if the battery is in a safe charging range.  
{     
  sysStatus.batteryState = System.batteryState();
  PMIC pmic(true);                                                                
  if (current.temperature < 32 || current.temperature > 113 )  {      // Reference: https://batteryuniversity.com/learn/article/charging_at_high_and_low_temperatures
    pmic.disableCharging();                                           // It is too cold or too hot to safely charge the battery
    sysStatus.batteryState = 1;                                       // Overwrites the values from the batteryState API to reflect that we are "Not Charging"
    return false;
  }
  else {
    pmic.enableCharging();                                            // It is safe to charge the battery
    return true;
  }
}

I hope this is helpful, please let me know.

Chip

4 Likes

Chip, Thanks for sharing.
To start the discussion, I noticed that you don't call pmic.unlock(), although I don't know if it's useful or not.

Link to Thread/Post

Is that suggestion still valid for LTS 2.x ?

@Rftop,

Not sure, I looked at how it is done on the Particle Tracker (@rickkas7 - thank you for the link)

and saw this:

int Tracker::enableCharging() {
    PMIC pmic(true);
    _batteryChargeEnabled = true;
    return (pmic.enableCharging()) ? SYSTEM_ERROR_NONE : SYSTEM_ERROR_IO;
}

int Tracker::disableCharging() {
    PMIC pmic(true);
    _batteryChargeEnabled = false;
    return (pmic.disableCharging()) ? SYSTEM_ERROR_NONE : SYSTEM_ERROR_IO;
}

I don’t see a call to pmic.unlock() in the Tracker code.

Thanks,

Chip

1 Like

Thanks for this thread; I was recently disappointed to find that, on 2.0.1, after so much talk and effort for so long about simply being able to enable/disable solar Vin charging on the Boron based on temp, that my initial first-time-in-a-while test of this yesterday totally failed (it did not charge after calling enableCharging with a Voltaic solar panel), but I have more troubleshooting to do and I hope this is now possible on Boron. It would bring me great convenience not to have to have an external 12V solar battery/panel/charger system. My stations are already measuring board temperature and so I have everything I theoretically need; only functioning firmware and a stable way to code it on the Boron seem to be missing.

What is the verdict on Vin being different apparently from VUSB in terms of solar charging? I can’t use VUSB and need to have some physical connector (awful design). I already designed my boards breaking out the Vin. It is possible to charge a Boron LiPo with a Voltaic 6V solar panel into Vin instead of Vusb, correct?

1 Like

Good discussion point.

We have just moved from Lead Acid to LiFePo batteries on rechargeable power source devices (>1 kWh). I am sure you know this, a word of caution is that all Li-ion batteries have Battery Management System IC of some sort built-in. These BMSs initially caused a few problems before we learnt to work within the parameters of nominal charging and discharging. Due to the fact that our devices are used inside buildings we don’t worry about them being too cold, however, we do stop recharging if the temperature goes to high, monitoring is done several times per second not once every hour, maybe that is a feature of you needing to sleep? Another factor is actually measuring the battery temperature? How are you doing that?

1 Like

This is exactly how I do it and from my initial testing, it works quite well. Here is a thread discussing specific settings related to this and my results using a Voltaic 6V 3.5 Watt panel. Boron Solar Charging with 1.5.0-rc1 - #59 by jgskarda

One consideration @chipmc is you may want to provide a little tolerance on the temperature readings. I believe for 2 reasons. 1) If the temperature is dropping quick in the area due to say a cold front moving through, it is quite possible the temperature to drop several degrees between your 1 hour interval and thus could still charge when below 32 degrees for up to 1 hour. Secondly, I am guessing you are not measuring actual battery temperature so depending on how hot it gets in normal scenarios, you may want to disable it well below the 113 degree mark. I used > 36 and < 90 degrees for my thresholds. That said, I'm in a much colder climate than you so maybe < 90 won't work for your application. Just something to consider if you need to sleep for 1 hour or more between checking.

By the way... thank you for providing your example code. Much appreciated!

@Paul_M,

Yes, I was a bit disappointed that the ability to disable charging was not included in the LTS line. That said, the LTS line is focused on the Enterprise use case and many of these users would, I imagine, use the B-Series and build their own charge management solution.

To answer your question, I don’t see any issue with charging on Vin, this is exactly what I do on my carrier boards.

Thanks,

Chip

@armor,

Great point on the battery management ICs. I have been careful to source and test on Adafruit LiPO batteries which have a data sheet:

and the Adafruit product page specifically states that they do not have a Thermister to control charging.

I don’t measure the temperature of the battery but I do have a temperature sensor on the carrier board which is physically next to the battery inside the enclosure. Now that you mention it, is this good enough?

As for measuring more than once an hour, this would be tough as my devices need their sleep. When it is too hot they are likely getting plenty of sun but when it is too cold, I probably need to conserve battery power. Perhaps I could add some buffer on my temp range currently testing for freezing and 113F (45C) so it would be less likely that the temp would change too much to take me outside my safety zone during the hour.

Thank you for the great questions!

Chip

@jgskarda,

Based on your great advice and @armor’s comments as well, I think I will change the thresholds as you suggested. Since I am tracking and reporting these events in my alerts, I can set up a report to monitor battery performance across the fleet and see if I can live with these limits.

Based on my early testing of the new carrier board, these devices can go up to a week without charging while reporting hourly during park hours, I think it will be better to play it safe.

Thank you again!

Chip

1 Like

Not sure if this is an potential issue or not but I wonder if it’s better to only call pmic.disableCharging() and pmic.enableCharging() only when you want to change the state of charge. In your case, it’s every hour so likely not a big deal as fairly infrequent but there might be others who read temperature more often even every second that may not want to constantly hammer these functions. I read temperature every 5 minutes in the most active state. I just started testing this:

//Enable/disable charging based on the temperature                                                        
if ((TempF < 340 || TempF > 900) && chargeEnabled) {     
  PMIC pmic(true); 
  pmic.disableCharging();
  chargeEnabled = false;
  Log.info("Battery charging is diabled due to unsafe temperature");                                           
}
else if ((TempF > 340 && TempF < 900) && !chargeEnabled) {
  PMIC pmic(true); 
  pmic.enableCharging();
  chargeEnabled = true;
  Log.info("Battery charging is enabled due to safe temperature"); 
}

Note: My TempF tag is the temperature reading from a MCP9808 on the carrier board itself and is scaled x10, my devices are only in cold weather not more than say 60 degrees so I kept my high side temperature at 90 degrees. I also currently rely on Particle.PublishVitals() every 2 hours to obtain battery charge status info so I did not include your method to override System.batteryState(). Looks like you have a Struct for this info that you must publish separately? I probably burn more data than I need to with Particle.PublishVitals() vs. your method and per your info may have incorrect info on charge status or not. Another item to add to my development list. :slight_smile:

2 Likes

I just built out a 5 Amp BQ4050 Battery Management Breakout board good for managing 1-4 Cell Li-Ion & LiFePo4 battery chemistries.

It does have 4 temp sensor inputs and full I2C communication.

This is a very advanced battery management IC, gives you total control of the battery pack.

Just got this built out a couple days ago, seems like it would be a great solution for you guys looking for more control and feedback regarding the battery side of the circuit.

Also built a BQ4050 Breakout and a separate FET & Shunt board for designs that need up to 20-25 amps of current flow. Still testing this one.

4 Likes

@jgskarda,

I think you are on the right point about not hammering this function. I think I will add this logic as well.

I was thinking that as I deploy sensors across the state, they will over the course of a year hit both the high and low temp limits. My capturing these “events” and reporting them to Ubidots, I can look across the fleet and over time, determine the following:

  • Is this really much of an issue, how often does this happen?
  • If so, should I look at other battery chemistries?
  • Do these temperature limits prevent charging over a long enough time to impact battery charge levels in a meaningful way?
  • Could I change device behaviors to compensate - such as moving to a 2-hour interval for reporting when the low-temp limit has been hit for 6 plus hours straight.

Will see and, thank you all for your input on this.

Chip

@RWB, wow, that is some serious current. Did you need to go to 2oz copper to carry 25A?

Chip

Yeah this would be a nice addition. Been tempted to try a B-Series if/when I do that I'd need some good examples of battery management. This seems like a great one! Thanks for sharing! Would like to eventually get to a 18650 multi cell battery pack. I assume the 4 temp sensor inputs is a temperature probe for each cell?

Wow... That's some major current flow for an IoT device. Is this pulling in some large coils of some sort or what's such a large current flow used for?

In any case... thanks for sharing. If/When I get around to playing with a B-Series I may reach out again to learn more.

Yes, it's Oshpark 2 layer board with 2oz copper on both sides stitched together for a 4oz copper trace.

Yea TI makes the best battery management chips. The BQ4050 is the simplest solution I have found for 1-4 cell packs while still have all the features and data available over I2C.

And yes there are 4 temp sensors you can use for 4 cells or mosfet temps to keep track of the power mosfet temps.

It's for a battery pack that can handle a few hundred watts of current.

2 Likes

We are looking to start supply of product to Canada and investigating battery charging and type requirements. I am wondering how much of an “real world” issue everyone has found this to be? We are using the PKCell Lipos which come with the electron kits without any user code protection for temperature in outdoor situations and have yet to see an issue from 150 devices in a climate of 0-45 degrees ambient. Admittedly 0 degrees C only occurs at night time for us when there is no charging attempt, hence my interest in any folks experience in colder climates.

I do understand the physics and the warnings of “Lithium plating” if charged under 5 degrees. However we have a board temperature sensor and publish temp on change and found at times some boards going below 0 degrees C and to approx 58 degrees still finding the batteries are still operational some over 2 years old.

There does not appear to be a degradation in our charge discharge graphs either.
Very keen to hear any real world feedback of these PKCell LiPO’s.

@Paul ,

I use the same batteries and I have them deployed in outdoor locations across North Carolina and some quite cold places in New Zealand. Overall, I have been very happy with this simple approach:

  • A Voltaic Systems 3.5W 6V panel
  • A large (470uF) cap across the Solar Panel inputs on my carrier board
  • A temperature sensor on the carrier board
  • Some simple code that limits charging based on temperature

Here is the code I use (temp in Fahrenheit - sorry)

bool isItSafeToCharge()                                               // Returns a true or false if the battery is in a safe charging range.  
{     
  sysStatus.batteryState = System.batteryState();       
  PMIC pmic(true);                                 
  if (current.temperature < 36 || current.temperature > 100 )  {      // Reference: https://batteryuniversity.com/learn/article/charging_at_high_and_low_temperatures (32 to 113 but with safety)
    pmic.disableCharging();                                           // It is too cold or too hot to safely charge the battery
    sysStatus.batteryState = 1;                                       // Overwrites the values from the batteryState API to reflect that we are "Not Charging"
    return false;
  }
  else {
    pmic.enableCharging();                                            // It is safe to charge the battery
    return true;
  }
}

This works very well with the following being the hardest cases:

  • In North Carolina in the summer, often when the sun is brightest, it is too hot to charge.
  • In the mountains we do get cold snaps and battery levels will dip noticeably. This year I will have counters installed at +2km elevation for the first time so, we will see.
  • The only issue with batteries I have had was when there was an extended outage and they got stuck trying to connect for +24 hours and drained the batteries to 0. This caused 5-6 to stop accepting a charge. This is something I still need to fix so, thank you for the reminder.

This winter, @phillycheeseman and I will have about 50 devices installed in the Toronto area so I should get some Canadian experience soon.

Hope this helps.

6 Likes

@chipmc , thank you for your feedback and code.

1 Like