ESP32-P4 Function EV Board

ESP32-P4-Function-EV-Board

The ESP32-P4 Function EV Board is an Espressif development board for the ESP32-P4 microprocessor. Together with the ESP32-P4-HMI-Subboard it provides a MIPI-DSI touchscreen display, an ES8311 audio codec with speaker amplifier, 10/100 Ethernet, a MIPI-CSI camera interface, a microSD card slot, and USB.

The espp::Esp32P4FunctionEvBoard component provides a singleton hardware abstraction for initializing the display, touch, audio, Ethernet, and SD card subsystems. The display panel (EK79007 1024x600 or ILI9881C 800x1280) is selectable via Kconfig.

Touch is polled by default (the GT911 INT pin is not routed to the ESP32-P4 on this board). If you wire the INT pin from the LCD expansion header to a free GPIO, you can switch to interrupt-driven touch via Kconfig (ESP_P4_EV_BOARD_TOUCH_INTERRUPT / ..._GPIO) or by passing the GPIO to initialize_touch().

Official board documentation:

Display panels (HMI subboard). Both panels plug into the shared LCD adapter board via its FPC connector:

EK79007 (7”, 1024x600) — the panel Espressif ships/documents for this board:

ILI9881C (10.1”, 800x1280) — a panel option supported by the BSP. Espressif does not publish a dedicated LCD-subboard schematic/datasheet for this panel on this board; refer to the esp-bsp esp_lcd_ili9881c driver for the panel/timing details.

API Reference

Header File

Classes

class Esp32P4FunctionEvBoard : public espp::BaseComponent

Board Support Package (BSP) for the Espressif ESP32-P4 Function EV Board used with the ESP32-P4-HMI-Subboard.

This class provides a singleton interface to the board’s peripherals:

  • MIPI-DSI display (Kconfig-selectable EK79007 1024x600 or ILI9881C 800x1280) with a GT911 capacitive multi-touch controller

  • ES8311 audio codec (+ NS4150B speaker amplifier) over I2S

  • 10/100 Ethernet (EMAC + IP101 RMII PHY)

  • microSD card (4-bit SDMMC)

  • MIPI-CSI camera (SC2336/OV5647) — pins wired, capture pipeline is a stub

All on-board control lines are direct ESP32-P4 GPIOs (this board has no I/O expander). The display, touch, and audio codec share a single I2C bus (SDA=GPIO7, SCL=GPIO8). The touch interrupt is not routed to the ESP32-P4 on this board, so touch is polled.

The class is a singleton and can be accessed using the get() method.

