Skip to content

Add XREAL XBX (A01 / A01+) glasses support - #133

Open
SolberLight wants to merge 1 commit into
wheaney:mainfrom
SolberLight:add-xbx-support
Open

Add XREAL XBX (A01 / A01+) glasses support#133
SolberLight wants to merge 1 commit into
wheaney:mainfrom
SolberLight:add-xbx-support

Conversation

@SolberLight

@SolberLight SolberLight commented Jul 19, 2026

Copy link
Copy Markdown

Add XREAL XBX (A01 / A01+) support

This adds head-tracking support for the XREAL XBX A01 (PID 0x0440) and XBX A01 Plus (PID 0x0442), mirroring how the existing Air/One devices work. Verified working on real XBX A01+ hardware: the IMU streams at ~1000 Hz, accelerometer reads ~1 g while stationary, and the gyro tracks head rotation.

Huge thanks to @taowen 🙏

The key discovery — how to get the XBX MCU to unlock its IMU stream — comes from @taowen and their reverse-engineering work in taowen/ar-glass-lib (see XrealXbxSession.kt). This PR is essentially a C port of that bootstrap.

Why the driver doesn't already work on XBX

The XBX IMU acks START_IMU_DATA but streams nothing under the normal XREAL init, because its firmware gates the IMU stream behind an SDK-identification handshake + a continuous heartbeat on the MCU that the current driver never performs. On top of that, the standard device_mcu_open reads R_ACTIVATION_TIME (0x29), which on XBX returns an error status — so the standard MCU open path fails on XBX and has to be replaced.

What this does

  • Device IDs (hid_ids.c/.h): registers 0x0440/0x0442 with imu_protocol_hid (same 0xAA framing as the Air), IMU on interface 1, MCU on interface 0, 64-byte reports.
  • XBX MCU bootstrap (device_mcu.c): a dedicated open path for XBX that runs the bootstrap commands (0x26, 0x57, 0x12, 0x02, 0x34, 0x35), the SDK-version handshake (0x31 = ASCII "3.1.1")this is the gate — then two acknowledged heartbeats, and finally starts a background thread that sends a heartbeat (0x1a) every 100 ms for the whole session. R_ACTIVATION_TIME is skipped for XBX.
  • Connect ordering (src/devices/xreal.c): for XBX the MCU is opened and bootstrapped (heartbeat running) before the IMU is opened; the IMU then uses the existing imu_protocol_hid path unchanged. The controller poll loop leaves the MCU handle to the heartbeat thread on XBX.
  • Display-mode/SBS control uses an XBX-specific ("Helen") encoding that isn't implemented here, so SBS is disabled for XBX — only IMU head tracking is exposed for now.

⚠️ Submodule: this needs a companion change

The protocol code (hid_ids, device_mcu) lives in the xrealInterfaceLibrary submodule, which is hosted separately at gitlab.com/wheaney/nrealAirLinuxDriver. This PR only contains the top-level (XRLinuxDriver) changessrc/devices/xreal.c and the README. The submodule changes can't appear in this PR's diff and are included below as a patch. A complete merge needs a companion change to the submodule (on GitLab) and then a submodule-pointer bump here. Happy to open the GitLab MR — just let me know the preferred workflow.

The src/devices/xreal.c change is written to compile against the current submodule (it detects XBX by PID locally), but XBX won't actually stream until the submodule patch below lands.

Build / testing status

Opened as a draft because it was not compile-verified in CI (developed on a machine without the Linux build deps). The protocol itself is verified end-to-end on XBX A01+ hardware via a Python proof-of-concept and taowen's Android implementation. Note: the submodule patch adds a pthread dependency to interface_lib (Threads::Threads) for the heartbeat thread.

Companion patch for the xrealInterfaceLibrary submodule (apply in modules/xrealInterfaceLibrary)
diff --git a/interface_lib/CMakeLists.txt b/interface_lib/CMakeLists.txt
index 903f0dd..63152ab 100644
--- a/interface_lib/CMakeLists.txt
+++ b/interface_lib/CMakeLists.txt
@@ -4,6 +4,8 @@ project(xrealAirLibrary C)
 set(CMAKE_C_STANDARD 17)
 
 find_package(json-c REQUIRED CONFIG)
