adding a "simulate LCD_on_serial functionality" to the standard lcd-libraries

Hi everybody,

I don't have all kinds of LC-displays handy. But an Arduino Mega 2560 which has mutliple serial interfaces.

So maybe it is possible to use a "simulate LCD on serial funcionality through a second serial connection.
This simulation should include the basic commands like
lcd.init(colums, rows)
lcd.clear,
lcd.setcursor
lcd.print

For LCDs like one fontsize character dotmatrix 16x2, 20x2, 20x4 40x4 LCDs.
including all kinds of graphical screens with hundreds of pixel rows and columms and differentsizes would be too much.

I have coded such a functionality with separate functions that contain the lcd.xxx-commands and the SIM_LCD-commands. For a 20x4-LCD.

Now my question is: is how can I add this functionality to a derived "class" or a modified library in the most effective way.

Ideal would be to just change the name of the library from original to "with-SIM-LCD" compile and run
program. And every single output in the LCD is done in parallel to the serial interface.
For "updating" the shown content just spit out 10 Linefeeds new serial output showing the updated content of the LCD.

I'm pretty experienced about programming in general but I'm a novice about how to create libraries with *.h and *.cpp-files, what classes, and "heritageing" is. So if the described way needs classes, etc. please include links to introducing tutorials about that or at least keywords with which I can search myself.

best regards Stefan

something to start :slight_smile:

template <int cols, int rows>
class SimLCD : public Print {
public:

  SimLCD(Stream& _out) : out(_out) {
  }

  void setCursor(int _col, int _row) {
    col = _col;
    row = _row;
  }

  void clear() {
    for (int i = 0; i < rows; i++) {
      for (int j = 0; j < cols; j++) {
        buff[i][j] = ' ';
      }
    }
    flush();
  }

  virtual size_t write(uint8_t b) {
   if (col < cols) {
     buff[row][col] = b;
     col++;
     return 1;
   }
   return 0;
  }

  virtual void flush() {
    out.println(buff[0]);
    out.println(buff[1]);
    out.println("================");
  }

private:
  Stream& out;
  char buff[rows][cols + 1] = {"              ", "             "};
  int col = 0;
  int row = 0;
};

SimLCD<16, 2> lcd(Serial);

void setup() {
  Serial.begin(115200);
  lcd.print("12345678901234567890");
  lcd.setCursor(8, 1);
  lcd.print("12345678901234567890");
  lcd.flush();
  lcd.clear();
  lcd.setCursor(8, 0);
  lcd.print("12345678901234567890");
  lcd.setCursor(0, 1);
  lcd.print("12345678901234567890");
  lcd.flush();
}

void loop() {
}

Hi Juraj,

thank you for posting the code. Maybe I wrote my question misunderstandable.
Maybe for experienced programmers some lines of code are easier to understand.

the standard call of send text to an LCD is

lcd.print("Hello World");

what I have coded is extra functions that do

void LCDsPrint(char AoC_Pointer[]) {
  LCD_Changed = true;
  lcd.print(AoC_Pointer);  
  SimLCDPrint(AoC_Pointer); 
}

where SimLCDPrint does send the characters to a serial connection
where the basic operation is

Serial.print(AoC_Pointer);

(of course there is more behind it as the LCD has a special behaviour about sending too much characters in a line etc.)

