Pi Zero USB OTG Gadget Mode: Ethernet over a Single USB Cable
One feature makes the Pi Zero family special compared with the larger Raspberry Pi boards: its data port is connected directly to the processor's USB On-The-Go controller. That means the Zero can act not only as a USB host, but also as a USB device, or gadget. Plug it into a laptop and it can appear as a network adapter, a keyboard, a serial port or a flash drive.
In this tutorial you will configure the most useful gadget, a virtual Ethernet adapter. One micro USB cable then both powers the Zero and gives you an SSH connection to it, with no Wi-Fi, no router and no monitor. This is ideal for labs where you cannot join the Wi-Fi, for demonstrations, and for the original Pi Zero that has no wireless at all.
How USB Gadget Mode Works
USB is asymmetric. A host (your laptop) controls the bus and a device (a mouse, a pen drive) answers. Most single-board computers only have host ports. On the Zero, the port labelled USB connects to the Synopsys DesignWare controller inside the SoC, whose Linux driver is called dwc2. With dwc2 loaded in peripheral mode, Linux gadget drivers can describe the Zero to the host as any USB device class.
The g_ether module is a ready-made gadget that presents a USB network interface. It supports CDC ECM, which Linux and macOS understand natively, and RNDIS, which Windows uses. On the Zero a new network interface called usb0 appears; on the laptop a new Ethernet adapter appears. You then give both ends IP addresses and talk over it like a normal network cable.
| Port on the Zero | Label | Use in this project |
|---|---|---|
| Outer micro USB | PWR IN | Leave empty; power comes through the data cable |
| Inner micro USB (closer to the HDMI port) | USB | Connect to the laptop with a data-capable cable |
| Mini HDMI | HDMI | Not needed |
Enabling the Ethernet Gadget on Bookworm
Two boot files need a small change. On Raspberry Pi OS Bookworm they are in /boot/firmware/ when you are logged in to the Pi, or at the top level of the FAT boot partition when the card is in your laptop. The easiest route for a Zero W or Zero 2 W is to log in once over Wi-Fi and run the commands below. For an original Zero, edit the two files on the card from your laptop with a plain text editor.
# 1. Put the USB controller into OTG/peripheral-capable mode.
# Append the overlay to the end of config.txt (it lands in the [all] section).
echo 'dtoverlay=dwc2' | sudo tee -a /boot/firmware/config.txt
# 2. Load dwc2 and g_ether at boot. cmdline.txt must stay ONE line,
# so insert the option right after the word 'rootwait'.
sudo sed -i 's/rootwait/rootwait modules-load=dwc2,g_ether/' /boot/firmware/cmdline.txt
# 3. Check both edits
tail -n 3 /boot/firmware/config.txt
cat /boot/firmware/cmdline.txt
Next, tell NetworkManager how to address the new usb0 interface. The simplest choice is link-local addressing. Both the Zero and the laptop pick a 169.254.x.x address automatically when no DHCP server answers, and the Pi's mDNS service lets you reach it as hostname.local without knowing the number.
# Link-local IPv4 and IPv6 on usb0; connects whenever the cable is plugged in
sudo nmcli connection add type ethernet ifname usb0 con-name usb-gadget \
ipv4.method link-local ipv6.method link-local connection.autoconnect yes
# Alternative: a fixed address if you prefer to set the laptop side manually
# sudo nmcli connection modify usb-gadget ipv4.method manual ipv4.addresses 192.168.7.2/24
sudo reboot
Connecting from the Laptop
- Shut the Pi down, remove its power supply and connect a data cable from the laptop to the Zero's USB port. The Zero powers up from the laptop.
- Wait about a minute for boot. A new network adapter should appear on the laptop: a USB Ethernet/RNDIS device on Windows, or a new Ethernet interface on Linux and macOS.
- Leave the adapter on automatic (DHCP). With no DHCP server, the laptop falls back to a 169.254.x.x link-local address; Windows can take up to a minute to do this.
- Run
ssh student@pizero.local, replacing the username and hostname with your own. - If you used the fixed-address alternative, set the laptop adapter to 192.168.7.1 with mask 255.255.255.0 and connect to 192.168.7.2.
On the Pi, a working link looks like this: $ ip -brief addr show usb0 usb0 UP 169.254.x.x/16 fe80::.../64 $ nmcli device status | grep usb0 usb0 ethernet connected usb-gadget
To give the Zero internet access through the laptop, enable connection sharing on the laptop side. On a Linux laptop using NetworkManager, set the new wired connection's IPv4 method to Shared to other computers; the laptop then runs DHCP and NAT. In that case switch the Pi's usb-gadget profile to ipv4.method auto so it accepts the address the laptop offers.
Troubleshooting
- No new adapter on the laptop: cable is charge-only, or it is plugged into PWR IN. Run
lsmod | grep -E 'dwc2|g_ether'on the Pi (over Wi-Fi or a screen) to confirm both modules are loaded. - Pi no longer boots after editing cmdline.txt: the file was split onto two lines or a space was lost. It must be a single line with options separated by spaces.
- Windows shows an unknown device: in some Windows versions the RNDIS driver is not matched automatically. In Device Manager, update the driver manually and choose the Microsoft Remote NDIS compatible device from the network adapter list.
- pizero.local does not resolve: try the IPv6 link-local address shown by
ip addron the Pi, or use the fixed-address method. - Unstable when powered from a laptop port: some laptop ports supply little current. Unplug USB accessories from the Zero, or use a powered hub.
Summary
With dtoverlay=dwc2 in config.txt, modules-load=dwc2,g_ether in cmdline.txt and a NetworkManager profile for usb0, a Pi Zero becomes a plug-in network device that needs nothing more than one USB cable. The same dwc2 controller is the foundation for other gadgets: a serial console with g_serial, mass storage with g_mass_storage, or a custom composite device built with libcomposite, which a later tutorial uses to make the Zero behave as a USB keyboard.