Projects

Saving Cells (LiPo)

Low Voltage Cut Off for LiPo Batteries

Matt Gilpin

Issue 56, March 2022

This article includes additional downloadable resources.
Please log in to access.

Log in

An ATtiny85-based project that automatically detects the number of LiPo cells being used and prevents them from being over-discharged.

The reason this project came about was that I was accidentally leaving 3S LiPo's connected to devices such as lights (for indoor drone racing etc), and letting them discharge below ~3V, which tends to damage the cells.

To stop this with a cutoff, I first looked at using a Zener diode and comparator to switch a MOSFET. But this had one major flaw, which was that I had to make a different board for each lipo voltage (or cell count) - I have 1s, 2s, 3s and 4s batteries, so that's 4 boards.

So, the final state of this project was to use an ATtiny85 microcontroller to read the voltage of the battery and automatically detect the cell count. From that, it can work out the safe cutoff voltage. It then uses a MOSFET to switch off the battery, and then enters an ultra low power sleep mode to prevent as much current from being drawn from a battery that is almost flat for potentially 10s of minutes or even hours.

I added a LED to confirm that it has correctly detected the cell count, and added a button for future use, although it is currently just being used as a reset button. One idea was to use it as a manual cell count selector, or even for setting the battery cutoff voltage.

As Lithium batteries are becoming more and more commonplace, polymer batteries (Li-Po for short), are ideal for a variety of high power applications due to their high energy density. This makes them excellent for applications such as radio-controlled (RC) aircraft, such as drones and model aeroplanes. They’re what I use to power my drones and other RC aircraft I’ve built over the years. You’ll also find them in those pocket-sized jump starters, and while they look like they’d be incapable of jump-starting a car, they can sure pack a punch.

All of these wonderful things that they provide us must come at a cost right? Well, yes. It’s actually quite an inherent problem. These batteries are just so energy-dense, and because they can release so much energy in a short amount of time, they can be quite dangerous and catch alight if untreated. Because Li-Po batteries can deliver greater current per volume, they are inherently more dangerous, so we need to take care of them.

While this project won’t be able to make Li-Po batteries any safer, we will be stopping them from over-discharging. You see, Li-Po batteries are temperamental things, they don’t like to be overcharged, or over-discharged. While draining a Li-Po a few volts below at a relatively low discharge rate won't make it catch on fire, charging it back may be a little more dangerous.

Not only is over-discharging the battery potentially dangerous, like with all batteries, it drastically reduces their lifespan. I’m sure we’ve all accidentally left something on and flattened a battery before. Just how many of us have left our car lights on overnight only to wake up in the morning and find the car won't start? While a jumpstart will usually resolve our issues, unfortunately, Li-Po batteries are a little less forgiving.

I recently got one of these little indoor FPV drones to fly around the house. They come with 4 or so of these little 250mAh 1S (1 Cell, S = cell) batteries and this super cool 6-way charger (shown here) that can charge up to 6 at once. Even better, it has an XT60 connector on it, so I can charge the smaller drone batteries with some of my 3S Li-Po’s that I use for my larger aircraft. However, this is where I let the Li-Po over-discharge several times.

To combat this mistake and increase the lifespan of my battery, I devised this simple battery cut off. I didn’t want just any battery cut-off. I wanted one that would work with all my other Li-Po batteries with various cell counts so that I wouldn’t have to create a separate circuit for each type.

The Broad Overview

Generally speaking, this battery cut-off is a relatively simple device. It uses an ATtiny85 as it's a small, low-cost and low-power microcontroller. The ATtiny monitors the voltage, controls a MOSFET and drives an optional LED. Inside the ATtiny85, there’s a 10bit ADC that will convert the varying battery voltage into ones and zeros that the microcontroller gives access to us in the code. While this won’t give us super fine control over cut-offs and whatnot, it’s more than sufficient for our problem as we’ll later find that there’s no clear ‘discharged’ battery voltage.