So what I'm asking for is:
how can I modify the existing LCD-library through "heritation" (don't know the exact word for it)

that a call of

lcd.print("Hello World");

does both: send characters to lcd and send characters to COM-Port

From the level of the users code all functions that do the COM-Port-stuff are hided away.

The only thing the user has to do is change

#include <LiquidCrystal_I2C.h>

to

#include <LiquidCrystal_I2C_plus_COM_Port.h>

and maybe specify a serial interface and a baudrate

#define Serial_to_double_output = 2
# define COM_Port_Baudrate = 115200;

and the user is done.
Which means the rest of the code can stay exactly the same as before. But the additional library does send characters to the LCD and to Serial2

This enables to test code written for a LCD without having the LCD connected, because all output to the LCD is "doubled" to the COM-Port connected to Serial2

best regards Stefan

It still isn't clear to me what you are really trying to do.
If you are trying to create some sort of simulated/virtual display that looks like the actual display by just grabbing the characters sent through print(), it won't work very well since if all you grab is the characters that go through the Print class print() calls, you will be missing out out many things that can update the display.
i.e. things like the LCD geometry, custom characters, the use of lcd.write(), cursor positioning, and various LCD modes that all affect what is showing on the actual physical displaywill be invisible to what you seem to be trying to do.

What are yo REALLY trying to accomplish?

--- bill

Hi berrybap,

I'm trying to simulate the basic functionality of a LCD
you have to define the size of the LCD (16x2, 20x4, 40x4)

and then
with basic functionality I mean the following functions:

print, clear, setcursor

all other functionality shall not be simulated. You are right custom characters, inversed mode etc. would be very hard to simulate. (maybe instead of simulation it could just spit out that a more special-function is called)

So whenever a code does not do fancy things like define custom characters, use inversemode etc. but instead write plane text and that's it the simulation shows what the LCD would show.

best regards Stefan

something like

#include <LiquidCrystal_I2C.h>

template <class LCD, int cols, int rows>
class SimLCD : public Print {
public:

  SimLCD(LCD& _lcd, Stream& _out) : lcd(_lcd), out(_out) {
  }

  void begin() {
    lcd.begin(cols, rows);
  }

  void setCursor(int _col, int _row) {
    lcd.setCursor(_col, _row);
    col = _col;
    row = _row;
  }

  void clear() {
    lcd.clear();
    for (int i = 0; i < rows; i++) {
      for (int j = 0; j < cols; j++) {
        buff[i][j] = ' ';
      }
    }
    flush();
  }

  virtual size_t write(uint8_t b) {
   lcd.write(b);
   if (col < cols) {
     buff[row][col] = b;
     col++;
     return 1;
   }
   return 0;
  }

  virtual void flush() {
    lcd.flush();
    out.println(buff[0]);
    out.println(buff[1]);
    out.println("================");
  }

private:
  LCD lcd;
  Stream& out;
  char buff[rows][cols + 1] = {"              ", "             "};
  int col = 0;
  int row = 0;
};


LiquidCrystal_I2C lcd0(0x27, 0, 0);

SimLCD<LiquidCrystal_I2C, 16, 2> lcd(lcd0, Serial);

void setup() {
  Serial.begin(115200);
  lcd.begin();
  lcd.print("12345678901234567890");
  lcd.setCursor(8, 1);
  lcd.print("12345678901234567890");
  lcd.flush();
  lcd.clear();
  lcd.setCursor(8, 0);
  lcd.print("12345678901234567890");
  lcd.setCursor(0, 1);
  lcd.print("12345678901234567890");
  lcd.flush();
}

void loop() {
}

?

Stefan,
Here are a few things that you may not realize about how these "LiquidCrystal" libraries and the Print class works.
All these "LiquidCrystal" libraries inherit the Print class. The Print class is what provides the print() method, not the LCD library.
The Print class uses a write() virtual function to send the output characters to the LCD library.
i.e. if you call lcd.print("Hello, World!") the LCD library never sees that string as the print() function is in the Print class code not the LCD library. The Print class print() function calls the LCD library write() function for each individual output character.

I'm still not totally clear on what you are trying to do other than attempting to create some sort of virtual LCD display.
Some of what will drive your choices is whether this is in addition to using a physical display or if it is a replacement of the physical display.

You may want to take a look at the hd44780 library.
It is an extensible library. It uses what are called i/o classes to communicate with the physical display.
It uses 5 virtual functions for its API. The upper level has all the "LiquidCrystal" API methods. It then uses the lower i/o class API methods to send/receive information/data to/from the physical LCD.
You could create your own i/o class and then do whatever you want for the hd44780 instructions you wish to emulate/simulate.
There would be no need to modify any of the hd44780 library files as you can simply put your i/o class header in the same directory as the sketch.
You would be primarily using the iowrite() function.
The i/o class gets handed a type (data / command), and a byte of information.
You can then choose to which hd44780 commands you wish to implement and handle them anyway you want.

There are already some i/o classes included with the hd44780 library to support some devices that are not hd44780 devices.
They are doing some mapping and translation from hd44780 commands to make the device look like a hd44780 device to the upper layers and to the sketch.

--- bill

in other words,
if you want to "simulate" a LCD, do an implementation of the LCD API which defines the "standard" methods for a LCD Arduino Playground - LCDAPI

you could start with an existing LCD library and replace the hardware related code with dummy methods.
let your virtual write method write to your designated (hardware)Serial.
Better let the user define which Serial should be used as dummy.

edit:

if you are lacy you could inherit from an existing LCD library and just override the write.

well, not enough. You have to overload the init / and or begin method also as you will need the begin of your (hardware)Serial.

and finally just replace the constructor.

I did a short test with my LCD Library.
Added the new class to the Hello World example and modified the constructor.

#include <Wire.h>
#include <NoiascaLiquidCrystal_I2C.h>  // download library from https://werner.rothschopf.net/202003_arduino_liquid_crystal_umlaute.htm

class LcdSimu : public LiquidCrystal_I2C
{
  public :
    LcdSimu(byte a, byte b, byte c) :
      LiquidCrystal_I2C (a, b, c)
    {}

  void begin()
  {
    Serial.begin(115200);
  }

  void init()
  {
    begin();
  }

  inline size_t write(uint8_t value) {
    Serial.write(value);
    return 1;
  }

};

//LiquidCrystal_I2C lcd(0x27, 20, 4);    // set the LCD address to 0x27 (or 0x3F) for a 20 chars and 4 line display

LcdSimu lcd(0x27, 20, 4);    // alternative: simulate the LCD


void setup()
{
  Wire.begin();                        // start I2C before you use the LCD
  lcd.init();                          // initialize the LCD
  lcd.backlight();
  // Print a message to the LCD
  lcd.setCursor(3, 0);
  lcd.print("Hello, world!");
  lcd.setCursor(5, 1);
  lcd.print("Arduino!");
  lcd.setCursor(0, 2);
  lcd.print("Arduino LC IIC 2020");
  lcd.setCursor(2, 3);
  lcd.print("Powered by noiasca");
}

void loop()
{
}

will give you

Hello, world!Arduino!Arduino LC IIC 2020Powered by noiasca

on the Serial.

Still don't know why someone would need this, but hey, yes it could be done...

@noiasca, as I understand it should print to LCD and to Serial, for any of the LCD library versions

noiasca:
...
Still don't know why someone would need this, but hey, yes it could be done...

If I don't have the same LCD laying around or if I don't want to wire up the LCD and want to do a quick test of a code posted in the Arduino-Forum

In this situation I find it very useful.

Thank you very much Juraj and noiasca for posting the code.
best regards Stefan

Hi Juraj,
yes that's the way I want it.

I guess this requires doing the same modifikations to each LCD-library I want to use in this way.
best regards Stefan

StefanL38:
If I don't have the same LCD laying around or if I don't want to wire up the LCD and want to do a quick test of a code posted in the Arduino-Forum

In this situation I find it very useful.

Given this seems to be for hd44780 based displays, it seems like it would be pretty simple to just get a 20x4 LCD with an i2c interface and use the hd44780 library.
Just mod a few lines in the sketch to use the hd44780 library headers and constructor (which never changes) instead of whatever "LiquidCrystal" library they were using.
No other modifications to their sketch code and you are good to go.

I do this quite often.

You can even use SoftwareWire if you want/need to use other pins for the i2c connection.

--- bill

StefanL38:
Hi Juraj,
yes that's the way I want it.

I guess this requires doing the same modifikations to each LCD-library I want to use in this way.
best regards Stefan

StefanL38, if you really want the LCD AND Serial working in the same time you have not only to override the write but also include a call of the method of the base class AND add the serial.write at the bottom...

untested:

  inline size_t write(uint8_t value) {
    size_t ret;
    ret = LiquidCrystal_I2C::write(value); // call the base method
    Serial.write(value);
    return ret;
  }

noiasca:
StefanL38, if you really want the LCD AND Serial working in the same time you have not only to override the write but also include a call of the method of the base class AND add the serial.write at the bottom...

untested:

  inline size_t write(uint8_t value) {

size_t ret;
    ret = LiquidCrystal_I2C::write(value); // call the base method
    Serial.write(value);
    return ret;
  }

but this would work for only the one LCD library

to be precise, for all compatible libraries which call their class LiquidCrystal_I2C
and - as we know for other reasons - there are a lot ...

noiasca:
to be precise, for all compatible libraries which call their class LiquidCrystal_I2C
and - as we know for other reasons - there are a lot ...

and that is why my proposed code in comment #5 has the LCD type as template

noiasca:
to be precise, for all compatible libraries which call their class LiquidCrystal_I2C
and - as we know for other reasons - there are a lot ...

While there appears to many libraries that contain a LiquidCrystal_I2C.h header file and corresponding LiquidCrystal_I2C, as far as I've seen, there are actually only two libraries.
Marco H's LiquidCrystal_I2C library and fm's NewLiquidCrytal library.

These two libraries are very different, have different capabilities and use very different and non compatible constructors.

--- bill


Here is the back story:

Most of the "LiquidCrystal_I2C" libraries out there are derived from a library originally written by Mario H written back in 2009 and can be found here:
https://hmario.home.xs4all.nl/arduino/LiquidCrystal_I2C/
Since this was before the days of the Arduino IDE library manager, there was no standard/common place to put it.
As a result MANY people and vendors grabbed the code and started hosting it themselves.
Most of these libraries are simply copies of this library with no modifications.
A few years back (in 2015) a person (marcoschwartz) created a github project for it so it could be added to the IDE library manager.
Unfortunately, he was unable to maintain it. So while it was now available in the IDE library manager, there were some bugs that came up, and they were not being fixed. One was so serious that the library would no longer work.
This caused many people and vendors to once again grab copies of the library and host their own copies of the library.
The sad part was this serious bug was literally a one character fix, that I immediately pointed out but it didn't get fixed for quite a while and when it did get fixed the library.properties file was screwed up so the IDE library manager could not find/see the new version of the library.
In Sept 2018 marcoschwartz asked for somone else to take over the repository.
The github repository library (around Dec 2018) has been reassigned to another individual (johnrickman) who now controls the repository.
But again the repository is not being actively maintained very well / properly.
To this day, there are still issues with tags and the version number in library.properties, which means that the IDE can't see the new versions to allow users to update the library.
In April 2019, johnrickman added a comment to the readme that said:

This repo is being transfered to GitLab at https://gitlab.com/tandembyte/liquidcrystal_i2c

and then in just 12 days ago the readme was changed to:

Status: Archived This repository has been transfered to GitLab at https://gitlab.com/tandembyte/LCD_I2C

I just went and looked at the gitlab repository and noticed that 3 months ago the maintainer has improperly attempted to re-licensed that code as MIT.
This is cannot be done as the code always was and still is licensed as LGPL 2.1+ so this attempted re-licensing is a copyright violation as you can't just change a s/w license.
I have created an issue for this in that repository noting this copyright violation.

The Arduino IDE library manager appears to still be using marcoschwartz's the github repository:

which is now redirected on github to johnrickman:

due to the ownership change.


The other "LiquidCrystal_I2C" i/o class was in fm's NewLiquidCrystal library.
While it can work on any hardware, most users do not have the technical skills to get it properly installed and working.
Due to its design it cannot ever be installed using the IDE library manager.
This library has also suffered from maintenance issues over the years.
As a result there are some vendors that picked up this library and re-hosted it with some fixes and a few have forked it and slimmed it down to just contain the i2c portion of the library.


All this mess is why I created the hh44780 library package.
The overall goal was to create a library that was easy to install, did not conflict with any other library, and was easier to use than the other existing libraries out there.
I believe it has achieved this especially for hd44780 LCDs with I2c backpacks.

--- bill

Hi Bill,

first of all thank you very much for pointing this out.
From what you have written I believe that your library is much better maintained than these others.

the 44780-controller is for 8bit or 4bit interfaces. You wrote

I believe it has achieved this especially for hd44780 LCDs with I2c backpacks.

does this mean that your "hh44780 library package" is working with I2C-LCDs?
And is a "hh44780 library package" somehow different from a library?
(which in my limited knowledge is just a *.h-file plus a *.cpp-file

I want to make a suggestion:
I looked up your library in the Arduino-libary-manager and filtered with keyword "44780" was found

then I filtered with keywords "I2C LCD" was found too.

But as there are so many many libraries all just listed up I would have never chosen your library.
The titlename is just "HD44780" and the text mentions I2C at the end.
From a quick crossreading I would have not recognized that your library can handle I2C.

So my suggestion is to rethink about the title or the first sentence of the description shown in the library manager.

And as a general question and suggestion: Does there exist a website that has ratings for libraries?
It is completely OK for me that anybody can contribute with his on libraries over Github.

I guess I'm unable to convince the "Chief-Arduino-Team" to do some classifying of the libraries in at least two groups
well tested and maintained by experts

and the "others". So an extra website that has ratings would help.

best regards Stefan

StefanL38:
the 44780-controller is for 8bit or 4bit interfaces. You wrote
does this mean that your "hh44780 library package" is working with I2C-LCDs?

Depends on what is meant by "I2C-LCDs".
There are many different types of LCD devices that use i2c for communication.
For LCDs that use i2c, the hd44780 library supports hd44780 compatible devices that use pcf8574 and MCP23008 i/o expander chips on backpacks as well as some LCD devices that have native i2c.

And is a "hh44780 library package" somehow different from a library?
(which in my limited knowledge is just a *.h-file plus a *.cpp-file

A single .h and single .cpp is just one possibility of an implementation of Arduino library.
The hd44780 library is an extensible layered library that supports multiple devices using i/o classes.
The base class handles the hd44780 instructions and timing while the i/o classes handle the i/o communication for the instructions and data transfers.
Each i/o class has its own examples and documentation.
That is why I call it a "package" as it isn't a simple / typical Arduino library for a single device.

But as there are so many many libraries all just listed up I would have never chosen your library.
The titlename is just "HD44780" and the text mentions I2C at the end.
From a quick crossreading I would have not recognized that your library can handle I2C.
So my suggestion is to rethink about the title or the first sentence of the description shown in the library manager.

The library is infinitely extensible. It is not possible to put everything supported in the title.
Plus just saying that it supports I2C isn't enough information.

In the description that shows up in the Arduino IDE library manager, the hd44780 library does say that it supports PCF8574 based backpacks, which is the type of device you appear to be using given the sample code you have shown.

And as a general question and suggestion: Does there exist a website that has ratings for libraries?

Not that I know of.

I guess I'm unable to convince the "Chief-Arduino-Team" to do some classifying of the libraries in at least two groups
well tested and maintained by experts
and the "others". So an extra website that has ratings would help.

It is an impossible task. Who knows what "well tested" and "experts" really means?
The Arduino playground wiki was an attempt to have a user contributed web area that could provide some of that type of information.
I thought it seemed to work quite well for a while then it started to get abused and was eventually locked down to read only.

There are some references to the hd44780 library on the now read-only playground:
https://playground.arduino.cc/Code/LCDi2c/
https://playground.arduino.cc/Code/LCD/
https://playground.arduino.cc/Main/LibraryList/#LCD
https://playground.arduino.cc/Code/LCDAPI/