+set(THREADS_PREFER_PTHREAD_FLAG ON)
+find_package(Threads REQUIRED)
 
 add_subdirectory(modules/hidapi)
 add_subdirectory(modules/Fusion/Fusion)
@@ -45,7 +47,7 @@ if(XOD_HAVE_HEADER)
 endif()
 
 target_link_libraries(xrealAirLibrary
-		PRIVATE hidapi::hidapi json-c::json-c Fusion m
+		PRIVATE hidapi::hidapi json-c::json-c Fusion m Threads::Threads
 )
 
 # Optionally bring in xreal_one_driver to satisfy the XO protocol implementation
diff --git a/interface_lib/include/device_mcu.h b/interface_lib/include/device_mcu.h
index a253317..7974c9c 100644
--- a/interface_lib/include/device_mcu.h
+++ b/interface_lib/include/device_mcu.h
@@ -187,8 +187,11 @@ struct device_mcu_t {
 	uint8_t disp_mode;
 	uint8_t blend_state;
 	uint8_t control_mode;
-	
+
 	device_mcu_event_callback callback;
+
+	// XBX-only: opaque handle to the background heartbeat thread context (NULL otherwise).
+	void* heartbeat;
 };
 
 typedef struct device_mcu_t device_mcu_type;
diff --git a/interface_lib/include/hid_ids.h b/interface_lib/include/hid_ids.h
index 147c0c2..c886be0 100644
--- a/interface_lib/include/hid_ids.h
+++ b/interface_lib/include/hid_ids.h
@@ -35,7 +35,7 @@
 
 #include "imu_protocol.h"
 
