#[macro_use]
extern crate log;
extern crate failure;
extern crate libusb;
#[macro_use]
extern crate failure_derive;
use libusb::{Context, Device, DeviceDescriptor, DeviceHandle, Direction, Recipient, RequestType,
Result as UsbResult, TransferType};
use std::time::Duration;
const VID: u16 = 0x1774;
const PID: u16 = 0x1001;
const SENSOR_ENDPOINT: u8 = 0x81;
const D1: f64 = -40.00;
const D2: f64 = 0.01;
const C1: f64 = -4.0;
const C2: f64 = 0.0405;
const C3: f64 = -2.8e-6;
const T1: f64 = 0.01;
const T2: f64 = 0.00008;
#[derive(Fail, Debug)]
pub enum UsbrhError {
#[fail(display = "Target device not found")]
DeviceNotFound,
#[fail(display = "Read error: {}", _0)]
ReadError(String),
#[fail(display = "{}", _0)]
Usb(#[cause] libusb::Error),
}
#[derive(Debug)]
pub struct SensorValues {
pub temperature: f64,
pub humidity: f64,
}
#[derive(Copy, Clone, Debug)]
struct Endpoint {
config: u8,
interface: u8,
setting: u8,
address: u8,
}
impl From<libusb::Error> for UsbrhError {
fn from(e: libusb::Error) -> Self {
UsbrhError::Usb(e)
}
}
type Result<T> = std::result::Result<T, UsbrhError>;
pub fn read_sensors(verbose: u32, device_num: u32) -> Result<SensorValues> {
let mut context = Context::new()?;
if verbose >= 2 {
context.set_log_level(libusb::LogLevel::Debug);
}
let ret = match open_device(&context, VID, PID, device_num)? {
Some((mut device, device_desc, mut handle)) => {
info!("device found");
read_device(&mut device, &device_desc, &mut handle)
}
None => Err(UsbrhError::DeviceNotFound),
};
ret
}
fn open_device(
context: &Context,
vid: u16,
pid: u16,
device_num: u32,
) -> Result<Option<(Device, DeviceDescriptor, DeviceHandle)>> {
let devices = match context.devices() {
Ok(d) => d,
Err(_) => return Ok(None),
};
let mut count = 0;
for device in devices.iter() {
let device_desc = match device.device_descriptor() {
Ok(d) => d,
Err(_) => continue,
};
if device_desc.vendor_id() == vid && device_desc.product_id() == pid {
count += 1;
if count == device_num {
return device
.open()
.map(|handle| Some((device, device_desc, handle)))
.map_err(UsbrhError::from);
}
}
}
Ok(None)
}
fn send_control_msg(handle: &mut DeviceHandle) -> UsbResult<()> {
let request_type: u8 =
libusb::request_type(Direction::Out, RequestType::Class, Recipient::Interface);
debug!("request_type: {:x}", request_type);
let request: u8 = 0x09;
let value: u16 = 0x0200;
let index: u16 = 0;
let buf = [0u8; 7];
let timeout = Duration::from_secs(5);
handle.write_control(request_type, request, value, index, &buf, timeout)?;
Ok(())
}
fn read_device(
device: &mut Device,
device_desc: &DeviceDescriptor,
handle: &mut DeviceHandle,
) -> Result<SensorValues> {
handle.reset()?;
match find_readable_endpoint(device, device_desc, TransferType::Interrupt) {
Some(endpoint) => {
debug!("endpoint found: {:?}", endpoint);
read_endpoint(handle, endpoint)
}
None => Err(UsbrhError::ReadError(
"No readable interrupt endpoint found".to_string(),
)),
}
}
fn find_readable_endpoint(
device: &mut Device,
device_desc: &DeviceDescriptor,
transfer_type: TransferType,
) -> Option<Endpoint> {
for n in 0..device_desc.num_configurations() {
let config_desc = match device.config_descriptor(n) {
Ok(c) => c,
Err(_) => continue,
};
for interface in config_desc.interfaces() {
for interface_desc in interface.descriptors() {
for endpoint_desc in interface_desc.endpoint_descriptors() {
if endpoint_desc.direction() == Direction::In
&& endpoint_desc.transfer_type() == transfer_type
{
return Some(Endpoint {
config: config_desc.number(),
interface: interface_desc.interface_number(),
setting: interface_desc.setting_number(),
address: endpoint_desc.address(),
});
}
}
}
}
}
None
}
fn read_endpoint(handle: &mut DeviceHandle, endpoint: Endpoint) -> Result<SensorValues> {
fn read(handle: &mut DeviceHandle) -> Result<SensorValues> {
debug!("send_control_msg()");
send_control_msg(handle)?;
std::thread::sleep(Duration::from_secs(1));
let mut buf = [0u8; 7];
let timeout = Duration::from_secs(5);
match handle.read_interrupt(SENSOR_ENDPOINT, &mut buf, timeout) {
Ok(len) if len == buf.len() => {
debug!("read buf: {:x?}", buf);
Ok(parse_sensor_values(&buf))
}
Ok(len) => Err(UsbrhError::ReadError(format!("{} != {}", len, buf.len()))),
Err(err) => Err(UsbrhError::from(err)),
}
}
match configure_endpoint(handle, &endpoint) {
Ok(_) => {
let has_kernel_driver = match handle.kernel_driver_active(endpoint.interface) {
Ok(true) => {
debug!("kernel driver is active");
handle.detach_kernel_driver(endpoint.interface)?;
true
}
_ => false,
};
debug!("kernel driver? {}", has_kernel_driver);
let ret = read(handle);
if has_kernel_driver {
handle.attach_kernel_driver(endpoint.interface)?;
}
ret
}
Err(err) => Err(UsbrhError::from(err)),
}
}
fn configure_endpoint(handle: &mut DeviceHandle, endpoint: &Endpoint) -> UsbResult<()> {
debug!("set_active_configuration({})", endpoint.config);
handle.set_active_configuration(endpoint.config)?;
debug!("claim_interface({})", endpoint.interface);
handle.claim_interface(endpoint.interface)?;
debug!(
"set_alternate_setting({}, {})",
endpoint.interface, endpoint.setting
);
handle.set_alternate_setting(endpoint.interface, endpoint.setting)?;
Ok(())
}
fn parse_sensor_values(buf: &[u8]) -> SensorValues {
assert_eq!(buf.len(), 7);
let temperature = (u32::from(buf[2]) << 8) | u32::from(buf[3]);
let temperature = D1 + D2 * f64::from(temperature);
let so_rh = f64::from(u32::from(buf[0]) << 8 | u32::from(buf[1]));
let rh_linear = C1 + (C2 * so_rh) + (C3 * (so_rh * so_rh));
let rh_true = (temperature - 25.0) * (T1 + T2 * so_rh) + rh_linear;
SensorValues {
temperature,
humidity: if rh_true >= 99.0 { 100.0 } else { rh_true },
}
}