M5Stack Tab5

I noticed there’s a new device in the M5Stack series: M5Stack Tab5. It looks like a great solution for a wall-mounted control panel for HomeAssistant. Does anyone already have experience with it? Looking at the specs, would it be hard to make it work with ESPHome?

7 Likes

It would be a great device! Any news?

I think the most difficult thing is to get the two esp32s to talk to each other so that WIFI works.

1 Like

They are working on it. Not everything is working yet as far as supported platforms but they have a board for espressif’s evaluation board. It talks to the C6 via SDIO.

It has the potential to be a tablet and voice assistant with LVGL. Someone got Quake running on a P4 demo board so it’s a huge upgrade.


Is there a beta version yet of ESPHome that will work on the M5Stack Tab5 ? I understand the challenge is creating code for both ESP32-P4 and ESP32-C6 (WiFi/BT) on the board.

Really appreciate you all working on this.

they have an example configuration on their devices page or on github. I compiled the example config they have on the page and it compiled and seems to be working!

WIP, will post the GitHub link once I clean up and tweak some things. The P4 is WAY faster then the S3, not sure if it’s the 32MB of PSRAM but super responsive with animation while being a pretty decent voice assistant. Not full duplex, only half duplex so stop word doesn’t work. I added a stop button in the screen and I can either tap the Darth Vader icon to start the voice assistant or hold it and let go to force it to stop listening (helpful with TV or music). Also had weird issues get the wake sound to work. The I2S bus keeps locking up so I gave up on that and moved on to LVGL

1 Like

hello, i have some problems with i2c and touchscreen, can someone help me please? can you share your yaml please?

Here is my YAML and 2 images used, most are mdi icons. I’ve added some gradient colors and animations also but still learning. Also, I started out with the documented YAML on the ESPHome devices website which will get you up and running with the sample card LVGL.

thanks a lot, thanks for your time, i think i have a i2c problem
esphome and esphome-dev does not working for me

[C][component:173]: Setup template.number took 1ms
[18:59:37.774][D][number:033]: 'screen brightness': Sending state 8.000000
[18:59:37.774][D][light:089]: 'Display Backlight' Setting:
[18:59:37.775][D][light:095]:   Color mode: 
[18:59:37.775][D][light:102]:   State: ON
[18:59:37.776][D][light:077]:   Brightness: 3%
[18:59:37.776][D][light:140]:   Transition length: 0.2s
[18:59:37.777][C][component:173]: Setup template.number took 3ms
[18:59:37.778][C][component:173]: Setup ledc.output took 0ms
[18:59:37.778][D][light:089]: 'Display Backlight' Setting:
[18:59:37.779][D][light:077]:   Brightness: 50%
[18:59:37.780][C][component:173]: Setup light took 1ms
[18:59:37.780][C][component:173]: Setup psram took 0ms
[18:59:37.781][C][component:173]: Setup ina226.sensor took 1ms
[18:59:37.784][C][component:173]: Setup touchscreen took 3ms
[18:59:37.835][D][esp-idf:000]: E (1907) i2c.master: I2C hardware NACK detected
[18:59:37.836][D][esp-idf:000]: E (1908) i2c.master: I2C transaction unexpected nack detected
[18:59:37.837][D][esp-idf:000]: E (1909) i2c.master: s_i2c_synchronous_transaction(945): I2C transaction failed
[18:59:37.838][D][esp-idf:000]: E (1909) i2c.master: i2c_master_execute_defined_operations(1366): I2C transaction failed
[18:59:37.838][D][esp-idf:000]: E (1910) i2c.master: I2C hardware NACK detected
[18:59:37.839][D][esp-idf:000]: E (1911) i2c.master: I2C transaction unexpected nack detected
[18:59:37.840][D][esp-idf:000]: E (1912) i2c.master: s_i2c_synchronous_transaction(945): I2C transaction failed
[18:59:37.841][D][esp-idf:000]: E (1913) i2c.master: i2c_master_execute_defined_operations(1366): I2C transaction failed
[18:59:37.842][E][component:314]: touchscreen set Error flag: Communication failed
[18:59:37.842][E][component:211]: touchscreen was marked as failed
[18:59:37.914][D][sensor:131]: 'Battery Percentage': Sending state nan % with 1 decimals of accuracy
[18:59:38.782][D][sensor:131]: 'Battery Voltage': Sending state 8.20375 V with 2 decimals of accuracy
[18:59:38.783][D][sensor:131]: 'Battery Current': Sending state 0.00050 A with 3 decimals of accuracy
[19:00:37.912][D][sensor:131]: 'Battery Percentage': Sending state 98.82288 % with 1 decimals of accuracy
[19:00:38.780][D][sensor:131]: 'Battery Voltage': Sending state 8.20375 V with 2 decimals of accuracy
[19:00:38.782][D][sensor:131]: 'Battery Current': Sending state 0.00050 A with 3 decimals of accuracy
1 Like

