12
Interfacing with Liquid Crystal Displays

Parts You'll Need for This Chapter

  • Arduino Uno or Adafruit METRO 328
  • USB cable (Type A to B for Uno, Type A to Micro-B for METRO)
  • Half-size or full-size breadboard
  • Assorted jumper wires
  • Pushbuttons (×2)
  • 220Ω resistor
  • 1kΩ resistor
  • 4.7kΩ resistors (×2)
  • 10kΩ resistors (×2)
  • 10kΩ trim potentiometer (might be included with LCD purchase)
  • 9V battery
  • 9V battery clip
  • L7805CV 5V voltage regulator
  • 10μF 50V electrolytic capacitors (×2)
  • 1N4001 diode
  • PN2222 NPN bipolar junction transistor (BJT)
  • 8Ω loudspeaker
  • TC74A0-5.0VAT I2C temperature sensor
  • Miniature 5V DC brushless cooling fan
  • 16×2 character LCD with header pins

One of the best things about designing embedded systems is that they can operate independently from a computer. Up until now, you've been tethered to the computer if you wanted to display any kind of information more complicated than an illuminated LED. By adding a liquid crystal display (LCD) to your Arduino, you can more easily display complex information (sensor values, timing information, settings, progress bars, and so on) directly on your Arduino project without having to interface with the serial monitor through a computer.

In this chapter, you will learn how to connect an LCD to your Arduino, and how to use the Arduino LiquidCrystal library to write text and arbitrary custom characters to your LCD. After you have the basics down, you will add some components from previous chapters to make a simple thermostat capable of obtaining local temperature data, reporting it to you, and controlling a fan to compensate for heat. An LCD will give you live information, a speaker will alert you when the temperature is getting too hot, and the fan will turn on to automatically cool you down.

Setting Up the LCD

To complete the examples in this chapter, you will use a parallel LCD screen. These are extremely common and come in all shapes and sizes. The most common is a 16×2 character display with a single row of 16 pins (14 if it does not have a backlight). In this chapter, you will use a 16-pin LCD display that can show a total of 32 characters (16 columns and 2 rows).

If your display didn't come with a 16-pin header already soldered on, you need to solder one on so that you can easily install it in your breadboard. With the header successfully soldered on, your LCD should look like the one shown in Figure 12-1, and you can insert it into your breadboard.

Image of an LCD with a 16-pin header soldered on that can be inserted into the breadboard.

Figure 12-1: LCD with headers soldered on

Next, you wire up your LCD to a breadboard and to your Arduino. All of these parallel LCD modules have the same pin-out and can be wired in one of two modes: 4-pin or 8-pin mode. You can accomplish everything you want to do using just 4 pins for communication; that's how you'll wire it up. There are also pins for enabling the display, setting the display to command mode or character mode, and setting it to read/write mode. Table 12-1 describes all of these pins.

Table 12-1: Parallel LCD pins

pin Number pin Name pin Purpose
1 VSS Ground connection
2 VDD +5V connection
3 V0 Contrast adjustment (to potentiometer)
4 RS Register selection (Character versus Command)
5 RW Read/write
6 EN Enable
7 D0 Data line 0 (unused in 4-pin mode)
8 D1 Data line 1 (unused in 4-pin mode)
9 D2 Data line 2 (unused in 4-pin mode)
10 D3 Data line 3 (unused in 4-pin mode)
11 D4 Data line 4
12 D5 Data line 5
13 D6 Data line 6
14 D7 Data line 7
15 A Backlight anode
16 K Backlight cathode

Here's a breakdown of the pin connections:

  • The contrast adjustment pin changes how dark the display is. It connects to the center pin of a potentiometer.
  • The register selection pin sets the LCD to command or character mode, so it knows how to interpret the next set of data that is transmitted via the data lines. Based on the state of this pin, data sent to the LCD is interpreted as either a command (for example, to move the cursor) or characters (for example, the letter a).
  • The RW pin is always tied to ground in this implementation, meaning that you are only writing to the display and never reading from it.
  • The EN pin is used to tell the LCD when data is ready.
  • Data pins 4 to 7 are used for actually transmitting data, and data pins 0 to 3 are left unconnected.
  • You can illuminate the backlight by connecting the anode pin to 5V and the cathode pin to ground if you are using an LCD with a built-in resistor for the backlight. If you are not, you must put a current-limiting resistor in-line with the anode or cathode pin. The datasheet for your device will tell you if you need to do this.

You can connect the communication pins of the LCD to any I/O pins on the Arduino. In this chapter, they are connected as shown in Table 12-2.

