Editorial Solution

LCD16x2 with I2C module

 

  • The PCF8574 IIC/I2C module for the LCD16x2 is an interface module that is used to communicate with the LCD16x2 display using the I2C (Inter-Integrated Circuit) protocol. 
  • Usually, the LCD16x2 requires a minimum of six GPIO pins (if we use it in 4-bit mode), i.e. higher numbers of GPIOs get engaged.
  •  The I2C LCD Driver Module Only requires 2 GPIOs for an LCD16x2 display, which will be helpful as we need to utilize a smaller number of GPIOs.
  • It simplifies the wiring and control of the LCD by reducing the number of pins required for communication.
  • This module is compatible with both LCD16x2 displays as well as LCD20x4 display
  • This LCD Module includes the POT to adjust the contrast of the LCD and pull-up resistors for SDA and SCL lines. So, we don’t need any additional connections.
  • We just plugin the I2C LCD Module at the back of the 16×2 LCD and make connections between the microcontroller and the I2C LCD Module.

Hardware Connections

Let's go through the hardware connections step by step.

  • Connect 5V and GND from any of the Arduino UNO to the LCD display.
  • Connect SDA and SCL pins of all devices.
  • We are going to use an internal pull-up resistor while connecting SDA and SCL.
  • Connect the potentiometer to Master 2.
  • Make sure the GND of all devices is common.

Circuit connection

Firmware

We have two I2C masters, Master 1 and Master 2, each requiring separate control to interface with the I2C 16x2 LCD. However, the standard LiquidCrystal_I2C library posed the following challenges:

  • Premature Bus Release
    • The original code calls Wire.endTransmission(); to release the I2C bus, allowing other masters to take control.
    • This premature release interferes with Master 1's communication if it hasn't completed its required commands.
  • Data Interference
    • Both masters cannot independently display their data (e.g., ADC values and system start time) without overwriting or interfering with each other.

To resolve these issues, we made custom modifications to the LiquidCrystal_I2C.h library. This allows both masters to communicate with the LCD and display their data independently without interference.

Issue with the Original Code

  • The original expanderWrite() function uses Wire.endTransmission(); to release the I2C bus after sending data.
  • In the Wire.endTransmission() function, the parameter stop determines whether the bus is released.
    • stop: true sends a stop message, releasing the bus after transmission (default behavior).
    • stop: false sends a restart condition, keeping the connection active for further communication.
  • By default, Wire.endTransmission(); uses stop: true, which causes the bus to be released prematurely. This allows other masters to take control of the bus before Master 1 finishes its required set of commands.

Solution

  • We replaced Wire.endTransmission(); with Wire.endTransmission(false);.
    This sends data and generates a restart condition, keeping the connection open for Master 1 to complete its commands.
  • Once Master 1 finishes, it calls Wire.endTransmission(); (with stop: true) to release the bus, allowing Master 2 to send its data.
  • Master 2 follows the same process, ensuring independent and uninterrupted communication.

Code before changes

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


Code after changes:

void LiquidCrystal_I2C::expanderWrite(uint8_t _data) {
  Wire.beginTransmission(_Addr);
  printIIC((int)(_data) | _backlightval);
  Wire.endTransmission(false);
}

 

Recommendation

  • Changes can be made directly to the source library. However, it is recommended to create a separate copy of the original LiquidCrystal_I2C library for custom modifications.

Steps to Create the Custom Library

  1. Create a Folder:
    • Navigate to the Arduino libraries directory.
    • Create a new folder and name it (e.g., MyLiquidCrystal_I2C).
  2. Copy Original Files (.h, .cpp):
    • Copy the .h and .cpp files from the original LiquidCrystal_I2C library into the newly created folder.
    • Rename the files (e.g., MyLiquidCrystal_I2C.h and MyLiquidCrystal_I2C.cpp) to reflect the custom library.
  3. Make Modifications:
    • Open the copied .h and .cpp files in a text editor or IDE.
    • Apply the necessary changes to the code (e.g., modifications to the expanderWrite() function).
  4. Restart the Arduino IDE:
    • Close and reopen the Arduino IDE to allow it to detect and load the newly created library.
  5. Include the Library in Your Sketch:
    • In your Arduino sketch, add the following line to use the custom library:
      #include <MyLiquidCrystal_I2C.h>

For information about how to add a custom library to the Arduino IDE and use examples from it, refer to Adding Library To Arduino IDE.

Code 1 (Master 1 for Timekeeping)

Displays the system's start time in seconds on the I2C16x2LCD display.

#include <MyLiquidCrystal_I2C.h>

// Create an LCD object with the detected I2C address
LiquidCrystal_I2C lcd(0x27, 16, 2); // Replace 0x27 with your LCD's I2C address

void setup() {
  lcd.begin(16, 2);      // Initialize the LCD with 16 columns and 2 rows
  lcd.backlight();       // Turn on the backlight
}

void loop() {
  unsigned long timeSec = millis() / 1000;  // Get system time in seconds
  char buff[20];                         // Buffer for time string
  uint8_t hours = timeSec / 3600;
  uint8_t minutes = (timeSec % 3600) / 60;
  uint8_t seconds = timeSec % 60;
  sprintf(buff, " %02d:%02d:%02d", hours, minutes, seconds);     // Convert time to string
  lcd.setCursor(0, 0);   // Set cursor to the first row, first column
  lcd.print("Uptime: ");
  lcd.setCursor(7, 0);
  lcd.print("          ");              // Print text
  lcd.setCursor(7, 0);
  lcd.print(buff);              // Print text
  Wire.endTransmission();       //It will release the I2C Bus 
  while(millis() % 1000);

}

Code 1 Explanation

  • Purpose: This code functions as an I2C master that interacts with an I2C-based 16x2 LCD to display system uptime in hours, minutes, and seconds.
  • Working:
    • The I2C master initializes the LCD with lcd.begin() and turns on the backlight using lcd.backlight().
    • The system uptime is calculated using millis() and formatted as hh:mm:ss.
    • The LCD is updated to display the formatted time string on the first row.
    • At the end of the loop, Wire.endTransmission() releases the I2C bus, allowing other devices to communicate if needed.
    • Updates are synchronized to happen every second using while(millis() % 1000).

Code 2 (Master 2 for Interfacing with Potentiometer and LCD)

Displays the potentiometer value (from A0) on the I2C16x2LCD display.

 #include <MyLiquidCrystal_I2C.h>

// Create an LCD object with the detected I2C address
LiquidCrystal_I2C lcd(0x27, 16, 2); // Replace 0x27 with your LCD's I2C address

void setup() {
  lcd.begin(16, 2);      // Initialize the LCD with 16 columns and 2 rows
  lcd.backlight();       // Turn on the backlight
}

void loop() {
  uint16_t adcValue = analogRead(A0);  // Read potentiometer value
  char buff[4];                     // Buffer for value
  sprintf(buff, "%d", adcValue);       // Convert value to string
  lcd.setCursor(0, 1);          // Set cursor to the second row, first column
  lcd.print("POT Value: ");  
  lcd.setCursor(11, 1);
  lcd.print("      ");              
  lcd.setCursor(11, 1);
  lcd.print(buff);              
  Wire.endTransmission();		//It will release the I2C Bus
  while(millis() % 1000);        
}

Code 2 Explanation

  • Purpose: This code also acts as an I2C master, but it reads analog input from a potentiometer and displays the value on an I2C-based 16x2 LCD.
  • Working:
    • The LCD is initialized similarly to Code 1.
    • The master reads the potentiometer value using analogRead(A0) and formats it into a string.
    • The formatted value is displayed on the second row of the LCD.
    • Just like Code 1, Wire.endTransmission() releases the I2C bus, ensuring that other devices can access it if required.
    • Updates occur every second.

Output

Hardware Setup


 

Output Video

 

 


 

Submit Your Solution