Next we'll work on getting Serial UART working. To get UART working, you will first need to make sure the correct serial ports are enabled. The exact method to enable it can vary from manufacturer to manufacturer. For the Pine64, we used armbian-config and enabled uart2, uart3, and uart4. You can check which serial ports are enabled by typing the following command:

ls /dev/tty*

You'll likely see a lot of ports listed. Finding which port is the proper one is usually found with searching the internet. They often have a letter between the tty part and the number. Here's what we have on the Pine64:

The only ones that have a letter between the tty and number are /dev/ttyS0, /dev/ttyS2, and /dev/ttyS3, which are in fact the correct ones for this board. The reason there is no /dev/ttyS1 is because that port is being used for the Serial Console, so we didn't enable it. You could try narrowing it down with the following command:

ls /dev/tty[a-zA-Z]*

This will list only ports with a letter A-Z (lower case or capital) after the tty part.

Parts Used

To test, we'll need a serial controlled board such as a GPS module, along with a couple other parts to connect it to the board. There's a couple version of the GPS module that are useful. The breakout version is useful for hooking up to GPIO and the USB version makes it easy to test serial via the USB port. For the purposes of this guide though, the breakout version is more useful as it can be connected to a USB to TTL serial cable.

Angled shot of GPS module breakout.
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...
Out of Stock
USB to TTL Serial Cable With Type A plug and 4 wire sockets
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...
In Stock
Angled shot of Half-Size Breadboard with Mounting Holes.
This cute 3.2″ × 2.1″ (82 × 53mm) solderless half-size breadboard has four bus lines and 30 rows of pins, our favorite size of solderless breadboard for...
In Stock
Angled shot of Premium Female/Male 'Extension' Jumper Wires - 40 x 6 (150mm)
Handy for making wire harnesses or jumpering between headers on PCB's. These premium jumper wires are 6" (150mm) long and come in a 'strip' of 40 (4 pieces of each of...
Out of Stock
Angled shot of Premium Male/Male Jumper Wires - 40 x 6 (150mm)
Handy for making wire harnesses or jumpering between headers on PCB's. These premium jumper wires are 6" (150mm) long and come in a 'strip' of 40 (4 pieces of each of...
In Stock

Testing Serial over Built-in UART

For the purposes of adding a new board, we only need to be concerned with whether the Serial over GPIO pins is working as there isn't really anything to configure to get Serial working over USB.

On several boards, the Built-in UART and serial console share the same pins and getting this working isn't always possible.


  • Connect the SBC 3.3V power pin to V+ Rail
  • Connect the SBC GND pin to GND Rail
  • Connect the SBC TX pin to the GPS RX
  • Connect the SBC RX pin to to the GPS TX
  • Connect the GPS Vin power pin to V+ Rail
  • Connect the GPS GND pin to GND Rail

Run the Test Script

After wiring it up, double-check your connections. Next, make sure PySerial and the GPS library are installed. If you chose a different sensor, make sure that library is installed:

sudo pip3 install pyserial adafruit-circuitpython-gps

Next save the simpletest code to your board as

# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT

# Simple GPS module demonstration.
# Will wait for a fix and print a message every second with the current location
# and other details.
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 = board.I2C()  # uses board.SCL and board.SDA
# i2c = board.STEMMA_I2C()  # For using the built-in STEMMA QT connector on a microcontroller

# Create a GPS module instance.
gps = adafruit_gps.GPS(uart, debug=False)  # Use UART/pyserial
# gps = adafruit_gps.GPS_GtopI2C(i2c, debug=False)  # 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:

# Turn on the basic GGA and RMC info (what you typically want)
# 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')
# Turn 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.
# 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 the location, etc. every second.
last_print = time.monotonic()
while True:
    # Make sure to call gps.update() every loop iteration and at least twice
    # as fast as data comes from the GPS unit (usually every second).
    # This returns a bool that's true if it parsed new data (you can ignore it
    # though if you don't care and instead look at the has_fix property).
    # Every second print out current location details if there's a fix.
    current = time.monotonic()
    if current - last_print >= 1.0:
        last_print = current
        if not gps.has_fix:
            # Try again if we don't have a fix yet.
            print("Waiting for fix...")
        # We have a fix! (gps.has_fix is true)
        # Print out details about the fix like location, date, etc.
        print("=" * 40)  # Print a separator line.
            "Fix timestamp: {}/{}/{} {:02}:{:02}:{:02}".format(
                gps.timestamp_utc.tm_mon,  # Grab parts of the time from the
                gps.timestamp_utc.tm_mday,  # struct_time object that holds
                gps.timestamp_utc.tm_year,  # the fix time.  Note you might
                gps.timestamp_utc.tm_hour,  # not get all data like year, day,
                gps.timestamp_utc.tm_min,  # month!
        print("Latitude: {0:.6f} degrees".format(gps.latitude))
        print("Longitude: {0:.6f} degrees".format(gps.longitude))
            "Precise Latitude: {:2.}{:2.4f} degrees".format(
                gps.latitude_degrees, gps.latitude_minutes
            "Precise Longitude: {:2.}{:2.4f} degrees".format(
                gps.longitude_degrees, gps.longitude_minutes
        print("Fix quality: {}".format(gps.fix_quality))
        # Some attributes beyond latitude, longitude and timestamp are optional
        # and might not be present.  Check if they're None before trying to use!
        if gps.satellites is not None:
            print("# satellites: {}".format(gps.satellites))
        if gps.altitude_m is not None:
            print("Altitude: {} meters".format(gps.altitude_m))
        if gps.speed_knots is not None:
            print("Speed: {} knots".format(gps.speed_knots))
        if gps.track_angle_deg is not None:
            print("Track angle: {} degrees".format(gps.track_angle_deg))
        if gps.horizontal_dilution is not None:
            print("Horizontal dilution: {}".format(gps.horizontal_dilution))
        if gps.height_geoid is not None:
            print("Height geoid: {} meters".format(gps.height_geoid))

Next, we need to make a few changes to the test script:

  • Comment out the lines that reference board.TXboard.RX and busio.uart
  • Uncomment the line to import serial
  • Uncomment the line to define the serial device.
  • Change the Serial port to the one you are testing

Now go ahead and run that code using the following command:

sudo python3

You should see it outputting some NMEA sentences about every 5 seconds or so:

You will want to perform the same test procedure on each UART port that you want to test. Be sure to change both your wiring and the corresponding pins in the test script if necessary.

Troubleshooting Tips

  • Make Sure the Serial UART is enabled on your board
  • Make sure the Serial Console isn't sharing the pins
  • Check your wiring
  • The board may just not work with Serial.

This guide was first published on Apr 01, 2020. It was last updated on Feb 22, 2024.

This page (Getting Serial UART Working) was last updated on Feb 12, 2024.

Text editor powered by tinymce.