Using CircuitPython’s Two USB Serial Ports

If you want to find more Python Shorts you can find them here.

Making a CircuitPython program talk to a usb serial device is quite easy to do:

import usb_cdc

# Send a line of text to the host
usb_cdc.data.write(b"Hello!\n")

# Wait for a line of text from the host
while True:
    if usb_cdc.data.connected:
        try:
            line = usb_cdc.data.readline()
            if line:
                print("Received:", line.decode("utf-8").strip())
        except Exception as e:
            print("Error reading:", e)

Howwever, when using CircuitPython on boards like the Raspberry Pi Pico, it exposes two USB serial ports over USB:

  • usb_cdc.console → used for the REPL (interactive shell)
  • usb_cdc.data → intended for raw programmatic communication

When you use a browser that supports Web Serial (like Chrome or Edge), it might show both ports as separate choices. It's easy to accidentally connect to the console port instead of the data port. Your CircuitPython code ends up listening on usb_cdc.data, but the browser is sending to usb_cdc.console.

There are two ways you can fix this:

  • Try both ports while connecting
  • Or disable the REPL/console port in production using:
# boot.py
import usb_cdc
usb_cdc.disable(console=True)

Note that if you disable the console port you will not be able to interact with the device using tools such as Thonny. You will have to erase the EEPROM in the PICO and the re-flash it to get back control of the device.