Skip to content

bkahlert/pihero

Repository files navigation

Pi Hero License Buy Me A Unicorn

Pi Hero Banner

About

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.

Features

Easy configuration

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: # ...

Discoverable

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.

network browser foo.local and bar.local in network browser network info foo foo.local info network info bar bar.local info

Accessible

File sharing

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 password

samba login samba login samba shares samba shares samba home share samba home share samba rootfs share samba rootfs share

Bluetooth PAN

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 discoverable Raspberry Pi
connected with Mac via Bluetooth PAN connected with Mac via Bluetooth PAN
ping with iPhone via Bluetooth PAN ping with iPhone via Bluetooth samba share via Bluetooth PAN 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

Ethernet over USB

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.

network devices USB network devices network device rpi 0 bar.local USB network device network device rpi 0 details 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.

Serial port

Additionally, a serial port over USB is set up.

serial device Serial device on the host serial login Login using serial connection serial logged in Logged-in using serial connection serial ping to host 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 with Ctrl+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 with Meta+Z X)

Maintainable

The pihero tool provides diagnostics, that help you resolve problems.

Simply type pihero diag to get a report on possible configuration problems.

Pi Hero commands Available Pi Hero commands Pi Hero succeeded diagnostics Succeeded diagnostics Pi Hero failed diagnostics Failed diagnostics

Extensible

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
mass storage drive info Drive info mass storage drive details Drive details
  • You can add any supported gadget function, e.g. hid, or mass_storage (see above) to the usb_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.

Installation

Preparations

  • Install Ansible on your computer.
  • Checkout this repository:
    git clone https://github.com/bkahlert/pihero.git
    cd pihero

Configuration

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

Model AirPort4 AirPort5 AirPort6 Macmini8,1 Macmini9,1 MacPro6,1 MacPro5,1 MacPro7,1
@ECOLOR=
225,225,223
MacPro7,1
@ECOLOR=
226,226,224
Xserve3,1
Kind Mac AirPort Extreme Time Capsule Mac Mac Mac Mac Mac Mac Mac
Icon com.apple.airport-express.png com.apple.airport-extreme.png com.apple.time-capsule.png com.apple.macmini-2018.png com.apple.macmini-2020.png com.apple.macpro-cylinder.png com.apple.macpro.png com.apple.macpro-2019.png com.apple.macpro-2019-rackmount.png com.apple.xserve.png
Sidebar com.apple.airport-express-sidebar.png com.apple.airport-extreme-sidebar.png com.apple.time-capsule-sidebar.png com.apple.macmini-2018-sidebar.png com.apple.macmini-2020-sidebar.png com.apple.macpro-cylinder-sidebar.png com.apple.macpro-sidebar.png com.apple.macpro-2019-sidebar.png com.apple.macpro-2019-rackmount-sidebar.png com.apple.xserve-sidebar.png

Raspberry Pi preparation

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

Ansible

  • 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 your ansible-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.

device info foo foo.local device information device info bar bar.local device information

Usage

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

Troubleshooting

You can run a diagnosis script with:

# all diagnostics
pihero diag

# or

# only USB gadget diagnostics
pihero gadget diag

usb 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

usb gadget log

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.

Contributing

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! 🙏

License

MIT. See LICENSE for more details.

About

Ansible-based tool to make your Raspberry Pi discoverable, accessible, and fun to use

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

Packages

No packages published