i2c lcd plug and play success with hd44780.h

Bill-- I wanted to give some positive feedback on the hd44780.h library.

In another thread I was working with an inexperienced person who wanted to add an hd44780 i2c display module to their project.

Getting power and the i2c connections correct is simple, but I was not looking forward to working them through my standard proceedure: sequester the original IDE Liquid Crystal library, install the f.malpartida library from bit bucket, run an i2c scanner, run the i2c lcd guesser. Configure the constructor in the sketch.

The application could also require a field replacement display, and having plug and play capability without working out a new address, constructor and reprogramming the sketch if the adaptor was different would be useful.

I suggested they download hd44680.h from the library manager and expect plug and play performance of the lcd with only contrast pot adjustment if they used the magic words.

#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h> // include i/o class header

hd44780_I2Cexp lcd; // declare lcd object: auto locate & config display for hd44780 chip

From that point, that the common lcd syntax lcd.begin(columns,rows), set cursor(), lcd.print() is straightforward.

Everything worked out very smoothly and the person was up and running quickly.

I think that your library has the potential to reduce the hd44780 i2c lcd display confusion seen on this board, and creates the opportunity for plug and play performance.

I supplied a “Hello World” sketch based on the basic example

#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h> // include i/o class header

hd44780_I2Cexp lcd; // declare lcd object: auto locate & config display for hd44780 chip

void setup()
{
  // initialize LCD with number of columns and rows:
  lcd.begin(16, 2);

  // Print a message to the LCD
  lcd.print("Hello, World!");
}

void loop()
{
  lcd.setCursor(0, 1);
  lcd.print(millis() / 1000);
  delay(1000);
}

The goal was to try get as close to "plug and play" as possible by eliminating many of the stumbling blocks of existing libraries. One of things that I really like is that you can swap out displays and there is no need to rebuild the code even if the i2c address or pin mapping changes since it is runtime detected.

There are still quite a few things that I want & need to do like getting the documentation in place and up on a server so that the example sketches can link to the documentation. I also need to get the shift register class added for those that want to go that route, but honestly PCF8574 backpacks are so cheap now, I don't think I'll be using any more shift registers for LCDs, especially since using PCF8574 backpacks also allows doing reads from the display which can be useful in some cases.

I also find that the I2CexpDiag sketch is useful to detect when pullup resistor are missing which is also a source of many issues.

I've tested the library code on several different processors & cores, including m328, 2560, pic32, arm on teensy 3 boards, and will soon be testing it on ESP8266 boards as soon as my boards come in.

I've also tested it with the SoftwareWire sofware i2c library on the AVR platform. Right now it requires that the wire object you create is called Wire so it can't be used to create a second software i2c bus when the h/w Wire bus is also being used. I'm planning on updating the hd44780_I2Cexp io class to work with any i2c class and any object name by using some templates.

BTW, the library does come with a "hello world" sketch for each supplied ioclass. It does a bit more than the example you show above and also checks to see if initialization works and will blink a built in led if initialization fails.

If you find issues or have suggestions, post issues on github and I'll address them.

--- bill

Great library.

Suggestions:

Put LCD in the library name be more Newbie friendly, meaningful and easily found/noticed. The HD44780 device from Hitachi dates to the 1980’s is now a somewhat archaic reference though the specification did set a standard for the electrical interface and command set. It is getting less frequently mentioned in LCD adverts and tutorials these days. It’s a bit like calling a “WiFi” library “IEEE802.11” :slight_smile:

Make the examples menu structure more Newbie friendly, to negotiate through a menu tree called “ioclass” to find a basic Hello World sketch is not very intuitive.

bodmer: Great library.

Suggestions:

Put LCD in the library name be more Newbie friendly, meaningful and easily found/noticed. The HD44780 device from Hitachi dates to the 1980's is now a somewhat archaic reference though the specification did set a standard for the electrical interface and command set. It is getting less frequently mentioned in LCD adverts and tutorials these days. It's a bit like calling a "WiFi" library "IEEE802.11" :)

