Using the second code page for symbols on the LCD, switching between pages

Good afternoon,

My LCD display (russian national make) has two built-in code pages (0 and 1) for standard character generation, both of them are full 256-symbol pages. As far as the LiquidCrystal library goes, initializing LCD_FUNCTIONSET (cmd 0x20) is quite hardwired into the code and _displayfunction property is a private one.
Are there any plans to add code page switching to the library or has anyone a normal non-hacky
recipe for augmenting _displayfunction attribute or affecting it somehow (just need |0x2) ?

If both answers are 'No', where can I contribute the changes to the code of this library?

Ok, for the second question - I'll try pulling changes to GitHub. Hope they'll get merged in.

Are there any plans to add code page switching to the library ...

I wouldn't expect so since the library is intended to implement the instructions available in the Hitachi HD44780U and it's clones.

Another possible problem is the fact that in the HD44780U (your controller may be different) there are some limitations on when the Function Set instruction can be performed.

"Note: Perform the function at the head of the program before executing any instructions (except for the read busy flag and address instruction). From this point, the function set instruction cannot be executed unless the interface data length is changed."

Don

Sounds like you are wanting to set the F bit.

You can currently set that bit by adding a parameter in begin() but it looks like the code will only set that bit if you also set the number of lines to 1.

 // for some 1 line displays you can select a 10 pixel high font
  if ((dotsize != LCD_5x8DOTS) && (lines == 1)) {
    _displayfunction |= LCD_5x10DOTS;
  }

I'm assuming you wanting to be able to set that bit regardless of of the number of lines?
(Seems resonable)
So it that would need to change to something like this:

  // for some displays you can select an alternate font
  // note: while this says "5x10DOTS", some displays use this bit
  // as a code page selector to select an alternate font rather than a different sized font.
  if ((dotsize != LCD_5x8DOTS)) {
    _displayfunction |= LCD_5x10DOTS;
  }

You might have success getting that change in, if explained appropriately, since it should't break any existing code.

In the mean time, there are some work "ugly" work arounds that you could do if you don't want to have depend on a modified library.

In the more recent version of LiquidCrystal (since IDE 1.6.0) you can cheat by setting the rows to 1 even if you have more rows since the setCursor() function doesn't sanity check the target row against what was pass in in begin() but rather how large the address table is (currently 4)
Also, the new row memory address calculation works correctly with either 1, 2, or 4 line displays for most displays.
So you could set the LCD_5x10DOTS options in begin() and the number of rows to one and cursor addressing should still work when the number of rows is more than 1.

I would avoid this method as it doesn't work for all versions of the IDE.
A better option would be to use the command() function which allows you send any command you want to the display and just send the command to set the F bit yourself.
(assuming the display accepts this command after initalization - some may not as Don pointed out)
While it won't set the F bit in the private _displayfunction variable, it isn't really needed since the only thing the runtime code uses that variable for after initialization is for 8bit vs 4 bit mode.

So just call command() to set it:
i.e. something like

lcd.command(LCD_FUNCTIONSET|LCD_2LINE|LCD_5x10DOTS);

ugly, but should do the job.
The actual argument will depend on the number of lines and the the 4/8 bit mode in use.

--- bill

Sounds like you are wanting to set the F bit.

I don't think so. I believe he wants to set DB1, not DB2.

This guess is based on information from this page: geek-mag.com - This website is for sale! - geek mag Resources and Information. scroll down toward the bottom or search for 'code page'.

Don

Don, I think you are correct. I totally miscalculated my bit numbers.

Using bit 1 breaks the 4bit initialization sequence - if you had to use it during the real 4/8 bit initialization sequence on a 4 bit only host.
That initialization sequence depends on bits 0 and 1 not being used and being ignored by the LCD module along with the ability to send half bytes on a 4 bit interface. Which you couldn't do if you had to set that bit as well.
Bits 0 and 1 need to be ignored in case the host and the module are out of nibble sync so that a 4 bit only host can reliably get the chip back into 8 bit mode so that it can set it to 4 bit mode.

