diff --git a/.github/workflows/build-all.yml b/.github/workflows/build-all.yml index 42b33ea..501c967 100644 --- a/.github/workflows/build-all.yml +++ b/.github/workflows/build-all.yml @@ -41,6 +41,25 @@ jobs: compression-level: 6 custom-hostname: picompose + # 2MICHAT-v2 + build-2michat-v2: + uses: ./.github/workflows/build-image-template.yml + with: + image-name: PiCompose-2MicHat-v2 + stage-list: stage0 stage1 stage2 ./01-stage-picompose ./02-stage-audiodriver-2michat-v2 ./04-stage-finish + compression: xz + compression-level: 6 + custom-hostname: picompose + + build-2michat-v2-lva: + uses: ./.github/workflows/build-image-template.yml + with: + image-name: PiCompose_2MicHat-v2_Linux-Voice-Assistant + stage-list: stage0 stage1 stage2 ./01-stage-picompose ./02-stage-audiodriver-2michat-v2 ./03-stage-linux-voice-assistant ./04-stage-finish + compression: xz + compression-level: 6 + custom-hostname: picompose + # RESPEAKER-LITE build-respeaker_lite: uses: ./.github/workflows/build-image-template.yml @@ -62,5 +81,5 @@ jobs: # RPI IMAGER JSON generate-rpi-imager-json: - needs: [build, build-2michat, build-respeaker_lite, build-2michat-lva, build-respeaker_lite-lva] + needs: [build, build-2michat, build-2michat-v2, build-respeaker_lite, build-2michat-lva, build-2michat-v2-lva, build-respeaker_lite-lva] uses: ./.github/workflows/create-rpi-image-json.yml diff --git a/02-stage-audiodriver-2michat-v2/01-driver/01-packages b/02-stage-audiodriver-2michat-v2/01-driver/01-packages new file mode 100644 index 0000000..775d501 --- /dev/null +++ b/02-stage-audiodriver-2michat-v2/01-driver/01-packages @@ -0,0 +1,4 @@ +git +build-essential +device-tree-compiler +alsa-utils diff --git a/02-stage-audiodriver-2michat-v2/01-driver/03-run-chroot.sh b/02-stage-audiodriver-2michat-v2/01-driver/03-run-chroot.sh new file mode 100755 index 0000000..a9160c6 --- /dev/null +++ b/02-stage-audiodriver-2michat-v2/01-driver/03-run-chroot.sh @@ -0,0 +1,44 @@ +#!/bin/bash -e + +# ReSpeaker 2-Mics Pi HAT **V2.0** uses a TLV320AIC3104 codec on I2C 0x18 — +# NOT the WM8960 codec (0x1a) used on V1. No out-of-tree DKMS module is +# needed: the mainline `snd-soc-tlv320aic3x` driver already ships in the +# kernel. We just need the device-tree overlay that wires it up to the Pi's +# I2S pins. The overlay source is maintained by Seeed in their +# seeed-linux-dtoverlays repo. + +echo "Building ReSpeaker 2-Mic HAT V2.0 device-tree overlay..." + +SRC=/tmp/seeed-linux-dtoverlays +rm -rf "$SRC" +git clone --depth 1 https://github.com/Seeed-Studio/seeed-linux-dtoverlays.git "$SRC" + +cd "$SRC" +make overlays/rpi/respeaker-2mic-v2_0-overlay.dtbo + +# detect boot config + overlays location +OVERLAYS_DIR=/boot/overlays +CONFIG=/boot/config.txt +if [ -d /boot/firmware/overlays ]; then + OVERLAYS_DIR=/boot/firmware/overlays +fi +if [ -f /boot/firmware/config.txt ]; then + CONFIG=/boot/firmware/config.txt +fi +if [ -f /boot/firmware/usercfg.txt ]; then + CONFIG=/boot/firmware/usercfg.txt +fi + +install -v -m 644 overlays/rpi/respeaker-2mic-v2_0-overlay.dtbo \ + "${OVERLAYS_DIR}/respeaker-2mic-v2_0.dtbo" + +# set boot params: enable I2C and load the v2 overlay +sed -i -e 's:#dtparam=i2c_arm=on:dtparam=i2c_arm=on:g' "$CONFIG" || true +grep -q "^dtparam=i2c_arm=on$" "$CONFIG" || echo "dtparam=i2c_arm=on" >> "$CONFIG" +grep -q "^dtoverlay=respeaker-2mic-v2_0$" "$CONFIG" || \ + echo "dtoverlay=respeaker-2mic-v2_0" >> "$CONFIG" + +cd / +rm -rf "$SRC" + +echo "Done. After boot the card appears as 'seeed2micvoicec' (bcm2835-i2s-tlv320aic3x-hifi)." diff --git a/02-stage-audiodriver-2michat-v2/02-set-audio-volume/01-run.sh b/02-stage-audiodriver-2michat-v2/02-set-audio-volume/01-run.sh new file mode 100755 index 0000000..fc50a65 --- /dev/null +++ b/02-stage-audiodriver-2michat-v2/02-set-audio-volume/01-run.sh @@ -0,0 +1,10 @@ +#!/bin/bash -e + +install -v -m 755 files/configure_audio.sh "${ROOTFS_DIR}/usr/bin/configure_audio.sh" +install -v -m 644 files/configure_audio.service "${ROOTFS_DIR}/etc/systemd/system/configure_audio.service" + +on_chroot << EOF +echo "Enable audio services" +systemctl daemon-reload +systemctl enable configure_audio.service +EOF diff --git a/02-stage-audiodriver-2michat-v2/02-set-audio-volume/files/configure_audio.service b/02-stage-audiodriver-2michat-v2/02-set-audio-volume/files/configure_audio.service new file mode 100644 index 0000000..945f217 --- /dev/null +++ b/02-stage-audiodriver-2michat-v2/02-set-audio-volume/files/configure_audio.service @@ -0,0 +1,15 @@ +[Unit] +Description=PiCompose - Configure Audio volume setting (ReSpeaker 2-Mic HAT V2.0) +After=user@1000.service sound.target alsa-restore.service +Wants=user@1000.service sound.target alsa-restore.service + +[Service] +Type=oneshot +Environment=XDG_RUNTIME_DIR=/run/user/1000 +ExecStart=/usr/bin/configure_audio.sh +RemainAfterExit=yes +Restart=on-failure +RestartSec=30s + +[Install] +WantedBy=multi-user.target diff --git a/02-stage-audiodriver-2michat-v2/02-set-audio-volume/files/configure_audio.sh b/02-stage-audiodriver-2michat-v2/02-set-audio-volume/files/configure_audio.sh new file mode 100755 index 0000000..2880059 --- /dev/null +++ b/02-stage-audiodriver-2michat-v2/02-set-audio-volume/files/configure_audio.sh @@ -0,0 +1,80 @@ +#!/bin/bash +set -u + +# TLV320AIC3104 on V2.0 ships quiet on both ends: HP DAC at -23.5 dB, +# HP/Line amps at ~89% and muted on some units, and the capture PGA at 27% +# (16 dB) — too low for microWakeWord detection. PCM is driven by wpctl as +# hardware-volume passthrough, so only the downstream stages need tuning. +# Runs every boot because WirePlumber can reset ALSA state between sessions. + +wait_for_card_and_control() { + local card="$1" + local control="$2" + local max_tries=30 + local count=0 + + while [ "$count" -lt "$max_tries" ]; do + count=$((count + 1)) + + if amixer -c "$card" info >/dev/null 2>&1; then + if amixer -c "$card" scontrols | grep -Fq "'$control'"; then + echo "Card $card with control '$control' is ready ($count/$max_tries)" + return 0 + fi + echo "Card $card found, but control '$control' not ready yet ($count/$max_tries)" + else + echo "Card $card not ready yet ($count/$max_tries)" + fi + + sleep 1 + done + + echo "Card $card with control '$control' did not become ready" + return 1 +} + +# Returns 0 on set-success or missing control; 1 on set-failure. +# Non-zero exit triggers the systemd retry. +set_control_if_exists() { + local card="$1" + local control="$2" + shift 2 + + if amixer -c "$card" scontrols | grep -Fq "'$control'"; then + echo "Setting $control on $card to $*" + if amixer -c "$card" set "$control" "$@"; then + return 0 + fi + echo "amixer set failed for '$control' on $card" >&2 + return 1 + fi + + echo "Control '$control' not found on $card, skipping" + return 0 +} + +# Same kernel alias as V1 (`seeed2micvoicec`); the PCM control distinguishes +# V2's tlv320aic3x from V1's wm8960. +if ! wait_for_card_and_control seeed2micvoicec PCM; then + echo "No TLV320AIC3104-based card became ready; is the V2.0 overlay loaded?" + exit 1 +fi +CARD="seeed2micvoicec" + +FAIL=0 +set_control_if_exists "$CARD" "HP DAC" 100% || FAIL=1 +set_control_if_exists "$CARD" "Line DAC" 100% || FAIL=1 +set_control_if_exists "$CARD" "HP" 100% unmute || FAIL=1 +set_control_if_exists "$CARD" "Line" 100% unmute || FAIL=1 +set_control_if_exists "$CARD" "PGA" 80% cap || FAIL=1 + +if [ "$FAIL" -ne 0 ]; then + echo "One or more amixer set calls failed; not storing state." >&2 + exit 1 +fi + +# Keep PipeWire sink at unity so HA/LVA volume reaches the ALSA stages +# above. Matches the 2michat-v1 pattern from #42. +wpctl set-volume @DEFAULT_AUDIO_SINK@ 1.0 + +alsactl store diff --git a/02-stage-audiodriver-2michat-v2/prerun.sh b/02-stage-audiodriver-2michat-v2/prerun.sh new file mode 100755 index 0000000..9acd13c --- /dev/null +++ b/02-stage-audiodriver-2michat-v2/prerun.sh @@ -0,0 +1,5 @@ +#!/bin/bash -e + +if [ ! -d "${ROOTFS_DIR}" ]; then + copy_previous +fi diff --git a/README.md b/README.md index 58935f9..9016706 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,8 @@ Here is a Overview for the specific images of each hardware: | **[Sattelite1](docs/hardware_sattelite1.md)**
**+Linux-Voice-Assistant**
**+Snapcast** | ReSpeaker Lite Board | • Satellite1 Hat Image
• Linux-Voice-Assistant (OpenHomeFoundation)
• Snapcast MultiRoom Audio Client
• Pre-configured for Home Assistant

