Skip to content

Commit 7421953

Browse files
authored
InkHUD: Add full touch support to T5s3 (#10286)
* InkHUD touch rework * Applet Switcher * Update ED047TC1.cpp * trunk fix * Custom tip screen for T5s3 * Update TouchScreenImpl1.cpp * Update ED047TC1.cpp * Delete variant.cpp
1 parent 9306e66 commit 7421953

31 files changed

Lines changed: 3020 additions & 591 deletions

src/PowerFSM.cpp

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,35 @@ static bool isPowered()
5858
return !isPowerSavingMode && powerStatus && (!powerStatus->getHasBattery() || powerStatus->getHasUSB());
5959
}
6060

61+
#if defined(T5_S3_EPAPER_PRO)
62+
static void t5BacklightOffForSleep()
63+
{
64+
t5BacklightSetForcedBySleep(true);
65+
}
66+
67+
static void t5BacklightWakeFromSleep()
68+
{
69+
t5BacklightSetForcedBySleep(false);
70+
}
71+
72+
static void t5BacklightOffForTimeout()
73+
{
74+
t5BacklightSetForcedByTimeout(true);
75+
t5TouchSetForcedByTimeout(true);
76+
}
77+
78+
static void t5BacklightOnFromUserInput()
79+
{
80+
t5BacklightHandleUserInput();
81+
t5TouchHandleUserInput();
82+
}
83+
#else
84+
static void t5BacklightOffForSleep() {}
85+
static void t5BacklightWakeFromSleep() {}
86+
static void t5BacklightOffForTimeout() {}
87+
static void t5BacklightOnFromUserInput() {}
88+
#endif
89+
6190
static void sdsEnter()
6291
{
6392
LOG_POWERFSM("State: SDS");
@@ -87,6 +116,7 @@ static void lsEnter()
87116
LOG_POWERFSM("lsEnter begin, ls_secs=%u", config.power.ls_secs);
88117
if (screen)
89118
screen->setOn(false);
119+
t5BacklightOffForSleep();
90120
secsSlept = 0; // How long have we been sleeping this time
91121

92122
// LOG_INFO("lsEnter end");
@@ -159,6 +189,8 @@ static void lsIdle()
159189
static void lsExit()
160190
{
161191
LOG_POWERFSM("State: lsExit");
192+
// Lift the light-sleep force-off gate when leaving LS.
193+
t5BacklightWakeFromSleep();
162194
}
163195

164196
static void nbEnter()
@@ -180,6 +212,8 @@ static void darkEnter()
180212
setBluetoothEnable(true);
181213
if (screen)
182214
screen->setOn(false);
215+
// Screen timeout enters DARK; ensure backlight also turns off.
216+
t5BacklightOffForTimeout();
183217
}
184218

185219
static void serialEnter()
@@ -289,12 +323,13 @@ void PowerFSM_setup()
289323
powerFSM.add_transition(&stateNB, &stateNB, EVENT_PACKET_FOR_PHONE, NULL, "Received packet, resetting win wake");
290324