The microcontroller then periodically measures the battery voltage and determines whether or not the power needs to be turned off, or stay on. To turn off the power in this circuit, a MOSFET was selected to simply act as a switch. If we drive the MOSFET to always be in the saturation region, the drain to source resistance will be very low, and thus consequently produce a minimal voltage drop. At higher currents, this can be significant as it will dissipate more heat. But because this project is really for lower currents (less than ~3A, unless you use a PCB version), we won’t need a heatsink. The MOSFET is capable of much more, but what really is limiting us is the stripboard. I have also supplied the Gerber files for a PCB design which is capable of a lot more current. These can be downloaded from the DIYODE website.

Since this project will be powered from single-cell Li-Po's to 6 cells Li-Po’s, we’ll need to use a voltage regulator to step down the voltage to 3.3V for the ATtiny85. I designed this circuit to use a low dropout regulator so that we can get as much voltage out of a single cell Li-Po - which can go down to 3.2V – before the ATtiny85 will shut off due to low voltage.

Finally, I added a switch and LED. The LED displays the battery cell count, while the switch just acts as a simple reset button, but as it’s connected to a general GPIO pin, it can be programmed to do anything.

That’s about all there is to it. The rest is all in the software, which we’ll later find out is also pretty straightforward, although may perhaps look a bit cryptographic at first.

How it works

Now that you have an understanding of roughly what’s going on in this project, let’s go through some more of the nitty-gritty details. First, we need to know exactly what we are protecting. Li-Po cells have a nominal voltage of 3.7V and when fully charged have a voltage of 4.2V. Unfortunately, this is where the agreed values end. There’s a bit of a discrepancy between what we consider is a discharged cell. Some say 3.0V, others say 3.2V or a different voltage altogether. For the sake of this project, I’m going to set my cut off at 3.2V.

Next, let's take a look at why the ATTiny85 is suitable for this project. In designing this circuit we need to consider it is designed to stop over-discharging the battery and once off, we want the device to consume as little energy as possible. A while back in issue 15, Rob Bell and Mike Hansell did an article on the power states and consumption of the ATtiny85. They measured the sleep mode to consume 4.6uA! In addition to this, we do very minimal amounts of processing, so any high performance microcontroller is just a waste. It’s only reading a voltage and turning on and off a MOSFET after all. We don’t need all the bells and whistles that other MCUs provide us such as multiple I2C, SPI and UART controllers. Finally, the ATtiny85 can also be found in a lot of places quite cheaply. You could also use the ATtiny45 or ATtiny25 as well for this project, although the exact code may vary slightly.

As for the voltage regulation, we could use a buck style converter, but to keep things simple, I’ve decided to go with a linear regulator. The power draw is going to be quite low anyway, so there’s no real benefit to using a buck in this case. Our ATtiny85 is capable of 2.7-5.5V (or down to 1.8V if we have the ATtiny85V variant). For this project, I decided to use the LM2936-3.3 regulator due to its abundance and decent characteristics which meet our input voltage requirement. It’s capable of delivering 50mA, which may seem like a small amount, but it’s much more than these little microcontrollers consume. Taking a look at the datasheet for this regulator reveals we need two capacitors, one on the input and another on the output for stability. This regulator also has a dropout voltage of 200mV at 50mA (will be less of a voltage drop at lower currents) which should give us 3V or so if we are using a single cell battery, which is enough for the ATtiny85 to function properly.

As for the MOSFET, we’ll need to use a ‘logic-level’ one since we’re controlling it with 3.3V from our ATtiny85. These typically have gate thresholds in the region of 1-2v. Something like the IRLZ44N will be more than sufficient. There are a lot of different MOSFETs that will work for us, so don’t feel as though you have to stick to what we chose. We’ll use this MOSFET on the low side of the output. This is so that we can tie the gate to ground through a 10k resistor so that the default state is off.

As mentioned above, we’ll use the internal 10bit ADC inside the ATtiny85 in single-ended mode. From the datasheet, we also found that the ATtiny85 has a few options for the voltage reference to use with the ADC. We’ll stick to the 2.56V internal voltage reference as it's good enough for our purposes and will be more accurate when using a single cell Li-Po when the voltage is close to or below 3.5V due to the supply no longer being 3.3V. This means the maximum voltage we can measure is 2.56V. We’ll need to use a resistor voltage divider to lower the battery voltage down for the microcontroller. To work out the resistor values, we can simply rearrange the standard voltage divider formula to give:

Plugging in values for Vout = 2.56V and Vin = 25.2V, we are left with R1 and R2. After using a spreadsheet, we found that the values of R1 = 24kΩ and R2 = 2.7kΩ satisfy this formula. Ideally, these values are as high as possible to limit the drain on the battery. If we go too high, the impedance of the ADC starts factoring into the equation and we don’t really want that.

Speaking of resistor values, we chose a 330Ω resistor for the LED as we only need it to indicate the cell count and not be super bright, wasting our battery capacity, and so keeping current to a minimum is ideal.

Finally, for good measure, we’ll add a polyfuse to the input in case our control logic short circuits. While this isn’t foolproof, it will give us some added protection, and will definitely hint to us that there is something wrong with our board. It gives us an extra chance before potentially shorting the Li-Po battery which in some cases can deliver hundreds of amps.

The Build:

Parts Required:JaycarAltronicsPakronics
1 x ATtiny85ZZ8721Z5105-
1 x LM2936 3.3V RegulatorZV1650--
1 x IRLZ44N MOSFET-Element 14: 8651418-
1 x 3mm Red LED*ZD0102Z0700ADA777
1 x 330Ω Resistor*RR0560R7546SS110990043
1 x 680Ω Resistor*RR0568R7554SS110990043
1 x 2.7kΩ Resistor*RR0582R7568-
1 x 10kΩ Resistor*RR0596R7582SS110990043
1 x 24kΩ Resistor*RR0605R7591-
1 x 100nF Capacitor*RC5360R2865DF-FIT0118
1 x 10µF 63V Capacitor*RE6075R5068DF-FIT0117
1 x Polyfuse (RXE075 or equivalent)RN3460--
1 x 8Pin IC SocketPI6452P0530-
1 x StripboardHP9540H0714-
2 x Screw TerminalsHM3172P2034A-

Parts Required:

* Quantity required, may only be sold in packs.

With the theory out of the way, let’s move on to the most exciting part of this project, the construction. Originally, I created a PCB for this specific circuit (files at the end), but I understand that not everyone wants to go through the trouble of ordering a PCB and putting it together. Instead, I’ll use good ol' Veroboard, specifically the stripboard variant as I find it easier to work with than the dotted board when joining components/tracks together.

Before creating this circuit on the board, I laid out the board in software with all the positionings for all the components and wires. Doing this beforehand helps minimise the errors made in the build process and reduces a lot of the troubleshooting process. Shown here is a layout diagram that I ended up opting for.

The first step in the build process is to cut the stripboard to size. After scoring the board 10 or so times with a sharp knife then breaking apart the board, I cut the Veroboard to 21 by 10 holes.

Usually when doing stripboard, if I’m freehanding it (doing the layout as I read the schematic) then I prefer to ‘break’ the tracks after everything has been soldered. But since we have the layout to start with, we’ll break the tracks out first.

To do this, you can use a small 3-4mm drill bit, a knife or this purpose-built tool called a spot face cutter such as the TD2461 from Jaycar, which is what I’ll be using.

Here’s the pattern of what I did for those playing along at home. Just note that I have reversed the image for your convenience.

While there is no strict guideline in soldering components per se, I tend to have my own preference when soldering through-hole components, which has evolved over the years. Generally speaking, the order I like to do things is by: soldering the small passive parts, connectors and wiring, then progressing to smaller active parts, larger passives and finally large bulky items.

With that being said, let’s solder the 8pin IC socket so we can get a reference point for all the other components.

Next, let’s solder the resistors. I’ll leave the capacitors until after I solder in the wiring in this case since they are relatively tall for this project.

Now let’s get onto adding the wire connections. First what I like to do is strip a few millimetres off the end of one end of the wires and bend it so that it can be placed into the hole that I wish to start the wire at.

After placing the wire inside the desired hole, I count out the needed spacing, and then cut about 3-5mm after that. Once cut, I then made a small indentation in the insulation at the correct length and then used my wire strippers to strip back the insulation. If done correctly, we are then left with a perfect wire connection.

