Skip to content

Instantly share code, notes, and snippets.

@gbaman
Last active March 26, 2024 20:51
Star You must be signed in to star a gist
Save gbaman/50b6cca61dd1c3f88f41 to your computer and use it in GitHub Desktop.
Simple guide for setting up OTG modes on the Raspberry Pi Zero

Raspberry Pi Zero OTG Mode

Simple guide for setting up OTG modes on the Raspberry Pi Zero - By Andrew Mulholland (gbaman).

The Raspberry Pi Zero (and model A and A+) support USB On The Go, given the processor is connected directly to the USB port, unlike on the B, B+ or Pi 2 B, which goes via a USB hub.
Because of this, if setup to, the Pi can act as a USB slave instead, providing virtual serial (a terminal), virtual ethernet, virtual mass storage device (pendrive) or even other virtual devices like HID, MIDI, or act as a virtual webcam!
It is important to note that, although the model A and A+ can support being a USB slave, they are missing the ID pin (is tied to ground internally) so are unable to dynamically switch between USB master/slave mode. As such, they default to USB master mode. There is no easy way to change this right now.
It is also important to note, that a USB to UART serial adapter is not needed for any of these guides, as may be documented elsewhere across the internet.

##Which process should I choose?
There are 2 routes you can take for setting up the Raspberry Pi Zero as a USB Slave (OTG mode).

  1. The quick route - The quick route doesn't require anything beside your Pi Zero, SD card and a Windows, Mac or Linux computer. It though only supports setting up the Pi Zero as a virtual Ethernet device (allowing full SSH, SFTP, VNC etc). For any of the other USB Gadget drivers (beside g_ether), use route 2.
  2. The modular slower, but more flexible route - This route requires a screen and keyboard to configure your Pi Zero once it has completed its first boot.

###1. Very quick way (No USB keyboard, mouse, HDMI monitor needed)
The newer method has now been brought out into a separate Gist, which can be found here.

###2. Modular, but slower to setup method For this method, a Pi Zero, SD card (with Raspbian Jessie lite or full), screen and keyboard are required.
You are able to set up any of the below modules using this method and are not just limited to g_ether. The required kernels are also now shipped with Raspbian 2016-05-10 releases and beyond. So no need to do a raspi-update.
No web connectivity is required, nor is a USB-UART adapter required for this method. This documentation is based off the initial excellent work done on this Github pull request.
Modules included

  • Serial (g_serial)
  • Ethernet (g_ether)
  • Mass storage (g_mass_storage)
  • MIDI (g_midi)
  • Audio (g_audio)
  • Keyboard/Mouse (g_hid)
  • Mass storage and Serial (g_acm_ms)
  • Ethernet and Serial (g_cdc)
  • Multi (g_multi) - Allows you to configure 2 from Ethernet, Mass storage and Serial
    In addition to the above modules, a few other (less useful) modules are included.
  • Webcam (g_webcam)
  • Printer (g_printer)
  • Gadget tester (g_zero)
  1. First, flash Jessie (only tested on full, lite version may also work though) onto a blank microSD card.
  2. (step only needed if running Raspbain version before 2016-05-10) Once it starts up again, run sudo BRANCH=next rpi-update. This will take a while.
  3. Next we need to make sure we are using the dwc2 USB driver echo "dtoverlay=dwc2" | sudo tee -a /boot/config.txt.
  4. And enable it in Raspbian echo "dwc2" | sudo tee -a /etc/modules
  5. Need to now pick which module you want to use from the list above, for example for ethernet echo "g_ether" | sudo tee -a /etc/modules. You can only pick one of the above modules to use at a time.

