# Display battery state of charge graphic on LCD

Hi all. I would like to display the state of charge of a 3 cell lipo pack on a lcd. I would like to use one of the lines to give me 16 steps of resolution, corresponding to a voltage range of 9.3-12.6v.

And I think i will need to map 9.3-12.6 to give a range of 0-16, then ideally I would like to make some custom chars, and have them display a image of a battery with both solid and empty sections. Reading a voltage is not a problem, and I can make custom characters.

The part I will need help with is how to code this up, would I have to use an array to display the chars?

An example would be when the voltage is <9.3v, to display something similar to the following..

|________________|

hope that makes sense, thanks

What kind of display? 16x2, or an actual TFT 3.2" display?

You can use the same method I made here. LCD Bargraph You can create your custom characters, and display them as so.

Hi, thanks for the reply. I forgot to say I'm using a 16x2 LCD in parallel mode.

Thanks, I have just read through that thread, It is a similar concept to some test code that I have just written.

I have tested it and it doesn't work as I had hoped, the SOC only displays 0, 5, 10 or 15 and no increments inbetween?

``````int analoginput = A1;
int soc = 0;
float vin = 0.0;

void setup(){
Serial.begin(9600);
delay(100);
}

void loop(){
soc = map(vin, 9.3, 12.6, 0, 15);
//soc = constrain(soc, 0, 15);
Serial.print(vin, 3);
Serial.print("  ");
Serial.println(soc);
delay(1400);
}
``````

Your minimum and maximum numbers are too small. Multiply vin by 10 and then typecast it as an INT, so instead of 9.3 and 12.6, it will 93 and 126.

``````int number = 93;

void setup()
{
Serial.begin(9600);

while( number < 127)
{
Serial.print(number);
Serial.print(" mapped: ");
Serial.println(map(number, 93, 126, 0, 15));
number ++;
delay(100);
}
}

void loop() {

}
``````

This code produces this: Nice and smooth.

93.00 mapped: 0
94.00 mapped: 0
95.00 mapped: 0
96.00 mapped: 1
97.00 mapped: 1
98.00 mapped: 2
99.00 mapped: 2
100.00 mapped: 3
101.00 mapped: 3
102.00 mapped: 4
103.00 mapped: 4
104.00 mapped: 5
105.00 mapped: 5
106.00 mapped: 5
107.00 mapped: 6
108.00 mapped: 6
109.00 mapped: 7
110.00 mapped: 7
111.00 mapped: 8
112.00 mapped: 8
113.00 mapped: 9
114.00 mapped: 9
115.00 mapped: 10
116.00 mapped: 10
117.00 mapped: 10
118.00 mapped: 11
119.00 mapped: 11
120.00 mapped: 12
121.00 mapped: 12
122.00 mapped: 13
123.00 mapped: 13
124.00 mapped: 14
125.00 mapped: 14
126.00 mapped: 15

whereas the other only showed (0, 5, 10, 15).

Thanks! I took me a while to grasp what you were saying but it makes sense now. I thought the values might get scaled up or something.

This works now.. time to start working out the LCD bit

``````int analoginput = A1;
int soc = 0;

float vin = 0.0;

void setup(){
Serial.begin(9600);
delay(100);
}

void loop(){
soc = map((vin*10), 93, 126, 0, 15);
soc = max(soc, 0);

Serial.print("Vin: ");
Serial.print(vin);
Serial.print("V  SOC: ");
Serial.println(soc);

delay(1400);
}
``````

Result, I have got it doing exactly what I wanted it to do. but the code is very over the top, clumsy and probably stupidly inefficient - how could it be optimized?

The whole code is to long to post but this gives you an idea of the section i need to improve :

``````soc = map(((vin*10) + 2), (underVoltage*10), 126, 0, 16);  // Scale up number and add small offset, then map to 1-16
soc = max(soc, 0);
soc = min(soc, 16);

if (soc == 0)
{
lcd.setCursor(0, 1);

lcd.write((uint8_t)3);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)5);
}

if (soc == 1)
{
lcd.setCursor(0, 1);

lcd.write((uint8_t)7);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)5);
}

if (soc == 2)
{
lcd.setCursor(0, 1);

lcd.write((uint8_t)7);
lcd.write((uint8_t)7);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)5);
}

if (soc == 3)
{
lcd.setCursor(0, 1);

lcd.write((uint8_t)7);
lcd.write((uint8_t)7);
lcd.write((uint8_t)7);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)5);
}

if (soc == 4)
{
lcd.setCursor(0, 1);

lcd.write((uint8_t)7);
lcd.write((uint8_t)7);
lcd.write((uint8_t)7);
lcd.write((uint8_t)7);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)5);
}

if (soc == 5)
{
lcd.setCursor(0, 1);

lcd.write((uint8_t)7);
lcd.write((uint8_t)7);
lcd.write((uint8_t)7);
lcd.write((uint8_t)7);
lcd.write((uint8_t)7);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)4);
lcd.write((uint8_t)5);
}

}
``````

You could use the Createchar function.

This is my method for my bar graph.

``````// Simple LCD Bar Graph
// By: Andrew Mascolo

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
uint8_t bar0[8]  = {
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0};
uint8_t bar1[8]  = {
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10};
uint8_t bar2[8]  = {
0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18};
uint8_t bar3[8]  = {
0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C};
uint8_t bar4[8]  = {
0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E};
uint8_t bar5[8]  = {
0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F};
int i,j=0, number=1;

LiquidCrystal_I2C lcd(0x20,20,4);  // set the LCD address to 0x20 for a 16 chars and 2 line display

void setup()
{
Serial.begin(9600);
lcd.init();                      // initialize the lcd
lcd.backlight();
lcd.createChar(0, bar0);
lcd.createChar(1, bar1);
lcd.createChar(2, bar2);
lcd.createChar(3, bar3);
lcd.createChar(4, bar4);
lcd.createChar(5, bar5);
lcd.home();
lcd.print("Hello world...");
delay(1000);
}

void loop()
{
}

void bargraph(unsigned int data, unsigned int row, unsigned int lcd_size)
{
j = map(data, 0, 1023, 0, (6 * lcd_size));
if(number <= j)
{
for(number; number < j; number++)
{
i = number / 6;
lcd.setCursor(i,row);
lcd.write(number % 6);
}
}
else
{
for(number; number > j; number--)
{
i = number / 6;
lcd.setCursor(i,row);
lcd.write(number % 6);
}
}
//Serial.print(number);
// Serial.print(" ");
//Serial.println(i);
}
``````

You can modify it however you like.

thanks, I will load it up later and try and work out how you have done it

Just thought of a more efficient way that I could adjust my code, I could draw a empty battery on the LCD then just write over it with the segments that need to be filled in, should make the code a little smaller!

I will still be trying Hazards method though