In the previous article we configured three USB OTG functions (USB Ethernet, USB serial communication, and USB storage) using the Configfs method. This was to enable multiple functions at the same time. If you want a single function, specify each function individually instead of using Composite.
Linux has a mechanism called USB Gadget Framework.
| gadget | Contents |
|---|---|
| g_ether | USB Ethernet |
| g_serial | USB Serial |
| g_mass_storage | USB Storage |
| g_hid | Keyboard and mouse |
| Composite | Simultaneous provision of multiple functions |
The last issue was how to set up the table Composite.
This time, we will try the easy method of using only one function with g_ether, g_serial, and g_mass_storage.
With a single module, multiple functions cannot be set at the same time, but instead are controlled by the kernel, so no additional work is required for automatic startup.
Here is how to set up each and test a single function.
Notes at the time of writing
For this environment, we used the PL-R5, an industrial Raspberry Pi based on the CM5. the OS is Raspberry Pi OS (bookworm). The host PC was tested on Linux/macOS.
Verification environment
Model: Industrial Raspi “PL-R5” (Raspberry Pi Compute Module 5 Rev 1.0)
OS: Debian GNU/Linux 12 (bookworm)
Kernel: 6.12.62+rpt-rpi-v8
macOS: Tahoe 26.3
When using Windows as the host PC, the RNDIS (Remote Network Driver Interface Specification) driver is required because ECM (Ethernet Control Model) is not supported. After connecting the USB cable, update the driver in Device Manager.
If only USB Ethernet is available, just enable it.
The rpi-usb-gadget package is included with the Raspberry Pi OS Trixie. If you enable it, it will be recognized as a USB Ethernet device without manual configuration and you can immediately connect via SSH without having to configure Wi-Fi or wired LAN.
The following is a quote from the official website.
“Starting with Raspberry Pi OS Trixie images dated 20.10.2025 or later, a new package called rpi-usb-gadget is included by default. activated, the Raspberry Pi presents itself as a USB Ethernet device, and you can connect via SSH immediately, without configuring Wi-Fi or Ethernet .”
After October 20, 2025, Raspberry Pi OS Trixie images will include a new package called rpi-usb-gadget by default. Once enabled, the Raspberry Pi will be recognized as a USB Ethernet device, allowing for immediate SSH connectivity without configuring Wi-Fi or Ethernet.
https://www.raspberrypi.com/news/usb-gadget-mode-in-raspberry-pi-os-ssh-over-usb/
If only USB Ethernet is available, it is easy now.
Just choose from raspi-config.
sudo raspi-config
Cable selection and destination port for USB OTG
In the case of PL-R5, the USB cable connection to the PC is not the usual USB Type-A, but the USB Type-C port used for writing the OS image. This is easy to understand. (The write switch remains OFF)

However, on the Pi 4/5, the USB Type-C port that is normally used for OTG is the USB Type-C port to which the power adapter is connected. Power is also supplied via USB and the OS is also booted.
However, with Pi 5, when directly connected to a PC with a USB Type-C cable, the decision as to which side to operate may not be well determined. Depending on the cable, it may not be properly recognized as Mass Storage Gadget mode.
With a USB Type-A to USB Type-C (USB 2.0 specification) cable, the USB-A side is always easily fixed as the host of the side to be operated, making role determination stable and most reliable.
The next candidate is USB Type-C to Type-C (only USB 2.0 is supported). This is not a sure thing, but it should be generally recognizable.
However, USB 3.x/PD compatible cables with e-Marker, cables claiming high-speed charging, and Thunderbolt 3/4 cables may cause unstable recognition depending on the environment.
As an exception, if you want to achieve USB OTG with the “PL-R5”, please use a USB cable with a switch that can turn the power on and off. The switch must be turned OFF to prevent power from being supplied via the USB cable.
This is because, unlike when rpibooting, the unit is already running on a power adapter.

Note that any model cannot be in gadget mode, of course, because data communication is not possible with a power-only USB Type-C cable.
Depending on each Raspberry Pi model, care should be taken when selecting a USB cable.
Preparation: Set USB OTG port to Device mode (manually)
If you do not use USB Ethernetization with raspi-config in the previous step, you need to configure it manually.
To use the PL-R5’s USB2.0 OTG port on the device side, first set the USB controller to enable Peripheral mode at boot time.
This is basically the same for USB Ethernet, serial communication, and USB storage.
/boot/firmware/config.txtAdd to
dtoverlay=dwc2,dr_mode=peripheral
However, since host is often included by default in CM5 series, if the following is the case, comment it out before adding it.
[cm5]
#dtoverlay=dwc2,dr_mode=host
dtoverlay=dwc2,dr_mode=peripheral
/boot/firmware/cmdline.txt(Separated by a single-byte space at the end and no line break)
modules-load=dwc2
Restart the system after the addition.
It is now enabled.
*If you use USB Ethernet with raspi-config as in the previous section, you do not need this setting.
USB Ethernet (manual setting)
What is USB Ethernet? It is a mechanism whereby the PL-R5 is recognized by the PC as a “USB-connected LAN adapter” with a single USB cable.
The network is assigned an IP address and directly connected peer-to-peer without the need for LAN cables.
It is connected to the virtual network interface usb0.

Practical examples
– Access to the initial setup Web UI
– As a maintenance access method
– For emergency connections in environments without DHCP
– Direct SSH connection
It is also possible to create a system in which “just connect the USB cable and the management screen will open.
If the server is running on the Raspberry Pi side, the server on the Raspberry Pi side can be used from the host PC side even when two units are connected.
It can operate without the need for existing DHCP, etc.
USB Ethernet single function test
*If you use USB Ethernet with raspi-config as in the previous section, you do not need this setting.
Create usb0 as a virtual network card (NIC) and test connecting it to the host PC.
/boot/firmware/cmdline.txtThe following changes are made to the description of
modules-load=dwc2
↓
modules-load=dwc2,g_ether
*If the host PC is Windows, no description is given.
It will be enabled after rebooting.
Tip: If the host PC is Windows
When Windows is used as the host PC, the notation g_ether is not necessary.modules-load=dwc2remain the same.
Instead, RNDIS must be enabled on the Windows side and the driver must be installed.
If the Windows side operation is automatically recognized as follows, driver installation is not necessary.
✅ Automatically recognized as “Remote NDIS Compatible Device”
✅ Shown on network adapter
If it is not recognized automatically, follow these steps:
1. open Device Manager
2. right click on “Unknown Device”
3. update driver
4. “Browse Computer”
5. “Select from List”
6. “Network Adapter”
7. “Microsoft”
8. “Remote NDIS Compatible Device


If the host PC is Linux/macOS as in this case, this procedure is not necessary.
Creation of virtual NIC (usb0)
Since we have already enabled the OTG port as Device mode, the next step is to create usb0 as a virtual network card (NIC) and assign it a fixed IP address.
Simply run the nmcli command to automatically connect them at the same time.
sudo nmcli con add type ethernet \
ifname usb0 con-name usb0 \
ipv4.method manual ipv4.addresses 192.168.7.2/24 \
connection.autoconnect yes
Connection ‘usb0’ (449a53a2-8cf5-4c9c-9320-ee8eddbdd3c4) successfully added.
If it appears like this, it is OK.
I will upload the link.
sudo ip link set usb0 up
Next, on the host PC side, set the IP address to 192.168.7.1/24in the network settings.
For example, if you are on macOS, when you connect the cable, “RNDIS/Ethernet Gadget” or something similar is newly added. If it is not there, go to Add Service and add Ethernet.
Open Details and manually set the IP address as follows
Host PC Network IP Settings
Configuration: Manual
IP Address: 192.168.7.1
Subnet Mask: 255.255.255.0
Router: (blank)

Then, execute the following command on the Pi side (PL-R5) to complete the connection.
sudo nmcli con up usb0
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/3)
You should be able to use the ip a command on the Pi side, ping or ssh connection from the host PC side as shown below.
# Check usb0 on the Pi
ip a
## From the host PC to the Pi
ping 192.168.7.2
ssh <username>@192.168.7.2
If communication is still connected even after disconnecting the wired LAN or Wi-Fi on the Pi side, you will know that you are connected via USB-OTG Ethernet.
If Apache is installed on the Pi side, the default Apache page will be displayed in the web browser on the host PC.

