

Building your own Raspbian-based USB gadget image
Sat, 11 Sep 2021
There are a few reasons you might want to build your own image instead of using my pre-built one. Maybe I got hit by a bus and havenāt kept the repository up to date. Maybe you are running a different SBC and canāt run Raspbian. (If you find a different SBC that works in gadget mode, [tell me]({{ site.url }}contact/)!) Maybe you just donāt trust me. These are all good reasons (except the bus).
This is how to build the image that I built. If youāre starting from a different base, hopefully I explain what Iām doing well enough to figure out how to do the equivalent.
If you use the official Pi imager, things are a lot easier. You simply name your Pi there, and set up your user and wifi accounts as part of the process of putting the image on the SD card. But when I originally wrote this, you had to do everything by hand. I'm leaving those instructions here for posterity, since they might help if you're using a non-Raspbian base, though as noted they're no longer strictly current for the current Raspbian version either.
Using the official imager
The official Raspberry Pi Imager will give you options to name your pi, create a user, and set up the wifi, all before flashing the image to the SD card. This is a lot easier! Jump down to "Wake up the Pi" after you've used the imager.
Legacy OS setup
Download an image for Raspberry Pi OS Lite. Even if you're using a Zero 2 (with the USB-C ports instead of micro-usb), use the legacy 32-bit version since it's Deb11. (Check to be sure it hasn't been upgraded, in which case tell me and I'll update these instructions.)
Flashing the OS
Weāre going to unpack that image onto the SD card, in a format that the Pi will run off of. Put the SD card in the reader and open Etcher or whatever imager you're using. They all work roughly the same way: pick a device, pick what you want to put on it, push the button. If you have other USB storage devices plugged in make sure you pick the right one.
Thatāll take awhile.
Naming your Pi
Once thatās done, your computer should recognize the SD card just like a thumb drive. It will have two partitions, boot
and rootfs
. Open rootfs
, and then the etc
folder. There will be a file named hostname
and a file named hosts
. Edit those using a basic text editor ā Notepad, Leafpad, etc. hostname
is just what it says: the hostname only. Thatās your Piās name, as itāll appear on your network. You could leave it as raspberrypi
, sure, but what if you get more Piās? Give it a simple letters-and-maybe-numbers name, lowercase, something like pe800
or janome
. Save that, and go to hosts
and do the same thing ā there will be other stuff in that file, but the last line will contain raspberrypi
and thatās what you want to replace.
Creating your user
Really-legacy os installs, including my pre-built image, have default users built in. With newer ones, you will probably need to pre-create a user.
Go to the partition named bootfs
(or boot
, depending on your exact setup). Create a file named userconf.txt
and put your desired username and encrypted password in the form:
pi:$6$6h7NdduAEpg.fiM3$xUP6cvynvcaeChXVgzly.vXkvxYHyHv/ksHo3QtUhX9MWdFWHh9NftZHcRW4TkM7ghuJp8ghBZBztIZcHRGPv1
Use this one if you don't have handy access to an OpenSSL password generator; you can change it on the Pi in a minute without needing to encrypt it.
Weāre going to tell the Pi that itās okay to allow ssh connections. Create a file named ssh
. Thatās it. No extension, no contents, it just has to exist, still in bootfs
. (This isn't necessary on more modern images but it won't hurt to have it and it's less hassle than discovering you need it.)
Let it on the wifi
We're still working the partition named bootfs
. First
Next weāre going to tell the Pi the wifi password. Create a file named wpa_supplicant.conf
, also in bootfs
. Edit that file to contain this:
country=US
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
ssid="WIFI_SSID"
scan_ssid=1
psk="WIFI_PASSWORD"
key_mgmt=WPA-PSK
}
Put in your actual wifi ssid (the network name) and password, of course. Save that file, unmount the SD card, and take it out of the reader.
Wake up the Pi
Put the SD card in the slot on the end of the Pi, and connect it to power. It should light up, and in a moment or two if all goes well it will be on your wifi network. The fun part is finding it. Open your SSH client and do what you need to do to connect ā add a host in something like Termius. Unless your base OS multicasts, you have to figure out what numeric address your network assigned it.
Your router/cable-modem may give you a list of everything itās assigned numbers to, in which case you just have to look for your Piās name in the list. If not, your computer should be able to tell you its own numeric address, and then itās just a matter of knocking on all the doors in that neighborhood. The address is going to be something like 192.0.0.100
or 10.0.0.115
and you just have to keep trying to connect to .101, .102, etc. as user pi
with password raspberry
until you find the right door and it lets you in.
(There are more technical ways of finding the Pi ā Iām trying to keep this simple and OS-agnostic.)
(hacker voice) āIām inā
If you've used the default password, the Pi will greet you by telling you you need to change the password. You probably do, because anybody else on your wifi can do that same door-knocking thing and log into your Pi. Unless your network is really badly configured, nobody outside the network can, but thereās malware that hangs around just looking for devices with factory passwords to build spamming and denial-of-service networks. Change it, and put that password in your password manager now.
If the Pi greets you with something like Please note that SSH may not work until a valid user has been set up.
and refuses to let you log in, power it down and put the SD card back on your computer, and doublecheck the steps under Creating your user
- the file may be in the wrong place or misnamed. If it doesn't recognize your password, re-check that the encrypted characters are all correct.
Enable the gadget-mode drivers
The drivers to run the thing in gadget mode are pre-installed but not enabled, so letās do that.
sudo nano /boot/config.txt
This will bring up an editor: use your arrow keys to navigate to the bottom and add:
# Go go gadget mode
dtoverlay=dwc2
If you have a line that reads
otg_mode=1
put a #
in front of it to disable it.
Hit Ctrl-X to exit, Y to save, enter to keep the same filename. Now do the same thing with the next file:
sudo nano /etc/modules
but this time add
# Go go gadget mode
dwc2
Carving a decoy
Now weāre going to have the Pi build a carefully-crafted file that it is going to hold up to its USB port for the embroidery machine to see as a USB drive. Type this at the command line.
sudo dd bs=1M if=/dev/zero of=/piusb.bin count=2048
When you hit enter, nothing will appear to happen (aside from the Pi LED blinking confidently). The Pi is busy making the blank for the decoy. Seriously, itās building a file of two billion zeroes, itās going to take awhile. When itās done, the prompt will come back. Now tell it to carve that blank into the shape of a USB drive.
sudo mkfs -t fat /piusb.bin
This will go quite a bit faster. Itās now got a file that looks like a 2GB FAT flash drive. Not large, but it should be compatible even with older embroidery machines. (If you want a larger flash drive for a 3d printer or TV or whatever, youāll need to go with exfat
instead; thatās a little outside the scope of this but DuckDuckGo is your friend.)
Now you need to give your imaginary USB drive an imaginary port (my analogy is getting a little stretched, bear with me.
sudo nano /etc/fstab
Thatāll bring up an editor. Go to the bottom of the file and add this line:
/piusb.bin /mnt/usb_share auto users,umask=000 0 2
Hit Ctrl-X to exit, Y to save, enter to keep the same filename. Now every time the Pi boots up, it will mount the imaginary USB drive into its imaginary port. Letās do that
sudo shutdown -r 0
The Pi will kick you out of your ssh (Termius, etc.) session. Give it a few moments to finish all its rebooting, and log back in (remember to use the new password you created. You recorded it somewhere, right?)
Testing the decoy
Now youāre ready to tell the Pi to hold that imaginary flash drive up to its data port for the embroidery machine to see.
sudo modprobe g_mass_storage file=/piusb.bin stall=0 ro=0 removable=1
At this point, if you plug the cable into the Piās data port (donāt forget the power blocker!), the embroidery machine will see a blank USB drive. If itās prone to āformattingā a newly-installed drive by putting directories on it, it will probably do that now, so let that finish before moving on. If your machine has internal storage and can save designs from it to the USB, you can give it a try. dir /mnt/usb_share
from the Pi should show you those files when itās done. When youāre done with that, take the imaginary drive away:
sudo modprobe -r g_mass_storage
The embroidery machine should react as though you unplugged a physical USB drive.
Creating a scratch directory
You don't want to write to files from wifi at the same time the embroidery machine is using them. So create a scratch directory:
mkdir /home/pi/smb_share
That's the one you'll connect to, and the watchdog program we'll build in a minute will manage shutting down the imaginary flash drive (which the embroidery machine will see as it being unplugged), copying the files over, and restarting the imaginary drive.
Connecting from a PC
If you run Linux on the desktop like I do, you can just use sshfs
to mount /home/pi/smb_share
to a local directory and skip this section. Itās much simpler to connect Linux-to-Linux that way, but Iām assuming you want to connect from a Windows machine, which requires Samba.
Back at the ssh session, type these commands:
sudo apt-get update; sudo apt-get install samba winbind -y; sudo nano /etc/samba/smb.conf
There will be a pause as the OS updates, some scrolling as the install works, maybe some prompts that you can just press Enter for, and finally youāll be editing a configuration file. Go to the bottom of the file and add:
[usb]
path = /home/pi/smb_share
browsable = yes
public = yes
available = yes
force user = pi
read only = no
create mask = 0700
directory mask = 0700
Ctrl-X, Y, enter. Then reload Samba to enact the changes:
sudo systemctl restart smbd.service
Now letās assign a Samba password:
sudo smbpasswd -a pi
It will ask for the old password (press enter), and then a new password twice. Record this one in your password manager too.
Now you can press Windows+e to open the Windows explorer, type \\PIHOSTNAME. You should see the usb_share folder, and be able to read, write, and delete files to it. Itās open to your whole network, so if you donāt want everyone in your house to be able to access it, remove the public = yes
line and use the password you created.
If you also want to mount it on a Linux machine, from that machine you just need to type:
sudo mount -t cifs -o username=pi%SAMBAPASSWORD,user=pi,uid=LOCALUSERNAME //XX.XX.XX.XXX/usb /mnt/PIHOSTNAME
(The mount point doesnāt have to be named with the Pi hostname if you have a different convention on your machine.)
Automate the sync with a watchdog
Whenever you make changes to smb_share, the imaginary USB drive needs to disconnect from the embroidery machine, copy the files over, and reconnect when youāre done. Weāre kind of bypassing some of the conflict resolution because everything assumes a USB stick can only be plugged into one machine at a time, and this will help make up for it. Itās not perfect (it can corrupt the files if the embroidery machine was writing to it when the disconnect happens), but itāll help keep things from being corrupted. Type these commands on the Pi:
sudo apt install python3-pip -y
sudo pip3 install watchdog -y
When the installations are done, type
sudo nano /usr/local/share/usb_share.py
Go to this link, copy the whole page, and paste it into the editor.
Ctrl-X, Y, enter. Now weāre going to tell the Pi to run this little watchdog program every time it boots:
sudo chmod +x /usr/local/share/usb_share.py
sudo nano /etc/systemd/system/usbshare.service
In the editor, paste:
[Unit]
Description=USB Share Watchdog
[Service]
Type=simple
ExecStart=/usr/local/share/usb_share.py
Restart=always
[Install]
WantedBy=multi-user.target
Ctrl-X, Y, enter.
sudo systemctl daemon-reload
sudo systemctl enable usbshare.service
sudo systemctl start usbshare.service
Thatās it! Your Pi is now a wireless USB stick!
You can type exit
to leave the ssh session. Donāt forget to delete the Raspbian image off your PC if you downloaded it there. It takes up a lot of space and you wonāt need it again.
Shutting down the Pi
To turn off the Pi, you want to do a clean shutdown. You could just unplug it, but that risks corrupting the SD card. Open the SSH client and log in as pi with the password you set at the very beginning. Type these commands to turn everything off.
sudo systemctl stop usbshare.service
sudo systemctl stop smbd.service
sudo modprobe -r g_mass_storage
sudo umount /mnt/usb_share
sudo shutdown -h 0
Itāll give you a quick warning and boot you out of the session (the zero is the number of seconds before shutdown, so we told it not to wait for anything).
After a second or so, you can unplug the Pi safety. Plugging it back in will turn it on, and it will restart everything and be ready to serve files again.
If you regularly turn the Pi on and off, youāll probably want to do two things. One, get a USB cable with a switch in it, so you donāt have to physically unplug it every time. Two, you can shorten those commands:
nano cleanstop
Copy and paste the above shutdown commands into the file, then ctrl-X, Y, enter. Now type
chmod u+x cleanstop
to tell the Pi thatās a file full of commands you can run. Now instead of typing all five commands, you can just say ./cleanstop
and it will shut down. You can even just type ./c
+ tab and it will autocomplete it for you to press enter on.
Rebuilding the decoy
The watchdog isn't perfect - my Janome apparently pokes at the USB drive now and again, enough to occasionally cause corruption if I forget and open the mounted directory while the machine is powered on. For this you'll want the newusb
command file.
nano newusb
Copy and paste these commands into it, ctrl-X, Y, enter.
# turn off the watchdog
sudo systemctl stop usbshare.service
# turn off the Mass Storage Gadget
sudo modprobe -r g_mass_storage
# unmount the virtual USB
sudo umount /mnt/usb_share
# reformat it
sudo mkfs -t fat /piusb.bin
# remount it
sudo mount -a
# copy the backup directory into it
cp -r /home/pi/smb_share/* /mnt/usb_share/
# sync (obvs)
sudo sync
# turn on the mass storage gadget
sudo modprobe g_mass_storage file=/piusb.bin stall=0 ro=0 removable=1
# turn on the watchdog
sudo systemctl start usbshare.service
The "#" lines are all comments telling you what it's doing. You can see it's shutting down all the services that use the drive - the watchdog, Samba, the Mass Storage Gadget bits - and then doing that mkfs
on the decoy. That will completely clear it. Then it copies the files back, and turns everything back on. Now type
chmod u+x newusb
to tell the Pi thatās a file full of commands you can run.
Now if your machine starts objecting to files, you can just say ./newusb
and it will reinitialize. You may need to re-mount the directory on your desktop machine to properly recognize it. One important bit: it only copies to the USB drive. Deleting files won't get them off the USB drive, so you'll want to do a newusb
now and then. (I'm working on a newer version that will let you sync either direction, and eliminate the need for ssh
to run these commands.)