M5Stack Tab5
M5Stack-Tab5
The M5Stack Tab5 is a development board for the ESP32-P4 microprocessor. It features a nice touchscreen display, a speaker, microphones, camera, uSD card, usb-a, usb-c, 3.5mm headphone, lithium-ion battery, and a host of expansion headers.
The espp::M5StackTab5 component provides a singleton hardware abstraction for initializing the touch, display, and audio subsystems.
API Reference
Header File
Classes
-
class M5StackTab5 : public espp::BaseComponent
The M5StackTab5 class provides an interface to the M5Stack Tab5 development board.
The class provides access to the following features:
5” 720p MIPI-DSI Display with GT911 multi-touch
Dual audio codecs (ES8388 + ES7210 AEC)
BMI270 6-axis IMU sensor
SC2356 2MP camera via MIPI-CSI (not yet implemented)
ESP32-C6 wireless module (Wi-Fi 6, Thread, ZigBee)
USB-A Host and USB-C OTG ports
RS-485 industrial interface (not yet implemented)
Grove and M5-Bus expansion headers (not yet implemented)
microSD card slot
NP-F550 removable battery with battery management via INA226
Real-time clock (RX8130CE)
Multiple buttons and interrupts
The class is a singleton and can be accessed using the get() method.
Example
espp::M5StackTab5 &tab5 = espp::M5StackTab5::get(); // tab5.set_log_level(espp::Logger::Verbosity::DEBUG); logger.info("Running on M5Stack Tab5"); // first let's get the internal i2c bus and probe for all devices on the bus logger.info("Probing internal I2C bus..."); auto &i2c = tab5.internal_i2c(); std::vector<uint8_t> found_addresses; for (uint8_t address = 1; address < 128; address++) { if (i2c.probe_device(address)) { found_addresses.push_back(address); } } logger.info("Found devices at addresses: {::#02x}", found_addresses); // Initialize the IO expanders logger.info("Initializing IO expanders..."); if (!tab5.initialize_io_expanders()) { logger.error("Failed to initialize IO expanders!"); return; } logger.info("Initializing lcd..."); // initialize the LCD if (!tab5.initialize_lcd()) { logger.error("Failed to initialize LCD!"); return; } // initialize the display with a pixel buffer (Tab5 is 1280x720 with 2 bytes per pixel) logger.info("Initializing display..."); auto pixel_buffer_size = tab5.display_width() * 10; // tab5.display_height(); if (!tab5.initialize_display(pixel_buffer_size)) { logger.error("Failed to initialize display!"); return; } 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 = tab5.touchpad_convert(touch); auto touchpad_data = tab5.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(tab5); std::lock_guard<std::recursive_mutex> lock(lvgl_mutex); draw_circle(touchpad_data.x, touchpad_data.y, 10); } } }; logger.info("Initializing touch..."); if (!tab5.initialize_touch(touch_callback)) { logger.error("Failed to initialize touch!"); 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.5f; // higher = more accelerometer, lower = more gyro static espp::MadgwickFilter f(beta); using Imu = espp::M5StackTab5::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; }; logger.info("Initializing IMU..."); // initialize the IMU if (!tab5.initialize_imu(kalman_filter_fn)) { logger.error("Failed to initialize IMU!"); return; } // initialize the uSD card using SdCardConfig = espp::M5StackTab5::SdCardConfig; SdCardConfig sdcard_config{}; if (!tab5.initialize_sdcard(sdcard_config)) { logger.warn("Failed to initialize uSD card, there may not be a uSD card inserted!"); } else { uint32_t size_mb = 0; uint32_t free_mb = 0; if (tab5.get_sd_card_info(&size_mb, &free_mb)) { logger.info("uSD card size: {} MB, free space: {} MB", size_mb, free_mb); } else { logger.warn("Failed to get uSD card info"); } } logger.info("Initializing RTC..."); // initialize the RTC if (!tab5.initialize_rtc()) { logger.error("Failed to initialize RTC!"); return; } auto current_time = std::tm{}; if (!tab5.get_rtc_time(current_time)) { logger.error("Failed to get RTC time"); return; } // only set the time if the year is before 2024 if (current_time.tm_year < 124) { // set the RTC time to a known value (2024-01-15 14:30:45) // Set time using std::tm std::tm time = {}; time.tm_year = 124; // 2024 - 1900 time.tm_mon = 0; // January (0-based) time.tm_mday = 15; // 15th time.tm_hour = 14; // 2 PM time.tm_min = 30; time.tm_sec = 45; time.tm_wday = 1; // Monday if (!tab5.set_rtc_time(time)) { logger.error("Failed to set RTC time"); return; } } else { logger.info("RTC time is already set to a valid value {:%Y-%m-%d %H:%M:%S}", current_time); } logger.info("Initializing battery management..."); // initialize battery monitoring if (!tab5.initialize_battery_monitoring()) { logger.error("Failed to initialize battery monitoring!"); return; } // enable charging tab5.set_charging_enabled(true); logger.info("Initializing sound..."); // initialize the sound if (!tab5.initialize_audio()) { logger.error("Failed to initialize sound!"); return; } // Brightness control with button logger.info("Initializing button..."); auto button_callback = [&](const auto &state) { logger.info("Button state: {}", state.active); if (state.active) { // Cycle through brightness levels: 25%, 50%, 75%, 100% static int brightness_level = 0; float brightness_values[] = {0.25f, 0.5f, 0.75f, 1.0f}; brightness_level = (brightness_level + 1) % 4; float new_brightness = brightness_values[brightness_level]; tab5.brightness(new_brightness); logger.info("Set brightness to {:.0f}%", new_brightness * 100); } }; if (!tab5.initialize_button(button_callback)) { logger.warn("Failed to initialize button"); } logger.info("Setting up LVGL UI..."); // set the background color to black lv_obj_t *bg = lv_obj_create(lv_screen_active()); lv_obj_set_size(bg, tab5.display_width(), tab5.display_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!"; 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 for line 0 (blue line, used for kalman filter) 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}, {tab5.display_width(), tab5.display_height()}}; lv_line_set_points(line0, line_points0, 2); lv_obj_add_style(line0, &style_line0, 0); // Create style for line 1 (red line, used for madgwick filter) 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}, {tab5.display_width(), tab5.display_height()}}; lv_line_set_points(line1, line_points1, 2); lv_obj_add_style(line1, &style_line1, 0); 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_t *disp = lv_display_get_default(); lv_disp_set_rotation(disp, rotation); // update the size of the screen lv_obj_set_size(bg, tab5.rotated_display_width(), tab5.rotated_display_height()); // refresh the display }; // 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) { rotate_display(); }, 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 logger.info("Starting LVGL task..."); 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 = 10 * 1024, }}); lv_task.start(); // load the audio and play it once as a test logger.info("Loading audio..."); auto num_bytes_loaded = load_audio(); logger.info("Loaded {} bytes of audio", num_bytes_loaded); // unmute the audio and set the volume to 60% tab5.mute(false); tab5.volume(60.0f); // set the brightness to 75% tab5.brightness(75.0f); // make a task to read out various data such as IMU, battery monitoring, etc. // and print it to screen logger.info("Starting data display task..."); 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 &tab5 = espp::M5StackTab5::get(); static auto imu = tab5.imu(); // Update the Date/Time from the RTC std::tm rtc_time; std::string rtc_text = ""; if (tab5.get_rtc_time(rtc_time)) { rtc_text = fmt::format("\n{:%Y-%m-%d %H:%M:%S}\n", rtc_time); } // Update the battery status auto battery_status = tab5.read_battery_status(); std::string battery_text = fmt::format("\nBattery: {:0.2f} V, {:0.1f} mA, {:0.1f} %, Charging: {}\n", battery_status.voltage_v, battery_status.current_ma, battery_status.charge_percent, battery_status.is_charging ? "Yes" : "No"); 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; // Update the IMU data 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(); // invert the axes gravity_vector.y = -gravity_vector.y; gravity_vector.x = -gravity_vector.x; // 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; } // separator for imu std::string imu_text = "\nIMU Data:\n"; imu_text += fmt::format("Accel: {:02.2f} {:02.2f} {:02.2f}\n", accel.x, accel.y, accel.z); imu_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)); imu_text += fmt::format("Angle: {:03.2f} {:03.2f}\n", espp::rad_to_deg(orientation.roll), espp::rad_to_deg(orientation.pitch)); imu_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 = tab5.rotated_display_width() / 2; int y0 = tab5.rotated_display_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[0].x = x0; line_points0[0].y = y0; 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); // invert the axes vx = -vx; 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[0].x = x0; line_points1[0].y = y0; line_points1[1].x = x1; line_points1[1].y = y1; std::string text = fmt::format("{}\n\n\n\n\n", label_text); text += battery_text; text += rtc_text; text += imu_text; 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 = "Data Display Task", .stack_size_bytes = 6 * 1024, .priority = 10, .core_id = 0, }}); imu_task.start(); // loop forever while (true) { std::this_thread::sleep_for(1s); }
Public Types
-
enum class ExpansionPort
Expansion port configuration.
Values:
-
enumerator GROVE
Grove connector.
-
enumerator M5_BUS
M5-Bus connector.
-
enumerator STAMP
STAMP expansion pads.
-
enumerator GPIO_EXT
GPIO extension header.
-
enumerator GROVE
-
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 Tab5 display.
-
using DisplayDriver = espp::Ili9881
Alias for the display driver used by the Tab5.
-
using TouchpadData = espp::TouchpadData
Alias for the touchpad data used by the Tab5 touchpad.
-
using touch_callback_t = std::function<void(const TouchpadData&)>
Alias for the touch callback when touch events are received.
Public Functions
-
inline I2c &internal_i2c()
Get a reference to the internal I2C bus
Note
The internal I2C bus is used for touchscreen, audio codecs, IMU, RTC, and power monitoring
- 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_lcd()
Initialize the LCD (low level display driver, MIPI-DSI + ST7703)
- Returns:
true if the LCD was successfully initialized, false otherwise
-
bool initialize_display(size_t pixel_buffer_size = 1280 * 720 / 10)
Initialize the LVGL display
- Parameters:
pixel_buffer_size – The size of the pixel buffer
- Returns:
true if the display was successfully initialized, false otherwise
-
bool initialize_touch(const touch_callback_t &callback = nullptr)
Initialize the GT911 multi-touch controller
- Parameters:
callback – The touchpad callback
- 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 = 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
-
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)
-
void set_backlight_enabled(bool enable)
Enable/disable the LCD backlight (routes through IO expander if mapped)
-
std::optional<bool> is_backlight_enabled() const
Query backlight enable state if readable
- Returns:
true if enabled, false if disabled; std::nullopt if unknown
-
size_t rotated_display_width() const
Get the display width in pixels, according to the current orientation
- Returns:
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
- Returns:
The display height in pixels, according to the current orientation
- bool initialize_audio (uint32_t sample_rate=48000, const espp::Task::BaseConfig &task_config={ .name="tab5_audio",.stack_size_bytes=CONFIG_M5STACK_TAB5_AUDIO_TASK_STACK_SIZE,.priority=20,.core_id=1})
Initialize the dual audio system (ES8388 codec + ES7210 AEC)
- Parameters:
sample_rate – The audio sample rate (default 48kHz)
task_config – The task configuration for the audio task
- Returns:
true if the audio system was successfully initialized, false otherwise
-
void enable_audio(bool enable)
Enable or disable the audio system
- Parameters:
enable – True to enable, 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
- 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 start_audio_recording(std::function<void(const uint8_t *data, size_t length)> callback)
Start recording audio
- Parameters:
callback – Function to call with recorded audio data
- Returns:
True if recording started successfully
-
void stop_audio_recording()
Stop recording audio.
-
bool initialize_imu(const Imu::filter_fn &orientation_filter = nullptr)
Initialize the BMI270 6-axis IMU
- Parameters:
orientation_filter – Optional orientation filter function
- Returns:
True if IMU was successfully initialized
-
bool initialize_battery_monitoring()
Initialize battery monitoring (INA226)
- Returns:
True if battery monitoring was successfully initialized
-
BatteryStatus read_battery_status()
Get the latest battery status from the INA226
- Returns:
Battery status structure
-
BatteryStatus get_battery_status() const
Get the most recent cached battery status
Note
This does not read from the INA226, use read_battery_status() to get the latest data
- Returns:
Battery status structure
-
void enable_battery_charging(bool enable)
Enable or disable battery charging
- Parameters:
enable – True to enable charging, false to disable
-
inline std::shared_ptr<BatteryMonitor> battery_monitor() const
Get the Battery Monitor Instance (INA226)
- Returns:
Shared pointer to the battery monitor
-
bool initialize_rtc()
Initialize the RX8130CE real-time clock
- Returns:
True if RTC was successfully initialized
-
bool set_rtc_time(uint64_t unix_timestamp)
Set the RTC time
- Parameters:
unix_timestamp – Unix timestamp to set
- Returns:
True if time was set successfully
-
bool set_rtc_time(const std::tm &time)
Set the RTC time
- Parameters:
time – The time to set
- Returns:
True if time was set successfully
-
bool get_rtc_time(std::tm &time)
Get the RTC time
- Parameters:
time – The time structure to fill
- Returns:
True if time was retrieved successfully
-
uint64_t get_unix_time()
Get the RTC time
- Returns:
Unix timestamp, or 0 if RTC not initialized
-
bool set_rtc_wakeup(uint32_t seconds_from_now)
Enable RTC wake-up interrupt
- Parameters:
seconds_from_now – Seconds from now to wake up
- Returns:
True if wake-up was set successfully
-
bool initialize_button(const button_callback_t &callback = nullptr)
Initialize the button
- Parameters:
callback – The callback function to call when pressed
- Returns:
True if button was successfully initialized
-
bool button_state() const
Get the button state
- Returns:
True if pressed, false otherwise
-
bool initialize_io_expanders()
Initialize the on-board IO expanders at addresses 0x43 and 0x44 Configures required directions and safe default output states.
-
bool lcd_reset(bool assert_reset)
Control the LCD reset (active-low) routed via IO expander (0x43 P4)
- Parameters:
assert_reset=true – drives reset low; false releases reset high.
- Returns:
true on success
-
bool touch_reset(bool assert_reset)
Control the GT911 touch reset (active-low) via IO expander (0x43 P5)
- Parameters:
assert_reset=true – drives reset low; false releases reset high.
- Returns:
true on success
-
bool set_speaker_enabled(bool enable)
Enable/disable the speaker amplifier (NS4150B SPK_EN on 0x43 P1)
- Parameters:
enable – True to enable speaker, false to disable
- Returns:
true on success
-
bool set_charging_enabled(bool enable)
Enable/disable battery charging (IP2326 CHG_EN on 0x44 P7)
- Parameters:
enable – True to enable charging, false to disable
- Returns:
true on success
-
bool get_charging_status()
Read battery charging status (IP2326 CHG_STAT on 0x44 P6) Returns true if charging is indicated asserted.
-
bool set_io_expander_output(uint8_t address, uint8_t bit, bool level)
Generic helpers to control IO expander pins (0x43/0x44) These perform read-modify-write on the output latch.
- Parameters:
address – 7-bit expander I2C address (e.g. 0x43 or 0x44)
bit – Bit index 0..7
level – Desired output level
- Returns:
true on success
-
std::optional<bool> get_io_expander_output(uint8_t address, uint8_t bit)
Read a single output bit from the expander output register
- Parameters:
address – 7-bit expander I2C address (e.g. 0x43 or 0x44)
bit – Bit index 0..7
- Returns:
std::optional<bool> containing the output state, or std::nullopt on error
-
std::optional<bool> get_io_expander_input(uint8_t address, uint8_t bit)
Read a single input bit from the expander input register
- Parameters:
address – 7-bit expander I2C address (e.g. 0x43 or 0x44)
bit – Bit index 0..7
- Returns:
std::optional<bool> containing the input state, or std::nullopt on error
-
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
-
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
Note
The uSD card is only available if it was successfully initialized and the mount point is valid
- Returns:
A pointer to the uSD card
-
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
-
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
See also
See also
- Returns:
The verbosity level of the logger
-
inline void set_log_level(espp::Logger::Verbosity level)
Set the log level for the logger
See also
See also
- 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
See also
See also
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
See also
See also
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
See also
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 M5StackTab5 &get()
Access the singleton instance of the M5StackTab5 class.
- Returns:
Reference to the singleton instance of the M5StackTab5 class
-
static inline constexpr size_t display_width()
Get the display width in pixels
- Returns:
The display width in pixels
-
static inline constexpr size_t display_height()
Get the display height in pixels
- Returns:
The display height in pixels
Public Static Attributes
-
struct BatteryStatus
Battery status structure.
Public Members
-
float voltage_v
Battery voltage in volts.
-
float current_ma
Battery current in milliamps.
-
float power_mw
Battery power in milliwatts.
-
float charge_percent
Estimated charge percentage (0-100)
-
bool is_charging
True if battery is charging.
-
bool is_present
True if battery is present.
-
float voltage_v
-
struct SdCardConfig
Configuration for the uSD card.