Files
linux-voice-assistant/docs/linux-voice-assistant-xvf3800-mute.md

8.2 KiB
Raw Permalink Blame History

XVF3800 Mute Button & USB Control Integration

This document explains how the ReSpeaker XVF3800 4‑Mic USB Array integrates its built‑in mute button and red mute LED with the Linux Voice Assistant (LVA).

The goal is:

  • Pressing the XVF3800’s hardware mute button updates the LVA mic state.
  • Muting/unmuting the LVA (via Home Assistant, MQTT, or other inputs) updates the XVF3800’s mic mute circuit and red LED.
  • No extra binaries (like the vendor xvf_host) are required — LVA talks to the XVF3800 directly via USB control transfers using pyusb.

1. How it Works (High‑Level)

The XVF3800 exposes a vendor control protocol over USB that can read and write internal GPIO pins (GPO). One of these pins drives both:

  • the microphone mute circuit
  • the red mute LED on the XVF3800 front panel

LVA’s XVF3800ButtonController does three things:

  1. Reads hardware mute state periodically
    It polls the XVF3800’s GPO state (pin X0D30) in a background thread using pyusb. If the value changes, LVA treats that as “the user pressed the XVF3800 mute button” and publishes a set_mic_mute event.

  2. Mirrors LVA mute → XVF3800
    It subscribes to mic_muted / mic_unmuted events. Whenever LVA’s mute state changes (for example, via MQTT, HA UI, or a GPIO button), the controller writes the new state back to X0D30 so that the mic mute circuit and red LED stay in sync.

  3. Initial sync on startup
    On first successful read, the controller treats the hardware mute state as the source of truth and publishes set_mic_mute so LVA starts aligned with whatever the XVF3800 is currently doing.

No PulseAudio/PipeWire configuration is required for this feature; it only depends on USB access to the XVF3800 and the pyusb library.


2. Prerequisites

  • You are running a branch of linux‑voice‑assistant that includes:
    • linux_voice_assistant/xvf3800_button_controller.py
    • The updated __main__.py and config.py wiring for XVF3800ButtonController
  • The ReSpeaker XVF3800 is connected over USB and appears in lsusb as:
    • 2886:001a Seeed Technology Co., Ltd. reSpeaker XVF3800 4-Mic Array
  • The LVA .venv includes pyusb (already present in pyproject.toml):
    source .venv/bin/activate
    pip install pyusb
    

On most systems, user‑mode processes can talk to the XVF3800 over USB without sudo once the device is enumerated. If you see USBError: [Errno 13] Access denied, you may need a udev rule — that’s outside this document but worth noting.


3. Enabling the XVF3800 Mute Integration

The integration is controlled via the button section of config.json. Two “modes” are supported:

  • "gpio" – legacy Raspberry Pi GPIO button (ReSpeaker 2‑Mic HAT, etc.)
  • "xvf3800" – USB‑based mute integration for the ReSpeaker XVF3800

3.1 Example config.json for XVF3800

Edit your LVA configuration file (usually linux_voice_assistant/config.json) and add or adjust the button block like this:

{
  "app": {
    "name": "Linux Voice Assistant"
  },

  // ... other sections (audio, wake_word, esphome, led, mqtt, etc.) ...

  "button": {
    "enabled": true,
    "mode": "xvf3800",
    // Optional: how often to poll the XVF3800 GPO state (seconds)
    "poll_interval_seconds": 0.05
  }
}

Notes:

  • "enabled": true turns on button support in general.
  • "mode": "xvf3800" switches from the GPIO‑based button controller to the USB‑based XVF3800 controller.
  • poll_interval_seconds is optional and defaults to 0.01 if omitted. Values between 0.02 and 0.1 are typically fine.

The GPIO‑specific fields (pin, long_press_seconds) are ignored in xvf3800 mode — the XVF3800’s onboard mute button is treated as a dedicated mute toggle only, not a wake/stop button.


4. Runtime Behaviour

Once enabled and the LVA service is restarted, the mute integration behaves as follows:

