And now another interesting little project.
After seeing various online projects, especially based on the PIC microcontroller, I decided to make a charger for NiMH batteries driven by an ATtiny85.
Why we need a microcontroller to manage charging a battery? The answer is very simple … The most economic charger that on the market, regulate the charge based on a timer, that is if I charge a 2500mAh battery for example, they provide a charging current of 250mAh, keeping the charge for eg 14 hours (a part of the energy will be lost, so 10 hours would not be enough).
This way of charging NiMh or NiCd batteries is not very functional. It is a slow process, and does not guarantee a 100% accurate cell charge, as well as increasing the risk of overload.
“Smart” chargers however, charge batteries correctly, but besides being quite expensive, their usually charge current is not very high, leading to charging times still quite long.
So in addition to the curiosity and the desire to create something of my own, this project was born …
But lets proceed with order, starting with a video showing the finished running project:
Let us now try to understand the operating principle.
First nickel batteries require a constant current to be charged properly. So we used a transistor-based constant current source, derived from what we saw in a previous post, and so far nothing special, but how do we know when the battery is fully charged?
We need to constantly read the voltage of the battery being charged, and when there will be a maximum peak voltage followed by a sudden drop of about 20mV, the battery may be considered charged. This phenomenon (called Negative Delta V) is related to the chemistry of this type of battery, and allows to establish precisely the right moment to terminate charging.
Thus we see the circuit diagram:
The schematic is pretty simple. The darlington transistor Q1 (TIP127), through LED1 (which must be absolutely RED) and the resistance R3 from 0.3 Ω, creates a constant current flowing in the direction of the 6-cell battery (7.2V), while the Schottky diode 1N5822, serves to protect the entire circuit in the case that is lacking the input voltage with the battery connected. The voltage divider R4-R5, serves to bring to port 7 of the ATtiny (A1) the battery voltage decreased to about 1/3, in such a way that it can operate with voltages up to 15V (the analog input of the ATtiny can not exceed 5V). The transistor Q2, driven by the port 5 of the ATtiny (D0), serves as a switch to enable or disable the LED-1 and the transistor Q1.
It’s really important that LED1 is RED, and this is not a matter of aesthetics, but because the power output is given by the ratio between the voltage of the LED (the red LED is about 1.8 to 2.2 V) and resistance R3. Using a green LED, the reference voltage is raised, and consequently also the current rises (and not just a little bit).
The circuit above, can deliver about 1.5-2.5 A: you may have to do some testing with various types of red LED, or vary the resistance, which must be at least 2W. If you can not find resistance with so low values, you can use 3 or 4 1Ω resistors in parallel.
Another thing to remember, is the power dissipated by the TIP127, which can be up to 20 watt (depending on the current and the voltage difference between the input and the battery), then a heat sink need to be mounted properly. In my case was enough to use the aluminum enclosure.
Last note is the input voltage, which must not be too high otherwise you risk to burn the transistor, but must not be even too low, because otherwise the dropout induced by the various components makes too low the charging current. Can fit approximately a difference of about 5V between the input voltage and the nominal value of the battery. For example, with 6-cell a voltage of 12.2 V is recommended (maybe 12). Of course you can also charge less cells, by suitably adjusting the input voltage. For a cell, we should feed the device with a voltage of about 1.2 + 5 = 6.2 V (maybe 6 or 7).
Let us consider now the printed circuit board:
Note the two gems that were not visible in the schematic. The ICSP connector (ie the ability to update the firmware on the ATtiny – via UsbTinyISP programmer for example – and the debug connector, to be connected to a TTL converter in order to read the battery voltage during charging.
The J1 connector is a jumper that must be closed at all times, except when updating the firmware of Attiny85.
About firmware … The source code is as follows:
/* NiMh Charger 0.9 with AtTiny85 @ 1Mhz by Luca Soltoggio 10/03/2012 - 20/04/2012 Use negative deltaV to determine end of charge. Suitable for NiMh and NiCD battery pack. Default is for 6 cells 2500mAh. Need some hardware/software adjustment for changing current / cells number See http://arduinoelettronica.wordpress.com/ */ const int inputPin = A1; const int outputPin = 0; const int numReadings = 30; // number of analog read before checking battery status const int multi = 1614; // multi coefficent for obtaining millivolts from analogread long interval = 1000; // interval for pulse charging and analog read - don't change this long interval2 = 250; // pulse off interval - you can adjust power with this. Use 100 for 2-4Ah battery packs, 500 for 1-2Ah battery pack long interval2b=interval2; long previousMillis,currentMillis,currentMillis2,trickleMillis = 0; unsigned int readingtemp; unsigned int total = 0; unsigned int average,medium,maxmedium = 0; boolean executed,endcharge,trickle=false; // booleans for controlling various activities (for example "end of charge") unsigned int myarray [7]; // array for keeping last 7 readings int index,i = 0; void setup() { Serial.begin(9600); pinMode(0,OUTPUT); // Some readings for initial check for (i=0;i<10;i++) { readingtemp = analogRead(inputPin); total=total+readingtemp; } average = (((float)total / 1023.0) * (float)multi) / 10.0 + 0.5; if (average<=70) endcharge=true; // If there is no battery, end charge Serial.println(average); total=0; average=0; } void pusharray() { // push the array for (i=0;i<=5;++i) { myarray[i]=myarray[i+1]; } myarray[6]=average; } void voltread() { readingtemp = analogRead(inputPin); // read analog input total= total + readingtemp; index++; // if numReadings reached, calculate the average if (index==numReadings) { index=0; average = (((float)total / 1023.0) * (float)multi) / numReadings + 0.5; if (average<=70) endcharge=true; // stop charge if battery is detached total=0; pusharray(); // insert new average in array medium=(float)(myarray[6]+myarray[5]+myarray[4]+myarray[3]+myarray[2]+myarray[1]+myarray[0])/7.0+0.5; // calculate the average of the last 7 readings if (medium>maxmedium) maxmedium=medium; // save the value of highest medium in "maxmedium" Serial.print(medium); Serial.print(","); Serial.print(maxmedium); Serial.print(","); Serial.println(myarray[6]); if ( ((medium+1) < maxmedium) && ((millis()/60000)>=11) ) { // if battery charged (average voltage is dropped 0.02v), but not in the firsts 11 mintues if (!trickle) trickleMillis=millis(); // start trickle timer trickle=true; // enter final trickle charging mode if ((millis()/60000)<=15) endcharge=true; // if battery is charged in the firts 15 minutes, don't apply trickle charge (maybe was yet charged) } } } void loop() { currentMillis = millis(); // executed every "interval" millis if(currentMillis - previousMillis > interval) { voltread(); // call reading and check volts function digitalWrite(outputPin,LOW); // temporaly stop charging previousMillis = currentMillis; executed=false; // boolean for setting and checking if has been yet turned ON charge // in the firsts 10 minutes and in the endings 15 minutes do a trickle charge (change OFF interval) if ( ( (trickle) && (((millis()-trickleMillis)/60000)<15) ) || ((millis()/60000)<10) ) { interval2=(interval-(interval-interval2b)/5); } else if ((millis()/60000)>=10) interval2=interval2b; // after initial trickle charghe set back right time if ( (trickle) && (((millis()-trickleMillis)/60000)>=15) ) endcharge=true; // if final trickle charge end, end charge } currentMillis2 = millis(); // executed "interval2" millis after turning OFF charge if ((currentMillis2 - previousMillis > interval2) && (!executed)) { executed=true; if (!endcharge) { digitalWrite(outputPin,HIGH); // if battery is not charged, re-enable charging } } }
The code is pretty simple and self-explanatory.
I conclude with a few pictures of the finished project:
Find my project on Fritzing:
http://fritzing.org/projects/smart-nimh-battery-power-charger-with-attiny85/
Until next time!