After completing the wires, I noticed that I was missing one resistor, so I added that in too. Once this had all been done, it was time to put in the tactile button. Technically, this switch and its accompanying resistor (R4) and wire are optional. Currently, all the button is doing is resetting the ATtiny85. However, this button is programmable, so you can do whatever you want or even just simply omit it.

As the pin spacing of the switch and the Veroboard don’t perfectly line up, you will need to bend the pins of the button inwards. You will be able to get it to fit as I have. Once this had been done, it’s time to install the three capacitors.

Just be careful to get the electrolytic the correct way around. After this, I soldered the LED and voltage regulator.

Just as a side note, we’ll later find that I installed the voltage regulator backwards. Anyway, let's continue by soldering in the PTC fuse.

After this, I installed the second largest part, the IRLZ44N MOSFET and then subsequently the two screw terminal connectors so we can connect our source and load.

With this done, it is time to solder the final connectors to the board.

With those soldered, we are now done with the construction. The next step would be to insert a flashed ATtiny85 (I’ll cover how we flash the software a little later). But it’s always a good idea to make sure the circuit is, in fact, giving us the correct voltages where we expect them.

Note: If you aren’t getting any voltage here make sure you are using a Polyfuse and haven’t accidentally used a MOV (I was originally given the wrong parts).

With 9V applied to the input and set to a low current limit in case anything went wrong, I measured the power pins on the IC socket to verify this was, in fact, 3.3V. This is where I learned that something was wrong with the voltage regulator – the voltage was around 8.5V.

At a first glance after quickly pulling up the datasheet of the regulator, all seemed fine. But after a closer inspection, we can see the pinout is given from the bottom and not the top. Pay close attention to how your datasheet is presented.
Note: This has since been corrected for the stripboard layout shown before.

After resoldering the regulator, the voltage measured 3.3V, as expected. With this checked, let's insert our ATtiny85 into its IC socket.

With that done, our board is now finished! It is worth mentioning that if you wish to deliver several amps with this board, you may want to add a layer of solder to the copper pads on the back of the board where the power runs through to ‘beef’ up the current-carrying capabilities. Alternatively, you can consider making a PCB version.

I encourage you to create your own PCB layout, but if you want to use mine, I’ve provided the Gerbers, which you can download from the DIYODE website. These should be good for several amps or more. I have also provided a bill-of-materials as well since the components are different to the stripboard build. This is also available on the website.

While originally the XT60 connectors were planned to be on the top side, due to a small mistake in the polarity of the plug, they ended up having to be soldered on the reverse. But after this small mishap, it occurred to me that the connectors act as a form of standoff preventing the bottom solder joins from touching the surface it was placed on.

The Code

Before getting into the nitty-gritty details, let me roughly explain what this code does from a high-level point of view. Initially after setting up the peripherals, that is, the ADC, MOSFET and LED pins, we continuously read the battery voltage and turn off the MOSFET if the battery voltage is too low. When we shut off the MOSFET, the ATtiny85 will then enter a low power sleep mode.

So, now you have a high-level view of this code, let’s dig into the nitty and gritty bits.

I wrote this code without using any libraries and working with the registers directly. When coding this way, the datasheet (found on our website) is your best friend and has all the information you need.

Like any C file, the first things to do are to include the required header files and define any macros that we’ll use later in the code. Since we’re only using the pins and a delay function, we’ll only include the following.

#include <avr/io.h>
#include <util/delay.h>
#define MOSFET 4
#define LED 1
Here I defined the MOSFET and LED pin in accordance with the pins we used on the board (PB4 and PB1). To use these pins, we first need to configure the data direction register (DDRx, or DDRB in this case). We then can turn the MOSFET pin on by setting the PORTx register.
//Configure Mosfet to output and turn on
DDRB |= (1 << LED)| (1 << MOSFET);
PORTB |= (1 << MOSFET);

The next step is to configure the ADC so that it can measure the battery voltage. First, we need to tell the AVR which pin we are using for the ADC since all IO pins can be used. To do this, we need to configure the internal multiplexer using the ADMUX register. We are using PB3 in single-ended mode along with the internal 2.56V voltage reference without external bypass capacitors. These settings can be found on pages 134 to 136 of the ATtiny85 datasheet. Once configured, we can go ahead and enable the ADC by setting the ADEN bit in the ADCSRA register.

