Arabic text support in LCDs [LiquidCrystalArabic.h]

LiquidCrystalArabic [github link]

License
Version

This library allows Arduino-based boards to render Arabic text on Hitachi HD44780 LCDs (or compatible chipsets), managing its unique characteristics such as right-to-left writing, character shaping, and connection rules.


Description

Key features include support for all common Arabic characters and ligatures (e.g., Lam-Alef), right-to-left text alignment, and an easy-to-use API for integrating Arabic text into Arduino projects. It is designed for Arduino enthusiasts, educators, and developers who need to display Arabic text on LCD screens for projects like signage, information displays, or educational tools.

Contributions and feedback are always welcome.


Download:

Available on github: https://github.com/balawi28/LiquidCrystalArabic

Dependencies

This library comes in two versions, each tailored for specific hardware setups:

  1. LiquidCrystalArabic_I2C.h:

    • Designed for I2C LCD displays.
    • Extends the LiquidCrystal I2C library by Frank de Brabander.
    • Use this version if your LCD is connected via I2C.
  2. LiquidCrystalArabic.h:

    • Designed for normal (parallel) LCD displays.
    • Extends the LiquidCrystal library by Arduino, Adafruit.
    • Use this version if your LCD is connected via parallel pins.

Installation:

  • Install the required base library (LiquidCrystal I2C or LiquidCrystal) before using this library.
  • The base libraries can be installed via the Arduino Library Manager. If multiple libraries with the same name exist, ensure you select the correct one by verifying the author's name.

Features

  • Supports rendering Arabic text on LCD displays of different sizes (such as 16x2 or 20x4).
  • Allows writing Arabic String directly in the source code (e.g., lcd.printArabic("نص عربي")).
  • A custom-designed Arabic font that fits within the constraints of 8x5 pixels for each character.
  • Supports all common Arabic characters and ligatures (e.g., Lam-Alef (لا)).
  • Handles right-to-left text alignment and character shaping.
  • Easy-to-use API for integrating Arabic text into Arduino projects.
  • Lightweight and optimized for low-memory microcontrollers.
  • Available in two versions: I2C and normal (parallel) LCD displays.

Limitations

  • 8-Character Limit: HD44780-based LCDs typically support only 8 custom characters at a time (see the "Handling the 8-Character Limit" section below).

Usage

The printArabic function is the core method for displaying Arabic text on the LCD. It handles right-to-left text alignment, character shaping, and ligatures. Below is a detailed explanation of its parameters and usage:

Function Signature:

void printArabic(String text, bool isRomanized=false, bool useLigatures=true);

Arabic Text Input

The library supports direct Arabic text input in the Arduino IDE. You can write Arabic strings directly in your code, and the library will handle the rendering on the LCD. For example:

lcd.printArabic("اللغة العربية"); // Direct Arabic text input

Demo Usage Example for I2C:

#include "LiquidCrystalArabic_I2C.h"

LiquidCrystalArabic lcd(0x27, 16, 2);

void setup() {
    lcd.init();           
    lcd.backlight();
    lcd.printArabic("اللغة العربية");
}

void loop() {}

Displaying Predefined LCD Characters Alongside Arabic Text

You can still print Predefined LCD Characters through the lcd.print method that is available in the original LiquidCrystal library. For example:

#include "LiquidCrystalArabic_I2C.h"

LiquidCrystalArabic lcd(0x27, 16, 2);

void setup() {
    Serial.begin(9600);
    lcd.init();           
    lcd.backlight();
    lcd.printArabic("انجليزي"); // Direct Arabic text input
    lcd.setCursor(0, 0); // Set the cursor to the top left cell
    lcd.print("English"); // Print English text
}

void loop() {}

Romanized Arabic Fallback

If your IDE or compiler has issues with direct Arabic text (e.g., syntax or compilation errors), you can use Romanized Arabic as an alternative. The library includes a built-in mapping of Romanized characters to their corresponding Arabic Unicode values. Simply pass the Romanized text and set the isRomanized parameter to true:

For example:

#include "LiquidCrystalArabic_I2C.h"

LiquidCrystalArabic lcd(0x27, 16, 2);

void setup() {
    Serial.begin(9600);
    lcd.init();           
    lcd.backlight();
    lcd.printArabic("allGo alArbeo", true); // Check the "Romanized Arabic Mapping" section to understand this mapping.
}

void loop() {}

Romanized Arabic Mapping

The following mapping is used to convert Romanized characters to Arabic:

Romanized Character Arabic Letter
a ا
b ب
t ت
T ث
j ج
H ح
K خ
d د
Z ذ
r ر
z ز
s س
V ش
X ص
x ض
P ط
p ظ
A ع
G غ
f ف
q ق
k ك
l ل
m م
n ن
h ه
w و
e ي
c ء
W آ
u ى
U أ
O ؤ
Y ئ
M ـ (Kashida)
o ة
i إ

