Need Code for ESP32 to 4004 Character LCD Module with I2C Interface

Does anyone have code for an ESP32 to write to and control a 4004 Character LCD module with an I2C interface?

The part number of the display with the plug in I2C interface is 4004A2-BLW-IIC made by Sureno from AliExpress.

I checked with Surenoo and they only have code for the Arduino.

The ESP32 board I'm using is a SparkFun IoT RedBoard - ESP32 Development Board

If the code doesn't work perhaps with several minor adjustments then you probably need to change boards.

In what way is Surenoo's code "only for the Arduino"? Does it use register level code?

Here is the Arduino code from Surenno. Not sure if it will work on an ESP32? Not sure if the Include and define statements are compatible the the ESP32. I'm a hardware guy, not a software guy. Any help would be appreciated.

#ifndef FDB_LIQUID_CRYSTAL_I2C_H
#define FDB_LIQUID_CRYSTAL_I2C_H

#include <inttypes.h>
#include <Print.h>

// commands
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20    
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80

// flags for display entry mode
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00

// flags for display on/off control
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00

// flags for display/cursor shift
#define LCD_DISPLAYMOVE 0x08
#define LCD_CURSORMOVE 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00

// flags for function set
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_1LINE 0x00
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00

// flags for backlight control
#define LCD_BACKLIGHT 0x08
#define LCD_NOBACKLIGHT 0x00

#define En B00000100  // Enable bit

// Add 4004 LCD
// Connect Pin 5 of LCD (R/W) to GND
// Connect Pin 5 of I2C interface to Enable 2 of LCD
//#define Rw B00000010  // Read/Write bit
#define En2 B00000010  // Enable 2 bit

#define Rs B00000001  // Register select bit


/**
* This is the driver for the Liquid Crystal LCD displays that use the I2C bus.
*
* After creating an instance of this class, first call begin() before anything else.
* The backlight is on by default, since that is the most likely operating mode in
* most cases.
*/
class LiquidCrystal_I2C : public Print {
public:
/**
* Constructor
*
* @param lcd_addr I2C slave address of the LCD display. Most likely printed on the
* LCD circuit board, or look in the supplied LCD documentation.
* @param lcd_cols Number of columns your LCD display has.
* @param lcd_rows Number of rows your LCD display has.
* @param charsize The size in dots that the display has, use LCD_5x10DOTS or LCD_5x8DOTS.
*/
LiquidCrystal_I2C(uint8_t lcd_addr, uint8_t lcd_cols, uint8_t lcd_rows, uint8_t charsize = LCD_5x8DOTS);

/**
* Set the LCD display in the correct begin state, must be called before anything else is done.
*/
void begin();

/**
 * Remove all the characters currently shown. Next print/write operation will start
 * from the first position on LCD display.
 */
void clear(uint8_t);

/**
* Next print/write operation will will start from the first position on the LCD display.
*/
void home();

/**
 * Do not show any characters on the LCD display. Backlight state will remain unchanged.
 * Also all characters written on the display will return, when the display in enabled again.
 */
void noDisplay();

/**
* Show the characters on the LCD display, this is the normal behaviour. This method should
* only be used after noDisplay() has been used.
*/
void display();

/**
* Do not blink the cursor indicator.
*/
void noBlink();

/**
* Start blinking the cursor indicator.
*/
void blink();

/**
* Do not show a cursor indicator.
*/
void noCursor();

/**
* Show a cursor indicator, cursor can blink on not blink. Use the
* methods blink() and noBlink() for changing cursor blink.
*/
void cursor();

void scrollDisplayLeft(uint8_t value);
void scrollDisplayRight(uint8_t value);
void printLeft();
void printRight();
void leftToRight();
void rightToLeft();
void shiftIncrement();
void shiftDecrement();
void noBacklight();
void backlight();
bool getBacklight();
void autoscroll();
void noAutoscroll();
void createChar(uint8_t, uint8_t[]);
void setCursor(uint8_t, uint8_t);
virtual size_t write(uint8_t);
void command(uint8_t);

inline void blink_on() { blink(); }
inline void blink_off() { noBlink(); }
inline void cursor_on() { cursor(); }
inline void cursor_off() { noCursor(); }

// Compatibility API function aliases
void setBacklight(uint8_t new_val); // alias for backlight() and nobacklight()
void load_custom_character(uint8_t char_num, uint8_t *rows); // alias for createChar()
void printstr(const char[]);

private:
void send(uint8_t, uint8_t);
void write4bits(uint8_t);
void expanderWrite(uint8_t);
void pulseEnable(uint8_t);
uint8_t _addr;
uint8_t _displayfunction;
uint8_t _displaycontrol;
uint8_t _displaymode;
uint8_t _cols;
uint8_t _rows;
uint8_t _charsize;
uint8_t _backlightval;

// Add for 4004 LCD
bool _useTwoChips;
bool _useEnable2;
};

