Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

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: about 1 month ago
JSON representation

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

Awesome Lists containing this project

README

        

# Pi Hero [![License](https://img.shields.io/github/license/bkahlert/pihero?color=29ABE2&label=License&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1OTAgNTkwIiAgeG1sbnM6dj0iaHR0cHM6Ly92ZWN0YS5pby9uYW5vIj48cGF0aCBkPSJNMzI4LjcgMzk1LjhjNDAuMy0xNSA2MS40LTQzLjggNjEuNC05My40UzM0OC4zIDIwOSAyOTYgMjA4LjljLTU1LjEtLjEtOTYuOCA0My42LTk2LjEgOTMuNXMyNC40IDgzIDYyLjQgOTQuOUwxOTUgNTYzQzEwNC44IDUzOS43IDEzLjIgNDMzLjMgMTMuMiAzMDIuNCAxMy4yIDE0Ny4zIDEzNy44IDIxLjUgMjk0IDIxLjVzMjgyLjggMTI1LjcgMjgyLjggMjgwLjhjMCAxMzMtOTAuOCAyMzcuOS0xODIuOSAyNjEuMWwtNjUuMi0xNjcuNnoiIGZpbGw9IiNmZmYiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxOS4yMTIiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L3N2Zz4%3D)](https://github.com/bkahlert/pihero/blob/master/LICENSE) [![Buy Me A Unicorn](https://img.shields.io/static/v1?label=&message=Buy%20Me%20A%20Unicorn&color=c21f73&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3MiA3MiI%2BPHBhdGggZmlsbD0iI0ZGRiIgZD0iTTIzLjc1NCAxMi4zNjJsMS42NjcgNy4xNjctNS4zMzMgNS4zMzMtOC4zMzQgMTQuMzMzIDEgNC42NjcgMi4xNjcgMS4zMzMgNC0uMTY3IDMuNS0zLjMzMyA2LjgzMy0xLjgzM3MxLjMzNCAxLjUgMi4xNjcgMyAzLjY2NyA0LjE2NyAzLjY2NyA0LjE2N2wuNSA2LTEuODMzIDYuMTY2LTIgMi44MzNzMjIgOS41IDMzLjE2Ni03bC0uNS02LTEuODMzLTUtMy4zMzMtNS4xNjYtMS0xLjUtLjE2Ny01LjE2Ny0yLjgzMy01LjMzMy01LTMtMi42NjctNC41LTUuMTY3LTQuMTY3LTYuNS0xLjUtNS42NjYgMS00LjE2Ny0yLjE2Ny0yLjMzNC0uMTY2eiIvPjxwYXRoIGZpbGw9IiNFQTVBNDciIGQ9Ik01MC42NzEgMjMuMTU1bDUuMjA4IDQuMDk1czUuNTY0IDguMjE4LS4zMjUgMTcuODJjLTcuMDUgMTEuNDkyIDAgMCAwIDAtMS42MTkgMy40NzUtMi4zMTUgNi43NDItMS43MzkgOS43MjJsLTUuMzEtNC40MTdWMzQuMjkybDIuMTY2LTExLjEzN3pNMjUuODk4IDE5LjI3MWwtMTUuMTEzLTcuMjUgNS4xNjYgNi4xMTkgNS4yMjQgNS44NTUgNC43MjMtNC43MjQiLz48cGF0aCBmaWxsPSIjOTJEM0Y1IiBkPSJNMjkuNzM3IDEzLjYzMWwxMC43NjcuMTM2czkuMjM4IDQuMDY2IDEwLjUzNiAxMS44MTZsLjY4NyA4Ljk1N2MtMi42MzMgNi41MzktMy4wNTYgMTQuMTI3IDIuMDg5IDIwLjgzNCAwIDAtNy4xNDUgMS4zMjEtOS44OTUtNy4xMUw0Mi4zMzggNDMuNWwuMzI1LTYuMDM0IDEuNDE3LTUuNjQzLS4yODMtNC44OTMtMi4yNzYtNC4zMTItMy41MzItMi44NDEtNS43OTItMi4wOC0yLjQ2LTQuMDY2Ii8%2BPHBhdGggZmlsbD0iIzYxQjJFNCIgZD0iTTU4LjQ1NSAzNi43NXM1LjUyIDYuNDA3IDYuOTk4IDE1LjEyYTguMDIgOC4wMiAwIDAxLS4xMzggMy4yNThjLS40MzEgMS43NTItLjgxNyA0Ljk5OC4xNDYgNy4zODMuNDY5IDEuMTYxLS41NjIgMi4zNjUtMS43ODkgMi4xMTEtMy43MS0uNzY4LTkuMjQzLTMuNjQ3LTEwLjI1Ni04LjA4N2ExLjgyNiAxLjgyNiAwIDAxLS4wNDItLjMyMWwtLjI2Ni01Ljc0NmMtLjAxMy0uMjg2LjA1Mi0uNTcuMTg3LS44MjFsMy42OTItNi44MzZjLjA2Ny0uMTIzLjExNy0uMjU1LjE0OS0uMzkybDEuMzE5LTUuNjY5Ii8%2BPGc%2BPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIiBkPSJNNTguNDU1IDM3Ljc4M0M2MC4yMjMgNDAuMTQ0IDY1IDQ0LjQ2NSA2NC41IDU0LjAyTTMyLjUgNDEuODg1czguNDc4IDYuNzgzIDAgMTguNzY1TTI0LjgwOSAxOS4xMzRMMTAuMjUgMTEuNzVsMTAuOTI1IDEyLjI0NSIvPjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIgZD0iTTM1LjE5NiAzMC44N2MuNTUgOC4zNTUtOS4zMjIgOS43MDMtMTEuOTU0IDEwLjMzNC0uMzMyLjA4LS42MzIuMjUtLjg3My40OTJsLTIuMjIzIDIuMjIzYy0uMzUuMzUtLjgyNC41NDYtMS4zMTguNTQ2aC0zLjUxMmEyLjc5NSAyLjc5NSAwIDAxLTIuNjUxLTEuOTExbC0uNTMxLTEuNTkzYTIuNzk1IDIuNzk1IDAgMDEuMjU1LTIuMzIybDguNzg2LTE0LjY0NCA0LjcyNC00LjcyNC0yLjExNi02LjkwNXM3LjgwMy0uNjk5IDguNDE0IDUuMzNjMCAwIDE2LjkyOCAyLjQ0MiAxMC41NTMgMTkuMzg0IDAgMC0xLjYyNSA1Ljk0OSAyLjM3NSAxMS4xODRNMzAuOTE3IDE0LjAyUzUzLjE2IDEwIDUwLjg3NSAzMy45OCIvPjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIgZD0iTTQ5LjkxOSAyMy4xNTVzMTQuNzY2IDYuNTg3IDUuNDU2IDIyLjIyYzAgMC01LjM3NSA2LjU2My42MjUgMTMuNjA0Ii8%2BPGNpcmNsZSBjeD0iMjQuNDE3IiBjeT0iMjguOTMiIHI9IjIiLz48L2c%2BPC9zdmc%2B)](https://www.buymeacoffee.com/bkahlert)