If the default config below doesn’t work then it really seems like an i2c issue, probably hardware if the below doesn’t work because it’s posted on esphomes site. Might be worth trying to “clean build files” and compiling from scratch if you haven’t already. Sometimes things get messed up in the build directory that can produce some interesting errors during compile.

I am having this same issue. It appears that the display driver was changed as of 10/14 per the note in the documentation.

Starting from October 14, 2025, the Tab5’s original independent display driver ILI9881C and touch driver GT911 will be replaced by the integrated display‑touch driver ST7123. Some early firmware builds may not run properly. The latest versions of M5Unified and M5GFX have already been adapted for compatibility with this new screen driver, and older programs can be recompiled using the latest M5Unified and M5GFX to achieve proper compatibility.

Saw that, there is a PR for the hardware changes by m5stack. See the post below for the PR YAML that needs to be added for it work due to the hardware changes. Just need to point to 2 external components. YAML below although spacing isn’t correct, will edit later on desktop because mobile is being a headache.

https://www.reddit.com/r/homeassistant/s/oZQyjzdnnJ

external_components:

source:

github://pr#12075

components:

refresh: 1h

source:

github://pr#12074

[st7123]

components: [mipi_dsi]

refresh: 1h

See above post, fixed via PR and external component. Confirmed working by 2 people on Reddit.

thanks for your help, i can confirm via PR the i2c is compiled now.

but now gives the mipi_dsi problems? sorry for my english and i am no programmer :wink:

Compiling .pioenvs/zentraleinheit02/src/esphome/components/mipi_dsi/mipi_dsi.cpp.o
src/esphome/components/mipi_dsi/mipi_dsi.cpp: In member function 'void esphome::mipi_dsi::MIPI_DSI::smark_failed(const esphome::LogString*, esp_err_t)':
src/esphome/components/mipi_dsi/mipi_dsi.cpp:17:20: error: no matching function for call to 'esphome::mipi_dsi::MIPI_DSI::mark_failed(const esphome::LogString*&)'
   17 |   this->mark_failed(message);
      |   ~~~~~~~~~~~~~~~~~^~~~~~~~~
In file included from src/esphome/components/mipi_dsi/mipi_dsi.h:8,
                 from src/esphome/components/mipi_dsi/mipi_dsi.cpp:3:
src/esphome/core/component.h:158:16: note: candidate: 'virtual void esphome::Component::mark_failed()'
  158 |   virtual void mark_failed();
      |                ^~~~~~~~~~~