291325
// Handle press events - note: we ignore button presses when in API mode
292-
powerFSM.add_transition(&stateLS, &stateON, EVENT_PRESS, NULL, "Press");
293-
powerFSM.add_transition(&stateNB, &stateON, EVENT_PRESS, NULL, "Press");
294-
powerFSM.add_transition(&stateDARK, isPowered() ? &statePOWER : &stateON, EVENT_PRESS, NULL, "Press");
295-
powerFSM.add_transition(&statePOWER, &statePOWER, EVENT_PRESS, NULL, "Press");
296-
powerFSM.add_transition(&stateON, &stateON, EVENT_PRESS, NULL, "Press"); // reenter On to restart our timers
297-
powerFSM.add_transition(&stateSERIAL, &stateSERIAL, EVENT_PRESS, NULL,
326+
powerFSM.add_transition(&stateLS, &stateON, EVENT_PRESS, t5BacklightOnFromUserInput, "Press");
327+
powerFSM.add_transition(&stateNB, &stateON, EVENT_PRESS, t5BacklightOnFromUserInput, "Press");
328+
powerFSM.add_transition(&stateDARK, isPowered() ? &statePOWER : &stateON, EVENT_PRESS, t5BacklightOnFromUserInput, "Press");
329+
powerFSM.add_transition(&statePOWER, &statePOWER, EVENT_PRESS, t5BacklightOnFromUserInput, "Press");
330+
powerFSM.add_transition(&stateON, &stateON, EVENT_PRESS, t5BacklightOnFromUserInput,
331+
"Press"); // reenter On to restart our timers
332+
powerFSM.add_transition(&stateSERIAL, &stateSERIAL, EVENT_PRESS, t5BacklightOnFromUserInput,
298333
"Press"); // Allow button to work while in serial API
299334

300335
// Handle critically low power battery by forcing deep sleep
@@ -314,11 +349,13 @@ void PowerFSM_setup()
314349
powerFSM.add_transition(&stateSERIAL, &stateSHUTDOWN, EVENT_SHUTDOWN, NULL, "Shutdown");
315350

316351
// Inputbroker
317-
powerFSM.add_transition(&stateLS, &stateON, EVENT_INPUT, NULL, "Input Device");
318-
powerFSM.add_transition(&stateNB, &stateON, EVENT_INPUT, NULL, "Input Device");
319-
powerFSM.add_transition(&stateDARK, &stateON, EVENT_INPUT, NULL, "Input Device");
320-
powerFSM.add_transition(&stateON, &stateON, EVENT_INPUT, NULL, "Input Device"); // restarts the sleep timer
321-
powerFSM.add_transition(&statePOWER, &statePOWER, EVENT_INPUT, NULL, "Input Device"); // restarts the sleep timer
352+
powerFSM.add_transition(&stateLS, &stateON, EVENT_INPUT, t5BacklightOnFromUserInput, "Input Device");
353+
powerFSM.add_transition(&stateNB, &stateON, EVENT_INPUT, t5BacklightOnFromUserInput, "Input Device");
354+
powerFSM.add_transition(&stateDARK, &stateON, EVENT_INPUT, t5BacklightOnFromUserInput, "Input Device");
355+
powerFSM.add_transition(&stateON, &stateON, EVENT_INPUT, t5BacklightOnFromUserInput,
356+
"Input Device"); // restarts the sleep timer
357+
powerFSM.add_transition(&statePOWER, &statePOWER, EVENT_INPUT, t5BacklightOnFromUserInput,
358+
"Input Device"); // restarts the sleep timer
322359

323360
powerFSM.add_transition(&stateDARK, &stateON, EVENT_BLUETOOTH_PAIR, NULL, "Bluetooth pairing");
324361
powerFSM.add_transition(&stateON, &stateON, EVENT_BLUETOOTH_PAIR, NULL, "Bluetooth pairing");

src/graphics/niche/Drivers/EInk/ED047TC1.cpp

Lines changed: 142 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,116 @@
2525

2626
using namespace NicheGraphics::Drivers;
2727

28+
#if defined(T5_S3_EPAPER_PRO_V2)
29+
// FastEPD helper symbols are defined in FastEPD.inl with C++ linkage.
30+
extern void bbepPCA9535DigitalWrite(uint8_t pin, uint8_t value);
31+
extern uint8_t bbepPCA9535DigitalRead(uint8_t pin);
32+
extern int bbepI2CWrite(unsigned char iAddr, unsigned char *pData, int iLen);
33+
extern int bbepI2CReadRegister(unsigned char iAddr, unsigned char u8Register, unsigned char *pData, int iLen);
34+
#endif
35+
36+
namespace
37+
{
38+
#if defined(T5_S3_EPAPER_PRO_V2)
39+
// FastEPD default V2 power callback blocks forever waiting for PWRGOOD.
40+
// Replace it with a timeout-safe version so boot never deadlocks.
41+
int safeEPDiyV7EinkPower(void *pBBEP, int bOn)
42+
{
43+
static bool warnedPgood = false;
44+
static bool warnedTpsPg = false;
45+
static bool warnedTpsWrite = false;
46+
47+
FASTEPDSTATE *pState = static_cast<FASTEPDSTATE *>(pBBEP);
48+
if (!pState) {
49+
return BBEP_ERROR_BAD_PARAMETER;
50+
}
51+
52+
if (bOn == pState->pwr_on) {
53+
return BBEP_SUCCESS;
54+
}
55+
56+
if (bOn) {
57+
bbepPCA9535DigitalWrite(8, 1); // OE on
58+
bbepPCA9535DigitalWrite(9, 1); // GMOD on
59+
bbepPCA9535DigitalWrite(13, 1); // WAKEUP on
60+
bbepPCA9535DigitalWrite(11, 1); // PWRUP on
61+
bbepPCA9535DigitalWrite(12, 1); // VCOM CTRL on
62+
delay(1);
63+
64+
const uint32_t pgoodStart = millis();
65+
bool pgoodSeen = false;
66+
while (!bbepPCA9535DigitalRead(14)) { // CFG_PIN_PWRGOOD
67+
if ((millis() - pgoodStart) > 1200) {
68+
if (!warnedPgood) {
69+
LOG_WARN("ED047TC1: PWRGOOD timeout, continuing with fallback power-on path");
70+
warnedPgood = true;
71+
}
72+
break;
73+
}
74+
delay(1);
75+
}
76+
if (bbepPCA9535DigitalRead(14)) {
77+
pgoodSeen = true;
78+
}
79+
80+
uint8_t ucTemp[4] = {0};
81+
ucTemp[0] = 0x01; // TPS_REG_ENABLE
82+
ucTemp[1] = 0x3f; // enable rails
83+
const int tpsEnableRc = bbepI2CWrite(0x68, ucTemp, 2);
84+
85+
const int vcom = pState->iVCOM / -10;
86+
ucTemp[0] = 3; // VCOM registers 3+4 (L + H)
87+
ucTemp[1] = static_cast<uint8_t>(vcom);
88+
ucTemp[2] = static_cast<uint8_t>(vcom >> 8);
89+
const int tpsVcomRc = bbepI2CWrite(0x68, ucTemp, 3);
90+
if ((tpsEnableRc == 0 || tpsVcomRc == 0) && !warnedTpsWrite) {
91+
LOG_WARN("ED047TC1: TPS write did not ACK, continuing with fallback");
92+
warnedTpsWrite = true;
93+
}
94+
95+
int iTimeout = 0;
96+
uint8_t u8Value = 0;
97+
while (iTimeout < 220 && ((u8Value & 0xfa) != 0xfa)) {
98+
bbepI2CReadRegister(0x68, 0x0F, &u8Value, 1); // TPS_REG_PG
99+
iTimeout++;
100+
delay(1);
101+
}
102+
if (iTimeout >= 220 && !warnedTpsPg) {
103+
if (pgoodSeen) {
104+
LOG_WARN("ED047TC1: TPS power-good register timeout, panel may still work");
105+
} else {
106+
LOG_WARN("ED047TC1: TPS power-good register timeout after PWRGOOD fallback");
107+
}
108+
warnedTpsPg = true;
109+
}
110+
111+
pState->pwr_on = 1;
112+
} else {
113+
bbepPCA9535DigitalWrite(8, 0); // OE off
114+
bbepPCA9535DigitalWrite(9, 0); // GMOD off
115+
bbepPCA9535DigitalWrite(11, 0); // PWRUP off
116+
bbepPCA9535DigitalWrite(12, 0); // VCOM CTRL off
117+
delay(1);
118+
bbepPCA9535DigitalWrite(13, 0); // WAKEUP off
119+
pState->pwr_on = 0;
120+
}
121+
122+
return BBEP_SUCCESS;
123+
}
124+
#endif
125+
126+
class SafeFastEPD : public FASTEPD
127+
{
128+
public:
129+
void installSafePowerHandler()
130+
{
131+
#if defined(T5_S3_EPAPER_PRO_V2)
132+
_state.pfnEinkPower = safeEPDiyV7EinkPower;
133+
#endif
134+
}
135+
};
136+
} // namespace
137+
28138
void ED047TC1::begin(SPIClass *spi, uint8_t pin_dc, uint8_t pin_cs, uint8_t pin_busy, uint8_t pin_rst)
29139
{
30140
// Parallel display — SPI parameters are not used
@@ -34,24 +144,48 @@ void ED047TC1::begin(SPIClass *spi, uint8_t pin_dc, uint8_t pin_cs, uint8_t pin_
34144
(void)pin_busy;
35145
(void)pin_rst;
36146

37-
epaper = new FASTEPD;
147+
SafeFastEPD *safeEpaper = new SafeFastEPD;
148+
epaper = safeEpaper;
38149

150+
int initRc = BBEP_ERROR_BAD_PARAMETER;
39151
#if defined(T5_S3_EPAPER_PRO_V1)
40-
epaper->initPanel(BB_PANEL_LILYGO_T5PRO, 28000000);
152+
initRc = epaper->initPanel(BB_PANEL_LILYGO_T5PRO, 28000000);
41153
#elif defined(T5_S3_EPAPER_PRO_V2)
42-
epaper->initPanel(BB_PANEL_LILYGO_T5PRO_V2, 28000000);
154+
initRc = epaper->initPanel(BB_PANEL_LILYGO_T5PRO_V2, 28000000);
43155
// Initialize all PCA9535 port-0 pins as outputs / HIGH
44156
for (int i = 0; i < 8; i++) {
45157
epaper->ioPinMode(i, OUTPUT);
46158
epaper->ioWrite(i, HIGH);
47159
}
160+
// On this board, the physical side key is labeled IO48; electrically it maps to PCA9535 IO12 (bit 2 on port-1).
161+
// FastEPD's generic V7 init drives 8..13 as outputs; force IO12 back to input
162+
// so variant touch-control polling can read the key reliably.
163+
epaper->ioPinMode(10, INPUT);
48164
#else
49165
#error "ED047TC1 driver: unsupported variant — define T5_S3_EPAPER_PRO_V1 or T5_S3_EPAPER_PRO_V2"
50166
#endif
51167

52-
epaper->setMode(BB_MODE_1BPP);
53-
epaper->clearWhite();
54-
epaper->fullUpdate(true); // Blocking initial clear
168+
if (initRc != BBEP_SUCCESS) {
169+
LOG_ERROR("ED047TC1 initPanel failed rc=%d", initRc);
170+
return;
171+
}
172+
173+
safeEpaper->installSafePowerHandler();
174+
175+
const int modeRc = epaper->setMode(BB_MODE_1BPP);
176+
if (modeRc != BBEP_SUCCESS) {
177+
LOG_WARN("ED047TC1 setMode failed rc=%d", modeRc);
178+
}
179+
180+
const int clearRc = epaper->clearWhite();
181+
if (clearRc != BBEP_SUCCESS) {
182+
LOG_WARN("ED047TC1 clearWhite failed rc=%d", clearRc);
183+
}
184+
185+
const int fullRc = epaper->fullUpdate(true); // Blocking initial clear
186+
if (fullRc != BBEP_SUCCESS) {
187+
LOG_WARN("ED047TC1 initial fullUpdate failed rc=%d", fullRc);
188+
}
55189
}
56190

57191
void ED047TC1::update(uint8_t *imageData, UpdateTypes type)
@@ -111,9 +245,8 @@ void ED047TC1::update(uint8_t *imageData, UpdateTypes type)
111245
epaper->fullUpdate(CLEAR_SLOW, false);
112246
epaper->backupPlane(); // Sync pPrevious so next partialUpdate has a correct baseline
113247
} else {
114-
// FAST: true partial update — compares pCurrent vs pPrevious and only applies
115-
// the update waveform to rows that actually changed. Unchanged rows get a neutral
116-
// signal (no visible effect). partialUpdate() updates pPrevious internally.
248+
// FAST: true partial update - compares pCurrent vs pPrevious and only applies
249+
// update waveform to rows that changed. partialUpdate() updates pPrevious.
117250
epaper->partialUpdate(false, 0, dstTotalRows - 1);
118251
}
119252
}

src/graphics/niche/InkHUD/Applet.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ class Applet : public GFX
104104
virtual void onFreeText(char c) {}
105105
virtual void onFreeTextDone() {}
106106
virtual void onFreeTextCancel() {}
107+
// Absolute display-space touch point, for touch-friendly UI interactions.
108+
// Return true if consumed.
109+
virtual bool onTouchPoint(uint16_t x, uint16_t y, bool longPress)
110+
{
111+
(void)x;
112+
(void)y;
113+
(void)longPress;
114+
return false;
115+
}
107116
// List of inputs which can be subscribed to
108117
enum InputMask { // | No Joystick | With Joystick |
109118
BUTTON_SHORT = 1, // | Button Click | Joystick Center Click |

0 commit comments

Comments
 (0)