Make the examples menu structure more Newbie friendly, to negotiate through a menu tree called "ioclass" to find a basic Hello World sketch is not very intuitive.

None of those are going to happen. A multi layered Arduino library of sub classes like this is not an easy thing to make easy to install and use. fm's new LiquidCrystal library has taken one approach and bundled everything. Adafruit has taken a different approach and used separate libraries. Both have some issues that I was trying to avoid. Here is some additional background.

In terms of naming, there are many types of "LCD" devices. Names like "LiquidCrystal" which is common and is used for the Arduino IDE bundled hd44780 library and using "LCD" is too generic for a library like this. This library is specifically for interfacing h/w to lcd devices that use and conform to the hd44780 interface. Keep in mind that this library is not just a simple single class library like LiquidCrystal or LiquidCrystal_I2C. hd44780 is an extensible library class. It is a framework that is used allow the creation of simple sub i/o classes that merely handle the communication between the hd44780 lcd and some sort of h/w interface like i2c, arduino pins, spi, shift registers etc... The hd44780 base layer class handles the public API as well as all the hd44780 initialization, control and hd44780 low level timing. The i/o classes are used to merely transport the hd44780 messages and data from the hd44780 class to the hd44780 device. hd44780 is VERY specific to hd44780 lcds.

I also wanted a name that was different from all the "LiquidCrystal" and "LCD" type names so that there would be no confusion of this library with any of the existing libraries and I needed a naming convention that ensured that there would not be naming collisions with other existing libraries or other libraries that may exist in the future - some of this is because the IDE has such a dumb way of handling Arduino "libraries".

The i/o sub classes are free to use any name they wish and don't have to be bundled/included with the hd44780 library package. However, the i/o sub classes that are included/bundled with the hd44780 library intentionally use hd44780 in their name to indicate that they are included with hd44780. Other hd44780 i/o sub class libraries that might be created by 3rd parties could use their own naming conventions.

I spent LOTS of time debating and going round and round how this should be done. In fact I had an early version of hd44780 that didn't include any i/o sub classes and used more generic names for the i/o sub classes and used a separate installable Arduino library for each i/o sub class. However, separate libraries makes the installation more difficult since a user must install multiple libraries before they can do anything. They have to install the hd44780 base class and the install the desired i/o sub class library. While that isn't so bad with newer IDEs that support the library manager, it starts to become a difficult hump for users that have older IDEs. In my opinion it was more important to have a common set of i/o sub classes pre-bundled with hd44780 to make the installation easier. Also, when i/o libraries are separate Arduino libraries, you have to go back to Team Arduino to get each new i/o class library added whenever support for a new i/o class is added, which is a pain. Also by bundling them, it ensures that all the i/o sub class libraries are up to date and in sync with the hd44780 base class library - which avoids another potential issue and reduces maintenance.

When there are multiple i/o sub classes bundled, things do get a bit "messy" since logically there are essentially multiple Arduino libraries bundled together. Where things do get a bit messy in in the examples area.

In the case of the hd44780 library package, the hd44780 class has its own examples, which can work on non hd4470 libraries like LiquidCrystal, LiquidCrystal_I2C, LiquidTWI, PCF8574_I2C_LCD, etc... as well as each hd44780 i/o sub class has its own examples.

There really isn't a way to handle the examples other than the to create "sub directories" to separate them by class, hence why you see the "ioClass" example tree. Something has to be there to separate out examples by bundled i/o class.

In terms of newbie friendly. I simply don't subscribe that Arduino users are all morons. Ease of use and intuitiveness is very important; however I have no intention of trying to dumb everything down to a ridiculous level that restricts or hinders functionality or has the potential to create confusion or issues down the road. In some cases a bit of reading the documentation will be required.