// Configure ADC
ADMUX = (1 << REFS1) | (1 << REFS2) | (1 << 1) |
(1); // 2.56v Vref
ADCSRA |= (1 << ADEN);

With that all configured, we made a helper function to measure the voltage since it will be used in a couple of places. The first thing we must do is tell the ADC to start its conversion process. As the ADC takes a small amount of time to acquire a reading, we need to wait until the ADC is finished before reading the result from the ADCL and ADCH registers. Fortunately, once complete, the ADC clears the ADSC bit and so we just need to wait for that to be cleared.

Now that we have an unsigned 10-bit integer (0 to 1024) stored across 2 8bit registers the next step is to convert it to something a little more meaningful. Since I designed the resistor divider to map 25.2V to 2.56V, it means that a value of 1024 corresponds to 25.2V. Since this is a linear function, each digit has a value of 25.2/1024 = 25mV/digit. To get the voltage of the battery in millivolts, we just need to return the value multiplied by 25.

/* Returns the voltage of a battery in mV */
uint16_t measure_voltage() {
ADCSRA |= (1 << ADSC); //Start conversion
while ((ADCSRA & (1 << ADSC)) != 0)
;
uint16_t value = ADCL | (ADCH << 8);
// Read value
// Value from 0 - 1024 -> 0 - 25.2v, 0.025v/digit
//-> 25,200 max value, -> 25/digit
return value * 25;
}

The reason I’m using millivolts here instead of volts is that we get more resolution since we are using integers and not floats. Using floats doesn’t benefit us here at all and we’ll later use integer division to calculate the cell count.

This brings me to the next lot of code – the cell count detection and battery cut off set point. I am making the assumption that each cell will at least be 3.4V, and so we can just divide the battery voltage by 3400mV. Since we’re using integer division, we’ll get the floor of the result, which is the cell count. Once we have this, we can just multiply this by our cut off voltage, in this case 3200mV.

// Measure voltage at start up to estimate battery size
uint16_t batt_ref = measure_voltage();
uint8_t cell_count = batt_ref / 3400; // Estimate cell >= 3.4v
uint16_t min_voltage = cell_count * 3200; //Cutoff voltage

After this has all been set up, we can continuously measure the battery voltage and check to see if it’s lower than the voltage cut off point. While im at it, I’ll just use a for loop to flash the optional onboard LED the cell count of the battery measured. Having this inside the while loop means that it will stop flashing once the battery has been cut off.

uint16_t meas;
while (meas = measure_voltage(), meas >= min_voltage) {
for (uint8_t i = 0; i < cell_count; i++) {
PORTB |= (1 << LED);
_delay_ms(150);
PORTB &= ~(1 << LED);
_delay_ms(100);
}
_delay_ms(2000);
}

The final bit of code is to turn the MOSFET off once the battery voltage has dropped to below the cut off voltage. We’ll also turn off the ADC here too as it consumes a small amount of power (~200uA is what I measured) when it’s enabled. Finally, I put the ATtiny85 into a low power sleep state. The different sleep states can be found on page 34/38 of the datasheet.

// turn off mosfet, adc and sleep
PORTB &= ~(1 << MOSFET);
ADCSRA &= ~(1 << ADEN);
MCUCR |= (1 << SE) | (1 << SM1);

Now that you have an understanding of how the code works, let's upload the file to the ATtiny85. I originally created and uploaded this code to the ATtiny85 using Microchip Studio (formally Atmel studio). I like using Microchip Studio as it gives you a lot more flexibility such as setting the fuse bits in the MCU. But today, I’ll use the Arduino IDE as it may be more familiar and you may even have it installed.

First, let me go over how we are going to flash the AVR (our ATtiny85). There is a multitude of ways to program the ATtiny85, from using an AVR programmer to using an Arduino Uno! In 2018, Tim Blythman published a project back in Issue 14 detailing how to program one of these ATtiny85s with an Arduino. So, if you want to use an Arduino, be sure to check that out. However, I’m going to use a Pololu AVR programmer (Item 3172), but you could also use the ISP programmer from Jaycar (XC4627).

