Waveshare ESP32-S3 TouchLCD

WS-S3-Touch

The Waveshare S3 TouchLCD is a development board featuring the ESP32-S3 module. It includes a small 1.69-inch touchscreen display, a buzzer, a 6-axis IMU, and a real-time clock (RTC).

The espp::WsS3Touch component provides a singleton hardware abstraction for initializing the touch, display, IMU, and audio subsystems.

API Reference

Header File

Classes

class WsS3Touch : public espp::BaseComponent

The WsS3Touch class provides an interface to the Waveshare ESP32-S3 TouchLCD development board(s).

The class provides access to the following features:

  • Touchpad (CST816T)

  • Display (ST7789V2, 240x280)

  • Audio (buzzer)

  • Interrupts

  • Buttons (boot)

  • I2C

  • IMU (Inertial Measurement Unit), 6-axis QMI8658

  • RTC (Real-Time Clock), PCF85063

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

Example

  using Bsp = espp::WsS3Touch;
  auto &bsp = Bsp::get();
  bsp.set_log_level(espp::Logger::Verbosity::INFO);

  auto touch_callback = [&](const auto &touch) {
    // NOTE: since we're directly using the touchpad data, and not using the
    // TouchpadInput + LVGL, we'll need to ensure the touchpad data is
    // converted into proper screen coordinates instead of simply using the
    // raw values.
    static auto previous_touchpad_data = bsp.touchpad_convert(touch);
    auto touchpad_data = bsp.touchpad_convert(touch);
    if (touchpad_data != previous_touchpad_data) {
      logger.info("Touch: {}", touchpad_data);
      previous_touchpad_data = touchpad_data;
      // if the button is pressed, clear the circles
      if (touchpad_data.btn_state) {
        std::lock_guard<std::recursive_mutex> lock(lvgl_mutex);
        clear_circles();
      }
      // if there is a touch point, draw a circle and play a click sound
      if (touchpad_data.num_touch_points > 0) {
        // set the PWM / frequency for the buzzer based on the touch point (x -> pwm, y ->
        // frequency)
        float pwm =
            touchpad_data.x / static_cast<float>(bsp.lcd_width()) * 100.0f; // scale to 0-100%
        // scale frequency to be in range [50 Hz, 10 KHz]
        static constexpr float min_frequency_hz = 50.0f;
        static constexpr float max_frequency_hz = 10000.0f;
        // make it a logarithmic scale so that the frequency is more sensitive to
        // the lower end of the touchpad
        float frequency_hz =
            min_frequency_hz * std::pow(max_frequency_hz / min_frequency_hz,
                                        touchpad_data.y / static_cast<float>(bsp.lcd_height()));
        bsp.buzzer(pwm, frequency_hz);
        std::lock_guard<std::recursive_mutex> lock(lvgl_mutex);
        draw_circle(touchpad_data.x, touchpad_data.y, 10);
      } else {
        // if there are no touch points, stop the buzzer
        bsp.buzzer(0.0f);
      }
    }
  };

  // initialize the button, clear the circles on the screen
  logger.info("Initializing the button");
  auto on_button_pressed = [&](const auto &event) {
    if (event.active) {
      logger.info("Button pressed");
      std::lock_guard<std::recursive_mutex> lock(lvgl_mutex);
      clear_circles();
      // play a click sound
      bsp.buzzer(50.0f, 1000.0f); // 50% duty cycle, 1 kHz frequency
    } else {
      logger.info("Button released");
      // stop the buzzer
      bsp.buzzer(0.0f); // stop the buzzer
    }
  };
  if (!bsp.initialize_button(on_button_pressed)) {
    logger.error("Failed to initialize button!");
    return;
  }

  // initialize the buzzer
  if (!bsp.initialize_buzzer()) {
    logger.error("Failed to initialize buzzer!");
    return;
  }
  // initialize the LCD
  if (!bsp.initialize_lcd()) {
    logger.error("Failed to initialize LCD!");
    return;
  }
  // set the pixel buffer to be 50 lines high
  static constexpr size_t pixel_buffer_size = bsp.lcd_width() * 50;
  // initialize the LVGL display
  if (!bsp.initialize_display(pixel_buffer_size)) {
    logger.error("Failed to initialize display!");
    return;
  }
  // initialize the touchpad
  if (!bsp.initialize_touch(touch_callback)) {
    logger.error("Failed to initialize touchpad!");
    return;
  }

  // initialize the RTC
  if (!bsp.initialize_rtc()) {
    logger.error("Failed to initialize RTC!");
    return;
  }
  // now set the time on the RTC
  std::tm timeinfo{
      .tm_sec = 0,
      .tm_min = 42,
      .tm_hour = 13,
      .tm_mday = 24,
      .tm_mon = 10,  // 0-11, so 10 is November
      .tm_year = 123 // years since 1900, so 100 is 2000
  };
  std::error_code ec;
  bsp.rtc()->set_time(timeinfo, ec);
  if (ec) {
    logger.error("Failed to set RTC time: {}", ec.message());
    return;
  }

  // make the filter we'll use for the IMU to compute the orientation
  static constexpr float angle_noise = 0.001f;
  static constexpr float rate_noise = 0.1f;
  static espp::KalmanFilter<2> kf;
  kf.set_process_noise(rate_noise);
  kf.set_measurement_noise(angle_noise);
  static constexpr float beta = 0.9f; // higher = more accelerometer, lower = more gyro
  static espp::MadgwickFilter f(beta);

  using Imu = Bsp::Imu;
  auto kalman_filter_fn = [](float dt, const Imu::Value &accel,
                             const Imu::Value &gyro) -> Imu::Value {
    // Apply Kalman filter
    float accelRoll = atan2(accel.y, accel.z);
    float accelPitch = atan2(-accel.x, sqrt(accel.y * accel.y + accel.z * accel.z));
    kf.predict({espp::deg_to_rad(gyro.x), espp::deg_to_rad(gyro.y)}, dt);
    kf.update({accelRoll, accelPitch});
    float roll, pitch;
    std::tie(roll, pitch) = kf.get_state();
    // return the computed orientation
    Imu::Value orientation{};
    orientation.roll = roll;
    orientation.pitch = pitch;
    orientation.yaw = 0.0f;
    return orientation;
  };

  auto madgwick_filter_fn = [](float dt, const Imu::Value &accel,
                               const Imu::Value &gyro) -> Imu::Value {
    // Apply Madgwick filter
    f.update(dt, accel.x, accel.y, accel.z, espp::deg_to_rad(gyro.x), espp::deg_to_rad(gyro.y),
             espp::deg_to_rad(gyro.z));
    float roll, pitch, yaw;
    f.get_euler(roll, pitch, yaw);
    // return the computed orientation
    Imu::Value orientation{};
    orientation.roll = espp::deg_to_rad(roll);
    orientation.pitch = espp::deg_to_rad(pitch);
    orientation.yaw = espp::deg_to_rad(yaw);
    return orientation;
  };

  // initialize the IMU
  if (!bsp.initialize_imu(kalman_filter_fn)) {
    logger.error("Failed to initialize IMU!");
    return;
  }

  logger.info("Initialization complete, starting LVGL!");

  // set the background color to black
  lv_obj_t *bg = lv_obj_create(lv_screen_active());
  lv_obj_set_size(bg, bsp.lcd_width(), bsp.lcd_height());
  lv_obj_set_style_bg_color(bg, lv_color_make(0, 0, 0), 0);

  // add text in the center of the screen
  lv_obj_t *label = lv_label_create(lv_screen_active());
  static std::string label_text =
      "\n\n\n\nTouch the screen!\nPress the home button to clear circles.";
  lv_label_set_text(label, label_text.c_str());
  lv_obj_align(label, LV_ALIGN_TOP_LEFT, 0, 0);
  lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_LEFT, 0);

  /*Create style*/
  static lv_style_t style_line0;
  lv_style_init(&style_line0);
  lv_style_set_line_width(&style_line0, 8);
  lv_style_set_line_color(&style_line0, lv_palette_main(LV_PALETTE_BLUE));
  lv_style_set_line_rounded(&style_line0, true);

  // make a line for showing the direction of "down"
  lv_obj_t *line0 = lv_line_create(lv_screen_active());
  static lv_point_precise_t line_points0[] = {{0, 0}, {bsp.lcd_width(), bsp.lcd_height()}};
  lv_line_set_points(line0, line_points0, 2);
  lv_obj_add_style(line0, &style_line0, 0);

  /*Create style*/
  static lv_style_t style_line1;
  lv_style_init(&style_line1);
  lv_style_set_line_width(&style_line1, 8);
  lv_style_set_line_color(&style_line1, lv_palette_main(LV_PALETTE_RED));
  lv_style_set_line_rounded(&style_line1, true);

  // make a line for showing the direction of "down"
  lv_obj_t *line1 = lv_line_create(lv_screen_active());
  static lv_point_precise_t line_points1[] = {{0, 0}, {bsp.lcd_width(), bsp.lcd_height()}};
  lv_line_set_points(line1, line_points1, 2);
  lv_obj_add_style(line1, &style_line1, 0);

  // make a label centered at the very top for the RTC time
  lv_obj_t *rtc_label = lv_label_create(lv_screen_active());
  lv_label_set_text(rtc_label, "");
  lv_obj_align(rtc_label, LV_ALIGN_TOP_MID, 0, 20); // add an offset so that it's always visible
  lv_obj_set_style_text_align(rtc_label, LV_TEXT_ALIGN_LEFT, 0);

  // add a button in the top left which (when pressed) will rotate the display
  // through 0, 90, 180, 270 degrees
  lv_obj_t *btn = lv_btn_create(lv_screen_active());
  lv_obj_set_size(btn, 50, 50);
  lv_obj_align(btn, LV_ALIGN_TOP_LEFT, 0, 0);
  lv_obj_t *label_btn = lv_label_create(btn);
  lv_label_set_text(label_btn, LV_SYMBOL_REFRESH);
  // center the text in the button
  lv_obj_align(label_btn, LV_ALIGN_CENTER, 0, 0);
  lv_obj_add_event_cb(
      btn,
      [](auto event) {
        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_t *disp = lv_display_get_default();
        lv_disp_set_rotation(disp, rotation);
      },
      LV_EVENT_PRESSED, nullptr);

  // disable scrolling on the screen (so that it doesn't behave weirdly when
  // rotated and drawing with your finger)
  lv_obj_set_scrollbar_mode(lv_screen_active(), LV_SCROLLBAR_MODE_OFF);
  lv_obj_clear_flag(lv_screen_active(), LV_OBJ_FLAG_SCROLLABLE);

  logger.info("Starting LVGL task handler!");

  // start a simple thread to do the lv_task_handler every 16ms
  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();

  // set the display brightness to be 75%
  bsp.brightness(75.0f);

  // make a task to read the rtc and print it to console
  espp::Task rtc_task({.callback = [&](std::mutex &m, std::condition_variable &cv) -> bool {
                         auto start = std::chrono::steady_clock::now();
                         static auto &bsp = Bsp::get();
                         static auto rtc = bsp.rtc();
                         std::error_code ec;
                         std::tm timeinfo = rtc->get_time(ec);
                         if (ec) {
                           logger.error("Failed to get RTC time: {}", ec.message());
                         } else {
                           // update the label with the current time
                           std::lock_guard<std::recursive_mutex> lock(lvgl_mutex);
                           lv_label_set_text_fmt(rtc_label, "%02d:%02d:%02d - %02d/%02d/%04d",
                                                 timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec,
                                                 timeinfo.tm_mday, timeinfo.tm_mon + 1,
                                                 timeinfo.tm_year + 1900);
                         }
                         std::unique_lock<std::mutex> lock(m);
                         cv.wait_until(lock, start + 1s);
                         return false;
                       },
                       .task_config = {
                           .name = "rtc_task",
                           .stack_size_bytes = 4 * 1024,
                       }});
  rtc_task.start();

  // make a task to read out the IMU data and print it to console
  espp::Task imu_task(
      {.callback = [&](std::mutex &m, std::condition_variable &cv) -> bool {
         // sleep first in case we don't get IMU data and need to exit early
         {
           std::unique_lock<std::mutex> lock(m);
           cv.wait_for(lock, 10ms);
         }
         static auto &bsp = Bsp::get();
         static auto imu = bsp.imu();

         auto now = esp_timer_get_time(); // time in microseconds
         static auto t0 = now;
         auto t1 = now;
         float dt = (t1 - t0) / 1'000'000.0f; // convert us to s
         t0 = t1;

         std::error_code ec;
         // update the imu data
         if (!imu->update(dt, ec)) {
           return false;
         }
         // get accel
         auto accel = imu->get_accelerometer();
         auto gyro = imu->get_gyroscope();
         auto temp = imu->get_temperature();
         auto orientation = imu->get_orientation();
         auto gravity_vector = imu->get_gravity_vector();

         // NOTE: because of the moutning of the IMU w.r.t the mounting of the
         // screen we have to rotate the axes.
         std::swap(gravity_vector.x, gravity_vector.y);
         gravity_vector.y = -gravity_vector.y;

         // now update the gravity vector line to show the direction of "down"
         // taking into account the configured rotation of the display
         auto rotation = lv_display_get_rotation(lv_display_get_default());
         if (rotation == LV_DISPLAY_ROTATION_90) {
           std::swap(gravity_vector.x, gravity_vector.y);
           gravity_vector.x = -gravity_vector.x;
         } else if (rotation == LV_DISPLAY_ROTATION_180) {
           gravity_vector.x = -gravity_vector.x;
           gravity_vector.y = -gravity_vector.y;
         } else if (rotation == LV_DISPLAY_ROTATION_270) {
           std::swap(gravity_vector.x, gravity_vector.y);
           gravity_vector.y = -gravity_vector.y;
         }

         std::string text = fmt::format("{}\n\n\n\n\n", label_text);
         text += fmt::format("Accel: {:02.2f} {:02.2f} {:02.2f}\n", accel.x, accel.y, accel.z);
         text += fmt::format("Gyro: {:03.2f} {:03.2f} {:03.2f}\n", espp::deg_to_rad(gyro.x),
                             espp::deg_to_rad(gyro.y), espp::deg_to_rad(gyro.z));
         text += fmt::format("Angle: {:03.2f} {:03.2f}\n", espp::rad_to_deg(orientation.roll),
                             espp::rad_to_deg(orientation.pitch));
         text += fmt::format("Temp: {:02.1f} C\n", temp);

         // use the pitch to to draw a line on the screen indiating the
         // direction from the center of the screen to "down"
         int x0 = bsp.lcd_width() / 2;
         int y0 = bsp.lcd_height() / 2;

         int x1 = x0 + 50 * gravity_vector.x;
         int y1 = y0 + 50 * gravity_vector.y;

         static lv_point_precise_t line_points0[] = {{x0, y0}, {x1, y1}};
         line_points0[1].x = x1;
         line_points0[1].y = y1;

         // Now show the madgwick filter
         auto madgwick_orientation = madgwick_filter_fn(dt, accel, gyro);
         float roll = madgwick_orientation.roll;
         float pitch = madgwick_orientation.pitch;
         [[maybe_unused]] float yaw = madgwick_orientation.yaw;
         float vx = sin(pitch);
         float vy = -cos(pitch) * sin(roll);
         [[maybe_unused]] float vz = -cos(pitch) * cos(roll);

         // NOTE: because of the moutning of the IMU w.r.t the mounting of the
         // screen we have to rotate the axes.
         std::swap(vx, vy);
         vy = -vy;

         // now update the line to show the direction of "down" based on the
         // configured rotation of the display
         if (rotation == LV_DISPLAY_ROTATION_90) {
           std::swap(vx, vy);
           vx = -vx;
         } else if (rotation == LV_DISPLAY_ROTATION_180) {
           vx = -vx;
           vy = -vy;
         } else if (rotation == LV_DISPLAY_ROTATION_270) {
           std::swap(vx, vy);
           vy = -vy;
         }

         x1 = x0 + 50 * vx;
         y1 = y0 + 50 * vy;

         static lv_point_precise_t line_points1[] = {{x0, y0}, {x1, y1}};
         line_points1[1].x = x1;
         line_points1[1].y = y1;

         std::lock_guard<std::recursive_mutex> lock(lvgl_mutex);
         lv_label_set_text(label, text.c_str());
         lv_line_set_points(line0, line_points0, 2);
         lv_line_set_points(line1, line_points1, 2);

         return false;
       },
       .task_config = {
           .name = "IMU",
           .stack_size_bytes = 6 * 1024,
           .priority = 10,
           .core_id = 0,
       }});
  imu_task.start();

  logger.info("Example started, waiting for touch events...");

  // loop forever
  while (true) {
    std::this_thread::sleep_for(1s);
  }

Public Types

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.

using DisplayDriver = espp::St7789

Alias for the display driver.

using TouchDriver = espp::Cst816

Alias for the touch driver.

using TouchpadData = espp::TouchpadData

Alias for the touchpad data.

using Imu = espp::Qmi8658<qmi8658::Interface::I2C>

Alias the IMU.

using Rtc = espp::Pcf85063

Alias the RTC.

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

Alias for the touch callback when touch events are received.

Public Functions

bool enable()

Initialize system power control and enable the device

Note

This is automatically called by the constructor

Returns:

true if the system was successfully initialized, false otherwise

bool disable()

Disable system power

Returns:

true if the system power control was successfully disabled

inline I2c &internal_i2c()

Get a reference to the internal I2C bus

Note

The internal I2C bus is used for the touchscreen, IMU, and RTC

Returns:

A reference to the internal I2C bus

inline espp::Interrupt &interrupts()

Get a reference to the interrupts

Returns:

A reference to the interrupts

bool initialize_touch(const touch_callback_t &callback = nullptr)

Initialize the touchpad

Note

This will configure the touchpad interrupt pin which will automatically call the touch callback function when the touchpad is touched

Note

This can be called even if you have not initialized the display or the LCD.

Parameters:

callback – The touchpad callback

Returns:

true if the touchpad was successfully initialized, false otherwise

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 most recent touchpad data

See also

touchpad_data()

Note

This method is a convenience method for integrating with LVGL, the data it returns is identical to the data returned by the touchpad_data() method

Parameters:
  • num_touch_points – The number of touch points

  • x – The x coordinate

  • y – The y coordinate

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

TouchpadData touchpad_convert(const TouchpadData &data) const

Convert touchpad data from raw reading to display coordinates

Note

Uses the touch_invert_x and touch_invert_y settings to determine if the x and y coordinates should be inverted

Parameters:

data – The touchpad data to convert

Returns:

The converted touchpad data

bool initialize_lcd()

Initialize the LCD (low level display driver)

Returns:

true if the LCD was successfully initialized, false otherwise

bool initialize_display(size_t pixel_buffer_size)

Initialize the display (lvgl display driver)

Note

This will also allocate two full frame buffers in the SPIRAM

Parameters:

pixel_buffer_size – The size of the pixel buffer

Returns:

true if the display was successfully initialized, false otherwise

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

Get a shared pointer to the display

Returns:

A shared pointer to the display

void brightness(float brightness)

Set the brightness of the backlight

Note

This function will only work after initialize_lcd() has been called

Parameters:

brightness – The brightness of the backlight as a percentage (0 - 100)

float brightness() const

Get the brightness of the backlight

Note

This function will only work after initialize_lcd() has been called

Returns:

The brightness of the backlight as a percentage (0 - 100)

Pixel *vram0() const

Get the VRAM 0 pointer (DMA memory used by LVGL)

Note

This is the memory used by LVGL for rendering

Note

This is null unless initialize_display() has been called

Returns:

The VRAM 0 pointer

Pixel *vram1() const

Get the VRAM 1 pointer (DMA memory used by LVGL)

Note

This is the memory used by LVGL for rendering

Note

This is null unless initialize_display() has been called

Returns:

The VRAM 1 pointer

void write_command(uint8_t command, std::span<const uint8_t> parameters, uint32_t user_data)

Write command and optional parameters to the LCD

Note

This method is designed to be used by the display driver

Note

This method queues the data to be written to the LCD, only blocking if there is an ongoing SPI transaction

Parameters:
  • command – The command to write

  • parameters – The command parameters to write

  • user_data – User data to pass to the spi transaction callback

void write_lcd_frame(const uint16_t x, const uint16_t y, const uint16_t width, const uint16_t height, uint8_t *data)

Write a frame to the LCD

Note

This method queues the data to be written to the LCD, only blocking if there is an ongoing SPI transaction

Parameters:
  • x – The x coordinate

  • y – The y coordinate

  • width – The width of the frame, in pixels

  • height – The height of the frame, in pixels

  • data – The data to write

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 data to be written to the LCD, only blocking if there is an ongoing SPI transaction

