Go Down

Topic: Display battery state of charge graphic on LCD (Read 445 times) previous topic - next topic

dtokez

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

HazardsMind

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.
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

dtokez

#2
Oct 06, 2013, 02:14 am Last Edit: Oct 06, 2013, 02:16 am by dtokez Reason: 1
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?

Code: [Select]
int analoginput = A1;
int soc = 0;
float vin = 0.0;

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

void loop(){
 vin = analogRead(analoginput)*5.09/1024*3.15;
 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);
}





HazardsMind

#3
Oct 06, 2013, 02:52 am Last Edit: Oct 06, 2013, 03:12 am by HazardsMind Reason: 1
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.
Code: [Select]
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.
Quote
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).
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

dtokez

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 :)

Code: [Select]
int analoginput = A1;
int soc = 0;

float vin = 0.0;


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



void loop(){
  vin = analogRead(analoginput)*5.09/1024*3.15;
  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);
}


dtokez

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 :
Code: [Select]
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);
   }

   }


HazardsMind

#6
Oct 06, 2013, 05:03 am Last Edit: Oct 06, 2013, 05:05 am by HazardsMind Reason: 1
You could use the Createchar function.

This is my method for my bar graph.
Code: [Select]

// 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()
{
  bargraph(analogRead(A6),1,20);
  //bargraph(analogRead(A6),3,20);
}

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.
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

dtokez

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

dtokez

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 :)

Go Up