Building your own Raspbian-based USB gadget image

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!) 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.

Download an image for Raspberry Pi OS Lite.

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 whichever imager you’ve chosen. Both work the same way: pick a device, pick what you want to put on it, push the button. With Etcher you’ll have to point it to the image you’ve downloaded, with Imager you’ll have to pick Lite. Both should find the card, though 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.

Let it on the wifi

Now go to the partition named boot. First 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.

Next we’re going to tell the Pi the wifi password. Create a file named wpa_supplicant.conf, also in boot. Edit that file to contain this:

1
2
3
4
5
6
7
8
9
10
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”

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.

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.

1
sudo nano /boot/config.txt

This will bring up an editor: use your arrow keys to navigate to the bottom and add:

1
2
# Go go gadget mode
dtoverlay=dwc2

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

1
2
3
# 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.

1
sudo dd bs=1M if=/dev/zero of=/piusb.bin count=2048

When you hit enter, nothing will appear to happen. 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.

1
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.

1
sudo nano /etc/fstab

That’ll bring up an editor. Go to the bottom of the file and add this line:

1
/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

1
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.

1
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:

1
sudo modprobe -r g_mass_storage

The embroidery machine should react as though you unplugged a physical USB drive.

Connecting from a PC

If you run Linux on the desktop like I do, you can just use sshfs to mount /mnt/usb_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 three commands:

1
2
3
sudo apt-get update
sudo apt-get install samba winbind -y
sudo nano /etc/samba/smb.conf

There will be a little pause and some scrolling as the install works, some prompts that you can just press Enter for, and then you’ll be editing a configuration file. Go to the bottom of the file and add:

1
2
3
4
5
6
7
8
9
[usb]
   path = /mnt/usb_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:

1
sudo systemctl restart smbd.service

Now let’s assign a Samba password:

1
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:

1
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 reconnect

The imaginary USB drive needs to disconnect from the embroidery machine while you’re writing it, 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, but it’ll help keep things from being corrupted. Type these commands on the Pi:

1
2
sudo apt install python3-pip
sudo pip3 install watchdog

When the installations are done, type

1
sudo nano /usr/local/share/usb_share.py

Go to this link, copy the whole page, and paste it into the editor.

We need to make a few little changes to it,

Right at the top, we want to add a sudo in front of each of the commands:

1
2
3
CMD_MOUNT = "sudo modprobe g_mass_storage file=/piusb.bin stall=0 ro=0 removable=1"
CMD_UNMOUNT = "sudo modprobe -r g_mass_storage"
CMD_SYNC = "sudo sync"

Ctrl-X, Y, enter. Now we’re going to tell the Pi to run that little watchdog program every time it boots:

1
2
sudo chmod +x /usr/local/share/usb_share.py
sudo nano /etc/systemd/system/usbshare.service

In the editor, paste:

1
2
3
4
5
6
7
8
9
10
[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.

1
2
3
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.

1
2
3
4
5
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:

1
nano cleanstop

Copy and paste the above shutdown commands into the file, then ctrl-X, Y, enter. Now type

1
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.

1
nano newusb

Copy and paste these commands into it, ctrl-X, Y, enter.

1
2
3
4
5
6
sudo systemctl stop usbshare.service
sudo systemctl stop smbd.service
sudo modprobe -r g_mass_storage
sudo umount /mnt/usb_share
sudo mkfs -t fat /piusb.bin
sudo shutdown -r 0

If you look closely, you’ll 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. And then it’s doing a slightly different shutdown, this one with -r for restart.

1
chmod u+x newusb

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, and it will be completely empty. REMEMBER: never put your only copy of anything on a USB drive, real or virtual.