sudo apt install apache2
In a web browser on the host PC, go to http://192.168.7.2/.
Did you see the Apache2 Debian Default Page?
You now have a connection with a single USB cable.
2.USB Serial
The next step is serial communication.
The PL-R5 is recognized by the PC as a “serial converter with USB connection” with a single USB cable.
Practical examples
– Check PL-R5 logs
– Command operation
– Two-way communication
– Bridge use with microcontroller
This time LAN (not joining a network) is also not required.
Since it is the most reliable maintenance route, it is likely to be used in industrial applications.
USB Serial single function test
The previous description of /boot/firmware/cmdline.txtis now changed to the following
modules-load=dwc2,g_ether
↓
modules-load=dwc2,g_serial
Reboot to activate.
/dev/ttyGS0is responsible for USB Serial.
This is disabled by default, so we will manually enable it and start the service for you.
sudo systemctl status serial-getty@ttyGS0
○ serial-getty@ttyGS0.service - Serial Getty on ttyGS0
Loaded: loaded (/lib/systemd/system/serial-getty@.service; disabled; preset: enabled)
Active: inactive (dead)
Docs: man:agetty(8)
man:systemd-getty-generator(8)
https://0pointer.de/blog/projects/serial-console.html
Activate and start service.
sudo systemctl enable serial-getty@ttyGS0
sudo systemctl start serial-getty@ttyGS0
Created symlink /etc/systemd/system/getty.target.wants/serial-getty@ttyGS0.service → /lib/systemd/system/serial-getty@.service.and a symbolic link is created.
After starting the service, I look at the status again and see that it is active (running).
● serial-getty@ttyGS0.service - Serial Getty on ttyGS0
Loaded: loaded (/lib/systemd/system/serial-getty@.service; enabled; preset: enabled)
Active: active (running) since Thu 2026-02-26 14:19:12 JST; 2s ago
Docs: man:agetty(8)
man:systemd-getty-generator(8)
https://0pointer.de/blog/projects/serial-console.html
Main PID: 2221 (agetty)
Tasks: 1 (limit: 4693)
CPU: 2ms
CGroup: /system.slice/system-serial\x2dgetty.slice/serial-getty@ttyGS0.service
└─2221 /sbin/agetty -o "-p -- \\u" --keep-baud 115200,57600,38400,9600 - vt220
On the Linux/macOS side of the terminal, check the port to be used for serial communication.
ls -l /dev/tty.usb*
crw-rw-rw- 1 root wheel 0x9000004 Feb 26 14:14 /dev/tty.usbmodem21101
Apparently, it is usbmodem21101 in my environment, so I will connect with this. Use the port that you have checked in your environment.
In the macOS terminal, I used the screen command to connect.
Finally, add the communication speed and run. (9600 bps by default (standard))
For Windows, you can do the same with PuTTY.
screen /dev/tty.usbmodem21101 115200
The first time, you will be asked for a login name and password like an SSH connection.
Apparently the connection is OK.

Termination Method
After executing the screen command, you can exit the screen not by exit, but by pressing Ctrl + A followed by K to kill the screen.
Ctrl + A → K → y
Testing of bidirectional communication
If you pass a log~ description to ttyGS0 from the PL-R5 side as follows, it will be displayed on the host terminal.
Originally, the commands are executed on the actual device, but the result is the same even if two terminals are used with an SSH connection.

echo "log: sensor=26.8C" > /dev/ttyGS0
Host-side display.
log: sensor=26.8C
USB Serial does not require networking.
No IP address or DHCP configuration is required to enable login and bidirectional communication.
The speed is slow, but it is sufficient for a text-centered maintenance path.
Result of executing the ping command
I try to run a ping from the host PC to the Pi side.

Screen screen blank?
No problem, let’s execute some commands on the Pi side. If you execute the command as before, you should see the result on a blank terminal screen on the host PC (e.g., ).
(For example, execute uname -a)
The pure white is the waiting state.
Exit this screen in the same way using the previous exit method.
USB Mass Storage
USB Mass Storage is a system whereby a PC recognizes a portion of the PL-R5 storage area as “USB memory” with a single USB cable, similar to Ethernet and Serial.
From the host PC side, it appears as a removable disk and can be opened in Finder or Explor for file management. If the permission is rw, files can be read and written.
In other words, it is a function that allows you to treat a designated area of the running PL-R5 as if it were a USB memory device.
You can specify image files such as .bin, .img, or partitions.
Essential differences between rpiboot and USB Mass Storage Gadget
It is difficult to understand, but the two meanings are different: writing an OS image file to the eMMC of the PL-R5 or CM5, and connecting in USB Mass Storage Gadget mode as USB OTG.
When connecting via rpiboot, the PL-R5/CM5 OS is not booted. The eMMC is shown connected directly to the PC via boot ROM.
On the other hand, in Gadget mode, PL-R5/CM5’s OS is running and the pre-designated area is shown as a USB memory.
| (data) item | rpiboot | Mass Storage Gadget |
|---|---|---|
| OS boot | No, I don’t. | I’m doing it. |
| subject (of taxation, etc.) | Entire eMMC | Arbitrary area |
| use | Writing OS | data transfer |
| control | boot ROM | OS |
The terminal screen after running rpiboot is shown below, and the mounted contents are displayed in a filer such as Finder after the run.
In the case of rpiboot, only the bootfs is mounted, and the contents are OS configuration files.