#endif // FDB_LIQUID_CRYSTAL_I2C_H

Do the logic levels of the modules match?

That's just the library header file. All it tells me is that they likely modified the LiquidCrystal_I2C library. Without seeing the .cpp file(s), that's all I can say.

here is the .ino file, .h and cpp file

.ino FILE

#include <LiquidCrystal_I2C_4004.h>

#include <avr/pgmspace.h>
#include <LiquidCrystal_I2C_4004.h>

#define LED 13

byte textLen;

const byte lcdAddr = 0x27;  // Address of I2C backpack
const byte lcdCols = 40;    // Number of character in a row
const byte lcdRows = 4;     // Number of lines
LiquidCrystal_I2C lcd(lcdAddr, lcdCols, lcdRows); // set the LCD address to 0x27 for a 16 chars and 2 line display

const unsigned int scrollDelay = 100;   // Miliseconds before scrolling next char
const unsigned int demoDelay = 2000;
void xyz(void);
void setup() {

  lcd.begin();
  delay(1000);
  lcd.home();
  lcd.backlight();
  pinMode(LED, OUTPUT);
  lcd.setCursor(0, 0); lcd.print("...blink......");
  lcd.blink(); delay(2000);
  lcd.setCursor(0, 0); lcd.print("...noBlink....");
  lcd.noBlink(); delay(2000);
  lcd.setCursor(0, 0); lcd.print("...cursor.....");
  lcd.cursor(); delay(2000);
  lcd.setCursor(0, 0); lcd.print("...noCursor...");
  lcd.noCursor(); delay(2000);
  lcd.setCursor(0, 0); lcd.print(".backlight off..");
  lcd.noBacklight(); delay(2000);
  lcd.setCursor(0, 0); lcd.print(".backlight on..");
  lcd.backlight(); delay(2000);

}

void loop() {

  lcd.clear(0);
  lcd.clear(1);
  lcd.setCursor(0, 0);
  lcd.print("Surenoo 4004LCD 0,0");
  lcd.setCursor(0, 1);
  blink_led();
  lcd.print("Surenoo 4004LCD 0,1");
  blink_led();
  lcd.setCursor(0, 2);
  lcd.print("Surenoo 4004LCD 0,2");
  blink_led();
  lcd.setCursor(0, 3);
  lcd.print("Surenoo 4004LCD 0,3");
  blink_led();
  xyz(0);
  xyz(1);
  lcd.clear(1);
  lcd.clear(0);
  lcd.setCursor(20, 0);
  lcd.print("Surenoo 4004LCD 20,0");
  lcd.setCursor(20, 1);
  lcd.print("Surenoo 4004LCD 20,1");
  delay(5000);
  lcd.clear(0);
  lcd.setCursor(20, 2);
  lcd.print("Surenoo 4004LCD 20,2");
  lcd.setCursor(20, 3);
  lcd.print("Surenoo 4004LCD 20,3");
  delay(5000);
  lcd.clear(1);
  lcd.setCursor(0, 0);
  lcd.print(".Teste.");
  for (int x = 0; x < 34; x++)
  {
    lcd.setCursor(x, 0);
    lcd.print(" Teste ");
    blink_led();
  }
  for (int x = 33; x > 0; x--)
  {
    lcd.setCursor(x, 0);
    lcd.print(" Teste ");
    blink_led();
  }

}