A lot, if not most of the issue right now is that the hd44780 library package is still in alpha state. Normally, I would never open up such a raw s/w package up to end users - especially when some of them are less technical users. In this case I made a very rare exception since recently on the forum I have seen so many users flounder with trying to get their i2c backpacks working. And many other users trying to be "helpful" were offering these struggling uses such bad and unhelpful advice.

The real documentation for the hd44780 package is not yet available. Once the documentation is in place, I believe that the naming will not be an issue and the names of the classes and example sub directories have a consistency. The documentation will be out on a server and all the example sketches as well as the main github page will have links to it. There will also be API example sketches that demonstrate each and every single API function.

So in terms of documentation, it will likely be the one of better documented lcd libraries out there. My intent is to have documentation similar to what I provide for the openGLCD library but to move the documentation to a server vs ship with the library as when it comes with the library, it cannot be brought from the IDE GUI. Whereas when it is on a server, you will be able to simply click on a link in the sketch to bring up a web page that has the documentation.

--- bill

@bperrybap

Thanks for the tome!

I saw the alpha status of the library and assumed there was still scope for change and it was not on general release. Too late now.

I had trouble finding it on Github, possibly because it has not been there long, it did not show up with obvious combinations of search strings.

I agree about not dumbing things down but it is easy to forget how daunting that first "Hello World" sketch looks. When I read this post I did wonder why cattledog felt the need to create a simpler sketch until I saw the one in the library. BTW I suspect the mins variable will overflow before millis() and both overflows will leave digits on the screen, but no one will run the sketch that long surely :)

Good news is that the diagnostic sketch compiled and ran on a NodeMCU, the only problem I have noticed is the software watchdog kicking in.

bodmer: @bperrybap I agree about not dumbing things down but it is easy to forget how daunting that first "Hello World" sketch looks. When I read this post I did wonder why cattledog felt the need to create a simpler sketch until I saw the one in the library.

My philosophy is that this library should be easy to install and to get up and running by newbies and less technical people but not to go overboard by reducing things down to the point of not showing certain capabilities. I am so disappointed by some of the decisions and the way some things are handled in Arduino (including some of the core code and libraries) in many cases because there was a presumption that it might scare off certain people or that it requires that users actually read a small amount of documentation/instructions. Goofy things like renaming standard C functions or C data types is a great example of doing things that are very silly in my opinion. That said, I do agree that it is a bit of a delicate balance when trying to make things functional but still easy to use.

My intention with the HelloWorld was provide an example that is a bit more complicated than just the typical absolute minimum HelloWorld. I wanted an example that should "just work" out of the box that also tries to push users a little bit to show them a few new things/tricks to try to advance their knowledge. At this point I don't have any plans to provide anything simpler; but might be open to some changes or tweaks given the proper arguments. One of things that I want to expose to users from the very beginning is that the hd44780 library begin() returns a success status that can be used to determine if initialization worked.

I do have plans to put in a few more comments into the hello world sketches for clarity that should help. I initially focused on the setup() and the loop() code which I think is fairly well commented and easy to understand even for a less technical newbie, but the lower level routines could use a bit of additional comments for clarity.

I think that once the library documentation in place, things in general should be clearer. The problem right now is that the only thing to look at is the github page summary and the example code which is no substitute for actual documentation.

BTW I suspect the mins variable will overflow before millis() and both overflows will leave digits on the screen, but no one will run the sketch that long surely :)

I'm aware of that, I was trying to keep things simple by using simple ints for all the variables. I've thought about updating that piece of code but so far it is very low priority since the overflow happens after a few weeks or running which is a long time for that type of example.

Good news is that the diagnostic sketch compiled and ran on a NodeMCU, the only problem I have noticed is the software watchdog kicking in.

I don't know how the watchdog works on that platform yet. I'll be getting in some devices in the next few weeks and will definitely look at that to see how/what needs to be done to ensure that doesn't happen. Maybe you could post an issue on github with information about the issue you are seeing and how to reproduce it to help out.

