# Simple circuit for lithium battery charge meter

I wanted a simple meter using 4 LEDs to display the charge of a lithium battery.  My project used a single battery(1S), so it needed to display voltages between 3 - 4.2 Volts.  If you need to measure multiple batteries in seris you should be able to modify the circuit by changing the voltage divider resistors, and regulate the voltage to the MCU.  I used an AVR ATTINY26L because I had them, but you can use any MCU that can handle a supply voltage from a little less than 3 volts to 4.5V.

### Components

• 1x Atmel ATTINY26L
• 4x LEDs or LED array
• 2x 1k resistors (for resistive divider to ADC)
• 4x resistors for LEDs. See LED resistor calculator
• 1x 0.1uF to 1uF larger capacitor (for bypass capacitor)
• 1x push button switch (optional)

### Circuit ### STK-500 configuration for programing

• ISP6PIN to SPROG1
• PORTE RST to PORTB PB7
• PORTE XT1 to PORTB PB4
• ATTINY26L in socket SCKT3700A1
• PORTB PB0 to LED0
• PORTB PB1 to LED1
• PORTB PB2 to LED2
• PORTB PB3 to LED3
• PORTA PA0 to Vin

### Fuses

Oddly internal oscillator was enabled already on my MCU, but you may need to enable it.

• default for avrdude is "safemode: Fuses OK (E:FF, H:F7, L:E1)"
• Set low fuse bits to 0xE1 for internal oscillator
• avrdude commandline flags "-U lfuse:w:0xE1:m"
• See http://eleccelerator.com/fusecalc/

### Problems with circuit

As the battery voltage decreases the brightness decreases. To fix this you could enable Vref on the MCU.  Attach the LED + Resistors between Vref and the PORTB pins, then invert variables in the code.  I probable whould have done this.

### Code

```/*  * Lithium battery level meter  *  * Created: ZyMOS 04/2020  *  * Microcontroller  *     ATTiny26  *  * Resistor divider to ADC Channel 0  *    R1/R2 = 1/2  *  * Internal RC Oscilator 1MHz  *    Fuse bits  *         CLKSEL[3:0] = 0001  *        PLLCK = 1 ?  *        Note: For all fuses “1” means unprogrammed while “0” means programmed.  *  * Leds output  *     PB0-PB3  *  */```

```#include <stdlib.h> #include <avr/io.h> #include <avr/pgmspace.h> #include <util/delay.h>```

```// Global uint16_t battery_level;```

```#define std_delay 35 #define F_CPU 12000000UL```

```// ADC void adc_init(void){```

```    ADMUX |=     (0 << ADLAR)| // Set left shift result                 (1 << REFS1)| // Set Vref to internal (2.56V), pin out not connected                 (0 << REFS0); // Set Vref to internal (2.56V), pin out not connected         // Int Ref, pin not connected```

```    ADCSR |=     (1 << ADEN)| // Enable ADC                 (1 << ADPS2)| // Set prescaler to 128 (111)                 (1 << ADPS1)| // Set prescaler to  128 (111)                 (1 << ADPS0);  // Set prescaler to  128 (111) }```

```// ADC read uint16_t adc_read(uint8_t ch) {    // ADC = Vin/Vref * 1024```

```    //Select ADC Channel ch must be 0-7    /* ch=0b00001001; */        ADMUX &= 0xF0;     ADMUX|=ch;```

```   //Start Single conversion    ADCSR |= (1<<ADSC);```

```   //Wait for conversion to complete    while(!(ADCSR & (1<<ADIF)));```

```   //Clear ADIF by writing one to it    ADCSR |= (1<<ADIF);```

```   return(ADC); }```

```// ADC average uint16_t adc_average(uint8_t adc_channel) {     uint16_t result=0;     uint16_t result2=0;```

```        uint8_t adc_samples=50;    //times 2         uint8_t adc_delay=50; //us         uint8_t z;    ```

```        // average first half         for(z=0;z<adc_samples;z++){              result = result + adc_read(adc_channel); // Read Analog value from channel-0             _delay_us(adc_delay);         }         result = result / adc_samples;```

```        // average second half         for(z=0;z<adc_samples;z++){              result2 = result2 + adc_read(adc_channel); // Read Analog value from channel-0             _delay_us(adc_delay);         }         result2 = result2 / adc_samples;```

```        // average 2 halfs         result = result2 + result;         result =  result / 2;```