void xyz(uint8_t nlcd) {
  // Scroll text to the right back to original position
  for (byte positionCounter = 0; positionCounter < lcdCols; positionCounter++) {
    lcd.scrollDisplayLeft(nlcd);
    delay(scrollDelay);
  }


  // Scroll entire text in a row to the left outside the screen
  for (byte positionCounter = 0; positionCounter < textLen; positionCounter++) {
    lcd.scrollDisplayLeft(nlcd);
    delay(scrollDelay);
  }
  // Scroll hidden text through entire row to the right outside the screen
  for (byte positionCounter = 0; positionCounter < textLen + lcdCols; positionCounter++) {
    lcd.scrollDisplayRight(nlcd);
    delay(scrollDelay);
  }
  // Scroll text to the right back to original position
  for (byte positionCounter = 0; positionCounter < lcdCols; positionCounter++) {
    lcd.scrollDisplayLeft(nlcd);
    delay(scrollDelay);
  }

}
void blink_led() {
  for (int i = 0; i < 5; i++) {
  digitalWrite(LED, HIGH);
  delay(100);
  digitalWrite(LED, LOW);
  delay(100);
  }
}

.h FILE

#ifndef FDB_LIQUID_CRYSTAL_I2C_H
#define FDB_LIQUID_CRYSTAL_I2C_H

#include <inttypes.h>
#include <Print.h>

// commands
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20    
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80

// flags for display entry mode
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00

// flags for display on/off control
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00

// flags for display/cursor shift
#define LCD_DISPLAYMOVE 0x08
#define LCD_CURSORMOVE 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00

// flags for function set
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_1LINE 0x00
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00

// flags for backlight control
#define LCD_BACKLIGHT 0x08
#define LCD_NOBACKLIGHT 0x00

#define En B00000100  // Enable bit

// Add 4004 LCD
// Connect Pin 5 of LCD (R/W) to GND
// Connect Pin 5 of I2C interface to Enable 2 of LCD
//#define Rw B00000010  // Read/Write bit
#define En2 B00000010  // Enable 2 bit

#define Rs B00000001  // Register select bit


/**
* This is the driver for the Liquid Crystal LCD displays that use the I2C bus.
*
* After creating an instance of this class, first call begin() before anything else.
* The backlight is on by default, since that is the most likely operating mode in
* most cases.
*/
class LiquidCrystal_I2C : public Print {
public:
/**
* Constructor
*
* @param lcd_addr I2C slave address of the LCD display. Most likely printed on the
* LCD circuit board, or look in the supplied LCD documentation.
* @param lcd_cols Number of columns your LCD display has.
* @param lcd_rows Number of rows your LCD display has.
* @param charsize The size in dots that the display has, use LCD_5x10DOTS or LCD_5x8DOTS.
*/
LiquidCrystal_I2C(uint8_t lcd_addr, uint8_t lcd_cols, uint8_t lcd_rows, uint8_t charsize = LCD_5x8DOTS);

/**
* Set the LCD display in the correct begin state, must be called before anything else is done.
*/
void begin();

/**
 * Remove all the characters currently shown. Next print/write operation will start
 * from the first position on LCD display.
 */
void clear(uint8_t);

/**
* Next print/write operation will will start from the first position on the LCD display.
*/
void home();

/**
 * Do not show any characters on the LCD display. Backlight state will remain unchanged.
 * Also all characters written on the display will return, when the display in enabled again.
 */
void noDisplay();

/**
* Show the characters on the LCD display, this is the normal behaviour. This method should
* only be used after noDisplay() has been used.
*/
void display();

/**
* Do not blink the cursor indicator.
*/
void noBlink();

/**
* Start blinking the cursor indicator.
*/
void blink();

/**
* Do not show a cursor indicator.
*/
void noCursor();

/**
* Show a cursor indicator, cursor can blink on not blink. Use the
* methods blink() and noBlink() for changing cursor blink.
*/
void cursor();

void scrollDisplayLeft(uint8_t value);
void scrollDisplayRight(uint8_t value);
void printLeft();
void printRight();
void leftToRight();
void rightToLeft();
void shiftIncrement();
void shiftDecrement();
void noBacklight();
void backlight();
bool getBacklight();
void autoscroll();
void noAutoscroll();
void createChar(uint8_t, uint8_t[]);
void setCursor(uint8_t, uint8_t);
virtual size_t write(uint8_t);
void command(uint8_t);

inline void blink_on() { blink(); }
inline void blink_off() { noBlink(); }
inline void cursor_on() { cursor(); }
inline void cursor_off() { noCursor(); }

// Compatibility API function aliases
void setBacklight(uint8_t new_val); // alias for backlight() and nobacklight()
void load_custom_character(uint8_t char_num, uint8_t *rows); // alias for createChar()
void printstr(const char[]);

private:
void send(uint8_t, uint8_t);
void write4bits(uint8_t);
void expanderWrite(uint8_t);
void pulseEnable(uint8_t);
uint8_t _addr;
uint8_t _displayfunction;
uint8_t _displaycontrol;
uint8_t _displaymode;
uint8_t _cols;
uint8_t _rows;
uint8_t _charsize;
uint8_t _backlightval;

// Add for 4004 LCD
bool _useTwoChips;
bool _useEnable2;
};