Image is currently work in progress! | | **[ReSpeaker 2-Mic HAT v1](docs/hardware_2mic_v1.md)** | ReSpeaker 2-Mics Pi HAT | • Base Image
• Seeed Voicecard Driver | | **[ReSpeaker 2-Mic HAT v1](docs/hardware_2mic_v1.md)**
**+Linux-Voice-Assistant**
**+Snapcast** | ReSpeaker 2-Mics Pi HAT | • 2-Mic HAT Image
• Linux-Voice-Assistant (OpenHomeFoundation)
• 2-Mic HAT GPIO LED Control
• Snapcast MultiRoom Audio Client
• Pre-configured for Home Assistant | +| **[ReSpeaker 2-Mic HAT v2.0](docs/hardware_2mic_v2.md)** | ReSpeaker 2-Mics Pi HAT | • Base Image
• TLV320AIC3104 device-tree overlay
• Per-boot codec mixer tuning for the V2.0 | +| **[ReSpeaker 2-Mic HAT v2.0](docs/hardware_2mic_v2.md)**
**+Linux-Voice-Assistant**
**+Snapcast** | ReSpeaker 2-Mics Pi HAT | • 2-Mic HAT v2.0 Image
• Linux-Voice-Assistant (OpenHomeFoundation)
• Snapcast MultiRoom Audio Client
• Pre-configured for Home Assistant | | **[ReSpeaker Lite](docs/hardware_respeaker_lite.md)** | ReSpeaker Lite Board | • Base Image
• Audio keep-alive service
• Workaround for connectivity issues in combination with the Pi Zero 2W.