Reference the wiring diagram shown in Figure 12-2, and hook up your LCD accordingly.

Now your LCD is ready for action! Once you get the code loaded in the next section, you can start displaying text on the screen. The potentiometer will adjust the contrast between the text and the background color of the screen.

Table 12-2: Communication pin Connections

LCD pin Arduino pin Number
RS pin 2
EN pin 3
D4 pin 4
D5 pin 5
D6 pin 6
D7 pin 7
Illustration of a liquid crystal display circuit wired to breadboard and Arduino.

Figure 12-2: LCD wired to breadboard and Arduino

Created with Fritzing

Using the LiquidCrystal Library to Write to the LCD

The Arduino IDE includes the LiquidCrystal library, a set of functions that makes it very easy to interface with the parallel LCD that you are using. The LiquidCrystal library has an impressive amount of functionality, including blinking the cursor, automatically scrolling text, creating custom characters, and changing the direction of text printing. This chapter does not cover every function, but instead gives you the tools you need to interface with the display's most important functions. You can find descriptions of the library functions and examples illustrating their use on the Arduino website, at blum.fyi/arduino-lcd-library. (This is also linked from exploringarduino.com/content2/ch12.)

Adding Text to the Display

In this first example, you add some text and an incrementing number to the display. This exercise demonstrates how to initialize the display, how to write text, and how to move the cursor. First, include the LiquidCrystal library:

#include <LiquidCrystal.h>

Then, initialize an LCD object, as follows:

LiquidCrystal lcd (2,3,4,5,6,7);

The arguments for the LCD initialization represent the Arduino pins connected to RS, EN, D4, D5, D6, and D7, in that order. In the setup, you call the library's begin() function to set up the LCD display with the character size. (The one I'm using is a 16×2 display, but you may be using another size, such as 20×4.) The arguments for this command represent the number of columns and the number of rows, respectively:

lcd.begin(16, 2);

After adding this code, you can call the library's print() and setCursor() commands to print text to a given location on the display. For example, if you want to print my name on the second line, you issue these commands:

lcd.setCursor(0,1);
lcd.print("Jeremy Blum");

The positions on the screen are indexed starting with (0,0) in the top-left position. The first argument of setCursor() specifies the column number, and the second argument specifies the row number. By default, the starting location is (0,0). So, if you call print() without first changing the cursor location, the text starts in the top-left corner.

Using this knowledge, you can now write a simple program that displays some text on the first row and that prints a counter that increments once every second on the second row. Listing 12-1 shows the complete program you need to accomplish this. Load it on to your Arduino and confirm that it works as expected. If you don't see anything, adjust the contrast with the potentiometer.

This program combines all the steps that you learned about earlier. The library is first included at the top of the program. A time variable is initialized to 0, so that it can be incremented once per second during the loop(). A LiquidCrystal object called lcd is created with the proper pins assigned based on the circuit you've already wired up. In the setup, the LCD is configured as having 16 columns and 2 rows, by calling lcd.begin(16,2). Because the first line never changes, it can be written in the setup. This is accomplished with a call to lcd.print(). Note that the cursor position does not need to be set first, because you want the text to be printed to position (0,0), which is already the default starting location. In the loop, the cursor is always set back to position (0,1) so that the number you print every second overwrites the previous number. The display updates once per second with the incremented time value.

Creating Special Characters and Animations

What if you want to display information that cannot be expressed using normal text? Maybe you want to add a Greek letter, a degree sign, or some progress bars. Thankfully, the LiquidCrystal library supports the definition of custom characters that can be written to the display. In the next example, you will use this capability to make an animated progress bar that scrolls across the display. After that, you will take advantage of custom characters to add a degree sign when measuring and displaying temperature.

Creating a custom character is pretty straightforward. If you take a close look at your LCD, you'll see that each character block is actually made up of a 5×8 grid of pixels. (Figure 12-3 shows a magnified view of this.) To create a custom character, you simply have to define the value of each of these pixels and send that information to the display. To try this out, you'll make a series of characters that will fill the second row of the display with an animated progress bar. Because each character space is 5 pixels wide, there will be a total of five custom characters: one with one column filled, one with two columns filled, and so on. To write the code for this, it helps to first visualize exactly what the pixel array will look like. Figure 12-3 shows how each of the five custom characters will look.

At the top of your sketch where you want to use the custom characters, create a byte array with 1s representing pixels that will be turned on, and with 0s representing pixels that will be turned off. The byte array representing the character that fills the first column (or the first 20 percent of the character) looks like this:

