Skip to content

Board detection: confirm boards only from positive evidence - #345

Merged
lovyan03 merged 10 commits into
m5stack:developfrom
ainyan03:detect_positive_evidence
Oct 10, 2026
Merged

lovyan03 merged 10 commits into
m5stack:developfrom
ainyan03:detect_positive_evidence

Conversation

@ainyan03

@ainyan03 ainyan03 commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Reworks the embedded board detection so that a board is confirmed (and saved to NVS) only from evidence that belongs to that board itself: an IC identity, its own touch controller, or the pull state of an internal line. Elimination ("not A, so B"), package facts and hints no longer confirm a board.

  • Detection results have three states: confirmed, candidate, unknown. A candidate is either provisional (the family is positively identified but the member is not; the display is built) or weak (a suggestion only; no display is built). getBoard() returns the adopted board, getBoardCandidate() the candidate.
  • NVS is written only when a confirmed board was adopted and the stored value differs. Unknown and candidate results never write (previously an unknown result wrote 0 and erased the hint).
  • The NVS / build hint is kept for every attempt and only affects probing order and provisional member selection. The final attempt re-measures the same gates instead of relaxing them.
  • M5GFX::setDetectConfig() / detect_config_t: the caller's fallback board is passed before init(). When a provisional member differs from it, no display is built and the caller's choice wins.
  • detect_config_t::fixed_board / getFixedBoard(): a fixed firmware identity (used by M5Unified for a positive BOARD_ID) starts that board directly, without running other families or touching NVS. Construction variants (panel, PMIC) are still observed. A BOARD_ID for another chip is rejected with a warning.
  • Families no longer run a representative's power sequence before the member is known, except where documented in the code:
    • PM1 family (StopWatch / PaperMono): members are identified from touch ACKs and G12/G13 pulls before power. The second power sequence is removed. 0x50 is no longer treated as a StopWatch rejection.
    • AXP family (Core2 / Tough / Station): Station is separated by G12/G32/G33 pulls before any Core2 power write. A family whose PMIC ID was read stops later families.
    • CoreS3 family (CoreS3 / CoreS3 SE / StackChan) and Cardputer family: members are chosen from independent ICs and released pull measurements. A failed read stays provisional instead of confirming another member.
    • Atom family (PICO-D4): requires G18 floating and a strong pull-down on G12 (IR transmitter) before touch-ratio member selection.
    • Stack family: the final-attempt bypass that drove pins without a signature is removed. The SD line pulls are required (a held line no longer admits Stack reset and G33 drive), and the LCD ID is still required.
  • Pin classification helper: probe_pin_pulls() with a released-pull sample and classify_pin_pull() (weak up / weak down / floating / pull dependent).
  • Package-only facts give weak candidates: StampC6 (flash capacity eFuse) and StampC5 (flash / PSRAM eFuse) no longer confirm or save. Later families still do not probe their pins. The unconditional StampP4 candidate is removed.

Behavior changes

  • Boards that are detected normally are unchanged. They were checked on 41 devices across ESP32 / PICO-D4 / PICO-V3 / S3 / C5 / C6 / H2 / P4 (board ID and display configuration match the previous detector).
  • An unknown result no longer erases the stored hint. A build that defines M5GFX_BOARD for a different board no longer writes NVS on every boot.
  • StampC6 / StampC5: Display.init() returns false, getBoard() is unknown and getBoardCandidate() reports the Stamp board.
  • P4 modules without board evidence: getBoardCandidate() is unknown (was StampP4 / StampP4X).
  • Third-party PICO-D4 boards are no longer confirmed as AtomLite / AtomU by touch ratio alone. M5Stack units with mismatched SD line pulls (modified or faulty) are no longer detected.
  • A Cardputer started with a key held reaches the candidate after all attempts (display still shown).

Testing

  • extras/board_spec: npm test (213 tests, host tests compile the production detection code) and npm run check. Tests that read M5Unified sources are skipped when M5Unified is not checked out.
  • Arduino and ESP-IDF builds on the CI matrix.
  • Detection on 41 devices, compared against the previous detector.

