From 61cb4946d61f6dfa52a3e74a175d403c60cfa238 Mon Sep 17 00:00:00 2001 From: Justin Mitchell Date: Sun, 21 Jun 2026 02:06:01 -0400 Subject: [PATCH] Fix I2C initialization for dual X3+X4 binary Sync the runtime board profile to the detected device before I2C consumers initialize. This ensures Wire.begin() is called when needed for X3 devices. Previously, the X4 profile remained active through initialization, causing Wire to never reinitialize after detection probe, leading to X3 I2C read failures with 'could not acquire lock' errors. --- lib/hal/HalGPIO.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/hal/HalGPIO.cpp b/lib/hal/HalGPIO.cpp index c6f8a7f3..a613d80b 100644 --- a/lib/hal/HalGPIO.cpp +++ b/lib/hal/HalGPIO.cpp @@ -206,6 +206,18 @@ void HalGPIO::begin() { #if FREEINK_MCU_C3 _deviceType = detectDeviceTypeWithFingerprint(); + // Sync the runtime board profile to the detected device NOW, before any I2C + // consumer begins. powerManager/clock/tilt begin() run immediately after this + // in setup() and rely on BoardConfig::ACTIVE: HalPowerManager only calls + // Wire.begin() when the active profile has an I2C gauge (gaugeAddr != 0), and + // the X3 clock/tilt raw-Wire paths assume that begin happened. The dual X3+X4 + // binary boots as X4 (gaugeAddr 0), so without this the X4 profile stays active + // through those begins, Wire is never (re)initialised after the detection probe + // end()s it, and every X3 read fails with lock==NULL ("could not acquire lock" + // / "NULL TX buffer pointer"). FreeInkDisplay also calls selectDevice() later, + // but that is after the I2C consumers — too late. selectDevice() is idempotent. + BoardConfig::selectDevice(deviceIsX3() ? BoardConfig::Board::XteinkX3 : BoardConfig::Board::XteinkX4); + if (deviceIsX4()) { pinMode(BAT_GPIO0, INPUT); pinMode(UART0_RXD, INPUT);