cryptnox-pos 1.0.0
Standalone USDC payment terminal firmware (ESP32 + Cryptnox smart card)
Loading...
Searching...
No Matches
ui.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
16
17/******************************************************************
18 * 1. Included files
19 ******************************************************************/
20
21#include "ui_internal.h"
22
23const char *TAG = "ui";
24
25/* No ambient-light sensing: the enclosure covers the LDR on GPIO 34, so any
26 * reading is of the inside of the case. Brightness is the slider only. */
27
28/******************************************************************
29 * 5. Shared state (written by ui_show_* on the main task, read by ui_task)
30 ******************************************************************/
32
33static volatile bool s_screen_dirty = true;
35
36/* Amount entry — parsed from the keypad string; starts empty (0). */
37uint64_t s_amount_units = 0ULL;
38
39/* Confirm-screen payload */
40uint64_t s_confirm_amount = 0ULL;
41char s_confirm_addr[64] = "";
42char s_confirm_fee[40] = ""; /* "Fee up to 0.0013 ETH", or "" */
43
44/* Tx-status payload. The info line is retexted in place via s_tx_info_dirty
45 * (the confirmation countdown): a rebuild per tick would restart the spinner. */
47char s_tx_info[72] = ""; /* holds a 66-char EVM hash */
48lv_obj_t *s_tx_info_lbl = NULL;
49static volatile bool s_tx_info_dirty = false;
50uint8_t s_cal_step = 0U;
51int16_t s_cal_raw[2][2] = {{0, 0}, {0, 0}};
52bool s_cal_pressed = false;
53int16_t s_cal_last_x = 0;
54int16_t s_cal_last_y = 0;
55static uint16_t s_cal_prev[4] = {0, 0, 0, 0}; /* restored on cancel/timeout */
56uint32_t s_cal_deadline = 0U;
57lv_obj_t *s_cal_countdown = NULL;
58
59/* The sheet that rises on a swipe up. While set, build_admin_screen() builds
60 * into it and does NOT clear the screen, so the sale screen stays underneath.
61 * Valid only for one dispatch in render_requested_screen(). */
62lv_obj_t *s_sheet = NULL;
63volatile bool s_sheet_pending = false;
64
65/* The card-wait note. Its own buffer, not the transaction screen's, so a setup
66 * message cannot turn up under the spinner on a payment. */
67char s_card_note[64] = "";
68
69/* Amount entry — keypad input string (e.g. "12.50") and its display label. The
70 * cents are their own label so they can be set in a smaller font past 100; below
71 * that they stay in the main label and this one holds "". */
72lv_obj_t *s_amount_label = NULL;
73lv_obj_t *s_amount_cents_label = NULL;
74/* The flex row the two of them sit in — kept because its placement is not
75 * fixed: see amount_row_place(). */
76lv_obj_t *s_amount_row = NULL;
77uint64_t s_amount_cents = 0; /* amount entered, in cents */
78
79/* The asset selector on the amount row, and the chevron it drops when it has to
80 * make room. Both die with the screen — cleared in clear_screen(). */
81lv_obj_t *s_asset_btn = NULL;
82lv_obj_t *s_asset_arrow = NULL;
83
84/* The Charge button, kept because it is enabled and disabled as digits arrive
85 * and leave — see charge_set_enabled(). */
86lv_obj_t *s_charge_btn = NULL;
87
88/* The status band's clock. Built by build_page(), so only on the sale flow (the
89 * admin tab bar and setup headers own that band). Retexted by the status timer. */
90lv_obj_t *s_clock_lbl = NULL;
91/* Whether Charge was tapped and is waiting on main. A flag, not the button's
92 * state: the keypad stays live and amount_update_display() would otherwise
93 * re-enable it on the next digit — see charge_set_busy(). */
94bool s_charge_busy = false;
95
96/* PIN entry — the textarea (password mode) is the live input; s_pin is the
97 * handoff buffer read by main via ui_take_pin() and wiped on read. */
98lv_obj_t *s_pin_ta = NULL;
99char s_pin[16] = {0};
100uint8_t s_pin_len = 0;
102uint16_t s_ap_count = 0;
103char s_wifi_ssid[33] = {0}; /* selected network */
104char s_wifi_pass[65] = {0}; /* entered passphrase (handoff) */
105char s_wifi_note[64] = {0}; /* why the picker reopened (may be empty) */
106lv_obj_t *s_wifi_pass_ta = NULL;
107lv_obj_t *s_wifi_eye_lbl = NULL; /* glyph swapped on reveal/hide */
108lv_obj_t *s_pin_eye_lbl = NULL; /* same, on the card-PIN keypad */
109lv_obj_t *s_admin_eye_lbl = NULL; /* same, on the admin-code keypad */
110
111/* Progress screen (UI_SCREEN_WIFI_CONNECTING), two pieces rather than one
112 * preformatted line: the name is stored raw so the label elides it by real
113 * glyph width, which no character budget can do for every SSID. */
114char s_wifi_caption[24] = {0}; /* "Scanning..." / "Connecting to" */
115char s_wifi_name[33] = {0}; /* network name; empty for none */
116
117/* Splash progress line. Written by the main task, applied by the UI task
118 * (LVGL is not thread-safe) — same hand-off shape as request_screen(). */
119char s_boot_step[40] = {0};
120lv_obj_t *s_boot_step_lbl = NULL;
121static volatile bool s_boot_step_dirty = false;
122
123/* The Tx tab's two gas rows, same hand-off. The config page (HTTP task) writes
124 * the caps while its card is raised over this screen, so the rows are retexted
125 * in place: a rebuild would take that card down. */
126lv_obj_t *s_fee_max_lbl = NULL;
127lv_obj_t *s_fee_prio_lbl = NULL;
128static volatile bool s_fees_dirty = false;
129
130/* Phone setup (UI_SCREEN_PROV). An int, not a prov_step_t: provision.h
131 * includes ui.h. Written by the main task, read by the UI task. */
132volatile int s_prov_step = 0;
133/* Why the last card read came to nothing, shown on the setup screen (the
134 * browser gets it too, but the person who tapped is looking at the panel).
135 * Cleared whenever a fresh read starts. */
136char s_prov_msg[64] = "";
137static volatile bool s_addr_modal_dirty = false;
138/* Firmware uploaded from a browser and waiting to be accepted here. Same
139 * handoff as the address modal above: the HTTP task never touches LVGL. */
140static volatile bool s_ota_modal_dirty = false;
141
142/* Startup fault (UI_SCREEN_BOOT_ERROR). */
144char s_boot_detail[64] = {0};
145lv_obj_t *s_admin_ta = NULL;
146lv_obj_t *s_admin_note_lbl = NULL;
147char s_admin_first[ADMIN_CODE_MAX + 1] = {0}; /* 1st of 2 passes */
148char s_admin_note[48] = {0};
149bool s_admin_confirming = false; /* 2nd pass of the creation */
150static bool s_welcome_sent = false; /* Start already reported */
151char s_welcome_sub[96] = {0}; /* line under the brand: fits main's update greeting */
152/* Penalty clock, monotonic since boot (lv_tick_elaps handles the wrap). The
153 * attempt count itself lives in NVS, so power-cycling shortens the current wait
154 * but never resets the escalation. */
156uint32_t s_admin_lock_ms = 0;
157/* What a correct code on the unlock screen is for (admin panel or portal). One
158 * screen serves both so the lockout cannot drift between two copies. */
160
161/* Whether the PIN keypad is collecting a card PIN for a *read* (deriving a payout
162 * address) rather than for a payment. Same reason as above: the card refuses to
163 * export a public key without a verified PIN, so the screen is the same one. */
164bool s_pin_for_card = false;
165
166/* Destination info shown on the settings "Tx" tab (set by main, static). */
167const char *s_addr_usdc = NULL;
168const char *s_addr_dest = NULL;
169
170/* Settings bottom-bar buttons (Reset shares the line with Close on About). */
171lv_obj_t *s_reset_btn = NULL;
172lv_obj_t *s_close_btn = NULL;
173
174/* The networks the picker offers are pos_net_t (assets.h); several chains share
175 * one (USDC and USDT are both Ethereum). */
176
177static ui_screen_t s_settings_return = UI_SCREEN_AMOUNT; /* screen to go back to */
178/* Which tab a (re)built settings page opens on. Zeroed on a fresh open, kept
179 * across rebuilds from inside the page (an asset pick returns to the Tx tab). */
180uint16_t s_settings_tab = 0U;
181
182/* The sale's chain is read from NVS wherever needed; there is no UI-side copy. */
183/* The asset the admin Tx tab is showing. Its own, not the sale's: looking up
184 * another asset there must not change what the next customer is charged in.
185 * Seeded from the sale's asset on each fresh open. */
187
190 return pos_asset_of(c);
191}
192
194const char *asset_name(pos_chain_t c) {
195 return asset(c)->ticker;
196}
197
205 const pos_net_info_t *ni = pos_net_info(asset(c)->net);
206 return settings_net_str(ni->long_test, ni->long_main);
207}
208
211 return asset(c)->caption;
212}
213
214/* The buffers above are written by the main task (and a few by the HTTP task)
215 * through the public ui_* calls, and read by the UI task while it builds a
216 * screen; without a lock a screen could show a torn message or Wi-Fi record.
217 * The UI task holds this for each pass of its loop (render, the targeted label
218 * updates and lv_timer_handler with its event callbacks), and every public call
219 * that writes shared state holds it for the copy. Recursive: an event callback
220 * that reaches back into a public ui_* call on the UI task already owns it.
221 * Writers wait for at most one pass of the loop, a few milliseconds. */
222static SemaphoreHandle_t s_ui_mx = NULL;
223
224struct UiLock {
225 UiLock() { if (s_ui_mx != NULL) { (void)xSemaphoreTakeRecursive(s_ui_mx, portMAX_DELAY); } }
226 ~UiLock() { if (s_ui_mx != NULL) { (void)xSemaphoreGiveRecursive(s_ui_mx); } }
227 UiLock(const UiLock &) = delete;
228 UiLock &operator=(const UiLock &) = delete;
229};
230
231/* prov_stop() takes up to ~2 s, so main runs it, not the UI task: the UI only
232 * asks. Rate-limited to once a second, since the deadline check would otherwise
233 * ask every 5 ms; a request lost to a full queue is simply asked again. */
234static uint32_t s_prov_stop_at = 0U;
235
236static void request_prov_stop(void) {
237 if ((s_prov_stop_at != 0U) && (lv_tick_elaps(s_prov_stop_at) < 1000U)) { return; }
238 s_prov_stop_at = lv_tick_get() | 1U; /* never 0 once asked */
239 if (s_cb != NULL) { s_cb(UI_EVENT_PROV_STOP, 0); }
240}
242 s_req_screen = s;
243 s_screen_dirty = true;
244}
245
246/* Sets both pieces at once, so no caller can leave a stale name behind. */
247void set_wifi_progress(const char *caption, const char *name) {
248 strncpy(s_wifi_caption, (caption != NULL) ? caption : "",
249 sizeof(s_wifi_caption) - 1);
250 s_wifi_caption[sizeof(s_wifi_caption) - 1] = '\0';
251 strncpy(s_wifi_name, (name != NULL) ? name : "", sizeof(s_wifi_name) - 1);
252 s_wifi_name[sizeof(s_wifi_name) - 1] = '\0';
253}
254
255/* amount_format, AMOUNT_CENTS_MAX(_NATIVE) and the keypad arithmetic are in
256 * money.h, where test_money can reach them. */
257
268
283static void open_admin_entry(void) {
284 s_settings_return = s_req_screen; /* remember where we came from */
285 if (!settings_has_admin_code()) { return; }
286 s_settings_tab = 0U; /* a fresh open starts on Screen */
288 s_admin_confirming = false;
289 s_admin_for_portal = false;
290 s_admin_note[0] = '\0';
291 /* Re-arm the wait from the persisted attempt count. The wait itself lives
292 * in RAM — persisting a deadline would need a trustworthy absolute clock,
293 * and the wall clock is what an attacker on the network can move. Deriving
294 * it from the NVS count makes the escalation survive reboots, so a power
295 * cycle cannot cut the cost of a guess to one reboot. */
297 s_admin_lock_start = lv_tick_get();
298 /* Arrive as a sheet. Set here rather than at the gesture, so the early
299 * return above cannot leave the flag armed for an unrelated visit. */
300 s_sheet_pending = true;
302}
303
304/* Runs on the UI task (inside lv_timer_handler), so touching shared state and
305 * invoking s_cb (which only posts to a queue) is safe here. */
306void btn_event_cb(lv_event_t *e) {
307 BtnAction act = static_cast<BtnAction>(
308 reinterpret_cast<intptr_t>(lv_event_get_user_data(e)));
309
310 /* The coin rows carry their chain in the action itself, so adding an asset is
311 * a row in the picker's table and nothing here. */
312 if ((act >= ACT_CHAIN_BASE) && (act < ACT_CHAIN_OF(POS_CHAIN__COUNT))) {
313 const pos_chain_t picked = static_cast<pos_chain_t>(act - ACT_CHAIN_BASE);
314 /* From the admin Tx tab the pick only changes what that tab shows. */
316 s_view_chain = picked;
317 close_modal();
318 request_screen(UI_SCREEN_SETTINGS); /* rebuild repoints the rows */
319 return;
320 }
321 settings_set_chain(picked);
322 /* The entered amount outlives the picker, so switching to an 18-decimal
323 * coin can leave a figure the new asset cannot carry. Clamp it to the
324 * new ceiling. */
327 }
328 close_modal();
329 /* Repoint the contract and payout strings before the rebuild reads
330 * them, or the Tx tab shows the previous asset's contract and, across
331 * the Ethereum/Tron divide, the previous network's payout address. */
333 /* Rebuild the screen the picker was opened from (s_req_screen: a modal
334 * is drawn over it, never instead of it). The amount survives; it lives
335 * in s_amount_cents, not in the widgets. */
337 return;
338 }
339
340 switch (act) {
341 case ACT_CONFIRM:
342 if (s_cb != NULL && s_amount_units > 0ULL) {
343 /* Before the callback, not after: main may answer at once or
344 * after a network round trip, and the screen must stop taking
345 * taps before either. */
348 }
349 break;
350 case ACT_CANCEL:
351 if (s_cb != NULL) { s_cb(UI_EVENT_CONFIRM_CANCEL, 0); }
352 break;
353 case ACT_SEND:
354 /* Collect the PIN on the keypad screen before signing. Cleared
355 * explicitly, so a card read abandoned by any route cannot leave the
356 * payment keypad reporting the wrong event. */
357 s_pin_for_card = false;
359 break;
360 case ACT_PIN_CANCEL:
361 CW_Utils::secure_wipe(reinterpret_cast<uint8_t *>(s_pin), sizeof(s_pin));
362 s_pin_len = 0;
363 if (s_pin_for_card) {
364 /* Reported so the waiting main task stops waiting; the card read
365 * has no confirm screen to fall back to. */
366 s_pin_for_card = false;
367 if (s_cb != NULL) { s_cb(UI_EVENT_CONFIRM_CANCEL, 0); }
369 } else {
371 }
372 break;
373 case ACT_NEW:
374 if (s_cb != NULL) { s_cb(UI_EVENT_TX_RETRY, 0); }
375 break;
376 case ACT_TX_RECHECK:
377 if (s_cb != NULL) { s_cb(UI_EVENT_TX_RECHECK, 0); }
378 break;
379 case ACT_WELCOME_OK:
380 /* Guarded: request_screen only takes effect on the UI task's next
381 * pass, so a double tap could otherwise emit twice. */
382 if (!s_welcome_sent) {
383 s_welcome_sent = true;
384 if (s_cb != NULL) { s_cb(UI_EVENT_WELCOME_DONE, 0); }
385 }
386 break;
387 case ACT_ADMIN_CANCEL:
388 CW_Utils::secure_wipe(reinterpret_cast<uint8_t *>(s_admin_first),
389 sizeof(s_admin_first));
390 if (s_admin_for_portal) {
391 /* Refuse the browser rather than leave it polling "waiting for
392 * the admin code". Back to the QR screen in wizard mode. */
393 prov_auth_resolve(false);
394 s_admin_for_portal = false;
397 } else {
399 }
400 break;
401 case ACT_CLOSE:
404 break;
405 case ACT_TOUCH_CAL:
407 /* Keep what is in force, so a cancel — or an operator who cannot
408 * hit anything — gets the working panel back. */
411 s_cal_step = 0U;
412 s_cal_pressed = false;
414 break;
415 case ACT_CAL_SAVE:
418 /* Read back what was actually stored: settings_set_touch_cal
419 * refuses a collapsed span, and the panel must then keep running
420 * on the numbers in NVS rather than the ones it rejected. */
423 break;
424 case ACT_CAL_CANCEL:
428 break;
429 case ACT_WIFI:
431 set_wifi_progress("Scanning...", NULL);
433 if (s_cb != NULL) { s_cb(UI_EVENT_WIFI_SCAN, 0); }
434 break;
435 case ACT_WIFI_CANCEL:
436 CW_Utils::secure_wipe(reinterpret_cast<uint8_t *>(s_wifi_pass),
437 sizeof(s_wifi_pass));
439 break;
442 break;
443 case ACT_PIN_REVEAL:
445 break;
446 case ACT_ADMIN_REVEAL:
448 break;
449 case ACT_RESET:
450 open_reset_confirm(); /* ask before wiping */
451 break;
453 /* Wipe stored settings (brightness, auto, Wi-Fi) and reboot — the
454 * device comes back up into first-run Wi-Fi setup. */
456 esp_restart();
457 break;
458 case ACT_MODAL_CLOSE:
459 close_modal();
460 break;
461 case ACT_PROV_OK:
462 case ACT_PROV_NO:
463 /* The panel is the only place a payout address or token contract
464 * proposed by the browser can be accepted. Commit (or drop) before
465 * closing, so it cannot outlive the card. */
466 (void)prov_pending_commit(act == ACT_PROV_OK);
467 close_modal();
468 break;
469 case ACT_PROV_FINISH:
470 if (s_cb != NULL) { s_cb(UI_EVENT_PROV_FINISH, 0); }
471 break;
472 case ACT_PORTAL:
474 break;
475 case ACT_PORTAL_CLOSE:
476 /* Closing the card closes the page: a config endpoint should not
477 * outlive the operator standing in front of the terminal. */
479 close_modal();
480 break;
481 case ACT_OTA_NO:
482 /* Refused. The staging is dropped and the page goes with it, so a
483 * declined image cannot be re-offered to whoever wanders past next. */
484 close_modal();
485 (void)ota_commit(false);
487 break;
488 case ACT_OTA_OK:
489 /* The panel is the only place firmware can be installed; the browser
490 * that uploaded it only got as far as this modal. Does not return on
491 * success — it reboots into the new slot. */
492 close_modal();
493 if (!ota_commit(true)) {
494 /* Nothing to install: the terminal rebooted since the upload
495 * (staging is RAM), or the slot refused to become bootable. Say
496 * so — a card that just closes looks like a silent success. */
499 }
500 break;
501 case ACT_NET_PICK:
503 break;
504 case ACT_NET_ETH:
505 case ACT_NET_POLY:
506 case ACT_NET_TRON:
507 /* Step 2: which coin on the network just picked. */
510 break;
511 case ACT_CHAIN_BASE:
512 /* Unreachable — the chain block above returns. Named only so -Wswitch
513 * keeps checking that every other action still has a case here. */
514 break;
515 }
516}
517
518/******************************************************************
519 * 9. UI task — owns LVGL init and the handler loop
520 ******************************************************************/
521static void ui_task(void *arg) {
522 (void)arg;
523
524 lv_init();
525
526 tft.init();
527 tft.setRotation(0); /* portrait, 240x320 */
528 tft.invertDisplay(true); /* CYD ILI9341 panel renders inverted otherwise */
529
530 /* CYD "milky gamma" fix: the 1-USB ILI9341_2 panels' gamma curve bands
531 * anti-aliased greys. Re-select a built-in curve via GAMMASET (0x26) for
532 * a clean ramp. See TFT_eSPI discussion #3018. */
533 tft.writecommand(0x26); /* GAMMASET */
534 tft.writedata(0x02);
535 delay(120);
536 tft.writecommand(0x26);
537 tft.writedata(0x01);
538
539 /* White, not black: the backlight comes on before LVGL's first frame and
540 * the theme is white, so black would flash at power-on. */
541 tft.fillScreen(TFT_WHITE);
542
543 touchSPI.begin(T_CLK, T_MISO, T_MOSI, T_CS);
544 touch.begin(touchSPI);
545 touch.setRotation(0); /* match the panel orientation */
546 touch_cal_load(); /* per-unit edge counts, before the first read */
547
548 /* Take over the backlight pin with LEDC PWM (after tft.init has touched
549 * it) so brightness is dimmable from the settings menu. Restore the saved
550 * level from NVS (defaults to 80% if never set). */
553
554 lv_disp_draw_buf_init(&s_draw_buf, s_buf, NULL, SCR_W * 40);
555 lv_disp_drv_init(&s_disp_drv);
556 s_disp_drv.hor_res = SCR_W;
557 s_disp_drv.ver_res = SCR_H;
558 s_disp_drv.flush_cb = disp_flush;
559 s_disp_drv.draw_buf = &s_draw_buf;
560 lv_disp_drv_register(&s_disp_drv);
561
562 /* After the display exists (a theme belongs to one) and before the first
563 * screen is built — lv_theme_apply runs at object creation, so anything
564 * created earlier would keep the default look. */
565 theme_init();
566 signal_init(); /* top-right Wi-Fi bars, on the top layer */
567
568 lv_indev_drv_init(&s_indev_drv);
569 s_indev_drv.type = LV_INDEV_TYPE_POINTER;
570 s_indev_drv.read_cb = indev_read;
571 lv_indev_drv_register(&s_indev_drv);
572
573 const esp_timer_create_args_t targs = {
574 .callback = &tick_cb,
575 .arg = NULL,
576 .dispatch_method = ESP_TIMER_TASK,
577 .name = "lv_tick",
578 .skip_unhandled_events = true,
579 };
580 esp_timer_handle_t th;
581 if (esp_timer_create(&targs, &th) == ESP_OK) {
582 (void)esp_timer_start_periodic(th, LV_TICK_PERIOD_MS * 1000);
583 }
584
585 ESP_LOGI(TAG, "UI initialized (LVGL %d.%d + TFT_eSPI/XPT2046)",
586 lv_version_major(), lv_version_minor());
587
588 /* On the task watchdog: a render or an event callback that never comes
589 * back (a wedged SPI transfer, a touch read stuck on the bus) resets the
590 * terminal instead of leaving a frozen panel in front of a customer. */
591 if (esp_task_wdt_add(NULL) != ESP_OK) {
592 ESP_LOGE(TAG, "UI loop not on the task watchdog");
593 }
594
595 while (true) {
596 /* Sleep first, unlocked — the writers' window — then one pass of the
597 * loop under the lock, released when the pass ends. See s_ui_mx. */
598 vTaskDelay(pdMS_TO_TICKS(5));
599 wdt_feed();
600 UiLock lk;
601 if (s_screen_dirty) {
602 s_screen_dirty = false;
604 }
605 /* Boot-status update from the main task; targeted rather than a splash
606 * rebuild, which would restart the logo on every step. */
607 if (s_boot_step_dirty) {
608 s_boot_step_dirty = false;
609 if ((s_req_screen == UI_SCREEN_SPLASH) && (s_boot_step_lbl != NULL)) {
610 lv_label_set_text(s_boot_step_lbl, s_boot_step);
611 }
612 }
613 /* Swipe up from the bottom edge, raised by indev_read. Gated to the
614 * amount screen: anywhere else (mid-sale, or over the admin panel
615 * itself) the gesture is dropped. */
616 if (s_swipe_admin) {
617 s_swipe_admin = false;
619 }
620 /* Touch calibration: corner taps are read raw, and the old values come
621 * back if nobody confirms in time — an operator who cannot hit Keep
622 * cannot hit Discard either. */
625 if ((s_cal_step >= 2U) && (s_cal_countdown != NULL)) {
626 int32_t left = (int32_t)(s_cal_deadline - lv_tick_get());
627 if (left <= 0) {
631 } else {
632 char c[40];
633 snprintf(c, sizeof(c), "Reverting in %d s",
634 (int)((left + 999) / 1000));
635 lv_label_set_text(s_cal_countdown, c);
636 }
637 }
638 }
639 /* Progress on the transaction screen, targeted so the spinner does not
640 * restart each poll. The label only exists on the spinner states. */
641 if (s_tx_info_dirty) {
642 s_tx_info_dirty = false;
643 if ((s_req_screen == UI_SCREEN_TX_STATUS) && (s_tx_info_lbl != NULL)) {
644 lv_label_set_text(s_tx_info_lbl, s_tx_info);
645 }
646 }
647 /* Gas caps stored from the config page. The labels exist only on the
648 * Tx tab (not on Tron); other screens read the caps when built. */
649 if (s_fees_dirty) {
650 s_fees_dirty = false;
651 if ((s_fee_max_lbl != NULL) && (s_fee_prio_lbl != NULL)) {
652 char f[16];
653 snprintf(f, sizeof(f), "%u",
654 static_cast<unsigned>(settings_get_max_fee_gwei()));
655 lv_label_set_text(s_fee_max_lbl, f);
656 snprintf(f, sizeof(f), "%u",
657 static_cast<unsigned>(settings_get_priority_fee_gwei()));
658 lv_label_set_text(s_fee_prio_lbl, f);
659 }
660 }
661 /* A value proposed from the config page. Built here, on the UI task, and
662 * after the screen render above — a modal raised straight from the main or
663 * HTTP task would be touching LVGL from two tasks at once. */
664 if (s_addr_modal_dirty) {
665 s_addr_modal_dirty = false;
667 }
668 /* Same handoff for firmware uploaded from the config page. */
669 if (s_ota_modal_dirty) {
670 s_ota_modal_dirty = false;
672 }
673 /* The admin page closes on its own deadline, checked here because it
674 * may touch LVGL. Deliberately NOT gated on the card still being up: a
675 * config server outliving its window because nobody was looking at the
676 * card is what the window prevents. Wizard mode has no deadline
677 * (prov_window_left_min() is 0 there), hence the mode test. */
678 /* ...and not while a firmware image is arriving: stopping httpd would
679 * drop the upload. This cannot hold the page open for ever: ota_post()
680 * gives up on a socket that has gone quiet (UPLOAD_MAX_STALLS). */
681 if ((prov_mode() == PROV_MODE_ADMIN) && (prov_window_left_min() == 0U) &&
682 !ota_receiving()) {
684 /* ...but NOT the firmware card (build_ota_confirm also sets
685 * s_portal_modal). The window covers the config *page*; a verified
686 * image waiting on this screen needs no page to install. */
687 if (s_portal_modal && !ota_staged(NULL, 0U, NULL)) { close_modal(); }
688 }
689 lv_timer_handler();
690 }
691}
692
693/******************************************************************
694 * 10. Public API
695 ******************************************************************/
696extern "C" void ui_init(ui_event_cb_t cb) {
697 s_ui_mx = xSemaphoreCreateRecursiveMutex(); /* before the task */
698 s_cb = cb;
700 s_screen_dirty = true;
701 /* LVGL rendering + nested event callbacks (tabview/modal) + NVS calls
702 * are stack-heavy; give the task plenty of headroom. */
703 xTaskCreate(ui_task, "ui", 16384, NULL, 4, NULL);
704}
705
706extern "C" void ui_show_splash(void) {
707 UiLock lk; /* shared with the UI task - see s_ui_mx */
709}
710
711extern "C" void ui_show_amount_entry(void) {
712 UiLock lk; /* shared with the UI task - see s_ui_mx */
713 s_amount_cents = 0U; /* fresh entry each time */
714 s_amount_units = 0U;
716}
717
718extern "C" void ui_show_confirm(uint64_t amount_units, const char *dest_addr,
719 const char *fee) {
720 UiLock lk; /* shared with the UI task - see s_ui_mx */
721 s_confirm_amount = amount_units;
722 (void)snprintf(s_confirm_fee, sizeof(s_confirm_fee), "%s",
723 (fee != NULL) ? fee : "");
724 if (dest_addr != NULL) {
725 strncpy(s_confirm_addr, dest_addr, sizeof(s_confirm_addr) - 1);
726 s_confirm_addr[sizeof(s_confirm_addr) - 1] = '\0';
727 } else {
728 s_confirm_addr[0] = '\0';
729 }
731}
732
733extern "C" size_t ui_take_pin(char *out, size_t n) {
734 UiLock lk; /* shared with the UI task - see s_ui_mx */
735 if ((out == NULL) || (n == 0U)) { return 0U; }
736 size_t len = s_pin_len;
737 if (len > (n - 1U)) { len = n - 1U; }
738 (void)CW_Utils::safe_memcpy(reinterpret_cast<uint8_t *>(out), n,
739 reinterpret_cast<const uint8_t *>(s_pin), len);
740 out[len] = '\0';
741 /* Handed off — wipe the UI's copy of the PIN. */
742 CW_Utils::secure_wipe(reinterpret_cast<uint8_t *>(s_pin), sizeof(s_pin));
743 s_pin_len = 0;
744 return len;
745}
746
747extern "C" void ui_show_wifi_list(const net_wifi_ap_t *aps, uint16_t n,
748 const char *note) {
749 UiLock lk; /* shared with the UI task - see s_ui_mx */
751 for (uint16_t i = 0U; i < s_ap_count; i++) {
752 s_aps[i] = aps[i];
753 }
754 /* Set on every call, so an earlier failure's note cannot linger. */
755 if (note != NULL) {
756 strncpy(s_wifi_note, note, sizeof(s_wifi_note) - 1);
757 s_wifi_note[sizeof(s_wifi_note) - 1] = '\0';
758 } else {
759 s_wifi_note[0] = '\0';
760 }
762}
763
764extern "C" void ui_set_addresses(const char *token_contract, const char *dest_addr) {
765 UiLock lk; /* shared with the UI task - see s_ui_mx */
766 s_addr_usdc = token_contract;
767 s_addr_dest = dest_addr;
768}
769
770extern "C" void ui_show_wifi_connecting(const char *ssid) {
771 UiLock lk; /* shared with the UI task - see s_ui_mx */
772 set_wifi_progress("Connecting to", ssid);
774}
775
776extern "C" void ui_set_boot_status(const char *step) {
777 UiLock lk; /* shared with the UI task - see s_ui_mx */
778 strncpy(s_boot_step, (step != NULL) ? step : "", sizeof(s_boot_step) - 1);
779 s_boot_step[sizeof(s_boot_step) - 1] = '\0';
780 s_boot_step_dirty = true; /* applied by the UI task — LVGL is single-thread */
781}
782
783extern "C" void ui_fees_changed(void) {
784 UiLock lk; /* shared with the UI task - see s_ui_mx */
785 s_fees_dirty = true; /* applied by the UI task — LVGL is single-thread */
786}
787
788extern "C" void ui_clock_changed(void) {
789 UiLock lk; /* shared with the UI task - see s_ui_mx */
790 /* Only the cache is invalidated; the status timer retexts the label on the
791 * UI task within three seconds. */
792 s_tz_dirty = true;
793}
794
795extern "C" void ui_show_boot_error(ui_boot_err_t kind, const char *detail) {
796 UiLock lk; /* shared with the UI task - see s_ui_mx */
797 s_boot_err = kind;
798 if (detail != NULL) {
799 strncpy(s_boot_detail, detail, sizeof(s_boot_detail) - 1);
800 s_boot_detail[sizeof(s_boot_detail) - 1] = '\0';
801 } else {
802 s_boot_detail[0] = '\0';
803 }
805}
806
807extern "C" void ui_show_welcome(const char *sub) {
808 UiLock lk; /* shared with the UI task - see s_ui_mx */
809 strncpy(s_welcome_sub,
810 ((sub != NULL) && (sub[0] != '\0')) ? sub
811 : "Let's configure your terminal.",
812 sizeof(s_welcome_sub) - 1U);
813 s_welcome_sub[sizeof(s_welcome_sub) - 1U] = '\0';
814 s_welcome_sent = false;
816}
817
818extern "C" void ui_show_admin_set(void) {
819 UiLock lk; /* shared with the UI task - see s_ui_mx */
820 s_admin_confirming = false;
821 s_admin_note[0] = '\0';
822 CW_Utils::secure_wipe(reinterpret_cast<uint8_t *>(s_admin_first),
823 sizeof(s_admin_first));
825}
826
827extern "C" size_t ui_take_wifi_creds(char *ssid, size_t ssid_n,
828 char *pass, size_t pass_n) {
829 UiLock lk; /* shared with the UI task - see s_ui_mx */
830 if ((ssid == NULL) || (pass == NULL) || (ssid_n == 0U) || (pass_n == 0U)) {
831 return 0U;
832 }
833 strncpy(ssid, s_wifi_ssid, ssid_n - 1U);
834 ssid[ssid_n - 1U] = '\0';
835 strncpy(pass, s_wifi_pass, pass_n - 1U);
836 pass[pass_n - 1U] = '\0';
837 /* Wipe the UI's copy of the passphrase. */
838 CW_Utils::secure_wipe(reinterpret_cast<uint8_t *>(s_wifi_pass), sizeof(s_wifi_pass));
839 return strlen(ssid);
840}
841
842extern "C" void ui_stage_wifi_creds(const char *ssid, const char *pass) {
843 UiLock lk; /* shared with the UI task - see s_ui_mx */
844 strncpy(s_wifi_ssid, (ssid != NULL) ? ssid : "", sizeof(s_wifi_ssid) - 1U);
845 s_wifi_ssid[sizeof(s_wifi_ssid) - 1U] = '\0';
846 strncpy(s_wifi_pass, (pass != NULL) ? pass : "", sizeof(s_wifi_pass) - 1U);
847 s_wifi_pass[sizeof(s_wifi_pass) - 1U] = '\0';
848}
849
850extern "C" void ui_show_prov(int step) {
851 UiLock lk; /* shared with the UI task - see s_ui_mx */
852 s_prov_step = step;
854}
855
856extern "C" void ui_show_prov_confirm(void) {
857 UiLock lk; /* shared with the UI task - see s_ui_mx */
858 s_addr_modal_dirty = true;
859}
860
861extern "C" void ui_show_prov_auth(void) {
862 UiLock lk; /* shared with the UI task - see s_ui_mx */
863 s_admin_for_portal = true;
864 s_admin_confirming = false;
865 s_admin_note[0] = '\0';
866 /* Re-arm the wait from the persisted attempt count, exactly as the swipe
867 * does: same code, same guessing budget, so not a cheaper door. */
869 s_admin_lock_start = lv_tick_get();
871}
872
873extern "C" void ui_show_card_pin(void) {
874 UiLock lk; /* shared with the UI task - see s_ui_mx */
875 s_pin_for_card = true;
876 s_prov_msg[0] = '\0'; /* a new attempt starts; drop the last one's reason */
878}
879
880extern "C" void ui_set_prov_note(const char *msg) {
881 UiLock lk; /* shared with the UI task - see s_ui_mx */
882 strncpy(s_prov_msg, (msg != NULL) ? msg : "", sizeof(s_prov_msg) - 1);
883 s_prov_msg[sizeof(s_prov_msg) - 1] = '\0';
884}
885
886extern "C" void ui_show_card_wait(const char *note) {
887 UiLock lk; /* shared with the UI task - see s_ui_mx */
888 strncpy(s_card_note, (note != NULL) ? note : "", sizeof(s_card_note) - 1);
889 s_card_note[sizeof(s_card_note) - 1] = '\0';
891}
892
893extern "C" void ui_show_ota_confirm(void) {
894 UiLock lk; /* shared with the UI task - see s_ui_mx */
895 s_ota_modal_dirty = true;
896}
897
898extern "C" void ui_show_tx_status(ui_tx_state_t state, const char *info) {
899 UiLock lk; /* shared with the UI task - see s_ui_mx */
900 s_tx_state = state;
901 if (info != NULL) {
902 strncpy(s_tx_info, info, sizeof(s_tx_info) - 1);
903 s_tx_info[sizeof(s_tx_info) - 1] = '\0';
904 } else {
905 s_tx_info[0] = '\0';
906 }
908}
909
910extern "C" void ui_set_tx_info(const char *info) {
911 UiLock lk; /* shared with the UI task - see s_ui_mx */
912 strncpy(s_tx_info, (info != NULL) ? info : "", sizeof(s_tx_info) - 1);
913 s_tx_info[sizeof(s_tx_info) - 1] = '\0';
914 s_tx_info_dirty = true;
915}
static const pos_asset_t * pos_asset_of(pos_chain_t chain)
The row describing chain.
Definition assets.h:145
@ POS_NET_TRON
Definition assets.h:54
@ POS_NET_ETH
Definition assets.h:52
@ POS_NET_POLY
Definition assets.h:53
static bool pos_chain_is_native_evm(pos_chain_t c)
true for ETH and POL: no contract, the amount goes in value, 21000 gas, 18 decimals....
Definition assets.h:183
static const pos_net_info_t * pos_net_info(pos_net_t net)
Names for net, or Ethereum's for a value out of range.
Definition assets.h:190
static const char *const TAG
Definition eth_rpc.cpp:33
static uint64_t amount_cents_cap(bool native)
Ceiling on what the keypad will accept, in cents.
Definition money.h:52
bool ota_receiving(void)
Whether an upload is in flight, so a second can be refused.
Definition ota.cpp:165
bool ota_staged(char *version, size_t version_n, bool *older)
Fetch the version of an image that has been received and verified but not yet installed.
Definition ota.cpp:275
bool ota_commit(bool install)
Resolve a staged image.
Definition ota.cpp:291
void ui_refresh_addresses(void)
Implemented by main: repoint those two rows at the selected chain.
Definition pay.cpp:170
static net_wifi_ap_t s_aps[PROV_MAX_APS]
void prov_auth_resolve(bool grant)
Answer a pending authorisation request from the panel.
static uint16_t s_ap_count
bool prov_pending_commit(bool accept)
Resolve a pending proposal from the panel.
static ui_event_cb_t s_cb
prov_mode_t prov_mode(void)
Which mode is running, or PROV_MODE_OFF.
unsigned prov_window_left_min(void)
Minutes left before the portal closes itself, 0 once it has.
@ PROV_MODE_WIZARD
Definition provision.h:106
@ PROV_MODE_ADMIN
Definition provision.h:107
const char * settings_net_str(const char *testnet, const char *mainnet)
Pick the string belonging to the network the terminal is on.
Definition settings.cpp:210
pos_chain_t settings_get_chain(void)
Selected chain, or POS_CHAIN_ETH_USDC if never set.
Definition settings.cpp:165
uint32_t settings_get_priority_fee_gwei(void)
EIP-1559 max priority fee (tip) per gas, in Gwei.
Definition settings.cpp:352
uint8_t settings_get_brightness(void)
Backlight level in percent, or 80 if never set.
Definition settings.cpp:215
void settings_set_chain(pos_chain_t chain)
Persist the selected chain.
Definition settings.cpp:181
void settings_factory_reset(void)
Erase all stored settings (brightness, auto, Wi-Fi creds, fees).
Definition settings.cpp:619
uint8_t settings_admin_fail_count(void)
Consecutive failed unlock attempts, persisted.
Definition settings.cpp:448
bool settings_has_admin_code(void)
true once an admin code exists.
Definition settings.cpp:372
void settings_set_touch_cal(uint16_t x_min, uint16_t x_max, uint16_t y_min, uint16_t y_max)
Persist a calibration. Rejected (and ignored) if an axis is inverted or collapsed — a bad store here ...
Definition settings.cpp:270
uint32_t settings_get_max_fee_gwei(void)
EIP-1559 max fee per gas, in Gwei.
Definition settings.cpp:347
pos_chain_t
Which chain (and therefore which asset) the terminal charges in.
Definition settings.h:33
@ POS_CHAIN_ETH_USDC
Definition settings.h:34
@ POS_CHAIN__COUNT
Definition settings.h:43
Definition ui.cpp:224
UiLock()
Definition ui.cpp:225
UiLock & operator=(const UiLock &)=delete
UiLock(const UiLock &)=delete
~UiLock()
Definition ui.cpp:226
A scanned access point (subset of fields the UI needs).
Definition net.h:33
One selectable asset.
Definition assets.h:66
const char * ticker
Definition assets.h:68
const char * caption
Definition assets.h:77
Names for one network, in both deployments.
Definition assets.h:92
const char * long_main
Definition assets.h:95
const char * long_test
Definition assets.h:94
static bool s_welcome_sent
Definition ui.cpp:150
uint64_t s_amount_cents
Definition ui.cpp:77
char s_wifi_name[33]
Definition ui.cpp:115
void ui_set_prov_note(const char *msg)
Put a failure line on the setup screen, in red under the step title.
Definition ui.cpp:880
lv_obj_t * s_sheet
Definition ui.cpp:62
static volatile bool s_screen_dirty
Definition ui.cpp:33
char s_wifi_note[64]
Definition ui.cpp:105
const char * asset_caption(pos_chain_t c)
Definition ui.cpp:210
static void open_admin_entry(void)
Open the admin panel's front door — the code screen, not the panel.
Definition ui.cpp:283
void ui_show_prov(int step)
Show the setup screen: QR code, AP name and passphrase.
Definition ui.cpp:850
bool s_admin_confirming
Definition ui.cpp:149
void set_wifi_progress(const char *caption, const char *name)
Definition ui.cpp:247
void ui_show_confirm(uint64_t amount_units, const char *dest_addr, const char *fee)
Switch to the confirm screen.
Definition ui.cpp:718
char s_confirm_fee[40]
Definition ui.cpp:42
lv_obj_t * s_clock_lbl
Definition ui.cpp:90
void ui_show_prov_auth(void)
Demand the admin code so a browser can be authorised.
Definition ui.cpp:861
static uint32_t s_prov_stop_at
Definition ui.cpp:234
void ui_show_welcome(const char *sub)
Greet the operator at the start of first-run setup, or after an update.
Definition ui.cpp:807
void ui_set_boot_status(const char *step)
Set the one-line progress note on the splash screen.
Definition ui.cpp:776
bool s_charge_busy
Definition ui.cpp:94
uint32_t s_admin_lock_start
Definition ui.cpp:155
char s_admin_note[48]
Definition ui.cpp:148
lv_obj_t * s_fee_max_lbl
Definition ui.cpp:126
int16_t s_cal_last_x
Definition ui.cpp:53
void ui_show_wifi_list(const net_wifi_ap_t *aps, uint16_t n, const char *note)
Show the scanned Wi-Fi networks for the user to pick from.
Definition ui.cpp:747
char s_admin_first[ADMIN_CODE_MAX+1]
Definition ui.cpp:147
bool s_cal_pressed
Definition ui.cpp:52
lv_obj_t * s_asset_arrow
Definition ui.cpp:82
void btn_event_cb(lv_event_t *e)
Definition ui.cpp:306
char s_wifi_pass[65]
Definition ui.cpp:104
lv_obj_t * s_pin_ta
Definition ui.cpp:98
static volatile bool s_ota_modal_dirty
Definition ui.cpp:140
static volatile bool s_tx_info_dirty
Definition ui.cpp:49
void ui_show_card_pin(void)
Ask for the card PIN before reading an address off a Cryptnox card.
Definition ui.cpp:873
lv_obj_t * s_amount_row
Definition ui.cpp:76
char s_prov_msg[64]
Definition ui.cpp:136
const char * s_addr_dest
Definition ui.cpp:168
void request_screen(ui_screen_t s)
Definition ui.cpp:241
void ui_show_ota_confirm(void)
Raise the modal that asks the operator to accept a firmware image the update page has uploaded,...
Definition ui.cpp:893
volatile bool s_sheet_pending
Definition ui.cpp:63
uint64_t amount_cents_max(void)
Definition ui.cpp:265
lv_obj_t * s_cal_countdown
Definition ui.cpp:57
void ui_init(ui_event_cb_t cb)
Initialise display + touch and start the UI task.
Definition ui.cpp:696
pos_chain_t s_view_chain
Definition ui.cpp:186
size_t ui_take_wifi_creds(char *ssid, size_t ssid_n, char *pass, size_t pass_n)
Fetch the selected SSID + entered password and wipe the UI's copy.
Definition ui.cpp:827
ui_tx_state_t s_tx_state
Definition ui.cpp:46
lv_obj_t * s_charge_btn
Definition ui.cpp:86
lv_obj_t * s_amount_cents_label
Definition ui.cpp:73
ui_boot_err_t s_boot_err
Definition ui.cpp:143
void ui_set_addresses(const char *token_contract, const char *dest_addr)
Provide the token contract and destination addresses for the confirm screen and the settings "Tx" tab...
Definition ui.cpp:764
char s_boot_step[40]
Definition ui.cpp:119
static volatile bool s_addr_modal_dirty
Definition ui.cpp:137
lv_obj_t * s_fee_prio_lbl
Definition ui.cpp:127
static void request_prov_stop(void)
Definition ui.cpp:236
void ui_fees_changed(void)
Note that the stored gas caps have changed, so the Tx tab can catch up.
Definition ui.cpp:783
void ui_show_tx_status(ui_tx_state_t state, const char *info)
Switch to the transaction-status screen.
Definition ui.cpp:898
ui_event_cb_t s_cb
Definition ui.cpp:31
lv_obj_t * s_tx_info_lbl
Definition ui.cpp:48
char s_pin[16]
Definition ui.cpp:99
void ui_stage_wifi_creds(const char *ssid, const char *pass)
Load credentials into the same handoff buffers the picker fills.
Definition ui.cpp:842
lv_obj_t * s_pin_eye_lbl
Definition ui.cpp:108
void ui_show_amount_entry(void)
Switch to the amount-entry screen (forces a value redraw).
Definition ui.cpp:711
char s_wifi_ssid[33]
Definition ui.cpp:103
void ui_show_prov_confirm(void)
Raise the modal that asks the operator to accept a value a browser proposed, reading the pending prop...
Definition ui.cpp:856
char s_confirm_addr[64]
Definition ui.cpp:41
uint64_t s_amount_units
Definition ui.cpp:37
uint8_t s_pin_len
Definition ui.cpp:100
static SemaphoreHandle_t s_ui_mx
Definition ui.cpp:222
void ui_show_admin_set(void)
Run the first-run admin-code creation (enter, then confirm).
Definition ui.cpp:818
lv_obj_t * s_amount_label
Definition ui.cpp:72
static volatile bool s_fees_dirty
Definition ui.cpp:128
lv_obj_t * s_wifi_eye_lbl
Definition ui.cpp:107
uint8_t s_cal_step
Definition ui.cpp:50
static volatile bool s_boot_step_dirty
Definition ui.cpp:121
lv_obj_t * s_reset_btn
Definition ui.cpp:171
void ui_set_tx_info(const char *info)
Replace the transaction screen's info line without rebuilding it.
Definition ui.cpp:910
void ui_show_splash(void)
Switch to the splash screen.
Definition ui.cpp:706
bool s_pin_for_card
Definition ui.cpp:164
char s_tx_info[72]
Definition ui.cpp:47
lv_obj_t * s_boot_step_lbl
Definition ui.cpp:120
int16_t s_cal_raw[2][2]
Definition ui.cpp:51
const char * asset_network(pos_chain_t c)
Definition ui.cpp:204
int16_t s_cal_last_y
Definition ui.cpp:54
lv_obj_t * s_admin_ta
Definition ui.cpp:145
volatile ui_screen_t s_req_screen
Definition ui.cpp:34
uint64_t s_confirm_amount
Definition ui.cpp:40
const char * asset_name(pos_chain_t c)
Definition ui.cpp:194
uint16_t s_settings_tab
Definition ui.cpp:180
static void ui_task(void *arg)
Definition ui.cpp:521
lv_obj_t * s_admin_eye_lbl
Definition ui.cpp:109
volatile int s_prov_step
Definition ui.cpp:132
uint32_t s_admin_lock_ms
Definition ui.cpp:156
void ui_show_wifi_connecting(const char *ssid)
Show a "Connecting to <ssid>…" screen while main associates.
Definition ui.cpp:770
lv_obj_t * s_close_btn
Definition ui.cpp:172
void ui_show_card_wait(const char *note)
"Hold your card to the reader" while the address is read.
Definition ui.cpp:886
lv_obj_t * s_asset_btn
Definition ui.cpp:81
const pos_asset_t * asset(pos_chain_t c)
Definition ui.cpp:189
static uint16_t s_cal_prev[4]
Definition ui.cpp:55
char s_welcome_sub[96]
Definition ui.cpp:151
lv_obj_t * s_admin_note_lbl
Definition ui.cpp:146
char s_wifi_caption[24]
Definition ui.cpp:114
void ui_show_boot_error(ui_boot_err_t kind, const char *detail)
Show a startup fault, naming the cause and what to do about it.
Definition ui.cpp:795
static ui_screen_t s_settings_return
Definition ui.cpp:177
char s_card_note[64]
Definition ui.cpp:67
bool s_admin_for_portal
Definition ui.cpp:159
size_t ui_take_pin(char *out, size_t n)
Copy the most recently entered PIN out and wipe the UI's copy.
Definition ui.cpp:733
void ui_clock_changed(void)
Note that the stored UTC offset has changed, so the clock can catch up.
Definition ui.cpp:788
const char * s_addr_usdc
Definition ui.cpp:167
uint32_t s_cal_deadline
Definition ui.cpp:56
char s_boot_detail[64]
Definition ui.cpp:144
lv_obj_t * s_wifi_pass_ta
Definition ui.cpp:106
ui_screen_t
Top-level screens of the payment flow.
Definition ui.h:35
@ UI_SCREEN_TOUCH_CAL
Definition ui.h:51
@ UI_SCREEN_AMOUNT
Definition ui.h:37
@ UI_SCREEN_BOOT_ERROR
Definition ui.h:45
@ UI_SCREEN_ADMIN_SET
Definition ui.h:46
@ UI_SCREEN_SETTINGS
Definition ui.h:43
@ UI_SCREEN_ADMIN_UNLOCK
Definition ui.h:47
@ UI_SCREEN_WIFI_LIST
Definition ui.h:40
@ UI_SCREEN_SPLASH
Definition ui.h:36
@ UI_SCREEN_CONFIRM
Definition ui.h:38
@ UI_SCREEN_TX_STATUS
Definition ui.h:44
@ UI_SCREEN_PROV
Definition ui.h:49
@ UI_SCREEN_WELCOME
Definition ui.h:48
@ UI_SCREEN_PIN
Definition ui.h:39
@ UI_SCREEN_CARD_WAIT
Definition ui.h:50
@ UI_SCREEN_WIFI_CONNECTING
Definition ui.h:42
ui_tx_state_t
States shown on the transaction-status screen.
Definition ui.h:94
@ UI_TX_STATE_PLACE_CARD
Definition ui.h:95
@ UI_EVENT_CONFIRM_CANCEL
Definition ui.h:58
@ UI_EVENT_WELCOME_DONE
Definition ui.h:65
@ UI_EVENT_PROV_FINISH
Definition ui.h:79
@ UI_EVENT_AMOUNT_CONFIRMED
Definition ui.h:56
@ UI_EVENT_WIFI_SCAN
Definition ui.h:60
@ UI_EVENT_PROV_STOP
Definition ui.h:86
@ UI_EVENT_TX_RETRY
Definition ui.h:62
@ UI_EVENT_TX_RECHECK
Definition ui.h:84
ui_boot_err_t
Startup faults shown on UI_SCREEN_BOOT_ERROR.
Definition ui.h:113
@ UI_BOOT_ERR_NFC
Definition ui.h:114
void(* ui_event_cb_t)(ui_event_t event, uint64_t payload)
Callback invoked from the UI task on user interaction.
Definition ui.h:128
void open_network_picker(void)
Definition ui_admin.cpp:656
bool s_portal_modal
Definition ui_admin.cpp:423
uint32_t admin_penalty_ms(uint8_t fails)
Definition ui_admin.cpp:739
void settings_persist(void)
Definition ui_admin.cpp:19
void touch_cal_poll(void)
Definition ui_admin.cpp:397
void close_modal(void)
Definition ui_admin.cpp:424
void open_coin_picker(pos_net_t net)
Definition ui_admin.cpp:692
void build_ota_confirm(void)
Definition ui_admin.cpp:593
void open_reset_confirm(void)
Definition ui_admin.cpp:457
void open_portal_window(void)
Definition ui_admin.cpp:510
void open_ota_gone(void)
Definition ui_admin.cpp:581
volatile bool s_swipe_admin
Definition ui_hal.cpp:103
lv_indev_drv_t s_indev_drv
Definition ui_hal.cpp:21
void tick_cb(void *arg)
Definition ui_hal.cpp:165
void touch_cal_load(void)
Definition ui_hal.cpp:43
uint16_t s_cal_ymax
Definition ui_hal.cpp:42
lv_disp_draw_buf_t s_draw_buf
Definition ui_hal.cpp:19
void indev_read(lv_indev_drv_t *drv, lv_indev_data_t *data)
Definition ui_hal.cpp:104
uint8_t s_brightness
Definition ui_hal.cpp:179
TFT_eSPI tft
Definition ui_hal.cpp:14
lv_disp_drv_t s_disp_drv
Definition ui_hal.cpp:20
uint16_t s_cal_xmin
Definition ui_hal.cpp:39
uint16_t s_cal_ymin
Definition ui_hal.cpp:41
lv_color_t s_buf[SCR_W *40]
Definition ui_hal.cpp:18
uint16_t s_cal_xmax
Definition ui_hal.cpp:40
void disp_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *px)
Definition ui_hal.cpp:22
void backlight_init(uint8_t pct)
Definition ui_hal.cpp:186
Private to the ui*.cpp files: the state and helpers they share.
#define T_MISO
#define T_CS
#define LV_TICK_PERIOD_MS
XPT2046_Touchscreen touch
#define ADMIN_CODE_MAX
#define SCR_H
volatile bool s_tz_dirty
void code_field_reveal(lv_obj_t *ta, lv_obj_t *eye_lbl)
Definition ui_sale.cpp:286
#define SCR_W
#define T_CLK
void render_requested_screen(void)
void build_prov_confirm(void)
Definition ui_setup.cpp:225
void charge_set_busy(void)
Definition ui_sale.cpp:100
void signal_init(void)
#define ACT_CHAIN_OF(chain)
SPIClass touchSPI
BtnAction
@ ACT_WIFI_PASS_REVEAL
@ ACT_OTA_NO
@ ACT_CAL_CANCEL
@ ACT_PIN_CANCEL
@ ACT_NET_PICK
@ ACT_ADMIN_REVEAL
@ ACT_MODAL_CLOSE
@ ACT_TX_RECHECK
@ ACT_RESET
@ ACT_SEND
@ ACT_WIFI_CANCEL
@ ACT_NET_POLY
@ ACT_NET_ETH
@ ACT_NEW
@ ACT_CAL_SAVE
@ ACT_PIN_REVEAL
@ ACT_PROV_OK
@ ACT_RESET_CONFIRM
@ ACT_CHAIN_BASE
@ ACT_ADMIN_CANCEL
@ ACT_WELCOME_OK
@ ACT_CANCEL
@ ACT_CONFIRM
@ ACT_PORTAL
@ ACT_WIFI
@ ACT_OTA_OK
@ ACT_TOUCH_CAL
@ ACT_CLOSE
@ ACT_PORTAL_CLOSE
@ ACT_PROV_NO
@ ACT_PROV_FINISH
@ ACT_NET_TRON
#define T_MOSI
void theme_init(void)
Install the theme. After lv_disp_drv_register(), before any object.
Definition ui_theme.cpp:157
#define WIFI_MAX_APS
static void wdt_feed(void)
Feed the task watchdog if, and only if, the calling task is subscribed.
Definition wdt.h:28