#endif // FDB_LIQUID_CRYSTAL_I2C_H

.cpp FILE

#include "LiquidCrystal_I2C_4004.h"
#include <inttypes.h>
#include <Arduino.h>
#include <Wire.h>

LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_addr, uint8_t lcd_cols, uint8_t lcd_rows, uint8_t charsize)
{
  _addr = lcd_addr;
  _cols = lcd_cols;
  _rows = lcd_rows;
  _charsize = charsize;
  _backlightval = LCD_BACKLIGHT;

  // Add for 4004 LCD
  if (lcd_cols == 40 && lcd_rows == 4)
    _useTwoChips = true;
  else
    _useTwoChips = false;
    _useEnable2 = false;
}

void LiquidCrystal_I2C::begin() {
  Wire.begin();
  _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;

  if (_rows > 1) {
    _displayfunction |= LCD_2LINE;
  }

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

  // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
  // according to datasheet, we need at least 40ms after power rises above 2.7V
  // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
  delay(50);

  // Now we pull both RS and R/W low to begin commands
  expanderWrite(_backlightval); // reset expanderand turn backlight off (Bit 8 =1)
  delay(1000);

  //put the LCD into 4 bit mode
  // this is according to the hitachi HD44780 datasheet
  // figure 24, pg 46

  // we start in 8bit mode, try to set 4 bit mode
  write4bits(0x03 << 4);
  delayMicroseconds(4500); // wait min 4.1ms

  // second try
  write4bits(0x03 << 4);
  delayMicroseconds(4500); // wait min 4.1ms

  // third go!
  write4bits(0x03 << 4);
  delayMicroseconds(150);

  // finally, set to 4-bit interface
  write4bits(0x02 << 4);

  // set # lines, font size, etc.
  command(LCD_FUNCTIONSET | _displayfunction);

  // turn the display on with no cursor or blinking default
  _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
  display();

  // clear it off
  clear(0);

  // Initialize to default text direction (for roman languages)
  _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;

  // set the entry mode
  command(LCD_ENTRYMODESET | _displaymode);

  home();

  if (_useTwoChips) {
    _useEnable2 = true;
    // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
    // according to datasheet, we need at least 40ms after power rises above 2.7V
    // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
    delay(50);

    // Now we pull both RS and R/W low to begin commands
    expanderWrite(_backlightval); // reset expanderand turn backlight off (Bit 8 =1)
    delay(1000);

    //put the LCD into 4 bit mode
    // this is according to the hitachi HD44780 datasheet
    // figure 24, pg 46

    // we start in 8bit mode, try to set 4 bit mode
    write4bits(0x03 << 4);
    delayMicroseconds(4500); // wait min 4.1ms

    // second try
    write4bits(0x03 << 4);
    delayMicroseconds(4500); // wait min 4.1ms

    // third go!
    write4bits(0x03 << 4);
    delayMicroseconds(150);

    // finally, set to 4-bit interface
    write4bits(0x02 << 4);

    // set # lines, font size, etc.
    command(LCD_FUNCTIONSET | _displayfunction);

    // turn the display on with no cursor or blinking default
    _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
    display();

    // clear it off
    clear(1);

    // Initialize to default text direction (for roman languages)
    _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;

    // set the entry mode
    command(LCD_ENTRYMODESET | _displaymode);

    home();

    _useEnable2 = false;
  }
}

/********** high level commands, for the user! */
void LiquidCrystal_I2C::clear(uint8_t value) {
  _useEnable2 = false;
  if (value==1){
  _useEnable2 = true;
  }
  command(LCD_CLEARDISPLAY);// clear display, set cursor position to zero
  delayMicroseconds(2000);  // this command takes a long time!
}

void LiquidCrystal_I2C::home() {
  command(LCD_RETURNHOME);  // set cursor position to zero
  delayMicroseconds(2000);  // this command takes a long time!
}

