Smart Panlee SC01 Plus

Smart Panlee SC01 Plus

The Smart Panlee SC01 Plus is an ESP32-S3 touchscreen display module with a 3.5-inch 320x480 ST7796 LCD, FT5x06 capacitive touch, direct I2S speaker playback, SPI microSD support, and published I2S / RS-485 / expansion GPIO pin maps.

The espp::SmartPanleeSc01Plus component provides a singleton hardware abstraction for the display, touch, backlight, audio output, and microSD card, while also exposing the board’s documented peripheral pins for application use.

API Reference

Header File

Classes

class SmartPanleeSc01Plus : public espp::BaseComponent

The SmartPanleeSc01Plus class provides an interface to the Smart Panlee SC01 Plus development board.

The class provides access to the following features:

  • ST7796 3.5-inch 320x480 display over an 8-bit Intel 8080 style bus

  • FT5x06 capacitive touch controller

  • PWM backlight control

  • I2S audio playback for the onboard speaker path

  • SPI microSD card mounting helpers

  • Published I2S, RS-485, and expansion pin maps

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

Example

  auto &board = espp::SmartPanleeSc01Plus::get();

  if (!board.initialize_lcd()) {
    logger.error("Failed to initialize LCD!");
    return;
  }

  if (!board.initialize_display(board.display_width() * 40)) {
    logger.error("Failed to initialize display!");
    return;
  }

  auto touch_callback = [&](const auto &touch) {
    static auto previous_touchpad_data = board.touchpad_convert(touch);
    auto touchpad_data = board.touchpad_convert(touch);
    if (touchpad_data != previous_touchpad_data) {
      previous_touchpad_data = touchpad_data;
      if (touchpad_data.num_touch_points > 0) {
        play_click(board);
        std::lock_guard<std::recursive_mutex> lock(lvgl_mutex);
        draw_circle(touchpad_data.x, touchpad_data.y, 10);
      }
    }
  };

  if (!board.initialize_touch(touch_callback)) {
    logger.warn("Touch initialization did not complete cleanly");
  }

  if (!board.initialize_audio()) {
    logger.warn("Audio initialization did not complete cleanly");
  } else {
    size_t wav_size = 0;
    size_t wav_sample_rate = 0;
    if (load_audio(wav_size, wav_sample_rate)) {
      logger.info("Loaded {} bytes of audio at {} Hz", wav_size, wav_sample_rate);
      board.audio_sample_rate(wav_sample_rate);
      board.volume(30.0f);
      board.mute(false);
    } else {
      logger.warn("Could not load the embedded click sound");
    }
  }

  board.brightness(80.0f);

  if (!board.initialize_sdcard()) {
    logger.info("No microSD card mounted");
  }

  auto i2s = board.i2s_pins();
  auto rs485 = board.rs485_pins();
  logger.info("I2S pins: bclk={}, ws={}, dout={}", i2s.bclk, i2s.ws, i2s.dout);
  logger.info("RS485 pins: rts={}, rxd={}, txd={}", rs485.rts, rs485.rxd, rs485.txd);

  lv_obj_t *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(8, 12, 24), 0);

  lv_obj_t *label = lv_label_create(lv_screen_active());
  lv_label_set_text(label, "Smart Panlee SC01 Plus\n\nTouch the screen to draw and play a click.\n"
                           "Press refresh to rotate.\nCheck serial output for SD card, pin, and "
                           "audio info.");
  lv_obj_align(label, LV_ALIGN_TOP_LEFT, 16, 16);
  lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_LEFT, 0);

  lv_obj_t *btn = lv_btn_create(lv_screen_active());
  lv_obj_set_size(btn, 56, 56);
  lv_obj_align(btn, LV_ALIGN_TOP_RIGHT, -12, 12);
  lv_obj_t *btn_label = lv_label_create(btn);
  lv_label_set_text(btn_label, LV_SYMBOL_REFRESH);
  lv_obj_align(btn_label, LV_ALIGN_CENTER, 0, 0);
  background = bg;
  initialize_circles();
  lv_obj_add_event_cb(
      btn,
      [](lv_event_t *event) {
        (void)event;
        rotate_display();
      },
      LV_EVENT_PRESSED, nullptr);

  lv_obj_set_scrollbar_mode(lv_screen_active(), LV_SCROLLBAR_MODE_OFF);
  lv_obj_clear_flag(lv_screen_active(), LV_OBJ_FLAG_SCROLLABLE);

  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 = "lv_task",
                          .stack_size_bytes = 6 * 1024,
                      }});
  lv_task.start();

