plip_ui.{h,c}: LVGL phone on the BOX-3 LCD driven by the plip_virtual
hook state. Idle/ringing shows a phone glyph + status ("Appel
entrant…"); off-hook shows a 3x4 dial keypad (1-9,*,0,#) with a live
number field. A fixed bottom touch button toggles the hook —
"Décrocher" (green) while ringing, "Raccrocher" (red) off-hook,
disabled when idle. Dialed digits are reported to the master
(reason "dial:<n>") and surfaced in GET /status.
plip_virtual gains the public pickup/hangup/dial API, a state
callback for the UI, and a state getter; enables Montserrat 24/48.
Build green; flashed and verified on a real ESP32-S3-BOX-3.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "main.c" "voice_ws_client.c" "scenario_server.c" "plip_virtual.c"
|
||||
SRCS "main.c" "voice_ws_client.c" "scenario_server.c" "plip_virtual.c" "plip_ui.c"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES
|
||||
driver
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "voice_ws_client.h"
|
||||
#include "scenario_server.h"
|
||||
#include "plip_virtual.h"
|
||||
#include "plip_ui.h"
|
||||
#include "scenario_mesh.h"
|
||||
|
||||
/* BSP header — provided by espressif/esp-box component */
|
||||
@@ -399,6 +400,9 @@ void app_main(void)
|
||||
* BOOT button as the virtual hook switch. */
|
||||
if (plip_virtual_init(scenario_server_handle(), s_spk_handle) != ESP_OK) {
|
||||
ESP_LOGW(TAG, "plip_virtual_init failed — virtual phone unavailable");
|
||||
} else if (plip_ui_init() != ESP_OK) {
|
||||
/* REST/ESP-NOW phone still works headless if the UI fails. */
|
||||
ESP_LOGW(TAG, "plip_ui_init failed — on-screen phone unavailable");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
// plip_ui — implementation. See plip_ui.h for the design.
|
||||
//
|
||||
// LVGL 9 widget tree, built once under bsp_display_lock(). The plip_virtual
|
||||
// state callback can fire from any task, so the callback re-takes the lock
|
||||
// before touching widgets. Touch events (keypad, bottom button) already run
|
||||
// in the LVGL task context, so they call plip_virtual_* directly.
|
||||
|
||||
#include "plip_ui.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "bsp/esp-box.h"
|
||||
#include "esp_log.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
#include "plip_virtual.h"
|
||||
|
||||
#define TAG "plip_ui"
|
||||
#define LOCK_MS 1000
|
||||
|
||||
static bool s_built = false;
|
||||
static lv_obj_t *s_phone_view; // idle / ringing illustration
|
||||
static lv_obj_t *s_keypad_view; // off-hook dial pad
|
||||
static lv_obj_t *s_status_lbl; // line under the phone glyph
|
||||
static lv_obj_t *s_number_lbl; // dialed digits
|
||||
static lv_obj_t *s_hook_btn; // fixed bottom button
|
||||
static lv_obj_t *s_hook_lbl; // its label
|
||||
static char s_number[21] = "";
|
||||
|
||||
static const char *KEYS[] = {
|
||||
"1", "2", "3",
|
||||
"4", "5", "6",
|
||||
"7", "8", "9",
|
||||
"*", "0", "#",
|
||||
};
|
||||
|
||||
// ---------- touch event handlers (LVGL task context) ----------
|
||||
|
||||
static void key_event_cb(lv_event_t *e)
|
||||
{
|
||||
const char *key = (const char *) lv_event_get_user_data(e);
|
||||
size_t len = strlen(s_number);
|
||||
if (len < sizeof(s_number) - 1) {
|
||||
s_number[len] = key[0];
|
||||
s_number[len + 1] = '\0';
|
||||
lv_label_set_text(s_number_lbl, s_number);
|
||||
// Report the running number to the master; "#" finalises (drop it
|
||||
// from the buffer first). The master decides what a valid number is.
|
||||
if (key[0] == '#' && len > 0) {
|
||||
s_number[len] = '\0';
|
||||
lv_label_set_text(s_number_lbl, s_number);
|
||||
}
|
||||
plip_virtual_dial(s_number);
|
||||
}
|
||||
}
|
||||
|
||||
static void hook_btn_event_cb(lv_event_t *e)
|
||||
{
|
||||
(void) e;
|
||||
switch (plip_virtual_state()) {
|
||||
case PLIP_HOOK_RINGING: plip_virtual_pickup(); break;
|
||||
case PLIP_HOOK_OFF: plip_virtual_hangup(); break;
|
||||
default: break; // idle: button is disabled, nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- view switching (must hold the display lock) ----------
|
||||
|
||||
static void apply_state_locked(plip_hook_state_t st)
|
||||
{
|
||||
switch (st) {
|
||||
case PLIP_HOOK_OFF:
|
||||
s_number[0] = '\0';
|
||||
lv_label_set_text(s_number_lbl, "");
|
||||
lv_obj_add_flag(s_phone_view, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_clear_flag(s_keypad_view, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_label_set_text(s_hook_lbl, "Raccrocher");
|
||||
lv_obj_set_style_bg_color(s_hook_btn, lv_palette_main(LV_PALETTE_RED), 0);
|
||||
lv_obj_clear_state(s_hook_btn, LV_STATE_DISABLED);
|
||||
break;
|
||||
case PLIP_HOOK_RINGING:
|
||||
lv_obj_clear_flag(s_phone_view, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_add_flag(s_keypad_view, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_label_set_text(s_status_lbl, "Appel entrant…");
|
||||
lv_label_set_text(s_hook_lbl, "Décrocher");
|
||||
lv_obj_set_style_bg_color(s_hook_btn, lv_palette_main(LV_PALETTE_GREEN), 0);
|
||||
lv_obj_clear_state(s_hook_btn, LV_STATE_DISABLED);
|
||||
break;
|
||||
case PLIP_HOOK_ON:
|
||||
default:
|
||||
lv_obj_clear_flag(s_phone_view, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_add_flag(s_keypad_view, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_label_set_text(s_status_lbl, "PLIP — en attente");
|
||||
lv_label_set_text(s_hook_lbl, "—");
|
||||
lv_obj_set_style_bg_color(s_hook_btn, lv_palette_darken(LV_PALETTE_GREY, 2), 0);
|
||||
lv_obj_add_state(s_hook_btn, LV_STATE_DISABLED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// plip_virtual callback — arbitrary task context, so take the lock here.
|
||||
static void on_hook_state(plip_hook_state_t st)
|
||||
{
|
||||
if (bsp_display_lock(LOCK_MS)) {
|
||||
apply_state_locked(st);
|
||||
bsp_display_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- widget tree ----------
|
||||
|
||||
static void build_phone_view(lv_obj_t *parent)
|
||||
{
|
||||
s_phone_view = lv_obj_create(parent);
|
||||
lv_obj_remove_style_all(s_phone_view);
|
||||
lv_obj_set_size(s_phone_view, LV_PCT(100), LV_PCT(100));
|
||||
lv_obj_set_flex_flow(s_phone_view, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_flex_align(s_phone_view, LV_FLEX_ALIGN_CENTER,
|
||||
LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
||||
|
||||
lv_obj_t *glyph = lv_label_create(s_phone_view);
|
||||
lv_label_set_text(glyph, LV_SYMBOL_CALL);
|
||||
lv_obj_set_style_text_font(glyph, &lv_font_montserrat_48, 0);
|
||||
lv_obj_set_style_text_color(glyph, lv_palette_main(LV_PALETTE_BLUE), 0);
|
||||
|
||||
s_status_lbl = lv_label_create(s_phone_view);
|
||||
lv_label_set_text(s_status_lbl, "PLIP — en attente");
|
||||
lv_obj_set_style_pad_top(s_status_lbl, 12, 0);
|
||||
}
|
||||
|
||||
static void build_keypad_view(lv_obj_t *parent)
|
||||
{
|
||||
s_keypad_view = lv_obj_create(parent);
|
||||
lv_obj_remove_style_all(s_keypad_view);
|
||||
lv_obj_set_size(s_keypad_view, LV_PCT(100), LV_PCT(100));
|
||||
lv_obj_set_flex_flow(s_keypad_view, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_flex_align(s_keypad_view, LV_FLEX_ALIGN_CENTER,
|
||||
LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
||||
lv_obj_add_flag(s_keypad_view, LV_OBJ_FLAG_HIDDEN);
|
||||
|
||||
s_number_lbl = lv_label_create(s_keypad_view);
|
||||
lv_label_set_text(s_number_lbl, "");
|
||||
lv_obj_set_style_text_font(s_number_lbl, &lv_font_montserrat_24, 0);
|
||||
lv_obj_set_style_pad_bottom(s_number_lbl, 6, 0);
|
||||
|
||||
lv_obj_t *grid = lv_obj_create(s_keypad_view);
|
||||
lv_obj_remove_style_all(grid);
|
||||
lv_obj_set_size(grid, 240, 150);
|
||||
lv_obj_set_flex_flow(grid, LV_FLEX_FLOW_ROW_WRAP);
|
||||
lv_obj_set_flex_align(grid, LV_FLEX_ALIGN_CENTER,
|
||||
LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
||||
|
||||
for (size_t i = 0; i < sizeof(KEYS) / sizeof(KEYS[0]); i++) {
|
||||
lv_obj_t *btn = lv_button_create(grid);
|
||||
lv_obj_set_size(btn, 64, 40);
|
||||
lv_obj_add_event_cb(btn, key_event_cb, LV_EVENT_CLICKED,
|
||||
(void *) KEYS[i]);
|
||||
lv_obj_t *lbl = lv_label_create(btn);
|
||||
lv_label_set_text(lbl, KEYS[i]);
|
||||
lv_obj_center(lbl);
|
||||
}
|
||||
}
|
||||
|
||||
static void build_hook_button(lv_obj_t *parent)
|
||||
{
|
||||
s_hook_btn = lv_button_create(parent);
|
||||
lv_obj_set_size(s_hook_btn, LV_PCT(96), 44);
|
||||
lv_obj_align(s_hook_btn, LV_ALIGN_BOTTOM_MID, 0, -4);
|
||||
lv_obj_add_event_cb(s_hook_btn, hook_btn_event_cb, LV_EVENT_CLICKED, NULL);
|
||||
|
||||
s_hook_lbl = lv_label_create(s_hook_btn);
|
||||
lv_label_set_text(s_hook_lbl, "—");
|
||||
lv_obj_center(s_hook_lbl);
|
||||
}
|
||||
|
||||
esp_err_t plip_ui_init(void)
|
||||
{
|
||||
if (s_built) return ESP_OK;
|
||||
if (!bsp_display_lock(LOCK_MS)) {
|
||||
ESP_LOGE(TAG, "display lock failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
lv_obj_t *scr = lv_screen_active();
|
||||
lv_obj_set_style_bg_color(scr, lv_color_black(), 0);
|
||||
|
||||
// Content area above the fixed bottom button.
|
||||
lv_obj_t *content = lv_obj_create(scr);
|
||||
lv_obj_remove_style_all(content);
|
||||
lv_obj_set_size(content, LV_PCT(100), 188);
|
||||
lv_obj_align(content, LV_ALIGN_TOP_MID, 0, 0);
|
||||
|
||||
build_phone_view(content);
|
||||
build_keypad_view(content);
|
||||
build_hook_button(scr);
|
||||
|
||||
apply_state_locked(plip_virtual_state());
|
||||
bsp_display_unlock();
|
||||
|
||||
plip_virtual_register_state_cb(on_hook_state);
|
||||
s_built = true;
|
||||
ESP_LOGI(TAG, "phone UI ready");
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// plip_ui — on-screen telephone for the phone-less PLIP annex (BOX-3 LCD).
|
||||
//
|
||||
// Two views on the 320x240 display, switched by the hook state of
|
||||
// plip_virtual:
|
||||
// - idle / ringing : a telephone illustration + status line; during a ring
|
||||
// the screen shows "Appel entrant…".
|
||||
// - off-hook : a 3x4 dial keypad (1-9, *, 0, #) with a number field.
|
||||
//
|
||||
// A fixed bottom button toggles the hook: "Décrocher" (green) while ringing,
|
||||
// "Raccrocher" (red) while off-hook. It is disabled when idle.
|
||||
//
|
||||
// All LVGL access is guarded with bsp_display_lock/unlock. The widget tree is
|
||||
// built once; hook-state changes only flip visibility + the bottom button,
|
||||
// driven by the plip_virtual state callback.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Build the phone UI on the active LVGL display and subscribe to
|
||||
// plip_virtual hook transitions. Call after bsp_display_start() and
|
||||
// plip_virtual_init(). Idempotent.
|
||||
esp_err_t plip_ui_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -2,7 +2,9 @@
|
||||
//
|
||||
// Threading: the ring tone runs in its own task (synthesised sine bursts on
|
||||
// the shared speaker I2S channel, French cadence 1.5 s on / 3.5 s off); hook
|
||||
// reports run in a tiny worker so the button path never blocks on HTTP.
|
||||
// reports run in a tiny worker so the button/touch paths never block on
|
||||
// HTTP. The UI callback fires from whichever context transitions the state
|
||||
// (httpd, button task, LVGL touch) — the UI does its own locking.
|
||||
|
||||
#include "plip_virtual.h"
|
||||
|
||||
@@ -29,19 +31,26 @@
|
||||
#define RING_OFF_MS 3500
|
||||
#define RING_AMPLITUDE 14000.0f
|
||||
#define HOOK_QUEUE_DEPTH 4
|
||||
|
||||
typedef enum { HOOK_ON = 0, HOOK_RINGING, HOOK_OFF } hook_state_t;
|
||||
#define DIAL_MAX 20
|
||||
|
||||
typedef struct {
|
||||
char state[8]; /* "off" | "on" */
|
||||
char reason[16]; /* "pickup" | "hangup" | "ring_timeout" */
|
||||
char reason[32]; /* "pickup" | "hangup" | "ring_timeout" | "dial:<n>" */
|
||||
} hook_event_t;
|
||||
|
||||
static i2s_chan_handle_t s_spk;
|
||||
static volatile hook_state_t s_state = HOOK_ON;
|
||||
static volatile bool s_ring_stop = false;
|
||||
static TaskHandle_t s_ring_task = NULL;
|
||||
static QueueHandle_t s_hook_queue = NULL;
|
||||
static i2s_chan_handle_t s_spk;
|
||||
static volatile plip_hook_state_t s_state = PLIP_HOOK_ON;
|
||||
static volatile bool s_ring_stop = false;
|
||||
static TaskHandle_t s_ring_task = NULL;
|
||||
static QueueHandle_t s_hook_queue = NULL;
|
||||
static plip_state_cb_t s_state_cb = NULL;
|
||||
static char s_dialed[DIAL_MAX + 1] = "";
|
||||
|
||||
static void set_state(plip_hook_state_t st)
|
||||
{
|
||||
s_state = st;
|
||||
if (s_state_cb) s_state_cb(st);
|
||||
}
|
||||
|
||||
/* ---------- hook reporting (mirrors PLIP's zacus_hook_client) ---------- */
|
||||
|
||||
@@ -129,8 +138,8 @@ static void ring_task(void *arg)
|
||||
elapsed += RING_OFF_MS;
|
||||
}
|
||||
|
||||
if (s_state == HOOK_RINGING) {
|
||||
s_state = HOOK_ON; /* nobody picked up */
|
||||
if (s_state == PLIP_HOOK_RINGING) {
|
||||
set_state(PLIP_HOOK_ON); /* nobody picked up */
|
||||
if (!s_ring_stop) hook_report("on", "ring_timeout");
|
||||
}
|
||||
ESP_LOGI(TAG, "ring end (state=%d)", (int) s_state);
|
||||
@@ -172,15 +181,15 @@ static esp_err_t handle_ring_post(httpd_req_t *req)
|
||||
}
|
||||
}
|
||||
|
||||
if (s_state == HOOK_OFF) {
|
||||
if (s_state == PLIP_HOOK_OFF) {
|
||||
return send_json(req, "409 Conflict", "{\"error\":\"off-hook\"}");
|
||||
}
|
||||
if (s_ring_task) ring_stop();
|
||||
|
||||
s_state = HOOK_RINGING;
|
||||
set_state(PLIP_HOOK_RINGING);
|
||||
if (xTaskCreate(ring_task, "plip_ring", 4096,
|
||||
(void *) (intptr_t) duration_ms, 4, &s_ring_task) != pdPASS) {
|
||||
s_state = HOOK_ON;
|
||||
set_state(PLIP_HOOK_ON);
|
||||
return send_json(req, "500 Internal Server Error",
|
||||
"{\"error\":\"ring task failed\"}");
|
||||
}
|
||||
@@ -192,7 +201,7 @@ static esp_err_t handle_ring_post(httpd_req_t *req)
|
||||
static esp_err_t handle_stop_post(httpd_req_t *req)
|
||||
{
|
||||
if (s_ring_task) ring_stop();
|
||||
if (s_state == HOOK_RINGING) s_state = HOOK_ON;
|
||||
if (s_state == PLIP_HOOK_RINGING) set_state(PLIP_HOOK_ON);
|
||||
return send_json(req, "200 OK", "{\"ok\":true}");
|
||||
}
|
||||
|
||||
@@ -206,35 +215,70 @@ static esp_err_t handle_play_post(httpd_req_t *req)
|
||||
|
||||
static esp_err_t handle_status_get(httpd_req_t *req)
|
||||
{
|
||||
char resp[96];
|
||||
char resp[128];
|
||||
snprintf(resp, sizeof(resp),
|
||||
"{\"off_hook\":%s,\"ringing\":%s,\"playing\":false}",
|
||||
s_state == HOOK_OFF ? "true" : "false",
|
||||
s_state == HOOK_RINGING ? "true" : "false");
|
||||
"{\"off_hook\":%s,\"ringing\":%s,\"playing\":false,"
|
||||
"\"dialed\":\"%s\"}",
|
||||
s_state == PLIP_HOOK_OFF ? "true" : "false",
|
||||
s_state == PLIP_HOOK_RINGING ? "true" : "false",
|
||||
s_dialed);
|
||||
return send_json(req, "200 OK", resp);
|
||||
}
|
||||
|
||||
/* ---------- public API ---------- */
|
||||
|
||||
void plip_virtual_pickup(void)
|
||||
{
|
||||
if (s_state != PLIP_HOOK_RINGING) return;
|
||||
ring_stop();
|
||||
s_dialed[0] = '\0';
|
||||
set_state(PLIP_HOOK_OFF);
|
||||
ESP_LOGI(TAG, "virtual pickup");
|
||||
hook_report("off", "pickup");
|
||||
}
|
||||
|
||||
void plip_virtual_hangup(void)
|
||||
{
|
||||
if (s_state != PLIP_HOOK_OFF) return;
|
||||
set_state(PLIP_HOOK_ON);
|
||||
ESP_LOGI(TAG, "virtual hangup");
|
||||
hook_report("on", "hangup");
|
||||
}
|
||||
|
||||
void plip_virtual_dial(const char *number)
|
||||
{
|
||||
if (s_state != PLIP_HOOK_OFF || !number || !*number) return;
|
||||
strlcpy(s_dialed, number, sizeof(s_dialed));
|
||||
char reason[32];
|
||||
snprintf(reason, sizeof(reason), "dial:%s", s_dialed);
|
||||
ESP_LOGI(TAG, "dialed \"%s\"", s_dialed);
|
||||
hook_report("off", reason);
|
||||
}
|
||||
|
||||
bool plip_virtual_button_press(void)
|
||||
{
|
||||
switch (s_state) {
|
||||
case HOOK_RINGING:
|
||||
ring_stop();
|
||||
s_state = HOOK_OFF;
|
||||
ESP_LOGI(TAG, "virtual pickup");
|
||||
hook_report("off", "pickup");
|
||||
case PLIP_HOOK_RINGING:
|
||||
plip_virtual_pickup();
|
||||
return true;
|
||||
case HOOK_OFF:
|
||||
s_state = HOOK_ON;
|
||||
ESP_LOGI(TAG, "virtual hangup");
|
||||
hook_report("on", "hangup");
|
||||
case PLIP_HOOK_OFF:
|
||||
plip_virtual_hangup();
|
||||
return true;
|
||||
default:
|
||||
return false; /* on-hook, not ringing: not ours */
|
||||
}
|
||||
}
|
||||
|
||||
void plip_virtual_register_state_cb(plip_state_cb_t cb)
|
||||
{
|
||||
s_state_cb = cb;
|
||||
}
|
||||
|
||||
plip_hook_state_t plip_virtual_state(void)
|
||||
{
|
||||
return s_state;
|
||||
}
|
||||
|
||||
esp_err_t plip_virtual_init(httpd_handle_t server, i2s_chan_handle_t spk)
|
||||
{
|
||||
if (!server || !spk) return ESP_ERR_INVALID_ARG;
|
||||
|
||||
@@ -24,6 +24,13 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Hook state, exposed for the on-screen phone UI (plip_ui).
|
||||
typedef enum {
|
||||
PLIP_HOOK_ON = 0, // idle, handset down
|
||||
PLIP_HOOK_RINGING,
|
||||
PLIP_HOOK_OFF, // picked up
|
||||
} plip_hook_state_t;
|
||||
|
||||
// Register the REST handlers on `server` and keep `spk` for the ring tone.
|
||||
// Call once after scenario_server_start(); the speaker channel must be
|
||||
// enabled (speaker_init() in main.c).
|
||||
@@ -34,6 +41,23 @@ esp_err_t plip_virtual_init(httpd_handle_t server, i2s_chan_handle_t spk);
|
||||
// handling (voice-streaming toggle).
|
||||
bool plip_virtual_button_press(void);
|
||||
|
||||
// Programmatic hook transitions (used by the touch UI). Both are no-ops
|
||||
// when the state does not allow the transition.
|
||||
void plip_virtual_pickup(void); // RINGING -> OFF (stops the ring)
|
||||
void plip_virtual_hangup(void); // OFF -> ON
|
||||
|
||||
// Report a dialed number to the master (reason "dial:<number>" on the
|
||||
// existing /voice/hook contract) and remember it for GET /status.
|
||||
// Only meaningful while off-hook; ignored otherwise.
|
||||
void plip_virtual_dial(const char *number);
|
||||
|
||||
// UI notification: called on every hook state change (from HTTP, button or
|
||||
// touch context — the callback must do its own LVGL locking).
|
||||
typedef void (*plip_state_cb_t)(plip_hook_state_t state);
|
||||
void plip_virtual_register_state_cb(plip_state_cb_t cb);
|
||||
|
||||
plip_hook_state_t plip_virtual_state(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -55,3 +55,7 @@ CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768
|
||||
# Force-enable legacy mode so both coexist (and prevent the runtime abort
|
||||
# in bsp_display_start when both drivers race to register on the same bus).
|
||||
CONFIG_I2C_ENABLE_LEGACY_DRIVERS=y
|
||||
|
||||
# PLIP phone UI fonts (plip_ui.c)
|
||||
CONFIG_LV_FONT_MONTSERRAT_24=y
|
||||
CONFIG_LV_FONT_MONTSERRAT_48=y
|
||||
|
||||
Reference in New Issue
Block a user