M5Unified needs the matching change (m5stack/M5Unified#399) for setDetectConfig() and getFixedBoard().

Introduce detection request and outcome types and run every chip's embedded detection through one session (attempt count, reset escalation, hint, failure reason). Keep confirmed identification separate from successful display adoption. Behavior is unchanged; the legacy C5/C61 autodetect output pointers are preserved and generated detector lists are checked against the session family limit.
Classify results after preparation as confirmed, provisional (family identified, member unresolved; display built) or weak (suggestion only; no display). Only confirmed adoption may write NVS, and only when the value differs; unknown and candidate results leave the stored hint intact.

Keep the hint for every attempt and use it only for ordering and provisional member selection. Restrict hinted-member probing to the first non-final attempt so retries recover other members. Remove chip-specific hint gates and final-attempt policies; the final attempt re-measures the same gates.

Add setDetectConfig()/detect_config_t to receive the application fallback board before init. A provisional member that differs from it is not constructed; the candidate is retained and the fallback wins on the final attempt. Select unresolved Core2/Tough and Cardputer members by fallback, then hint, then representative, excluding Tough when AXP2101 is observed. Keep PaperMono when PM1 member evidence is unreadable. Mark the Stack signature bypass provisional and report unreadable UnitC6L identification as a weak candidate without replacing an earlier one.

Retain directly adopted panels by setup success rather than confidence, keep candidate metadata aligned after setup failures, and warn about configuration changes after init only when the value changes. Add member-suppression injection and host tests for the result projection.
Add detect_config_t::fixed_board and getFixedBoard(). A fixed identity is resolved only against the chip construction table: no detector probing, no package filtering and no NVS access. Startup uses a clean result and a transaction over the board pins, keeps the accepted identity across display startup failures and retries with reset escalation on later init calls. An identity for another chip is rejected with a warning.

Share read-only panel, PMIC and VBUS variant observations with family detection, and add fixed startup callbacks for Core2, Tough, CoreInk, CoreS3, AirQ and Atom displays in their required power order. Member selection by touch, camera, capacitance or firmware is omitted; failed variant reads keep the default panel with a warning. Core2-family startup stops when the PMIC identity is unreadable, and fixed Tough uses the observed AXP2101 backlight instead of writing AXP192 registers.

Protect pins owned by active OPI memory, capture the StopWatch TE GPIO before construction, and warn when GPIO ownership cannot be acquired. Add host tests for pin guards, retry policy and the BOARD_ID selection guard.
Probe both StopWatch and PaperMono touch controllers and G12/G13 pulls before selecting a member. Pull-based abnormal rows select a provisional member only on the final attempt. Remove the StopWatch rescue power sequence and the NFC 0x50 exclusion (both members can carry that device).

Cold PaperMono keeps one pre-confirmation power sequence for D/D and U/U pulls, followed only by its own touch check. This path can also reach StopWatch when touch is unanswered and both IRQ lines read low or are driven high; it is kept as a documented cold-start exception and never switches members during refinement.

IOE1 boots hundreds of milliseconds later than PM1, so its response no longer gates identification; the power lists wait for it once before the first IOE1 access (500 ms deadline from the wait). Pending refinement after a power failure retries on non-final attempts instead of adopting an unresolved member; this also applies to Core2/Tough and CoreS3/CoreS3 SE refinement when their power sequence fails. Add touch-address injection and host tests.
Confirm Core2 and Tough from exclusive touch responses before power. For silent AXP192 members, use G12 with G32/G33 to choose the shared Core2/Tough power sequence or the Station LCD probe. Station has only a weak G12 pull-down, so both Port A lines must read up before Core2 power is chosen; a Station with external pull-ups on both Port A lines can still take that path. Station LCD misses and ambiguous subdivisions never select Core2 power from a hint or preference.

Contradictory touch answers (M-Bus devices at 0x2E/0x38) never confirm a member. AXP2101 with unanswered touch takes all five attempts before a provisional Core2. A cold AXP192 Core2/Tough with a Port A line held low follows the Station branch and stays unidentified after a Station LCD miss.

Treat post-power panel observation failure as optional and keep the touch-confirmed board. After a valid AXP or PM1 ID, later families are not probed in the same attempt even when member confirmation fails. Prepare SD SPI mode before panel observation, including fixed startup. Declare G12/G32/G33 as probe pins so the transaction restores them. Add pre/post-power injection and host tests.
Extend probe_pin_pulls() with optional released samples after each bias and add classify_pin_pull(): strong up/down, weak up/down, floating, pull dependent (no released sample) and conflicting. A zero release delay keeps the previous probe sequence.

Replace the DualKey-only floating probe with the common probe and classifier. Its release wait grows from 32 to 128 microseconds so slower weak pulls are not taken as floating. Add C++11 host tests for all sample combinations.
CoreS3 family: the camera or a long release band confirms the camera family; ambiguous or unavailable release without a camera retries and selects fallback, hint or representative only on the final attempt (unavailable deliberately uses all five attempts). StackChan evidence (G5/G6/G7 base pulls, then IOE ACK and firmware in a gated 50 ms window) is observed before member selection. An ACK with unreadable firmware retries before a provisional member; base firmware below 0x04 remains CoreS3. Confirmed SE is kept when camera observation fails and dedicated GPIO timing is unavailable.

Cardputer family: read both variant ICs when both pin pairs are high; conflicting answers fail, and the plain Cardputer requires all four lines to float after release. Booting with a key pressed reaches a displayable provisional member after all attempts.

Carry the release band in detector-private refine state. Add catalog notes, injection controls and host outcome tables.
Atom family (PICO-D4): require G18 floating before the Voice probe and a strong G12 pull-down (IR transmitter) before Lite/Matrix/U touch scanning. Unrelated PICO-D4 boards no longer become Atom members from touch ratios alone. Document the Atom IR wiring.

Stack family: remove the hint/final-attempt bypass. The SD line pulls are required, so a held line can no longer admit Stack reset and drive G33 (which can release the TimerCam power hold). Modified or faulty units with mismatched SD pulls stay unidentified. G32 is not used as evidence because the same pad is camera output on TimerCam and external SDA on Station and Paper.
StampC6 (flash capacity eFuse) and StampC5 (flash/PSRAM eFuse) matches only suggest the module: they no longer confirm, construct or save. Later families still do not probe these pins, and an earlier weak candidate is preserved. Display initialization reports failure for these modules; an application fallback takes precedence.

Remove the unconditional StampP4/StampP4X detector: the chip revision alone duplicates the application default without board evidence. Other P4 candidates and fixed startup are unchanged. Log the unresolved weak candidate.
Skip checks that read M5Unified sources when its checkout is absent, so the board specification workflow runs with M5GFX alone. Name the family-member and package-candidate tests by behavior.
@ainyan03
ainyan03 force-pushed the detect_positive_evidence branch from bf576b6 to e49fc66 Compare October 10, 2026 23:23
@lovyan03
lovyan03 merged commit bc55dbe into m5stack:develop Oct 10, 2026
26 checks passed
@ainyan03
ainyan03 deleted the detect_positive_evidence branch October 10, 2026 23:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants