Python based library for talking to character based LCDs.
Switch branches/tags
Nothing to show
Clone or download
OrBin and dhylands Fixed to match the up-to-date MicroPython API (Pi…
…n.low() and Pin.high() were removed in v1.4.6) (#16)

Looks good to me - Thanks
Latest commit 92e5d02 Jun 7, 2018

lcd_api and i2c_lcd

Python code for talking to HD44780 compatible character based dot matrix LCDs.

This code is synchronous. Peter Hinch put together an async driver for the HD77480 over here.

You can communicate with the LCDs using either 4 or 8 GPIO pins.

Alternatively, the I2C classes implement 8-bit GPIO expander boards PCF8574 and MCP23008 which reduces the number of required GPIO pins to two (SCL, SDA). The boards usually mount behind the LCDs and are commonly called "backpacks".

The most commonly used display is a "1602" or "16x2", which features 16 columns and 2 rows of characters. There are also other LCDs using the same HD44780 controller. eg. 8x2, 16x1, 16x4, 20x2, 20x4, 40x1, 40x2. Each come in various backlight and pixel colours.

There are also variants of the code for MicroPython. All of the files which start with pyb_ were tested on the pyboard. Files starting with esp8266_ were tested on a WeMos D1 Mini. Files starting with nodemcu_ were tested on a NodeMCU development board. The files containing adafruit_lcd were tested on an Adafruit I2C / SPI character LCD backpack


File Description ESP32 GPIO HAL ESP32 test using 4-bit GPIO ESP8266 PCF8574 I2C HAL ESP8266 test using PCF8574 backpack Linux PCF8574 I2C HAL Linux test using PCF8574 backpack Core logic NodeMCU GPIO HAL NodeMCU test using 4-bit GPIO Pyboard GPIO HAL Pyboard test using 8-bit GPIO Pyboard test using 4-bit GPIO Pyboard MCP23008 I2C HAL Pyboard test using Adafruit backpack Pyboard Grove I2C RGB LCD HAL Pyboard test using Grove I2C RGB LCD Pyboard PCF8574 I2C HAL Pyboard test using PCF8574 backpack

The files which end in are examples which show how the corresponding file is used. was tested on a BeagleBone Black using a 2 x 16 LCD with an I2C module similar to this one.

This code was adapted from some C code that I had written previously for the AVR.

To install on your BBB:

git clone
cd python_lcd
sudo pip install -e .

And to test:

sudo lcd/

Since my LCD was a 5v device, I used a level converter to convert from BBB's 3.3v to the LCD's 5v.

I put together some [photos here] (

Coming from the BeagleBone Black the wire colors are:

Color  Pin   Name
------ ----- ------
Black  P9-1  GND
Red    P9-3  3.3v
Orange P9-7  SYS_5V
Yellow P9-19 SCL
White  P9-20 SDA

The photo shows Orange connected to P9-5. I discovered that P9-7 is controlled by the onboard voltage regulators, so when you do a "sudo poweroff" then SYS_5V drops to 0v when the BBB is powered off. P9-5 (VDD_5V) remains at 5v after the BBB is powered off.

And the colors going to the LCD are:

Color  Name
------ ----
Black  GND
Red    5v
White  SDA
Yellow SCL

I used a SparkFun level shifter (this particular model is no longer available).

Some examples of other level shifters which could be used:

I found a circuit mentioned in this Google+ post and thought I would include it here, since it's related to the LCDs these drivers interface with. LCD Schematic

The circuit allows for digitally controlling the contrast via PWM and also controlling the backlight brightness via PWM.

Custom characters

The HD44780 displays come with 3 possible CGROM font sets. Japanese, European and Custom. Test which you have using:


If you see Pi (π), you have a Japanese A00 ROM. If you see a division sign (÷), you have a European A02 ROM.

Characters match ASCII characters in range 32-127 (0x20-0x7F) with a few exceptions:

  • 0x5C is a Yen symbol instead of backslash
  • 0x7E is a right arrow instead of tilde
  • 0x7F is a left arrow instead of delete

Only the ASCII characters are common between the two ROMs 32-125 (0x20-0x7D) Refer to the HD44780 datasheet for the table of characters.

The first 8 characters are CGRAM or character-generator RAM. You can specify any pattern for these characters.

To design a custom character, start by drawing a 5x8 grid. I use dots and hashes as it's a lot easier to read than 1s and 0s. Draw pixels by replacing dots with hashes. Where possible, leave the bottom row unpopulated as it may be occupied by the underline cursor.

Happy Face (where .=0, #=1)

To convert this into a bytearray for the custom_char() method, you need to add each row of 5 pixels to least significant bits of a byte (the right side).

Happy Face (where .=0, #=1)
..... == 0b00000 == 0x00
.#.#. == 0b01010 == 0x0A
..... == 0b00000 == 0x00
..#.. == 0b00100 == 0x04
..... == 0b00000 == 0x00
#...# == 0b10001 == 0x11
.###. == 0b01110 == 0x0E
..... == 0b00000 == 0x00

Next, add each byte from top to bottom to a new byte array and pass to custom_char() with location 0-7.

happy_face = bytearray([0x00,0x0A,0x00,0x04,0x00,0x11,0x0E,0x00])
lcd.custom_char(0, happy_face)

custom_char() does not print anything to the display. It only updates the CGRAM. To display the custom characters, use putchar() with chr(0) through chr(7).


Characters are displayed by reference. Once you have printed a custom character to the lcd, you can overwrite the custom character and all visible instances will also update. This is useful for drawing animations and graphs, as you only need to print the characters once and then can simply modify the custom characters in CGRAM.


# smiley faces
happy = bytearray([0x00,0x0A,0x00,0x04,0x00,0x11,0x0E,0x00])
sad = bytearray([0x00,0x0A,0x00,0x04,0x00,0x0E,0x11,0x00])
grin = bytearray([0x00,0x00,0x0A,0x00,0x1F,0x11,0x0E,0x00])
shock = bytearray([0x0A,0x00,0x04,0x00,0x0E,0x11,0x11,0x0E])
meh = bytearray([0x00,0x0A,0x00,0x04,0x00,0x1F,0x00,0x00])
angry = bytearray([0x11,0x0A,0x11,0x04,0x00,0x0E,0x11,0x00])
tongue = bytearray([0x00,0x0A,0x00,0x04,0x00,0x1F,0x05,0x02])

# icons
bell = bytearray([0x04,0x0e,0x0e,0x0e,0x1f,0x00,0x04,0x00])
note = bytearray([0x02,0x03,0x02,0x0e,0x1e,0x0c,0x00,0x00])
clock = bytearray([0x00,0x0e,0x15,0x17,0x11,0x0e,0x00,0x00])
heart = bytearray([0x00,0x0a,0x1f,0x1f,0x0e,0x04,0x00,0x00])
duck = bytearray([0x00,0x0c,0x1d,0x0f,0x0f,0x06,0x00,0x00])
check = bytearray([0x00,0x01,0x03,0x16,0x1c,0x08,0x00,0x00])
cross = bytearray([0x00,0x1b,0x0e,0x04,0x0e,0x1b,0x00,0x00])
retarrow = bytearray([0x01,0x01,0x05,0x09,0x1f,0x08,0x04,0x00])

# battery icons
battery0 = bytearray([0x0E,0x1B,0x11,0x11,0x11,0x11,0x11,0x1F]))  # 0% Empty
battery1 = bytearray([0x0E,0x1B,0x11,0x11,0x11,0x11,0x1F,0x1F]))  # 16%
battery2 = bytearray([0x0E,0x1B,0x11,0x11,0x11,0x1F,0x1F,0x1F]))  # 33%
battery3 = bytearray([0x0E,0x1B,0x11,0x11,0x1F,0x1F,0x1F,0x1F]))  # 50%
battery4 = bytearray([0x0E,0x1B,0x11,0x1F,0x1F,0x1F,0x1F,0x1F]))  # 66%
battery5 = bytearray([0x0E,0x1B,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F]))  # 83%
battery6 = bytearray([0x0E,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F]))  # 100% Full
battery7 = bytearray([0x0E,0x1F,0x1B,0x1B,0x1B,0x1F,0x1B,0x1F]))  # ! Error