A simple custom I2C character LCD interface for Arduino projects

Character LCDs are a fantastic and cost-effective option when your project calls for a user-friendly output method. Besides being cheap and easy to use, these displays often offer enough usable screen real-estate for displaying simple status messages and interactive menu screens. However, the standard 16-pin interface can be quite a hassle to work with, and all the wires quickly clutter up your previously simple Arduino project. While there are some I2C character LCDs out in the wild, these models are often more expensive and sometimes difficult to work with. Therefore, I decided to build a simple-to-use alternative that allows you to control pretty much any standard 14 and 16-pin LCD display with only four wires.

Introduction to the project

Using a character LCD with an Arduino (or any other MCU for that matter) typically requires you to connect four supply wires, three control lines, and eight parallel data lines. An additional analog input pin allows you to adjust the contrast. Note that most displays let you use only four data wires. In that case, you’ll have to transmit the display command using two half-bytes. In addition, some LCDs only have fourteen input pins due to the absence of an LED backlight. Sometimes, the backlight is already internally connected to the logic-supply pins.

Figure 1: A simple project that utilizes a standard 16×2 character LCD. Note the large number of GPIO pins that the display occupies.

As the example in fig. 1 shows, using the four-wire method reduces the number of occupied GPIO pins. However, you’ll still have to make quite a few connections, which makes working with a character LCD more troublesome than it should be. My goal for this project is to make the schematic look something like this:

Figure 2: The goal of this project is to build a simple I2C interface that makes it easier to use standard character LC-displays with an Arduino. Note how this set up only requires two communication lines (pull-up resistors omitted) and two power wires.

Note that I also want to remove the need for an external contrast-adjustment potentiometer. Instead, I want to include it on the finished expansion board. I think it’s also a good idea to add a few buttons or a simple beeper next to the display. However, I’ll omit those here as my main focus is to build a display adapter board that makes it easier to use a standard character LCD in Arduino projects.

I’d also like to mention that I’ll only utilize the display to show standard ASCII characters. Therefore, I’ll ignore custom characters in this project. However, you can change the software to include custom characters if you need them in your project.

Designing the adapter board

Designing such an adapter board shouldn’t be too difficult. All we need is a 16-pin header for connecting the LCD, an MCU that takes control over the display, two trimmers, and several components to make the MCU work. My idea is to outsource the LCD control functionality. Instead of letting the Arduino-board control the display, I put the second microcontroller in charge of communicating with the LCD. That second MCU receives commands from the Arduino via I2C so that it knows what to display. Note that I use the Arduino as an example here. In reality, you can use any other I2C compatible device. In addition to reducing the number of wires, this approach also has the added benefit of making the main MCU easier to program, especially if there’s no readily available LCD control library that you can use. As long as the MCU supports I2C, you’re good to go because the secondary controller takes care of communicating with the display.

I’m planning to use an ATMega328PU as the secondary MCU. This is the same chip that enables the Arduino UNO, and I demonstrated how to utilize it in your projects in the capacitive touch sensor series. Therefore, I’ll skip the details here. Instead, I’ll give you the complete schematic diagram right away:

Figure 3: The schematic of the custom I2C LCD adapter board. Click here to view a high-res version.

As you can see, there’s not a whole lot to see here other than what I already mentioned earlier. Apart from the MCU and the components that go along with it, I added two trimmers and connected the 16-pin header to the MCU, as described in figure one. Note that I decided to swap some connections to make the PCB easier to route:

Figure 4: The second revision of the adapter board PCB design

Note that fig. 4 shows revision B of the board. In the first revision, I forgot to include the I2C pull-up resistors, and that design relied on external components. However, as the entire goal of this project is to reduce the need for external components, I included the pull-up resistors in the PCB design. According to a discussion I linked below, the ATMega328PU, combined with the Arduino Wire library, uses internal pull-up resistors on the I2C lines. Therefore, you can omit the two 10K resistors between the two trimmers. Either way, here’s an image of the prototype that I assembled on a piece of perfboard:

Figure 5: The top side of the I2C LCD Adapter prototype that I assembled on a piece of perfboard

As you can see, I used a 28-pin DIP socket so that I can easily remove the MCU when I want to flash or update the firmware. I recommend that you also do it this way because those ATMega328PU ICs can be rather expensive. Also: please note that I’m aware that cheap I2C to LCD adapters exist and that you can buy them on various sites. However, this blog isn’t about buying stuff. It’s about making as much as you can yourself. I also like the challenge and the added customizability of homemade solutions.

Download or order the PCB design

You can quickly order the PCB design on pcbway. Note that this is an affiliate link, and I’ll receive a small percentage of what you spend as a coupon to spend on pcbway for my own projects.

I’ve also uploaded the EAGLE files here for the ones of you who wish to order the PCBs somewhere else, want to modify the design, or etch them at home.

The software

There’s not a whole lot to say about the software. It utilizes the Arduino I2C library called Wire to receive incoming data. Then, the adapter-board MCU interprets the incoming bytes. It supports three commands:

INIT_W_HW … Display Width (e.g., 16 columns)
H … Display Height (e.g., 2 rows)
Initializes the display and sets the display width and height
CLEARNoneClears the display
CURSOR_X_YX … Cursor position X
Y … Cursor position Y
Moves the cursor to the specified X and Y coordinates on the display. Note that the coordinates start at 0;0. To move the display to the first position, use X = 0 and Y = 0. To move it to the start of the second row, use X = 0 and Y = 1.

I uploaded the code with comments to GitHub, and I also added a usage example that demonstrates how sending the commands works. As I didn’t go into detail here, please don’t hesitate to leave a comment or contact me if you have any questions! The code is open-source, so please feel free to contribute if you have suggestions or find a bug. Also, as usual, feel free to share the code, but please include a link to this page (or the GitHub repository), and don’t use it commercially!

Figure 6: The finished project works as intended! The Arduino UNO only requires four wires to use the display.


Working with 14/16-pin LCDs can be quite a hassle, and all the wires can quickly clutter up your otherwise clean and uncomplicated Arduino project. Therefore, I decided to design an adapter board that outsources the LCD communication to another MCU. The main MCU then communicates with the adapter board via I2C. Doing so only requires four instead of up to 16 wires.


[Figure 1] Arduino Reaction Tester – digikey.com
How to connect an LCD to your Arduino – digikey.com
Arduino UNO I2C pull-up resistor discussion – arduino.cc

Leave your two cents, comment here!

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.