Public Methods

LiquidCrystalArabic(uint8_t lcd_addr, uint8_t lcd_cols, uint8_t lcd_rows)

Constructor: Initializes the LCD with the specified I2C address, number of columns, and rows.

  • Parameters:
    • lcd_addr: I2C address of the LCD.
    • lcd_cols: Number of columns in the LCD.
    • lcd_rows: Number of rows in the LCD.
void setCursorArabic(uint8_t x, uint8_t y)

Sets the cursor position for Arabic text, accounting for right-to-left writing.

  • Parameters:
    • x: Column position (0-based, from the right).
    • y: Row position (0-based).
void moveCursorToLeft()

Moves the cursor one position to the left, wrapping around to the next row if necessary. Useful for right-to-left text rendering.

void printArabic(String text, bool isRomanized = false, bool useLigatures = true)

Prints Arabic text on the LCD. Supports both direct Arabic text and Romanized Arabic input.

  • Parameters:
    • text: The text to display (Arabic or Romanized).
    • isRomanized: If true, treats the input as Romanized Arabic.
    • useLigatures: If true, handles Arabic ligatures (e.g., "لا") as single characters.

Handling the 8-Character Limit

LCD displays typically support only 8 custom characters at a time, which is a challenge for rendering Arabic text due to its many character forms, where each letter has different forms (e.g., "هـ" vs. "ـهـ" vs. "ـه" vs. "ه"). Here’s how the library makes the best use of this limitation:

Optimizations:

  1. Reuse Character Slots: If the same character form appears multiple times, it only occupies one slot in the LCD's limited memory.
  2. Ligature Handling: Ligatures such as "لا" (which combines ل and ا) are treated as a single character, consuming only one slot despite being two characters.
  3. Common Combinations: Frequent Arabic combinations like "ال" (which combines ا and ل) are treated as a single character.
  4. Leverage Already Defined Characters: Predefined characters (e.g., space) are printed without consuming additional custom character slots.
  5. User Control: Users can display large texts by splitting them into smaller sections, ensuring the 8-character limit isn’t exceeded in each print call.

Example:

For the text "بلادي بلا حروب وحصار", the library:

  1. Start with an empty character set (0/8 slots used).
  2. Process each letter:
Character Form Action Slot
بـ Initial form New character 1/8
ـلا Ligature Treated as one character 2/8
د Isolated form New character 3/8
ي Isolated form New character 4/8
Space - Already defined in LCD memory -
بـ Initial form Already defined in a previous step -
ـلا Ligature Already defined in a previous step -
حـ Initial form New character 5/8
ـر Final form New character 6/8
و Isolated form New character 7/8
ب Isolated form New character (different from initial form بـ) 8/8
Space - Already defined in LCD memory -
و Isolated form Already defined in a previous step -
حـ Initial form Already defined in a previous step -
  1. Stop at 8 characters: The next letters (ـصـ and ـا and ر) cannot be displayed because the 8-character limit is reached.
6 Likes

Cool. Simulates on wokwi.com too.

1 Like

This is interesting, I never thought about testing it there.
Thanks for the input.

Good job taking the whole maker thing a couple steps closer to the Middle East (and more) people. :heart:

1 Like

Wow!
Very interesting and well-documented library. I am glad to be the first to give this project a star on GitHub :slight_smile:
I think this code could be useful not only for LCD displays. I have a library for working with HUB75 LED matrices, which supports the output of national fonts. Currently it implements work with Cyrillic and Turkish fonts.
I would be interested in displaying Arabic on such a screen using your library. Can I use your code and font for this? Do you have other fonts, larger?

WBR,
Dmitry

1 Like

Thank you for your support.

Of course, you can use my library as you wish. Unfortunately, I don't have any other fonts, but you can use the font defined in the library—just make it larger.

Thank you

But where you find this font? Did you draw it yourself?

I designed it from scratch to custom-fit the 8*5 LCD cell size constraint.

1 Like

thanks for the great job
i've tested it & its great but i have problem when using it in the loop
i get random strings on the screen

lcd.setCursor(0, 0);
lcd.printArabic(" وقت تشغيل 2");
lcd.setCursor(0, 1);
lcd.printArabic(" وقت تشغيل 1");

thanks for help

another issue i've faced
when printing a string in arabic & when i want to print another one it starts from the last symbol of the old message

would you test this please

