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 59410e9..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)
{
@@ -357,6 +363,13 @@ namespace m5
}
#endif
+ 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);
bool res = false;
@@ -365,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(); }
}
@@ -798,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..0e8cdf3 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) {
@@ -1650,30 +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)
-#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;
#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;