byte p20[8] = {
 B10000,
 B10000,
 B10000,
 B10000,
 B10000,
 B10000,
 B10000,
 B10000,
};
Illustration of five custom progress bar characters depicted as filled pixel positions for 20, 40, 60 80 and 100 percentages.

Figure 12-3: Five custom progress bar characters

I chose to call this byte array p20, to represent that it is filling 20 percent of one character block (the p stands for percent). Note how the ones and zeros corresponded to the filled pixel positions from Figure 12-3.

In the setup() function, call the createChar() function to assign your byte array to a custom character ID. Custom character IDs start at 0 and go up to 7, so you can have a total of eight custom characters. To map the 20-percent character byte array to custom character 0, type the following within your setup() function:

lcd.createChar(0, p20);

When you're ready to write a custom character to the display, place the cursor in the right location and use the library's write() function with the ID number:

lcd.write((byte)0);

In the preceding line, (byte) casts, or changes, the 0 to a byte value. This is necessary only when writing character ID 0 directly (without a variable that is defined to 0), to prevent the Arduino compiler from throwing an error caused by the variable type being ambiguous. Try removing (byte) from this command and observe the error that the Arduino IDE displays. You can write other character IDs without it, like this:

lcd.write(1);

Putting this all together, you can add the rest of the characters and put two nested for() loops in your program loop to handle updating the progress bar. The completed code looks like Listing 12-2.

At the beginning of each pass through the loop, the 16-character-long string of spaces is written to the display, clearing the progress bar before it starts again. The outer for() loop iterates through all 16 positions. At each character position, the inner for() loop keeps the cursor there and writes an incrementing progress bar custom character to that location. The byte cast is not required here because the ID 0 is defined by the j variable in the for() loop.

Building a Personal Thermostat

Now, let's make this display a bit more useful. To do so, you add the temperature sensor from Chapter 10, “The I2C Bus,” a fan (using your motor skills from Chapter 4, “Using Transistors and Driving DC Motors”), and the speaker from Chapter 6, “Making Sounds and Music.” The display shows the temperature and the current fan state. When it gets too hot, the speaker makes a noise to alert you, and the fan turns on. When it gets sufficiently cool again, the fan turns off. Using two pushbuttons and the debounce code in Listing 2-5 in Chapter 2, “Digital Inputs, Outputs, and Pulse-Width Modulation,” you add the ability to increment or decrement the desired temperature.

Setting Up the Hardware

