13 December 2016

Smart Battery charger


Smart Battery charger with ATtiny13 

Constant current charger for rechargable batteries and trickle charger for Alkalines.

After a lot of experimentation this is an improved design of the alkaline battery trickle charger presented in a previous post. The new design charges alkaline batteries with fixed peak, variable duty cycle current pulses and automatic shut down. In addition, one charging mode can be used to charge NiMh or NiCd batteries with constant current. Actually, I was somewhat disappointed from the alkaline batteries charging results (lots of leaking), so I though that I should find a better use for my hardware.

This is the schematic of the improved charger.

Apart from the code the difference from the previous circuit comes from the way the T1, T2 transistors are placed. Once T1 is turned on by the μC it provides current (around 10mA) to the LED1. Voltage drop on this LED is constant and depends on its color. (1.6V for RED diode, 1.9V for green, 3V for white).

The transistor T2 is connected as a constant current source. The current through T2's emitter (which is almost equal to collector) is  Ie = (Vled-Veb)/R2. So for a given LED and resistor this current, effectively the charging current, is practically constant and independent of battery voltage and other circuit elements. For instance, for a white LED and a 100ohm resistor, the current during charging pulse will be Ic ≃ Ie = (3-0.6)/100, approximately 24 mA. So by keeping the "charging pin" of the Attiny13 permanently high you have a constant current charger that can be used for NiCd or NiMH batteries.
The code for this version of the charger has been changed so that it can facilitate the new feature.
Obviously, you can easily adapt the code to fit your own needs and experimentation. 


More ideas - enhancements
By changing the resistor R2 or LED1 color, the current peak value may be altered acording to the formula above. This efect can also be achived by adding a resistor in the circuit (parallel or in series) that can be sort-circuited with a switch. In this way a manualy selectable constant current charger is impemented.
If you need to charge batteries higher than 4.5V (e.g 9V NiCd, NiMH) then voltage measurement should be taken through a voltage divider. Attiny pins can not tolerate voltage higher than 5.5V and the chip may be damaged. In this case the variable "chargeVoltage" values in the sketch should be changed to the proportion set by the divider. I suggest using two high value equal 1% resistors (e.g. 100Kohm or not less than 22Kohm) so that the calcated values are just divided by two. Note that these resistors discharge the battary slowly, if it is left on the charger.
Since there are still two of the at13's pins left free, one more Led (LED3) can be added and used for somewhat friendlier user interface. A red Led is attached to pin 7. Pin 1 is the Reset pin of the ATtiny so you don't want to use this, unless you want to be in the business of AVR FUSE fiddling and high voltage programmers...
You can see the design with all the extras mentioned here in the following schematic.
Code can be copied from here:


#include <avr/io.h>
#include <avr/interrupt.h>

const uint8_t  greenLed = 0;        // PB0 -> at13 pin 5
const uint8_t  redLed = 2;          // PB2 -> pin 7
const uint8_t  chargePin = 3;       // pin 2
const uint8_t  buttonPin = 1;       // pin 6 INT0
const uint8_t  sensorPin = 2;       // at13 pin 3 -> Analog 2
const uint16_t  Charged = 310;      //(1.5/5)*1024;      ~330 -> fully charged 1.6Volt (2 Cell = 655)
uint16_t cellVoltage = 0;
uint8_t offTime;
uint8_t onTime;
volatile uint8_t mode = 2;
volatile boolean noChange;

void pulsePin(uint8_t pin, uint8_t t1, uint8_t t2) {
  digitalWrite(pin, HIGH);
  delay(t1);
  //if(t2!=0){}
  digitalWrite(pin, LOW);
  delay(t2);
}

void selectTimings(uint8_t m) {
  switch (m) {
    case 1:
      onTime = 60;
      offTime = 140;         //30%
      break;
    case 2:
      onTime = 100;
      offTime = 100;         //50%
      break;
    case 3:
      onTime = 150;
      offTime = 50;         //75%
      break;
  }
}

void displayMode(uint8_t m) {
  digitalWrite(greenLed, LOW);
  delay(100);
  for (uint8_t i = 0; i < m; i++) {
    pulsePin(greenLed, 20, 20);
  }
  delay(50);
}

ISR(INT0_vect) {
  cli();
  mode += 1;
  if (mode > 4) {
    mode = 0;
  }
  noChange = FALSE;
  sei();
}

void setup() {
  pinMode(buttonPin, INPUT);
  pinMode(chargePin, OUTPUT);
  pinMode(greenLed, OUTPUT);
  pinMode(redLed, OUTPUT);
  digitalWrite(chargePin, LOW);
  MCUCR = 1 << ISC01;             // set INT0 as falling edge trigger
  GIMSK = 1 << INT0;              // enable INTO in global interrupt mask
  sei();                          // enable interrupt
}

void loop()
{ // ERROR - battery not connected OR below 0.8V OR detected voltage is near 5V limit
  cellVoltage = analogRead(sensorPin);
  digitalWrite(redLed, HIGH);
  while ((cellVoltage < (Charged / 2)) || (cellVoltage > 1000)) {   // if error wait, blinking fast
    pulsePin(greenLed, 5, 5);
    cellVoltage = analogRead(sensorPin); //pin 3
  }
  digitalWrite(redLed, LOW);
  noChange = TRUE;
  selectTimings(mode);            // select timings depended on mode
  displayMode(mode);              // blink green led to indicate mode 1,2,3

  if (mode == 4) {
    digitalWrite(chargePin, HIGH);
  }
  else if (mode == 0) {
    digitalWrite(chargePin, LOW);
  }
  // -------------- Charging LOOP
  while (noChange) {
    if (mode == 0) {
      pulsePin(greenLed, 100, 10);
    }
    else {
      cellVoltage = analogRead(2);                                        // Control checks
      if ((cellVoltage < (Charged / 2)) || (cellVoltage > 1000)) {        // if error -> stop
        mode = 0;
        noChange =FALSE;
      }
      else if (mode == 4) {
        if (cellVoltage > 285) {            // mode 4 checking Vbat > (1.4/5)*1024
          digitalWrite(greenLed, HIGH);     // 1.4V indication, no automatic shutdown
        }
      }
      else {                                // mode 1, 2, 3
        if (cellVoltage > Charged) {    
          digitalWrite(greenLed, HIGH);       // indicate battery charged
          mode = 5;                           // enter non manual mode
          offTime = 970;                      // sustain battery charge with very slow charge
          onTime = 30;
        }
        else if (mode == 5) {                 // if charged bat voltage dropped then charge again
          if (cellVoltage < Charged - 3) {
            digitalWrite(greenLed, LOW);
            mode = 2;
            noChange = FALSE;
          }
        }
        pulsePin(chargePin, onTime, offTime);   // trickle Charge modes 1,2,3,5
      }                                       // end charging control checkings
    }                                         // end charging LOOP
  }
}
 
I've also designed the PCB for this circuit. You can can download Fritzing file here. (Not actually built...  yet)
The pcb design can be used for the previous (no extras) version as well with the following alterations: R8, LED4, S2, R1 are left out. R9, Led2 are replaced with a jumber. LED1 (or/and LED2)  is replaced with any color diode you prefer.

2 comments:

  1. Hello, what is/was the usage of S1 (is not used in the code)?

    ReplyDelete
    Replies
    1. Hi,
      I'm pleasantly surprised I received a comment after so long, for this project. Thanks for your time.
      Well, S1 is actually a push button connected to AT13's pin 6 for changing the duty cycle. So, in the code you find "buttonPin = 1", PB1 is the IC pin 8. S1 is not used as a variable name...
      Please read my previous post on the same subject, perhaps you will find it helpful.

      Delete