June 8, 2025
Virtual UART over USB (Raspberry pi)

Virtual UART over USB (Raspberry pi)

We recently published an article on Ethernet over USB using the USB gadgets functionality in Linux, which you can find here : . Now, we are expanding on that by implementing a virtual USART/serial port on the same USB port that is running the Ethernet USB gadget.

Recap: Ethernet Over USB

In our earlier article, we detailed how to set up Ethernet over USB using the USB gadgets functionality in Linux. This allows a Linux device to act as a USB Ethernet device, providing network connectivity to a host computer. This setup is beneficial for scenarios where traditional networking options are unavailable or impractical.

The Need for Virtual UART

While Ethernet over USB provides network communication, there are situations where serial communication is preferable or required. This is especially true for embedded systems and hardware development, where debugging or interfacing with hardware often relies on serial communication. Implementing a Virtual UART over the same USB port that supports Ethernet can streamline development and reduce the need for multiple physical connections.

Implementing Virtual UART Over USB

Connect the Usb Type-C port Raspberry pi 4 to pc.

Next , we will need to enable the dwc2 overlay by modifying the boot configuration. Open the config.txt file by :

sudo nano /boot/config.txt

# Add the following line at the end:

dtoverlay=dwc2

# Save and exit (CTRL+X, then Y).

Next , we will need to create usb acm gadget by executing following script :

#!/bin/bash

GADGETDIR='mygadget'
SERIAL=`cat /proc/cpuinfo | grep Serial | cut -d ' ' -f 2`
HOSTPREFIX="02"
DEVICEPREFIX="06"
MANUFACTURER="nobody"
PRODUCT='nothing'

# Calculate MAC addresses based on unique serial number of raspberry pi 
padded='00000000000000'$SERIAL
basemac=${padded: -12} # last 12 characters
hostmac=$HOSTPREFIX:${basemac: -10:2}:${basemac: -8:2}:${basemac: -6:2}:${basemac: -4:2}:${basemac: -2:2}
devmac=$DEVICEPREFIX:${basemac: -10:2}:${basemac: -8:2}:${basemac: -6:2}:${basemac: -4:2}:${basemac: -2:2}

# Check if running as root
if [ "$(id -u)" -ne 0 ]; then
    echo "Must be root" >&2
    exit 1
fi

# load the libcomposite module

modprobe libcomposite

mkdir -p /sys/kernel/config/usb_gadget/$GADGETDIR
cd /sys/kernel/config/usb_gadget/$GADGETDIR || exit

echo 0x1d6b > idVendor
echo 0x0104 > idProduct
echo 0x0100 > bcdDevice
echo 0x0200 > bcdUSB
mkdir -p strings/0x409
echo $SERIAL > strings/0x409/serialnumber
echo $MANUFACTURER > strings/0x409/manufacturer
echo $PRODUCT > strings/0x409/product
mkdir -p configs/c.1/strings/0x409
echo "Config 1: ECM network" > configs/c.1/strings/0x409/configuration
echo 250 > configs/c.1/MaxPower
mkdir -p functions/ecm.usb0
# Assign static mac address
echo $hostmac > functions/ecm.usb0/host_addr
echo $devmac > functions/ecm.usb0/dev_addr
ln -s functions/ecm.usb0 configs/c.1/

# this will create the virtual serial port

ln -s functions/acm.usb0 configs/c.1/  
ls /sys/class/udc > UDC

After running the above script if you go to “/dev/” search/grep for of tty port then your output will look something like this on raspberry pi

ls /dev/ |grep tty
tty
tty0
tty1
tty10
tty11
tty12
tty13
tty14
tty15
tty16
tty17
tty18
tty19
tty2
tty20
tty21
tty22
tty23
tty24
tty25
tty26
tty27
tty28
tty29
tty3
tty30
tty31
tty32
tty33
tty34
tty35
tty36
tty37
tty38
tty39
tty4
tty40
tty41
tty42
tty43
tty44
tty45
tty46
tty47
tty48
tty49
tty5
tty50
tty51
tty52
tty53
tty54
tty55
tty56
tty57
tty58
tty59
tty6
tty60
tty61
tty62
tty63
tty7
tty8
tty9
ttyGS0
ttyprintk

in this tty<Number> and ttyprintk are standard tty ports but ttyGS0 is created by our script .

if you are running linux on pc/laptop then open up your terminal and type :

sudo dmesg 

# Now your output will look something like this 

[10016.632226] usb 3-3.2: device descriptor read/64, error -110
[10028.345433] usb 3-3.2: device descriptor read/64, error -71
[10028.525349] usb 3-3.2: new high-speed USB device number 26 using xhci_hcd
[10028.616464] usb 3-3.2: New USB device found, idVendor=1d6b, idProduct=0104, bcdDevice= 1.00
[10028.616482] usb 3-3.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[10028.616488] usb 3-3.2: Product: nothing
[10028.616493] usb 3-3.2: Manufacturer: nobody
[10028.616496] usb 3-3.2: SerialNumber: 10000000f47d98a9
[10028.623829] cdc_ether 3-3.2:1.0 usb0: register 'cdc_ether' at usb-0000:00:14.0-3.2, CDC Ethernet Device, 02:00:f4:7d:98:a9
[10028.624358] cdc_acm 3-3.2:1.2: ttyACM4: USB ACM device
[10028.637347] cdc_ether 3-3.2:1.0 enp0s20f0u3u2: renamed from usb0
[10029.462694] overlayfs: "xino" feature enabled using 3 upper inode bits.
[10032.500087] wlan0: Driver requested disconnection from AP 1e:61:b4:1b:50:9f
[10035.854742] wlan0: authenticate with 1e:61:b4:1b:50:9f
[10035.854793] wlan0: 80 MHz not supported, disabling VHT
[10035.859649] wlan0: send auth to 1e:61:b4:1b:50:9f (try 1/3)
[10036.002056] wlan0: send auth to 1e:61:b4:1b:50:9f (try 2/3)
[10036.005777] wlan0: authenticated
[10036.012104] wlan0: associate with 1e:61:b4:1b:50:9f (try 1/3)
[10036.027873] wlan0: RX AssocResp from 1e:61:b4:1b:50:9f (capab=0x31 status=0 aid=1)
[10036.040193] wlan0: associated

Here we are only interested in line which has cdc_acm in it .

  • cdc_acm 3-3.2:1.2: ttyACM4: USB ACM device

From this we get the name of the tty device created by raspberry pi scirpt i.e. ttyACM4

if you are on windows then open your device manager to know the name of serial com port.

Testing Your Setup

Once the gadget is configured, you can connect the device to a host computer. The host should recognize both the Ethernet interface and the virtual serial port. You can use tools like screen, minicom, or putty to communicate over the virtual UART.

To verify Ethernet connectivity, use standard networking commands (e.g., ping). For serial communication, you can send data back and forth using terminal software.

on rapberry pi

install the picocom / minicom or tio and open ttyGS0 with your preferred software. In this we will use picocom which can be installed and run by

sudo apt install picocom
sudo picocom -b 115200 /dev/ttyGS0

on pc/laptop running linux

sudo picocom -b 115200 /dev/ttyACM4

For window you can use hyper terminal.

Now, when you type something on the PC running Picocom, it should appear on the Raspberry Pi running Picocom, and vice versa.

Conclusion

Implementing a Virtual UART over USB alongside Ethernet in Linux enhances the versatility of your device’s communication capabilities. By utilizing USB gadgets, you can streamline connections and simplify development processes. This dual functionality is particularly valuable in embedded systems and IoT applications, where multiple forms of communication are often necessary.

Leave a Reply

Your email address will not be published. Required fields are marked *