The hardware setup for this project is a conglomeration of previous projects. You should treat the fan similarly to the motors you learned about in Chapter 4. The recommended 5V brushless fan for this project will consume more power than your Arduino can provide from an I/O pin, so you'll need to drive it with a transistor. To drive the fan, use an NPN transistor, referencing the schematic that you used in Chapter 4 (Figure 4-1). Similarly to the 5V DC motors that you used in the roving car project from Chapter 4, this fan should be powered off its own 5V supply. Plug a 9V battery into the Arduino's barrel jack and use that as the input voltage to the linear regulator (the VIN pin on the Arduino can be used to connect the battery's 9V supply to the regulator). The regulator will generate a 5V supply to be used by the fan. This will ensure that electrical noise from the fan turning on and off does not impact the performance of the temperature sensor (which is powered using the Arduino's on-board voltage regulator). As you did in Chapter 4, ensure that you equip the regulator with 10μF decoupling capacitors on its input and output. Remember that the regulator's ground must be connected to your Arduino's ground. Figure 12-4 shows a schematic of the components that you'll be adding to the LCD that you've already wired up (it does not show the LCD and potentiometer that you've already wired).

Diagram depicting the complete wiring setup of LCD thermostat additions, with the regulator's ground connected to the Arduino's ground.

Figure 12-4: LCD thermostat additions schematic

Created with EAGLE

Referencing only the schematic drawing, try to add the fan, 5V regulator, drive transistor, protection diode, temperature sensor, speaker, and pushbuttons. Since this fan is a brushless motor, you can omit the small capacitor that you put across the leads of the brushed DC motor in Chapter 4; there are no brushes to make the RF (radio frequency) interference that the capacitor is normally used to reduce. Note how the 5V supply generated by the L7805CV voltage regulator is distinct from the Arduino's 5V supply.

You might need to rearrange your placement of the LCD and trim potentiometer to make room for all your circuitry.

The two buttons have one side connected to power; the other side is connected to ground through 10kΩ pull-down resistors and to the Arduino.

The speaker is connected to an I/O pin through a 220Ω resistor and to ground. The frequency of the sound will be set in the program.

You connect the I2C temperature sensor exactly as you did in Chapter 10. Don't forget the pull-up resistors!

Plug a 9V battery holder into the Arduino's barrel jack. This will enable you to draw 9V power from the Arduino's VIN pin for powering your 5V linear regulator.

When wiring up the linear regulator, recall that the stripe on the decoupling capacitors represents the negative pin of the capacitor, which should be connected to the shared ground. On the diode, the side with the stripe should be connected to the fan's positive wire and the fan's 5V supply; the other side should be connected to the fan's negative wire and the NPN transistor's collector pin.

The diagram in Figure 12-5 shows the complete wiring setup with everything you need to create this project. It's possible to fit this all onto a half-sized breadboard, but as you can see from the wiring diagram, it is quite cramped. You may wish to consider using a full size breadboard for this project.

Illustration of the LCD thermostat system, with a 9V battery plugged into the Arduino for the fan to work properly.

Figure 12-5: LCD thermostat system

Image created with Fritzing

Displaying Data on the LCD

Having some parameters in place beforehand makes writing information to the LCD screen easier. First, use degrees Celsius for the display, and second, assume that you'll always be showing two digits for the temperature. Once the software is running, the LCD display will look something like Figure 12-6.

The Current:and Set:strings are static; they can be written to the screen once at the beginning and left there. Similarly, because the temperatures are assumed to be two digits, you can statically place both °C strings into the correct locations. The current reading will be displayed in position (8,0) and will be updated on every run through the loop(). The desired, or set, temperature will be placed in position (8,1) and updated every time a button is used to adjust its value. The fan indicator in the lower-right corner of the display will be at position (15,1). It should update to reflect the fan's state every time it changes.

Image of an LCD display in which the “Current:”  and “Set:”  strings are static; degree symbol, fan off indicator, and fan on indicator are not part of the LCD character set.

Figure 12-6: LCD display

The degree symbol, fan off indicator, and fan on indicator are not part of the LCD character set. Before using them in your sketch, you need to create them as byte arrays at the beginning of your program. As before, visualize the custom characters as pixel arrays first, as shown in Figure 12-7.

Then, define the custom characters to the specifications using the following snippet:

//Custom degree character
byte degree[8] = {
 B00110,
 B01001,
 B01001,
 B00110,
 B00000,
 B00000,
 B00000,
 B00000,
};

//Custom "fan on" indicator
byte fan_on[8] = {
 B00100,
 B10101,
 B01110,
 B11111,
 B01110,
 B10101,
 B00100,
 B00000,
};

//Custom "fan off" indicator
byte fan_off[8] = {
 B00100,
 B00100,
 B00100,
 B11111,
 B00100,
 B00100,
 B00100,
 B00000,
};

You write the static parts of the display in setup(). Move the cursor to the right locations, and with the LCD library's write() and print() functions, update the screen, as shown in the following snippet:

//Make custom characters
lcd.createChar(0, degree);
lcd.createChar(1, fan_off);
lcd.createChar(2, fan_on);

//Print a static message to the LCD
lcd.setCursor(0,0);
lcd.print("Current:");
lcd.setCursor(10,0);
lcd.write((byte)0);
lcd.setCursor(11,0);
lcd.print("C");
lcd.setCursor(0,1);
lcd.print("Set:");
lcd.setCursor(10,1);
lcd.write((byte)0);
lcd.setCursor(11,1);
lcd.print("C");
lcd.setCursor(15,1);
lcd.write(1);
Illustration of three LCD thermostat custom characters as pixel arrays: (left) Degrees, (middle) Fan On, and (right) Fan Off.

Figure 12-7: LCD thermostat custom characters

You also update the fan indicator and temperature values each time through loop(). You need to move the cursor to the right location each time before you update these characters.

In the event that the I2C temperature sensor returns no data, you can halt the program (with while(1); as you did in Chapter 10). Instead of sending an error message to the serial monitor, use lcd.clear() to empty the LCD screen and return the cursor to the (0,0) position. Then, print an error message: lcd.print("I2C Error");.

Adjusting the Set Point with a Button

In Chapter 2, you used a debounce() function. Here, you modify it slightly to use it with multiple buttons. One button will increase the set point, and the other will decrease it. You need to define variables for holding the previous and current button states:

//Variables for debouncing
boolean lastDownTempButton = LOW;
boolean currentDownTempButton = LOW;
boolean lastUpTempButton = LOW;
boolean currentUpTempButton = LOW;

You can modify the debounce() function to support multiple buttons. To accomplish this, add a second argument that specifies which button you want to debounce:

//A debouncing function that can be used by both buttons
boolean debounce(boolean last, int pin)
{
 boolean current = digitalRead(pin);
 if (last != current)
 {
  delay(5);
  current = digitalRead(pin);
 }
 return current;
}

In loop(), you want to check both buttons using the debounce() function, change the set_temp variable as needed, and update the set value that is displayed on the LCD:

//Debounce both buttons
currentDownTempButton = debounce(lastDownTempButton, DOWN_BUTTON);
currentUpTempButton = debounce(lastUpTempButton, UP_BUTTON);
 
//Turn down the set temp
if (lastDownTempButton == LOW && currentDownTempButton == HIGH)
{
 set_temp--;
}
//Turn up the set temp
else if (lastUpTempButton == LOW && currentUpTempButton == HIGH)
{
 set_temp++;
}
//Print the set temp
lcd.setCursor(8,1);
lcd.print(set_temp);
//Update the button state with the current
lastDownTempButton = currentDownTempButton;
lastUpTempButton = currentUpTempButton;

The preceding code snippet first runs the debounce() function for each button, and then adjusts the set temperature variable if one of the buttons has been pressed. Afterward, the temperature displayed on the LCD is updated, as are the button state variables.

Adding an Audible Warning and a Fan

In this section, you add code to control the fan and the speaker. Although the LCD showing you live information is nice, you'll often find it useful to have an additional form of feedback to tell you when something is happening—for example, having the speaker beep when the fan turns on. In this example, you use tone() paired with delay() and a notone() command. You could instead add a duration argument to tone() to determine the duration of the sound. You want to make sure that the tone plays only one time per alert condition (and does not beep forever when above the set temperature).

Using a state variable, you can detect when the speaker has beeped and thus keep it from beeping again until after the temperature dips below the set temperature and resets the state variable.

When the fan turns on, an indicator changes on the LCD (represented by the custom character you defined at the top of the program). The following code snippet checks the temperature and controls the speaker, the fan indicator on the LCD, and the fan:

//If it's too hot!
if (c >= set_temp)
{
 //Check if the speaker has already beeped
 if (!one_time)
 { 
  tone(SPEAKER, 400);
  delay(500);
  one_time = true;
 }
 //Turn off the speaker when it's done
 else
 {
  noTone(SPEAKER);
 }
 //Turn the Fan on and update display
 digitalWrite(FAN, HIGH);
 lcd.setCursor(15,1);
 lcd.write(2);
}
//If it's not too hot!
else
{
 //Make sure the speaker is off
 //reset the "one beep" variable
 //update the fan state and LCD display
 noTone(SPEAKER);
 one_time = false;
 digitalWrite(FAN, LOW);
 lcd.setCursor(15,1);
 lcd.write(1);
}

The one_time variable is used to make sure that the beep plays only one time instead of continuously. Once the speaker has beeped for 500 ms at 400 Hz, the variable is set to true and is reset to false only when the temperature drops back below the desired temperature.

Bringing It All Together: The Complete Program

It's time to bring all the parts together into a cohesive whole. You need to make sure that you include the appropriate libraries, define the pins, and initialize the state variables at the top of the sketch. Listing 12-3 shows the complete program. Load it on to your Arduino and compare your results to the demo video showing the system in action.

You no longer need to have the Arduino and components tethered to the computer to see what the temperature is. Since this project has already been designed to be powered with a battery, simply unplug the USB cable, and your thermostat will continue to run using the connected battery. If you like, you can plug in a battery or wall power supply and place it anywhere in your room.

Taking This Project to the Next Level

You could expand the functionality of this program in all kinds of ways. Here are a few suggestions for further improvements you can make:

  • Use pulse-width modulation (PWM) to control fan speed so that it changes according to how far over the set temperature you are.
  • Add LED indicators that display visual alerts.
  • Make the speaker alert into a melody instead of a tone.
  • Add a light sensor and automatically adjust the backlight brightness of the display by putting an NPN transistor on the backlight cathode pin and using PWM to drive it based on the ambient brightness.

Summary

In this chapter, you learned the following:

  • Parallel LCDs can be interfaced with the Arduino through a standard wiring scheme.
  • You can create custom characters for your LCD by generating arbitrary bitmaps.
  • You can modify your debounce function from Chapter 2 to debounce multiple buttons.
  • You can combine multiple sensors, motors, buttons, and displays into one coherent project.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset