Merge branch 'imu-kickoff'
This commit is contained in:
commit
976b7a79db
59
README.md
59
README.md
|
|
@ -2,43 +2,52 @@
|
||||||
|
|
||||||
work in progress; webxr support for xreal devices
|
work in progress; webxr support for xreal devices
|
||||||
|
|
||||||
Read the history of these projects:
|
> WebXR Bindings and Test Utilities / Getting-Started Example(s) Basis for NPM package
|
||||||
|
|
||||||
> https://jakedownsdev.substack.com/p/open-sourcing-nreal-air-development?utm_source=profile&utm_medium=reader2
|
🙌 🥽 💪 based on combined effort of the Xreal Open Source Community:
|
||||||
|
|
||||||
based on combined effort of:
|
|
||||||
|
|
||||||
### Community Open Source Xreal Drivers:
|
|
||||||
|
|
||||||
- [Windows](https://github.com/MSmithDev/AirAPI_Windows) - .dll
|
|
||||||
- [Android](https://github.com/SARankDirector-Minecraft/XR-android-driver) - .aar
|
|
||||||
- [Linux](https://gitlab.com/TheJackiMonster/nrealAirLinuxDriver) - .so
|
|
||||||
- [Mac](https://gitlab.com/DanBurkhardt/nrealAirLinuxDriver/-/tree/main) - .dylib
|
|
||||||
- [WebXR](https://github.com/jakedowns/xreal-webxr) - WebXR Bindings and Test Utilities / Getting-Started Example(s) Basis for NPM package
|
|
||||||
- [Rust](https://github.com/badicsalex/ar-drivers-rs) - Cross-Platform Rust Library
|
|
||||||
- [XR Gaming Decky Plugin](https://github.com/wheaney/decky-XRGaming) - XR Gaming Decky Plugin
|
|
||||||
- [XRLinux](https://github.com/wheaney/XRLinuxDriver)
|
|
||||||
- [OpenVR-xrealAirGlassesHMD](https://github.com/wheaney/OpenVR-xrealAirGlassesHMD) - OpenVR driver for xReal Air Glasses as a HMD
|
|
||||||
|
|
||||||
### Projects using Open Source Xreal Drivers
|
### Projects using Open Source Xreal Drivers
|
||||||
|
|
||||||
- [OpenGL demo program](https://github.com/abls/real-air)
|
- [OpenGL demo program](https://github.com/abls/real-air)
|
||||||
- [kassandra](https://www.youtube.com/watch?v=7pH2VvUTZIQ) Kassandra: AR for all Macs with Nreal Air, is now in Public Beta
|
- [kassandra](https://www.youtube.com/watch?v=7pH2VvUTZIQ) 🍎 Mac
|
||||||
- [breezy desktop](https://github.com/wheaney/breezy-desktop) - Linux
|
- [breezy desktop](https://github.com/wheaney/breezy-desktop) 🐧 Linux
|
||||||
- [cozy desk](https://cozydesk.space/download/) - Mac
|
- [cozy desk](https://cozydesk.space/download/) 🍏 Mac
|
||||||
- [PhoenixHeadTracker](https://github.com/iVideoGameBoss/PhoenixHeadTracker/releases) - Windows
|
- [PhoenixHeadTracker](https://github.com/iVideoGameBoss/PhoenixHeadTracker/releases) 🖼️ Windows
|
||||||
- [Monado - xreal support](https://gitlab.freedesktop.org/monado/monado/-/tree/main/src/xrt/drivers/nreal_air) - Linux + OpenXR (via [TheJackiMonster](https://gitlab.com/TheJackiMonster))
|
- [Monado - xreal support](https://gitlab.freedesktop.org/monado/monado/-/tree/main/src/xrt/drivers/nreal_air) 🐧 Linux + OpenXR
|
||||||
|
|
||||||
|
### Xreal Open Source Community Drivers:
|
||||||
|
|
||||||
|
- [WebXR](https://github.com/jakedowns/xreal-webxr)
|
||||||
|
- [Windows](https://github.com/MSmithDev/AirAPI_Windows)
|
||||||
|
- [Android](https://github.com/SARankDirector-Minecraft/XR-android-driver)
|
||||||
|
- [Linux](https://gitlab.com/TheJackiMonster/nrealAirLinuxDriver)
|
||||||
|
- [Mac](https://gitlab.com/DanBurkhardt/nrealAirLinuxDriver/-/tree/main)
|
||||||
|
- [Rust](https://github.com/badicsalex/ar-drivers-rs)
|
||||||
|
- [XR Gaming Decky Plugin](https://github.com/wheaney/decky-XRGaming)
|
||||||
|
- [XR Linux Driver](https://github.com/wheaney/XRLinuxDriver)
|
||||||
|
- [OpenVR-xrealAirGlassesHMD](https://github.com/wheaney/OpenVR-xrealAirGlassesHMD)
|
||||||
|
|
||||||
### Utilities
|
### Utilities
|
||||||
|
|
||||||
- [Unofficial Firmware Mirror](https://air.msmithdev.com/) - Web Firmware Updater Mirror
|
- 🪞 [Unofficial Firmware Archive Mirror](https://air.msmithdev.com/)
|
||||||
- [IMU Inspector](https://github.com/abls/imu-inspector)
|
- 🔍 [IMU Inspector](https://github.com/abls/imu-inspector)
|
||||||
|
- 📊 [Real Utilities](https://github.com/edwatt/real_utilities/)
|
||||||
|
|
||||||
|
|
||||||
|
### Special Thanks!
|
||||||
|
- [Tobias Frisch, TheJackiMonster](https://gitlab.com/TheJackiMonster)
|
||||||
|
- [Matt Smith, MSmithDev](https://github.com/MSmithDev)
|
||||||
|
- [Ed Watt](https://github.com/edwatt)
|
||||||
|
- [Andy, abls, noot](https://github.com/abls)
|
||||||
|
- [Wayne Heaney, wheaney](https://github.com/wheaney)
|
||||||
|
- [Alex Badics, badicsalex](https://github.com/badicsalex)
|
||||||
|
- [SARankDirector-Minecraft](https://github.com/SARankDirector-Minecraft)
|
||||||
|
|
||||||
### Roadmap
|
### Roadmap
|
||||||
|
|
||||||
- [OpenXR](#coming-soon) # coming soon
|
- 🌐 🥽 [OpenXR](#coming-soon) # coming soon
|
||||||
- [CloudXR](#on-hold) # on hold
|
- ☁️ 🥽 [CloudXR](#on-hold) # on hold
|
||||||
|
|
||||||
|
### Read the history of this project:
|
||||||
|
|
||||||
|
🤩 [Open-Sourcing Nreal Air Development - SubStack](https://jakedownsdev.substack.com/p/open-sourcing-nreal-air-development?utm_source=profile&utm_medium=reader2)
|
||||||
|
|
@ -88,10 +88,10 @@
|
||||||
<div>current brightness: <span class="current-brightness">?</span></div>
|
<div>current brightness: <span class="current-brightness">?</span></div>
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
<div id="imu-labels"></div>
|
<!-- <div id="imu-labels"></div> -->
|
||||||
<div id="imu"></div>
|
<!-- <div id="imu"></div> -->
|
||||||
<div id="imu-bars"></div>
|
<!-- <div id="imu-bars"></div> -->
|
||||||
<div id="sparklines"></div>
|
<!-- <div id="sparklines"></div> -->
|
||||||
|
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
@ -486,7 +486,7 @@
|
||||||
window.sparkline_elements = [];
|
window.sparkline_elements = [];
|
||||||
|
|
||||||
window.onload = async () => {
|
window.onload = async () => {
|
||||||
|
/*
|
||||||
for(var i = 0; i<41; i++){
|
for(var i = 0; i<41; i++){
|
||||||
let child = document.createElement('div');
|
let child = document.createElement('div');
|
||||||
child.classList.add('bar');
|
child.classList.add('bar');
|
||||||
|
|
@ -524,6 +524,7 @@
|
||||||
|
|
||||||
document.getElementById('sparklines').append(_sl_wrapper)
|
document.getElementById('sparklines').append(_sl_wrapper)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
if (!Manager?.hidSupported()) {
|
if (!Manager?.hidSupported()) {
|
||||||
document.getElementById('noSupportHid').style.display = "block";
|
document.getElementById('noSupportHid').style.display = "block";
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,10 @@ window.logPackets = () => {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
export default class Glasses extends EventTarget {
|
export default class Glasses extends EventTarget {
|
||||||
|
|
||||||
|
/* RepeatingDeviceReportPoll */
|
||||||
|
imu_poller_instance = null;
|
||||||
|
|
||||||
constructor(device) {
|
constructor(device) {
|
||||||
console.log('constructing');
|
console.log('constructing');
|
||||||
super();
|
super();
|
||||||
|
|
@ -138,6 +142,25 @@ export default class Glasses extends EventTarget {
|
||||||
this._interestMsg = [];
|
this._interestMsg = [];
|
||||||
this._reports = new Map();
|
this._reports = new Map();
|
||||||
this._captures = [];
|
this._captures = [];
|
||||||
|
|
||||||
|
// creates it, but doesn't start it...
|
||||||
|
this.imu_poller_instance = new RepeatingDeviceReportPoll({
|
||||||
|
interval: 100,
|
||||||
|
callback: async ()=>{
|
||||||
|
this.sendReportTimeout(Protocol.MESSAGES.R_IMU_DATA, [0x40]).then((report)=>{
|
||||||
|
if(report){
|
||||||
|
console.log('got report',report)
|
||||||
|
}else{
|
||||||
|
console.log('no report')
|
||||||
|
}
|
||||||
|
}).catch((e)=>{
|
||||||
|
console.error('error sending report',e)
|
||||||
|
}).finally(()=>{
|
||||||
|
//console.log('finally')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// set input listener
|
// set input listener
|
||||||
device.oninputreport = this._handleInputReport.bind(this);
|
device.oninputreport = this._handleInputReport.bind(this);
|
||||||
|
|
||||||
|
|
@ -153,6 +176,14 @@ export default class Glasses extends EventTarget {
|
||||||
// this.renderSparklines();
|
// this.renderSparklines();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startIMUPolling(){
|
||||||
|
this.imu_poller_instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
stopIMUPolling(){
|
||||||
|
this.imu_poller_instance.end();
|
||||||
|
}
|
||||||
|
|
||||||
renderSparklines(){
|
renderSparklines(){
|
||||||
if(!window.imu_output){
|
if(!window.imu_output){
|
||||||
return;
|
return;
|
||||||
|
|
@ -229,7 +260,7 @@ export default class Glasses extends EventTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(report.msgId === 0){
|
if(report.msgId === 0){
|
||||||
// console.log(report.payload.length, report.status)
|
console.log(report.payload.length, report.status)
|
||||||
imu_report_current++;
|
imu_report_current++;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -430,4 +461,36 @@ export default class Glasses extends EventTarget {
|
||||||
toString() {
|
toString() {
|
||||||
return `<Glasses deviceName=${this._device.productName} vid=${this._device.vendorId} pid=${this._device.vendorId}>`;
|
return `<Glasses deviceName=${this._device.productName} vid=${this._device.vendorId} pid=${this._device.vendorId}>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class RepeatingDeviceReportPoll {
|
||||||
|
timer = null;
|
||||||
|
constructor(opts){
|
||||||
|
opts = opts || {};
|
||||||
|
opts = {
|
||||||
|
interval: 100,
|
||||||
|
callback: ()=>{}, // this is the function that will be called
|
||||||
|
...opts
|
||||||
|
}
|
||||||
|
this.opts = opts;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
start(){
|
||||||
|
this.ended = false;
|
||||||
|
this.timer = setInterval(() => {
|
||||||
|
if(this.ended){
|
||||||
|
clearInterval(this.timer)
|
||||||
|
}else{
|
||||||
|
this.opts.callback()
|
||||||
|
}
|
||||||
|
},this.opts.interval)
|
||||||
|
}
|
||||||
|
|
||||||
|
end(){
|
||||||
|
// stop the timer on next tick
|
||||||
|
this.ended = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -513,7 +513,11 @@ export async function startIMU() {
|
||||||
if(!glasses){
|
if(!glasses){
|
||||||
return Promise.reject('no device connected')
|
return Promise.reject('no device connected')
|
||||||
}
|
}
|
||||||
return glasses.sendReportTimeout(Protocol.MESSAGES.W_TOGGLE_IMU, [1])
|
|
||||||
|
// kick off polling
|
||||||
|
glasses.startIMUPolling();
|
||||||
|
|
||||||
|
return glasses.sendReportTimeout(Protocol.MESSAGES.W_TOGGLE_IMU, [0x1])
|
||||||
.then(report => {
|
.then(report => {
|
||||||
console.warn('startIMU -> report',report);
|
console.warn('startIMU -> report',report);
|
||||||
if (reportSuccess(report)){
|
if (reportSuccess(report)){
|
||||||
|
|
@ -529,7 +533,7 @@ export async function stopIMU() {
|
||||||
return 'no device connected'
|
return 'no device connected'
|
||||||
}
|
}
|
||||||
// arg 2: 0 is what turns "off" the stream
|
// arg 2: 0 is what turns "off" the stream
|
||||||
return glasses.sendReportTimeout(Protocol.MESSAGES.W_TOGGLE_IMU, [0])
|
return glasses.sendReportTimeout(Protocol.MESSAGES.W_TOGGLE_IMU, [0x0])
|
||||||
.then(report => {
|
.then(report => {
|
||||||
console.warn('stopIMU -> report',report);
|
console.warn('stopIMU -> report',report);
|
||||||
if (reportSuccess(report)){
|
if (reportSuccess(report)){
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,72 @@ export const NREAL_VENDOR_ID = 0x3318;
|
||||||
export const BOOT_PRODUCT_ID = 0x0423;
|
export const BOOT_PRODUCT_ID = 0x0423;
|
||||||
export const IMU_TIMEOUT = 250;
|
export const IMU_TIMEOUT = 250;
|
||||||
|
|
||||||
|
export const ERRORS = {
|
||||||
|
DEVICE3_ERROR_NO_ERROR: 0,
|
||||||
|
DEVICE3_ERROR_NO_DEVICE: 1,
|
||||||
|
DEVICE3_ERROR_NO_HANDLE: 2,
|
||||||
|
DEVICE3_ERROR_NO_ALLOCATION: 3,
|
||||||
|
DEVICE3_ERROR_WRONG_SIZE: 4,
|
||||||
|
DEVICE3_ERROR_FILE_NOT_OPEN: 5,
|
||||||
|
DEVICE3_ERROR_FILE_NOT_CLOSED: 6,
|
||||||
|
DEVICE3_ERROR_LOADING_FAILED: 7,
|
||||||
|
DEVICE3_ERROR_SAVING_FAILED: 8,
|
||||||
|
DEVICE3_ERROR_UNPLUGGED: 9,
|
||||||
|
DEVICE3_ERROR_UNEXPECTED: 10,
|
||||||
|
DEVICE3_ERROR_WRONG_SIGNATURE: 11,
|
||||||
|
DEVICE3_ERROR_INVALID_VALUE: 12,
|
||||||
|
DEVICE3_ERROR_NOT_INITIALIZED: 13,
|
||||||
|
DEVICE3_ERROR_PAYLOAD_FAILED: 14,
|
||||||
|
DEVICE3_ERROR_UNKNOWN: 15,
|
||||||
|
}
|
||||||
|
|
||||||
|
class Device3Packet {
|
||||||
|
/*
|
||||||
|
uint8_t signature [2];
|
||||||
|
uint8_t temperature [2];
|
||||||
|
uint64_t timestamp;
|
||||||
|
uint8_t angular_multiplier [2];
|
||||||
|
uint8_t angular_divisor [4];
|
||||||
|
uint8_t angular_velocity_x [3];
|
||||||
|
uint8_t angular_velocity_y [3];
|
||||||
|
uint8_t angular_velocity_z [3];
|
||||||
|
uint8_t acceleration_multiplier [2];
|
||||||
|
uint8_t acceleration_divisor [4];
|
||||||
|
uint8_t acceleration_x [3];
|
||||||
|
uint8_t acceleration_y [3];
|
||||||
|
uint8_t acceleration_z [3];
|
||||||
|
uint8_t magnetic_multiplier [2];
|
||||||
|
uint8_t magnetic_divisor [4];
|
||||||
|
uint8_t magnetic_x [2];
|
||||||
|
uint8_t magnetic_y [2];
|
||||||
|
uint8_t magnetic_z [2];
|
||||||
|
uint32_t checksum;
|
||||||
|
uint8_t _padding [6];
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
this.signature = new Uint8Array(2);
|
||||||
|
this.temperature = new Uint8Array(2);
|
||||||
|
this.timestamp = new Uint8Array(8);
|
||||||
|
this.angular_multiplier = new Uint8Array(2);
|
||||||
|
this.angular_divisor = new Uint8Array(4);
|
||||||
|
this.angular_velocity_x = new Uint8Array(3);
|
||||||
|
this.angular_velocity_y = new Uint8Array(3);
|
||||||
|
this.angular_velocity_z = new Uint8Array(3);
|
||||||
|
this.acceleration_multiplier = new Uint8Array(2);
|
||||||
|
this.acceleration_divisor = new Uint8Array(4);
|
||||||
|
this.acceleration_x = new Uint8Array(3);
|
||||||
|
this.acceleration_y = new Uint8Array(3);
|
||||||
|
this.acceleration_z = new Uint8Array(3);
|
||||||
|
this.magnetic_multiplier = new Uint8Array(2);
|
||||||
|
this.magnetic_divisor = new Uint8Array(4);
|
||||||
|
this.magnetic_x = new Uint8Array(2);
|
||||||
|
this.magnetic_y = new Uint8Array(2);
|
||||||
|
this.magnetic_z = new Uint8Array(2);
|
||||||
|
this.checksum = new Uint8Array(4);
|
||||||
|
this._padding = new Uint8Array(6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const MESSAGES = {
|
export const MESSAGES = {
|
||||||
|
|
||||||
R_MCU_APP_FW_VERSION: 0x26,//MCU APP FW version.
|
R_MCU_APP_FW_VERSION: 0x26,//MCU APP FW version.
|
||||||
|
|
@ -22,6 +88,9 @@ export const MESSAGES = {
|
||||||
W_ACTIVATION_TIME: 0x2A,//Write activation time
|
W_ACTIVATION_TIME: 0x2A,//Write activation time
|
||||||
W_SLEEP_TIME: 0x1E,//Write unsleep time
|
W_SLEEP_TIME: 0x1E,//Write unsleep time
|
||||||
|
|
||||||
|
R_IMU_DATA: 0x80,//IMU data
|
||||||
|
UNKNOWN_40: 0x40,//Unknown
|
||||||
|
|
||||||
W_TOGGLE_IMU: 0x19,
|
W_TOGGLE_IMU: 0x19,
|
||||||
W_CANCEL_ACTIVATION: 0x19,
|
W_CANCEL_ACTIVATION: 0x19,
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue