Weird "custom" character

So I’ve just learnt about custom characters for a 16x2 LCD and even managed to write my name in custom “cursive” characters . But now, when try to use a new set of custom chars I made, they just display as 4 horizontal lines inside a char.

Here is the code:

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);

byte customChar0[8] = {
  0b01010,
  0b00100,
  0b01110,
  0b00001,
  0b01111,
  0b10001,
  0b01111,
  0b00000
};

byte customChar1[8] = {
  0b00100,
  0b01010,
  0b01110,
  0b00001,
  0b01111,
  0b10001,
  0b01111,
  0b00000
};
byte customChar2[8] = {
  0b00100,
  0b01010,
  0b01100,
  0b00100,
  0b00100,
  0b00100,
  0b01110,
  0b00000
};
byte customChar3[8] = {
  0b00000,
  0b00000,
  0b01110,
  0b10000,
  0b01110,
  0b00001,
  0b11110,
  0b00100
};
byte customChar4[8] = {
  0b01000,
  0b01000,
  0b11100,
  0b01000,
  0b01000,
  0b01001,
  0b00110,
  0b00000
};

void setup() {
  // put your setup code here, to run once:

  lcd.createChar(0, customChar0);
  lcd.createChar(1, customChar1);
  lcd.createChar(2, customChar2);
  lcd.createChar(3, customChar3);
  lcd.createChar(4, customChar4);

  lcd.clear();
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.write(byte(0));
  lcd.setCursor(1, 0);
  lcd.write(byte(1));
  lcd.setCursor(2, 0);
  lcd.write(byte(2));
  lcd.setCursor(3, 0);
  lcd.write(byte(3));
  lcd.setCursor(4, 0);
  lcd.write(byte(4));


}

void loop() {
  // put your main code here, to run repeatedly:

}

What did I do wrong?
BTW, the code s for a 16x2 with I2C.
EDIT: If I run the cursive letters code first and then this one, some characters remain from it but the others are still 4 horizontal lines.

Thanks.

Guys, I've figured it out!
For some reason, the setup part has to be "reversed" so it looks like this:

void setup() {
  lcd.init();
lcd.backlight();
  lcd.createChar(0, customChar0);
  lcd.createChar(1, customChar1);
  lcd.createChar(2, customChar2);
  lcd.createChar(3, customChar3);
  lcd.setCursor(0,0);
  lcd.write(byte(0));
  lcd.setCursor(1,0);
  lcd.write(byte(1));
  lcd.setCursor(2,0);
  lcd.write(byte(2));
  lcd.setCursor(3,0);
  lcd.write(byte(3));
}

I have no idea why, but this might help some of you :wink:

I have no idea why

This has been explained before by Bill (bperrybap) but I'm not sure where.

If I remember correctly its basically because you have to get the library functioning before you can have it save your custom characters.

EDIT: There are other issues involving custom characters and I'm pretty sure Bill will be here soon to explain them to you.

Don

The reason is, you have to send commands to the LCD to program the custom characters.
But you can't send commands to the LCD until the LCD is properly initialized to 4 bit mode.
With the LiquidCrystal_I2C library being used, that doesn't happen until init() is called.

It is very unfortunate that the LiquidCrystal library example here:
https://www.arduino.cc/en/Reference/LiquidCrystalCreateChar
is a really bad example.

Normally begin() initializes the h/w for many different Arduino libraries.
In the case of the IDE liquidCrystal library the constructor initializes the LCD to to 4/8 bit mode and into a default 16x1 mode. Calling begin() re-initializes the LCD and sets the library up for the proper cols and rows.
If you are wondering why the createChar() is before begin() in that example, it because if those lines were reversed, the example would fail to work. (for a different issue)

This relates to what Don mentioned about having some issues with createChar(). The IDE bundled LiquidCrystal library along with pretty much every hd44780 LCD library out there (except the hd44780 library) has what I'd call a "bug". The createChar() functions leaves the display in CGRAM programming mode when it returns. This means that if you do a print() or a write() after programming a character, it won't write the characters to the display, it will write the data to CGRAM instead of DDRAM display ram. Which means it corrupts custom characters rather than displaying on the LCD.
The way to work around this is to do something that puts the LCD back into DDRAM mode.
That is call clear(), home(), or setCursor() after programming a character before you want to display something.
The hd44780 library does not have this issue.

Now if you are wondering why a library like LiquidCrystal_I2C can't also initialize the LCD the same way in its constructor, the reason is that library needs another library to communicate with the device. (Wire in this case) and constructors for each library are not guaranteed to be called in any order. So the Wire library constructor may not be called prior to the LiquidCrystal_I2C (or any other library that uses Wire) is called.
Also, it needs to use interrupts and not everything is properly set up yet to use all the Arduino services.
If you try to use Wire in a constructor the Arduino processor will hang.
Because of this the LCD and Wire library initialization is deferred until begin() (or init() for that library) is called.
It is actually better to do it there vs the way the IDE LiquidCrystal library does it since it also avoids double initialization like what is happening in the LiquidCrystal library.

It does mean that you can't talk to the LCD at all until the LCD has been initialized by calling begin() or init() in that library. But if you think about it, it makes sense that the LCD should be initialized before you try to talk to it or configure it.

If you install the hd44780 library (available in the IDE library manager).
It includes some custom character examples that demonstrate how to program custom characters and various ways to use them.
The i/o class for an PCF8574 i/o expander based hd44780 LCD backpack is hd44780_I2Cexp.

The hd44780 library also supports using AVR progmem if you want to keep your custom character data from eating up RAM.

--- bill

Wow, Bill, thanks a very lot for taking your time to explain this!