My I2C LCD library for the Electric Imp

The date month isn’t correct, need to look into that.

omri, mine is also a PCF8574 IO expander (LCM1602 IIC). jwehr, when I use your code I get a return of 0 and the backlight comes on but no text. When you say you pull up pins 1,2 and 3…on what device? The LCD or the IMP? I’m using the April breakout board and there is no pin 3. I’m starting to wonder if my LCD or backpack is defective. I ordered a couple of the units omri linked:

We will see if they work any better.

The date month isn't correct, need to look into that.
I think that d["month"] starts counting with 0

Well, I have progress. I have a cursor and a flashing box. But still no text. Here’s my latest code:

`/* Basic code to write text to New Haven LCD display via I2C */
// June 2013
// New Haven NHD-C0216CIZ-FSW-FBW-3V3 is I2C LCD display, 3V, 2 lines of 16 characters, $11 @ Digikey
// Resources:
// (datasheet)
// (example for a similar LCD)
// (app note for LCD controller used by New Haven, see font table on last pages)
// font table: (last pages)

class NewHaven_LCD {
// Data Members
i2cPort = null;
// This device has a manufacturer hardwired I2C address of 0b01111100=0x7C for write
// change the last bit to one, 0b01111101=0x7D for read.
i2c_writeAddress = 0x40;
i2c_readAddress = 0x41;

constructor( i2c_port ) {
    // example:   local myLCD = NewHaven_LCD(I2C_12);
    if(i2c_port == I2C_12)
        // Configure I2C bus on pins 1 & 2
        i2cPort = hardware.i2c12;
    else if(i2c_port == I2C_89)
        // Configure I2C bus on pins 8 & 9
        i2cPort = hardware.i2c89;
        server.log("Invalid I2C port " + i2c_port + " specified in NewHaven_LCD::constructor.");
    // initialize the LCD communications:
    server.log( "Initializing LCD with return value: " + init_LCD() + " (0 means successful I2C communication.)" );


function init_LCD() {        
    //    These initialization parameters follow page 11 of datasheet
    //    Datasheet first sets the function to 0x38= 8-bit bus, 2-line display, extension instruction mode,
    //    then immediately follows this by the 0x39 function. I can't see any reason to do this, so I removed the first function set.
    imp.sleep(0.01); // Wait 10 milliseconds
    local result = i2cPort.write(i2c_writeAddress, 
                                format("%c%c",0x00,     // Control byte: one instrution word will follow
                                0x38) );  // function: 8-bit bus, 2-line display, normal instruction mode.
    imp.sleep(0.01); // Wait a while,

    HD44780 Commands

Function Set: 8-bit, 1 Line, 5x7 Dots				0x30
Function Set: 8-bit, 2 Line, 5x7 Dots				0x38
Function Set: 4-bit, 1 Line, 5x7 Dots				0x20
Function Set: 4-bit, 2 Line, 5x7 Dots				0x28
Entry Mode	(Options are 0x4, 5, 6 and 7)	        0x06    Entry mode 6, cursor-moves-right.
Blank the display (without clearing)                0x08
(clearing display without clearing DDRAM content)
Restore the display (with cursor hidden)            0x0C
Turn on visible underline cursor				    0x0E
Turn on visible blinking-block cursor               0x0F
Shift entire display left						    0x18
Shift entire display right							0x1C
Move cursor left by one character					0x10
Move cursor right by one character					0x14
Clear Display (also clear DDRAM content)			0x01
Set DDRAM address or coursor position on display	0x80 + address*128 + address*
Set CGRAM address or set pointer to CGRAM location	0x40 + address**64 + address**

/* result = result | i2cPort.write(i2c_writeAddress,
format("%c%c%c%c%c%c%c%c",0x00, // Control byte: indicates many instruction bytes will follow.
0x14,0x71,0x5E,0x6D,0x0C,0x01,0x06) );
result = result | i2cPort.write(i2c_writeAddress,
format("%c%c%c%c%c%c%c%c",0x00, // Control byte: indicates many instruction bytes will follow.
0x10,0x71,0x5E,0x6D,0x0C,0x01,0x06) );

    imp.sleep(0.02); // Wait 10 milliseconds
    return result;


function clear_display() {
    return i2cPort.write(i2c_writeAddress, format("%c%c",0x80,     // Control byte: one instruction word will follow
                                                  0x01) );  // Clear display.

function goto_line1start() { // returns home to the start of the 1st line, without clearing.
    return i2cPort.write(i2c_writeAddress, format("%c%c",0x80,     // Control byte: one instruction word will follow
                                                  0x02) );  // set cursor to home position

function goto_line2start() { // returns to the start of the 2nd line, without clearing.
    return i2cPort.write(i2c_writeAddress, format("%c%c",0x80, // Control byte: one instruction word will follow
                                           0xc0) );  // set cursor to position 0x40, set command is 0x80. 0x80+0x40 = 0xC0

function show( text ) {
// Each line allows for 40 characters, of which only the first 16 show.        
return i2cPort.write(i2c_writeAddress, format("%c",0x40)+     // Control byte: data bytes follow, data is RAM data.
                                           text ); 

function get_date_string() {
    local d = date(); // the time() function would return seconds since 1970.
    return ( d["year"] + "/" + d["month"] + "/" + d["day"] + "  " +
             d["hour"] + ":" + d["min"] + ":" + d["sec"] + " UTC" );


imp.configure(“NewHaven_LCD test code”, [], []);

// uses i2c on pins 1, 2
local myLCD = NewHaven_LCD(I2C_12);
local counter = 0;

function bigLoop() {
counter = counter +1;

server.log( myLCD.get_date_string() + ", using impee " + hardware.getimpeeid() );

myLCD.goto_line1start(); "Hello world" );
myLCD.goto_line2start();"Is this thing on?");

imp.wakeup(5, bigLoop); // sleep for 10 seconds



Just curious, why are you using 0x40 as your write address? Shouldn’t it be 0x7C? Are you using a different screen?

I am using a serial LCD backpack I purchased and soldered to the LCD. The write address I know is correct. I can actually shift the flashing cursor to the right (for some reason, not left) and get a return of 0. I just can’t get any words to show. At least I’m making a little progress.

@sconner - I wrapped your code in a < code > </ code > block to make it more readable :slight_smile:

Thanks beardedinventor! I’m new to this stuff. I’ll remember that for the future!

No worries - just spreading the code tag love :slight_smile:

Well, I’ve tried two other serial LCD screens. Still with no luck. :frowning:

Here is my latest try with a different serial display I got from Ebay

This is the most basic code I could come up with. It returns a 0 but never displays anything. I’m about to give up.

local i2c_add = (0x27<<1);

function writeToDisplay(){
// Clear the display
server.log("LCD i2c write during clear was: ");

local data = format("%c%c%c%c%c%c",0x00,0x38,0x0E,0x0F,0x06,0x01);

hardware.i2c12.write(i2c_add, data);
server.log(hardware.i2c12.write(i2c_add, data));

hardware.i2c12.write(i2c_add, format("%c",0x40)+"blah0");
server.log("test 0");


imp.configure(“LCD”, [], []);


@sconner try this code

Power the LCD with 3.3 volts and not 5! (or, connect a zener diode to pull down the I2C bus to 3.3)

I have tried powering with 3.3 and with 5 using zeners. I’m pretty sure I’ve tried that code, but I’ll double check when I get home.

Yours works with my second LCD omri! Thanks! I’ll work with it and see if I can figure out what I was doing wrong. Thanks again!


I have Omri Bahumi’s code working just great, but the LiquidCrystal_I2C has a call for setting contrast. Forgive my ignorance, but how can this be added to this library?

Hi Omri,

How can display a Degree Character that I have saved in the LCD?

This is the code I used to create it:

` function createCustomChar(charMatrix = [], asciiCode = 0)
// Set one of the HD44780’s eight custom 5 x 8 user-definable characters

	if (asciiCode < 0 || asciiCode > 7) return
	if (charMatrix.len() == 0 || charMatrix == null) return
    server.log("charMatrix.len " + charMatrix.len());
	this._lcd.sendCommand(0x40 | (asciiCode << 3))

	for (local i = 0 ; i < 8 ; i++)
		this._lcd.send(charMatrix[i], 1);


This the actual creation of the character:
lcd.createCustomChar("0x7,0x5,0x5,0x7,0x0,0x0,0x0,0x0", 0)

Hello @omri

Do you know if your library support this LCD? -->