ESP BOX

Esp-Box

The ESP32-S3-BOX and ESP32-S3-BOX-3 are development boards for the ESP32-S3 module. They feature a nice touchscreen display, a speaker, microphones, and expansion headers.

The espp::EspBox component provides a singleton hardware abstraction for initializing the touch, display, and audio subsystems, as well as automatically determining which version of the Box it’s running on.

API Reference

Header File

Classes

class EspBox : public espp::BaseComponent

The EspBox class provides an interface to the ESP32-S3-BOX and ESP32-S3-BOX-3 development boards.

The class provides access to the following features:

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

Example

  espp::EspBox &box = espp::EspBox::get();
  box.set_log_level(espp::Logger::Verbosity::INFO);
  logger.info("Running on {}", box.box_type());

  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 = box.touchpad_convert(touch);
    auto touchpad_data = box.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) {
        play_click(box);
        std::lock_guard<std::recursive_mutex> lock(lvgl_mutex);
        draw_circle(touchpad_data.x, touchpad_data.y, 10);
      }
    }
  };

  // initialize the sound
  if (!box.initialize_sound()) {
    logger.error("Failed to initialize sound!");
    return;
  }
  // initialize the LCD
  if (!box.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 = box.lcd_width() * 50;
  espp::Task::BaseConfig display_task_config = {
      .name = "Display",
      .stack_size_bytes = 6 * 1024,
      .priority = 10,
      .core_id = 0,
  };
  // initialize the LVGL display for the esp-box
  if (!box.initialize_display(pixel_buffer_size, display_task_config)) {
    logger.error("Failed to initialize display!");
    return;
  }
  // initialize the touchpad
  if (!box.initialize_touch(touch_callback)) {
    logger.error("Failed to initialize touchpad!");
    return;
  }

  // set the background color to black
  lv_obj_t *bg = lv_obj_create(lv_screen_active());
  lv_obj_set_size(bg, box.lcd_width(), box.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());
  lv_label_set_text(label, "Touch the screen!\nPress the home button to clear circles.");
  lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
  lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 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_refr_get_disp_refreshing();
        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);

  // 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",
                      }});
  lv_task.start();

  // load the audio file (wav file bundled in memory)
  size_t wav_size = load_audio();
  logger.info("Loaded {} bytes of audio", wav_size);

  // unmute the audio and set the volume to 60%
  box.mute(false);
  box.volume(60.0f);

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

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

Public Types

enum class BoxType

The type of the box.

Values:

enumerator UNKNOWN

unknown box

enumerator BOX

ESP32-S3-BOX.

enumerator BOX3

ESP32-S3-BOX-3.

using Pixel = lv_color16_t

Alias for the pixel type used by the ESP-Box display.

using DisplayDriver = espp::St7789

Alias for the display driver used by the ESP-Box display.

Public Functions

BoxType box_type() const

Get the type of the box

See also

BoxType

Returns

The type of the box that was detected

I2c &internal_i2c()

Get a reference to the internal I2C bus

Note

The internal I2C bus is used for the touchscreen and audio codec

Returns

A reference to the internal I2C bus

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

Warning

This method should be called after the display has been initialized if you want the touchpad to be recognized and used with LVGL and its objects.

Parameters

callback – The touchpad callback

Returns

true if the touchpad was successfully initialized, false otherwise

std::shared_ptr<TouchpadInput> touchpad_input() const

Get the touchpad input

Returns

A shared pointer to the touchpad input

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, const espp::Task::BaseConfig &task_config={.name="Display",.stack_size_bytes=4096,.priority=10,.core_id=0}, int update_period_ms=16)

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

  • task_config – The task configuration for the display task

  • update_period_ms – The update period of the display task

Returns

true if the display was successfully initialized, false otherwise

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

Parameters

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

float brightness() const

Get the brightness of the backlight

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

uint8_t *frame_buffer0() const

Get the frame buffer 0 pointer

Note

This memory is designed to be used by the application developer and is provided as a convenience. It is not used by the display driver.

Note

This is null unless initialize_display() has been called

Returns

The frame buffer 0 pointer

uint8_t *frame_buffer1() const

Get the frame buffer 1 pointer

Note

This memory is designed to be used by the application developer and is provided as a convenience. It is not used by the display driver.

Note

This is null unless initialize_display() has been called

Returns

The frame buffer 1 pointer

void write_lcd(const uint8_t *data, size_t length, uint32_t user_data)

Write data 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
  • data – The data to write

  • length – The length of the data

  • 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_sound(uint32_t default_audio_rate = 48000)

Initialize the sound subsystem

Parameters

default_audio_rate – The default audio rate

Returns

true if the sound subsystem was successfully initialized, false otherwise

void enable_sound(bool enable)

Enable or disable sound

Note

This method sets the power pin to the appropriate value

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

Returns

The audio buffer size, in bytes

void mute(bool mute)

Mute or unmute the audio

Parameters

mute – true to mute the audio, false to unmute the audio

bool is_muted() const

Check if the audio is muted

Returns

true if the audio is muted, false otherwise

void volume(float volume)

Set the volume

Parameters

volume – The volume in percent (0 - 100)

float volume() const

Get the volume

Returns

The volume in percent (0 - 100)

void play_audio(const std::vector<uint8_t> &data)

Play audio

Parameters

data – The audio data to play

void play_audio(const uint8_t *data, uint32_t num_bytes)

Play audio

Parameters
  • data – The audio data to play

  • num_bytes – The number of bytes to play

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

Access the singleton instance of the EspBox class.

Returns

Reference to the singleton instance of the EspBox 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

static inline constexpr auto get_mute_pin()

Get the GPIO pin for the mute button (top of the box)

Returns

The GPIO pin for the mute button