How to manage multiple serial devices on a Mac

I connect to my Mac many USB devices that communicate over a serial (UART) bus to send debug information to the host or to receive data and code. You know, Raspberry Pi Picos, Adafruit Feathers, FTDI cables — that kind of thing. Often I have more than one connected. Is there an easy way to see what’s connected without listing /dev every time and to remember connected devices’ paths?

A USB serial device attached to my Mac

There is: I wrote a script. It’s in the form of a Z Shell function, so you can add it to your .zshrc file so it’s always ready to use.

Here it is:

dlist() {
    local devices=($(/bin/ls /dev/cu.usb* 2>/dev/null))
    if [ ${#devices} -gt 0 ]; then
        if [[ ${#devices} -eq 1 ]]; then
            echo $devices
        else
            local count=1
            for device in $devices; do
                if [[ -n "$1" && "$1" == "$count" ]]; then
                    echo $device
                elif [[ -z "$1" ]]; then
                    echo "$count. $device"
                fi
                ((count+=1))
            done
        fi
    fi
}

How does it work? Just run dlist at the command line to list connected serial devices. If there are more than one, they’re presented as a list:

1. /dev/cu.usbmodem1101
2. /dev/cu.usbserial-01AB8E0B

The beauty is that to use the device, just used $(dlist) anywhere you had previously entered the device’s file path. The shell calls the function and substitutes in the path.

For example, let’s say you want to send a file to a Pico running MicroPython. Then you’d use:

python -m pyboard.py -d $(dlist) -f cp main.py :main.py

No need to find out whether the device is at /dev/cu.usbserial-01AB8E0B or whatever, or to keep copying and pasting that path.

Running the script: dlist in action
dlist in action

If you have multiple devices, just pass in the number of the device you want from the list. Let’s say device 2 is the one you want to send the file to, then you’d enter:

python -m pyboard.py -d $(dlist 2) -f cp main.py :main.py

Meanwhile you can be viewing debug logs from the other device with:

minicom -o -D $(dlist 1)

You can get the Z Shell function from this Gist.

For purists, there’s a separate version for Bash, implemented as an executable script, here.