Pi Hero is an Ansible-based tool to make your Raspberry Pi discoverable, accessible, and fun to use.
Pi Hero makes your Raspberry Pi:
- show a splash screen during boot and shutdown,
- show up in your network with a nice icon,
- share device information,
- create an Ethernet over USB connection,
- configure Windows-compatible Samba shares,
- accept serial connection, and
- allow for custom features like behaving like a keyboard, mouse, mass storage, etc.
- create a Bluetooth PAN
Supported are even old models like the Raspberry Pi Zero with no network accessibility at all.
A sample inventory for sample devices, namely foo.local
and bar.local
is provided to get you started:
# Device "foo" with the following features:
# - Bluetooth PAN with custom CIDR and one (pre-)trusted device
# - Ethernet over USB
foo.local:
bt_pan:
cidr: 10.11.10.10/29
devices: { mac: 00:11:22:33:44:55, pin: '*', trusted: true }
usb_gadget:
ethernet:
# Device "bar" with custom model and the following features:
# - Ethernet over USB with custom CIDR
# - USB Serial Port
# - custom USB Mass Storage
bar.local:
device_info: { model: MacPro7,1@ECOLOR=226,226,224 }
usb_gadget:
ethernet: { cidr: 10.10.20.20/29 }
serial:
mass_storage: # ...
Avahi is installed to make your Raspberry Pi discoverable in your network. By default, your Raspberry Pi shows up as a 4th-generation AirPort device. The samba shares, SSH, and SFTP services are advertised as well.
foo.local and bar.local in network browser | foo.local info | bar.local info |
---|
Samba shares are configured for home directories and the root directory /
.
ℹ️ Don't forget to set a Samba password using
sudo smbpasswd -a $USER
samba login | samba shares | samba home share | samba rootfs share |
---|
If your device has Bluetooth, a Personal Area Network PAN
—more specifically a Network Access Point NAP
—can be created.
It lets your configured devices connect to your Raspberry Pi via Bluetooth.
discoverable Raspberry Pi |
connected with Mac via Bluetooth PAN |
ping with iPhone via Bluetooth | samba share accessed with iPhone via Bluetooth |
---|
Devices are identified by their MAC address. To find out your device's MAC address:
- On macOS, run
system_profiler SPBluetoothDataType | grep "Address:" | head -n 1
- On an iPhone, go to
Settings
>General
>About
>Bluetooth
- On Windows, run
ipconfig /all | findstr "Bluetooth"
- On Linux, run
bt-adapter -i | grep "Address:" | head -n 1
If your device has no Wi-Fi or Ethernet port, or you use them for a different purpose, just connect via USB. An Ethernet over USB network interface is automatically created and configured.
USB network devices | bar.local USB network device | bar.local network device details |
---|
By default, your Raspberry Pi uses the IP address 10.10.10.10
, and
configures your computer via DHCP.
The most complicated part is to use the right USB port: you'll need to use the so-called USB-OTG port. It's typically the one in the middle.
Additionally, a serial port over USB is set up.
Serial device on the host | Login using serial connection | Logged-in using serial connection | Successful ping of host |
---|
- To list all available serial ports, type
ls /dev/tty.usbmodem*
. - To connect to the first available serial port, you need a terminal emulator program such as:
screen $(ls /dev/tty.usbmodem* | head -n 1) 115200
(exit withCtrl+A
K
)cu -s 115200 -l $(ls /dev/tty.usbmodem* | head -n 1)
(exit with~.
)minicom -b 115200 -D $(ls /dev/tty.usbmodem* | head -n 1)
(exit withMeta+Z
X
)
The pihero
tool provides diagnostics, that help you resolve problems.
Simply type pihero diag
to get a report on possible configuration problems.
Available Pi Hero commands | Succeeded diagnostics | Failed diagnostics |
---|
The USB-based features are based on the Linux kernel's multifunction composite gadget, or g_multi. The gadget is configured using a shell script that supports any gadget function, you would like your Raspberry Pi to provide.
The serial port and USB over Ethernet functions are supported by default.
The following configuration shows a custom USB mass storage gadget that creates a 1 GB removable hard drive:
usb_gadget:
mass_storage: |
#!/usr/bin/env bash
# create disk image
if [ ! -f /data/hdd.img ]; then
mkdir -p /data
fallocate -l 1GB /data/hdd.img
mkfs.exfat -v -L 'RaspiDrive' -f /data/hdd.img
fi
# create mass storage gadget, see kernel.org/doc/html/latest/usb/gadget-testing.html#mass-storage-function
echo 1 >stall
echo /data/hdd.img > lun.0/file
echo 'SanDisk Cruzer Edge 1.20' > lun.0/inquiry_string
echo 1 > lun.0/removable
Drive info | Drive details |
---|
- You can add any supported gadget function, e.g.
hid
, ormass_storage
(see above) to theusb_gadget
node of your Ansible configuration. - The value needs to be a shell script (either inline, or the path to it on your device).
- The most part is already done for you.
- All you have to do is to configure the function itself.
- The necessary
functions/<function>.<instance>
directory is already created and the working directory of your script. - If your script does not exit with code
0
, the function will simply not be enabled. - Eventually occurred problems are logged, see Troubleshooting.
- Install Ansible on your computer.
- Checkout this repository:
git clone https://github.com/bkahlert/pihero.git cd pihero
- Copy the sample inventory to
inventory/berries
and adapt it to your needs:cp -r inventory/sample inventory/berries
By default, your device is advertised as an AirPort (4th generation) device.
After having tried a dozen configurations, this one turned out to be the best,
because it's recognized by most devices, is of kind Mac
but looks like a
tiny network device and not like a classical computer.
If you'd like to go with a different configuration, these are the ones I'd recommend:
- Flash the latest Raspberry Pi OS Lite image to your SD card.
- Boot your Raspberry Pi and connect it to your network.
Using Wi-Fi | Using Ethernet | Using USB |
---|---|---|
Configure your Wi-Fi upfront. The easiest way to do that is using the Raspberry Pi Imager's customization feature. |
Connect your Pi with an Ethernet cable to your DHCP enabled network. | Use a USB to Ethernet adapter and connect to your network. |
- Locate your Raspberry Pi
- Ideally you should be able to ping your device using
ping raspberry.local
, or the hostname you configured. - If that doesn't work, you can
- use netmon,
- check your Router's web interface, or
- scan your network with
nmap -sn nmap -sn 192.168.0.0/24
(adapt to your network)
- If you can't, you can use
nmap
to scan your network for devices:
- Ideally you should be able to ping your device using
-
Start the setup process using:
# Setup only the device foo.local ansible-playbook playbook.yml -l foo.local # Setup only the device foo.local declared in the given inventory, and use the specified IP address to connect ansible-playbook playbook.yml -l foo.local \ -e "ansible_host=10.10.10.99" \ -i inventory/other/hosts.yml
-
If you used the provided sample inventory with two Raspberry Pis,
the output should look like the one in sample-installation.md. -
Alternatively, you can integrate Pi Hero in your playbook with the following snippet:
- hosts: "{{ inventory if inventory is defined else 'all' }}" gather_facts: false tasks: - name: check if plymouth-themes directory exists ansible.builtin.stat: { path: "{{ playbook_dir }}/plymouth-themes/" } # ← used to store custom splash screens register: plymouth_themes_stat - name: set fact for local_plymouth_themes_dir set_fact: { local_plymouth_themes_dir: "{{ playbook_dir }}/plymouth-themes/" } when: plymouth_themes_stat.stat.isdir is defined and plymouth_themes_stat.stat.isdir tags: [ never, pihero ] - name: run Pi Hero playbook, if --tags pihero is specified ansible.builtin.import_playbook: ../../pihero/playbook.yml # ← location of Pi Hero tags: [ never, pihero ]
To run the Pi Hero playbook, add
--tags pihero
to youransible-playbook
command.
-
-
Wait for the playbook to finish and your Raspberry Pi to reboot.
- If you used a USB to Ethernet adapter, you should remove the adapter and connect to your computer directly.
- You can do so when the Raspberry Pi is about to reboot.
- If you missed this moment, just unplug the adapter and restart.
Your Raspberry Pi is now ready to use, and should show up in your network properly.
foo.local device information | bar.local device information |
---|
Log in using ssh foo.local
and you'll be greeted
with a message containing information about the configured features:
Linux gadget 6.1.21-v8+ #1642 SMP PREEMPT aarch64
─=≡▰▩▩[ 蓬•o•]⊐ Shares
smb://foo.local/pi → /home/pi
smb://foo.local/rootfs → / (read-only)
─=≡▰▩▩[ 蓬•o•]⊐ USB gadget
FEATURES: mass_storage, serial, ethernet
─=≡▰▩▩[ 蓬•o•]⊐ USB ethernet
INTERFACE: usb0 — IP 10.10.10.10 / 255.255.255.248 — DHCP: 10.10.10.11 - 10.10.10.14
HELP: pihero gadget
─=≡▰▩▩[ 蓬•o•]⊐ Bluetooth PAN
INTERFACE: usb0 — IP 10.11.10.10 / 255.255.255.248 — DHCP: 10.11.10.11 - 10.11.10.14
HELP: pihero pan
Last login from 10.10.10.12
You can run a diagnosis script with:
# all diagnostics
pihero diag
# or
# only USB gadget diagnostics
pihero gadget diag
It produces output like the following with additional hints on how to proceed
Checking if /boot/config.txt contains dtoverlay=dwc2... ✔︎
Checking if /boot/cmdline.txt contains modules-load=dwc2... ✔︎
Checking if /boot/cmdline.txt contains no line breaks... ✔︎
Checking if libcomposite module is loaded... ✔︎
Checking if usb-gadget service is active... ✔︎
# ...
Checking if usb0 interface config is not malformed... ✔︎
Checking if usb0 interface config is not malformed... ✔︎
Checking if usb0 interface exists... ✔︎
All checks passed.
Useful commands:
- check usb-gadget service: systemctl status usb-gadget.service; journalctl -b -u usb-gadget.service
- run usb-gadget yourself: sudo usb-gadget
- scan for connected hosts: command -v nmap >/dev/null 2>&1 || sudo apt-get install -yqq nmap; nmap -sn 10.10.10.11-14
- check networking: systemctl status networking
- check dnsmasq service: systemctl status dnsmasq.service
- check dnsmasq config: dnsmasq --test
- stop dnsmasq service: sudo systemctl stop dnsmasq.service
- start dnsmasq manually: dnsmasq --no-daemon --log-queries
As advised by the diagnosis script, you can also try:
journalctl -b -u usb-gadget.service
This prints something like the following, which is quite handy when debugging custom gadget functions:
systemd[1]: Starting Pi Hero USB Gadget...
usb-gadget[642]: Setting up USB gadget pihero...
usb-gadget[642]: Creating gadget pihero...
usb-gadget[642]: Creating configuration c.1...
usb-gadget[642]: Creating function ecm.usb0...
usb-gadget[642]: Associating function ecm.usb0 with configuration c.1...
usb-gadget[642]: Creating function acm.usb0...
usb-gadget[642]: Associating function acm.usb0 with configuration c.1...
usb-gadget[642]: Creating function mass_storage.usb0...
usb-gadget[642]: Delegating creation of function mass_storage.usb0 to usb-gadget-custom...
usb-gadget[664]: Creating custom function mass_storage.usb0 using inline script...
usb-gadget[664]: Invoking /tmp/tmp.mrFN5Lqm7H mass_storage usb0 in functions/mass_storage.usb0...
usb-gadget[668]: /tmp/tmp.mrFN5Lqm7H: line 12: lun.1/removable: No such file or directory
usb-gadget[664]: ERROR: Invocation terminated with exit code 1.
usb-gadget[664]: ERROR: Failed to execute script /tmp/tmp.mrFN5Lqm7H for function mass_storage.usb0.
usb-gadget[642]: ERROR: The function mass_storage.usb0 failed to create. It won't be associated with configuration c.1.
usb-gadget[642]: This is what functions/mass_storage.usb0 looked like:
usb-gadget[642]: Directory /sys/kernel/config/usb_gadget/pihero/functions/mass_storage.usb0
usb-gadget[642]: total 0
usb-gadget[642]: drwxr-xr-x 2 root root 0 Aug 6 23:40 lun.0/
usb-gadget[642]: -rw-r--r-- 1 root root 4.0K Aug 6 23:40 stall
usb-gadget[642]:
usb-gadget[642]: functions/mass_storage.usb0/lun.0:
usb-gadget[642]: total 0
usb-gadget[642]: -rw-r--r-- 1 root root 4.0K Aug 6 23:40 cdrom
usb-gadget[642]: -rw-r--r-- 1 root root 4.0K Aug 6 23:40 nofua
usb-gadget[642]: -rw-r--r-- 1 root root 4.0K Aug 6 23:40 removable
usb-gadget[642]: -rw-r--r-- 1 root root 4.0K Aug 6 23:40 ro
usb-gadget[642]: --w------- 1 root root 4.0K Aug 6 23:40 forced_eject
usb-gadget[642]: -rw-r--r-- 1 root root 4.0K Aug 6 23:40 inquiry_string
usb-gadget[642]: -rw-r--r-- 1 root root 4.0K Aug 6 23:40 file
usb-gadget[642]: Enabling gadget in /sys/kernel/config/usb_gadget/pihero... ✔︎
systemd[1]: Finished Pi Hero USB Gadget.
The relevant line here is /tmp/tmp.mrFN5Lqm7H: line 12: lun.1/removable: No such file or directory
.
When you look closely, you can see that lun.1
was used instead of lun.0
.
Want to contribute? Awesome! The most basic way to show your support is to star the project, or to raise issues. You can also support this project by making a PayPal donation to ensure this journey continues indefinitely!
Thanks again for your support, it is much appreciated! 🙏
MIT. See LICENSE for more details.