There is a USB connectivity issue with the Pi Zero 2W. I cannot recommend this board if you want to use it with that. Use Pi3 or higher. | | **[ReSpeaker Lite](docs/hardware_respeaker_lite.md)**
**+Linux-Voice-Assistant**
**+Snapcast** | ReSpeaker Lite Board | • ReSpeaker Lite Image
• Linux-Voice-Assistant (OpenHomeFoundation)
• Snapcast MultiRoom Audio Client
• Pre-configured for Home Assistant
• Workaround for connectivity issues in combination with the Pi Zero 2W.

There is a USB connectivity issue with the Pi Zero 2W. If you want to use it with that, you need to use Pi3 or higher. | diff --git a/docs/hardware_2mic_v2.md b/docs/hardware_2mic_v2.md new file mode 100644 index 0000000..dbc6c0b --- /dev/null +++ b/docs/hardware_2mic_v2.md @@ -0,0 +1,41 @@ +# Hardware - ReSpeaker 2-Mics Pi HAT v2.0 + +ReSpeaker 2-Mics Pi HAT + +The V2.0 is a silent hardware revision of the 2-Mics Pi HAT. Visually identical, electrically different: the WM8960 codec used on v1 (I2C `0x1a`) is replaced with a **TLV320AIC3104** codec (I2C `0x18`). This matters because the v1 driver stage does not produce a working sound card on v2 hardware, and because the mixer defaults differ. + +Features (same as v1): +- Support the Raspberry Pi 3B / 4B / Zero 2 W +- Two microphones (Mic L and Mic R) +- Two Grove connectors +- One User-defined button +- 3.5 mm audio interface +- XH2.54-2P audio output interface + +## Order + +### Base: + +- [Raspberry Pi Zero 2 W](https://amzn.to/3M0G4hC) +- [SD-Card](https://amzn.to/4qfx06l) +- [US MicroUSB Power Supply](https://amzn.to/4c52mt3) +- [Cable for Speaker](https://amzn.to/3ZvU0Dz) + +### ReSpeaker 2-Mics Pi HAT v2.0 + +- [Seeed Studio ReSpeaker 2-Mics Pi HAT V2.0](https://wiki.seeedstudio.com/respeaker_2_mics_pi_hat_v2/) + +## Technical notes + +Unlike v1 — which needs an out-of-tree DKMS kernel module — v2 uses the mainline `snd-soc-tlv320aic3x` driver that already ships with the Raspberry Pi OS kernel. The image only needs: + +1. The device-tree overlay `respeaker-2mic-v2_0.dtbo` (built from [Seeed-Studio/seeed-linux-dtoverlays](https://github.com/Seeed-Studio/seeed-linux-dtoverlays)), enabled via `dtoverlay=respeaker-2mic-v2_0` in `/boot/firmware/config.txt`. +2. `dtparam=i2c_arm=on` in `/boot/firmware/config.txt`. +3. Per-boot mixer tuning — the TLV320 ships with several attenuators well below 100 % (`HP DAC` at -23.5 dB in particular), producing a card that appears to work but is inaudible at typical application volumes. + +The PiCompose `02-stage-audiodriver-2michat-v2` stage performs all three steps automatically. The mixer tuning is applied by `configure_audio.service` on every boot, the same pattern other audiodriver stages follow after #42: amixer on the codec-specific controls, then `wpctl set-volume @DEFAULT_AUDIO_SINK@ 1.0` to keep the PipeWire sink at unity. PipeWire / WirePlumber manage ALSA state per session and can reset the mixer between reboots, so a first-boot-only guard would let users end up stuck at 0%. Customization via `amixer` is still possible at runtime — it just won't survive a reboot. + +## Additional information + +- [Seeed Studio Wiki — ReSpeaker 2-Mics Pi HAT v2.0](https://wiki.seeedstudio.com/respeaker_2_mics_pi_hat_v2/) +- [seeed-linux-dtoverlays (overlay source)](https://github.com/Seeed-Studio/seeed-linux-dtoverlays)