These AVR programmers have 6 connections. These are: VCC, Ground, Reset, MOSI , MISO and SCK. Each of these needs to be connected to the corresponding pins on the AVR. To simplify things in future, you can make an adapter board where the ATiny85 slots in. However, I’ll be using a breadboard with a 6pin IDC breakout board I made – you can also buy a more professional version as well. Here’s how I wired it up, note that the notch on the IDC cable is closest to the board and with the wire coming out of the top.

Once it's all wired up its time to flash the software. But before we can do that, we first need to download the ATiny85 definitions so that the Arduino IDE can compile our code (if you have done this previously on another occasion, you can skip this). First, you’ll need to add a URL into the Boards Manager so that it can download the ATtiny definitions. To do this, first navigate to the preferences page through the menu by selecting File > Preferences and it should bring you to a window that looks like this.

Next, open up the additional board manager urls dialog by pressing the button next to the URL textbox. It will bring up a menu like below where you will need to paste the flowing link in:
https://raw.githubusercontent.com/damellis/attiny/ide-1.6.x-boards-manager/package_damellis_attiny_index.json

Press Ok and Ok again to close the two dialog boxes. After telling the IDE where to search, we now need to download the actual definitions. Open the boards manager by going to Tools > Board > Boards Manager and search for “attiny” then press install.

After this completes, close the dialog box. Now you are ready to select the ATtiny85 as the platform that we wish to compile for. You will also want to make sure that the processor selected is the “ATtiny85” one, and that you are using the internal 1MHz (8MHz will also work, but not needed).

At this point, you can connect the AVR programmer (and ATtiny) to your computer and then select the correct port in the Arduino IDE.

Now, its time to upload the code, first copy and paste the following code into the IDE. Alternatively, you can download the file from my GitHub account: https://gist.github.com/matty0005

#include <avr/io.h>
#include <util/delay.h>
#define MOSFET 4
#define LED 1
/* Returns the voltage of a battery in mV */
uint16_t measure_voltage() {
ADCSRA |= (1 << ADSC); //Start conversion
while ((ADCSRA & (1 << ADSC)) != 0)
;
uint16_t value = ADCL | (ADCH << 8); // Read val
// Value from 0 - 1024 -> 0 - 25.2v, 0.025v/digit
// -> 25,200 max value, -> 25/digit
return value * 25;
}
int main(void) {
//Configure Mosfet to output and turn on
DDRB |= (1 << LED)| (1 << MOSFET);
PORTB |= (1 << MOSFET);
// Configure ADC
ADMUX = (1 << REFS1) | (1 << REFS2) | (1 << 1) | (1); // 2.56v Vref
ADCSRA |= (1 << ADEN);
// Measure voltage at start up to estimate
// battery size
uint16_t batt_ref = measure_voltage();
uint8_t cell_count = batt_ref / 3400;
// Estimate cell >= 3.4v
uint16_t min_voltage = cell_count * 3200;
//Cutoff voltage
uint16_t meas;
while (meas = measure_voltage(),
meas >= min_voltage) {
for (uint8_t i = 0; i < cell_count; i++) {
PORTB |= (1 << LED);
_delay_ms(150);
PORTB &= ~(1 << LED);
_delay_ms(100);
}
_delay_ms(2000);
}
// turn off mosfet, adc and sleep
PORTB &= ~(1 << MOSFET);
ADCSRA &= ~(1 << ADEN);
MCUCR |= (1 << SE) | (1 << SM1);
}

Finally, you can hit the upload button and if all of our wiring is correct, it will succeed and have flashed properly. Now that it has been flashed, it’s time to insert the ATtiny85 into the IC socket on our board. At this point, we have completed the project, so let's test it to make sure it all works.

Testing and Troubleshooting

Before we deploy it live on a battery, let’s first do some tests to validate the circuit works and has no errors. To do this, connect a bench power supply to the input and arbitrarily set it to 12.3V. The LED should blink three times if everything is in order, indicating a 3 cell battery. Setting a modest current limit, such as 50mA, is also a good idea in case something else goes wrong.

Note: We have a video on our website of this being demonstrated.