-#define NUM_SUPPORTED_PRODUCTS 10
+#define NUM_SUPPORTED_PRODUCTS 12
 
 #ifdef __cplusplus
 extern "C" {
@@ -46,6 +46,9 @@ extern const uint16_t xreal_product_ids [NUM_SUPPORTED_PRODUCTS];
 
 bool is_xreal_product_id(uint16_t product_id);
 
+// XBX (A01 / A01 Plus) glasses require a dedicated MCU bootstrap and heartbeat.
+bool is_xreal_xbx_product_id(uint16_t product_id);
+
 const imu_protocol* xreal_imu_protocol(uint16_t product_id);
 int xreal_imu_interface_id(uint16_t product_id);
 int xreal_mcu_interface_id(uint16_t product_id);
diff --git a/interface_lib/src/device_mcu.c b/interface_lib/src/device_mcu.c
index 4f26bed..24a4296 100644
--- a/interface_lib/src/device_mcu.c
+++ b/interface_lib/src/device_mcu.c
@@ -25,6 +25,7 @@
 #include "device_mcu.h"
 #include "device.h"
 
+#include <pthread.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -50,6 +51,16 @@
 #define MAX_PACKET_SIZE 64
 #define PACKET_HEAD 0xFD
 
+// XBX (A01 / A01 Plus) MCU bootstrap. XBX gates the IMU stream behind an SDK-identification
+// handshake and a continuous heartbeat, and returns an error status for R_ACTIVATION_TIME, so
+// it uses a dedicated open path. The bootstrap sequence was discovered by taowen
+// (https://github.com/taowen/ar-glass-lib).
+#define DEVICE_MCU_XBX_MSG_SDK_VERSION 0x31
+#define DEVICE_MCU_XBX_SDK_VERSION "3.1.1"
+#define DEVICE_MCU_XBX_HEARTBEAT_INTERVAL_MS 100
+#define DEVICE_MCU_XBX_RESPONSE_TIMEOUT_MS 250
+#define DEVICE_MCU_XBX_RESPONSE_ATTEMPTS 8
+
 static bool send_payload(device_mcu_type* device, uint8_t size, const uint8_t* payload) {
 	int payload_size = size;
 	if (payload_size > MAX_PACKET_SIZE) {
@@ -176,6 +187,151 @@ static bool do_payload_action(device_mcu_type* device, uint16_t msgid, uint8_t l
 	return false;
 }
 
+typedef struct device_mcu_heartbeat_t {
+	pthread_t thread;
+	volatile bool running;
+} device_mcu_heartbeat_type;
+
+static uint64_t device_mcu_monotonic_ns(void) {
+	struct timespec ts;
+	clock_gettime(CLOCK_MONOTONIC, &ts);
+	return (uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec;
+}
+
+static bool device_mcu_send_heartbeat(device_mcu_type* device) {
+	const uint64_t now = htole64(device_mcu_monotonic_ns());
+	uint8_t payload[8];
+	memcpy(payload, &now, sizeof(now));
+	return send_payload_action(device, DEVICE_MCU_MSG_P_HEARTBEAT, sizeof(payload), payload);
+}
+
+// Sends an XBX bootstrap command and waits for any framed acknowledgement. XBX bootstrap acks
+// don't follow the standard status-byte convention, so a valid response packet (matching head)
+// is treated as success (mirroring the reference implementation's non-empty-response check).
+static bool device_mcu_xbx_command(device_mcu_type* device, uint16_t msgid, uint8_t len, const uint8_t* data) {
+	if (!send_payload_action(device, msgid, len, data)) {
+		device_mcu_error("Sending XBX bootstrap command failed");
+		return false;
+	}
+
+	static device_mcu_packet_type packet;
+	for (int attempts = 0; attempts < DEVICE_MCU_XBX_RESPONSE_ATTEMPTS; attempts++) {
+		memset(&packet, 0, sizeof(packet));
+		int transferred = hid_read_timeout(
+				device->handle,
+				(uint8_t*) &packet,
+				MAX_PACKET_SIZE,
+				DEVICE_MCU_XBX_RESPONSE_TIMEOUT_MS
+		);
+
+		if (transferred == -1) {
+			device_mcu_error("Device may be unplugged");
+			return false;
+		}
+
+		if (transferred > 0 && packet.head == PACKET_HEAD) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static void* device_mcu_heartbeat_loop(void* arg) {
+	device_mcu_type* device = (device_mcu_type*) arg;
+	device_mcu_heartbeat_type* heartbeat = (device_mcu_heartbeat_type*) device->heartbeat;
+
+	while (heartbeat->running) {
+		device_mcu_send_heartbeat(device);
+
+		// Drain any acknowledgements so the input buffer doesn't fill up over time.
+		static device_mcu_packet_type packet;
+		for (int i = 0; i < 4; i++) {
+			if (hid_read_timeout(device->handle, (uint8_t*) &packet, MAX_PACKET_SIZE, 0) <= 0) {
+				break;
+			}
+		}
+
+		const struct timespec interval = {
+			.tv_sec = 0,
+			.tv_nsec = DEVICE_MCU_XBX_HEARTBEAT_INTERVAL_MS * 1000000L
+		};
+		nanosleep(&interval, NULL);
+	}
+
+	return NULL;
+}
+
+// Bootstraps an XBX MCU and starts the continuous heartbeat thread. Once this returns
+// successfully the heartbeat thread is the sole owner of the MCU handle for the session, so the
+// caller must not perform other MCU traffic on XBX devices while streaming.
+static device_mcu_error_type device_mcu_open_xbx(device_mcu_type* device) {
+	// Drain any stale input before starting the handshake.
+	static device_mcu_packet_type drain;
+	for (int i = 0; i < 4; i++) {
+		if (hid_read_timeout(device->handle, (uint8_t*) &drain, MAX_PACKET_SIZE, 0) <= 0) {
+			break;
+		}
+	}
+
+	const uint8_t one[4] = { 0x01, 0x00, 0x00, 0x00 };
+	const struct {
+		uint16_t msgid;
+		uint8_t len;
+		const uint8_t* data;
+	} bootstrap[] = {
+		{ 0x26, 0, NULL },
+		{ 0x57, 0, NULL },
+		{ 0x12, sizeof(one), one },
+		{ 0x02, sizeof(one), one },
+		{ 0x34, 0, NULL },
+		{ 0x35, 0, NULL },
+	};
+
+	for (size_t i = 0; i < sizeof(bootstrap) / sizeof(bootstrap[0]); i++) {
+		if (!device_mcu_xbx_command(device, bootstrap[i].msgid, bootstrap[i].len, bootstrap[i].data)) {
+			device_mcu_error("XBX MCU bootstrap command failed");
+			return DEVICE_MCU_ERROR_PAYLOAD_FAILED;
+		}
+	}
+
+	// SDK-version handshake: this is the gate that enables the IMU stream.
+	if (!device_mcu_xbx_command(device, DEVICE_MCU_XBX_MSG_SDK_VERSION,
+			(uint8_t) strlen(DEVICE_MCU_XBX_SDK_VERSION), (const uint8_t*) DEVICE_MCU_XBX_SDK_VERSION)) {
+		device_mcu_error("XBX MCU SDK version handshake failed");
+		return DEVICE_MCU_ERROR_PAYLOAD_FAILED;
+	}
+
+	// Two initial acknowledged heartbeats before starting the continuous heartbeat.
+	for (int i = 0; i < 2; i++) {
+		const uint64_t now = htole64(device_mcu_monotonic_ns());
+		uint8_t payload[8];
+		memcpy(payload, &now, sizeof(now));
+		if (!device_mcu_xbx_command(device, DEVICE_MCU_MSG_P_HEARTBEAT, sizeof(payload), payload)) {
+			device_mcu_error("XBX MCU initial heartbeat failed");
+			return DEVICE_MCU_ERROR_PAYLOAD_FAILED;
+		}
+	}
+
+	device_mcu_heartbeat_type* heartbeat = calloc(1, sizeof(device_mcu_heartbeat_type));
+	if (!heartbeat) {
+		device_mcu_error("Failed allocating heartbeat context");
+		return DEVICE_MCU_ERROR_PAYLOAD_FAILED;
+	}
+
+	heartbeat->running = true;
+	device->heartbeat = heartbeat;
+
+	if (pthread_create(&heartbeat->thread, NULL, device_mcu_heartbeat_loop, device) != 0) {
+		free(heartbeat);
+		device->heartbeat = NULL;
+		device_mcu_error("Failed starting heartbeat thread");
+		return DEVICE_MCU_ERROR_PAYLOAD_FAILED;
+	}
+
+	return DEVICE_MCU_ERROR_NO_ERROR;
+}
+
 device_mcu_error_type device_mcu_open(device_mcu_type* device, device_mcu_event_callback callback) {
 	if (!device) {
 		device_mcu_error("No device");
@@ -219,6 +375,12 @@ device_mcu_error_type device_mcu_open(device_mcu_type* device, device_mcu_event_
 		return DEVICE_MCU_ERROR_NO_HANDLE;
 	}
 
+	// XBX glasses need a different open path (bootstrap + heartbeat) and don't support the
+	// activation-based sequence below.
+	if (is_xreal_xbx_product_id(device->product_id)) {
+		return device_mcu_open_xbx(device);
+	}
+
 	device_mcu_clear(device);
 
 	if (!send_payload_action(device, DEVICE_MCU_MSG_R_ACTIVATION_TIME, 0, NULL)) {
@@ -721,11 +883,20 @@ device_mcu_error_type device_mcu_close(device_mcu_type* device) {
 		device_mcu_error("No device");
 		return DEVICE_MCU_ERROR_NO_DEVICE;
 	}
-	
+
+	// Stop the XBX heartbeat thread before closing the handle it uses.
+	if (device->heartbeat) {
+		device_mcu_heartbeat_type* heartbeat = (device_mcu_heartbeat_type*) device->heartbeat;
+		heartbeat->running = false;
+		pthread_join(heartbeat->thread, NULL);
+		free(heartbeat);
+		device->heartbeat = NULL;
+	}
+
 	if (device->handle) {
 		hid_close(device->handle);
 	}
-	
+
 	memset(device, 0, sizeof(device_mcu_type));
 	device_exit();
 
diff --git a/interface_lib/src/hid_ids.c b/interface_lib/src/hid_ids.c
index 522a1f5..e94df68 100644
--- a/interface_lib/src/hid_ids.c
+++ b/interface_lib/src/hid_ids.c
@@ -47,7 +47,9 @@ const uint16_t xreal_product_ids[NUM_SUPPORTED_PRODUCTS] = {
     0x0437, // XREAL One
     0x0438, // XREAL One
     0x043e, // XREAL One S
-    0x043d  // XREAL One S
+    0x043d, // XREAL One S
+    0x0440, // XREAL XBX A01
+    0x0442  // XREAL XBX A01 Plus
 };
 
 const imu_protocol* xreal_imu_protocols[NUM_SUPPORTED_PRODUCTS] = {
@@ -60,7 +62,9 @@ const imu_protocol* xreal_imu_protocols[NUM_SUPPORTED_PRODUCTS] = {
     &imu_protocol_xreal_one, // XREAL One
     &imu_protocol_xreal_one, // XREAL One
     &imu_protocol_xreal_one, // XREAL One S
-    &imu_protocol_xreal_one  // XREAL One S
+    &imu_protocol_xreal_one, // XREAL One S
+    &imu_protocol_hid,       // XREAL XBX A01
+    &imu_protocol_hid        // XREAL XBX A01 Plus
 };
 
 const int xreal_imu_hid_interface_ids[NUM_SUPPORTED_PRODUCTS] = {
@@ -73,7 +77,9 @@ const int xreal_imu_hid_interface_ids[NUM_SUPPORTED_PRODUCTS] = {
     -1, // XREAL One
     -1, // XREAL One
     -1, // XREAL One S
-    -1  // XREAL One S
+    -1, // XREAL One S
+    1,  // XREAL XBX A01
+    1   // XREAL XBX A01 Plus
 };
 
 const int xreal_mcu_hid_interface_ids[NUM_SUPPORTED_PRODUCTS] = {
@@ -86,7 +92,9 @@ const int xreal_mcu_hid_interface_ids[NUM_SUPPORTED_PRODUCTS] = {
     -1, // XREAL One
     -1, // XREAL One
     -1, // XREAL One S
-    -1  // XREAL One S
+    -1, // XREAL One S
+    0,  // XREAL XBX A01 MCU
+    0   // XREAL XBX A01 Plus MCU
 };
 
 const uint16_t xreal_imu_hid_max_payload_sizes[NUM_SUPPORTED_PRODUCTS] = {
@@ -99,7 +107,9 @@ const uint16_t xreal_imu_hid_max_payload_sizes[NUM_SUPPORTED_PRODUCTS] = {
     -1, // XREAL One
     -1, // XREAL One
     -1, // XREAL One S
-    -1  // XREAL One S
+    -1, // XREAL One S
+    64, // XREAL XBX A01
+    64  // XREAL XBX A01 Plus
 };
 
 static int xreal_product_index(uint16_t product_id) {
@@ -116,6 +126,10 @@ bool is_xreal_product_id(uint16_t product_id) {
     return xreal_product_index(product_id) >= 0;
 }
 
+bool is_xreal_xbx_product_id(uint16_t product_id) {
+    return (product_id == 0x0440) || (product_id == 0x0442);
+}
+
 const imu_protocol* xreal_imu_protocol(uint16_t product_id) {
     const int index = xreal_product_index(product_id);
 

Registers the XREAL XBX A01 (PID 0x0440) and XBX A01 Plus (PID 0x0442) and
wires up their connect flow so head tracking works.

Unlike the Air/One devices, XBX gates its IMU stream behind an MCU
SDK-identification handshake plus a continuous 100 ms heartbeat, and it returns
an error status for R_ACTIVATION_TIME. So the MCU must be opened and
bootstrapped (heartbeat running) *before* the IMU is opened. This commit
reorders xreal_device_connect() accordingly for XBX and keeps the background
heartbeat thread as the sole owner of the MCU handle while streaming (the
controller poll loop skips MCU reads for XBX).

Display-mode/SBS control uses an XBX-specific ("Helen") encoding that isn't
implemented yet, so it is disabled for XBX; only IMU head tracking is exposed.

The MCU bootstrap + heartbeat protocol itself lives in the
xrealInterfaceLibrary submodule and is provided as a companion patch (see PR
description) — this repo change depends on it.

Verified on real XBX A01+ hardware (IMU streams ~1000 Hz; accel = 1 g
stationary; gyro tracks head rotation).
@SolberLight
SolberLight marked this pull request as ready for review July 19, 2026 00:37

@wheaney wheaney left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking this on!

Comment thread src/devices/xreal.c
Comment on lines +54 to +57
// XBX (A01 / A01 Plus) glasses gate their IMU stream behind an MCU SDK handshake + heartbeat,
// so they need the MCU opened and bootstrapped before the IMU is opened (see xreal_device_connect).
#define XREAL_XBX_A01_ID_PRODUCT 0x0440
#define XREAL_XBX_A01_PLUS_ID_PRODUCT 0x0442

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make this a little more generic. I'm thinking we create a list of id_product values that require an MCU-first approach.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that's a great idea

Comment thread src/devices/xreal.c
25.0, // XREAL One S
25.0 // XREAL One S
25.0, // XREAL One S
10.0, // XREAL XBX A01

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you verify that this default look-ahead creates the best pinning effect? It looks like in general you copied the values for the Air glasses, which is probably the safe bet, but it's worth playing around with the look-ahead slider in the settings to see if any other values appear to work better (easier to use that dynamic slider than tinker with it here and recompile each time to test, then you can hardcode the best default). The way I usually test is to pin the screen with an edge of the screen near something else in the background (with light passthrough so I can see the background). Then move my head somewhat quickly and see how well the pinning resists following the quick movement (it will sort of jump in the direction you move and then quickly go back to where it was pinned). The best look-ahead value will create the most solid pinning (none will be perfect, though).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes that's a good idea. I've concluded on this value based on a custom software i've made for the glasses, so they might not be perfect indeed, i'll try out your method to refine it

Comment thread src/devices/xreal.c
Comment on lines +180 to +182
static bool is_xreal_xbx(uint16_t product_id) {
return product_id == XREAL_XBX_A01_ID_PRODUCT || product_id == XREAL_XBX_A01_PLUS_ID_PRODUCT;
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have this check the list of mcu-first id_products, give it a more generic name (like requires_mcu_connection_first or something similar).

Comment thread src/devices/xreal.c
Comment on lines +252 to +257
if (device_is_xbx && !connected && glasses_controller) {
device_mcu_close(glasses_controller);
free(glasses_controller);
glasses_controller = NULL;
mcu_enabled = false;
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can live inside the if-block where it's relevant.

Comment thread src/devices/xreal.c
// must not read/poll the MCU; it just stays alive until disconnect (detected via the IMU).
while (connected && glasses_imu && mcu_enabled &&
(device_is_xbx || device_mcu_read(glasses_controller, 100) == DEVICE_MCU_ERROR_NO_ERROR)) {
if (!device_is_xbx) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (device->sbs_mode_supported) seems more appropriate

@SolberLight

Copy link
Copy Markdown
Author

@wheaney thank you for your feedback ! I'll check them asap :)

@SolberLight

Copy link
Copy Markdown
Author

@wheaney how would you handle changes in the submodule ? Clicking it through github doesn't let me access it. It there a specific process ?

@wheaney

wheaney commented Jul 31, 2026

Copy link
Copy Markdown
Owner

@SolberLight I've refactored and merged your patch into my fork of the nrealAirLinuxDriver package in gitlab. I added a write lock that allows callers to issue MCU commands without risk of colliding with the heartbeat logic (the heartbeat no longer does HID read calls after the initial "open"). That should allow us to continue to support switching to SBS mode and any other MCU commands we want. Please pull in this latest commit and let me know if it doesn't work the same as it did with your patch.

Also, I think we may be able to update XRLinuxDriver's xreal.c implementation so that it always attempts to open the MCU first (no conditional logic, just swap the ordering for every device). The only conditional check we'd need to add is that the device connection should fail if the MCU fails to open for devices that require a heartbeat (right now it allows MCU connection to be skipped if the IMU connection opens fine).

@wheaney

wheaney commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Oh, but with that said, I also have a much larger refactor in place that makes this unusable at the moment. I'll need to update XRLinuxDriver to fully incorporate.

@wheaney

wheaney commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Okay, the latest version (2.10.0) has your MCU-heartbeat driver changes plus the larger refractor I was referring to. You should be able to rebase with minimal-to-no conflicts and make the changes I described above.

@jumper047

Copy link
Copy Markdown

I rebased this branch on latest master (using Claude - sorry, not good enough with C) and it doesn't work for me. I have glasses and can relatively easy build and test xrlinuxdriver - please let me know if I can help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants