fix leds
This commit is contained in:
@@ -1,13 +1,18 @@
|
||||
# HeadMic - Vixy's Ears
|
||||
# For Raspberry Pi 5 (head-vixy)
|
||||
|
||||
# Python 3.13 compatibility
|
||||
setuptools>=75.0.0
|
||||
|
||||
# Web framework
|
||||
fastapi>=0.104.0
|
||||
uvicorn>=0.24.0
|
||||
python-multipart>=0.0.6
|
||||
|
||||
# Audio
|
||||
pyaudio>=0.2.13
|
||||
webrtcvad>=2.0.10
|
||||
numpy>=1.24.0
|
||||
|
||||
# Wake word detection
|
||||
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_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
|
||||
|
||||
# Parameter indices (within resource)
|
||||
DOA_VALUE_IDX = 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_BRIGHTNESS_IDX = 1
|
||||
LED_COLOR_IDX = 3 # single uint32 color
|
||||
LED_RING_COLOR_IDX = 5 # 12 × uint32
|
||||
# Command IDs (wValue in USB control transfer)
|
||||
DOA_VALUE_CMD = 18 # returns (angle 0-359, vad 0/1)
|
||||
LED_EFFECT_CMD = 12 # 0=off, 1=breath, 2=rainbow, 3=solid, 4=doa, 5=ring
|
||||
LED_BRIGHTNESS_CMD = 13
|
||||
LED_COLOR_CMD = 15 # single uint32 color
|
||||
LED_DOA_COLOR_CMD = 16 # two uint32 values: base + indicator
|
||||
LED_RING_COLOR_CMD = 17 # 12 × uint32
|
||||
|
||||
|
||||
class XVF3800:
|
||||
@@ -48,29 +49,41 @@ class XVF3800:
|
||||
self.bus = usb_device.bus
|
||||
self.address = usb_device.address
|
||||
|
||||
def _read(self, resid: int, param_idx: int, length: int) -> bytes:
|
||||
"""Read parameter via USB control transfer."""
|
||||
wValue = (resid << 8) | param_idx
|
||||
def _read(self, resid: int, cmdid: int, length: int) -> bytes:
|
||||
"""Read parameter via USB control transfer.
|
||||
wValue = cmdid | 0x80 (read flag), wIndex = resid."""
|
||||
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)
|
||||
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""
|
||||
|
||||
def _write(self, resid: int, param_idx: int, data: bytes):
|
||||
"""Write parameter via USB control transfer."""
|
||||
wValue = (resid << 8) | param_idx
|
||||
def _write(self, resid: int, cmdid: int, data: bytes):
|
||||
"""Write parameter via USB control transfer.
|
||||
wValue = cmdid, wIndex = resid."""
|
||||
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:
|
||||
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 ---
|
||||
|
||||
def read_doa(self) -> tuple[int, bool]:
|
||||
"""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:
|
||||
return 0, False
|
||||
angle, vad = struct.unpack_from("<HH", data)
|
||||
@@ -79,29 +92,28 @@ class XVF3800:
|
||||
# --- LEDs ---
|
||||
|
||||
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):
|
||||
"""Solid color on all LEDs. color is 0xRRGGBB."""
|
||||
self._write(GPO_RESID, LED_COLOR_IDX, struct.pack("<I", color))
|
||||
self._write(GPO_RESID, LED_EFFECT_IDX, struct.pack("<I", 3))
|
||||
self._write(GPO_RESID, LED_COLOR_CMD, struct.pack("<I", color))
|
||||
self._write(GPO_RESID, LED_EFFECT_CMD, struct.pack("<I", 3))
|
||||
|
||||
def led_breath(self, color: int, brightness: int = 128):
|
||||
"""Breathing effect."""
|
||||
self._write(GPO_RESID, LED_COLOR_IDX, struct.pack("<I", color))
|
||||
self._write(GPO_RESID, LED_BRIGHTNESS_IDX, struct.pack("<I", brightness))
|
||||
self._write(GPO_RESID, LED_EFFECT_IDX, struct.pack("<I", 1))
|
||||
self._write(GPO_RESID, LED_COLOR_CMD, struct.pack("<I", color))
|
||||
self._write(GPO_RESID, LED_BRIGHTNESS_CMD, struct.pack("<I", brightness))
|
||||
self._write(GPO_RESID, LED_EFFECT_CMD, struct.pack("<I", 1))
|
||||
|
||||
def led_doa(self, base_color: int = 0x003333, doa_color: int = 0x00FFFF):
|
||||
"""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)
|
||||
self._write(GPO_RESID, 4, data) # param 4 = LED_DOA_COLOR
|
||||
self._write(GPO_RESID, LED_EFFECT_IDX, struct.pack("<I", 4))
|
||||
self._write(GPO_RESID, LED_DOA_COLOR_CMD, data)
|
||||
self._write(GPO_RESID, LED_EFFECT_CMD, struct.pack("<I", 4))
|
||||
|
||||
def led_rainbow(self, brightness: int = 128):
|
||||
self._write(GPO_RESID, LED_BRIGHTNESS_IDX, struct.pack("<I", brightness))
|
||||
self._write(GPO_RESID, LED_EFFECT_IDX, struct.pack("<I", 2))
|
||||
self._write(GPO_RESID, LED_BRIGHTNESS_CMD, struct.pack("<I", brightness))
|
||||
self._write(GPO_RESID, LED_EFFECT_CMD, struct.pack("<I", 2))
|
||||
|
||||
|
||||
class XVF3800Manager:
|
||||
|
||||
Reference in New Issue
Block a user