To confirm that the MOSFET was switched on, I connected a multimeter to the output of the board to see a voltage of 12.3V present on the output.

If the LED doesn’t turn on but you get a reading on the output, then it could be that the LED is broken. This is what happened to me (you may have noticed the LED change between the build and testing just described). I desoldered the LED and tested it on a bench power supply and found that no light was coming out of it. This could have been a dead LED to start with or was an IR photodiode (as there was no current drawn) that ended up in my red LED bin. Either way, after replacing it with a known good LED, the board started flashing.

If this still doesn’t work, it could be due to the ATiny85 not been flashed properly. If you suspect this is the case, check to make sure that the chip is getting power (either 3.3v or 5v will do). Some AVR programmers don’t actually provide power. Instead, they use the VCC connection to measure the voltage to automatically set the correct logic level voltage for communication.

If your ATtiny85 isn’t getting power, it’s worth mentioning again to make sure that you in fact bought a Polyfuse and not a MOV (metal oxide varistor). On first acquiring the parts, I found that I had been given MOVs and not PTCs. To be fair, it’s not a hard mistake to make since they look alike and both part numbers had “075” in them. A quick way to test is to put your multimeter on continuity or ohms mode and see if the PTC is a closed circuit. If it's open circuit, its likely a MOV.

Back to testing, as I lowered the voltage on the power supply from 12.3V, the voltage, of course, dropped on the output, but also cut off once the voltage reached around 9.6V. I captured this on the oscilloscope as well so you can see it a bit easier.

If your build cuts off at a slightly lower voltage like mine does every so often when testing, then it’s likely due to the fact that the code only measures the battery voltage every 2 to 3 seconds. So, what can happen is that it drops from something just above 9.6V to something below the threshold, for example 9.4V, between taking measurements. Usually, a battery won’t drain this fast, and should cut off closer to the 9.6V cut-off as set by the code.

To verify this, I powered the board with about 11V, and then reduced it to 9.7V and let it sit there. After some time and not shutting off, I lowered the voltage to a smidge under 9.6V, and sure enough a couple of seconds later it shut off.

If yours is a smidge out, it’s probably ok. It’s likely due to the tolerances in the resistors and ADC. Technically, we can account for this in the code with a ‘calibration offset’, which is how a lot of things are done in the industry. But this is overkill for our purposes. On the other hand, if yours is quite a way out, you may want to double check your connections and the resistor values (and that they are in the correct place).

I conducted a range of other tests at different input voltages to validate the cell count was always correctly determined. If your board incorrectly registers the battery cell count, then it could be simply a scaling factor, which you can correct in software. Remember that we used 3.4V (3400mV) for our cell count calculation, so if your voltage is just below some multiple of 3.4V, then it will identify it as having one less cell.

It’s also worth keeping in mind that when testing these boards, if you power the board and then increase the voltage so that it hits another battery category, it won’t register that new battery voltage. This is because we only do the cell count calculation once at power up.

Where to from here?

Why stop here? We’d love to see how you tweaked this design. You don’t have to stop here just because I did. This was created to meet my needs for a quick and simple Li-Po battery cut-off.

To give you some ideas to think about, perhaps you might want to adapt this to a different battery chemistry such as a 12V lead acid battery. Li-Po batteries aren’t the only type that can be damaged from over-discharging.

The button on the board is connected to PB5 which is the reset pin by default. There’s nothing stopping you from doing something else with the button. You could explore using the button to switch between battery chemistries or cell count. Perhaps a timer function would be useful to keep you from leaving things on for too long. You could even extend this design such that it serves as a timer.

Perhaps you’d like to take a completely different approach and measure the current flowing through the device, then turn it off when a specified current threshold is reached – similar to overcurrent protection. Alternatively, you might wish to draw a specific number of watt-hours from your battery.

Another improvement that could be made is to clean up the code, particularly around the LED flashing section.

Currently, I’m using a simple for loop, which, although functional, means that we can only measure when we’re not flashing. You could use timers and interrupts built into ATtiny85 to do the blinking without the need to take up CPU time to sleep for a couple hundred milliseconds. ■

Matt Gilpin

Engineering & Computer Science Student