LCD custom character in char array

Hey Guys.

I have a bunch of special characters defined for my LCD (HD44780 16x2).
For various reasons I am trying to display them not using lcd.write(), but concatenating them into one char array and display using just lcd.print().

I use below code to do that:

// include the library code:
#include <LiquidCrystal.h>

// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = 9, en = 10, d4 = 11, d5 = A1, d6 = A2, d7 = A3;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

// make some custom characters:
byte heart[8] = {
  0b00000,
  0b01010,
  0b11111,
  0b11111,
  0b11111,
  0b01110,
  0b00100,
  0b00000
};

byte smiley[8] = {
  0b00000,
  0b00000,
  0b01010,
  0b00000,
  0b00000,
  0b10001,
  0b01110,
  0b00000
};

byte frownie[8] = {
  0b00000,
  0b00000,
  0b01010,
  0b00000,
  0b00000,
  0b00000,
  0b01110,
  0b10001
};

byte armsDown[8] = {
  0b00100,
  0b01010,
  0b00100,
  0b00100,
  0b01110,
  0b10101,
  0b00100,
  0b01010
};

byte armsUp[8] = {
  0b00100,
  0b01010,
  0b00100,
  0b10101,
  0b01110,
  0b00100,
  0b00100,
  0b01010
};

byte mychar3[8]  = {
    B00001, 
    B00010, 
    B10110, 
    B11001, 
    B10001, 
    B10001, 
    B10001,
    B00000
};

void setup() {
  // initialize LCD and set up the number of columns and rows:
  Serial.begin(9600);
  Serial.println("======RESTART=======");
  lcd.begin(16, 2);

  lcd.createChar(0, heart);
  lcd.createChar(1, smiley);
  lcd.createChar(2, mychar3);
  lcd.createChar(3, armsDown);
  lcd.createChar(4, armsUp);

  // set the cursor to the top left
  lcd.setCursor(0, 0);

  // Print a message to the lcd.
  lcd.print("I ");
  lcd.write(byte(0)); // when calling lcd.write() '0' must be cast as a byte
  lcd.print(" Arduino! ");
  lcd.write((byte)1);
  Serial.println("End setup");
  delay(2000);
}

void loop() {
  char buf[17];
  strcpy(buf,"Test \x01\x02 ca:");
  lcd.setCursor(0,0);
  lcd.clear();
  lcd.print("Hello World");
  lcd.setCursor(0,1);
  lcd.print(buf);
  
  delay(5000);
}

This executes nice and results are as expected. LCD displays string with my characters.
Problems starting when the special character is a part of a word and is not separated by space.
Below example does not work as expected:

// include the library code:
#include <LiquidCrystal.h>

// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = 9, en = 10, d4 = 11, d5 = A1, d6 = A2, d7 = A3;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

// make some custom characters:
byte heart[8] = {
  0b00000,
  0b01010,
  0b11111,
  0b11111,
  0b11111,
  0b01110,
  0b00100,
  0b00000
};

byte smiley[8] = {
  0b00000,
  0b00000,
  0b01010,
  0b00000,
  0b00000,
  0b10001,
  0b01110,
  0b00000
};

byte frownie[8] = {
  0b00000,
  0b00000,
  0b01010,
  0b00000,
  0b00000,
  0b00000,
  0b01110,
  0b10001
};

byte armsDown[8] = {
  0b00100,
  0b01010,
  0b00100,
  0b00100,
  0b01110,
  0b10101,
  0b00100,
  0b01010
};

byte armsUp[8] = {
  0b00100,
  0b01010,
  0b00100,
  0b10101,
  0b01110,
  0b00100,
  0b00100,
  0b01010
};

byte mychar3[8]  = {
    B00001, 
    B00010, 
    B10110, 
    B11001, 
    B10001, 
    B10001, 
    B10001,
    B00000
};

void setup() {
  // initialize LCD and set up the number of columns and rows:
  Serial.begin(9600);
  Serial.println("======RESTART=======");
  lcd.begin(16, 2);

  lcd.createChar(0, heart);
  lcd.createChar(1, smiley);
  lcd.createChar(2, mychar3);
  lcd.createChar(3, armsDown);
  lcd.createChar(4, armsUp);

  // set the cursor to the top left
  lcd.setCursor(0, 0);

  // Print a message to the lcd.
  lcd.print("I ");
  lcd.write(byte(0)); // when calling lcd.write() '0' must be cast as a byte
  lcd.print(" Arduino! ");
  lcd.write((byte)1);
  Serial.println("End setup");
  delay(2000);
}

void loop() {
  char buf[17];
  \\strcpy(buf,"Test \x01\x02 ca:"); //this is working fine
  strcpy(buf,"Test \x01\x02ca:"); //removed space after \x02 - LCD shows garbage instead of \x02 special character.
  lcd.setCursor(0,0);
  lcd.clear();
  lcd.print("Hello World");
  lcd.setCursor(0,1);
  lcd.print(buf);
  
  delay(5000);
}

It seems like added byte with special character is mixed with next char in array and produces garbage…

Kindly please help.

c and a are hex digits. I suspect that the compiler is interpreting \x02ca as a single hex number.

Try printing the ca portion separately.

 strcpy(buf,"Test \x01\x02""ca:");

Edit: This is necessary any time the next character you want to print is a hex digit (0..9 A..F).

I see someone just posted that you can separate the text into two separate strings, the compiler will automatically concatenate adjacent quoted text.

Since the LCD can have eight special characters, and you are only using five, skip number 0 and start with 1 instead, then you don't have to use .write()

wildbill:
c and a are hex digits. I suspect that the compiler is interpreting \x02ca as a single hex number.

Try printing the ca portion separately.

You are right!

pcbbc:

 strcpy(buf,"Test \x01\x02""ca:");

Edit: This is necessary any time the next character you want to print is a hex digit (0..9 A..F).

This is working fine!
Great guys. Thanks a lot!

I would octal literals instead. Hex isn't needed for this.
Octal works well for the custom characters.
Since they are 0-7, the values of the digits will look a lot like decimal values.
By using octal literals you can embed them into the string and even put them in the AVR proprietary PROGMEM if you want.

The hd44780 library includes an example (LCDCustomChars) that shows examples of how to use octal literals.
With octal literals you can use 3, 2, or 1 digits depending on what is following the literal.
i.e. if there are no digits following the literal you can use just a single digit.
If you are concerned about accidental misparsing like what happened with the hex literal, just use all 3 octal digits.

Here are some examples:

lcd.print("\1\2ca:"); // can be used since no digit follows
lcd.print("\01\02ca:"); // can be used since no digit follows
lcd.print("\001\002ca:"); // all 3 digits can always be used since it guarantees no misparsing.

lcd.print(F("\1\2ca:")); 

strcpy(buf,"Test \1\2ca:");
strcpy(buf,"Test \01\02ca:");
strcpy(buf,"Test \001\002ca:");

--- bill