LCD bargraph not displaying correctly

Hi,

I'm trying to use the code from this blog post:
http://www.electronicsblog.net/arduino-lcd-horizontal-progress-bar-using-custom-characters/

...with an I2C LCD, using the LiquidCrystal_I2C library. I've updated the sketch to use the newer library, and have a 5K pot attached. The vertical bars display incorrectly - compare the image on the blog to mine, attached here.

What is different about the Liquid Crystal I2C library vs. the original Liquid Crystal library, that would cause this? And how do I fix it?

Jeff

//http://www.electronicsblog.net
//Arduino LCD horizontal progress bar using custom characters

//#include <LiquidCrystal.h>

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address

#define length 16.0

double percent=100.0;
unsigned char b;
unsigned int peace;

// custom characters

// LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
byte p1[8] = {
  B0001,
  B0001,
  B0001,
  B0001,
  B0001,
  B0001,
  B0001,
  B0001};

byte p2[8] = {
  B11000,
  B11000,
  B11000,
  B11000,
  B11000,
  B11000,
  B11000,
  B11000};

byte p3[8] = {
  B11100,
  B11100,
  B11100,
  B11100,
  B11100,
  B11100,
  B11100,
  B11100};

byte p4[8] = {
  B11110,
  B11110,
  B11110,
  B11110,
  B11110,
  B11110,
  B11110,
  B11110};

byte p5[8] = {
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111};

void setup()   {

  delay(100);
  lcd.createChar(0, p1);
  lcd.createChar(1, p2);
  lcd.createChar(2, p3);
  lcd.createChar(3, p4);
  lcd.createChar(4, p5);

  lcd.begin(16, 2);

}

void loop()
{
  lcd.setCursor(0,0);

  //ADC conversion

  unsigned int value = analogRead(0);

  percent = value/1024.0*100.0;

  lcd.print(value);

  lcd.print(" - ");

  lcd.print(percent);
  lcd.print(" %   ");

  lcd.setCursor(0,1);

  double a=length/100*percent;

  // drawing black rectangles on LCD

  if (a>=1) {

    for (int i=1;i<a;i++) {
      lcd.write(4);
      b=i;
    }

    a=a-b;

  }

  peace=a*5;

  // drawing character's colums

  switch (peace) {

  case 0:

    break;

  case 1:
    lcd.print((char)0);
    break;

  case 2:
    lcd.write(1);
    break;

  case 3:
    lcd.write(2);
    break;

  case 4:
    lcd.write(3);
    break;

  }

  //clearing line
  for (int i =0;i<(length-b);i++) {
    lcd.print(" ");
  }
  ;
}

LCD bars.png

What is your LCD model number?

It's version 1 on this page:
http://arduino-info.wikispaces.com/LCD-Blue-I2C

The model number of your LCD is irrelevant.

B0001 is not the same as 0x10. Try B10000.

Don

Don - Thanks for catching that typo with the binary number; I had converted the original code from hex to binary to make it easier to visualize the images used in the custom characters.

However, fixing the typo has no effect on the missing lines in the bargraph display.

When I turn up the contrast on the display, and look closer, I can see that only every other line is being drawn. It's the odd-numbered ones...lines 1,3,5, and 7.

It's the odd-numbered ones...lines 1,3,5, and 7.

Yeah, that's why I wanted the model number, so I could look at the datasheet.

Hi Jeff,
I've just tried that code and go the same result, but there again I only got my i2c LCD working yesterday, thank's to bparrybab's i2cLCDguesser sketch. The barograph looks quite smart, Can't see much wrong in your code, but again I'm only a learner at C, used picaxe and basic in the past. Here's a picture of my LCD and the result from Bill's guesser sketch!! same as your's....

Hi arduinodlb,
I don't think it has much to do with the LCD itself, as most HD477.. based LCD's have the same pinout/function, but there are at least 2 or 3 versions of the small i2c expander backpack that fits on the back of the LCD. In fact I have another one winging it way from China right now, and it looks slightly different to the one I have now. There's a good chance that the LCD pins will be mapped to the chip differently??
I'll keep an eye on this one, best of luck.
Regards

Mel.

PS. Take a look here: http://arduino-info.wikispaces.com/LCD-Blue-I2C

Hi Cactus,

No, I'm not saying it has anything necessarily to do with the LCD model. But, the code in general looks fine, and it was obvious that it's missing rows in the User Generated Characters, so I needed a datasheet as a starting point to see how Character Generation works on these LCDs in order to start looking into what the problem is. I don't use LCDs myself, so I just wanted a datasheet as a starting point for looking into this problem.

So, a couple of things:

  1. Can you please provide a link to the exact library you are using?
    I had a quick look at this one https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads/LiquidCrystal_V1.2.1.zip, but I'm not sure that's the one you're actually using.

  2. I know the demo does it this way

  lcd.createChar(0, p1);
  lcd.createChar(1, p2);
  lcd.createChar(2, p3);
  lcd.createChar(3, p4);
  lcd.createChar(4, p5);

  lcd.begin(16, 2);

but looking at the library above, it seems to me that the begin() should come before the createChar() lines. That probably isn't your problem though.

  lcd.begin(16, 2);

  lcd.createChar(0, p1);
  lcd.createChar(1, p2);
  lcd.createChar(2, p3);
  lcd.createChar(3, p4);
  lcd.createChar(4, p5);

