8.2 KiB
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 usingpyusb.
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:
-
Reads hardware mute state periodically
It polls the XVF3800’s GPO state (pin X0D30) in a background thread usingpyusb. If the value changes, LVA treats that as “the user pressed the XVF3800 mute button” and publishes aset_mic_muteevent. -
Mirrors LVA mute → XVF3800
It subscribes tomic_muted/mic_unmutedevents. 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. -
Initial sync on startup
On first successful read, the controller treats the hardware mute state as the source of truth and publishesset_mic_muteso 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__.pyandconfig.pywiring forXVF3800ButtonController
- The ReSpeaker XVF3800 is connected over USB and appears in
lsusbas:2886:001a Seeed Technology Co., Ltd. reSpeaker XVF3800 4-Mic Array
- The LVA
.venvincludespyusb(already present inpyproject.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": trueturns on button support in general."mode": "xvf3800"switches from the GPIO‑based button controller to the USB‑based XVF3800 controller.poll_interval_secondsis optional and defaults to0.01if 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
- The XVF3800 firmware changes the GPO state of pin X0D30 and lights or clears the red mute LED.
- The
XVF3800ButtonControllersees the changed GPO values the next time it polls USB. - It publishes a
set_mic_muteevent on the LVAEventBuswith the new state. - The
MicMuteHandler:- Updates
ServerState.mic_mutedandmic_muted_event(pausing/resuming the audio capture thread). - Publishes
mic_muted/mic_unmutedevents used by other components (LED controller, MQTT, etc.). - Notifies the MQTT controller so the HA “Mute Microphone” entity stays in sync.
- Updates
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
- Home Assistant toggles the LVA “Mute Microphone” switch (or another input
publishes a
set_mic_muteevent). - The
MicMuteHandlerapplies the new mute state, as usual. - It emits a
mic_mutedormic_unmutedevent. - The
XVF3800ButtonControllerreceives this event and writes the new state back to X0D30 via USB. - 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:
lsusbshows2886:001awhile LVA is running.- The LVA service is running as the user who owns the
.venvand has access to USB devices. - If necessary, add a udev rule that grants access to
idVendor=0x2886andidProduct=0x001afor 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
MicMuteHandlerandMqttControllermessages when pressing the XVF3800 mute button.
6.3 HA Mute Switch Works but XVF3800 LED Doesn’t Change
- Ensure
button.enabledistrueand"mode": "xvf3800"inconfig.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.pyand the updated__main__.pywiring.
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.