Public Types

using Pixel = lv_color16_t

Alias for the pixel type used by the display.

using DisplayDriver = espp::St7796

Alias for the ST7796 display driver wrapper.

using TouchDriver = espp::Ft5x06

Alias for the FT5x06-family capacitive touch controller.

using TouchpadData = espp::TouchpadData

Alias for the touch data structure exposed to callers.

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

Callback type invoked when fresh touch data is available.

Public Functions

SmartPanleeSc01Plus(const SmartPanleeSc01Plus&) = delete

Deleted copy constructor.

SmartPanleeSc01Plus &operator=(const SmartPanleeSc01Plus&) = delete

Deleted copy assignment operator.

SmartPanleeSc01Plus(SmartPanleeSc01Plus&&) = delete

Deleted move constructor.

SmartPanleeSc01Plus &operator=(SmartPanleeSc01Plus&&) = delete

Deleted move assignment operator.

inline I2c &internal_i2c()

Get the internal I2C bus used for touch and board peripherals.

Returns:

Reference to the internal I2C instance.

inline espp::Interrupt &interrupts()

Get the interrupt manager used by the BSP.

Returns:

Reference to the interrupt manager.

bool initialize_touch(const touch_callback_t &callback = nullptr)

Initialize the touch controller and optional interrupt-driven callback.

Parameters:

callback – Callback invoked when touch data changes.

Returns:

True if touch support was initialized, false otherwise.

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

Get the LVGL touch input wrapper created by initialize_display().

Returns:

Shared pointer to the touchpad input wrapper, or nullptr if not initialized.

TouchpadData touchpad_data() const

Get the most recently cached touch data.

Returns:

Latest cached touchpad data.

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

Read the cached touch data using the signature expected by TouchpadInput.

Parameters:
  • num_touch_points – Filled with the number of active touch points.

  • x – Filled with the x coordinate of the primary touch point.

  • y – Filled with the y coordinate of the primary touch point.

  • btn_state – Filled with the button state for LVGL compatibility.

TouchpadData touchpad_convert(const TouchpadData &data) const

Convert raw touch data into display-oriented coordinates.

Parameters:

data – Raw cached touch data.

Returns:

Touch data transformed for the current display rotation and inversion settings.

bool initialize_lcd()

Initialize the low-level LCD transport and backlight control.

Returns:

True if the LCD interface was initialized, false otherwise.

bool initialize_display(size_t pixel_buffer_size = 320 * 40)

Initialize the LVGL display wrapper.

Parameters:

pixel_buffer_sizeDisplay buffer size in pixels.

Returns:

True if the display wrapper was initialized, false otherwise.

inline std::shared_ptr<Display<Pixel>> display() const

Get the high-level LVGL display wrapper.

Returns:

Shared pointer to the display wrapper, or nullptr if not initialized.

void brightness(float brightness)

Set the display backlight brightness.

Parameters:

brightness – Brightness percentage in the range [0, 100].

float brightness() const

Get the display backlight brightness.

Returns:

Brightness percentage in the range [0, 100].

size_t rotated_display_width() const

Get the current display width after applying LVGL rotation.

Returns:

Rotated width in pixels.

size_t rotated_display_height() const

Get the current display height after applying LVGL rotation.

Returns:

Rotated height in pixels.

bool initialize_audio()

Initialize the speaker audio path using the board’s published I2S pins.

Returns:

True if audio playback support was initialized, false otherwise.

bool initialize_audio(uint32_t sample_rate)

Initialize the speaker audio path using the specified sample rate.

Parameters:

sample_rate – Audio sample rate in Hz.

Returns:

True if audio playback support was initialized, false otherwise.

bool initialize_audio(uint32_t sample_rate, const espp::Task::BaseConfig &task_config)

Initialize the speaker audio path using a custom task configuration.

Parameters:
  • sample_rate – Audio sample rate in Hz.

  • task_configTask configuration for the background audio pump.

Returns:

True if audio playback support was initialized, false otherwise.

void audio_sample_rate(uint32_t sample_rate)

Set the audio sample rate.

Parameters:

sample_rate – Audio sample rate in Hz.

