Added HID documentation and added cfg(windows) to the relevant patterns.

This commit is contained in:
Simon Johnston 2020-08-19 12:40:04 -07:00
parent 7b4ebd5432
commit 0e4105b669
2 changed files with 155 additions and 23 deletions

View File

@ -147,19 +147,23 @@ pub enum SolidColor {
/// A pattern the light can be set to show.
#[derive(Clone, Debug)]
pub enum Pattern {
/// A preset pattern
/// A preset pattern that cycles between red and blue.
Police,
/// A preset pattern
/// A preset pattern that cycles between green,. yellow, and red.
TrafficLights,
/// A preset pattern
/// Preset random patterns
Random(u8),
/// A preset pattern (accepted on Windows only)
/// A preset pattern
#[cfg(target_os = "windows")]
Rainbow,
/// A preset pattern (accepted on Windows only)
/// A preset pattern
#[cfg(target_os = "windows")]
Sea,
/// A preset pattern (accepted on Windows only)
/// A preset pattern
#[cfg(target_os = "windows")]
WhiteWave,
/// A preset pattern (accepted on Windows only)
/// A preset pattern
#[cfg(target_os = "windows")]
Synthetic,
}
@ -254,9 +258,13 @@ impl Display for Pattern {
Pattern::Police => "police".to_string(),
Pattern::TrafficLights => "traffic lights".to_string(),
Pattern::Random(n) => format!("random {}", n),
#[cfg(target_os = "windows")]
Pattern::Rainbow => "rainbow".to_string(),
#[cfg(target_os = "windows")]
Pattern::Sea => "sea".to_string(),
#[cfg(target_os = "windows")]
Pattern::WhiteWave => "white wave".to_string(),
#[cfg(target_os = "windows")]
Pattern::Synthetic => "synthetic".to_string(),
}
)
@ -276,8 +284,13 @@ impl FromStr for Pattern {
"random 3" => Ok(Pattern::Random(3)),
"random 4" => Ok(Pattern::Random(4)),
"random 5" => Ok(Pattern::Random(5)),
#[cfg(target_os = "windows")]
"rainbow" => Ok(Pattern::Rainbow),
#[cfg(target_os = "windows")]
"sea" => Ok(Pattern::Sea),
#[cfg(target_os = "windows")]
"white wave" => Ok(Pattern::WhiteWave),
#[cfg(target_os = "windows")]
"synthetic" => Ok(Pattern::Synthetic),
_ => Err(error::ErrorKind::InvalidPattern.into()),
}

View File