![Pi Hero Banner](assets%2Fpihero-banner.svg)

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

| [![network browser](docs%2Fnetwork-browser.png) foo.local and bar.local in network browser](./docs/network-browser.png) | [![network info foo](docs%2Fnetwork-info-foo.png) foo.local info](./docs/network-info-foo.png) | [![network info bar](docs%2Fnetwork-info-bar.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`
> [![samba password](docs%2Fsamba-password.png)](./docs/samba-password.png)

| [![samba login](docs%2Fsamba-login.png) samba login](./docs/samba-login.png) | [![samba shares](docs%2Fsamba-shares.png) samba shares](./docs/samba-shares.png) | [![samba home share](docs%2Fsamba-home-share.png) samba home share](./docs/samba-home-share.png) | [![samba rootfs share](docs%2Fsamba-rootfs-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%2Fbt-pan-discoverable.png) discoverable Raspberry Pi
](./docs/bt-pan-discoverable.png) | [![connected with Mac via Bluetooth PAN](docs%2Fbt-pan-connected.png) connected with Mac via Bluetooth PAN
](./docs/bt-pan-connected.png) | [![ping with iPhone via Bluetooth PAN](docs%2Fbt-pan-ping.png) ping with iPhone via Bluetooth](./docs/bt-pan-ping.png) | [![samba share via Bluetooth PAN](docs%2Fbt-pan-share.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.

| [![network devices](docs%2Fnetwork-devices.png) USB network devices](./docs/network-devices.png) | [![network device rpi 0](docs%2Fnetwork-device-rpi0.png) bar.local USB network device](./docs/network-device-rpi-0.png) | [![network device rpi 0 details](docs%2Fnetwork-device-rpi0-details.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](docs%2Fserial-device.png) Serial device on the host](./docs/serial-device.png) | [![serial login](docs%2Fserial-login.png) Login using serial connection](./docs/serial-login.png) | [![serial logged in](docs%2Fserial-logged-in.png) Logged-in using serial connection](./docs/serial-logged-in.png) | [![serial ping to host](docs%2Fserial-ping-to-host.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.

| [![Pi Hero commands](docs%2Fpihero-commands.png) Available Pi Hero commands](./docs/pihero-commands.png) | [![Pi Hero succeeded diagnostics](docs%2Fpihero-diag-succeeded.png) Succeeded diagnostics](./docs/pihero-diag-succeeded.png) | [![Pi Hero failed diagnostics](docs%2Fpihero-diag-failed.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
```

| [![mass storage drive info](docs%2Fmass_storage-drive-info.png) Drive info](./docs/mass-storage-drive-info.png) | [![mass storage drive details](docs%2Fmass_storage-drive-details.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 | ![com.apple.airport-express.png](docs%2Fmodels%2Fcom.apple.airport-express.png) | ![com.apple.airport-extreme.png](docs%2Fmodels%2Fcom.apple.airport-extreme.png) | ![com.apple.time-capsule.png](docs%2Fmodels%2Fcom.apple.time-capsule.png) | ![com.apple.macmini-2018.png](docs%2Fmodels%2Fcom.apple.macmini-2018.png) | ![com.apple.macmini-2020.png](docs%2Fmodels%2Fcom.apple.macmini-2020.png) | ![com.apple.macpro-cylinder.png](docs%2Fmodels%2Fcom.apple.macpro-cylinder.png) | ![com.apple.macpro.png](docs%2Fmodels%2Fcom.apple.macpro.png) | ![com.apple.macpro-2019.png](docs%2Fmodels%2Fcom.apple.macpro-2019.png) | ![com.apple.macpro-2019-rackmount.png](docs%2Fmodels%2Fcom.apple.macpro-2019-rackmount.png) | ![com.apple.xserve.png](docs%2Fmodels%2Fcom.apple.xserve.png) |
| Sidebar | ![com.apple.airport-express-sidebar.png](docs%2Fmodels%2Fcom.apple.airport-express-sidebar.png) | ![com.apple.airport-extreme-sidebar.png](docs%2Fmodels%2Fcom.apple.airport-extreme-sidebar.png) | ![com.apple.time-capsule-sidebar.png](docs%2Fmodels%2Fcom.apple.time-capsule-sidebar.png) | ![com.apple.macmini-2018-sidebar.png](docs%2Fmodels%2Fcom.apple.macmini-2018-sidebar.png) | ![com.apple.macmini-2020-sidebar.png](docs%2Fmodels%2Fcom.apple.macmini-2020-sidebar.png) | ![com.apple.macpro-cylinder-sidebar.png](docs%2Fmodels%2Fcom.apple.macpro-cylinder-sidebar.png) | ![com.apple.macpro-sidebar.png](docs%2Fmodels%2Fcom.apple.macpro-sidebar.png) | ![com.apple.macpro-2019-sidebar.png](docs%2Fmodels%2Fcom.apple.macpro-2019-sidebar.png) | ![com.apple.macpro-2019-rackmount-sidebar.png](docs%2Fmodels%2Fcom.apple.macpro-2019-rackmount-sidebar.png) | ![com.apple.xserve-sidebar.png](docs%2Fmodels%2Fcom.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](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.**

| [![device info foo](docs%2Fdevice-info-foo.png) foo.local device information](./docs/device-info-foo.png) | [![device info bar](docs%2Fdevice-info-bar.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
```

[![usb gadget diag](docs%2Fusb-gadget-diag.png)](./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
```

[![usb gadget log](docs%2Fusb-gadget-log.png)](./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.