4.1 Pressing the XVF3800 Mute Button

  1. The XVF3800 firmware changes the GPO state of pin X0D30 and lights or clears the red mute LED.
  2. The XVF3800ButtonController sees the changed GPO values the next time it polls USB.
  3. It publishes a set_mic_mute event on the LVA EventBus with the new state.
  4. The MicMuteHandler:
    • Updates ServerState.mic_muted and mic_muted_event (pausing/resuming the audio capture thread).
    • Publishes mic_muted / mic_unmuted events used by other components (LED controller, MQTT, etc.).
    • Notifies the MQTT controller so the HA “Mute Microphone” entity stays in sync.

From the user’s perspective:

  • The red LED on the XVF3800 tracks mic mute.
  • The HA “Mute Microphone” switch also tracks the hardware button.
  • The audio engine actually stops capturing when muted.

4.2 Toggling Mute from Home Assistant / MQTT / Other Inputs

  1. Home Assistant toggles the LVA “Mute Microphone” switch (or another input publishes a set_mic_mute event).
  2. The MicMuteHandler applies the new mute state, as usual.
  3. It emits a mic_muted or mic_unmuted event.
  4. The XVF3800ButtonController receives this event and writes the new state back to X0D30 via USB.
  5. The XVF3800’s mic mute circuit and red LED update to match LVA.

This keeps hardware, LVA state, and Home Assistant UI aligned.


5. Coexistence with Other Buttons

The XVF3800 mute integration is intentionally narrow in scope:

  • It does not implement short vs. long press behaviour.
  • It does not trigger wake‑word, stop‑word, or playback controls.
  • It is purely “mute on / mute off” with bidirectional sync.

If you also have a GPIO button (e.g. a ReSpeaker 2‑Mic HAT momentary switch) wired for wake/stop behaviour, you can:

  • Leave button.mode: "gpio" and not use the XVF3800 mute integration, or
  • Move wake/stop to the GPIO button controller and leave the XVF3800 button as mute‑only by setting mode: "xvf3800" and handling wake/stop elsewhere.

At the moment, button.mode is single‑valued — it selects either the GPIO controller or the XVF3800 controller, not both at once. If you ever want to support both concurrently, that would be a separate enhancement (e.g. a list of button backends).


6. Troubleshooting

6.1 No USB Access / Permission Errors

Symptoms in the logs (journalctl or console):

Failed to initialize XVF3800 USB client; mute button integration will be disabled
USBError: [Errno 13] Access denied (insufficient permissions)

Check:

  • lsusb shows 2886:001a while LVA is running.
  • The LVA service is running as the user who owns the .venv and has access to USB devices.
  • If necessary, add a udev rule that grants access to idVendor=0x2886 and idProduct=0x001a for your LVA user, then reload udev and replug the device.

The controller will keep retrying USB initialization; once permissions are fixed, it should connect without restarting LVA.

6.2 Mute Button Works but HA Entity Doesn’t Update

  • Verify MQTT is enabled and configured correctly in config.json.
  • Confirm that the HA “Mute Microphone” entity exists and belongs to the correct LVA device.
  • Check logs for MicMuteHandler and MqttController messages when pressing the XVF3800 mute button.

6.3 HA Mute Switch Works but XVF3800 LED Doesn’t Change

  • Ensure button.enabled is true and "mode": "xvf3800" in config.json.
  • Look for log lines like:
    • Initializing XVF3800ButtonController (mode=xvf3800)
    • Connected to ReSpeaker XVF3800 for mute control
  • If you see only GPIO logs, confirm that the branch you’re running includes xvf3800_button_controller.py and the updated __main__.py wiring.

7. Summary

With mode: "xvf3800", the Linux Voice Assistant treats the ReSpeaker XVF3800’s onboard mute button and red LED as a first‑class mute control:

  • Pressing the XVF3800 mute button updates LVA and Home Assistant.
  • Muting via Home Assistant or other inputs updates the XVF3800 hardware.
  • The audio engine, LEDs, and MQTT entities stay in sync with the device’s actual hardware mute state.

This integration is fully optional and coexists with existing audio and LED behaviour — it simply adds a reliable, hardware‑based mute path for the XVF3800‑based satellites.