Print-derived Serial LCD library for Arduino






4.67/5 (8 votes)
A new serial LCD library for Arduino with the power of 'Print' class
Introduction
LCD displays are mostly used in Arduino-based systems to show output of your application. They are cheap, widely-available, easy-to-use. Therefore, you can find many kinds of LCD display on the market. One drawback is that it occupies to many pins on your Arduino board. As a solution, serial LCD displays has emerged, thanks to kindhearted manufacturers :)
Background
Arduino IDE comes with a LiquidCrystal
library ([3]) for Hitachi-HD44780-based LCDs ([4]). You command the controller by selecting & writing registers according to your needs. In 4-bit usage, you need 6 digital pin of your Arduino out of 14. What a waste.
Figure 1: A 2x16 LCD
Some functionality provided by Hitachi-based controller:
- Positioning cursor
- Setting cursor type (hide, underline, blink)
- Scrolling display (to-left, to-right)
- Display on/off
- Clear screen
LiquidCrystal
library exposes those functions by commanding Hitachi controller. Dedicated pins are specified in the constructor.
Let's see an example:
#include <Arduino.h> #include <LiquidCrystal.h> /* Pin configuration: LCD pin <=> Arduino Digital ----------------------------- register select <=> 12 enable <=> 11 data4 <=> 2 data5 <=> 3 data6 <=> 4 data7 <=> 5 */ LiquidCrystal lcd(12,11,2,3,4,5); void setup() { lcd.begin(16,2); // specify columns & rows count lcd.print("that is a test"); delay(5000); lcd.clear(); // clear screen lcd.setCursor(1,1); // second column of second row } void loop() { while(1); }
A LiquidCrystal
instance (lcd
) is created with a given pin configuration at the beginning of the code. The library uses this pin configuration to send commands and data to LCD.
LiquidCrystal lcd(12,11,2,3,4,5);
In setup
function, LCD is initialized with its columns & rows count. The library uses this information when it sets cursor position.
lcd.begin(16,2); // specify columns & rows count
For example, if you want to move cursor to second column of second row, target cursor position is decided by using values provided in begin
function. The next print
will start from that position.
lcd.setCursor(1,1); // second column of second row
I hope this sample is enough to see how to use an LCD with LiquidCrystal
library.
Serial LCD
Now, it is time to talk about serial LCD. When it comes to serial LCD, those low-level driving issues are diverted to another microcontroller. You command this microcontroller, and it commands LCD (Hitachi controller). To do this, it provides you a serial interface with an only one line. Information flow is one-way, from Arduino to serial LCD, therefore it needs only one digital pin of your Arduino. You use only the transmit line of HardwareSerial
([7]) or SoftwareSerial
([8]) to communicate with your serial LCD.
Figure 2: A serial LCD (connected with SoftwareSerial
)
What I have done in this library is to implement communication protocol ([2]) with my specific serial LCD ([1]). My implementation has similar functionality with LiquidCrystal
as you may expect since both are based on Hitachi controller.
Commands to serial LCD are composed of 1-byte directives with parameters. To drive serial LCD, you send those commands over serial transmit.
Command: Set backlight
Command byte: 0x80
Parameter: 0x00-0xFF (0-255). corresponds to brightness between 0%-100%
Command: Set baudrate (after reset), default 9600
Command byte: 0x81
Parameters: 0x00-0x0A (0-10), corresponding values 300, 1200, 2400, 2400, 9600, 14400, 19200, 28800, 38400, 57600, 115200
Following commands control LCD's behavior and all are preceded by 0xFE command byte.
Command: Clear screen, set cursor position to 0
Command byte: 0x01 (no parameter)
Command: Move cursor right
Command byte: 0x14 (no parameter)
Command: Move cursor left
Command byte: 0x10 (no parameter)
Command: Scroll display right
Command byte: 0x1C (no parameter)
Command: Display off (backlight doesn't change)
Command byte: 0x08 (no parameter)
Command: Display on
Command byte: 0x0C (no parameter)
Command: Underline cursor
Command byte: 0x0E (no parameter)
Command: Blink cursor
Command byte: 0x0D (no parameter)
Command: Cursor off (if display is ON)
Command byte: 0x0C (no parameter)
Command: Set cursor position
Command byte: 0x80
Parameter: 1-byte position index, starting from 0 (top-left)
Command: Toggle splash screen
Command byte: 0x1E (no parameter)
(I had developed another library ([9]) for serial LCD, but it is not good enough in terms of design & functionality. I prefer to count it as an experimental study.)
Using the code
Here is the class declaration of SerialLcd2
:
class SerialLcd2: public Print { protected: // underlying stream connected to serial LCD. // can be HardwareSerial or SoftwareSerial Stream* _Stream; public: // constructor SerialLcd2(Stream* stream): _Stream(stream) { }; // override for Print::write(uint8_t) size_t write(uint8_t b) { return this->_Stream->write(b); }; // line feed, same as print('\n'); void printLine() { this->write(SL_CHR_LF); }; // back space void del() { this->write(SL_CHR_BCK); }; // direction: SL_CMD_CURSOR_MV_R|SL_CMD_CURSOR_MV_L void moveCursor(uint8_t cmd); // cmd = SL_CMD_CURSOR_OFF|SL_CMD_CURSOR_UNDERLINE|SL_CMD_CURSOR_BLINK void showCursor(uint8_t cmd); // pos: 0..(max chars-1) void setCursorPos(uint8_t pos); // clear screen void clr(); // backlight: 0-255 void setBacklight(uint8_t b); // direction: SL_CMD_SCROLL_R|SL_CMD_SCROLL_L void scrollScreen(uint8_t cmd); // on if true void displayOn(bool); // toggle splash screen display. void toggleSplash(); // set baud rate void setBaudRate(uint8_t); };
What I did here is to implement serial communication protocol and some other handy functions.
Let's see an example.
SerialLcd2* lcd; // pointer for SerialLcd2 Stream* s; // stream to be connected
To connect via hardware serial (Serial), we first need to initialize serial port of Arduino. I haven't changed my LCD's baud rate, therefore baud rate of the connection will be 9600 bps.
Serial.begin(9600); s = &Serial; // connect to tx(pin 1)
If we want to connect with SoftwareSerial
, similar initialization is needed except we have to reserve pins for SoftwareSerial
.
SoftwareSerial ss(2,3); // rx=2, tx=3(connection) ss.begin(9600); s = &ss;
Then create a SerialLcd2
instance with its serial Stream
([6]).
lcd = new SerialLcd2(s);
Caution! We should connect Stream
's transmit line to the LCD's receive line, which is its only communication line.
Now, we are ready to use it.
First of all, we can print anything as Print
class ([5]) does without any further processing. This is accomplished by overriding virtual write
function of Print
class since Print
class uses it in all print versions.
lcd->print(1); // integer lcd->print('A'); // char lcd->print(0.1); // float lcd->print("string"); // const char* lcd->print(F("flash string")); // flashmem string
Don't use println
, because it doesn't move the cursor to the start of the next line as you may expect. Instead send '\n' directly or use printLine()
function. Then, the cursor goes to the next line, or to the first if it is on the last line of LCD.
lcd->print('\n'); // or lcd->printLine();
One good thing, you can delete last printed character as if you press backspace.
lcd->del();
The rest is about controlling LCD as in LiquidCrystal
library of Arduino.
/* screen control */ lcd->clr(); // clear screen lcd->setBacklight(0); // backlight off lcd->setBacklight(0xFF); // backlight max lcd->scrollScreen(SL_CMD_SCROLL_R); // scroll right lcd->scrollScreen(SL_CMD_SCROLL_L); // scroll left lcd->displayOn(false); // hides printed text and makes backlight off lcd->displayOn(true); // shows printed text and makes backlight max /* cursor control */ lcd->showCursor(SL_CMD_CURSOR_OFF); // cursor off lcd->showCursor(SL_CMD_CURSOR_UNDERLINE); // underline cursor lcd->showCursor(SL_CMD_CURSOR_BLINK); // blink cursor // moves cursor to the first column of the first row lcd->setCursorPos(0); // moves cursor to the first column of the second row // if the total number of columns is 16 lcd->setCursorPos(16); lcd->moveCursor(SL_CMD_CURSOR_MV_R); // increments cursor position lcd->moveCursor(SL_CMD_CURSOR_MV_L); // decrements cursor position /* misc */ lcd->setBaudRate(4); // sets to 9600bps after reset lcd->toggleSplash(); // toggles splash screen for my specific serial LCD
Some points to mention:
- In the serial command of LCD, setting display on/off doesn't have any effect on backlight. I have changed this behavior in my implementation. If you call
displayOn(false)
, backlight is also turned off, and vice versa. - Setting cursor position is different from the
LiquidCrystal
library. You pass [column,row] values toLiquidCrystal::setCursor
function, and pass [offset-from-topleft] toSerialLcd2::setCursorPos
. - Baud rate and splash screen toggling don't exist in
LiquidCrystal
You can find an example application for the usage of the library in the download section.
Points of Interest
The good thing with this library is that it derives from Print
class. Therefore it has the power of Print
class. You can print anything as you do with Print, such as formatted numbers or strings from flash memory of Arduino.
Another thing is that it uses HardwareSerial
or SoftwareSerial
without any difference since it sees both as Stream
. Therefore, you can connect receive line of serial LCD to any pin which is configured as serial transmit.
Behind the scenes, serial LCD kit makes use of LiquidCrystal library in its firmware ([10]). This is achieved by the same techniques used in an Arduino board. Serial kit has a ATmega328 MCU and Arduino bootloader is uploaded on it. Therefore, it is possible to upload any code with its serial programming interface. The firmare is just another Arduino code which can be thought as proxy between your Arduino and LCD. You can even change the communication protocol of serial LCD and create your very own. Enjoy!
If you have any idea/suggestion regarding to this implementation, I will happy to hear them.
Reference
[1] Serial LCD kit from Cooking-Hacks.com
[2] Serial communication with serial LCD
[3] Arduino LiquidCrystal library
[4] Hitachi LCD controller
[5] Arduino Print class
[6] Arduino Stream class
[7] Arduino Serial
[8] Arduino SoftwareSerial
[9] Another implementation of mine for SerialLcd
[10] Firmware of serial LCD kit