--- bill

Issues posted on Github.

Cool... just tried the I2CexpDiag sketch running on an STM32F103 ARM based board and it worked fine on I2C port 1.

bodmer: Cool... just tried the I2CexpDiag sketch running on an STM32F103 ARM based board and it worked fine on I2C port 1.

Did you have to do anything to the code or did it "just work"? i.e. did you have to edit the code to declare your own Wire object?

Does the STM board use s/w or h/w i2c? It would be interesting to see what the byte transfer time is for that board. (run LCDiSpeed & LCDiSpeed400)

--- bill

bperrybap: Does the STM board use s/w or h/w i2c? It would be interesting to see what the byte transfer time is for that board. (run LCDiSpeed & LCDiSpeed400)

--- bill

The port I conected the I2C LCD is a hardware port so I assume it was used that way.

LCDiSpeed: ByteXfer 704us 16x2FPS: 41.75 Ftime 23.95ms

LCDiSpeed400 reported error:

LCDiSpeed.ino:202:7: error: 'class TwoWire' has no member named 'setClock'

The I2CexpDiag needed a small tweak to add a delay at the start of setup() to allow the USB COM port to enumerate so the serial messages did not get lost. I'm guessing all the device specific stuff is in the Wire libraries rather than the HD44780 library.

bodmer: The port I conected the I2C LCD is a hardware port so I assume it was used that way.

LCDiSpeed: ByteXfer 704us 16x2FPS: 41.75 Ftime 23.95ms

704us seems slow. Normally I see around 550us ByteXfer with the hd44780_I2Cexp i/o class This is on AVR m328, AVR M2560 and pic32 processors. I get around 760us when I run a software based I2C using the SoftwareWire library on the AVR m328.

Around 550us is what I'd expect to see for a h/w implementation running at 100Khz bit clock.

bodmer: LCDiSpeed400 reported error:

LCDiSpeed.ino:202:7: error: 'class TwoWire' has no member named 'setClock'

The I2CexpDiag needed a small tweak to add a delay at the start of setup() to allow the USB COM port to enumerate so the serial messages did not get lost. I'm guessing all the device specific stuff is in the Wire libraries rather than the HD44780 library.

The Serial boolean is supposed to wait until the USB com port is ready but even on Leonardo it doesn't seem to work very well. It works great on Teensy. What I find works at least on Leonardo is to start the IDE debugger terminal before the upload. (this is noted in the instructions) Does starting the terminal session before the upload still have issues on the STM board?

With respect to the error for LCDiSpeed400, it looks like the STM Wire library is incomplete. They didn't implement the newer function to allow the sketch to set the i2c bit clock. This is an issue in the STM core - it probably should be bugged so that they can have a reminder to fix it.

--- bill

Yes, the STM board was using bit bashed I2C on the hardware I2C pins!

I had to hack the library to use the HardWire.h lib and set a faster 400kHz clock, then the results are:

LCDiSpeed400, STM, HW I2C: ByteXfer 129us 16x2FPS: 228.30 Ftime 4.38ms

I think it was 505us with 100kHz clock and hardware I2C but I might have remembered that wrong, but since 4 x 129us = 516us that sounds about right if it is bit rate limited.

For comparison the results on an UNO were:

LCDiSpeed400, UNO, HW I2C: ByteXfer 201us 16x2FPS: 146.58 Ftime 6.82ms

bodmer: Yes, the STM board was using bit bashed I2C on the hardware I2C pins!

I had to hack the library to use the HardWire.h lib and set a faster 400kHz clock, then the results are:

Cool. When you say you had to modify the library. Which library? the STM Wire library or hd44780? I'm curious why the STM core wasn't using the i2c h/w originally for the Wire library. Does the STM Wire library default to using s/w instead of h/w ic2?