@ -1,5 +1,107 @@
/*!
Implementation of the Device trait for USB connected lights.
# Specification
The identifiers used to discover the device using the HID API are as follows:
1. The vendor identifier (VID) is `0x04D8`.
1. The product identifier (PID) is `0xF372`.
The following command groups exist for controlling the lights. Note that byte 0, the USB HID _report
identifier_, must always be set to `0x00`. Also, trailing `0x00` values need not be written.
| Command Group | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|----------------|--------|--------|--------|--------|--------|--------|--------|--------|--------|
| Simple | `0x00` | `0x00` | COLOR* | `0x00` | `0x00` | `0x00` | `0x00` | `0x00` | `0x00` |
| Solid | `0x00` | `0x01` | LED | RED | GREEN | BLUE | `0x00` | `0x00` | `0x00` |
| Fade | `0x00` | `0x02` | LED | RED | GREEN | BLUE | TIME | `0x00` | `0x00` |
| Strobe | `0x00` | `0x03` | LED | RED | GREEN | BLUE | SPEED | `0x00` | REPEAT |
| Wave | `0x00` | `0x04` | WTYPE | RED | GREEN | BLUE | `0x00` | REPEAT | SPEED |
| Pattern | `0x00` | `0x06` | PTYPE | REPEAT | `0x00` | `0x00` | `0x00` | `0x00` | `0x00` |
| Productivity | `0x00` | `0x0A` | COLOR | `0x00` | `0x00` | `0x00` | `0x00` | `0x00` | `0x00` |
| Get Ver/Serial | `0x00` | `0x80` | `0x00` | `0x00` | `0x00` | `0x00` | `0x00` | `0x00` | `0x00` |
## Notes
1. The values for LED, COLOR, WTYPE, and PTYPE, are shown in the corresponding tables below.
1. The values for RED, GREEN, BLUE, are `0..255` and correspond to standard RGB color.
1. The value for TIME is the number of seconds to fade from the current color to the new color specified.
1. The value for SPEED is the time to cycle through the change.
1. The value of REPEAT is the number of times to repeat the wave or pattern.
1. The response data for the get version and serial number command group is described below.
## LED values
| Value | Addressed LED |
|--------------|--------------------|
| `0x01..0x06` | Specific LED |
| `0x41` | all back LEDs |
| `0x42` | all front LEDs |
| `0xFF` | all LEDs one color |
The addressable LEDs on the _Flag_ USB product are as follows, shown in vertical orientation:
| Back | Front |
|------|-------|
| 6 | 3 |
| 5 | 2 |
| 4 | 1 |
## COLOR values
1. For the command group _Productivity_ all the values below are valid.
1. For the command group _Simple_ the values 'E' and 'D' are not valid.
| Letter | Value | Color |
|--------|--------|-----------|
| 'E' | `0x45` | _Enable_ |
| 'D' | `0x44` | _Disable_ |
| 'R' | `0x52` | Red |
| 'G' | `0x47` | Green |
| 'B' | `0x42` | Blue |
| 'C' | `0x43` | Cyan |
| 'M' | `0x4D` | Magenta |
| 'Y' | `0x59` | Yellow |
| 'W' | `0x57` | White |
| 'O' | `0x4F` | Off |
## WTYPE values
| Value | Pattern |
|--------|--------------------|
| `0x01` | Short |
| `0x02` | Long |
| `0x03` | Overlapping Short |
| `0x04` | Overlapping Long |
| `0x05` | ? |
1. Luxafor describe wave type as a value `0x01..0x05` and yet there seems to be no description of `0x05` anywhere.
## PTYPE values
| Value | Pattern | Windows Only |
|--------|--------------|--------------|
| `0x00` | ? | Unknown |
| `0x01` | Luxafor/Traffic Lights | No |
| `0x02` | Random 1 | No |
| `0x03` | Random 2 | No |
| `0x04` | Random 3 | No |
| `0x05` | Police | No |
| `0x06` | Random 4 | No |
| `0x07` | Random 5 | No |
| `0x08` | Rainbow Wave | Yes |
1. Luxafor describe pattern type as a value `0x00..0x08` and yet there seems to be no description of `0x00` anywhere.
## Version/Serial response
| 0 | 1 | 2 | 3 |
|--------|------------|-------------|------------|
| `0x80` | FW Version | Serial High | Serial Low |
The serial number is returned as a pair, (high,low) bytes.
*/
use crate::{Device, Pattern, SolidColor};
@ -33,6 +135,8 @@ pub struct USBDevice<'a> {
const LUXAFOR_VENDOR_ID: u16 = 0x04d8;
const LUXAFOR_PRODUCT_ID: u16 = 0xf372;
const HID_REPORT_ID: u8 = 0;
const MODE_SOLID: u8 = 1;
#[allow(dead_code)]
const MODE_FADE: u8 = 2;
@ -66,6 +170,7 @@ const PATTERN_RANDOM_3: u8 = 4;
const PATTERN_RANDOM_4: u8 = 6;
const PATTERN_RANDOM_5: u8 = 7;
const PATTERN_POLICE: u8 = 5;
#[cfg(target_os = "windows")]
const PATTERN_RAINBOW_WAVE: u8 = 8;
// ------------------------------------------------------------------------------------------------
@ -132,14 +237,7 @@ impl<'a> Device for USBDevice<'a> {
};
let mode = if blink { MODE_STROBE } else { MODE_SOLID };
trace!("{} ({:#04x},{:#04x},{:#04x})", mode, r, g, b);
let result = self.hid_device.write(&[mode, LED_ALL, r, g, b]);
match result {
Ok(_) => Ok(()),
Err(err) => {
error!("Could not write to HID device: {:?}", err);
Err(crate::error::ErrorKind::InvalidRequest.into())
}
}
self.write(&[HID_REPORT_ID, mode, LED_ALL, r, g, b])
}
fn set_pattern(&self, pattern: Pattern) -> crate::error::Result<()> {
@ -154,19 +252,16 @@ impl<'a> Device for USBDevice<'a> {
4 => PATTERN_RANDOM_4,
_ => PATTERN_RANDOM_5,
},
#[cfg(target_os = "windows")]
Pattern::Rainbow => PATTERN_RAINBOW_WAVE,
#[cfg(target_os = "windows")]
Pattern::Sea => 9,
#[cfg(target_os = "windows")]
Pattern::WhiteWave => 10,
#[cfg(target_os = "windows")]
Pattern::Synthetic => 11,
};
let result = self.hid_device.write(&[MODE_PATTERN, LED_ALL, pattern]);
match result {
Ok(_) => Ok(()),
Err(err) => {
error!("Could not write to HID device: {:?}", err);
Err(crate::error::ErrorKind::InvalidRequest.into())
}
}
self.write(&[HID_REPORT_ID, MODE_PATTERN, pattern, 255])
}
}
@ -186,6 +281,30 @@ impl<'a> USBDevice<'a> {
);
Ok(Self { hid_device, id })
}
fn write(&self, buffer: &[u8]) -> crate::error::Result<()> {
trace!(
"writing [{:?}]",
buffer
.iter()
.map(|b| format!("{:#04x}", b))
.collect::<Vec<String>>()
.join(", ")
);
let result = self.hid_device.write(buffer);
match result {
Ok(bytes_written) => if bytes_written == buffer.len() {
Ok(())
} else {
error!("Bytes written, {}, did not match buffer length {}", bytes_written, buffer.len());
Err(crate::error::ErrorKind::InvalidRequest.into())
}
Err(err) => {
error!("Could not write to HID device: {:?}", err);
Err(crate::error::ErrorKind::InvalidRequest.into())
}
}
}
}
// ------------------------------------------------------------------------------------------------