commit ec7edd02094395b9635a7a0024f3209b201f478b Author: hodasemi Date: Fri Dec 8 22:52:15 2023 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..34fa140 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +Cargo.lock +target/ +sample/build/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..03829f3 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "workbench.colorCustomizations": { + "activityBar.background": "#043617", + "titleBar.activeBackground": "#064B21", + "titleBar.activeForeground": "#EFFEF5" + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..88381fe --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,14 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "cargo", + "command": "build", + "problemMatcher": [ + "$rustc" + ], + "group": "build", + "label": "rust: cargo build" + } + ] +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6e1c122 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "viture_rs_sdk" +version = "0.1.0" +edition = "2021" + +[dependencies] +shared_library = "0.1.9" +paste = "1.0.14" +anyhow = { version = "1.0.75", features = ["backtrace"] } +library_loader = { git = "ssh://gitea@gavania.de:23/hodasemi/library_loader.git" } \ No newline at end of file diff --git a/README b/README new file mode 100644 index 0000000..4202a24 --- /dev/null +++ b/README @@ -0,0 +1,36 @@ +The VITURE One SDK for Linux offers a set of APIs designed to enable the VITURE One XR Glasses’ head tracking features. This SDK empowers developers to seamlessly access IMU (Inertial Measurement Unit) data. + +Key functionalities and features include: + +1. USB Initialization & Device Access: + +Enables initialization of the USB library to access and interact with the XR Glasses. + +2. IMU Data Handling: + +Facilitates the reception and handling of Inertial Measurement Unit (IMU) data. +Allows control over IMU event reporting, enabling or pausing IMU data stream. + +3. Event Callbacks: + +Supports callback mechanisms to receive events and changes from the XR Glasses. +Callbacks for IMU data and other event changes provide flexibility in handling device responses. + +4. Device Control: + +Provides methods to control the XR Glasses states, such as switching resolutions (e.g., 3840x1080 or 1920x1080). + +5. Error Handling: + +Defines error codes and error states for various command executions, allowing developers to handle potential issues gracefully. + +6. Resource Management: + +Includes functions to release resources, ensuring proper cleanup post-usage. + +Overall, the SDK empowers developers to initialize, control, and interact with the XR Glasses, managing IMU data, device states, and event callbacks effectively while handling potential errors. + + +API Reference: For detailed API information, kindly refer to the respective *.h file. + + diff --git a/include/viture.h b/include/viture.h new file mode 100644 index 0000000..869bf84 --- /dev/null +++ b/include/viture.h @@ -0,0 +1,104 @@ +#ifndef GLASSES_CONTROLLER_AIR_OTA_H +#define GLASSES_CONTROLLER_AIR_OTA_H + +/** + * A typical sequence might resemble the following: + * @code + * 1. init the usblib and provide the IMU callback: + * @ref init(CallbackIMU imuCallback, CallbackMCU mcuCallback) + * 2. Control the IMU data reading thread by enabling or pausing it: + * @set_imu(bool onOff); + * + * 3. Release the USB resources: + * @ref deinit() + * + * @endcode + * + */ + + +#ifdef __cplusplus +extern "C" { +#endif + +// The outcome of the command, such as ‘set_imu’, when communicated to the glasses device determines the subsequent action or state. This result signifies the success or failure of the command execution, influencing functionalities like IMU data reporting or device settings. +#define ERR_SUCCESS 0 +#define ERR_FAILURE 1 +#define ERR_INVALID_ARGUMENT 2 +#define ERR_NOT_ENOUGH_MEMORY 3 +#define ERR_UNSUPPORTED_CMD 4 +#define ERR_CRC_MISMATCH 5 +#define ERR_VER_MISMATCH 6 +#define ERR_MSG_ID_MISMATCH 7 +#define ERR_MSG_STX_MISMATCH 8 +#define ERR_CODE_NOT_WRITTEN 9 + +// State Definitions +#define STATE_OPEN 1 +#define STATE_OFF 0 + +#define ERR_WRITE_FAIL -1 +#define ERR_RSP_ERROR -2 +#define ERR_TIMEOUT -3 + + +/** + * Callback for IMU Data + * data: the first 12 byte is the data of IMU, others are reserved + * uint8_t eulerRoll[4]; + * uint8_t eulerPitch[4]; + * uint8_t eulerYaw[4]; + * + * len: the length of data + * ts: the timestamp + */ +typedef void (*CallbackIMU)(uint8_t *data, uint16_t len, uint32_t ts); + +/* + * Callback for event change from the Glasses, such as 3D/2D switch event + * msgid + */ +typedef void (*CallbackMCU)(uint16_t msgid, uint8_t *data, uint16_t len, uint32_t ts); + +/** + * init the usblib and gets the glasses device + * + * imuCallback: the Callback to receive the IMU data + * mcuCallback: the Callback to get the event from the glasses + */ +bool init(CallbackIMU imuCallback, CallbackMCU mcuCallback); + +/** + * Release the resources, paired with init + */ +void deinit(); + +/** + * Opens or pauses the IMU event reporting + * onOff true the glass will report the imu data + */ +int set_imu(bool onOff); + +/** + * Returns the IMU reporting state + * 1 for IMU data reporting is on, 0 for off, <0 for error + */ +int get_imu_state(); + +/** + * Switches the resolution of the glasses + * true: 3840*1080 false: 1920*1080 + */ +int set_3d(bool onOff); + +/** + * Returns the resolution state of the glasses + * 1 for 3840*1080, 0 for 1920*1080, <0 for error + */ +int get_3d_state(); + +#ifdef __cplusplus +} +#endif + +#endif //GLASSES_CONTROLLER_AIR_OTA_H diff --git a/libs/libviture_one_sdk.a b/libs/libviture_one_sdk.a new file mode 100644 index 0000000..24ee1ba Binary files /dev/null and b/libs/libviture_one_sdk.a differ diff --git a/libs/libviture_one_sdk.so b/libs/libviture_one_sdk.so new file mode 100755 index 0000000..ad41c05 Binary files /dev/null and b/libs/libviture_one_sdk.so differ diff --git a/sample/CMakeLists.txt b/sample/CMakeLists.txt new file mode 100644 index 0000000..e875bc1 --- /dev/null +++ b/sample/CMakeLists.txt @@ -0,0 +1,27 @@ +# For more information about using CMake with Android Studio, read the +# documentation: https://d.android.com/studio/projects/add-native-code.html + +# Sets the minimum version of CMake required to build the native library. + +cmake_minimum_required(VERSION 3.10.2) + +project(viture_sdk_demo VERSION 1.0.1) +set(LIB_NAME viture_sdk) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) + + +add_library ( + ${LIB_NAME} + SHARED + IMPORTED) + +set_target_properties( + ${LIB_NAME} PROPERTIES + IMPORTED_LOCATION + ${CMAKE_CURRENT_SOURCE_DIR}/libs/libviture_one_sdk.so) + +add_executable(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c) + +target_link_libraries(${PROJECT_NAME} + ${LIB_NAME}) diff --git a/sample/include/viture.h b/sample/include/viture.h new file mode 100644 index 0000000..869bf84 --- /dev/null +++ b/sample/include/viture.h @@ -0,0 +1,104 @@ +#ifndef GLASSES_CONTROLLER_AIR_OTA_H +#define GLASSES_CONTROLLER_AIR_OTA_H + +/** + * A typical sequence might resemble the following: + * @code + * 1. init the usblib and provide the IMU callback: + * @ref init(CallbackIMU imuCallback, CallbackMCU mcuCallback) + * 2. Control the IMU data reading thread by enabling or pausing it: + * @set_imu(bool onOff); + * + * 3. Release the USB resources: + * @ref deinit() + * + * @endcode + * + */ + + +#ifdef __cplusplus +extern "C" { +#endif + +// The outcome of the command, such as ‘set_imu’, when communicated to the glasses device determines the subsequent action or state. This result signifies the success or failure of the command execution, influencing functionalities like IMU data reporting or device settings. +#define ERR_SUCCESS 0 +#define ERR_FAILURE 1 +#define ERR_INVALID_ARGUMENT 2 +#define ERR_NOT_ENOUGH_MEMORY 3 +#define ERR_UNSUPPORTED_CMD 4 +#define ERR_CRC_MISMATCH 5 +#define ERR_VER_MISMATCH 6 +#define ERR_MSG_ID_MISMATCH 7 +#define ERR_MSG_STX_MISMATCH 8 +#define ERR_CODE_NOT_WRITTEN 9 + +// State Definitions +#define STATE_OPEN 1 +#define STATE_OFF 0 + +#define ERR_WRITE_FAIL -1 +#define ERR_RSP_ERROR -2 +#define ERR_TIMEOUT -3 + + +/** + * Callback for IMU Data + * data: the first 12 byte is the data of IMU, others are reserved + * uint8_t eulerRoll[4]; + * uint8_t eulerPitch[4]; + * uint8_t eulerYaw[4]; + * + * len: the length of data + * ts: the timestamp + */ +typedef void (*CallbackIMU)(uint8_t *data, uint16_t len, uint32_t ts); + +/* + * Callback for event change from the Glasses, such as 3D/2D switch event + * msgid + */ +typedef void (*CallbackMCU)(uint16_t msgid, uint8_t *data, uint16_t len, uint32_t ts); + +/** + * init the usblib and gets the glasses device + * + * imuCallback: the Callback to receive the IMU data + * mcuCallback: the Callback to get the event from the glasses + */ +bool init(CallbackIMU imuCallback, CallbackMCU mcuCallback); + +/** + * Release the resources, paired with init + */ +void deinit(); + +/** + * Opens or pauses the IMU event reporting + * onOff true the glass will report the imu data + */ +int set_imu(bool onOff); + +/** + * Returns the IMU reporting state + * 1 for IMU data reporting is on, 0 for off, <0 for error + */ +int get_imu_state(); + +/** + * Switches the resolution of the glasses + * true: 3840*1080 false: 1920*1080 + */ +int set_3d(bool onOff); + +/** + * Returns the resolution state of the glasses + * 1 for 3840*1080, 0 for 1920*1080, <0 for error + */ +int get_3d_state(); + +#ifdef __cplusplus +} +#endif + +#endif //GLASSES_CONTROLLER_AIR_OTA_H diff --git a/sample/libs/libviture_one_sdk.so b/sample/libs/libviture_one_sdk.so new file mode 100755 index 0000000..ad41c05 Binary files /dev/null and b/sample/libs/libviture_one_sdk.so differ diff --git a/sample/src/main.c b/sample/src/main.c new file mode 100644 index 0000000..1ec92ac --- /dev/null +++ b/sample/src/main.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "viture.h" + +#define LOG(...) fprintf(stderr, __VA_ARGS__) + +static float makeFloat(uint8_t *data) +{ + float value = 0; + uint8_t tem[4]; + tem[0] = data[3]; + tem[1] = data[2]; + tem[2] = data[1]; + tem[3] = data[0]; + memcpy(&value, tem, 4); + return value; +} + +static void imuCallback(uint8_t *data, uint16_t len, uint32_t ts) +{ + LOG("demo imuCallback len %d ts %d\n", len, ts); + for (int i = 0; i < len; i++) { + LOG("%02x", data[i]); + if (i == len -1) { + LOG("\n"); + } + } + float eulerRoll = makeFloat(data); + float eulerPitch = makeFloat(data + 4); + float eulerYaw = makeFloat(data + 8); + + // LOG("demo imuCallback data roll %f pitch %f yaw %f\n", eulerRoll, eulerPitch, eulerYaw); + +} + +static void mcuCallback(uint16_t msgid, uint8_t *data, uint16_t len, uint32_t ts) +{ + LOG("demo mcuCallback len %d ts %d\n", len, ts); + for (int i = 0; i < len; i++) { + LOG("%02x", data[i]); + if (i == len -1) { + LOG("\n"); + } + } + +} + +int main() +{ + init(imuCallback, mcuCallback); + set_imu(true); + + + fd_set read_fds; + struct timeval timeout; + char input_buffer[256]; + + while (1) { + + FD_ZERO(&read_fds); + + FD_SET(STDIN_FILENO, &read_fds); + + timeout.tv_sec = 0; + timeout.tv_usec = 0; + int result; + + int ready = select(STDIN_FILENO + 1, &read_fds, NULL, NULL, &timeout); + + if (ready == -1) { + perror("select"); + exit(EXIT_FAILURE); + } else if (ready > 0) { + + if (FD_ISSET(STDIN_FILENO, &read_fds)) { + if (fgets(input_buffer, sizeof(input_buffer), stdin) != NULL) { + printf("Your input is: %s\n", input_buffer); + if (!strncmp("imuoff", input_buffer, strlen("imuoff"))) { + result = set_imu(false); + printf("set_imu off: %d\n", result); + } + if (!strncmp("imuon", input_buffer, strlen("imuon"))) { + result = set_imu(true); + printf("set_imu on: %d\n", result); + } + if (!strncmp("quit", input_buffer, strlen("quit"))) { + printf("quit over.\n"); + break; + } + if (!strncmp("3d", input_buffer, strlen("3d"))) { + result = set_3d(true); + printf("set_3d on: %d\n", result); + } + if (!strncmp("2d", input_buffer, strlen("2d"))) { + result = set_3d(false); + printf("set_3d off: %d\n", result); + } + + if (!strncmp("get3d", input_buffer, strlen("get3d"))) { + result = get_3d_state(); + printf("get_3D state: %d\n", result); + } + if (!strncmp("getimu", input_buffer, strlen("getimu"))) { + result = get_imu_state(); + printf("getimu state: %d\n", result); + } + + } else { + perror("fgets"); + exit(EXIT_FAILURE); + } + } + } + + } + set_imu(false); + deinit(); + return 0; +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..deb20e9 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,172 @@ +use library_loader::load_function_ptrs_from_lib; + +load_function_ptrs_from_lib!(VitureLibrary, "libs/libviture_one_sdk.so", { + init( + imu: extern fn(*const u8, u16, u32), + mcu: extern fn(u16, *const u8, u16, u32) + ) -> bool, + deinit() -> (), + set_imu(on_off: bool) -> i32, + get_imu_state() -> i32, + set_3d(on_off: bool) -> i32, + get_3d_state() -> i32, +}); + +static mut IMU_CALLBACK: Option ()>> = None; +static mut MCU_CALLBACK: Option ()>> = None; + +extern "C" fn imu(data: *const u8, len: u16, ts: u32) { + unsafe { + if let Some(imu_callback) = &IMU_CALLBACK { + imu_callback(std::slice::from_raw_parts(data, len as usize), ts); + } + } +} + +extern "C" fn mcu(msgid: u16, data: *const u8, len: u16, ts: u32) { + unsafe { + if let Some(mcu_callback) = &MCU_CALLBACK { + mcu_callback(msgid, std::slice::from_raw_parts(data, len as usize), ts); + } + } +} + +#[allow(non_camel_case_types)] +#[repr(C)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum VitureResult { + ERR_TIMEOUT = -3, + ERR_RSP_ERROR = -2, + ERR_WRITE_FAIL = -1, + + ERR_SUCCESS = 0, + ERR_FAILURE = 1, + ERR_INVALID_ARGUMENT = 2, + ERR_NOT_ENOUGH_MEMORY = 3, + ERR_UNSUPPORTED_CMD = 4, + ERR_CRC_MISMATCH = 5, + ERR_VER_MISMATCH = 6, + ERR_MSG_ID_MISMATCH = 7, + ERR_MSG_STX_MISMATCH = 8, + ERR_CODE_NOT_WRITTEN = 9, +} + +impl From for VitureResult { + fn from(value: i32) -> Self { + match value { + -3 => Self::ERR_TIMEOUT, + -2 => Self::ERR_RSP_ERROR, + -1 => Self::ERR_WRITE_FAIL, + + 0 => Self::ERR_SUCCESS, + 1 => Self::ERR_FAILURE, + 2 => Self::ERR_INVALID_ARGUMENT, + 3 => Self::ERR_NOT_ENOUGH_MEMORY, + 4 => Self::ERR_UNSUPPORTED_CMD, + 5 => Self::ERR_CRC_MISMATCH, + 6 => Self::ERR_VER_MISMATCH, + 7 => Self::ERR_MSG_ID_MISMATCH, + 8 => Self::ERR_MSG_STX_MISMATCH, + 9 => Self::ERR_CODE_NOT_WRITTEN, + + _ => panic!("unsupported value: {value}"), + } + } +} + +pub struct Viture { + library: VitureLibrary, +} + +impl Viture { + pub fn new(imu_callback: IMU, mcu_callback: MCU) -> Result + where + IMU: Fn(&[u8], u32) -> () + 'static, + MCU: Fn(u16, &[u8], u32) -> () + 'static, + { + let library = VitureLibrary::load().map_err(|err| { + println!("Err: {err:?}"); + VitureResult::ERR_FAILURE + })?; + + unsafe { + IMU_CALLBACK = Some(Box::new(imu_callback)); + MCU_CALLBACK = Some(Box::new(mcu_callback)); + } + + let me = Self { library }; + + if !unsafe { me.library.init(imu, mcu) } { + return Err(VitureResult::ERR_FAILURE); + } + + Ok(me) + } + + pub fn set_imu(&self, on_off: bool) -> Result<(), VitureResult> { + unsafe { + let result = VitureResult::from(self.library.set_imu(on_off)); + + if result == VitureResult::ERR_SUCCESS { + Ok(()) + } else { + Err(result) + } + } + } + + pub fn get_imu(&self) -> Result { + unsafe { + let result = self.library.get_imu_state(); + + match result { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(VitureResult::from(result)), + } + } + } + + pub fn set_3d(&self, on_off: bool) -> Result<(), VitureResult> { + unsafe { + let result = VitureResult::from(self.library.set_3d(on_off)); + + if result == VitureResult::ERR_SUCCESS { + Ok(()) + } else { + Err(result) + } + } + } + + pub fn get_3d(&self) -> Result { + unsafe { + let result = self.library.get_3d_state(); + + match result { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(VitureResult::from(result)), + } + } + } +} + +impl Drop for Viture { + fn drop(&mut self) { + unsafe { + self.library.deinit(); + } + } +} + +#[cfg(test)] +mod test { + #[test] + fn initialisation() -> anyhow::Result<()> { + let _viture = super::Viture::new(|_, _| {}, |_, _, _| {}) + .map_err(|err| anyhow::anyhow!("{err:?}"))?; + + Ok(()) + } +}