void loop() {
  if ((RoutinerunOnce1) == 0) {
    lcd.setCursor(0, 0);
    lcd.printArabic("  وقت تشغيل 2");
    delay(1000);
    lcd.setCursor(0, 0);
    lcd.printArabic("  وقت تشغيل 1");
    RoutinerunOnce1 = 1;
  }
}is there a way to refresh the library with every new print on lcd ?
thanks

This is a response to Post #10.

The difficulty with writing Arabic (right to left) inside Romanized programming code (left to right) is when the cursor must act "Arabic" inside the text and "Roman" outside the text.

I found that you MUST fill the 15-character row, so if your Arabic string is 10 characters, you must add 5 "blank" spaces to make the row print correctly.

#include "LiquidCrystalArabic_I2C.h" // https://github.com/balawi28/LiquidCrystalArabic
// #include <LiquidCrystal_I2C.h> // LiquidCrystal_I2C.h is called in LiquidCrystalArabic_I2C.h

LiquidCrystalArabic lcd(0x27, 16, 2);

void setup() {
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();

  lcd.setCursor(0, 0);
  lcd.printArabic("وقت تشغيل 2");
  lcd.printArabic("     "); // fill the first line up to 15 characters

  lcd.setCursor(0, 1);
  lcd.printArabic("وقت تشغيل 1");

  // lcd.printArabic("allGo alArbeo", true);
}

void loop() {}

I do not know, but I will guess, to write Arabic, you should create an array of each individual consonant (الحروف الساكنة) for example char a[0] = 'ة' and char a[1] = 'ن'then count through the array and display the individual consonant.

This is a response to Post #11... The "spaces" seem to make it work, but I believe if you put each consonant as one element in an array, and count through the array of the Arabic word, it will be easier to control.

#include "LiquidCrystalArabic_I2C.h" // https://github.com/balawi28/LiquidCrystalArabic
// #include <LiquidCrystal_I2C.h> // LiquidCrystal_I2C.h

LiquidCrystalArabic lcd(0x27, 16, 2);

int RoutinerunOnce1 = 0;

void setup() {
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();

  // lcd.setCursor(0, 0);
  // lcd.printArabic("وقت تشغيل 2");
  // lcd.printArabic("     "); // fill the first line up to 15 characters

  // lcd.setCursor(0, 1);
  // lcd.printArabic("وقت تشغيل 1");

  // lcd.printArabic("allGo alArbeo", true);
}

void loop() {
  if ((RoutinerunOnce1) == 0) {
    lcd.setCursor(0, 0);
    lcd.printArabic("وقت تشغيل 2");
    lcd.printArabic("   "); // fill the first line up to 15 characters
    delay(1000);
    lcd.setCursor(0, 0);
    lcd.printArabic("  وقت تشغيل 1");
    RoutinerunOnce1 = 1;
  }
}

thanks for your reply
would you please test this simple code & give me an example on what you suggest?

void loop() {
  if ((RoutinerunOnce1) == 0) {
    lcd.setCursor(0, 0);
    lcd.printArabic("  وقت تشغيل 2");
    delay(1000);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.printArabic("  وقت تشغيل 1");
    RoutinerunOnce1 = 1;
  }
}

See post #13. Not simple. It took some analysis.

its complicated as you said
so i will draw my own characters i think its simpler

thank you

is it so complicate for you - count the arabic letters in your text and fill the line up to 15 characters after that?

did you checked my problem ?
i can write characters but when i write another word it take place aftet the old one

I not quite understand. Where do you expect it to be?

As a reply to post #10:

First of all, I recommend using setCursorArabic instead of setCursor Method, because in your code, the two sentences are not on different lines and will keep overriding each other.

No need to add spaces in the text, as setCursorArabic should take care of the positioning.

This is the modified code, and it works fine for me:

#include "LiquidCrystalArabic_I2C.h"

LiquidCrystalArabic lcd(0x27, 16, 2);

void setup() {
    lcd.init();           
    lcd.backlight();
}

void loop() {
    lcd.setCursorArabic(0, 0);
    lcd.printArabic("وقت تشغيل 2");
    lcd.setCursorArabic(0, 1);
    lcd.printArabic("وقت تشغيل 1");
    delay(1000);
}

1 Like

As a reply to post #14:

Once again, if you use setCursorArabic, the issue will be resolved.

For example, this code alternates between the two texts every 1 second:

#include "LiquidCrystalArabic_I2C.h"

LiquidCrystalArabic lcd(0x27, 16, 2);

void setup() {
    lcd.init();           
    lcd.backlight();
}

void loop() {
    lcd.setCursorArabic(0, 0);
    lcd.printArabic("وقت تشغيل 2");
    delay(1000);
    lcd.clear();
    lcd.setCursorArabic(0, 0);
    lcd.printArabic("وقت تشغيل 1");
    delay(1000);
}
2 Likes