uint32_t audio_sample_rate() const

Get the active audio sample rate.

Returns:

Audio sample rate in Hz, or 0 if audio is not initialized.

size_t audio_buffer_size() const

Get the internal playback buffer size.

Returns:

Audio buffer size in bytes.

void mute(bool mute)

Mute or unmute speaker playback.

Parameters:

mute – True to mute playback, false to unmute it.

bool is_muted() const

Query whether speaker playback is muted.

Returns:

True if playback is muted.

void volume(float volume)

Set the software playback volume.

Parameters:

volume – Volume percentage in the range [0, 100].

float volume() const

Get the software playback volume.

Returns:

Volume percentage in the range [0, 100].

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

Queue raw PCM audio bytes for playback.

Parameters:

data – Audio payload to play.

void play_audio(const uint8_t *data, uint32_t num_bytes)

Queue raw PCM audio bytes for playback.

Parameters:
  • data – Pointer to PCM audio bytes.

  • num_bytes – Number of bytes to play.

bool initialize_sdcard()

Initialize and mount the optional microSD card with default settings.

Returns:

True if the card was mounted successfully, false otherwise.

bool initialize_sdcard(const SdCardConfig &config)

Initialize and mount the optional microSD card with custom settings.

Parameters:

config – Mount configuration.

Returns:

True if the card was mounted successfully, false otherwise.

bool is_sd_card_available() const

Check whether the microSD card is currently mounted.

Returns:

True if the card is mounted and available.

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

Query mounted microSD capacity and free space.

Parameters:
  • size_mb – Optional output for total capacity in megabytes.

  • free_mb – Optional output for free space in megabytes.

Returns:

True if card information was retrieved successfully, 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 SmartPanleeSc01Plus &get()

Access the singleton instance.

Returns:

Reference to the singleton board-support instance.

static inline constexpr I2sPins i2s_pins()

Get the published I2S pin mapping for the board.

Returns:

I2S pin mapping.

static inline constexpr SdCardPins sd_card_pins()

Get the published SPI microSD pin mapping for the board.

Returns:

MicroSD pin mapping.

static inline constexpr Rs485Pins rs485_pins()

Get the published RS-485 pin mapping for the board.

Returns:

RS-485 pin mapping.

static inline constexpr ExternalPins external_pins()

Get the published external expansion GPIO mapping for the board.

Returns:

External GPIO mapping.

static inline constexpr size_t bytes_per_pixel()

Get the number of bytes per pixel used by the panel.

Returns:

Bytes per pixel.

static inline constexpr size_t display_width()

Get the native display width.

Returns:

Width in pixels.

static inline constexpr size_t display_height()

Get the native display height.

Returns:

Height in pixels.

Public Static Attributes

static constexpr char mount_point[] = "/sdcard"

Mount point used when the optional microSD card is mounted.

struct ExternalPins

External expansion GPIOs published for the board.

Public Members

gpio_num_t io1

External GPIO 1.

gpio_num_t io2

External GPIO 2.

gpio_num_t io3

External GPIO 3.

gpio_num_t io4

External GPIO 4.

gpio_num_t io5

External GPIO 5.

gpio_num_t io6

External GPIO 6.

struct I2sPins

Published I2S pins for the SC01 Plus speaker interface.

Public Members

gpio_num_t mclk

Optional master clock pin.

gpio_num_t bclk

I2S bit clock pin.

gpio_num_t ws

I2S word-select / LRCLK pin.

gpio_num_t dout

I2S data-out pin.

gpio_num_t din

Optional I2S data-in pin.

struct Rs485Pins

RS-485 UART control and data pins published for the board.

Public Members

gpio_num_t rts

RS-485 direction-control pin.

gpio_num_t rxd

UART RX pin.

gpio_num_t txd

UART TX pin.

struct SdCardConfig

Configuration for mounting the optional microSD card.

Public Members

bool format_if_mount_failed = {false}

Format the card if mounting fails.

int max_files = {5}

Maximum number of simultaneously open files.

size_t allocation_unit_size = {16 * 1024}

FAT allocation unit size in bytes.

struct SdCardPins

SPI microSD pin mapping published for the board.

Public Members

gpio_num_t clk

SPI clock pin.

gpio_num_t mosi

SPI MOSI pin.

gpio_num_t miso

SPI MISO pin.

gpio_num_t cs

SPI chip-select pin.