In the library I looked at, here's the CreateChar function

void LCD::createChar(uint8_t location, uint8_t charmap[]) 
{
   location &= 0x7;            // we only have 8 locations 0-7
   
   command(LCD_SETCGRAMADDR | (location << 3));
   delayMicroseconds(30);
   
   for (int i=0; i<8; i++) 
   {
      write(charmap[i]);      // call the virtual write method
      delayMicroseconds(40);
   }
}

It's possible that the delayMicroseconds(40); is too short, thus skipping every second line. I would try increasing that to 100 to see if that has any effect.

That being said, as I say, I don't know what library you're using, and I don't have an LCD myself.

I think I found it. Seems to be a difference between the old LiquidCrystal and LiquidCrystal_I2C.

Original code from blog:

void setup()   {

  delay(100);
  lcd.createChar(0, p1);
  lcd.createChar(1, p2);
  lcd.createChar(2, p3);
  lcd.createChar(3, p4);
  lcd.createChar(4, p5);

  lcd.begin(16, 2);

}

I moved lcd.begin(16,2); up so it is executed before lcd.createChar set of commands, like so:

void setup()   {

  delay(100);
  lcd.begin(16, 2);      // moved to here, works with LiquidCrystal_I2C
  lcd.createChar(0, p1);
  lcd.createChar(1, p2);
  lcd.createChar(2, p3);
  lcd.createChar(3, p4);
  lcd.createChar(4, p5);

//   lcd.begin(16, 2);

}

And we cross-posted! It seems to be the solution.

What I had found was that another sketch with custom characters was working, but this one didn't seem to have its own characters uploading...I was seeing characters from the other sketch. Comparing the two solved it.

Actually,
That code with the begin() after the createChar() calls is wrong.

begin() must be the first thing that is done before attempting to use other LiquidCrystal library
API calls.
It is pure luck that it worked at all (actually it didn't) with the begin() after the createChar().

The reason that begin() must be first
is that the Arduino environment doesn't properly initialize itself before
the object constructors are called.
Because of that, the constructors cannot always talk to hardware to do all the h/w initialization
because things like interrupts are not properly set up.

To work around this, a begin() function was added to libraries.
The object constructors save away all the parameters and then
the begin() function is where the hardware is actually initialzed.
begin() is called from setup() which called much later in the boot/startup process
and by that time all needed MCU and Arduino initializations have taken place.
If you attempt to talk to the device before you call begin() then
the results are un-predictable and are very likely to not work as desired.

While the LiquidCrystal library Tutorial and its references pages don't explicity
state that begin() must be called before any of the other LiquidCrystal methods,
if it isn't called first, the sketch will be attempting to use un initialized hardware.

The LiquidCrystal library should reject any attempts to talk to the LCD before proper initialization is done,
but as of today, this isn't done by libraries and that is why the results are unpredictable.

In the case of the LiquidCrystal i2c interface, attempting to talk to the LCD before
calling begin() will result in the lower level code trying to talk to the LCD
before the Wire library (i2c) is setup and before the PCF8574 chip is initialized.

The simple solution as you have seen is simple do the begin() first thing
as is required.

--- bill

Bill,

Thanks for the detailed info. The example LiquidCrystal Library - Custom Characters sketch that installed with the SDK shows:

void setup() {
  // create a new character
  lcd.createChar(0, heart);
  // create a new character
  lcd.createChar(1, smiley);
  // create a new character
  lcd.createChar(2, frownie);
  // create a new character
  lcd.createChar(3, armsDown);  
  // create a new character
  lcd.createChar(4, armsUp);  

  // set up the lcd's number of columns and rows: 
  lcd.begin(16, 2);
  // Print a message to the lcd.
  lcd.print("I "); 
  lcd.write(0);
  lcd.print(" Arduino! ");
  lcd.write(1);

}

So you're saying this is incorrect? Shouldn't it be updated?

Jeff

Yes, it is wrong and should be updated.
I entered a bug report for it. Here is the bug report link:

If you look at the LiquidCrystal library code, (LiquidCrystal.cpp)
the constructors call the function init()
init() saves away which pins are used and sets up some internal variables.
It does not initialize the LCD command/data interface or the LCD.

The begin() function intializes the LCD command/data interface
and initializes the LCD into a known state, as well as clearing the display.
The most important thing that is done during LCD initialization is to go
through a sequence to get the host (Arduino in this case)
in sync with the LCD with respect to the command/data interface.
This is very important when using 4 bit mode because if you are out of sync
then the LCD will end up seeing the command/data nibbles scrambled and then nothing
will work correctly.

I'll go look through the other LiquidCrystal examples to see if there are
any other similar errors.

--- bill

Hi Bill, Jeff and others,
Just looked at your comments, then all I had to do was to move the a begin() to the start of the void setup loop and it now works fine.... Another problem solved by those who know!!

Here's a picture...

Thanks again,
Regards
Mel.

Mel,

Glad it's working for you! I only got my first LCD earlier this week, so it's all new to me too. But at this point I think I've explored most of the features...

  • How to hook it up and figure out the libraries.
  • Why you need to print blank spaces to "erase" previous characters.
  • How to define and use custom characters, for double-sized letters or bar graphs.

Those seem to be most of what you can do. And I found a bug in the demos in the process!

Jeff

The sketch works but I don't understand some of the code. Everything after if (a>=1) {. Can anyone explain each line of the code?