void LiquidCrystal_I2C::setCursor(uint8_t col, uint8_t row) {
  int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
  //int row_offsets4004[] = { 0x00, 0x40, 0x14, 0x54 }; // teste?
  int row_offsets4004[] = { 0x00, 0x40, 0x00, 0x40 }; // ORIGINAL

  if (row > _rows) {
    row = _rows - 1;  // we count rows starting w/0
  }
  if (_useTwoChips) {    // if 4004
    if ( row > 1)
      _useEnable2 = true;
    else
      _useEnable2 = false;
    command(LCD_SETDDRAMADDR | (col + row_offsets4004[row]));
  } else {   // if 1602 or other
    command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
  }

}

// Turn the display on/off (quickly)
void LiquidCrystal_I2C::noDisplay() {
  _displaycontrol &= ~LCD_DISPLAYON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal_I2C::display() {
  _displaycontrol |= LCD_DISPLAYON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Turns the underline cursor on/off
void LiquidCrystal_I2C::noCursor() {
  _displaycontrol &= ~LCD_CURSORON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal_I2C::cursor() {
  _displaycontrol |= LCD_CURSORON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Turn on and off the blinking cursor
void LiquidCrystal_I2C::noBlink() {
  _displaycontrol &= ~LCD_BLINKON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal_I2C::blink() {
  _displaycontrol |= LCD_BLINKON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// These commands scroll the display without changing the RAM
void LiquidCrystal_I2C::scrollDisplayLeft(uint8_t value) {
  _useEnable2 = false;
  if (value==1){
  _useEnable2 = true;
  }
  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
}
void LiquidCrystal_I2C::scrollDisplayRight(uint8_t value) {
  _useEnable2 = false;
  if (value==1){
  _useEnable2 = true;
  }
  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}

// This is for text that flows Left to Right
void LiquidCrystal_I2C::leftToRight(void) {
  _displaymode |= LCD_ENTRYLEFT;
  _useEnable2 = true;
  command(LCD_ENTRYMODESET | _displaymode);
  _useEnable2 = false;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This is for text that flows Right to Left
void LiquidCrystal_I2C::rightToLeft(void) {
  _displaymode &= ~LCD_ENTRYLEFT;
  _useEnable2 = true;
  command(LCD_ENTRYMODESET | _displaymode);
  _useEnable2 = false;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This will 'right justify' text from the cursor
void LiquidCrystal_I2C::autoscroll(void) {
  _displaymode |= LCD_ENTRYSHIFTINCREMENT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This will 'left justify' text from the cursor
void LiquidCrystal_I2C::noAutoscroll(void) {
  _displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// Allows us to fill the first 8 CGRAM locations
// with custom characters
void LiquidCrystal_I2C::createChar(uint8_t location, uint8_t charmap[]) {
  location &= 0x7; // we only have 8 locations 0-7
  command(LCD_SETCGRAMADDR | (location << 3));
  for (int i = 0; i < 8; i++) {
    write(charmap[i]);
  }
}

// Turn the (optional) backlight off/on
void LiquidCrystal_I2C::noBacklight(void) {
  _backlightval = LCD_NOBACKLIGHT;
  expanderWrite(0);
}

void LiquidCrystal_I2C::backlight(void) {
  _backlightval = LCD_BACKLIGHT;
  expanderWrite(0);
}
bool LiquidCrystal_I2C::getBacklight() {
  return _backlightval == LCD_BACKLIGHT;
}


/*********** mid level commands, for sending data/cmds */

inline void LiquidCrystal_I2C::command(uint8_t value) {
  send(value, 0);
}

inline size_t LiquidCrystal_I2C::write(uint8_t value) {
  send(value, Rs);
  return 1;
}


/************ low level data pushing commands **********/

// write either command or data
void LiquidCrystal_I2C::send(uint8_t value, uint8_t mode) {
  uint8_t highnib = value & 0xf0;
  uint8_t lownib = (value << 4) & 0xf0;
  write4bits((highnib) | mode);
  write4bits((lownib) | mode);
}

void LiquidCrystal_I2C::write4bits(uint8_t value) {
  expanderWrite(value);
  pulseEnable(value);
}

void LiquidCrystal_I2C::expanderWrite(uint8_t _data) {
  Wire.beginTransmission(_addr);
  Wire.write((int)(_data) | _backlightval);
  Wire.endTransmission();
}

void LiquidCrystal_I2C::pulseEnable(uint8_t _data) {
  if (_useEnable2) {
    expanderWrite(_data | En2); // En high
    delayMicroseconds(1); // enable pulse must be >450ns

    expanderWrite(_data & ~En2); // En low
    delayMicroseconds(50); // commands need > 37us to settle
  } else {
    expanderWrite(_data | En); // En high
    delayMicroseconds(1); // enable pulse must be >450ns

    expanderWrite(_data & ~En); // En low
    delayMicroseconds(50); // commands need > 37us to settle
  }

}

void LiquidCrystal_I2C::load_custom_character(uint8_t char_num, uint8_t *rows) {
  createChar(char_num, rows);
}

void LiquidCrystal_I2C::setBacklight(uint8_t new_val) {
  if (new_val) {
    backlight(); // turn backlight on
  } else {
    noBacklight(); // turn backlight off
  }
}

void LiquidCrystal_I2C::printstr(const char c[]) {
  //This function is not identical to the function used for "real" I2C displays
  //it's here so the user sketch doesn't have to be changed
  print(c);
}

Not sure I will have to check

please link the product so we can find the datasheet/description of that product.

there is no datasheet on the display. The I2C interface board has a PCF8574T

There are several different adapter boards on the market.
Please don't less us guess what you have gotten.
If you can't provide a link, make some good pictures.
Then include how you have wired the LCD to the adapter and the adapter to the ESP.

The I2c board comes with the display. I have not tried it out yet. The company has only provided me with Arduino code which I have posted. I need to use an ESP32 instead
Here is a link to the companies website where I purchased the board.
display with i2c board

"Need Code" sound selfish and like "can somebody write the complete code for me"

The Arduino-Forum has a search-function
The search-function found this thread

I2C is I2C .
And the PCF8574 is the most common used chip for these adapters

Here is a WOKWI-Simulation that uses an ESP32 and a 20x4 Display.
This display works with the standard-"Arduino"-libraries.

The advantage of this modular-concept of libraries is that
higher-protocol-levels of any kind can be used with different microcontrollers.

If you lookup the library-files where the lib-files are stored and there is no special version for your microcontroller then it will work with your microcontroller.

example for a special library that is stored in the hardware-package
....packages\arduino\ hardware\esp32 \2.0.17\tools\sdk\esp32s3\include\esp_wifi\include\esp_wifi.h

user @noiasca

has written a library that can be used for 40x4-LCds
This library offers even language-specific characters

I successfully compiled the example-code for an ESP32
which is this one
....sketchbook\libraries\NoiascaLiquidCrystal\examples\02_I2C_PCF8574\0209_I2C_4004

You can download the library from here
https://werner.rothschopf.net/microcontroller/202012_arduino_liquid_crystal_language_en.htm

The manufacturers website

says
SLC4004B is 40 characters wide, 4 rows character lcd module, AIP31066 controller (Industry-standard HD44780 compatible controller), 6800 4/8-bit parallel interface,

controller is HD44780-compatibel
I2C-chip is most common
conclusion: should work

1 Like

needs a precise definition of what PCF8574 pin is used for what LCD connection, hence I would like to force the TO to come up with that information, either via a datasheet or with clear pictures.
Otherwise it could happen, that he will not be able to get it working.

Imho worth reading:
https://werner.rothschopf.net/microcontroller/202012_arduino_liquid_crystal_4004_en.htm#caveats

THANKS to everyone for helping me with this problem. I'll try to get it up and running and let you know.

That is the header file (or the .h file if you like). Again. So it is of no help.

No help for ESP32 ? or arduino? If it is of no help will the code still work? If not, what should be in its place?

Have you tried the code with an ESP32? Unless it is doing something AVR specific it may work.

The HD44780 chip set only supports 80 characters. To get 40X4 they treat it as 2 40x2 in one package. The code has to handle the display as 2 different parts. To free up a pin on the I2C interface they tied the R/-W pin low so it is in write only mode. This frees up a pin to use as a second enable.

You have not shown the .cpp so we can't see what changes might be needed for an ESP32

I corrected my previous posting to show both the .h and .cpp files as shown above.

Thanks for the help .

I briefly looked at the .cpp and didn't see any processor specific code. I would suggest trying the code with the ESP32.