USB Mass Storage, on the other hand, displays the contents of the specified area.

This is a bit complicated because both look like USB drives when viewed from the host PC.
Share the specified area
USB Mass Storage entities can be handled in units of a single image file or partition.
The PL-R5 OS can decide to show this area as a USB flash drive.
It can be any image file or any partition, not the entire eMMC.
Files such as usb.img and usb.bin created on the PL-R5 side can be shown to the host PC side, but it is dangerous to mount them only on one of them. This is because data can be rewritten from either side.
In this test, partitions are not separated. Create /opt/usb, create an image file there as piusb.bin, and publish it to the host PC side.
Example of operational configuration
The following explanation is based on the pattern of mounting for log collection as a practical operation.
For example, on the PL-R5 side, the method is to create a dedicated partition where logs are stored and have them handled on a partition-by-partition basis.
This would have little impact on the system if it were to break.
In eMMC:
├ /rootfs
├ /data
└ /log_export ← This partition is exposed as USB storage
Normally, logs are output to /var/log.
As a possible operation, it is recommended that log data be periodically copied to a USB flash drive (/log_export) and be published to the host PC in a read-only manner.
It seems that accidents can be prevented considerably by not mounting /log_export on the PL-R5 side normally, having it mounted only when copying log data, and unmounting it after copying.
USB Mass Storage single function test
As before, change the description of /boot/firmware/cmdline.txtto the following
modules-load=dwc2,g_serial
↓
modules-load=dwc2,g_mass_storage
Reboot to activate.
Module options also set
For USB Mass Storage, only one more move is required in cmdline.txt.
Separate them with a single space and then append the next one to the end of the line as well. (cmdline.txt does not break lines.) Be careful when changing the path to the
file.
g_mass_storage.file=/opt/usb/piusb.bin g_mass_storage.stall=0 g_mass_storage.removable=1 g_mass_storage.ro=0
The file to mount is /opt/usb/piusb.bin. It will be created after this. stall=0for no error handling and removable=1for “removable” in the filer Finder or Explorer. ro=0is read/write and set to 1 for read-only.
Creating Image Files
Create a usb directory under /opt and place it there. In this example, the capacity is 128 MB.
sudo mkdir -p /opt/usb
sudo dd if=/dev/zero of=/opt/usb/piusb.bin bs=1M count=128
sudo mkfs.vfat -n CM5USB /opt/usb/piusb.bin
-n CM5USBis the label that will be displayed when the image file is mounted. Change it as desired.
Now reboot and it will appear as a USB flash drive in Linux/Mac/Windows filers, etc.
Investigating Errors
I won’t go into detail, but I will leave you with a command to try if it doesn’t work.
Everything is executed on the Pi side.
I found out when I looked it up with the commands I wanted to try in the communication survey.
ip route
ip route | grep usb0
ip addr show usb0
cat /sys/class/net/usb0/carrier
ip link show
systemctl status cm5-usb-gadget.service
nmcli dev status
Single function USB OTG is easy
As in the previous article, three functions can be set up at the same time if configured using the Configfs method. However, there are many settings to be made.
If you want to use a single function, it is easiest to set it up with g_ether and so on.
In the case of USB Ethernet in particular, it is prebuilt into the current Raspberry Pi OS and only needs to be enabled from raspi-config.
Since USB OTG allows direct access with a single USB cable, it may be something useful to set up for both industrial and personal use.
Please test once in advance, including the selection of the USB cable, to see how it will work.
Article contributed by Raspida
Raspberry Pi information site that even non-engineers can enjoy using raspida.com a Raspberry Pi information site that even non-engineers can enjoy and handle. He also contributes technical blog articles to the PiLink site on the Raspberry Pi for industrial use.

