respeaker 0.3.0

USB control library for Seeed ReSpeaker Mic Array v2.0 and XVF3800 (LEDs + DSP tuning), with reSpeaker Lite identification
Documentation
//! LED ring control — protocol from `pixel_ring/usb_pixel_ring_v2.py`.

use crate::color::Rgb;
use crate::error::{Error, Result};
use crate::{LED_INDEX, LED_TIMEOUT, VENDOR_OUT};
use rusb::{DeviceHandle, UsbContext};

/// Center VAD LED policy (`set_vad_led`).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum VadLed {
    Off = 0,
    On = 1,
    /// Follow voice-activity detection.
    Auto = 2,
}

/// Borrowed LED-ring controller for Mic Array v2.0.
pub struct PixelRing<'a, T: UsbContext> {
    handle: &'a DeviceHandle<T>,
}

impl<'a, T: UsbContext> PixelRing<'a, T> {
    pub(crate) fn new(handle: &'a DeviceHandle<T>) -> Self {
        Self { handle }
    }

    pub fn trace(&self) -> Result<()> {
        self.write(0, &[0])
    }

    pub fn mono(&self, color: Rgb) -> Result<()> {
        self.write(1, &color.mono_payload())
    }

    pub fn off(&self) -> Result<()> {
        self.mono(Rgb::BLACK)
    }

    pub fn listen(&self) -> Result<()> {
        self.write(2, &[0])
    }

    pub fn wakeup(&self) -> Result<()> {
        self.listen()
    }

    pub fn speak(&self) -> Result<()> {
        self.write(3, &[0])
    }

    pub fn think(&self) -> Result<()> {
        self.write(4, &[0])
    }

    pub fn wait(&self) -> Result<()> {
        self.think()
    }

    pub fn spin(&self) -> Result<()> {
        self.write(5, &[0])
    }

    pub fn customize(&self, colors: &[Rgb; 12]) -> Result<()> {
        let mut data = [0u8; 48];
        for (i, c) in colors.iter().enumerate() {
            let base = i * 4;
            data[base] = c.r;
            data[base + 1] = c.g;
            data[base + 2] = c.b;
        }
        self.write(6, &data)
    }

    pub fn set_brightness(&self, brightness: u8) -> Result<()> {
        if brightness > 0x1f {
            return Err(Error::InvalidValue(
                "brightness must be in 0..=31 (0x1F)".into(),
            ));
        }
        self.write(0x20, &[brightness])
    }

    pub fn set_color_palette(&self, a: Rgb, b: Rgb) -> Result<()> {
        let data = [a.r, a.g, a.b, 0, b.r, b.g, b.b, 0];
        self.write(0x21, &data)
    }

    pub fn set_vad_led(&self, state: VadLed) -> Result<()> {
        self.write(0x22, &[state as u8])
    }

    pub fn set_volume(&self, volume: u8) -> Result<()> {
        if volume > 12 {
            return Err(Error::InvalidValue("volume must be in 0..=12".into()));
        }
        self.write(0x23, &[volume])
    }

    fn write(&self, command: u16, data: &[u8]) -> Result<()> {
        self.handle
            .write_control(VENDOR_OUT, 0, command, LED_INDEX, data, LED_TIMEOUT)?;
        Ok(())
    }
}