Example

  auto &board = Board::get();
  board.set_log_level(espp::Logger::Verbosity::INFO);
  logger.info("Display panel: {}", board.get_display_controller_name());

  // Probe the internal I2C bus
  auto &i2c = board.internal_i2c();
  std::vector<uint8_t> found;
  for (uint8_t addr = 1; addr < 128; addr++) {
    if (i2c.probe_device(addr)) {
      found.push_back(addr);
    }
  }
  logger.info("Found {} I2C device(s)", found.size());

  // Display
  if (!board.initialize_lcd()) {
    logger.error("Failed to initialize LCD!");
    return;
  }
  size_t pixel_buffer_size = board.display_width() * 50;
  if (!board.initialize_display(pixel_buffer_size)) {
    logger.error("Failed to initialize display!");
    return;
  }

  // Build the LVGL UI: a title, a status label, a rotate button, and a
  // transparent layer that the touch handler draws circles onto.
  lv_obj_t *bg = nullptr;
  lv_obj_t *title = nullptr;
  static lv_obj_t *status_label = nullptr;
  {
    std::lock_guard<std::recursive_mutex> lock(lvgl_mutex);
    bg = lv_obj_create(lv_screen_active());
    lv_obj_set_size(bg, board.display_width(), board.display_height());
    lv_obj_set_style_bg_color(bg, lv_color_make(0, 0, 0), 0);

    title = lv_label_create(lv_screen_active());
    lv_label_set_text(title, "ESP32-P4 Function EV Board  -  touch to draw!");
    lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 8);

    status_label = lv_label_create(lv_screen_active());
    lv_obj_set_style_text_align(status_label, LV_TEXT_ALIGN_LEFT, 0);
    lv_obj_align(status_label, LV_ALIGN_TOP_LEFT, 8, 40);

    initialize_circle_layer(board.display_width(), board.display_height());
    lv_obj_set_scrollbar_mode(lv_screen_active(), LV_SCROLLBAR_MODE_OFF);
    lv_obj_clear_flag(lv_screen_active(), LV_OBJ_FLAG_SCROLLABLE);
    if (circle_layer) {
      lv_obj_move_foreground(circle_layer);
    }
  }

  // Cycle the display rotation (0 -> 90 -> 180 -> 270) and resize the background
  // and circle layers to match the new orientation. Static so the (non-capturing)
  // button event callback below can call it; it captures app_main locals by
  // reference, which is safe because app_main never returns.
  static auto rotate_display = [&]() {
    std::lock_guard<std::recursive_mutex> lock(lvgl_mutex);
    clear_circles();
    static auto rotation = LV_DISPLAY_ROTATION_0;
    rotation = static_cast<lv_display_rotation_t>((static_cast<int>(rotation) + 1) % 4);
    lv_display_set_rotation(lv_display_get_default(), rotation);
    lv_obj_set_size(bg, board.rotated_display_width(), board.rotated_display_height());
    if (circle_layer) {
      lv_obj_set_size(circle_layer, board.rotated_display_width(), board.rotated_display_height());
      lv_obj_align(circle_layer, LV_ALIGN_CENTER, 0, 0);
      lv_obj_move_foreground(circle_layer);
      lv_obj_invalidate(circle_layer);
    }
    // re-align the labels to the (now reoriented) screen edges
    lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 8);
    if (status_label) {
      lv_obj_align(status_label, LV_ALIGN_TOP_LEFT, 8, 40);
    }
  };

  {
    std::lock_guard<std::recursive_mutex> lock(lvgl_mutex);
    lv_obj_t *rotate_btn = lv_btn_create(lv_screen_active());
    lv_obj_set_size(rotate_btn, 50, 50);
    lv_obj_align(rotate_btn, LV_ALIGN_TOP_RIGHT, -8, 8);
    lv_obj_t *btn_label = lv_label_create(rotate_btn);
    lv_label_set_text(btn_label, LV_SYMBOL_REFRESH);
    lv_obj_align(btn_label, LV_ALIGN_CENTER, 0, 0);
    lv_obj_add_event_cb(
        rotate_btn, [](lv_event_t *) { rotate_display(); }, LV_EVENT_CLICKED, nullptr);
  }

  // On-screen status state. These are filled in as each subsystem initializes
  // below, and rendered immediately by the status task, so the display shows SD /
  // Ethernet / RTPS coming online live instead of staying blank until the whole
  // bring-up finishes.
  static std::atomic<int> touch_x{0}, touch_y{0}, touch_n{0};
  static std::atomic<bool> sd_card_mounted{false};
  static std::atomic<uint32_t> sd_card_size_mb{0};
  static std::atomic<bool> rtps_running{false}, rtps_has_peers{false};
  static std::atomic<uint32_t> rtps_value{0};
  static int64_t status_start_us = esp_timer_get_time();

  // Status updater: starts now (right after the display is up) and refreshes the
  // on-screen status ~10x/s. Ethernet state is read live from the board; SD and
  // RTPS state are published into the atomics above as those subsystems come up.
  espp::Task status_task(espp::Task::Config{
      .callback = [&board](std::mutex &m, std::condition_variable &cv) -> bool {
        const size_t free_internal = heap_caps_get_free_size(MALLOC_CAP_INTERNAL) / 1024;
        const size_t free_psram = heap_caps_get_free_size(MALLOC_CAP_SPIRAM) / 1024;
        const int uptime_s = static_cast<int>((esp_timer_get_time() - status_start_us) / 1'000'000);
        std::string eth_text = "(no link)";
        if (board.is_ethernet_connected()) {
          auto ip = board.ethernet_ip();
          eth_text = std::to_string(esp_ip4_addr1_16(&ip)) + "." +
                     std::to_string(esp_ip4_addr2_16(&ip)) + "." +
                     std::to_string(esp_ip4_addr3_16(&ip)) + "." +
                     std::to_string(esp_ip4_addr4_16(&ip));
        }
        std::string rtps_text =
            rtps_running ? ("publishing #" + std::to_string(rtps_value.load()) +
                            (rtps_has_peers ? "" : " (no peers)"))
                         : (board.is_ethernet_connected() ? std::string("not started")
                                                          : std::string("waiting for network"));
        std::string status =
            "Panel:    " + std::string(board.get_display_controller_name()) + " (" +
            std::to_string(board.display_width()) + "x" + std::to_string(board.display_height()) +
            ")\n" + "Touch:    " + std::to_string(touch_n.load()) + " pts (" +
            std::to_string(touch_x.load()) + ", " + std::to_string(touch_y.load()) + ")\n" +
            "SD card:  " +
            (sd_card_mounted ? std::to_string(sd_card_size_mb.load()) + " MB" : "none") + "\n" +
            "Ethernet: " + eth_text + "\n" + "RTPS:     " + rtps_text + "\n" +
            "System:   " + std::to_string(free_internal) + " KB int, " +
            std::to_string(free_psram) + " KB psram free, up " + std::to_string(uptime_s) + " s";
        {
          std::lock_guard<std::recursive_mutex> lock(lvgl_mutex);
          if (status_label) {
            lv_label_set_text(status_label, status.c_str());
          }
        }
        std::unique_lock<std::mutex> lock(m);
        cv.wait_for(lock, 100ms);
        return false;
      },
      .task_config = {.name = "p4-ev status", .stack_size_bytes = 6144}});
  status_task.start();

  // Touch: draw a circle wherever the screen is touched, and play a click on
  // each new touch-down. play_audio() is non-blocking, and the click is gated to
  // the touch-down edge so it doesn't retrigger every poll while held/dragging.
  //
  // The touch task polls at ~16 ms, so we must NOT draw a circle on every poll:
  // a held/stationary finger would stack many translucent (LV_OPA_70) circles at
  // the same point and they'd composite to look fully opaque. Draw only on a new
  // touch-down or once the point has moved at least one radius, so a stationary
  // touch draws a single circle and a drag leaves a spaced trail.
  static constexpr int kCircleRadius = 10;
  board.initialize_touch([&](const auto &data) {
    auto td = board.touchpad_convert(data);
    static Board::TouchpadData prev_td = {};
    touch_n = td.num_touch_points;
    touch_x = td.x;
    touch_y = td.y;
    if (td.num_touch_points > 0) {
      const bool new_touch = (prev_td != td);
      if (new_touch && !audio_bytes.empty()) {
        board.play_audio(audio_bytes); // non-blocking, touch-down edge only
      }
      if (new_touch) {
        std::lock_guard<std::recursive_mutex> lock(lvgl_mutex);
        draw_circle(td.x, td.y, kCircleRadius);
      }
    }
    prev_td = td;
  });

  // Run the LVGL task handler periodically
  espp::Task lv_task({.callback = [](std::mutex &m, std::condition_variable &cv) -> bool {
                        {
                          std::lock_guard<std::recursive_mutex> lock(lvgl_mutex);
                          lv_task_handler();
                        }
                        std::unique_lock<std::mutex> lock(m);
                        cv.wait_for(lock, 16ms);
                        return false;
                      },
                      .task_config = {.name = "lvgl", .stack_size_bytes = 8192}});
  lv_task.start();

  // microSD (optional — only present if a card is inserted)
  bool sd_ok = board.initialize_sdcard({.format_if_mount_failed = false});
  uint32_t sd_size_mb = 0, sd_free_mb = 0;
  if (sd_ok) {
    board.get_sd_card_info(&sd_size_mb, &sd_free_mb);
    logger.info("SD card: {} MB total, {} MB free", sd_size_mb, sd_free_mb);
  } else {
    logger.warn("No SD card mounted");
  }
  sd_card_mounted = sd_ok;
  sd_card_size_mb = sd_size_mb; // published to the status task

  // Audio (ES8311) — load the embedded click sound first so we can initialize
  // the codec directly at the clip's sample rate (changing the sample rate after
  // the audio task is running is racy, so we avoid it here).
  size_t wav_size = 0, wav_sample_rate = 0;
  bool have_audio = load_audio(wav_size, wav_sample_rate);
  uint32_t audio_rate = have_audio ? static_cast<uint32_t>(wav_sample_rate) : 48000;
  if (board.initialize_audio(audio_rate)) {
    board.mute(false);
    board.volume(60.0f);
    if (have_audio) {
      logger.info("Loaded {} bytes of click audio @ {} Hz", wav_size, wav_sample_rate);
    }
  }

  // Ethernet (IP101) — DHCP; the callback fires once an IP is acquired
  static std::atomic<bool> have_ip{false};
  static std::string ip_str{"(no link)"};
  board.initialize_ethernet([&](esp_ip4_addr_t ip) {
    char buf[16];
    esp_ip4addr_ntoa(&ip, buf, sizeof(buf));
    ip_str = buf;
    have_ip = true;
    logger.info("Ethernet IP: {}", ip_str);
  });

  // BOOT button — clears the drawn circles
  //
  // NOTE: GPIO35 is shared with Ethernet RMII TXD1 on this board, so the BOOT
  //       button can't be used as a runtime input while Ethernet is active
  //       (initialize_button() refuses to run when Ethernet is up). It is
  //       disabled here since this example uses Ethernet.
  bool button_initialized = board.initialize_button([&](const auto &event) {
    if (event.active) {
      std::lock_guard<std::recursive_mutex> lock(lvgl_mutex);
      clear_circles();
    }
  });
  if (button_initialized) {
    logger.error("BOOT button incorrectly initialized while Ethernet is active!");
  } else {
    logger.info("BOOT button not initialized (shared with Ethernet RMII TXD1 pin)");
  }

  // Connectivity self-test: once we have an IP (and a moment for RTPS discovery),
  // ping the gateway and the discovered peer once, then stop. This makes it easy
  // to tell board-vs-network problems apart (e.g. gateway reachable but peer not
  // => client isolation / L2 reachability problem, not the board).
  espp::Task ping_task(
      espp::Task::Config{.callback = [&logger](std::mutex &m, std::condition_variable &cv) -> bool {
                           if (!have_ip) {
                             std::unique_lock<std::mutex> lk(m);
                             cv.wait_for(lk, 250ms);
                             return false; // keep waiting for an IP
                           }
                           // give RTPS discovery a few seconds to find the peer
                           {
                             std::unique_lock<std::mutex> lk(m);
                             cv.wait_for(lk, 4s);
                           }
                           logger.info("=== Connectivity self-test (ping) ===");
                           esp_netif_ip_info_t ip_info{};
                           if (auto *netif = esp_netif_get_default_netif()) {
                             esp_netif_get_ip_info(netif, &ip_info);
                           }
                           char gw[16] = {0};
                           esp_ip4addr_ntoa(&ip_info.gw, gw, sizeof(gw));
                           ping_target(logger, "gateway", gw);
                           std::string peer;
                           {
                             std::lock_guard<std::mutex> lk(g_peer_mutex);
                             peer = g_peer_addr;
                           }
                           if (!peer.empty()) {
                             ping_target(logger, "peer", peer);
                           } else {
                             logger.warn("Ping self-test: no RTPS peer discovered yet to ping");
                           }
                           logger.info("=== Connectivity self-test done ===");
                           return true; // one-shot
                         },
                         .task_config = {.name = "ping-test", .stack_size_bytes = 8192}});
  ping_task.start();

Note

The BOOT button cannot be used simultaneously with the ethernet PHY, since the BOOT button is connected to the PHY’s RMII_TXD1 pin. If you need to use the BOOT button, you must disable the ethernet PHY.

Public Types

enum class DisplayController

Enum for the display controller type (selected via Kconfig)

Values:

enumerator UNKNOWN
enumerator EK79007
enumerator ILI9881C
using button_callback_t = espp::Interrupt::event_callback_fn

Alias for the button callback function.

using Pixel = lv_color16_t

Alias for the pixel type used by the display.

using DisplayDriver = espp::display_drivers::Controller

Alias for the low-level display driver interface.

using TouchDriver = espp::Gt911

Alias for the GT911 touch controller.

using TouchpadData = espp::TouchpadData

Alias for the touchpad data.

using touch_callback_t = std::function<void(const TouchpadData&)>

Alias for the touch callback when touch events are received.

Callback invoked when the Ethernet link goes up (with the assigned IP)

Public Functions

inline I2c &internal_i2c()

Get a reference to the internal I2C bus

Note

Shared by the GT911 touch, ES8311 codec, and camera SCCB

Returns:

A reference to the internal I2C bus

inline espp::Interrupt &interrupts()

Get a reference to the interrupts

Returns:

A reference to the interrupts

