by Floris Wouterlood – July 7, 2018
Summary
A lcd display is an effective and economic means to visually present data collected by an Arduino. On the market is a great variety of lcd displays. Most common are monochrome displays capable of presenting two lines with each 16 characters (16×2) and displays that show four lines with each 20 characters (20×4). Here we will discuss the basics of connecting a 16×2 lcd display to an Arduino. After that we will connect a Dallas DS18B20 temperature sensor to the Arduino with the purpose to display sensor data. Simple sketches are provided at successive stages while we will walk through each sketch.
Introduction
Data collected with a sensor connected to an Arduino can be presented in many ways. They can be displayed on your computer via Serial Monitor, transmitted to another Arduino or even to a server on the internet to have them available for final display on your smartphone, tablet or any other suitable platform. An elegant, maybe the ‘historical’ way that is very useful in standalone situations, is to use a display of some sorts connected directly to the Arduino board. Most common today are displays based on lcd, OLED or TFT technology. I assume that lcd displays were the first made available to the Arduino community. Most are compatible with the de facto Hitachi HD44780 standard. Many manufacturers, mostly Chinese, are flushing the market with affordable lcd displays ready to be connected with an Arduino.
Figure 1: Front view of a classic, China made 16×2 lcd display ready to connect with an Arduino. The pin interface has 16 pins numbered 1 to 16. On some displays (like this one) connectivity code is printed onto the printed circuit board. Note that the 220 Ω resistor between 5V and pin 15 is necessary to reduce the voltage to the backlight led to 3.3V. The 10kΩ pot meter acts as a variable resistor betwee pin 3 and GND to fine tune contrast. It functions well without being connected to 5V.
Such displays are available in various tastes, colors and numbers of characters that can be displayed. As this is a ‘basic’ paper, I will discuss here the 16×2 monochrome display (two lines of 16 characters each) because I regard this device as the ‘mother of all lcd displays’. They are perfect for the beginner and they may as well perfectly serve needs of more advanced Arduino hobbyists. Many permanent projects can be equipped with a lcd display to provide visual information, for instance digital clocks and timers, water temperature control devices, moisture sensor devices, tachometers, barometers, complete weather stations, etcetera.
Figure 2: Back view of the lcd display of figure 1. The type of display (1602 – 16 characters on two lines) is indicated on the back. Drops of black resin cover the Hitachi driver chips.
Potentiometer on pin 3; backlight
Pin 3 of the display must be connected with the middle contact, named in jargon the ‘’wiper’, of a 10 kΩ potentiometer. Only one of the other two pins of the potentiometer must be connected to GND. You can ignore the other pin. This pot meter supplies a voltage to the display that adjusts the contrast of the characters against the (fixed illumination) background. Power for the background illumination is supplied via pins 15 and 16 of the display. Background illumination is achieved with a white led and a diffuser. The diffuser sticks out on the side of the display (figure 1). As leds are typically 3.3V devices, a 220 Ω resistor is needed in series at pin 15 to protect the backlight led from receiving too much power.
As the potentiometer is only needed to fine-tune the contrast one can experiment to connect pin 3 via a resistor with a fixed value with GND of the Arduino. Begin the experiments with a 10 kΩ resistor and replace it stepwise with one with a lower value until a satisfactory contrast is achieved. With the display shown in figures 1 and 2 a 470 Ω resistor sufficed.
Pin connectivity with an Arduino
Pins 1 and 2 of the lcd display are reserved for power: GND to pin 1 and 5V to pin 2.
Pin 3 receives power from the potentiometer (see previous section).
Pin 4 of the display (RS pin) is connected to pin 12 of the Arduino, while pin 6 of the display (RS enable) is connected to pin 11 of the Arduino.
Data from the Arduino to be displayed on the lcd display run via pins 11,12,13 and 14 of the display. Display pin 11 is connected with pin 5 of the Arduino, display pin 12 with pin 4 of the Arduino, display pin 13 with pin 3 of the Arduino, and display pin 14 with pin 2 of the Arduino.
Pins can be connected with Dupont wires or they can be connected by soldering wires. In the prototype phase it is handy to use a breadboard.
Basic sketch for testing the display
A basic sketch that brings the lcd display to life (and no more than that) is discussed in this section: basic_15x2_lcd_display.ino..
Once the proper pins of the lcd display are connected with the proper pins of the Arduino and connectivity has been double checked, the Arduino is connected to a computer and the following sketch can be compiled and uploaded.
// basic_16x2_lcd_display
// sends two lines of characters to the lcd display
// by Floris Wouterlood
// open source – use as you wish
// July 7, 2018
The sketch starts with reference to a library. This library is supplied with the Arduino IDE, so don’t worry about starting a search on the internet to download it.
// libraries
#include <LiquidCrystal.h>
Now we have to instruct the Arduino which of its interface are connected with the lcd display:
LiquidCrystal lcd (12, 11, 5, 4, 3, 2);
Pins ’11’ and 12′ refer here to the RS and RS-enable functions while 5,4,3 and 2 refer to the pins used to transfer data from the Arduino to the display. Note that you can setup data connectivity of your LCD from other pins on the Arduino, e.g. pins 8, 7, 6 and 5, but in all cases you have to declare these pins explicitly in the ‘LiquidCrystal lcd (…) statement.
Next comes the setup section of the sketch:
void setup () {
lcd.begin (16, 2);
This instruction tells the software that the connected lcd display is a 16×2 type. If your lcd is a 20×4 type, then the proper instruction is ‘lcd.begin (20, 4);’
The position where character display starts is instructed with the ‘lcd.setCursor’ statement:
lcd.setCursor (0, 0);
This instruction forces the display to start displaying on first line, first position.
Then the text to be displayed follows:
lcd.print (“Hello World”);
And the instructions are repeated to display text on the second line:.
lcd.set Cursor (0, 1);
lcd.print (“second line”);
The setup section is closed with an accolade:
}
And then the loop is announced. However, since in this basic sketch only one text is displayed once and forever in the ‘setup’ section there is no need to add instructions to the ‘loop’ section:
void loop() {
// empty! – nothing to do here !
}
Figure 3: Wiring of the 16×2 lcd display with an Arduino; display of the text as programmed in the sketch basic_2x16_lcd_dsplay.ino.
Character set
When the contrast potentiometer is turned to maximum contrast it can easily be seen that the characters are embedded in rectangle like structures. In a 16×2 lcd display there are two lines. Each consists of 16 of these rectangles. Each rectangle is formed by a matrix of 8 pixels high and 5 pixels wide. Each of the 40 individual pixels in a rectangle can be set ‘ON’ or ‘OFF’. Usually they are in ‘OFF ‘ position, that is: they have he same intensity as the background. Any letter, number or special character consists of a special configuration of ‘ON’ and ‘OFF’ pixels in their 8×5 matrix. In the HDM44780 chip 255 pre-programmed characters are available by default. This set of characters is called the lcd’s ‘ASCII character set’.
The following sketch, ‘ascii_lcd_character_set.ino’ displays on the lcd display in a loop the following data. On the first line comes the decimal (ascii) value of the character while on the second line of the display the character itself appears.
// ascii_lcd_character_set
// by Floris Wouterlood
// open source – use as you wish
// July 7, 2018
// libraries
#include <LiquidCrystal.h>
LiquidCrystal lcd (12, 11, 5, 4, 3, 2);
void setup() {
lcd.begin (16, 2);
lcd.clear ();
lcd.setCursor (0,0);
lcd.print (“DECIMAL = “);
lcd.setCursor (0,1);
lcd.print (“ASCII = “);
}
void loop() {
char ascii=0x00+33; // starting from 34th character
for (int count=33; count<256; count++){
lcd.setCursor (10,0);
lcd.print (” “); // clear previous count
lcd.setCursor (10,0); //cursor back to pos 10
lcd.print (count);
lcd.setCursor (11,1);
lcd.print (ascii);
ascii++;
delay (1000);
}
}
Program your own custom characters
One of the funny things with the character set is that there is room for creating your own favorite character, icon or emoji. The Arduino has memory space to hold eight custom characters. Creating your own character works as follows.
Figure 4: Creating your own custom character in the 5×8 lcd pixel matrix of a lcd display ‘character’ rectangle. Here we create a ‘smiley’ and a ‘weepy’. In the byte matrix a ‘1’ means ‘pixel ON’ while a ‘0’ means ‘pixel OFF’.
Each character is built up of five bytes. Each bit corresponds with one pixel of the character, and as a bit can be ‘0’ or ‘1’, whether or not a pixel is displayed, is defined by how the byte has been set.
An example is shown in Figure 4. Please note that every byte is responsible for the eight vertical pixels that belong to the byte’s column. This is obviously done to support efficient left-right scrolling of text.
The sketch ‘glyph.ino’ is an example of programming and displaying two specific characters, one named ‘smile’ and the other named ‘weep’.
Sketch ‘glyph.ino’
// glyph
/// two custom characters – smile and weep
// by Floris Wouterlood
// open source – use as you wish
// July 7, 2018
We have to insert the reference to the library and we have to define the interface pins (see above) :
// libraries
#include <LiquidCrystal.h>
LiquidCrystal lcd (12, 11, 5, 4, 3, 2);
Here follow the definitions of the ‘smile’ and the ‘weep’ characters:
byte smile[8] = {
B00000,
B00000,
B10001,
B00100,
B10001,
B01110,
B00000,
B00000
};
byte weep[8] = {
B00000,
B00000,
B10001,
B00100,
B00000,
B01110,
B10001,
B00000
};
And we proceed with the setup section:
void setup() {
While we have defined the 8×5 pixel matrix of each custom character an additional instruction named ‘lcd.createChar’, is required before we can print the custom character to the lcd. Up to 8 custom characters are allowed:
lcd.createChar (0, smile);
lcd.createChar (1, weep);
lcd.begin (16, 2);
lcd.clear ();
lcd.setCursor (0,0);
lcd.write ((uint8_t)0);
}
Note on the uint8_t: In the official reference section on the Arduino forum (https://www.arduino.cc/en/Reference/LiquidCrystalCreateChar) the instruction says ‘lcd.write(byte(0)’. The Arduino IDE compiler needs to know however that this byte is of the unsigned 8-bit type: an uint8_t.
The loop of the sketch is quite straightforward: we print the ‘smile’ character to the first position of the lcd display, leave it in place for one second and then replace it with the ‘weep’ character. As these instructions are in the loop section the program will run forever.
void loop() {
lcd.setCursor (0,0);
lcd.write ((uint8_t)0);
delay (1000);
lcd.set Cursor (0,0);
lcd.write ((uint8_t)1);
delay (1000);
}
It is interesting to create a custom character for the superscript ‘degree’ character when we are going to display temperatures in the next section of this paper.
Below is the pixel array that creates a superscript-‘o’:
byte degr[8] = {
B00110,
B01001,
B01001,
B00110,
B00000,
B00000,
B00000,
B00000
};
The alternative is the instruction ‘lcd.print((char)223);’
Displaying temperature
Temperature can be measured with sensors developed by different manufacturers.
One of the most popular in the Arduino community is the Dallas DS18B20 (figure 5).
This is a small, cheap and accurate sensor that records temperatures between -55 and 125 oC (-67°F to +257°F). Such a range is perfect for most applications. The accuracy in the working range (-10 to +85 oC) is 0.5 oC. One big advantage is that the DS18B20 is a one-wire device: for communication with an Arduino only one pin on the microcontroller board is necessary. Multiple DS18B20s can be connected with the Arduino via the same communication wire because each DS18B20 has a unique 8-bit identification tag. Calling this tag produces the response only of the sensor with that tag while other, identical sensors wired through the same line do not respond. A so-called pull-up resistor with a value of 4.7 kΩ between the data wire and 5V is necessary to maintain a stable signal (see wiring diagram in figure 6). If this resistor is absent, the sensor may not be recognized by the Arduino or readings may be unreliable.
Figure 6: Arduino, lcd display and DS18B20 sensor combined.
Sketch
The sketch, named ‘single_DS18B20_lcd_display.ino’ uses three libraries: the built-in LiquidCrystal.h and the external OneWire.h and DallasTemperature.h. The external libraries are available in the public domain (https://www.arduinolibraries.info/libraries/one-wire).
Let’s walk through the sketch. We will deal with temperatures in centigrade only, but with the insertion of a small formula temperatures can be expressed in degree Fahrenheit.
// single_DS18B20_lcd_display
// by Floris Wouterlood
// Dallas DS18B20 temperature sensor on pin 9
// open source – use as you wish
// July 7, 2018
//
// credits to – among others
// Jim Studt
// Paul Stoffregen
// David A. Mellis
// Limor Fried (http://www.ladyada.net)
// Tom Igoe
The libraries are listed here, and for LiquidCrystal the parameters are provided:
// libraries
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd (12, 11, 5, 4, 3, 2); // include the library code
As we want to display °C we have to define superscript-‘o’ as a custom character degree sign:
byte degr[8] = { // create character degree centigrade
B00110,
B01001,
B01001,
B00110,
B00000,
B00000,
B00000,
B00000
};
The OneWire library needs to know with which Arduino pin the sensor is expected to communicate. In our example the sensor’s data pin is connected to Arduino pin 9. In the example we soft define this pin (as a variable). In case we might need another pin we only have to change here the pin number and not worry whether the pin number must be changed elsewhere in the sketch.
#define ONE_WIRE_BUS_PIN 9 // DS18B20 data pin wired to pin 9 of Arduino
OneWire oneWire (ONE_WIRE_BUS_PIN); // pin on arduino that gets data from the sensor
DallasTemperature sensors(&oneWire); // pass oneWire reference to Dallas Temperature
Next we need to supply the unique device address. Each DS18B20 has its own 8-byte long address which can be detected with a sketch, ‘DS18B20_address_finder.ino’.
The device address of the sensor used in this sketch is 0x28, 0xFF, 0x7B, 0x74, 0xA3, 0x15, 0x04, 0x93.
DeviceAddress Probe = {0x28, 0xFF, 0x7B, 0x74, 0xA3, 0x15, 0x04, 0x93};
Once the variables have been declared the sketch proceeds with the setup section:
void setup()
{
Serial.begin (9600); // handy to have Serial Monitor on line
Serial.print (“Initializing Temperature Control Library Version “);
Serial.println (DALLASTEMPLIBVERSION);
sensors.begin (); // Initialize the temperature measurement library
sensors.setResolution (Probe, 10); // set resolution of the DS18B20 to 10 bit
lcd.createChar(0, degr); // special character ‘superscript-o’
lcd.begin (16, 2); // set up the LCD’s number of columns and rows
lcd.clear ();
lcd.setCursor (0, 0); // print the permanent stuff to the LCD display
lcd.print (“DS18B20 sensor”);
lcd.setCursor (13,1);
lcd.write ((uint8_t)0);
lcd.setCursor (0, 1);
lcd.print (“temp:”);
lcd.setCursor (14,1);
lcd.print (“C”);
delay (1000);
}
The setup part takes care of identifying the sensor to the Arduino, it creates the special character ‘superscipt-degree’, sets up the lcd and instructs the lcd display to display (print) the so-called ‘permanent’ characters that is the characters that will be seen on the display al the time. In the ‘loop’ section only the variable data (temperature readouts) need to be sent to the lcd display. This is efficient, improves the speed and avoids flickering of the display. We also have Serial Monitor at hand in case trouble shooting is necessary.
void loop()
{
Serial.println ();
Serial.print (“Number of Devices found on bus = “);
Serial.println (sensors.getDeviceCount());
Serial.print (“Getting temperatures… “);
Serial.println ();
sensors.requestTemperatures (); // request sensor to report temperature
Serial.print (“Sensor temperature is: “);
printTemperature (Probe); // call the subroutine and display
Serial.println ();
delay (1000);
}
Subroutines: These are calculations or procedures that are repeatedly necessary in the sketch. These tasks can be placed outside the loop and called from within the loop. Often, subroutines deal with a specific task, here control of the dynamic part of getting and displaying sensor data on the lcd display Apart from preventing chaos and therefore supporting higher efficiency the strategy of using subroutines is particularly helpful for debugging. Once a subroutine works the programmer can focus on the main job of the sketch or on other subroutines.
// subroutine deviceAddress and print data to serial monitor and LCD
void printTemperature(DeviceAddress deviceAddress){
float tempC = sensors.getTempC(deviceAddress);
if (tempC == -127.00)
{
Serial.print (“Error getting temperature”);
}
else
{
Serial.print (tempC,1); // Serial Monitor part
Serial.println (” *C”);
Serial.println ();
Serial.println (“============”);
lcd.setCursor (8,1); // LCD part
lcd.print (” “);
lcd.setCursor (8,1); // LCD part
lcd.print (tempC,1);
}
}
Discussion
In this paper we have connected a 16×2 lcd ‘classic’ display to an Arduino, discussed the sketch necessary to bring the display to life, attached a DS18B20 temperature sensor to the Arduino and, finally, display sensor data using a custom character.
This classic way of connecting a lcd display to an Arduino uses 6 pins on the Arduino. With a simple application such as a one-wire temperature sensor this is not a major problem. However when an application uses more pins, or when multiple sensors must be connected with the Arduino a ‘shortage’ of pins may threaten the project. In that case an I2C lcd-display might help because the I2C protocol needs only the analog pins A4 and A5 of the Arduino. 16×2 and 20×4 lcd displays with backpack I2C extenders working with I2C are currently available while these extenders can be bought also separately. However, there is a price, that is extra use of memory. The sketch ‘single_DS18B20_lcd_display’ uses 9,106 bytes of program memory and 487 bytes of dynamic memory to run with a 16×2 lcd display. The same sketch compiled for the same but now I2C expander-supported 16×2 lcd display, gobbles up 10,928 bytes of program memory and 720 bytes of dynamic memory. The advantage of I2C is less wires and less required pins at the expense of a higher memory load.
Sketches
The four sketches discussed above can be downloaded plus the sketch ‘DS18B20_address_finder.ino’
The skeches are packed into a ZIP file named Arduino_classic_lcd_display.zip.
Please download, unZIP and open with the Arduino IDE