When I started working on Open Vapors, I thought the stumbling point would be the PID algorithm or safe AC line control. However, it turned out; I spent a significant amount of time understanding how to print to the Arduino LCD display correctly.
If you need an easy to use RGB LCD, check out the Grove LCD from SeeedStudio. They sent me one to check out. The LCD comes with Seeed’s “grove connector system” which can connect to a variety of their Arduino-compatible boards. You can also pick up the Grove Base Shield which adds a variety of Grove connectors to an Arduino Uno. The Grove LCD makes it super easy to connect up a character LCD. It is very plug-and-play.
As I dig into my latest project, the lessons I learned back then are coming back to me. Here are 7 tips for driving an Arduino LCD display, like one with 2×20 or 4×20 characters.
1. Buffer the Arduino LCD Display
One approach I see many people try with a character LCD is letting their code directly print to the display. Using a simple buffer might look like it adds unnecessary complexity. One positive point is that you get a more predictable behavior. A trade-off is that you do need to use up a few more bytes of RAM. So, how do you implement a buffer?
First, declare some character arrays. Depending on your application, it might make sense to use a multi-dimensional array. If not, I find it more straightforward to have individual arrays for each line. For example, I might declare “line0” and “line1” each with 21 characters. (Remember, you should always have an extra character for the null terminator.)
Modify the buffer when your code needs to make a change on the Arduino LCD display. Modify the character array, aka string, variable.
line0 = 8;
line0 = 5;
line0 = ‘C’
Lastly, use a separate function to lcd.print() each line. Then in your loop() call the subroutine to update the screen. If you have some code, like a large for-loop or a lot of floating point math, you can also call the update display function there. That way the screen stays responsive.
2. Refresh the entire screen every time you change something
The display flickers if it is cleared then re-printed. An I2C serial backpack makes the flicker worse. SPI and UART fare a little better, but there is still some latency.
An alternative is never clear the screen. Just keep re-writing regardless if anything changes. So instead of trying to change one character at a time, print an entire line. The line printing approach works great if you are using a buffer and the print code from above.
This approach does have a minor issue though. Some people misinterpret a bug as something wrong with the display. Let’s look at an example that shows this problem, and another way to modify the buffers.
3. Using sprintf() to clear a character lcd
Pretend we built a temperature logger that prints the current temperature to the character display. While debugging, we keep seeing characters getting left behind or the temperature is displayed wrong.
The code prints “Temperature: 5”.
Then at some point, it gets slightly warmer, so the LCD now shows
Later in the day, it cools off, and now your display is showing
Wait a minute. 85 isn’t colder than 15! And in this case, it did not matter if we displayed in Celsius, Fahrenheit, or Kelvin.
So, what happened? Only the characters that get updated are being, well, updated. The extra “5” is from printing “15.” Nothing is clearing that block on the LCD.
So there are two options here. Clear the display EVERY TIME you go to print to the screen or print blank characters any place that should be blank. The second method is called “padding.”
I would recommend the padding approach for two reasons:
Deterministic Behavior. I prefer operations like screen drawing to always take the same amount of time. Otherwise, you could run into hard to track down timing bugs. So on a 20 character wide display, I always print 20 characters. (Although, I may not print all of the lines at the same time.)
Flickering. The act of clearing the display and then re-printing it could lead to flickering characters. This action is similar to pulse width modulating a LED. And the same limitation applies there. If the flashing is slow enough, human eyes will see it as flashing.
Using sprintf() isn’t difficult, but it does require the use of a buffer. (Sound familiar?) Here I will show you how to use sprintf(). As the name suggests, sprintf() is similar to printf(). If you have never used C’s printf(), check out this explanation for all of its parameters.
Here’s how to left-pad using sprintf().
int temperature = 8;
sprintf(msg, "Temp: %-7d", temperature);
In this case, I picked the value 7, since “Temperature: ” takes up 14 spaces of the 20 space display. Adjust that amount as necessary in your code.
4. How to print floats on an LCD
You might have noticed in my example above; I used integers for temperature. There is a reason: sprintf() does not support floats. Well, more correctly, avr-libc’s implementation does not support floats without a compiler flag. An easy workaround is a function called dtostrf(). (This function appears to be unique to avr-libc.)
// Buffer for float
float temperature = 15.12;
// Now you can sprintf
sprintf(line0, "Temp: %-7sC", float_str); // %6s right pads the string
It takes a double, or float, and converts it into an ASCII string. We can then include this string in the sprintf() call.
5. I2C, use Fast LiquidCrystal
It has been a long time since I wrote the code for Open Vapors. However, I do remember at the time there was a “Fast LiquidCrystal” library. It optimized driving character displays over I2C. The before and after with the library was unbelievable. While still not as fast as directly driving the parallel interface, it was pretty close.
I only mention the age because lots can happen with libraries. If the update rate for your display seems slow, check to see if there is a more optimized library available to drive it.
6. Reversed Row and Column
Maybe this issue is just me, but it seems like row and column are reversed for the LCD commands. To select a cursor location set the column, then the row. Like: lcd.setCursor(2,1). That is the third column on the second row AND NOT the second column on the third row. (Rows and columns are 0-based, fyi).
I have no amazing wisdom bits. It is just a matter of RTFM.
7. Don’t forget the f() macro
Just like debugging with Serial.print(), using lcd.print() can waste RAM. Wrap all strings inside of the famous F() macro. If you have not used it before, you can see my F() macro explanation here. In short, make sure statements that look like this:
Are changed to look like this:
These are my tips, so now it is your turn. What tips have you used when driving an Arduino LCD display?
By clicking “Accept”, you consent to the use of ALL the cookies.
Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.
The _ga cookie, installed by Google Analytics, calculates visitor, session and campaign data and also keeps track of site usage for the site's analytics report. The cookie stores information anonymously and assigns a randomly generated number to recognize unique visitors.
This cookie is installed by Google Analytics.
Set by Google to distinguish users.
Installed by Google Analytics, _gid cookie stores information on how visitors use a website, while also creating an analytics report of the website's performance. Some of the data that are collected include the number of visitors, their source, and the pages they visit anonymously.
This cookie is used for identifying the visitor browser on re-visit to the website.
YouTube sets this cookie via embedded youtube-videos and registers anonymous statistical data.