1+ #include < BoardConfig.h>
12#include < HalGPIO.h>
23#include < Logging.h>
34#include < Preferences.h>
45#include < SPI.h>
56#include < Wire.h>
7+ #include < XteinkDetect.h>
68#include < esp_sleep.h>
79
810// Global HalGPIO instance
911HalGPIO gpio;
1012
1113namespace X3GPIO {
1214
13- struct X3ProbeResult {
14- bool bq27220 = false ;
15- bool ds3231 = false ;
16- bool qmi8658 = false ;
17-
18- uint8_t score () const {
19- return static_cast <uint8_t >(bq27220) + static_cast <uint8_t >(ds3231) + static_cast <uint8_t >(qmi8658);
20- }
21- };
22-
23- bool readI2CReg8 (uint8_t addr, uint8_t reg, uint8_t * outValue) {
24- Wire.beginTransmission (addr);
25- Wire.write (reg);
26- if (Wire.endTransmission (false ) != 0 ) {
27- return false ;
28- }
29- if (Wire.requestFrom (addr, static_cast <uint8_t >(1 ), static_cast <uint8_t >(true )) < 1 ) {
30- return false ;
31- }
32- *outValue = Wire.read ();
33- return true ;
34- }
35-
3615bool readI2CReg16LE (uint8_t addr, uint8_t reg, uint16_t * outValue) {
3716 Wire.beginTransmission (addr);
3817 Wire.write (reg);
@@ -60,58 +39,6 @@ bool readBQ27220CurrentMA(int16_t* outCurrent) {
6039 return true ;
6140}
6241
63- bool probeBQ27220Signature () {
64- uint16_t soc = 0 ;
65- uint16_t voltageMv = 0 ;
66- if (!readI2CReg16LE (I2C_ADDR_BQ27220 , BQ27220_SOC_REG , &soc)) {
67- return false ;
68- }
69- if (soc > 100 ) {
70- return false ;
71- }
72- if (!readI2CReg16LE (I2C_ADDR_BQ27220 , BQ27220_VOLT_REG , &voltageMv)) {
73- return false ;
74- }
75- return voltageMv >= 2500 && voltageMv <= 5000 ;
76- }
77-
78- bool probeDS3231Signature () {
79- uint8_t sec = 0 ;
80- if (!readI2CReg8 (I2C_ADDR_DS3231 , DS3231_SEC_REG , &sec)) {
81- return false ;
82- }
83- const uint8_t tensDigit = (sec >> 4 ) & 0x07 ;
84- const uint8_t onesDigit = sec & 0x0F ;
85-
86- return tensDigit <= 5 && onesDigit <= 9 ;
87- }
88-
89- bool probeQMI8658Signature () {
90- uint8_t whoami = 0 ;
91- if (readI2CReg8 (I2C_ADDR_QMI8658 , QMI8658_WHO_AM_I_REG , &whoami) && whoami == QMI8658_WHO_AM_I_VALUE ) {
92- return true ;
93- }
94- if (readI2CReg8 (I2C_ADDR_QMI8658_ALT , QMI8658_WHO_AM_I_REG , &whoami) && whoami == QMI8658_WHO_AM_I_VALUE ) {
95- return true ;
96- }
97- return false ;
98- }
99-
100- X3ProbeResult runX3ProbePass () {
101- X3ProbeResult result;
102- Wire.begin (X3_I2C_SDA , X3_I2C_SCL , X3_I2C_FREQ );
103- Wire.setTimeOut (6 );
104-
105- result.bq27220 = probeBQ27220Signature ();
106- result.ds3231 = probeDS3231Signature ();
107- result.qmi8658 = probeQMI8658Signature ();
108-
109- Wire.end ();
110- pinMode (20 , INPUT );
111- pinMode (0 , INPUT );
112- return result;
113- }
114-
11542} // namespace X3GPIO
11643
11744namespace {
@@ -162,24 +89,19 @@ HalGPIO::DeviceType detectDeviceTypeWithFingerprint() {
16289 return nvsToDeviceType (cachedValue);
16390 }
16491
165- // No cache yet: run active X3 fingerprint probe and persist result.
166- const X3GPIO ::X3ProbeResult pass1 = X3GPIO::runX3ProbePass ();
167- delay (2 );
168- const X3GPIO ::X3ProbeResult pass2 = X3GPIO::runX3ProbePass ();
169-
170- const uint8_t score1 = pass1.score ();
171- const uint8_t score2 = pass2.score ();
172- LOG_INF (" HW" , " X3 probe scores: pass1=%u(bq=%d rtc=%d imu=%d) pass2=%u(bq=%d rtc=%d imu=%d)" , score1, pass1.bq27220 ,
173- pass1.ds3231 , pass1.qmi8658 , score2, pass2.bq27220 , pass2.ds3231 , pass2.qmi8658 );
174- const bool x3Confirmed = (score1 >= 2 ) && (score2 >= 2 );
175- const bool x4Confirmed = (score1 == 0 ) && (score2 == 0 );
92+ // No cache yet: use FreeInk's canonical two-pass X3 fingerprint and persist
93+ // only confirmed results. Inconclusive probes deliberately remain uncached.
94+ uint8_t score1 = 0 ;
95+ uint8_t score2 = 0 ;
96+ const freeink::XteinkVerdict verdict = freeink::detectXteinkVerdict (&score1, &score2);
97+ LOG_INF (" HW" , " Xteink probe scores: pass1=%u pass2=%u verdict=%u" , score1, score2, static_cast <unsigned >(verdict));
17698
177- if (x3Confirmed ) {
99+ if (verdict == freeink::XteinkVerdict::X3Confirmed ) {
178100 writeNvsDeviceValue (NVS_KEY_DEV_CACHED , NvsDeviceValue::X3 );
179101 return HalGPIO::DeviceType::X3 ;
180102 }
181103
182- if (x4Confirmed ) {
104+ if (verdict == freeink::XteinkVerdict::X4Confirmed ) {
183105 writeNvsDeviceValue (NVS_KEY_DEV_CACHED , NvsDeviceValue::X4 );
184106 return HalGPIO::DeviceType::X4 ;
185107 }
@@ -191,15 +113,27 @@ HalGPIO::DeviceType detectDeviceTypeWithFingerprint() {
191113} // namespace
192114
193115void HalGPIO::begin () {
194- inputMgr.begin ();
195- SPI .begin (EPD_SCLK , SPI_MISO , EPD_MOSI , EPD_CS );
196-
116+ #if FREEINK_MCU_C3
197117#ifdef FORCE_DEVICE_X3
198118 _deviceType = DeviceType::X3 ;
199119 LOG_INF (" HW" , " Device override active via build flag: X3" );
200120#else
201121 _deviceType = detectDeviceTypeWithFingerprint ();
202122#endif
123+ BoardConfig::selectDevice (deviceIsX3 () ? BoardConfig::Board::XteinkX3 : BoardConfig::Board::XteinkX4);
124+
125+ // Resolve the per-batch controller before SPI owns the display pins.
126+ freeink::applyXteinkDisplayController ();
127+ if (deviceIsX3 () && BoardConfig::ACTIVE .displayController == BoardConfig::DisplayController::UC8279 ) {
128+ BoardConfig::selectDevice (BoardConfig::Board::XteinkX3Uc8279);
129+ }
130+
131+ SPI .begin (EPD_SCLK , SPI_MISO , EPD_MOSI , EPD_CS );
132+ #else
133+ _deviceType = DeviceType::X4 ;
134+ #endif
135+
136+ inputMgr.begin ();
203137
204138 if (deviceIsX4 ()) {
205139 pinMode (BAT_GPIO0 , INPUT );
@@ -297,7 +231,8 @@ HalGPIO::WakeupReason HalGPIO::getWakeupReason() const {
297231
298232 const bool usbConnected = isUsbConnected ();
299233
300- if (wakeupCause == ESP_SLEEP_WAKEUP_GPIO && resetReason == ESP_RST_DEEPSLEEP ) {
234+ if (resetReason == ESP_RST_DEEPSLEEP &&
235+ (wakeupCause == ESP_SLEEP_WAKEUP_GPIO || wakeupCause == ESP_SLEEP_WAKEUP_EXT1 )) {
301236 return WakeupReason::PowerButton;
302237 }
303238 if (wakeupCause == ESP_SLEEP_WAKEUP_UNDEFINED && resetReason == ESP_RST_POWERON && !usbConnected) {
0 commit comments