Using the modules

  • g_serial - To use the standard serial module, you need to tell the Pi to forward the serial console to it with sudo systemctl enable getty@ttyGS0.service, then you can connect to the device via Putty or Screen.
  • g_ether - Using virtual ethernet, you should simply be able to ssh into the address of your Raspberry Pi. To do this, there is a little extra configuration required though. There is a few ways we could set up the point to point networking. The proper way would be to set up a DHCP server on one of the ends. A far simplier was though is just to give the Raspberry Pi a fixed IP address. To do this, you will need to run echo -e "interface usb0 \nstatic ip_address=169.254.64.64" | sudo tee -a /etc/dhcpcd.conf. You can then access the Raspberry Pi Zero by connecting to 169.254.64.64, or by using raspberrypi.local if your computer has Bonjour installed (Mac and most Linux OSs including Raspbian). Note this method does not support adding a fixed address to the cmdline.txt file. For that, you have to use the Ethernet only kernel below.
  • g_mass_storage - To have your Pi Zero appear as a mass storage device (flash drive), first create a mini filesystem in a file on your Pi with sudo dd if=/dev/zero of=/piusb.bin bs=512 count=2880 and set it up as a fat32 filesystem with sudo mkdosfs /piusb.bin. Then, when enabling it, add file=/piusb.bin stall=0 onto the end, for example sudo modprobe g_mass_storage file=/piusb.bin stall=0.

In theory, most USB devices should work alongside these kernels, to switch to USB OTG mode, simply don't use an OTG adapter cable and use a standard USB cable to plug your Pi Zero into another computer, it should auto switch.

Legacy guides

The legacy guides can be found on a separate Gist.

@jasonmnemonic
Copy link

Hi. Good guide! Just wondering if after one has setup a USB Mass Storage gadget, is there a way to point to a different piusb.bin file without restarting the Pi?
I mean what I have in mind is on Windows, when it swaps to a different file, the Device Manager momentarily loses the storage and then reappears. Is there a way to do this?
Thank you!

sudo rmmod g_mass_storage
sudo modprobe g_mass_storage file=/otherpiusb.bin stall=0

I don't think there is a "nice" way to do this though, it's like pulling out a usb stick without ejecting it first.

Thanks Curtman. BTW, before more post, I didn't want to spam the forum unnecessarily but I did say thanks in the previous post hence I did not make a post after yours. However, I have a slight other issue which I think you may guide me. I just have to mention that I am new to Pi and not a Linux guru but if you could point me to how I may resolve my problems, I could investigate further and perhaps contribute to others in solving this similar problem as mine.

I have been looking to setup a Pi as a webcam to a Windows PC. So I have a RPI0W with a Pi Camera and I have been using uvc-gadget code for this setup. I am using ConfigFS to configure OTG. So as a standalone webcam, it works and streams to PC. A multi-gadget device with webcam and USB storage works. An ethernet gadget works. However, when it is a multi-gadget webcam and ethernet device, Windows Device Manager shows no errors. I can ping the Pi on the ethernet but WIndows Camera App cannot stream. It shows the 0xa00f4271 (0x80070018). My Pi logs shows something as follows:

configfs-gadget gadget: uvc_function_bind
dwc2 20980000.usb: bound driver configfs-gadget
IPv6: ADDRCONF(NETDEV_UP): usb0: link is not ready
dwc2 20980000.usb: new device is high-speed
dwc2 20980000.usb: new device is high-speed
dwc2 20980000.usb: new address 1
configfs-gadget gadget: high-speed config #1: c
configfs-gadget gadget: uvc_function_set_alt(2, 0)
configfs-gadget gadget: reset UVC Control
configfs-gadget gadget: uvc_function_set_alt(3, 0)
IPv6: ADDRCONF(NETDEV_CHANGE): usb0: link becomes ready
configfs-gadget gadget: uvc_function_set_alt(3, 0)
configfs-gadget gadget: uvc_function_set_alt(3, 0)
dwc2 20980000.usb: dwc2_hsotg_ep_sethalt(ep ceadd390 ep0, 1)
dwc2 20980000.usb: dwc2_hsotg_ep_sethalt(ep ceadd390 ep0, 1)
random: crng init done
dwc2 20980000.usb: dwc2_hsotg_ep_sethalt(ep ceadd390 ep0, 1)
dwc2 20980000.usb: dwc2_hsotg_ep_sethalt(ep ceadd390 ep0, 1)
dwc2 20980000.usb: dwc2_hsotg_ep_sethalt(ep ceadd390 ep0, 1)
dwc2 20980000.usb: dwc2_hsotg_ep_sethalt(ep ceadd390 ep0, 1)
Bluetooth: Core ver 2.22

I think the problem is dwc2_hsotg_ep_sethalt but I do not know what to do next.

I have been trying with the latest Jessie, Buster and first official Stretch and I have the same issue.

Any ideas, help please? Like before, thank you very much, kind sir! :-)

@Curtman
Copy link

Curtman commented Dec 24, 2020

Sorry I did not see your reply sooner. To be honest I completely gave up on the Raspberry Pi for these sort of things. It seems like the rest of the world uses DWC3, but the DWC2 driver is where raspi development is at for some reason and we're stuck with it. I had exactly these sorts of problems with the pi, and I switched to using the RockPi, which has the extremely wonderful benefit of supporting USB3 OTG and is very fast. I spent months trying to debug my project and found the same code runs flawlessly on the RockPi with none of these weird errors.

@GregoriusT
Copy link

So I got g_multi mode to "work" on my Pi Zero W 1.1 to the point of being able to read the contents of the piusb.bin from my Kubuntu Computer, but I cannot write to it even with "ro=0", so something is broken there.

And I also cannot SSH into the Pi whenever Mass Storage mode is active (even if the Storage is not mounted to the Host Computer), it will just disconnect and reconnect the Ethernet Module all the time.

I would like to know how I can make it possible to write to the thing, and how I can make it so that the Pi lets me connect to it over SSH again, while Mass Storage Mode is active (since the Ethernet over USB Connection fails, not a fault of SSH itself obviously).


Before all these weird Issues popped up, this was originally supposed to be a nice fun project of making a USB Stick, that can boot a Linux Image from all types of older x86 32 bit Computers up to x86 64 bit Computers from nowadays by just switching some value in a script somewhere (or adding a switch to the GPIO, I don't know yet).
Because you still need one USB Stick per Architecture to make it work, and I wanted to proof of concept this with a single Pi Zero for fun.

@Raspberryy
Copy link

Hi, everyone :)
When I was looking for a solution to emulate a USB printer, I came across the post here by gwshaw. As of today I can not find any solution on how to use g_printer together with the raspberry pi zero which is why I worked on that the last couple of weeks.

I am really happy to now share my solution. Basically, to use the g_printer you have to use the module parameters vendorID, productID and iPNPstring. Either take these from your own printer or find them here. I suggest to only add dtoverlay=dwc2,dr_mode=peripheral to the end of config.txt. You will load the g_printer module after the boot later.

Craig W. Nadler posted a good example codeon how to use the /dev/g_printer device descriptor.

I created a repository for a little more detailed explanation if you want to learn more about what exactly you will need to do to use the Raspberry Pi Zero as a printer.

@adam-burns
Copy link

So I got g_multi mode to "work" on my Pi Zero W 1.1 to the point of being able to read the contents of the piusb.bin from my Kubuntu Computer, but I cannot write to it even with "ro=0", so something is broken there.

Perhaps the issue is file permissions? I found this Kernel Gadget Testing document which states if the file cannot be opened read/write, it'll be set to read only.

@adam-burns
Copy link

And I also cannot SSH into the Pi whenever Mass Storage mode is active (even if the Storage is not mounted to the Host Computer), it will just disconnect and reconnect the Ethernet Module all the time.

On a RPi0W running 5.10.17+ RPi Foundation kernel, I'm also experiencing this with g_multi. With various different configuration attempts of placing modules in /boot/cmdline.txt and/or /etc/modules - I can get either g_mass_storage working or g_ether

I also note that in 5.10.17+ these g_ modules are now in /lib/modules/5.10.17+/kernel/drivers/usb/gadget/legacy with other modules also in /lib/modules/5.10.17+/kernel/drivers/usb/gadget/functions - perhaps there is a new approach to this?

Almost all references I have found to USB gadget setup on RPi are some years old now, and on current kernel, I cannot seem to get g_multi working from any of the suggested configs.

Has anyone got a working config going on an up-to-date RPiOS system?

Thanks in advance for any help or suggestion!

@adam-burns
Copy link

adam-burns commented Apr 6, 2021

Almost all references I have found to USB gadget setup on RPi are some years old now, and on current kernel, I cannot seem to get g_multi working from any of the suggested configs.

To answer my own question, see USB Gadgets and COMPOSITE USB GADGETS ON THE RASPBERRY PI ZERO which outlines newer method of using libcomposite & configfs.

@amrutnrp
Copy link

Update for Raspberry pi OS (2021) :-
Few observations for anyone wandering the internet.

After first boot through HDMI display, if you want to set static IP address, you may want to add
sudo ifconfig usb0 169.254.3.14/16
by sudo nano /etc/rc.local
This works reliably.

@notarealnumber
Copy link

If I attach a ethernet hat to the Zero will I still have the OTG functionality? I am asking because I saw a HAT from Waveshare where to OTG USB port is blocked by the box. So, maybe the ethernet HAT will make use of that USB port?

Also, the Raspberry Pi Compute Module 4 seems to have OTG but can it act as a USB slave? In the manual it says:

2.4. USB 2.0 (Highspeed)
The USB 2.0 interface supports up to 480MBps signalling. The differential pair should be routed as a 90Ω differential pair.
The P N signals should ideally be matched to 0.15mm
 TIP
The USB interface is disabled to save power by default on the CM4 . To enable it you need to add
dtoverlay=dwc2,dr_mode=host to the config.txt file
 NOTE
The port is capable of being used as a true USB On-The-Go (OTG) port. While there is no official documentation, some
users have had success making this work. The USB_OTG pin is used to select between USB host and device that is
typically wired to the ID pin of a Micro usb connector. To use this functionality it must be enabled in the OS that is
used. If using either as a fixed slave or fixed master, please tie the USB OTGID pin to ground

Should work if I understand it correctly?!

@drj-io
Copy link

drj-io commented May 2, 2022

Good stuff, works great on RPI and plugged into a Mac. For g_audio, the devices show up as the following on the mac:

  • Capture Inactive
  • Playback Inactive

Is there a way to add a custom name the device?

Otherwise working well.

@HannesMrg
Copy link

Hi. Good guide! Just wondering if after one has setup a USB Mass Storage gadget, is there a way to point to a different piusb.bin file without restarting the Pi?
I mean what I have in mind is on Windows, when it swaps to a different file, the Device Manager momentarily loses the storage and then reappears. Is there a way to do this?
Thank you!

sudo rmmod g_mass_storage sudo modprobe g_mass_storage file=/otherpiusb.bin stall=0

I don't think there is a "nice" way to do this though, it's like pulling out a usb stick without ejecting it first.

Do you maybe have any experience on how simultaneous access is handled?
I Use the Pi as a mass storage for windows with 'g_mass_storage ' and have the Partition simultaneously mounted inside the Pi, what happens when Pi and Windows both want to write or read at the same time?

@wudier74
Copy link

Thank you so much for sharing.

@wudier74
Copy link

It has served me well.

@zacyang-dz
Copy link

is there any way to use the route one (g_ether) and the route two which is the other g_ modules at the same time? I want to have the mass storage and the ssh on one windows without other internet connections.
I've tried .. after i finished the route 1 , when I am trying to emulate the storage device , the pc didnot have any sign after i did the modprobe command even i re-plugin , sometimes it cannot be recognized after doing that
is there a way to do this?
thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment