UART / Serial

After I2C and SPI, the third most popular "bus" protocol used is serial (also sometimes referred to as 'UART'). This is a non-shared two-wire protocol with an RX line, a TX line and a fixed baudrate. The most common devices that use UART are GPS units, MIDI interfaces, fingerprint sensors, thermal printers, and a scattering of sensors.

One thing you'll notice fast is that most linux computers have minimal UARTs, often only 1 hardware port. And that hardware port may be shared with a console.

There are two ways to connect UART / Serial devices to your Raspberry Pi. The easy way, and the hard way.

We'll demonstrate wiring up & using an Ultimate GPS with both methods

Adafruit Ultimate GPS Breakout - 66 channel w/10 Hz updates

PRODUCT ID: 746
We carry a few different GPS modules here in the Adafruit shop, but none that satisfied our every desire - that's why we designed this little GPS breakout board. We believe this is...
$39.95
IN STOCK

The Easy Way - An External USB-Serial Converter

By far the easiest way to add a serial port is to use a USB to serial converter cable or breakout. They're not expensive, and you simply plug it into the USB port. On the other end are wires or pins that provide power, ground, RX, TX and maybe some other control pads or extras.

Here are some options, they have varying chipsets and physical designs but all will do the job. We'll list them in order of recommendation.

The first cable is easy to use and even has little plugs that you can arrange however you like, it contains a CP2102

USB to TTL Serial Cable - Debug / Console Cable for Raspberry Pi

PRODUCT ID: 954
The cable is easiest way ever to connect to your microcontroller/Raspberry Pi/WiFi router serial console port. Inside the big USB plug is a USB<->Serial conversion chip and at...
OUT OF STOCK

The CP2104 Friend is low cost, easy to use, but requires a little soldering, it has an '6-pin FTDI compatible' connector on the end, but all pins are broken out the sides

Adafruit CP2104 Friend - USB to Serial Converter

PRODUCT ID: 3309
Long gone are the days of parallel ports and serial ports. Now the USB port reigns supreme! But USB is hard, and you just want to transfer your every-day serial data from a...
$5.95
IN STOCK

Both the FTDI friend and cable use classic FTDI chips, these are more expensive than the CP2104 or PL2303 but sometimes people like them!

FTDI Friend + extras

PRODUCT ID: 284
Note: These use genuine FTDI chips, either we purchase them or they are manufactured to our specifications with the requirement of genuine FTDI chips 10/22/14 -
OUT OF STOCK

FTDI Serial TTL-232 USB Cable

PRODUCT ID: 70
Just about all electronics use TTL serial for debugging, bootloading, programming, serial output, etc. But it's rare for a computer to have a serial port anymore. This is a USB to...
$17.95
IN STOCK

You can wire up the GPS by connecting the following

  • GPS Vin  to USB 5V or 3V (red wire on USB console cable)
  • GPS Ground to USB Ground (black wire)
  • GPS RX to USB TX (green wire)
  • GPS TX to USB RX (white wire)

Once the USB adapter is plugged in, you'll need to figure out what the serial port name is. You can figure it out by unplugging-replugging in the USB and then typing dmesg | tail -10 (or just dmesg) and looking for text like this:

At the bottom, you'll see the 'name' of the attached device, in this case its ttyUSB0, that means our serial port device is available at /dev/ttyUSB0

The Hard Way - Using Built-in UART

If you don't want to plug in external hardware to the Pi you can use the built in UART on the RX/TX pins.

But, if you do this, you'll lose the serial console, so if you're using a PiUART or console cable or HAT that lets you connect directly to the console, that will no longer work and you'll have to use the HDMI+Keyboard or ssh method of running commands!

This isn't a big deal, in fact the serial login-console isn't even enabled by default on Raspbian anymore, but it's worth a warning!

Disabling Console & Enabling Serial

Before wiring up, make sure you have disabled the console.

Run sudo raspi-config and select the following:

Interfacing Options

Serial

Select No on enabling the login shell

Select Yes on enabling serial port hardware

Once complete you should have no console and yes on serial interface:

Then reboot

Once you've rebooted, you can use the built in UART via /dev/ttyS0

Wire the GPS as follows:

  • GPS Vin  to 3.3V (red wire)
  • GPS Ground to Ground (black wire)
  • GPS RX to TX (green wire)
  • GPS TX to RX (white wire)

Install the CircuitPython GPS Library

OK onto the good stuff, you can now install the Adafruit GPS CircuitPython library.

As of this writing, not all libraries are up on PyPI so you may want to search before trying to install. Look for circuitpython and then the driver you want.

