From 0a7abe7fdbac966d72e1d055a04b622540e9252e Mon Sep 17 00:00:00 2001 From: Kaisei Yokoyama Date: Sat, 23 May 2020 21:47:32 +0900 Subject: [PATCH 1/6] Update Cargo.toml --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 1adfdc5..466342b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ repository = "https://github.com/KaiseiYokoyama/joycon-rs" edition = "2018" description = " a framework for dealing with Nintendo Switch Joy-Con on Rust easily and efficiently" readme = "README.md" +categories = ["game-development", "hardware-support"] keywords = ["nintendo_switch", "joycon", "gamedev", "hid", "bluetooth"] license = "Apache-2.0" exclude = ["/images/*"] From 35366a7b11546be64e970ccdd68b343384aefa91 Mon Sep 17 00:00:00 2001 From: Kaisei Yokoyama Date: Thu, 28 May 2020 22:00:22 +0900 Subject: [PATCH 2/6] #27 Read stick calibration Factory / User --- examples/scan_for_joycons.rs | 1 + src/joycon/device.rs | 193 ++++++++++++++++++++++++++++++++++- src/lib.rs | 1 + 3 files changed, 193 insertions(+), 2 deletions(-) diff --git a/examples/scan_for_joycons.rs b/examples/scan_for_joycons.rs index 37bf2ba..aaea07f 100644 --- a/examples/scan_for_joycons.rs +++ b/examples/scan_for_joycons.rs @@ -19,6 +19,7 @@ fn main() -> JoyConResult<()> { devices.iter() .try_for_each::<_, JoyConResult<()>>(|d| { if let Ok(device) = d.lock() { + dbg!(&device); let device: &HidDevice = device.deref().try_into()?; println!("{:?}", device.get_product_string()?); } diff --git a/src/joycon/device.rs b/src/joycon/device.rs index 08fc63a..da41dcf 100644 --- a/src/joycon/device.rs +++ b/src/joycon/device.rs @@ -8,10 +8,183 @@ pub enum JoyConDeviceType { ProCon = 2, } +pub mod calibration { + use super::*; + + pub mod stick { + use super::*; + + #[derive(Debug, Clone, Hash, Eq, PartialEq)] + pub struct AxisCalibration { + max: i16, + center: i16, + min: i16, + } + + impl AxisCalibration { + /// Max above center + pub fn max(&self) -> i16 { + self.max + } + + /// Center + pub fn center(&self) -> i16 { + self.center + } + + /// Min above center + pub fn min(&self) -> i16 { + self.min + } + } + + #[derive(Debug, Clone, Hash, Eq, PartialEq)] + pub struct StickCalibration { + x: AxisCalibration, + y: AxisCalibration, + } + + impl StickCalibration { + pub fn x(&self) -> &AxisCalibration { + &self.x + } + + pub fn y(&self) -> &AxisCalibration { + &self.y + } + } + + #[derive(Debug, Clone, Hash, Eq, PartialEq)] + pub struct JoyConSticksCalibration { + left: Option, + right: Option, + } + + impl JoyConSticksCalibration { + pub fn left(&self) -> &Option { + &self.left + } + + pub fn right(&self) -> &Option { + &self.right + } + } + + impl From<[u8; 18]> for JoyConSticksCalibration { + fn from(stick_cal: [u8; 18]) -> Self { + fn stick_cal_to_data(stick_cal: &[u8]) -> [u16; 6] { + let mut data = [0u16; 6]; + + data[0] = ((stick_cal[1] as u16) << 8) & 0xF00 | stick_cal[0] as u16; + data[1] = ((stick_cal[2] as u16) << 4) | ((stick_cal[1] as u16) >> 4); + data[2] = ((stick_cal[4] as u16) << 8) & 0xF00 | stick_cal[3] as u16; + data[3] = ((stick_cal[5] as u16) << 4) | ((stick_cal[4] as u16) >> 4); + data[4] = ((stick_cal[7] as u16) << 8) & 0xF00 | stick_cal[6] as u16; + data[5] = ((stick_cal[8] as u16) << 4) | ((stick_cal[7] as u16) >> 4); + + data + } + + let left_stick_calibration = { + let left_stick_cal = &stick_cal[0..9]; + if left_stick_cal.iter() + .all(|u| u == &0xFF) { + None + } else { + let left_stick_data = stick_cal_to_data(&stick_cal[0..9]); + + let left_stick_calibration = + StickCalibration { + x: AxisCalibration { + center: left_stick_data[2] as i16, + max: left_stick_data[2] as i16 + left_stick_data[0] as i16, + min: left_stick_data[2] as i16 - left_stick_data[4] as i16, + }, + y: AxisCalibration { + center: left_stick_data[3] as i16, + max: left_stick_data[3] as i16 + left_stick_data[1] as i16, + min: left_stick_data[3] as i16 - left_stick_data[5] as i16, + }, + }; + + Some(left_stick_calibration) + } + }; + + let right_stick_calibration = { + let right_stick_cal = &stick_cal[9..18]; + if right_stick_cal.iter() + .all(|u| u == &0xFF) { + None + } else { + let right_stick_data = stick_cal_to_data(right_stick_cal); + + let right_stick_calibration = + StickCalibration { + x: AxisCalibration { + center: right_stick_data[0] as i16, + max: right_stick_data[0] as i16 + right_stick_data[4] as i16, + min: right_stick_data[0] as i16 - right_stick_data[2] as i16, + }, + y: AxisCalibration { + center: right_stick_data[1] as i16, + max: right_stick_data[1] as i16 + right_stick_data[5] as i16, + min: right_stick_data[1] as i16 - right_stick_data[3] as i16, + }, + }; + + Some(right_stick_calibration) + } + }; + + JoyConSticksCalibration { + left: left_stick_calibration, + right: right_stick_calibration, + } + } + } + + pub fn get_factory_calibration(device: &HidDevice) -> Option { + device.write(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x60, 0, 0, 18]) + .ok()?; + + let mut buf = [0u8; 64]; + device.read(&mut buf) + .ok()?; + + let mut report = [0u8; 18]; + report.copy_from_slice(&buf[20..38]); + + Some(JoyConSticksCalibration::from(report)) + } + + pub fn get_user_calibration(device: &HidDevice) -> Option { + device.write(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x12, 0x80, 0, 0, 20]) + .ok()?; + + let mut buf = [0u8; 64]; + device.read(&mut buf) + .ok()?; + + let mut report = [0u8; 18]; + { + let (left, right) = report.split_at_mut(9); + left.copy_from_slice(&buf[20..29]); + right.copy_from_slice(&buf[31..40]); + } + + Some(JoyConSticksCalibration::from(report)) + } + } +} + + pub struct JoyConDevice { hid_device: Option, serial_number: String, device_type: JoyConDeviceType, + stick_factory_calibration: calibration::stick::JoyConSticksCalibration, + stick_user_calibration: calibration::stick::JoyConSticksCalibration, } impl JoyConDevice { @@ -41,6 +214,14 @@ impl JoyConDevice { &self.serial_number } + pub fn stick_factory_calibration(&self) -> &calibration::stick::JoyConSticksCalibration { + &self.stick_factory_calibration + } + + pub fn stick_user_calibration(&self) -> &calibration::stick::JoyConSticksCalibration { + &self.stick_user_calibration + } + /// Set blocking mode. /// /// # Notice @@ -73,12 +254,18 @@ impl JoyConDevice { let hid_device = hidapi.open_serial(device_info.vendor_id(), device_info.product_id(), serial)?; + let stick_factory_calibration = calibration::stick::get_factory_calibration(&hid_device) + .ok_or(JoyConDeviceError::FailedStickCalibrationLoading)?; + let stick_user_calibration = calibration::stick::get_user_calibration(&hid_device) + .ok_or(JoyConDeviceError::FailedStickCalibrationLoading)?; Ok( JoyConDevice { hid_device: Some(hid_device), serial_number: serial.to_string(), device_type, + stick_factory_calibration, + stick_user_calibration, } ) } @@ -123,12 +310,14 @@ impl JoyConDevice { impl Debug for JoyConDevice { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - writeln!(f, "JoyConDevice {{ hid_device: {}, serial_number: {}, device_type: {:?} }}", + writeln!(f, "JoyConDevice {{ hid_device: {}, serial_number: {}, device_type: {:?}, stick_factory_calibration: {:?}, stick_user_calibration: {:?} }}", if self.is_connected() { "Connected" } else { "Disconnected" }, &self.serial_number, - &self.device_type + &self.device_type, + &self.stick_factory_calibration, + &self.stick_user_calibration, ) } } diff --git a/src/lib.rs b/src/lib.rs index 8d8bd95..7303d55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -186,6 +186,7 @@ pub mod result { pub enum JoyConDeviceError { InvalidVendorID(u16), InvalidProductID(u16), + FailedStickCalibrationLoading, } impl From for JoyConError { From 548f4af08f3d3df7b88ca237a557302771c50811 Mon Sep 17 00:00:00 2001 From: Kaisei Yokoyama Date: Fri, 29 May 2020 12:18:58 +0900 Subject: [PATCH 3/6] #27 Read IMU calibration --- src/joycon/device.rs | 300 +++++++++++++++++++++++++++++++++---------- src/lib.rs | 1 + 2 files changed, 232 insertions(+), 69 deletions(-) diff --git a/src/joycon/device.rs b/src/joycon/device.rs index da41dcf..afa28fc 100644 --- a/src/joycon/device.rs +++ b/src/joycon/device.rs @@ -39,33 +39,26 @@ pub mod calibration { } #[derive(Debug, Clone, Hash, Eq, PartialEq)] - pub struct StickCalibration { - x: AxisCalibration, - y: AxisCalibration, + pub enum StickCalibration { + Available { + x: AxisCalibration, + y: AxisCalibration, + }, + Unavailable, } - - impl StickCalibration { - pub fn x(&self) -> &AxisCalibration { - &self.x - } - - pub fn y(&self) -> &AxisCalibration { - &self.y - } - } - + #[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct JoyConSticksCalibration { - left: Option, - right: Option, + left: StickCalibration, + right: StickCalibration, } impl JoyConSticksCalibration { - pub fn left(&self) -> &Option { + pub fn left(&self) -> &StickCalibration { &self.left } - pub fn right(&self) -> &Option { + pub fn right(&self) -> &StickCalibration { &self.right } } @@ -87,53 +80,47 @@ pub mod calibration { let left_stick_calibration = { let left_stick_cal = &stick_cal[0..9]; - if left_stick_cal.iter() - .all(|u| u == &0xFF) { - None + if left_stick_cal.iter().all(|u| u == &0xFF) + { + StickCalibration::Unavailable } else { let left_stick_data = stick_cal_to_data(&stick_cal[0..9]); - let left_stick_calibration = - StickCalibration { - x: AxisCalibration { - center: left_stick_data[2] as i16, - max: left_stick_data[2] as i16 + left_stick_data[0] as i16, - min: left_stick_data[2] as i16 - left_stick_data[4] as i16, - }, - y: AxisCalibration { - center: left_stick_data[3] as i16, - max: left_stick_data[3] as i16 + left_stick_data[1] as i16, - min: left_stick_data[3] as i16 - left_stick_data[5] as i16, - }, - }; - - Some(left_stick_calibration) + StickCalibration::Available { + x: AxisCalibration { + center: left_stick_data[2] as i16, + max: left_stick_data[2] as i16 + left_stick_data[0] as i16, + min: left_stick_data[2] as i16 - left_stick_data[4] as i16, + }, + y: AxisCalibration { + center: left_stick_data[3] as i16, + max: left_stick_data[3] as i16 + left_stick_data[1] as i16, + min: left_stick_data[3] as i16 - left_stick_data[5] as i16, + }, + } } }; let right_stick_calibration = { let right_stick_cal = &stick_cal[9..18]; - if right_stick_cal.iter() - .all(|u| u == &0xFF) { - None + if right_stick_cal.iter().all(|u| u == &0xFF) + { + StickCalibration::Unavailable } else { let right_stick_data = stick_cal_to_data(right_stick_cal); - let right_stick_calibration = - StickCalibration { - x: AxisCalibration { - center: right_stick_data[0] as i16, - max: right_stick_data[0] as i16 + right_stick_data[4] as i16, - min: right_stick_data[0] as i16 - right_stick_data[2] as i16, - }, - y: AxisCalibration { - center: right_stick_data[1] as i16, - max: right_stick_data[1] as i16 + right_stick_data[5] as i16, - min: right_stick_data[1] as i16 - right_stick_data[3] as i16, - }, - }; - - Some(right_stick_calibration) + StickCalibration::Available { + x: AxisCalibration { + center: right_stick_data[0] as i16, + max: right_stick_data[0] as i16 + right_stick_data[4] as i16, + min: right_stick_data[0] as i16 - right_stick_data[2] as i16, + }, + y: AxisCalibration { + center: right_stick_data[1] as i16, + max: right_stick_data[1] as i16 + right_stick_data[5] as i16, + min: right_stick_data[1] as i16 - right_stick_data[3] as i16, + }, + } } }; @@ -148,32 +135,189 @@ pub mod calibration { device.write(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x60, 0, 0, 18]) .ok()?; - let mut buf = [0u8; 64]; - device.read(&mut buf) - .ok()?; + for _ in 0..5 { + let mut buf = [0u8; 64]; + device.read_timeout(&mut buf, 20) + .ok()?; + + match buf[14..20] { + [0x10, 0x3D, 0x60, 0, 0, 18] => {} + _ => continue, + } - let mut report = [0u8; 18]; - report.copy_from_slice(&buf[20..38]); + let mut report = [0u8; 18]; + report.copy_from_slice(&buf[20..38]); - Some(JoyConSticksCalibration::from(report)) + return Some(JoyConSticksCalibration::from(report)); + } + + None } pub fn get_user_calibration(device: &HidDevice) -> Option { device.write(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x12, 0x80, 0, 0, 20]) .ok()?; - let mut buf = [0u8; 64]; - device.read(&mut buf) + for _ in 0..5 { + let mut buf = [0u8; 64]; + device.read_timeout(&mut buf, 20) + .ok()?; + + match buf[14..20] { + [0x10, 0x12, 0x80, 0, 0, 20] => {} + _ => continue, + } + + let mut report = [0u8; 18]; + { + let (left, right) = report.split_at_mut(9); + left.copy_from_slice(&buf[20..29]); + right.copy_from_slice(&buf[31..40]); + } + + return Some(JoyConSticksCalibration::from(report)); + } + + None + } + } + + pub mod imu { + use super::*; + use std::fmt::Debug; + use std::hash::Hash; + + #[derive(Debug)] + pub struct XYZ { + pub x: T, + pub y: T, + pub z: T, + } + + impl Clone for XYZ where T: Debug + Clone { + fn clone(&self) -> Self { + Self { + x: self.x.clone(), + y: self.y.clone(), + z: self.z.clone(), + } + } + } + + impl Hash for XYZ where T: Debug + Hash { + fn hash(&self, state: &mut H) { + self.x.hash(state); + self.y.hash(state); + self.z.hash(state); + } + } + + impl PartialEq for XYZ where T: Debug + PartialEq { + fn eq(&self, other: &Self) -> bool { + self.x == other.x + && self.y == other.y + && self.z == other.z + } + } + + impl Eq for XYZ where T: Debug + Eq {} + + #[derive(Debug, Clone, Hash, Eq, PartialEq)] + pub enum IMUCalibration { + Available { + /// Acc XYZ origin position when completely horizontal and stick is upside + acc_origin_position: XYZ, + /// Acc XYZ sensitivity special coeff, for default sensitivity: ±8G. + acc_sensitivity_special_coeff: XYZ, + /// Gyro XYZ origin position when still + gyro_origin_position: XYZ, + /// Gyro XYZ sensitivity special coeff, for default sensitivity: ±2000dps. + gyro_sensitivity_special_coeff: XYZ, + }, + Unavailable, + } + + impl From<[u8; 24]> for IMUCalibration { + fn from(value: [u8; 24]) -> Self { + use std::slice::Iter; + use std::iter::Cloned; + + if value.iter().all(|v| v == &0xFF) { + return IMUCalibration::Unavailable; + } + + fn convert(little: u8, big: u8) -> i16 { + i16::from_be_bytes([big, little]) + } + + fn iter_to_xyz_i16(iter: &mut Cloned>) -> XYZ { + let x = convert(iter.next().unwrap(), iter.next().unwrap()); + let y = convert(iter.next().unwrap(), iter.next().unwrap()); + let z = convert(iter.next().unwrap(), iter.next().unwrap()); + + XYZ { x, y, z } + } + + let mut iter = value.iter().cloned(); + + let acc_origin_position = iter_to_xyz_i16(&mut iter); + let acc_sensitivity_special_coeff = iter_to_xyz_i16(&mut iter); + let gyro_origin_position = iter_to_xyz_i16(&mut iter); + let gyro_sensitivity_special_coeff = iter_to_xyz_i16(&mut iter); + + IMUCalibration::Available { + acc_origin_position, + acc_sensitivity_special_coeff, + gyro_origin_position, + gyro_sensitivity_special_coeff, + } + } + } + + pub fn get_factory_calibration(device: &HidDevice) -> Option { + device.write(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x20, 0x60, 0, 0, 24]) + .ok()?; + + for _ in 0..5 { + let mut buf = [0u8; 64]; + device.read_timeout(&mut buf, 20) + .ok()?; + + match buf[14..20] { + [0x10, 0x20, 0x60, 0, 0, 24] => {} + _ => continue, + } + + let mut report = [0u8; 24]; + report.copy_from_slice(&buf[20..44]); + + return Some(IMUCalibration::from(report)); + } + + None + } + + pub fn get_user_calibration(device: &HidDevice) -> Option { + device.write(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x28, 0x80, 0, 0, 24]) .ok()?; - let mut report = [0u8; 18]; - { - let (left, right) = report.split_at_mut(9); - left.copy_from_slice(&buf[20..29]); - right.copy_from_slice(&buf[31..40]); + for _ in 0..5 { + let mut buf = [0u8; 64]; + device.read_timeout(&mut buf, 20) + .ok()?; + + match buf[14..20] { + [0x10, 0x28, 0x80, 0, 0, 24] => {} + _ => continue, + } + + let mut report = [0u8; 24]; + report.copy_from_slice(&buf[20..44]); + + return Some(IMUCalibration::from(report)); } - Some(JoyConSticksCalibration::from(report)) + None } } } @@ -185,6 +329,8 @@ pub struct JoyConDevice { device_type: JoyConDeviceType, stick_factory_calibration: calibration::stick::JoyConSticksCalibration, stick_user_calibration: calibration::stick::JoyConSticksCalibration, + imu_factory_calibration: calibration::imu::IMUCalibration, + imu_user_calibration: calibration::imu::IMUCalibration, } impl JoyConDevice { @@ -222,6 +368,14 @@ impl JoyConDevice { &self.stick_user_calibration } + pub fn imu_factory_calibration(&self) -> &calibration::imu::IMUCalibration { + &self.imu_factory_calibration + } + + pub fn imu_user_calibration(&self) -> &calibration::imu::IMUCalibration { + &self.imu_user_calibration + } + /// Set blocking mode. /// /// # Notice @@ -258,6 +412,10 @@ impl JoyConDevice { .ok_or(JoyConDeviceError::FailedStickCalibrationLoading)?; let stick_user_calibration = calibration::stick::get_user_calibration(&hid_device) .ok_or(JoyConDeviceError::FailedStickCalibrationLoading)?; + let imu_factory_calibration = calibration::imu::get_factory_calibration(&hid_device) + .ok_or(JoyConDeviceError::FailedIMUCalibrationLoading)?; + let imu_user_calibration = calibration::imu::get_user_calibration(&hid_device) + .ok_or(JoyConDeviceError::FailedIMUCalibrationLoading)?; Ok( JoyConDevice { @@ -266,6 +424,8 @@ impl JoyConDevice { device_type, stick_factory_calibration, stick_user_calibration, + imu_factory_calibration, + imu_user_calibration, } ) } @@ -310,7 +470,7 @@ impl JoyConDevice { impl Debug for JoyConDevice { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - writeln!(f, "JoyConDevice {{ hid_device: {}, serial_number: {}, device_type: {:?}, stick_factory_calibration: {:?}, stick_user_calibration: {:?} }}", + writeln!(f, "JoyConDevice {{ hid_device: {}, serial_number: {}, device_type: {:?}, stick_factory_calibration: {:?}, stick_user_calibration: {:?}, imu_factory_calibration: {:?}, imu_user_calibration: {:?} }}", if self.is_connected() { "Connected" } else { "Disconnected" }, @@ -318,6 +478,8 @@ impl Debug for JoyConDevice { &self.device_type, &self.stick_factory_calibration, &self.stick_user_calibration, + &self.imu_factory_calibration, + &self.imu_user_calibration ) } } diff --git a/src/lib.rs b/src/lib.rs index 7303d55..0ab7a81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -187,6 +187,7 @@ pub mod result { InvalidVendorID(u16), InvalidProductID(u16), FailedStickCalibrationLoading, + FailedIMUCalibrationLoading, } impl From for JoyConError { From 9bc7dee30054d950a7ab3c17c57540f52b2606dd Mon Sep 17 00:00:00 2001 From: Kaisei Yokoyama Date: Fri, 29 May 2020 13:58:47 +0900 Subject: [PATCH 4/6] #53 Read colors from device --- src/joycon/device.rs | 73 ++++++++++++++++++++++++++++++++++++++++++-- src/lib.rs | 1 + 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/joycon/device.rs b/src/joycon/device.rs index afa28fc..d3de4c5 100644 --- a/src/joycon/device.rs +++ b/src/joycon/device.rs @@ -46,7 +46,7 @@ pub mod calibration { }, Unavailable, } - + #[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct JoyConSticksCalibration { left: StickCalibration, @@ -322,6 +322,66 @@ pub mod calibration { } } +pub mod color { + use super::*; + + #[derive(Debug, Clone, Hash, Eq, PartialEq)] + pub struct Color { + /// Controller body color. ex. [30, 220, 0] (Splatoon2 green) + pub body: [u8; 3], + pub buttons: [u8; 3], + pub left_grip: Option<[u8; 3]>, + pub right_grip: Option<[u8; 3]>, + } + + impl From<[u8; 12]> for Color { + fn from(array: [u8; 12]) -> Self { + let body = [array[0], array[1], array[2]]; + let buttons = [array[3], array[4], array[5]]; + let left_grip = if array[6..9].iter().all(|a| a == &0xFF) { + None + } else { + Some([array[6], array[7], array[8]]) + }; + let right_grip = if array[9..12].iter().all(|a| a == &0xFF) { + None + } else { + Some([array[9], array[10], array[11]]) + }; + + Color { + body, + buttons, + left_grip, + right_grip, + } + } + } + + pub fn get_color(device: &HidDevice) -> Option { + device.write(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x50, 0x60, 0, 0, 12]) + .ok()?; + + for _ in 0..5 { + let mut buf = [0u8; 64]; + device.read_timeout(&mut buf, 20) + .ok()?; + + match buf[14..20] { + [0x10, 0x50, 0x60, 0, 0, 12] => {} + _ => continue, + } + + let mut report = [0u8; 12]; + report.copy_from_slice(&buf[20..32]); + + return Some(Color::from(report)); + } + + None + } +} + pub struct JoyConDevice { hid_device: Option, @@ -331,6 +391,7 @@ pub struct JoyConDevice { stick_user_calibration: calibration::stick::JoyConSticksCalibration, imu_factory_calibration: calibration::imu::IMUCalibration, imu_user_calibration: calibration::imu::IMUCalibration, + color: color::Color, } impl JoyConDevice { @@ -376,6 +437,8 @@ impl JoyConDevice { &self.imu_user_calibration } + pub fn color(&self) -> &color::Color { &self.color } + /// Set blocking mode. /// /// # Notice @@ -416,6 +479,8 @@ impl JoyConDevice { .ok_or(JoyConDeviceError::FailedIMUCalibrationLoading)?; let imu_user_calibration = calibration::imu::get_user_calibration(&hid_device) .ok_or(JoyConDeviceError::FailedIMUCalibrationLoading)?; + let color = color::get_color(&hid_device) + .ok_or(JoyConDeviceError::FailedColorLoading)?; Ok( JoyConDevice { @@ -426,6 +491,7 @@ impl JoyConDevice { stick_user_calibration, imu_factory_calibration, imu_user_calibration, + color, } ) } @@ -470,7 +536,7 @@ impl JoyConDevice { impl Debug for JoyConDevice { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - writeln!(f, "JoyConDevice {{ hid_device: {}, serial_number: {}, device_type: {:?}, stick_factory_calibration: {:?}, stick_user_calibration: {:?}, imu_factory_calibration: {:?}, imu_user_calibration: {:?} }}", + writeln!(f, "JoyConDevice {{ hid_device: {}, serial_number: {}, device_type: {:?}, stick_factory_calibration: {:?}, stick_user_calibration: {:?}, imu_factory_calibration: {:?}, imu_user_calibration: {:?}, color: {:?} }}", if self.is_connected() { "Connected" } else { "Disconnected" }, @@ -479,7 +545,8 @@ impl Debug for JoyConDevice { &self.stick_factory_calibration, &self.stick_user_calibration, &self.imu_factory_calibration, - &self.imu_user_calibration + &self.imu_user_calibration, + &self.color, ) } } diff --git a/src/lib.rs b/src/lib.rs index 0ab7a81..76171fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -188,6 +188,7 @@ pub mod result { InvalidProductID(u16), FailedStickCalibrationLoading, FailedIMUCalibrationLoading, + FailedColorLoading, } impl From for JoyConError { From 095f395d9074aa841708b3987fd19af5044d31c3 Mon Sep 17 00:00:00 2001 From: Kaisei Yokoyama Date: Fri, 29 May 2020 22:23:47 +0900 Subject: [PATCH 5/6] #51 impl --- src/joycon/device.rs | 125 ++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 2 + 2 files changed, 126 insertions(+), 1 deletion(-) diff --git a/src/joycon/device.rs b/src/joycon/device.rs index d3de4c5..03c3711 100644 --- a/src/joycon/device.rs +++ b/src/joycon/device.rs @@ -180,6 +180,75 @@ pub mod calibration { None } + + #[derive(Debug, Clone, Hash, Eq, PartialEq)] + pub struct StickParameters { + dead_zone: u16, + range_ratio: u16, + } + + impl StickParameters { + pub fn dead_zone(&self) -> u16 { + self.dead_zone + } + + pub fn range_ratio(&self) -> u16 { + self.range_ratio + } + } + + impl From<[u8; 18]> for StickParameters { + fn from(array: [u8; 18]) -> Self { + fn decode(stick_cal: &[u8]) -> [u16; 12] { + let mut data = [0u16; 12]; + + data[0] = ((stick_cal[1] as u16) << 8) & 0xF00 | stick_cal[0] as u16; + data[1] = ((stick_cal[2] as u16) << 4) | ((stick_cal[1] as u16) >> 4); + data[2] = ((stick_cal[4] as u16) << 8) & 0xF00 | stick_cal[3] as u16; + data[3] = ((stick_cal[5] as u16) << 4) | ((stick_cal[4] as u16) >> 4); + data[4] = ((stick_cal[7] as u16) << 8) & 0xF00 | stick_cal[6] as u16; + data[5] = ((stick_cal[8] as u16) << 4) | ((stick_cal[7] as u16) >> 4); + data[6] = ((stick_cal[10] as u16) << 8) & 0xF00 | stick_cal[9] as u16; + data[7] = ((stick_cal[11] as u16) << 4) | ((stick_cal[10] as u16) >> 4); + data[8] = ((stick_cal[13] as u16) << 8) & 0xF00 | stick_cal[12] as u16; + data[9] = ((stick_cal[14] as u16) << 4) | ((stick_cal[13] as u16) >> 4); + data[10] = ((stick_cal[16] as u16) << 8) & 0xF00 | stick_cal[15] as u16; + data[11] = ((stick_cal[17] as u16) << 4) | ((stick_cal[16] as u16) >> 4); + + data + } + + let decoded = decode(&array); + + StickParameters { + dead_zone: decoded[2], + range_ratio: decoded[3], + } + } + } + + pub fn get_parameters(device: &HidDevice) -> Option { + device.write(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x86, 0x60, 0, 0, 18]) + .ok()?; + + for _ in 0..5 { + let mut buf = [0u8; 64]; + device.read_timeout(&mut buf, 20) + .ok()?; + + match buf[14..20] { + [0x10, 0x86, 0x60, 0, 0, 18] => {} + _ => continue, + } + + let mut report = [0u8; 18]; + report.copy_from_slice(&buf[20..38]); + + return Some(StickParameters::from(report)); + } + + None + } } pub mod imu { @@ -319,6 +388,50 @@ pub mod calibration { None } + + #[derive(Debug, Clone, Hash, Eq, PartialEq)] + pub struct IMUOffsets { + x: i16, + y: i16, + z: i16, + } + + impl From<[u8; 6]> for IMUOffsets { + fn from(array: [u8; 6]) -> Self { + let x = i16::from_le_bytes([array[0], array[1]]); + let y = i16::from_le_bytes([array[2], array[3]]); + let z = i16::from_le_bytes([array[4], array[5]]); + + IMUOffsets { + x, + y, + z, + } + } + } + + pub fn get_offsets(device: &HidDevice) -> Option { + device.write(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x60, 0, 0, 6]) + .ok()?; + + for _ in 0..5 { + let mut buf = [0u8; 64]; + device.read_timeout(&mut buf, 20) + .ok()?; + + match buf[14..20] { + [0x10, 0x80, 0x60, 0, 0, 6] => {} + _ => continue, + } + + let mut report = [0u8; 6]; + report.copy_from_slice(&buf[20..26]); + + return Some(IMUOffsets::from(report)); + } + + None + } } } @@ -387,8 +500,10 @@ pub struct JoyConDevice { hid_device: Option, serial_number: String, device_type: JoyConDeviceType, + stick_parameters: calibration::stick::StickParameters, stick_factory_calibration: calibration::stick::JoyConSticksCalibration, stick_user_calibration: calibration::stick::JoyConSticksCalibration, + imu_offsets: calibration::imu::IMUOffsets, imu_factory_calibration: calibration::imu::IMUCalibration, imu_user_calibration: calibration::imu::IMUCalibration, color: color::Color, @@ -471,10 +586,14 @@ impl JoyConDevice { let hid_device = hidapi.open_serial(device_info.vendor_id(), device_info.product_id(), serial)?; + let stick_parameters = calibration::stick::get_parameters(&hid_device) + .ok_or(JoyConDeviceError::FailedStickParameterLoading)?; let stick_factory_calibration = calibration::stick::get_factory_calibration(&hid_device) .ok_or(JoyConDeviceError::FailedStickCalibrationLoading)?; let stick_user_calibration = calibration::stick::get_user_calibration(&hid_device) .ok_or(JoyConDeviceError::FailedStickCalibrationLoading)?; + let imu_offsets = calibration::imu::get_offsets(&hid_device) + .ok_or(JoyConDeviceError::FailedIMUOffsetsLoading)?; let imu_factory_calibration = calibration::imu::get_factory_calibration(&hid_device) .ok_or(JoyConDeviceError::FailedIMUCalibrationLoading)?; let imu_user_calibration = calibration::imu::get_user_calibration(&hid_device) @@ -487,8 +606,10 @@ impl JoyConDevice { hid_device: Some(hid_device), serial_number: serial.to_string(), device_type, + stick_parameters, stick_factory_calibration, stick_user_calibration, + imu_offsets, imu_factory_calibration, imu_user_calibration, color, @@ -536,14 +657,16 @@ impl JoyConDevice { impl Debug for JoyConDevice { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - writeln!(f, "JoyConDevice {{ hid_device: {}, serial_number: {}, device_type: {:?}, stick_factory_calibration: {:?}, stick_user_calibration: {:?}, imu_factory_calibration: {:?}, imu_user_calibration: {:?}, color: {:?} }}", + writeln!(f, "JoyConDevice {{ hid_device: {}, serial_number: {}, device_type: {:?}, stick_parameters: {:?}, , stick_factory_calibration: {:?}, stick_user_calibration: {:?}, imu_offsets: {:?}, imu_factory_calibration: {:?}, imu_user_calibration: {:?}, color: {:?} }}", if self.is_connected() { "Connected" } else { "Disconnected" }, &self.serial_number, &self.device_type, + &self.stick_parameters, &self.stick_factory_calibration, &self.stick_user_calibration, + &self.imu_offsets, &self.imu_factory_calibration, &self.imu_user_calibration, &self.color, diff --git a/src/lib.rs b/src/lib.rs index 76171fd..630d49b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -186,7 +186,9 @@ pub mod result { pub enum JoyConDeviceError { InvalidVendorID(u16), InvalidProductID(u16), + FailedStickParameterLoading, FailedStickCalibrationLoading, + FailedIMUOffsetsLoading, FailedIMUCalibrationLoading, FailedColorLoading, } From 52b459602d6b18808bb1c6a1e21a3024c29878a4 Mon Sep 17 00:00:00 2001 From: Kaisei Yokoyama Date: Fri, 29 May 2020 22:26:43 +0900 Subject: [PATCH 6/6] Update Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 466342b..ca94797 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "joycon-rs" -version = "0.5.0-alpha" +version = "0.5.1" authors = ["Kaisei Yokoyama "] repository = "https://github.com/KaiseiYokoyama/joycon-rs" edition = "2018"