Given the patch on that geek-mag page, the LCD module must accept this function set after all the interface size function sets are completed, since it is sent after all the 4/8 bit initialization is complete.

I don't think that patch would be allowed. I'd also be against it.

Given that the "code page" function set can be done after the initialization is complete,
I'd recommend doing it in the sketch code using a command() call.
That way it will work on any version of IDE and is not dependent on any specific library patches.
1 simple line of code in the sketch and its taken care of.

--- bill

Using bit 1 breaks the 4bit initialization sequence . . .

That shouldn't be the case since the datasheet flowchart shows asterisks for bits 0 and 1.

It's been a long time since I looked at the initialization instructions in the LiquidCrystal library but if the initialization sequence is actually followed as depicted in the datasheet flowchart** there should be no problem with having non zero bits in position DB1 and DB0. The lower nibble should be ignored during the 'reset' sequence and should only have significance with the final 'Function Set' command.

Since the actual 'reset' mechanism is undocumented we can only speculate about what is actually going on. My theory is that after the controller detects more than one 'Function Set' instruction it realizes that a 'reset' is desired and it then disregards the lower nibble in all but the final one.

Don

** with two caveats
(1) the missing time delay is inserted after the third 'Function Set' in the reset sequence.
(2) you don't forget that the display is left 'OFF" and has to be turned back 'ON'.

Thank you for your replies!

I did a fork of the library from GitHub and did some experimenting with it. So, it turns out that setting DB1 (0x02) in functionSet after the startup initialization works as expected - the code page is really switched and it doesn't break the 4bit initialization sequence - this sequence is hardcoded with '4bit init protocol' + functionSet at the end. As the code page is initially 0 - everything works just fine (it even works with page1 as default).

Calling functionSet (as soon as you don't change the 4/8 bit mode) also works as expected - the code page is switched back and forth even after some data is transmitted for display.

The only caveat that should be adhered - code page switching does affect the text on the screen that is already there, as it is not 'font'-switching. This code may end up with unintended garbage:

 lcd.setCodePage(1);
  lcd.print("bunch of \x??\x??\x?? chars");
  lcd.setCodePage(0);
  lcd.setCursor(1,1);
  lcd.print("\x99C"); // degrees celsium

This bit of code will provide garbage on the first display line.

It turns out that the setCodePage() function doesn't break anything (at least with my clone) and i'll be happy to give it to someone to test on the other models of lcd that also have a second code page. I'll probably do a pull request today. But, in case you are interested - I put the patch into attachments.

patch.txt (2.57 KB)

bperrybap:
Given that the "code page" function set can be done after the initialization is complete,
I'd recommend doing it in the sketch code using a command() call.
That way it will work on any version of IDE and is not dependent on any specific library patches.
1 simple line of code in the sketch and its taken care of.

--- bill

Bill,
While command() is available to be called later, unfortunately _displayfunction attribute is not - it is private. And 'guessing' in the code what init()/begin() ended up with for its value looks a bit untidy.

bperrybap:
That way it will work on any version of IDE and is not dependent on any specific library patches.
1 simple line of code in the sketch and its taken care of.

--- bill

Sorry Bill :slight_smile:
One of the reasons to have library versions and library update notifications :slight_smile:

Those, who don't use the second page will not be affected, those who do should be advised of the library version it was introduced into. It is as much like extending an arbitrary protocol :slight_smile:

Cheers,
Alex

Don,
The "reset" sequence is not a magic sequence of specific instructions.
It is much simpler than that.
If you dig deep into the instructions and look at the bit patterns of what will be sent, in all combinations of 4/8 bit interface modes and nibble sync (when in 4 bit mode), that sequence of instructions is a sequence of instructions that first gets the interface back to 8 bit mode and then if using 4 bit mode, puts the module into 4 bit mode.
The f/w can be dumb and just process commands it sees. It doesn't have to do anything special or tract any state or timing information.

In fact the commands sent to the display for 4 bit and 8 bit initialization is absolutely identical other than when using 4 bit mode, an extra go to 4 bit mode will be sent to put the display into 4 bit mode.
The code in the LiquidCrystal library in this area is a bit of a wreck and can easily be simplified to make it clearer as to what is really happening.

If you want more details, PM me and I'll send you a full write up from an arduino hd44780 library I'm about to release.

altishchenko,
It is the initialization process which sets up the 4/8 bit mode and host to lcd module nibble sync where the setting of this bit could be an issue.
That is what I said in post #5.
The example code on the gee-mag page was setting that bit after the nibble sync initialization so clearly it worked when done after the 4/8 bit init/nibble-sync code.

altishchenko:
Sorry Bill :slight_smile:
One of the reasons to have library versions and library update notifications :slight_smile:

Those, who don't use the second page will not be affected, those who do should be advised of the library version it was introduced into. It is as much like extending an arbitrary protocol :slight_smile:

Cheers,
Alex

Ah... but there is the rub. You don't really know that it won't affect other LCDs.
In fact, I'd be very concerned if CPAGE1 was ever set during the initial 4/8 bit host initialization, particularly when the host and the LCD are out of nibble sync. I believe it breaks getting back into nibble sync since it can break getting back to 8 bit mode.
So it really isn't like extending an arbitrary protocol.
Yeah, I see that in this patch _cpage is initially set to CPAGE0 which is zero, so it doesn't affect the current initialization code, as long as _cpage default initialization remains zero.
The questions becomes why even mess with those bits that early given it can cause potential issues and it isn't needed?

Those early init commands are not really retries so there is no need to mess with those bits that early, particuarly given it has the potential to break 4 bit nibble resynchronization.
At a minimum I'd suggest not setting the CPAGE bits until after the host 4/8 bit sync sequence is completed.
But since the default is CPAGE0, which is zero there really isn't anything to set so why alter their current init code at all?

The reason I bring all this up is that from looking at the code in the LiquidCrystal library in that area, it is clear that the authors, do not understand what that 4/8 bit initialization command sequence is doing.
For example in the 3 lines like this in the 8 bit init sequence:

    command(LCD_FUNCTIONSET | _displayfunction);

it is not necessary to OR in the displayfucntion value here. It will be set later after the 8/4 bit nibble-sync is established.
In fact if they really understood the command sequence, then they would have understood that the 4 and 8 bit sync command sequence is really identical and the only difference is a final command to send the display to 4 bit mode. That is why 4 bit has 4 commands and 8 bit has 3 commands. The point of first 3 commands of both (which are the same commands) is to get the display back into 8 bit mode even if the display was already in 4 bit mode and expecting the 2nd nibble of a command when the sequence started.

Since they don't fully understand that code, I'm guessing that they could be very nervous about your proposed changes in begin().
So why do them, given they are not needed?
Why not limit the changes to those needed for the addition of the setCodePage() function.
(which I'm still against, but seems more likely to get accepted)

In terms of getting this into the mainline LiquidCrystal library, I see this as a big struggle for a couple of reasons.
The main one being that it is a vary narrow solution that seems to be for a specific LCD module.
On top of that, the same result can be accomplished using the existing command() api from the sketch with no changes needed in the library code.
Given that any sketch that wants to use this code page capability requires doing something custom outside the ordinary/existing LiquidCrystal API, why not just do it using command() API which works on any version of the LiquidCrystal library instead of having to also require a modified LiquidCrystal library?

Having access to the _displayfunction attribute is not necessary since its only used is to track 4bit vs 8bit mode during runtime sending of bytes to the display.

I think if you really wanted to get something into the mainline LiquidCrystal library it would need to be more generic.
Perhaps relaxing the use of the 3rd argument to begin() to be:
begin(cols, lines, displaybits)

where displaybits would be the lower 3 bits that were blindly inserted after the 4/8 bit initialization sequence was done regardless of number of lines on the display.

Or limited to just adding the setCodePage() function.

As an alternative you could create a wrapper class to extend the LiquidCrystal class in a new library.
That way simply include your new class library header use your class name and get the added capabilities you want/need.
And with your own class library you could add it to the library manager so anybody could add it using the IDE library manager all without having to modify the stock IDE LiquidCrystal library.
And with your own library, you could even supply examples to show how to use the extended API and functionality.

--- bill

If you want more details, PM me and I'll send you a full write up from an arduino hd44780 library I'm about to release.

I am fully aware of how an HD44780U is supposed to be initialized. If you do a Google search for 'LCD Initialization' my write-up is typically the first hit.

If you want to see my implementation then do a Google search for 'LCD programming example' and you will get similar results. Make sure that at some point you follow the link back to the 'list of all LCD programming examples' and read my programming philosophy.

The QR code in my avatar will get you to those pages as well.

The code in the LiquidCrystal library in this area is a bit of a wreck ...

That's an understatement if I ever saw one.

The code also did not accurately follow the sequence recommended in the datasheet flowchart when it was rewritten by Limor Fried for v0017 and she refused to correct it when I pointed the out the error. The discrepancy remains to this day, let me know if you haven't found it already. My second caveat in reply #6 is related to the problem.

At a minimum I'd suggest not setting the CPAGE bits until after the host 4/8 bit sync sequence is completed.

I don't think it is as simple as that since setting the CPAGE bit involves using what the LCD controller perceives as another 'Function Set' command. As I interpret the datasheet note quoted in reply #2 there are some (poorly defined) restrictions on doing this.

In terms of getting this into the mainline LiquidCrystal library, I see this as a big struggle for a couple of reasons. The main one being that it is a vary narrow solution that seems to be for a specific LCD module.

I agree. This is essentially what I said in my initial response.

Don

HD44780 (Hitachi), KS0066 (Samsung)

bperrybap:
It is the initialization process which sets up the 4/8 bit mode and host to lcd module nibble sync where the setting of this bit could be an issue.
That is what I said in post #5.
The example code on the gee-mag page was setting that bit after the nibble sync initialization so clearly it worked when done after the 4/8 bit init/nibble-sync code.

Ah... but there is the rub. You don't really know that it won't affect other LCDs.
...
The questions becomes why even mess with those bits that early given it can cause potential issues and it isn't needed?

Bill,
I agree with this, the only thing that I know for sure is that it won't break HD44780 and KS0066(Samsung) as per their specs these bits are safely ignored during the 8-bit init sequence and they are totally unused in the 4-bit setup mode.
Surely, setting the bit too early is not the best approach and today, having read a few other datasheets for other crystals, I would rather revert the setting to the last FS in the begin() function.

bperrybap:
Those early init commands are not really retries so there is no need to mess with those bits that early, particuarly given it has the potential to break 4 bit nibble resynchronization.
At a minimum I'd suggest not setting the CPAGE bits until after the host 4/8 bit sync sequence is completed.
But since the default is CPAGE0, which is zero there really isn't anything to set so why alter their current init code at all?

They are syncs, surely, and in the current version of my fork it is done as I said above.

bperrybap:
The reason I bring all this up is that from looking at the code in the LiquidCrystal library in that area, it is clear that the authors, do not understand what that 4/8 bit initialization command sequence is doing.
For example in the 3 lines like this in the 8 bit init sequence:

    command(LCD_FUNCTIONSET | _displayfunction);

it is not necessary to OR in the displayfucntion value here. It will be set later after the 8/4 bit nibble-sync is established.
In fact if they really understood the command sequence, then they would have understood that the 4 and 8 bit sync command sequence is really identical and the only difference is a final command to send the display to 4 bit mode. That is why 4 bit has 4 commands and 8 bit has 3 commands. The point of first 3 commands of both (which are the same commands) is to get the display back into 8 bit mode even if the display was already in 4 bit mode and expecting the 2nd nibble of a command when the sequence started.

Well, they are trying to be generic and for that case ORing _displayfunction in the init sequence just brings in the LCD_8BITMODE in while other bits are ignored. For the safer side I would go with just command(0x30)!

bperrybap:
Why not limit the changes to those needed for the addition of the setCodePage() function.
(which I'm still against, but seems more likely to get accepted)

Did this right now, pull request will contain just that.

bperrybap:
Having access to the _displayfunction attribute is not necessary since its only used is to track 4bit vs 8bit mode during runtime sending of bytes to the display.

Some displays are nervous about this 4/8 bit unfortunately. Of course the author of the sketch is aware of how many DB lines he is using, but that is going into too much of a library implementation detail for generic usability.

bperrybap:
I think if you really wanted to get something into the mainline LiquidCrystal library it would need to be more generic.
Perhaps relaxing the use of the 3rd argument to begin() to be:
begin(cols, lines, displaybits)
where displaybits would be the lower 3 bits that were blindly inserted after the 4/8 bit initialization sequence was done regardless of number of lines on the display.
Or limited to just adding the setCodePage() function.

Just a function it is now. I thought about begin() in the first place, but somehow didn't like the idea. Standalone function is more explicit - you either use it or not, changing an interface to begin is not that forgiving. And wrapper class or subclass will not do the trick also - _displayfunction is still unaccessible :slight_smile:
And a brand new library just for the code page - too much of a bother :slight_smile:

The reason behind all this is that this particular LCD is readily available on the market and contains our national character set in code page 1. Shops like Amperka.ru (our type of Adafruit) will benefit of this function for the sample projects and tutorials they put on-line for their customers. Using a modified fork may sound like a good idea for the case, but maintaining compatibility with original LC library may be a bit of strain for hobbyists, hence the attempt to put the function into official LC.

Alex

altishchenko:
Some displays are nervous about this 4/8 bit unfortunately. Of course the author of the sketch is aware of how many DB lines he is using, but that is going into too much of a library implementation detail for generic usability.

Actually, the sketch author and sketch code is not always aware of the hd44780 interface width.
The LCD could be attached to some sort of backpack that uses i2c or a serial interface.
In that case the sketch doesn't have to know the data width being used on the hd44780 interface since the lcd library will be taking care of it.

And in that case, a modified/updated LiquidCrystal library will be of no help since that library will not be the library used to communicate with the device.

As of now there are so many different hd44780 arduino libraries out there and the LiquidCrystal library that comes with the IDE is just one of many.

And right now given that a pcf8574 i2c backpack is about $1 USD, many hd44780 LCDs are being attached to an i2c backpack vs hooked up directly to arduino pins.

So it will be a real challenge to get widespread adoption for support like this.
And that is why I proposed using either command() or a wrapper class library.
If you used a template you could create a wrapper class around any existing liquidcrystal library class so that it could work with library and not be limited to the LiquidCrystal code.
The user would just use your wrapper class along with whatever hd44780 library they wanted/needed to use to create the lcd constructor object.

My interest was that I'm about to release a new hd44780 library package that includes support for many different interfaces and I wanted to look at including support for something like this.
I think usage of this library package will really take off particularly since the i2c backpack sub library makes i2c backpacks "just work" which as of today no existing library does.

I'd would like to add support for this but I'm still thinking about the best way to handle it.

--- bill

Bill,
Can I have a look at your package? Is it on github or elsewhere?

As for the code page stuff - as I said it is more of the convenience, attempt to make things work out of the box for the newcomers. People buying pre-packaged arduino kits with all things included and a step-by-step tutorial on how to use it for themselves or for their kids are not the people who have 3 boxes of resistors and "stuff" lying around :wink: Even, tutorials themselves start with a brief introduction to C++ - that is the auditory.
Asking them to manually update some particular obscure library off github and do it right (so it will work) may be a bit of stretch, when the only thing they want is 'Hello, the temperature is XX C' in their speak.

And, I am still against using command() directly. It is more of a hack to the end user and I would make it private(!) be I one of the LC maintainers - this is too dangerous a function to leave it exposed.

Cheers,
Alex

Sorry for bragging, just noticed another issue - pulseEnable() enforces 100us delay at the end of its completion, which, in turn, affects the timing of the init sequence too! Doubling the waits after commands :frowning: It's a mess...

Adding extra time anywhere is not a problem as far as the controller is concerned since it is perfectly happy being run using manual switches without a microprocessor at all. Adding extra time in the initialization routine should not be of concern since it is only run once.

What is more disturbing to me is the lack of useful comments in the library documentation. This 100uS delay is a good example. The comment that accompanies this step says "// commands need > 37us to settle".

Here are my problems with this particular comment:

  • It is not clear that this delay has nothing to do with the enable pulse (which only requires 10 nS to 'setttle').
  • The purpose of this delay is actually to implement the 'Execution Time' required by each instruction and the comment should reflect this fact. The term 'commands' (plural) and the 37uS value are the tip-offs here.
  • The use of a 100uS delay (which is significantly longer than the 37uS value stated) is important but not explained.

Don

Don,
My points exactly!

Also, the other guy in a github conversation is having issues with all these extra delays in the library and ESP8266 wifi startup... Not a problem for Arduino though.

And for a cleaner code I'd remove all the delayMicroseconds() from the init block apart from the first 40ms as they are clearly doubled in pulseEnable(), which is run anyway. It is a simple library, just needs some clean up and line up to specs. (My LCD works perfectly if I change 100us to 50us in pulse...)
Also delayMicroseconds(1) is nonsense even according to Arduino library reference, should be >3 to be accurate.

altishchenko:
Bill,
Can I have a look at your package? Is it on github or elsewhere?

Its not on github yet. It will be soon.
Since I can't do private repos on github but it needs to be on github to work with the library manager, I don't want to push it up to a public repo until I'm close to ready to release it.
But I can notify you as soon as I put it up, if you would like.

While it is currently up and working, I'm still finalizing/tweaking some of the internal i/o class to class APIs and I really don't want this being forked until those APIs are all pretty much locked down.
The overall concept is similar to fm's LiquidCrystal replacement library. (which I also worked on)
But this package will not have all the classes bundled into a single arduino library directory.
It will separate them out into separate libraries.
It will also not attempt to masquerade as the IDE bundled LiquidCrystal library.
This eliminates all the installation, build, and potential library collision issues in that library.
And since it will use the library manager, when using more recent IDEs, users can install it with just a few clicks and get update notices when updates become available.

As for the code page stuff - as I said it is more of the convenience, attempt to make things work out of the box for the newcomers. People buying pre-packaged arduino kits with all things included and a step-by-step tutorial on how to use it for themselves or for their kids are not the people who have 3 boxes of resistors and "stuff" lying around :wink: Even, tutorials themselves start with a brief introduction to C++ - that is the auditory.

And that is why I'd recommend using the already existing command() api.
command() exists, and works with any version of the IDE even to back before 1.0 so you are not having to depend on any updates to a library to get support to control that display.
You could create a tutorial and an example of how to set the code page, that should work on any version of the IDE and with any hd44780 library - once they create their lcd library constructor for the library they are using.
Consider this, even if you manage to get this new API into the Arduino.cc IDE bundled LiquidCrystal library, (which is still not a given, but assuming it does get in) there are still dozens of other hd44780 libraries that won't have it, including the i2c backpack libraries.
This is important to remember as lots of users are using LCDs that are not using them with directly connected pins or with the Arduino.cc IDE LiquidCrystal library.
Keep in mind that other h/w like teensy, or chipkit, and Intel Arduino 101 ship with their own versions of the LiquidCrystal library so even while a sketch may be using the "LiquidCrystal" library, depending on the board hardware, it may not be using the LiquidCrystal library that Arduino.cc ships with their IDE.
So even if this new API were to be added to the Arduino.cc IDE bundled LiquidCrystal library that Arduino.cc ships in their release, there is no guarantee that the user is running an IDE that has this update or is using an arduino board that would use that LiquidCrystal library.
Some Arduino users have replaced the stock IDE LiquidCrystal library with fm's library. Those users would also not see the new API unless that library were updated.
Also many Arduino users do not always update to the latest version of the IDE and since recent versions of the IDE can break some libraries or sketches, some users are intentionally not upgrading as they don't have the skill set to fix the simple things that broke on the newer IDEs.

Users of a tutorial and examples that depend on a new API that are using environments that either don't use the Arduino.cc LiquidCrystal library or have an older IDE without the the updates to support this new API, or were using a board, or core that used a different LiquidCrystal library would have to patch their lcd library themselves in order to have the API that the tutorial and examples depend on.

Based on what I've seen over the years in the Arduino forums, trying to get less technical users to modify a library even for tiny simple changes, is too large a step for some of those users.
However, less technical users would easily be able to run an example that uses command() or even create their own code by cutting and pasting the single line that uses the command() API to get the code page set for that display.

I'm not really against updating the library for new functionality, it is just that given the current real world reality of the state of Arduino hd44780 libraries, IDEs, and core extensions, any nice simple easy to use tutorial that depends on new API functions existing in libraries supplied by another party will be confined to a very limited set of use cases and will fail to work in many common use scenarios.

Asking them to manually update some particular obscure library off github and do it right (so it will work) may be a bit of stretch, when the only thing they want is 'Hello, the temperature is XX C' in their speak.

I agree that asking users to install a library from github/bitbucket etc is a stretch for some users.
But with adequate instructions and a properly formed .zip image many less technical users can handle it using the IDE "install from zip" capability.
This is what many of the users are already having to do today to get their i2c backpacks up and running.
The biggest issue here related to installation, is that several of the libraries out there are not providing properly formed .zip files so the users can't use the IDE to do the install. This creates lots of support issues as the manual install process is more complicated and many less technical users screw it up and make a mess of things.

But that isn't the way libraries should be installed now.
In more recent IDEs, Libraries can use the library manager so users can install them with just few clicks and get notifications about updates whenever the author pushes them out.
The user does not have mess with zip files or know anything about github, even though github is used under the hood.

And, I am still against using command() directly. It is more of a hack to the end user and I would make it private(!) be I one of the LC maintainers - this is too dangerous a function to leave it exposed.

I'm not a particular fan of having to use it either.
However, given that there are so many hd44780 libraries out there, including multiple "LiquidCrystal" libraries, I think it is unreasonable to expect a common api across all of them much less even having a a new API exist across all of the "LiquidCrystal" libraries, so you are kind stuck using it if you really want to enable and promote the use of this display.

Now there are ways you could hide it if that is a goal.
The most integrated / clean way would be to create a wrapper class library.

In terms of dangerous. Yes when abused, it can mess with the current display initialization which could cause a display to be come illegible.
However, it was made public as a way to provide unanticipated functionality.
And even if abused and things go haywire on the display, I don’t' think it will actually damage the hardware so it isn't that dangerous.

Have you looked at lcdproc (linux/unix) tool, or LCD Smartie (Windows)?
There are some Arduino sketches out there that provide support for these packages.
It becomes trivial to create an arduino sketch on top of an LCD library to support these as long as the library supports a raw command interface like command().

Those packages do some amazing things, but they want/need to have full control over the hd44780 interface which includes being able to send direct commands to the display.

--- bill

And for a cleaner code I'd remove all the delayMicroseconds() from the init block apart from the first 40ms as they are clearly doubled in pulseEnable(), which is run anyway.

I don't think so. That's 4.1 mS, not 4.1 uS.

Don