https://github.com/bkahlert/pihero
Ansible-based tool to make your Raspberry Pi discoverable, accessible, and fun to use
https://github.com/bkahlert/pihero
ansible ethernet gadget network raspberry-pi raspberrypi rpi usb
Last synced: 11 months ago
JSON representation
Ansible-based tool to make your Raspberry Pi discoverable, accessible, and fun to use
- Host: GitHub
- URL: https://github.com/bkahlert/pihero
- Owner: bkahlert
- License: mit
- Created: 2023-08-05T23:44:40.000Z (almost 3 years ago)
- Default Branch: master
- Last Pushed: 2024-03-17T17:10:42.000Z (over 2 years ago)
- Last Synced: 2024-10-17T12:27:06.835Z (over 1 year ago)
- Topics: ansible, ethernet, gadget, network, raspberry-pi, raspberrypi, rpi, usb
- Language: Shell
- Homepage:
- Size: 6.31 MB
- Stars: 3
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
README
# Pi Hero [](https://github.com/bkahlert/pihero/blob/master/LICENSE) [](https://www.buymeacoffee.com/bkahlert)

## About
**Pi Hero** is an Ansible-based tool to make your [Raspberry Pi](https://www.raspberrypi.com/)
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](https://www.raspberrypi.com/products/raspberry-pi-zero/) with no network accessibility at all.
## Features
### Easy configuration
A [sample inventory](inventory/sample/hosts.yml) for sample devices, namely `foo.local` and `bar.local` is provided to get you started:
```yaml
# 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.
| [ foo.local and bar.local in network browser](./docs/network-browser.png) | [ foo.local info](./docs/network-info-foo.png) | [ bar.local info](./docs/network-info-bar.png) |
|-------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
### 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`
> [](./docs/samba-password.png)
| [ samba login](./docs/samba-login.png) | [ samba shares](./docs/samba-shares.png) | [ samba home share](./docs/samba-home-share.png) | [ samba rootfs share](./docs/samba-rootfs-share.png) |
|------------------------------------------------------------------------------|----------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|
#### 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
](./docs/bt-pan-discoverable.png) | [ connected with Mac via Bluetooth PAN
](./docs/bt-pan-connected.png) | [ ping with iPhone via Bluetooth](./docs/bt-pan-ping.png) | [ samba share accessed with iPhone via Bluetooth](./docs/bt-pan-share.png) |
|-------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------|
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.
| [ USB network devices](./docs/network-devices.png) | [ bar.local USB network device](./docs/network-device-rpi-0.png) | [ bar.local network device details](./docs/network-device-rpi-0-details.png) |
|--------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
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 on the host](./docs/serial-device.png) | [ Login using serial connection](./docs/serial-login.png) | [ Logged-in using serial connection](./docs/serial-logged-in.png) | [ Successful ping of host](./docs/serial-ping-to-host.png) |
|--------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------|
- 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.
| [ Available Pi Hero commands](./docs/pihero-commands.png) | [ Succeeded diagnostics](./docs/pihero-diag-succeeded.png) | [ Failed diagnostics](./docs/pihero-diag-failed.png) |
|----------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------|
### Extensible
The USB-based features are based on the Linux
kernel's [multifunction composite gadget, or g_multi](https://www.kernel.org/doc/html/latest/usb/gadget_multi.html).
The gadget is configured using [a shell script](roles/usb_gadget/templates/usb-gadget.sh.j2) 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:
```yaml
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](./docs/mass-storage-drive-info.png) | [ Drive details](./docs/mass-storage-drive-details.png) |
|-----------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------|
- 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/.` 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](#troubleshooting).
## Installation
### Preparations
- Install Ansible on your computer.
- Checkout this repository:
```shell
git clone https://github.com/bkahlert/pihero.git
cd pihero
```
### Configuration
- Copy the [sample inventory](inventory/sample) to `inventory/berries` and adapt it to your needs:
```shell
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 |  |  |  |  |  |  |  |  |  |  |
| Sidebar |  |  |  |  |  |  |  |  |  |  |
### 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](https://github.com/bkahlert/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:
```shell
# 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](inventory/sample/hosts.yml) with two Raspberry Pis,
the output should look like the one in [sample-installation.md](sample-installation.md).
- Alternatively, you can integrate Pi Hero in your playbook with the following snippet:
```yaml
- 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.**
| [ foo.local device information](./docs/device-info-foo.png) | [ bar.local device information](./docs/device-info-bar.png) |
|-----------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------|
## Usage
Log in using `ssh foo.local` and you'll be greeted
with a message containing information about the configured features:
```text
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:
```shell
# all diagnostics
pihero diag
# or
# only USB gadget diagnostics
pihero gadget diag
```
[](./docs/usb-gadget-diag.png)
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:
```shell
journalctl -b -u usb-gadget.service
```
[](./docs/usb-gadget-log.png)
This prints something like the following, which is quite handy when debugging
custom gadget functions:
```text
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](https://www.paypal.me/bkahlert) to ensure this journey continues indefinitely!
Thanks again for your support, it is much appreciated! :pray:
## License
MIT. See [LICENSE](LICENSE) for more details.