(If you don't see it you can open up a github issue on circuitpython to remind us!)

Once you know the name, install it with

pip3 install adafruit-circuitpython-gps

You'll notice we also installed a dependancy called pyserial. This is a great thing about pip, if you have other required libraries they'll get installed too!

We also recommend an adafruit-blinka update in case we've fixed bugs:

pip3 install --upgrade adafruit_blinka

Run that code!

The finish line is right up ahead. You can now run one of the (many in some cases) example scripts we've written for you.

Check out the examples for your library by visiting the repository for the library and looking in the example folder. In this case, it would be https://github.com/adafruit/Adafruit_CircuitPython_GPS/tree/master/examples

Lets start with the simplest, the echo example

# Simple GPS module demonstration.
# Will print NMEA sentences received from the GPS, great for testing connection
# Uses the GPS to send some commands, then reads directly from the GPS
import time
import board
import busio

import adafruit_gps

# Create a serial connection for the GPS connection using default speed and
# a slightly higher timeout (GPS modules typically update once a second).
# These are the defaults you should use for the GPS FeatherWing.
# For other boards set RX = GPS module TX, and TX = GPS module RX pins.
uart = busio.UART(board.TX, board.RX, baudrate=9600, timeout=10)

# for a computer, use the pyserial library for uart access
#import serial
#uart = serial.Serial("/dev/ttyUSB0", baudrate=9600, timeout=10)

# If using I2C, we'll create an I2C interface to talk to using default pins
#i2c = busio.I2C(board.SCL, board.SDA)

# Create a GPS module instance.
gps = adafruit_gps.GPS(uart)     # Use UART/pyserial
#gps = adafruit_gps.GPS_GtopI2C(i2c)  # Use I2C interface

# Initialize the GPS module by changing what data it sends and at what rate.
# These are NMEA extensions for PMTK_314_SET_NMEA_OUTPUT and
# PMTK_220_SET_NMEA_UPDATERATE but you can send anything from here to adjust
# the GPS module behavior:
#   https://cdn-shop.adafruit.com/datasheets/PMTK_A11.pdf

# Turn on the basic GGA and RMC info (what you typically want)
gps.send_command(b'PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0')
# Turn on just minimum info (RMC only, location):
#gps.send_command(b'PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0')
# Turn off everything:
#gps.send_command(b'PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0')
# Tuen on everything (not all of it is parsed!)
#gps.send_command(b'PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0')

# Set update rate to once a second (1hz) which is what you typically want.
gps.send_command(b'PMTK220,1000')
# Or decrease to once every two seconds by doubling the millisecond value.
# Be sure to also increase your UART timeout above!
#gps.send_command(b'PMTK220,2000')
# You can also speed up the rate, but don't go too fast or else you can lose
# data during parsing.  This would be twice a second (2hz, 500ms delay):
#gps.send_command(b'PMTK220,500')

# Main loop runs forever printing data as it comes in
timestamp = time.monotonic()
while True:
    data = gps.read(32)  # read up to 32 bytes
    # print(data)  # this is a bytearray type

    if data is not None:
        # convert bytearray to string
        data_string = ''.join([chr(b) for b in data])
        print(data_string, end="")

    if time.monotonic() - timestamp > 5:
        # every 5 seconds...
        gps.send_command(b'PMTK605')  # request firmware version
        timestamp = time.monotonic()

We'll need to configure this code to work with our UART port name.

  • If you're using a USB-to-serial converter, the device name is probably /dev/ttyUSB0 - but check dmesg to make sure
  • If you're using the built-in UART on a Pi, the device name is /dev/ttyS0 - note that last character is a zero

Comment out the lines that reference board.TX, board.RX and busio.uart and uncomment the lines that import serial and define the serial device, like so:

Download: file
# Define RX and TX pins for the board's serial port connected to the GPS.
# These are the defaults you should use for the GPS FeatherWing.
# For other boards set RX = GPS module TX, and TX = GPS module RX pins.
#RX = board.RX
#TX = board.TX

# Create a serial connection for the GPS connection using default speed and
# a slightly higher timeout (GPS modules typically update once a second).
#uart = busio.UART(TX, RX, baudrate=9600, timeout=3000)

# for a computer, use the pyserial library for uart access
import serial
uart = serial.Serial("/dev/ttyUSB0", baudrate=9600, timeout=3000)

And update the "/dev/ttyUSB0" device name if necessary to match your USB interface

Whichever method you use, you should see output like this, with $GP "NMEA sentences" - there probably wont be actual location data because you haven't gotten a GPS fix. As long as you see those $GP strings sorta like the below, you've got it working!

This guide was first published on Jun 30, 2018. It was last updated on Jun 30, 2018. This page (UART / Serial) was last updated on Dec 05, 2019.