inline DisplayController get_display_controller() const

Get the display controller type for the configured panel

Returns:

The display controller type

inline const char *get_display_controller_name() const

Get a string name for the configured display controller

Returns:

String name of the controller

bool initialize_lcd()

Initialize the LCD (MIPI-DSI + configured panel driver)

Returns:

true if the LCD was successfully initialized, false otherwise

bool initialize_display(size_t pixel_buffer_size = 0)

Initialize the LVGL display

Parameters:

pixel_buffer_size – The size of the pixel buffer, in pixels. If 0, a default based on the detected panel width is used.

Returns:

true if the display was successfully initialized, false otherwise

bool initialize_touch(const touch_callback_t &callback = nullptr, gpio_num_t interrupt_pin = touch_interrupt_default)

Initialize the GT911 multi-touch controller

Note

The HMI subboard does not route the GT911 INT pin to the ESP32-P4 by default (hence polling); the LCD expansion header exposes it, so wiring it to a free GPIO enables the interrupt-driven path.

Parameters:
  • callback – The touchpad callback

  • interrupt_pin – GPIO wired to the GT911 touch INT pin. If GPIO_NUM_NC (the default, unless interrupt-driven touch is enabled via Kconfig), the GT911 is polled in a task. If a valid GPIO is provided, touch is read from a GPIO interrupt on that pin instead of polling.

Returns:

true if the touchpad was successfully initialized, false otherwise

inline size_t bytes_per_pixel() const

Get the number of bytes per pixel for the display

Returns:

The number of bytes per pixel

inline std::shared_ptr<TouchpadInput> touchpad_input() const

Get the touchpad input

Returns:

A shared pointer to the touchpad input

inline TouchpadData touchpad_data() const

Get the most recent touchpad data

Returns:

The touchpad data

void touchpad_read(uint8_t *num_touch_points, uint16_t *x, uint16_t *y, uint8_t *btn_state)

Get the touchpad data for LVGL integration

Parameters:
  • num_touch_points – The number of touch points

  • x – The x coordinate

  • y – The y coordinate

  • btn_state – The button state (0 = released, 1 = pressed)

TouchpadData touchpad_convert(const TouchpadData &data) const

Convert touchpad data from raw reading to display coordinates

Parameters:

data – The touchpad data to convert

Returns:

The converted touchpad data

void brightness(float brightness)

Set the display brightness

Parameters:

brightness – The brightness as a percentage (0-100)

float brightness() const

Get the display brightness

Returns:

The brightness as a percentage (0-100)

inline size_t display_width() const

Get the display width in pixels (of the detected/active panel)

Note

Valid after initialize_lcd() has detected the panel.

Returns:

The display width in pixels

inline size_t display_height() const

Get the display height in pixels (of the detected/active panel)

Note

Valid after initialize_lcd() has detected the panel.

Returns:

The display height in pixels

size_t rotated_display_width() const

Get the display width in pixels, according to the current orientation.

size_t rotated_display_height() const

Get the display height in pixels, according to the current orientation.

inline const std::shared_ptr<DisplayDriver> &display_driver() const

Get a shared pointer to the low-level display driver

Returns:

A shared pointer to the display driver

void write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data)

Write lines to the LCD

Note

This method queues the panel transfer asynchronously.

bool initialize_audio (uint32_t sample_rate=48000, const espp::Task::BaseConfig &task_config={ .name="p4_ev_audio",.stack_size_bytes=CONFIG_ESP_P4_EV_BOARD_AUDIO_TASK_STACK_SIZE,.priority=20,.core_id=0})

Initialize the audio system (ES8311 codec)

Parameters:
  • sample_rate – The audio sample rate in Hz (default 48kHz)

  • task_config – The task configuration for the audio task

Returns:

true if the audio system was successfully initialized

void set_speaker_enabled(bool enable)

Enable or disable the speaker amplifier (NS4150B PA on GPIO53)

Parameters:

enable – True to enable the amplifier, false to disable

void volume(float volume)

Set the audio volume

Parameters:

volume – The volume as a percentage (0-100)

float volume() const

Get the audio volume

Returns:

The volume as a percentage (0-100)

void mute(bool mute)

Mute or unmute the audio

Parameters:

mute – True to mute, false to unmute

bool is_muted() const

Check if audio is muted

Returns:

True if muted, false otherwise

uint32_t audio_sample_rate() const

Get the audio sample rate

Returns:

The audio sample rate, in Hz

void audio_sample_rate(uint32_t sample_rate)

Set the audio sample rate

Parameters:

sample_rate – The audio sample rate, in Hz

size_t audio_buffer_size() const

Get the audio buffer size, in bytes

Returns:

The audio buffer size, in bytes

void play_audio(const uint8_t *data, uint32_t num_bytes)

Play audio data

Parameters:
  • data – The audio data to play

  • num_bytes – The number of bytes to play

void play_audio(std::span<const uint8_t> data)

Play audio data

Parameters:

data – The audio data to play

bool initialize_sdcard(const SdCardConfig &config)

Initialize microSD / uSD card

Parameters:

config – Configuration for the uSD card

Returns:

True if uSD card was successfully initialized

inline bool is_sd_card_available() const

Check if SD card is present and mounted

Returns:

True if SD card is available

inline sdmmc_card_t *sdcard() const

Get the uSD card handle

Returns:

A pointer to the uSD card, or nullptr if not initialized

bool get_sd_card_info(uint32_t *size_mb, uint32_t *free_mb) const

Get SD card info

Parameters:
  • size_mb – Pointer to store size in MB

  • free_mb – Pointer to store free space in MB

Returns:

True if info retrieved successfully

Initialize the Ethernet interface (EMAC + IP101 RMII PHY, DHCP client)

Note

Requires the ESP-IDF default event loop. The BSP creates it if needed.

Parameters:

on_link_up – Optional callback invoked when an IP address is acquired

Returns:

True if Ethernet was successfully initialized and started

inline bool is_ethernet_connected() const

Check whether the Ethernet link is up (cable connected + negotiated)

Returns:

True if the link is up

inline esp_ip4_addr_t ethernet_ip() const

Get the most recently acquired IPv4 address (0 if none)

Returns:

The IPv4 address

bool initialize_camera()

Initialize the MIPI-CSI camera (SC2336/OV5647).

Note

Not yet implemented — the camera pins/SCCB are documented in this BSP but the esp_video capture pipeline is not wired up. This always returns false for now.

Returns:

True if successful

bool initialize_button(const button_callback_t &callback = nullptr)

Initialize the BOOT button

Parameters:

callback – The callback function to call when pressed/released

Returns:

True if the button was successfully initialized

bool button_state() const

Get the button state

Returns:

True if pressed, false otherwise

inline const std::string &get_name() const

Get the name of the component

Note

This is the tag of the logger

Returns:

A const reference to the name of the component

inline void set_log_tag(const std::string_view &tag)

Set the tag for the logger

Parameters:

tag – The tag to use for the logger

inline espp::Logger::Verbosity get_log_level() const

Get the log level for the logger

Returns:

The verbosity level of the logger

inline void set_log_level(espp::Logger::Verbosity level)

Set the log level for the logger

Parameters:

level – The verbosity level to use for the logger

inline void set_log_verbosity(espp::Logger::Verbosity level)

Set the log verbosity for the logger

See also

set_log_level

Note

This is a convenience method that calls set_log_level

Parameters:

level – The verbosity level to use for the logger

inline espp::Logger::Verbosity get_log_verbosity() const

Get the log verbosity for the logger

See also

get_log_level

Note

This is a convenience method that calls get_log_level

Returns:

The verbosity level of the logger

inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)

Set the rate limit for the logger

Note

Only calls to the logger that have _rate_limit suffix will be rate limited

Parameters:

rate_limit – The rate limit to use for the logger

Public Static Functions

static inline Esp32P4FunctionEvBoard &get()

Access the singleton instance.

Returns:

Reference to the singleton instance

Public Static Attributes

static constexpr char mount_point[] = "/sdcard"

Mount point for the uSD card.

static constexpr gpio_num_t touch_interrupt_default = GPIO_NUM_NC

Default touch INT GPIO used by initialize_touch(). GPIO_NUM_NC means the GT911 is polled; if interrupt-driven touch is enabled via Kconfig this is the configured GPIO (CONFIG_ESP_P4_EV_BOARD_TOUCH_INTERRUPT_GPIO).

struct SdCardConfig

Configuration for the uSD card.

Public Members

bool format_if_mount_failed = false

Format the uSD card if mount failed.

int max_files = 5

The maximum number of files to open at once.

size_t allocation_unit_size = 2 * 1024

The allocation unit size in bytes.