`        return(result);`

`}`

```/* void set_battery_leds(uint16_t battery_level){ */     /* PORTB &= 0b11110000; */     /* if(battery_level > 100){ */         /* PORTB |= 0b00010000; */     /* } */ /* } */```

```void led_test(void){     PORTB = 0b00000000;     _delay_ms(std_delay);     PORTB = 0b00001000;     _delay_ms(std_delay);     PORTB = 0b00000100;     _delay_ms(std_delay);     PORTB = 0b00000010;     _delay_ms(std_delay);     PORTB = 0b00000001;     _delay_ms(std_delay);     PORTB = 0b00000010;     _delay_ms(std_delay);     PORTB = 0b00000100;     _delay_ms(std_delay);     PORTB = 0b00001000;     _delay_ms(std_delay);     PORTB = 0b00000000;     _delay_ms(std_delay);```

`}`

```void display_led_value(uint16_t value){          /* Value = Rgain * Vbatt/Vref + 1024 */```

```    // Full = 4.2     // Empty = 3     // Resistor gain = 1/2     // LEDs = 0 : <3.1V      :         value <= 620     // LEDs = 1 : 3.1 - 3.3V : 620 < value <= 660     // LEDs = 2 : 3.3 - 3.6V : 660 < value <= 720     // LEDs = 3 : 3.6 - 3.9V : 720 < value <= 780     // LEDs = 4 : >3.9V      : 780 < value```

```    // 2.19V = 780 (876)     // 2.012 = 720 (804)     // 1.855 = 660 (742)     // 1.741 = 620 (696)          /* Theoretical */     /* uint16_t value0=620; */     /* uint16_t value1=660; */     /* uint16_t value2=720; */     /* uint16_t value3=780; */```

```    /* Actual */     uint16_t value0=560;     uint16_t value1=594;     uint16_t value2=630;     uint16_t value3=702;```

```    _delay_ms(std_delay*10);          if(value <= value0){         PORTB = 0b00000000;         _delay_ms(std_delay);```

```    }else if(value > value0 && value <= value1){         PORTB = 0b00000000;         _delay_ms(std_delay);         PORTB = 0b00001000;         _delay_ms(std_delay);```

```    }else if(value > value1 && value <= value2){         PORTB = 0b00000000;         _delay_ms(std_delay);         PORTB = 0b00001000;         _delay_ms(std_delay);         PORTB = 0b00001100;         _delay_ms(std_delay);```

```    }else if(value > value2 && value <= value3 ){         PORTB = 0b00000000;         _delay_ms(std_delay);         PORTB = 0b00001000;         _delay_ms(std_delay);         PORTB = 0b00001100;         _delay_ms(std_delay);         PORTB = 0b00001110;         _delay_ms(std_delay);```

```    }else if(value > value3){         /* PORTB = 0b00000000; */         /* _delay_ms(std_delay); */         /* PORTB = 0b00001000; */         /* _delay_ms(std_delay); */         /* PORTB = 0b00001100; */         /* _delay_ms(std_delay); */         /* PORTB = 0b00001110; */         /* _delay_ms(std_delay); */         PORTB = 0b00001111;         _delay_ms(std_delay);```

`    }`

`    /* _delay_ms(std_delay); */`

`}`

```int main(void) {```

```    /* DDRA &= ~(1 << DDA7);  // PD0 is now an input */     /* PORTA |= (1 << PORTA7);   // turn On the Pull-up enabled */     // port A7, input with pullup resistor```

`    /* std_delay=50 */`

```    /* uint8_t stabilize_count=0; */     /* uint8_t stabilize_delay=5; */          uint8_t adc_channel = 0;```

```    /* initialize display, cursor off */     adc_init();```

```    // Set led outputs     DDRB |= (1 << DDB0) | (1 << DDB1) | (1 << DDB2)| (1 << DDB3);          // startup delay     _delay_ms(std_delay);          // Startup led display     led_test();```

`    while(1) {`

```        /* uint16_t x;         */         /* for ( x = 0; x < 600; x++ ) { //loop 60 sec */```

```            // loop delay             /* _delay_ms(std_delay); */                      // get the battery level from adc             battery_level = adc_average(adc_channel);                                      display_led_value(battery_level);             /* set_battery_leds(battery_level); */```

`        /* } //loop once a min */`

```    } // infinite loop } //main```