Parameters:
  • xs – The x start coordinate

  • ys – The y start coordinate

  • xe – The x end coordinate

  • ye – The y end coordinate

  • data – The data to write

  • user_data – User data to pass to the spi transaction callback

bool initialize_button(const button_callback_t &callback = nullptr)

Initialize the boot button (middle button)

Parameters:

callback – The callback function to call when the button is pressed

Returns:

true if the button was successfully initialized, false otherwise

bool button_state() const

Get the boot button state

Returns:

The button state (true = button pressed, false = button released)

bool initialize_buzzer()

Initialize the audio (buzzer)

Returns:

true if the audio was successfully initialized, false otherwise

bool buzzer(float pwm, size_t frequency_hz = 1000)

Play audio

Parameters:
  • pwm – The PWM value to use for the audio output (0.0 - 100.0)

  • frequency_hz – The frequency of the audio output in Hz (default is 1000)

bool initialize_imu (const Imu::filter_fn &orientation_filter=nullptr, const Imu::ImuConfig &imu_config={ .accelerometer_range=Imu::AccelerometerRange::RANGE_8G,.accelerometer_odr=Imu::ODR::ODR_250_HZ,.gyroscope_range=Imu::GyroscopeRange::RANGE_2048_DPS,.gyroscope_odr=Imu::ODR::ODR_250_HZ, })

