fix leds
This commit is contained in:
@@ -1,13 +1,18 @@
|
|||||||
# HeadMic - Vixy's Ears
|
# HeadMic - Vixy's Ears
|
||||||
# For Raspberry Pi 5 (head-vixy)
|
# For Raspberry Pi 5 (head-vixy)
|
||||||
|
|
||||||
|
# Python 3.13 compatibility
|
||||||
|
setuptools>=75.0.0
|
||||||
|
|
||||||
# Web framework
|
# Web framework
|
||||||
fastapi>=0.104.0
|
fastapi>=0.104.0
|
||||||
uvicorn>=0.24.0
|
uvicorn>=0.24.0
|
||||||
|
python-multipart>=0.0.6
|
||||||
|
|
||||||
# Audio
|
# Audio
|
||||||
pyaudio>=0.2.13
|
pyaudio>=0.2.13
|
||||||
webrtcvad>=2.0.10
|
webrtcvad>=2.0.10
|
||||||
|
numpy>=1.24.0
|
||||||
|
|
||||||
# Wake word detection
|
# Wake word detection
|
||||||
pvporcupine>=3.0.0
|
pvporcupine>=3.0.0
|
||||||
|
|||||||
70
xvf3800.py
70
xvf3800.py
@@ -28,15 +28,16 @@ PID = 0x001A
|
|||||||
CTRL_REQUEST_TYPE_OUT = usb.util.CTRL_OUT | usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_RECIPIENT_DEVICE if PYUSB_AVAILABLE else 0
|
CTRL_REQUEST_TYPE_OUT = usb.util.CTRL_OUT | usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_RECIPIENT_DEVICE if PYUSB_AVAILABLE else 0
|
||||||
CTRL_REQUEST_TYPE_IN = usb.util.CTRL_IN | usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_RECIPIENT_DEVICE if PYUSB_AVAILABLE else 0
|
CTRL_REQUEST_TYPE_IN = usb.util.CTRL_IN | usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_RECIPIENT_DEVICE if PYUSB_AVAILABLE else 0
|
||||||
|
|
||||||
# Resource IDs
|
# Resource IDs (wIndex in USB control transfer)
|
||||||
GPO_RESID = 20
|
GPO_RESID = 20
|
||||||
|
|
||||||
# Parameter indices (within resource)
|
# Command IDs (wValue in USB control transfer)
|
||||||
DOA_VALUE_IDX = 18 # returns (angle 0-359, vad 0/1)
|
DOA_VALUE_CMD = 18 # returns (angle 0-359, vad 0/1)
|
||||||
LED_EFFECT_IDX = 0 # 0=off, 1=breath, 2=rainbow, 3=solid, 4=doa, 5=ring
|
LED_EFFECT_CMD = 12 # 0=off, 1=breath, 2=rainbow, 3=solid, 4=doa, 5=ring
|
||||||
LED_BRIGHTNESS_IDX = 1
|
LED_BRIGHTNESS_CMD = 13
|
||||||
LED_COLOR_IDX = 3 # single uint32 color
|
LED_COLOR_CMD = 15 # single uint32 color
|
||||||
LED_RING_COLOR_IDX = 5 # 12 × uint32
|
LED_DOA_COLOR_CMD = 16 # two uint32 values: base + indicator
|
||||||
|
LED_RING_COLOR_CMD = 17 # 12 × uint32
|
||||||
|
|
||||||
|
|
||||||
class XVF3800:
|
class XVF3800:
|
||||||
@@ -48,29 +49,41 @@ class XVF3800:
|
|||||||
self.bus = usb_device.bus
|
self.bus = usb_device.bus
|
||||||
self.address = usb_device.address
|
self.address = usb_device.address
|
||||||
|
|
||||||
def _read(self, resid: int, param_idx: int, length: int) -> bytes:
|
def _read(self, resid: int, cmdid: int, length: int) -> bytes:
|
||||||
"""Read parameter via USB control transfer."""
|
"""Read parameter via USB control transfer.
|
||||||
wValue = (resid << 8) | param_idx
|
wValue = cmdid | 0x80 (read flag), wIndex = resid."""
|
||||||
try:
|
try:
|
||||||
data = self.dev.ctrl_transfer(CTRL_REQUEST_TYPE_IN, 0, wValue, 0, length, timeout=1000)
|
data = self.dev.ctrl_transfer(
|
||||||
|
CTRL_REQUEST_TYPE_IN, 0,
|
||||||
|
0x80 | cmdid, # wValue: cmdid with read bit
|
||||||
|
resid, # wIndex: resource ID
|
||||||
|
length * 4, # bytes to read (length is in words)
|
||||||
|
timeout=1000
|
||||||
|
)
|
||||||
return bytes(data)
|
return bytes(data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug("USB read error (resid=%d, param=%d): %s", resid, param_idx, e)
|
logger.debug("USB read error (resid=%d, cmd=%d): %s", resid, cmdid, e)
|
||||||
return b""
|
return b""
|
||||||
|
|
||||||
def _write(self, resid: int, param_idx: int, data: bytes):
|
def _write(self, resid: int, cmdid: int, data: bytes):
|
||||||
"""Write parameter via USB control transfer."""
|
"""Write parameter via USB control transfer.
|
||||||
wValue = (resid << 8) | param_idx
|
wValue = cmdid, wIndex = resid."""
|
||||||
try:
|
try:
|
||||||
self.dev.ctrl_transfer(CTRL_REQUEST_TYPE_OUT, 0, wValue, 0, data, timeout=1000)
|
self.dev.ctrl_transfer(
|
||||||
|
CTRL_REQUEST_TYPE_OUT, 0,
|
||||||
|
cmdid, # wValue: command ID
|
||||||
|
resid, # wIndex: resource ID
|
||||||
|
data,
|
||||||
|
timeout=1000
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug("USB write error (resid=%d, param=%d): %s", resid, param_idx, e)
|
logger.debug("USB write error (resid=%d, cmd=%d): %s", resid, cmdid, e)
|
||||||
|
|
||||||
# --- DoA ---
|
# --- DoA ---
|
||||||
|
|
||||||
def read_doa(self) -> tuple[int, bool]:
|
def read_doa(self) -> tuple[int, bool]:
|
||||||
"""Read Direction of Arrival. Returns (angle 0-359, vad True/False)."""
|
"""Read Direction of Arrival. Returns (angle 0-359, vad True/False)."""
|
||||||
data = self._read(GPO_RESID, DOA_VALUE_IDX, 4)
|
data = self._read(GPO_RESID, DOA_VALUE_CMD, 2) # 2 uint16 words
|
||||||
if len(data) < 4:
|
if len(data) < 4:
|
||||||
return 0, False
|
return 0, False
|
||||||
angle, vad = struct.unpack_from("<HH", data)
|
angle, vad = struct.unpack_from("<HH", data)
|
||||||
@@ -79,29 +92,28 @@ class XVF3800:
|
|||||||
# --- LEDs ---
|
# --- LEDs ---
|
||||||
|
|
||||||
def led_off(self):
|
def led_off(self):
|
||||||
self._write(GPO_RESID, LED_EFFECT_IDX, struct.pack("<I", 0))
|
self._write(GPO_RESID, LED_EFFECT_CMD, struct.pack("<I", 0))
|
||||||
|
|
||||||
def led_solid(self, color: int):
|
def led_solid(self, color: int):
|
||||||
"""Solid color on all LEDs. color is 0xRRGGBB."""
|
"""Solid color on all LEDs. color is 0xRRGGBB."""
|
||||||
self._write(GPO_RESID, LED_COLOR_IDX, struct.pack("<I", color))
|
self._write(GPO_RESID, LED_COLOR_CMD, struct.pack("<I", color))
|
||||||
self._write(GPO_RESID, LED_EFFECT_IDX, struct.pack("<I", 3))
|
self._write(GPO_RESID, LED_EFFECT_CMD, struct.pack("<I", 3))
|
||||||
|
|
||||||
def led_breath(self, color: int, brightness: int = 128):
|
def led_breath(self, color: int, brightness: int = 128):
|
||||||
"""Breathing effect."""
|
"""Breathing effect."""
|
||||||
self._write(GPO_RESID, LED_COLOR_IDX, struct.pack("<I", color))
|
self._write(GPO_RESID, LED_COLOR_CMD, struct.pack("<I", color))
|
||||||
self._write(GPO_RESID, LED_BRIGHTNESS_IDX, struct.pack("<I", brightness))
|
self._write(GPO_RESID, LED_BRIGHTNESS_CMD, struct.pack("<I", brightness))
|
||||||
self._write(GPO_RESID, LED_EFFECT_IDX, struct.pack("<I", 1))
|
self._write(GPO_RESID, LED_EFFECT_CMD, struct.pack("<I", 1))
|
||||||
|
|
||||||
def led_doa(self, base_color: int = 0x003333, doa_color: int = 0x00FFFF):
|
def led_doa(self, base_color: int = 0x003333, doa_color: int = 0x00FFFF):
|
||||||
"""DoA indicator mode — shows beam direction on LED ring."""
|
"""DoA indicator mode — shows beam direction on LED ring."""
|
||||||
# LED_DOA_COLOR takes two uint32 values: base + indicator
|
|
||||||
data = struct.pack("<II", base_color, doa_color)
|
data = struct.pack("<II", base_color, doa_color)
|
||||||
self._write(GPO_RESID, 4, data) # param 4 = LED_DOA_COLOR
|
self._write(GPO_RESID, LED_DOA_COLOR_CMD, data)
|
||||||
self._write(GPO_RESID, LED_EFFECT_IDX, struct.pack("<I", 4))
|
self._write(GPO_RESID, LED_EFFECT_CMD, struct.pack("<I", 4))
|
||||||
|
|
||||||
def led_rainbow(self, brightness: int = 128):
|
def led_rainbow(self, brightness: int = 128):
|
||||||
self._write(GPO_RESID, LED_BRIGHTNESS_IDX, struct.pack("<I", brightness))
|
self._write(GPO_RESID, LED_BRIGHTNESS_CMD, struct.pack("<I", brightness))
|
||||||
self._write(GPO_RESID, LED_EFFECT_IDX, struct.pack("<I", 2))
|
self._write(GPO_RESID, LED_EFFECT_CMD, struct.pack("<I", 2))
|
||||||
|
|
||||||
|
|
||||||
class XVF3800Manager:
|
class XVF3800Manager:
|
||||||
|
|||||||
Reference in New Issue
Block a user