cryptnox-pos
1.0.0
Standalone USDC payment terminal firmware (ESP32 + Cryptnox smart card)
Toggle main menu visibility
Loading...
Searching...
No Matches
ui_hal.cpp
Go to the documentation of this file.
1
/*
2
* SPDX-License-Identifier: LGPL-3.0-or-later
3
* Copyright (c) 2026 Cryptnox SA
4
*/
5
11
12
#include "
ui_internal.h
"
13
14
TFT_eSPI
tft
;
15
SPIClass
touchSPI
(VSPI);
16
XPT2046_Touchscreen
touch
(
T_CS
,
T_IRQ
);
17
/* Partial draw buffer — 40 lines (no PSRAM on the CYD, keep it small). */
18
lv_color_t
s_buf
[
SCR_W
* 40];
19
lv_disp_draw_buf_t
s_draw_buf
;
20
lv_disp_drv_t
s_disp_drv
;
21
lv_indev_drv_t
s_indev_drv
;
22
void
disp_flush
(lv_disp_drv_t *drv,
const
lv_area_t *area, lv_color_t *px) {
23
uint32_t w = (area->x2 - area->x1 + 1);
24
uint32_t h = (area->y2 - area->y1 + 1);
25
26
tft
.startWrite();
27
tft
.setAddrWindow(area->x1, area->y1, w, h);
28
/* swap=true converts LVGL's native 16-bit order to the panel's. If colours
29
* come out byte-swapped/garbled, flip this bool or set CONFIG_LV_COLOR_16_SWAP. */
30
tft
.pushColors(
reinterpret_cast<
uint16_t *
>
(&px->full), w * h,
true
);
31
tft
.endWrite();
32
33
lv_disp_flush_ready(drv);
34
}
35
36
/* Raw XPT2046 counts at the panel's edges, cached from NVS (indev_read runs
37
* every few ms, no place to open flash). The 200/3800 defaults are close enough
38
* on most CYDs to reach the calibration screen on an uncalibrated panel. */
39
uint16_t
s_cal_xmin
= 200U;
40
uint16_t
s_cal_xmax
= 3800U;
41
uint16_t
s_cal_ymin
= 200U;
42
uint16_t
s_cal_ymax
= 3800U;
43
void
touch_cal_load
(
void
) {
44
settings_get_touch_cal
(&
s_cal_xmin
, &
s_cal_xmax
, &
s_cal_ymin
, &
s_cal_ymax
);
45
}
46
47
/* Uncalibrated sample, for the calibration screen itself — the only caller that
48
* must not go through the mapping it is measuring. */
49
bool
touch_raw
(int16_t *rx, int16_t *ry) {
50
if
(!
touch
.tirqTouched() || !
touch
.touched()) {
return
false
; }
51
TS_Point p =
touch
.getPoint();
52
*rx = p.x;
53
*ry = p.y;
54
return
true
;
55
}
56
57
/* @p sz is the raw pressure, for the second-contact guard — see
58
* touch_jump_filter(). Higher means a harder press or one more finger. */
59
static
bool
touch_to_screen
(int16_t *sx, int16_t *sy, int16_t *sz) {
60
if
(!
touch
.tirqTouched() || !
touch
.touched()) {
61
return
false
;
62
}
63
TS_Point p =
touch
.getPoint();
64
*sz = (int16_t)p.z;
65
int16_t mx = map(p.x,
s_cal_xmin
,
s_cal_xmax
, 0,
SCR_W
);
66
int16_t my = map(p.y,
s_cal_ymin
,
s_cal_ymax
, 0,
SCR_H
);
67
if
(mx < 0) { mx = 0; }
68
if
(mx >=
SCR_W
) { mx =
SCR_W
- 1; }
69
if
(my < 0) { my = 0; }
70
if
(my >=
SCR_H
) { my =
SCR_H
- 1; }
71
*sx = mx;
72
*sy = my;
73
return
true
;
74
}
75
76
/* Swallow taps carried over from the screen we just left: a press is ignored
77
* until this tick (ms) AND until the finger has been released at least once.
78
* Set on every screen (re)build in render_requested_screen(). */
79
uint32_t
s_input_block_until
= 0;
80
bool
s_wait_release
=
false
;
81
82
/* The white card the sale-flow screens draw into — see build_page(). NULL on
83
* every other screen, which is how the driver below tells the two apart. */
84
lv_obj_t *
s_page_card
= NULL;
85
86
/* Second-contact guard — see touch_jump_filter(). Sale screens only: every
87
* control there is a tap, so nothing legitimately jumps, while the admin panel's
88
* sliders and scrolling lists cross far more than 25px in a 30ms read period. */
89
static
touch_jump_t
s_jump
= { 0, 0, 0,
false
};
90
static
bool
s_jump_logged
=
false
;
/* one log line per press */
91
92
/* Swipe up from the bottom edge — the admin panel's door. Detected in the
93
* driver, not as an LVGL gesture: the screen is covered by a keypad and a
94
* Charge button, and a gesture delivered to a button never reaches the screen.
95
*
96
* The travel is deliberately longer than a fat-finger slip on the Charge button
97
* directly above the handle — that button commits a sale, so the two must not be
98
* confusable in either direction. */
99
#define SWIPE_BAND_H 44
/* press must START in this bottom band */
100
#define SWIPE_MIN_DY 58
/* ...and travel at least this far up */
101
static
bool
s_swipe_armed
=
false
;
102
static
int16_t
s_swipe_y0
= 0;
103
volatile
bool
s_swipe_admin
=
false
;
/* handed to the UI task loop */
104
void
indev_read
(lv_indev_drv_t *drv, lv_indev_data_t *data) {
105
(void)drv;
106
int16_t x, y, z = 0;
107
bool
pressed =
touch_to_screen
(&x, &y, &z);
108
109
/* Two fingers read as one point between them — hold the first one's. Ahead
110
* of the swipe so an artifact cannot arm it, and kept on for an armed press.
111
* The swipe's travel uses the raw point: that drag is the one big move on a
112
* sale screen, and a false swipe only opens a PIN-locked screen. */
113
const
int16_t raw_x = x, raw_y = y;
114
if
(!pressed || (
s_page_card
!= NULL)) {
115
touch_jump_filter
(&
s_jump
, pressed, z, &x, &y);
116
}
117
118
/* The numbers TOUCH_Z_STEP_PCT is tuned from. Once per press, at INFO
119
* (CONFIG_LOG_MAXIMUM_LEVEL=3 compiles ESP_LOGD/V away). Never fires with a
120
* thumb down: lower the percentage. Fires on one-finger taps: raise it. */
121
if
(!pressed) {
122
s_jump_logged
=
false
;
123
}
else
if
(!
s_jump_logged
&& ((x != raw_x) || (y != raw_y))) {
124
s_jump_logged
=
true
;
125
ESP_LOGI(
TAG
,
"second contact: %d,%d held at %d,%d (z=%d, z_min=%d)"
,
126
(
int
)raw_x, (
int
)raw_y, (
int
)x, (
int
)y, (
int
)z,
127
(
int
)
s_jump
.z_min);
128
}
129
130
/* Bottom-edge swipe up. Armed on a press that starts in the band, fired
131
* once it has travelled far enough; the rest of the drag is swallowed via
132
* s_wait_release so the button under the finger never sees a click. The UI
133
* task decides which screen it is allowed on. */
134
if
(pressed) {
135
if
(!
s_swipe_armed
&& (y >= (
SCR_H
-
SWIPE_BAND_H
))) {
136
s_swipe_armed
=
true
;
137
s_swipe_y0
= y;
138
}
else
if
(
s_swipe_armed
&& ((
s_swipe_y0
- raw_y) >=
SWIPE_MIN_DY
)) {
139
s_swipe_armed
=
false
;
140
s_swipe_admin
=
true
;
141
s_wait_release
=
true
;
142
data->state = LV_INDEV_STATE_REL;
143
return
;
144
}
145
}
else
{
146
s_swipe_armed
=
false
;
147
}
148
149
/* After a screen change, ignore a lingering or reflexive tap from the old
150
* screen (e.g. cancelling the tx right after validating the PIN). */
151
if
(lv_tick_get() <
s_input_block_until
||
s_wait_release
) {
152
if
(!pressed) {
s_wait_release
=
false
; }
/* finger lifted — re-arm */
153
data->state = LV_INDEV_STATE_REL;
154
return
;
155
}
156
157
if
(pressed) {
158
data->state = LV_INDEV_STATE_PR;
159
data->point.x = x;
160
data->point.y = y;
161
}
else
{
162
data->state = LV_INDEV_STATE_REL;
163
}
164
}
165
void
tick_cb
(
void
*arg) {
166
(void)arg;
167
lv_tick_inc(
LV_TICK_PERIOD_MS
);
168
}
169
170
/******************************************************************
171
* 4b. Backlight — LEDC PWM dimming on the CYD's GPIO 21
172
******************************************************************/
173
#define BL_GPIO 21
174
#define BL_LEDC_MODE LEDC_LOW_SPEED_MODE
175
#define BL_LEDC_TIMER LEDC_TIMER_0
176
#define BL_LEDC_CH LEDC_CHANNEL_0
177
#define BL_LEDC_RES LEDC_TIMER_10_BIT
/* duty 0..1023 */
178
#define BL_PWM_HZ 5000
/* above the audible range */
179
uint8_t
s_brightness
= 80;
/* backlight %, restored from NVS */
180
void
backlight_set_pct
(uint8_t pct) {
181
if
(pct > 100U) { pct = 100U; }
182
uint32_t duty = (1023U * pct) / 100U;
183
(void)ledc_set_duty(
BL_LEDC_MODE
,
BL_LEDC_CH
, duty);
184
(void)ledc_update_duty(
BL_LEDC_MODE
,
BL_LEDC_CH
);
185
}
186
void
backlight_init
(uint8_t pct) {
187
ledc_timer_config_t t = {};
188
t.speed_mode =
BL_LEDC_MODE
;
189
t.duty_resolution =
BL_LEDC_RES
;
190
t.timer_num =
BL_LEDC_TIMER
;
191
t.freq_hz =
BL_PWM_HZ
;
192
t.clk_cfg = LEDC_AUTO_CLK;
193
(void)ledc_timer_config(&t);
194
195
ledc_channel_config_t c = {};
196
c.gpio_num =
BL_GPIO
;
197
c.speed_mode =
BL_LEDC_MODE
;
198
c.channel =
BL_LEDC_CH
;
199
c.timer_sel =
BL_LEDC_TIMER
;
200
c.duty = 0;
201
c.hpoint = 0;
202
(void)ledc_channel_config(&c);
203
204
backlight_set_pct
(pct);
205
}
TAG
static const char *const TAG
Definition
eth_rpc.cpp:33
settings_get_touch_cal
void settings_get_touch_cal(uint16_t *x_min, uint16_t *x_max, uint16_t *y_min, uint16_t *y_max)
Raw XPT2046 range that maps to the panel's four edges.
Definition
settings.cpp:260
touch_jump_t
Definition
touch_cal.h:150
touch_jump_filter
static void touch_jump_filter(touch_jump_t *st, bool pressed, int16_t z, int16_t *x, int16_t *y)
Definition
touch_cal.h:172
BL_GPIO
#define BL_GPIO
Definition
ui_hal.cpp:173
BL_LEDC_TIMER
#define BL_LEDC_TIMER
Definition
ui_hal.cpp:175
s_swipe_admin
volatile bool s_swipe_admin
Definition
ui_hal.cpp:103
SWIPE_MIN_DY
#define SWIPE_MIN_DY
Definition
ui_hal.cpp:100
BL_LEDC_MODE
#define BL_LEDC_MODE
Definition
ui_hal.cpp:174
s_indev_drv
lv_indev_drv_t s_indev_drv
Definition
ui_hal.cpp:21
touch_to_screen
static bool touch_to_screen(int16_t *sx, int16_t *sy, int16_t *sz)
Definition
ui_hal.cpp:59
touch_raw
bool touch_raw(int16_t *rx, int16_t *ry)
Definition
ui_hal.cpp:49
s_page_card
lv_obj_t * s_page_card
Definition
ui_hal.cpp:84
SWIPE_BAND_H
#define SWIPE_BAND_H
Definition
ui_hal.cpp:99
s_swipe_y0
static int16_t s_swipe_y0
Definition
ui_hal.cpp:102
tick_cb
void tick_cb(void *arg)
Definition
ui_hal.cpp:165
BL_LEDC_CH
#define BL_LEDC_CH
Definition
ui_hal.cpp:176
s_swipe_armed
static bool s_swipe_armed
Definition
ui_hal.cpp:101
s_wait_release
bool s_wait_release
Definition
ui_hal.cpp:80
backlight_set_pct
void backlight_set_pct(uint8_t pct)
Definition
ui_hal.cpp:180
touch_cal_load
void touch_cal_load(void)
Definition
ui_hal.cpp:43
s_cal_ymax
uint16_t s_cal_ymax
Definition
ui_hal.cpp:42
s_draw_buf
lv_disp_draw_buf_t s_draw_buf
Definition
ui_hal.cpp:19
s_jump_logged
static bool s_jump_logged
Definition
ui_hal.cpp:90
s_jump
static touch_jump_t s_jump
Definition
ui_hal.cpp:89
indev_read
void indev_read(lv_indev_drv_t *drv, lv_indev_data_t *data)
Definition
ui_hal.cpp:104
s_brightness
uint8_t s_brightness
Definition
ui_hal.cpp:179
tft
TFT_eSPI tft
Definition
ui_hal.cpp:14
s_disp_drv
lv_disp_drv_t s_disp_drv
Definition
ui_hal.cpp:20
s_input_block_until
uint32_t s_input_block_until
Definition
ui_hal.cpp:79
s_cal_xmin
uint16_t s_cal_xmin
Definition
ui_hal.cpp:39
s_cal_ymin
uint16_t s_cal_ymin
Definition
ui_hal.cpp:41
s_buf
lv_color_t s_buf[SCR_W *40]
Definition
ui_hal.cpp:18
s_cal_xmax
uint16_t s_cal_xmax
Definition
ui_hal.cpp:40
disp_flush
void disp_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *px)
Definition
ui_hal.cpp:22
BL_LEDC_RES
#define BL_LEDC_RES
Definition
ui_hal.cpp:177
backlight_init
void backlight_init(uint8_t pct)
Definition
ui_hal.cpp:186
BL_PWM_HZ
#define BL_PWM_HZ
Definition
ui_hal.cpp:178
ui_internal.h
Private to the ui*.cpp files: the state and helpers they share.
T_CS
#define T_CS
Definition
ui_internal.h:100
LV_TICK_PERIOD_MS
#define LV_TICK_PERIOD_MS
Definition
ui_internal.h:212
touch
XPT2046_Touchscreen touch
T_IRQ
#define T_IRQ
Definition
ui_internal.h:102
SCR_H
#define SCR_H
Definition
ui_internal.h:113
SCR_W
#define SCR_W
Definition
ui_internal.h:111
touchSPI
SPIClass touchSPI
main
ui_hal.cpp
Generated by
1.17.0