Initialize the IMU

Parameters:
  • orientation_filter – The orientation filter, if provided

  • imu_config – The IMU configuration

Returns:

true if the IMU was successfully initialized, false otherwise

std::shared_ptr<Imu> imu() const

Get the IMU

Returns:

A shared pointer to the IMU

bool initialize_rtc()

Initialize the RTC

Returns:

true if the RTC was successfully initialized, false otherwise

std::shared_ptr<Rtc> rtc() const

Get the RTC

Returns:

A shared pointer to the RTC

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 WsS3Touch &get()

Access the singleton instance of the WsS3Touch class.

Returns:

Reference to the singleton instance of the WsS3Touch class

static inline constexpr size_t lcd_width()

Get the width of the LCD in pixels

Returns:

The width of the LCD in pixels

static inline constexpr size_t lcd_height()

Get the height of the LCD in pixels

Returns:

The height of the LCD in pixels

static inline constexpr auto get_lcd_dc_gpio()

Get the GPIO pin for the LCD data/command signal

Returns:

The GPIO pin for the LCD data/command signal

Public Static Attributes

static constexpr size_t SPI_MAX_TRANSFER_BYTES = SPI_LL_DMA_MAX_BIT_LEN / 8

Maximum number of bytes that can be transferred in a single SPI transaction to the Display. 32k on the ESP32-S3.