RkBlog

Hardware, programming and astronomy tutorials and reviews.

Handling LCD displays via USB UART from your computer

How to control displays from computers via serial UART communications in Python

Communication between two UART (Universal Asynchronous Receiver and Transmitter) devices is quite simple. One device sends data to receiver of the second devices. That device may respond by sending a response data to first device receiver. UART devices are very common. Arduino made many USB-UART adapters easily available so now you can easily plug such adapter to your computer a communicate with other UART devices like LCDs without microcontroller board in between (like Arduino).

When connected to a PC such LCD can be used to display computer temperature, fan speed, CPU, RAM or GPU utilization and so on. With buttons it can create menu or more complex interfaces that would configure or display date from more complex applications. PC modders may notice that there are preconfigured PC panels with LCDs displaying such data.

In this article I'll present a hobbytronics LCD serial adapter and some Python code for scripting it when connected to a computer.

Introduction to UART displays

In some shops you may find adapters for plain LCD displays that offer serial connection. One is offered by hobbytronics and second by sparkfun. There are pre-made displays with such adapters or adapters only that should be soldered to an LCD. Adapters have a pre-programmed microcontroller that offers us a simple UART API, while hiding more complex LCD communication API. Also serial communication is quite cross-platform. Pyserial library used for serial communication in Python works on Linux, Windows and other Unix-alike operating systems.

You may find more displays with serial communications, like color TFT display or things like VGA adapters which can draw text and basic UI on a display with D-SUB connection. More advanced displays will usually have more complex communication - be sure to check if the manufacturer provides good description or even examples and libraries for it.

Handling 2x16 LCD via UART

The hobbytronics adapter I used supports 16-pin displays like the classical 2x16 or even 4x16 listed on their website. The adapter can be soldered to the LCD or you can solder goldpins to connect it via breadboard to various displays (one at a time). We will also need USB-UART module running at 5V (some are 3.3V, like those for Raspberry Pi) and some connection wires.

For MS Windows you will need drivers for the chipset used in the adapter. Check if there are available for your Windows version.

USB-UART adapter

USB UART adapter

hobbytronics adapter soldered to a display

hobbytronics adapter soldered to a display

Adapter connected via breadboard

Adapter connected via breadboard

To connect two UART devices we connect transmitter (TX or RXD) with receiver (RX or TXD) of the second device and vice versa. If the second device doesn't send responses then you can skip the second connection. So for connecting USB-UART to LCD display we have to connect:

In case of 4D Systems USB UART adapter there are TX and RX labels, while on Baite adapter I have RXD and TXD. If you connect wrong pins then you won't see anything on the display.

RX and TX labels on USB UART adapter

RX and TX labels on USB UART adapter

As for the adapter there is PDF with the documentation and some examples. In short sending specific letters triggers specific actions.

PySerial and LCD display

On page 5 of the documentation you can find a table with all special numbers that trigger different action on the display. On page 14 you will find an example using pyserial to display some text on the display (via Raspberry Pi in this case):

import serial
import time
serialport = serial.Serial("/dev/ttyAMA0", 9600, timeout=5)
time.sleep(1)
serialport.write(chr(5)+chr(2)+chr(16)+chr(0xFF))
serialport.write(chr(4)+chr(0xFF))
serialport.write(chr(7)+chr(250)+chr(0xFF))
serialport.write(chr(2)+chr(1)+chr(1)+chr(0xFF))
serialport.write(chr(1)+"Welcome to"+chr(0xFF))
serialport.write(chr(2)+chr(2)+chr(1)+chr(0xFF))
serialport.write(chr(1)+"Hobbytronics"+chr(0xFF))
count = 0
while 1 :
    serialport.write(chr(2)+chr(1)+chr(13)+chr(0xFF))
    serialport.write(chr(1)+str(count)+chr(0xFF))
    time.sleep(1)
    count = count + 1
serialport.close()

There is a lot of magic numbers and it's not very clear what's going on. chr function changes given number to corresponding it letter from the ASCII characters set. The table in the documentation lists those number and their meaning (although the display receives characters). The 0xFF (255 written in hex) is used to terminate every command.

Everything starts with a serial connection that looks like so:

serial.Serial("/dev/ttyAMA0", 9600, timeout=5)

First argument is the name of the device. /dev/ttyAMA0 is the UART on Raspberry Pi. Under Linux when you connect USB UART you will get /dev/ttyUSB0 and alike (you can check that in dmesg). Under Windows you will get some virtual COM port. The second argument is baud rate - connection speed. Documentation should indicate which baud rates are supported. The 9600 baud rate is quite common and usually used. Under Linux if you don't add permission in udev config you will have to execute scripts as superuser (sudo or root).

Handling Hobbytronics UART LCD adapter in Python

The example in the documentation isn't a nice and reusable library, so I wrote this simple class that implements the core functionality of the adapter:

import time

import serial


class LCD(object):
    END = chr(0xFF)

    def __init__(self, device, rows, columns):
        self.commands = {
            'display_string': chr(1),
            'set_cursor_position': chr(2),
            'clear_line': chr(3),
            'clear': chr(4),
            'set_lcd_type': chr(5),
            'backlight': chr(7)
        }
        self.device = device
        self.rows = rows
        self.columns = columns
        self.connection = None

    def configure(self):
        self.connection = serial.Serial(self.device, 9600, timeout=5)
        time.sleep(1)
        self.execute_command('set_lcd_type', chr(self.rows) + chr(self.columns))
        self.clear()

    def clear(self):
        self.execute_command('clear', '')

    def clear_line(self, line):
        self.execute_command('clear_line', chr(line))

    def set_backlight(self, brightness):
        self.execute_command('backlight', chr(brightness))

    def set_cursor_position(self, row, column):
        self.execute_command('set_cursor_position', chr(row) + chr(column))

    def display_string(self, string):
        self.execute_command('display_string', string)

    def close(self):
        self.connection.close()

    def execute_command(self, command, payload):
        self.connection.write(self.commands[command] + payload + self.END)
And now you can use the adapter with much more readable and shorter code:
from datetime import datetime

lcd = LCD("/dev/ttyUSB0", rows=2, columns=16)
lcd.configure()
lcd.set_backlight(25)

while True:
    day = datetime.now().strftime('%d, %b %Y')
    current_time = datetime.now().strftime('%H:%M:%S')

    lcd.set_cursor_position(1, 1)
    lcd.display_string(day)
    lcd.set_cursor_position(2, 1)
    lcd.display_string(current_time)
    time.sleep(1)
PcDuino displaying date and time

PcDuino displaying date and time

This code will run on any computer that runs a OS that supports pyserial and USB UART adapter used. That covers PCs, laptops and mini computer/single board computers like Raspberry Pi or PcDuino. In case of Raspberry you have to use a powered USB HUB for it to work, as the USB ports on the Pi have limited power - only 0,1A instead of the standard 0,5A.

USB UART running on Raspberry via powered USB HUB

USB UART running on Raspberry via powered USB HUB

Raspberry Pi as well as some other mini computers or more industry grade motherboards may have UART built in. Then you can skip the USB adapter and use the UART pins. In case of Raspberry Pi the TXD pin in on fourth edge pin, just next to GND and 5V needed to power the display. In this setup the device is not /dev/ttyUSB0 but /dev/ttyAMA0.

Using Raspberry Pi UART to control the LCD

Using Raspberry Pi UART to control the LCD

Other displays

If you go through few shops you will find other UART enabled displays. More advanced displays will have more complex API so be sure to check the documentation, example or libraries provided before buying. For example 4D Systems has a line of LCDs with touch support. There is a full blown IDE to program those displays with widgets like touch switches or widgets like charts, gauges and much more. For simpler use there are UART commands. You can even buy a Raspberry Pi, Arduino or Beaglebone starter kits.

In case of Python and UART there is python-picaso-lcd library which wraps the serial API into a usable form, like so:

import time

from picaso_lcd import display

disp = display.Display('/dev/ttyUSB0', 9600)
time.sleep(3)
disp.cls()
LANDSCAPE = 1
disp.set_orientation(LANDSCAPE)

texter = display.DisplayText(disp)
texter.put_string("I'm an LCD display!\n")
texter.put_string("I can display:\n-text\n-basic graphics\n-and do touch events")
time.sleep(15)
4D Systems display scripted from Python over UART

4D Systems display scripted from Python over UART

RkBlog

Raspberry Pi, mini computers, Android and microcontrollers, 21 April 2014,

Comment article