LCD 1602 (and similar) databus sniffer

I haven't gone through the state code in full details, but my initial impression is that it could have issues.
The 4 bit/8 bit mode selection using function set commands is not about seeing or processing 3 function set commands.
While many people mistakenly think that the 3 function sets are retries or a specific 3 command sequence that the LCD detects, that isn't how it really works.
All LCD instructions are always internally processed 8 bits a time.
The only difference between 8 bit mode and 4 bit mode is how the 8 bits are constructed before processing the 8 bit instructions.
And the function set commands are to be processed whenever they are seen with no concern if the host or the LCD are in nibble sync just like any other LCD instruction.

The magic is that bit position the DL bit for 8 bit and 4 bit mode was very carefully chosen to ensure that when processing the 8 bit instructions, even if the host and the LCD are out of sync with each other, the instructions will eventually put the LCD back into 8 bit mode then into 4 bit mode.
The LCD can end up processing a garbage/unknown instruction during this process if there is a synchronization issue when the host and the LCD are in different 8/4 modes or out of nibble sync in 4 bit mode.
I have a very long description of this process in the hd44780 library file hd44780.cpp in the begin() function.
It is definitely worth a read if you want to deep dive into how the function set 4/8 bit initialization really works.

I would recommend keeping collect() very simple.
Its job should be to only que up 8 bit data transferred to the LCD. This keeps it simple as well as fast.
collect() should not be concerned about getting into nibble sync with the host.
LCD Instructions can be processed by code in loop()
You pop off a bus transfer byte to always get an 8 bit instruction to process.
You process the instructions you know how to process and toss the ones you don't.
If you see a function set instruction with DL set then you set something that tells collect() to process in 8 bit mode.
If you see a function set instruction with DL clear, then you set something that tells collect() to process in 4 bit mode and the nibble state is for the first nibble.
It really can be that simple.

Also by handling instruction processing in loop, you can spit out messages about processing the function set messages and the 8/4 bit mode changes.
The instruction processing could easily be expanded to process more instructions as needed/desired.

For development, tracking, and potentially others contributing, it could be useful to put the project into a git repository and up on
github, bitbucket, atlassian, etc...

--- bill