src/esphome/core/component.h:158:16: note:   candidate expects 0 arguments, 1 provided
src/esphome/core/component.h:160:8: note: candidate: 'void esphome::Component::mark_failed(const char*)'
  160 |   void mark_failed(const char *message) {
      |        ^~~~~~~~~~~
src/esphome/core/component.h:160:32: note:   no known conversion for argument 1 from 'const esphome::LogString*' to 'const char*'
  160 |   void mark_failed(const char *message) {
      |                    ~~~~~~~~~~~~^~~~~~~
src/esphome/components/mipi_dsi/mipi_dsi.cpp: In member function 'virtual void esphome::mipi_dsi::MIPI_DSI::setup()':
src/esphome/components/mipi_dsi/mipi_dsi.cpp:102:24: error: no matching function for call to 'esphome::mipi_dsi::MIPI_DSI::mark_failed(const esphome::LogString*)'
  102 |       this->mark_failed(LOG_STR("Malformed init sequence"));
      |       ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/esphome/core/component.h:158:16: note: candidate: 'virtual void esphome::Component::mark_failed()'
  158 |   virtual void mark_failed();

Which ESPHOME version are you using? regular or dev?

Have you tried cleaning build files under the 3 dot options for the device in ESPHome builder? Things can get messed up and throw errors on occasion because of possible files that exist. ESPHome tries to compile only what is needed for faster minor updates. If that doesn’t work post your config either on here or GitHub, mainly the top two off the ESPHome device page for the M5Tab with the updated changes per the external components PR above.

thank you again, yes i have always clean the build files.
i have config a new device, with this yaml

esphome:
  name: zentraleinheit01
  friendly_name: zentraleinheit01

esp32:
  variant: esp32p4
  flash_size: 16MB
  framework:
    type: esp-idf
    advanced:
      enable_idf_experimental_features: true

#external_components:
#  - source: github://pr#12075
#    components: [st7123]
#    refresh: 1h
#  - source: github://pr#12074
#    components: [mipi_dsi]
#    refresh: 1h

# Enable logging
logger:
  hardware_uart: USB_SERIAL_JTAG

# Enable Home Assistant API
api:
  encryption:
    key: ""

ota:
  - platform: esphome
    password: "a8d10c6a7a5288df734cd2d18cc32f7c"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  manual_ip:
    static_ip: 10.0.0.70
    gateway: 0.0.0.0
    subnet: 255.255.255.0

network:
  enable_ipv6: false    

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  #ap:
  #  ssid: "Zentraleinheit01"
  #  password: "HowBkg7ogQom"

esp32_hosted:
  variant: esp32c6
  active_high: true
  clk_pin: GPIO12
  cmd_pin: GPIO13
  d0_pin: GPIO11
  d1_pin: GPIO10
  d2_pin: GPIO9
  d3_pin: GPIO8
  reset_pin: GPIO15
  slot: 1

psram:
  mode: hex
  speed: 200MHz

i2c:
  - id: bsp_bus
    sda: GPIO31
    scl: GPIO32
    frequency: 400kHz

pi4ioe5v6408:
  - id: pi4ioe1
    address: 0x43
    # 0: O - wifi_antenna_int_ext
    # 1: O - speaker_enable
    # 2: O - external_5v_power
    # 3: NC
    # 4: O - lcd reset
    # 5: O - touch panel reset
    # 6: O - camera reset
    # 7: I - headphone detect
  - id: pi4ioe2
    address: 0x44
    # 0: O - wifi_power
    # 1: NC
    # 2: NC
    # 3: O - usb_5v_power
    # 4: O - poweroff pulse
    # 5: O - quick charge enable (inverted)
    # 6: I - charging status
    # 7: O - charge enable

switch:
  - platform: gpio
    id: wifi_power
    name: "WiFi Power"
    pin:
      pi4ioe5v6408: pi4ioe2
      number: 0
    restore_mode: ALWAYS_ON
  - platform: gpio
    id: usb_5v_power
    name: "USB Power"
    pin:
      pi4ioe5v6408: pi4ioe2
      number: 3
  - platform: gpio
    id: quick_charge
    name: "Quick Charge"
    pin:
      pi4ioe5v6408: pi4ioe2
      number: 5
      inverted: true
  - platform: gpio
    id: charge_enable
    name: "Charge Enable"
    pin:
      pi4ioe5v6408: pi4ioe2
      number: 7

  - platform: gpio
    id: wifi_antenna_int_ext
    pin:
      pi4ioe5v6408: pi4ioe1
      number: 0
  - platform: gpio
    id: speaker_enable
    name: "Speaker Enable"
    pin:
      pi4ioe5v6408: pi4ioe1
      number: 1
    restore_mode: ALWAYS_ON
  - platform: gpio
    id: external_5v_power
    name: "External 5V Power"
    pin:
      pi4ioe5v6408: pi4ioe1
      number: 2

binary_sensor:
  - platform: gpio
    id: charging
    name: "Charging Status"
    pin:
      pi4ioe5v6408: pi4ioe2
      number: 6
      mode: INPUT_PULLDOWN

  - platform: gpio
    id: headphone_detect
    name: "Headphone Detect"
    pin:
      pi4ioe5v6408: pi4ioe1
      number: 7

select:
  - platform: template
    id: wifi_antenna_select
    name: "WiFi Antenna"
    options:
      - "Internal"
      - "External"
    optimistic: true
    on_value:
      - if:
          condition:
            lambda: return i == 0;
          then:
            - switch.turn_off: wifi_antenna_int_ext
          else:
            - switch.turn_on: wifi_antenna_int_ext

sensor:
  - platform: ina226
    address: 0x41
    adc_averaging: 16
    max_current: 8.192A
    shunt_resistance: 0.005ohm
    bus_voltage:
      id: battery_voltage
      name: "Battery Voltage"
    current:
      id: battery_current
      name: "Battery Current"
      # Positive means discharging
      # Negative means charging

    # Tab5 built-in battery discharges from full (8.23 V) to shutdown threshold (6.0 V)
  - platform: template
    name: "Battery Percentage"
    lambda: |-
      float voltage = id(battery_voltage).state;
      // Adjust these values based on your battery's actual min/max voltage
      float min_voltage = 6.0;  // Discharged voltage
      float max_voltage = 8.23;  // Fully charged voltage
      float percentage = (voltage - min_voltage) / (max_voltage - min_voltage) * 100.0;
      if (percentage > 100.0) return 100.0;
      if (percentage < 0.0) return 0.0;
      return percentage;
    update_interval: 60s
    unit_of_measurement: "%"
    accuracy_decimals: 1

touchscreen:
  - platform: gt911
    interrupt_pin: GPIO23
    update_interval: never
    reset_pin:
      pi4ioe5v6408: pi4ioe1
      number: 5
    calibration:
      x_min: 0
      x_max: 720
      y_min: 0
      y_max: 1280
    id: touch

esp_ldo:
  - voltage: 2.5V
    channel: 3

display:
  - platform: mipi_dsi
    dimensions:
      height: 1280
      width: 720
    model: M5Stack-Tab5
    reset_pin:
      pi4ioe5v6408: pi4ioe1
      number: 4
    show_test_card: true

output:
  - platform: ledc
    pin: GPIO22
    id: backlight_pwm
    frequency: 1000Hz

light:
  - platform: monochromatic
    output: backlight_pwm
    name: "Display Backlight"
    id: backlight
    restore_mode: RESTORE_DEFAULT_ON
    default_transition_length: 250ms

lvgl:
  byte_order: little_endian

captive_portal:

this is including the two external components

src/esphome/components/mipi_dsi/mipi_dsi.cpp: In member function 'bool esphome::mipi_dsi::MIPI_DSI::check_buffer_()':
src/esphome/components/mipi_dsi/mipi_dsi.cpp:225:22: error: no matching function for call to 'esphome::mipi_dsi::MIPI_DSI::mark_failed(const esphome::LogString*)'
  225 |     this->mark_failed(LOG_STR("Could not allocate buffer for display!"));
      |     ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/esphome/core/component.h:158:16: note: candidate: 'virtual void esphome::Component::mark_failed()'
  158 |   virtual void mark_failed();
      |                ^~~~~~~~~~~
src/esphome/core/component.h:158:16: note:   candidate expects 0 arguments, 1 provided
src/esphome/core/component.h:160:8: note: candidate: 'void esphome::Component::mark_failed(const char*)'
  160 |   void mark_failed(const char *message) {
      |        ^~~~~~~~~~~
src/esphome/core/component.h:160:32: note:   no known conversion for argument 1 from 'const esphome::LogString*' to 'const char*'
  160 |   void mark_failed(const char *message) {
      |                    ~~~~~~~~~~~~^~~~~~~
*** [.pioenvs/zentraleinheit01/src/esphome/components/mipi_dsi/mipi_dsi.cpp.o] Error 1
========================= [FAILED] Took 146.13 seconds =========================

and this is without external components

[05:54:20.145][C][component:173]: Setup light took 3ms
[05:54:20.145][C][component:173]: Setup psram took 0ms
[05:54:20.148][C][component:173]: Setup ina226.sensor took 2ms
[05:54:20.150][C][component:173]: Setup touchscreen took 2ms
[05:54:20.206][D][esp-idf:000]: E (221) i2c.master: I2C hardware NACK detected
[05:54:20.207][D][esp-idf:000]: E (222) i2c.master: I2C transaction unexpected nack detected
[05:54:20.208][D][esp-idf:000]: E (222) i2c.master: s_i2c_synchronous_transaction(945): I2C transaction failed
[05:54:20.209][D][esp-idf:000]: E (223) i2c.master: i2c_master_execute_defined_operations(1401): I2C transaction failed
[05:54:20.209][D][esp-idf:000]: E (224) i2c.master: I2C hardware NACK detected
[05:54:20.210][D][esp-idf:000]: E (225) i2c.master: I2C transaction unexpected nack detected
[05:54:20.211][D][esp-idf:000]: E (225) i2c.master: s_i2c_synchronous_transaction(945): I2C transaction failed
[05:54:20.212][D][esp-idf:000]: E (226) i2c.master: i2c_master_execute_defined_operations(1401): I2C transaction failed

Ah, looking at the PR they left out something on the Reddit thead. Change model under display to “model: M5STACK-TAB5-V2” per the example in the link below. The display driver wasn’t actually changed, only the touch chip so display model needed a new name

yes i have found also now the two models, but with

model: M5STACK-TAB5-V2

i might have a hardware defect? i dont know :laughing:

|-- lvgl @ 8.4.0
Compiling .pioenvs/zentraleinheit01/src/esphome/components/mipi_dsi/mipi_dsi.cpp.o
src/esphome/components/mipi_dsi/mipi_dsi.cpp: In member function 'void esphome::mipi_dsi::MIPI_DSI::smark_failed(const esphome::LogString*, esp_err_t)':
src/esphome/components/mipi_dsi/mipi_dsi.cpp:17:20: error: no matching function for call to 'esphome::mipi_dsi::MIPI_DSI::mark_failed(const esphome::LogString*&)'
   17 |   this->mark_failed(message);
      |   ~~~~~~~~~~~~~~~~~^~~~~~~~~
In file included from src/esphome/components/mipi_dsi/mipi_dsi.h:8,
                 from src/esphome/components/mipi_dsi/mipi_dsi.cpp:3:
src/esphome/core/component.h:158:16: note: candidate: 'virtual void esphome::Component::mark_failed()'
  158 |   virtual void mark_failed();
      |                ^~~~~~~~~~~
src/esphome/core/component.h:158:16: note:   candidate expects 0 arguments, 1 provided
src/esphome/core/component.h:160:8: note: candidate: 'void esphome::Component::mark_failed(const char*)'
  160 |   void mark_failed(const char *message) {
      |        ^~~~~~~~~~~
src/esphome/core/component.h:160:32: note:   no known conversion for argument 1 from 'const esphome::LogString*' to 'const char*'
  160 |   void mark_failed(const char *message) {
      |                    ~~~~~~~~~~~~^~~~~~~
src/esphome/components/mipi_dsi/mipi_dsi.cpp: In member function 'virtual void esphome::mipi_dsi::MIPI_DSI::setup()':
src/esphome/components/mipi_dsi/mipi_dsi.cpp:102:24: error: no matching function for call to 'esphome::mipi_dsi::MIPI_DSI::mark_failed(const esphome::LogString*)'
  102 |       this->mark_failed(LOG_STR("Malformed init sequence"));
      |       ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/esphome/core/component.h:158:16: note: candidate: 'virtual void esphome::Component::mark_failed()'
  158 |   virtual void mark_failed();
      |                ^~~~~~~~~~~
src/esphome/core/component.h:158:16: note:   candidate expects 0 arguments, 1 provided
src/esphome/core/component.h:160:8: note: candidate: 'void esphome::Component::mark_failed(const char*)'
  160 |   void mark_failed(const char *message) {
      |        ^~~~~~~~~~~
src/esphome/core/component.h:160:32: note:   no known conversion for argument 1 from 'const esphome::LogString*' to 'const char*'
  160 |   void mark_failed(const char *message) {
      |                    ~~~~~~~~~~~~^~~~~~~
src/esphome/components/mipi_dsi/mipi_dsi.cpp:113:26: error: no matching function for call to 'esphome::mipi_dsi::MIPI_DSI::mark_failed(const esphome::LogString*)'
  113 |         this->mark_failed(LOG_STR("Malformed init sequence"));
      |         ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/esphome/core/component.h:158:16: note: candidate: 'virtual void esphome::Component::mark_failed()'
  158 |   virtual void mark_failed();
      |                ^~~~~~~~~~~
src/esphome/core/component.h:158:16: note:   candidate expects 0 arguments, 1 provided
src/esphome/core/component.h:160:8: note: candidate: 'void esphome::Component::mark_failed(const char*)'
  160 |   void mark_failed(const char *message) {
      |        ^~~~~~~~~~~
src/esphome/core/component.h:160:32: note:   no known conversion for argument 1 from 'const esphome::LogString*' to 'const char*'
  160 |   void mark_failed(const char *message) {
      |                    ~~~~~~~~~~~~^~~~~~~
src/esphome/components/mipi_dsi/mipi_dsi.cpp: In member function 'bool esphome::mipi_dsi::MIPI_DSI::check_buffer_()':
src/esphome/components/mipi_dsi/mipi_dsi.cpp:225:22: error: no matching function for call to 'esphome::mipi_dsi::MIPI_DSI::mark_failed(const esphome::LogString*)'
  225 |     this->mark_failed(LOG_STR("Could not allocate buffer for display!"));
      |     ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/esphome/core/component.h:158:16: note: candidate: 'virtual void esphome::Component::mark_failed()'
  158 |   virtual void mark_failed();
      |                ^~~~~~~~~~~
src/esphome/core/component.h:158:16: note:   candidate expects 0 arguments, 1 provided
src/esphome/core/component.h:160:8: note: candidate: 'void esphome::Component::mark_failed(const char*)'
  160 |   void mark_failed(const char *message) {
      |        ^~~~~~~~~~~
src/esphome/core/component.h:160:32: note:   no known conversion for argument 1 from 'const esphome::LogString*' to 'const char*'
  160 |   void mark_failed(const char *message) {
      |                    ~~~~~~~~~~~~^~~~~~~
*** [.pioenvs/zentraleinheit01/src/esphome/components/mipi_dsi/mipi_dsi.cpp.o] Error 1
========================== [FAILED] Took 9.49 seconds ==========================

In the YAML you posted above the external components are commented out. Are they still commented out? If so remove the comments, they need to be in there.

yes, sorry i commented out also, it does not work for me :grimacing: :joy: