# Pcf8574 i2c adapter modified for LCD 4004 with clear

**URL:** <https://forum.arduino.cc/t/pcf8574-i2c-adapter-modified-for-lcd-4004-with-clear/985110>\
**Category:** Displays\
**Created:** [April 26, 2022, 2:22am UTC](https://forum.arduino.cc/t/pcf8574-i2c-adapter-modified-for-lcd-4004-with-clear/985110 "2022-04-26T02:22:50Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![sineirm](https://avatars.discourse-cdn.com/v4/letter/s/8baadc/32.png) [@sineirm](https://forum.arduino.cc/u/sineirm)\
**Post date:** [April 26, 2022, 2:22am UTC](https://forum.arduino.cc/t/pcf8574-i2c-adapter-modified-for-lcd-4004-with-clear/985110/1 "2022-04-26T02:22:50Z")

</div>

Hello everybody.  
A while ago I got a 40x4 LCD module (4004), and looking for a code I found the post below.  
After some small modifications I managed to solve the problem of it not clearing the display completely (clear).

[Original post](https://forum.arduino.cc/t/lcd-4004-40x4-i2c-working/473183) and mod for I2c adaper.

[I2C adapter original hardware here](https://avrhelp.mcselec.com/index.html?lcd_i2c_pcf8574.htm)

Solution problem "clear" and idea for more:

**LiquidCrystal\_I2C\_4004.h**

```cpp
#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

```

_ **LiquidCrystal\_I2C\_4004.cpp** _

```cpp
#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);
}

```

_ **Arduino Code test:** _

```cpp
#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("Coordenadas 0,0");
  lcd.setCursor(0, 1);
  blink_led();
  lcd.print("Coordenadas 0,1");
  blink_led();
  lcd.setCursor(0, 2);
  lcd.print("Coordenadas 0,2");
  blink_led();
  lcd.setCursor(0, 3);
  lcd.print("Coordenadas 0,3");
  blink_led();
  xyz(0);
  xyz(1);
  lcd.clear(1);
  lcd.clear(0);
  lcd.setCursor(20, 0);
  lcd.print("Coordenadas 20,0");
  lcd.setCursor(20, 1);
  lcd.print("Coordenadas 20,1");
  delay(5000);
  lcd.clear(0);
  lcd.setCursor(20, 2);
  lcd.print("Coordenadas 20,2");
  lcd.setCursor(20, 3);
  lcd.print("Coordenadas 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);
  }
}

```

---

<div class="post-metadata">

**Author:** ![system](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/system/32/1140315_2.png) [@system](https://forum.arduino.cc/u/system)\
**Post date:** [October 23, 2022, 2:23am UTC](https://forum.arduino.cc/t/pcf8574-i2c-adapter-modified-for-lcd-4004-with-clear/985110/2 "2022-10-23T02:23:28Z")

</div>

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.
