From 1a6e545d2ff87979000c5dedd6b311d2b7a98cd3 Mon Sep 17 00:00:00 2001
From: ainyan03 <205502311+ainyan03@users.noreply.github.com>
Date: Sat, 10 Oct 2026 23:23:04 +0000
Subject: [PATCH 1/3] Pass the fallback board to display detection before init
Call Display.setDetectConfig() with cfg.fallback_board before Display.init(), so a provisional detection result that differs from it is not constructed and the fallback is selected instead.
---
src/M5Unified.hpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/M5Unified.hpp b/src/M5Unified.hpp
index 59410e9..b711b90 100644
--- a/src/M5Unified.hpp
+++ b/src/M5Unified.hpp
@@ -357,6 +357,9 @@ namespace m5
}
#endif
+ m5gfx::detect_config_t detect_config;
+ detect_config.fallback_board = cfg.fallback_board;
+ Display.setDetectConfig(detect_config);
auto brightness = Display.getBrightness();
Display.setBrightness(0);
bool res = false;
From 4ebb3d38efaa3c98755d3f0330edc4d424546ca9 Mon Sep 17 00:00:00 2001
From: ainyan03 <205502311+ainyan03@users.noreply.github.com>
Date: Sat, 10 Oct 2026 23:23:04 +0000
Subject: [PATCH 2/3] Use a positive BOARD_ID as a fixed board identity
Pass a positive BOARD_ID to M5GFX as the fixed board. M5Unified selects the accepted fixed board first and keeps it when display startup fails; an explicit fallback for a different board is ignored with a warning. On ESP32-S3, the early GPIO46 hold is applied for a fixed board only when that board uses GPIO46 for power hold.
---
README.md | 16 ++++++++++++++++
src/M5Unified.hpp | 34 +++++++++++++++++++++++-----------
src/M5Unified.inl | 12 +++++++++---
3 files changed, 48 insertions(+), 14 deletions(-)
diff --git a/README.md b/README.md
index 3d1b1c0..68e66a8 100644
--- a/README.md
+++ b/README.md
@@ -452,3 +452,19 @@ has another name, or `M5UNIFIED_ARDUINO_COMPONENT=OFF` to disable this.
|Current
Voltage
Monitor| --- | --- | --- | --- | --- | --- | --- | --- |`INA3221`
40h/41h
(opt)|Current
Voltage
Monitor|
+
+
+### Fixed-board firmware
+
+Defining a positive `BOARD_ID` when building an application using M5Unified fixes
+its device identity to that `board_t` value. An undefined, explicitly empty, or zero value
+keeps automatic detection. Fixed startup reads only variants needed by the
+selected board and does not use NVS or probe other board families.
+An accepted fixed identity takes precedence over `config_t::fallback_board`,
+including when display startup fails. Call `M5.begin()` before initializing
+`M5.Display` for this configuration to take effect.
+
+A positive `BOARD_ID` unsupported by the chip also stops automatic detection;
+it does not fall back to probing another board. `BOARD_ID` has no effect if
+`M5.Display.init()` runs before `M5.begin()`. A valueless compiler option
+`-DBOARD_ID` defines the value as 1 (M5Stack), rather than an empty value.
diff --git a/src/M5Unified.hpp b/src/M5Unified.hpp
index b711b90..14ca00a 100644
--- a/src/M5Unified.hpp
+++ b/src/M5Unified.hpp
@@ -348,7 +348,13 @@ namespace m5
// Skipped when the application already ran Display.init(): GPIO46 may then carry the display
// bus (StopWatch QSPI io2), which this output would overwrite. The hold is applied below,
// once the pin map is known, only on boards that use GPIO46 as their power hold.
+#if defined (BOARD_ID) && ((BOARD_ID + 0) > 0)
+ // A fixed board can use GPIO46 for its display rather than power hold.
+ const bool gpio46_hold = Display.getBoard() == board_t::board_unknown
+ && _get_power_hold_pin(static_cast(BOARD_ID)) == GPIO_NUM_46;
+#else
const bool gpio46_hold = (Display.getBoard() == m5gfx::board_t::board_unknown);
+#endif
m5gfx::gpio::pin_backup_t gpio46_backup(GPIO_NUM_46);
if (gpio46_hold)
{
@@ -359,6 +365,10 @@ namespace m5
m5gfx::detect_config_t detect_config;
detect_config.fallback_board = cfg.fallback_board;
+ // A positive BOARD_ID fixes the device to its board_t value in M5Unified.
+#if defined (BOARD_ID) && ((BOARD_ID + 0) > 0)
+ detect_config.fixed_board = static_cast(BOARD_ID);
+#endif
Display.setDetectConfig(detect_config);
auto brightness = Display.getBrightness();
Display.setBrightness(0);
@@ -368,20 +378,21 @@ namespace m5
} else {
res = Display.init_without_reset(false);
}
- auto board = Display.getBoard();
- // printf("auto detect board:%d\n",board);
+ const auto fixed_board = Display.getFixedBoard();
+ auto board = fixed_board != board_t::board_unknown ? fixed_board : Display.getBoard();
bool board_detected = (board != board_t::board_unknown);
- if (!board_detected)
+ if (fixed_board != board_t::board_unknown)
+ {
+ // Accepted firmware identity survives display startup failure.
+ if (cfg.fallback_board != board_t::board_unknown && cfg.fallback_board != fixed_board)
+ {
+ Log(ESP_LOG_WARN, "fallback board:%u ignored for fixed board:%u",
+ static_cast(cfg.fallback_board), static_cast(fixed_board));
+ }
+ }
+ else if (!board_detected)
{
board = cfg.fallback_board;
- // UIFlow selects board-specific firmware with BOARD_ID and M5GFX_BOARD.
-#if defined (CONFIG_IDF_TARGET_ESP32P4) && defined (BOARD_ID)
-#if BOARD_ID == 31
- if (board == board_t::board_unknown) { board = board_t::board_M5CoreP4X; }
-#elif BOARD_ID == 35
- if (board == board_t::board_unknown) { board = board_t::board_M5Tab5X; }
-#endif
-#endif
if (board == board_t::board_unknown) { board = Display.getBoardCandidate(); }
if (board == board_t::board_unknown) { board = _default_fallback_board(); }
}
@@ -801,6 +812,7 @@ namespace m5
bool _probe_i2c_addr(uint8_t sda, uint8_t scl, uint8_t addr);
static void _setup_pinmap(board_t);
+ static int8_t _get_power_hold_pin(board_t);
static bool _speaker_enabled_cb_core2(void* args, bool enabled);
static bool _speaker_enabled_cb_cores3(void* args, bool enabled);
static bool _speaker_enabled_cb_sticks3(void* args, bool enabled);
diff --git a/src/M5Unified.inl b/src/M5Unified.inl
index 0de5e30..98300c4 100644
--- a/src/M5Unified.inl
+++ b/src/M5Unified.inl
@@ -86,6 +86,8 @@ namespace m5
int8_t M5Unified::_get_pin_table[pin_name_max];
#if defined (M5UNIFIED_PC_BUILD)
+ int8_t M5Unified::_get_power_hold_pin(board_t) { return -1; }
+
void M5Unified::_setup_pinmap(board_t)
{
std::fill(_get_pin_table, _get_pin_table + pin_name_max, 255);
@@ -475,6 +477,13 @@ static constexpr const uint8_t _pin_table_mbus[][31] = {
{ board_t::board_unknown , 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
};
+ int8_t M5Unified::_get_power_hold_pin(board_t id)
+ {
+ for (const auto& row : _pin_table_other1)
+ { if (row[0] == id) { return static_cast(row[1]); } }
+ return -1;
+ }
+
void M5Unified::_setup_pinmap(board_t id)
{
if (id == board_t::board_M5Tab5X) {
@@ -1654,9 +1663,6 @@ static constexpr const uint8_t _pin_table_mbus[][31] = {
if (pkg == EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ6) { return board_t::board_M5TimerCam; }
return board_t::board_M5AtomLite;
#elif defined (CONFIG_IDF_TARGET_ESP32S3)
-#if defined (BOARD_ID) && BOARD_ID == 147
- return board_t::board_M5DualKey;
-#endif
// ESP32-S3 package 0 is QFN56 and package 1 is LGA56.
if (m5gfx::get_pkg_ver() == 1) { return board_t::board_M5StampS3Mini; }
return board_t::board_M5StampS3;
From ecf8ac1d1c6a709b1985d641aa0a6a4fc0a3eb76 Mon Sep 17 00:00:00 2001
From: ainyan03 <205502311+ainyan03@users.noreply.github.com>
Date: Sat, 10 Oct 2026 23:23:04 +0000
Subject: [PATCH 3/3] Leave rejected or unsupported board defaults unknown
The package default is used only when nothing was detected. Restrict it to boards no detector can reject: StampPico (PICO-D4), AtomPsram (PICO-V3), StampS3/StampS3Mini, StampC3U and StampP4/StampP4X stay. ESP32 D0WDQ6 (all members have detectors) and other ESP32 packages without M5 boards return unknown instead of TimerCam/AtomLite. ESP32-C6, ESP32-H2 and ESP32-C5 return unknown instead of NanoC6/NanoH2/StampC5; StampC6 and StampC5 arrive as display candidates.
Unknown boards get no board-specific power hold, LEDs or buttons; on ESP32 the generic internal I2C (G21/G22) is still started. A TimerCam whose RTC does not answer no longer holds G33; use the TimerCam board definition, cfg.fallback_board or BOARD_ID. Arduino board-definition selections are unchanged.
---
src/M5Unified.inl | 21 +++++++--------------
1 file changed, 7 insertions(+), 14 deletions(-)
diff --git a/src/M5Unified.inl b/src/M5Unified.inl
index 98300c4..0e8cdf3 100644
--- a/src/M5Unified.inl
+++ b/src/M5Unified.inl
@@ -1659,27 +1659,20 @@ static constexpr const uint8_t _pin_table_mbus[][31] = {
const auto pkg = m5gfx::get_pkg_ver();
if (pkg == 6) { return board_t::board_M5AtomPsram; } // PICO-V3
if (pkg == 5) { return board_t::board_M5StampPico; } // PICO-D4
- // The legacy detector defaulted D0WDQ6 to TimerCam after other probes failed.
- if (pkg == EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ6) { return board_t::board_M5TimerCam; }
- return board_t::board_M5AtomLite;
+ // StampPico and AtomPsram have no positive detector and remain defaults.
+ // All D0WDQ6 members have detectors; other packages have no M5 boards.
+ // Do not assume a rejected member. Unknown retains the generic I2C map.
+ return board_t::board_unknown;
#elif defined (CONFIG_IDF_TARGET_ESP32S3)
// ESP32-S3 package 0 is QFN56 and package 1 is LGA56.
if (m5gfx::get_pkg_ver() == 1) { return board_t::board_M5StampS3Mini; }
return board_t::board_M5StampS3;
#elif defined (CONFIG_IDF_TARGET_ESP32C3)
return board_t::board_M5StampC3U;
-#elif defined (CONFIG_IDF_TARGET_ESP32C6)
- // Preserve legacy package/flash defaults when no detector confirms a board.
- if (m5gfx::get_pkg_ver() == 1)
- {
- return REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_4_REG, EFUSE_FLASH_CAP) == 2
- ? board_t::board_M5StampC6 : board_t::board_M5NanoC6;
- }
+#elif defined (CONFIG_IDF_TARGET_ESP32C6) || defined (CONFIG_IDF_TARGET_ESP32H2) || defined (CONFIG_IDF_TARGET_ESP32C5)
+ // Package-only Stamp suggestions arrive as detector candidates. Do not
+ // assume a Nano or Stamp after its positive detector rejects the board.
return board_t::board_unknown;
-#elif defined (CONFIG_IDF_TARGET_ESP32H2)
- return board_t::board_M5NanoH2;
-#elif defined (CONFIG_IDF_TARGET_ESP32C5)
- return board_t::board_M5StampC5;
#elif defined (CONFIG_IDF_TARGET_ESP32P4)
// Unidentified P4 modules have no display or board-specific power contract.
esp_chip_info_t info;