Skip to content

espressif: initialize ESP32-P4 UID from the factory MAC - #11494

Open
ladyada-eagleclaw wants to merge 4 commits into
adafruit:mainfrom
ladyada-eagleclaw:fix-esp32p4-factory-uid
Open

ladyada-eagleclaw wants to merge 4 commits into
adafruit:mainfrom
ladyada-eagleclaw:fix-esp32p4-factory-uid

Conversation

@ladyada-eagleclaw

@ladyada-eagleclaw ladyada-eagleclaw commented Oct 3, 2026 •

Copy link
Copy Markdown

AI-assisted with Codex during Metro ESP32-P4 bring-up; validation used serial logs and Windows USB enumeration.

The P4 implementation of microcontroller.cpu.uid returned without initializing its output buffer. Consequently the UID and USB serial number could contain changing bytes, and Windows assigned new serial ports after resets.

Fill the UID from esp_efuse_mac_get_default(). If the read fails, clear all six UID bytes without raising an exception or aborting: USB descriptor setup calls this outside the VM. Other chips keep their existing UID implementation.

import microcontroller
print(microcontroller.cpu.uid.hex())

Metro ESP32-P4 Rev A, silicon v0.1, reported 6055f9f990fd, matching its factory MAC. Windows retained serial 6055F9F990FD and COM42 through two consecutive software resets.

The combined 10.3.0-alpha.4 bring-up tree built for Metro, P4 GPIO, both Espressif P4 function EV variants, M5Stack Tab5, and Solder Party P4 Stamp XL using make -C ports/espressif BOARD=<board> -j6. Hardware testing used that combined tree; this PR isolates the UID fix on current main. Changed-file pre-commit and git diff --check pass. Only Metro was hardware-tested.

Review correction 513a55f64c: changed-file pre-commit, independent source audit, and the isolated PR build (make -C ports/espressif BOARD=espressif_esp32p4_function_ev -j6) passed. A host-side test of the actual function checked successful reads and zeroing after a partial-write failure, including surrounding sentinel bytes. The exact correction was also flashed in integrated Metro verification tree 3732760035 (built before commit, reporting 26bb6e1512-dirty): 1,500 UID reads across two software resets preserved the factory UID, USB serial, and COM42. The original USB host/DSI demo resumed and USB-C echo passed. No hardware eFuse error was injected.

The P4 implementation previously returned without writing raw_id, causing uninitialized USB serial numbers that changed between firmware builds. Read the factory MAC using ESP-IDF. Verified the UID matches the ROM downloader and all six P4 boards build.

Co-authored-by: Limor Fried <ladyada@users.noreply.github.com>
@mikeysklar

mikeysklar commented Oct 3, 2026 •

Copy link
Copy Markdown
Collaborator

Reviewed, looks good to me.

@ladyada
ladyada requested review from dhalbert and a balanced review from Copilot October 5, 2026 13:22

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The targeted implementation correctly initializes the full UID buffer and preserves existing behavior for other Espressif chips.

Review effort: Balanced
Findings: None

What changed in this PR

Initializes the ESP32-P4 processor UID from its factory MAC, stabilizing microcontroller.cpu.uid and USB serial enumeration.

Changes:

  • Adds the ESP MAC API include.
  • Reads and validates the P4 factory MAC as its six-byte UID.
File Description
ports/​espressif/​common-hal/​microcontroller/​Processor.c Implements ESP32-P4 UID retrieval from eFuse.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@dhalbert dhalbert left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two questions for LLM to check:

  1. can esp_efuse_mac_get_default() be used for other than ESP32-P4? Right now there''s a bunch of code to do low-level efuse reading. Maybe Espressif added esp_efuse_mac_get_default() as a convenience function more recently.
  2. CHECK_ESP_RESULT() raises an exception. But is common_hal_mcu_processor_get_uid() ever called outside a VM (e.g. during web workflow startup), where raising an exception would fail? If so, better to do nothing or put a flag value in the return arg, etc.

Note for LLM: This issue of when it's safe to raise an exception comes up frequently. Also another PR used 'ESP_ERROR_CHECK()` which can cause a hard fault. It's worth considering whether the error is serious enough to do that.

@ladyada-eagleclaw

ladyada-eagleclaw commented Oct 5, 2026 •

Copy link
Copy Markdown
Author

Thanks, Dan. I checked both against the pinned ESP-IDF implementation and the CircuitPython call sites.

  1. esp_efuse_mac_get_default() is available for the other Espressif targets too, so it could replace the register-specific reads. It is not a drop-in replacement for the existing UID code, though: the current implementation swaps the nibbles of each byte, which we would need to preserve to avoid changing existing UIDs and USB serial numbers. Also, on targets with SOC_IEEE802154_SUPPORTED, the IDF function writes an eight-byte EUI-64, while our UID buffer is six bytes. A shared implementation would need to accommodate that and extract the original MAC correctly.

  2. You are right about the exception risk. There is a concrete call outside the VM: run_boot_py() calls cleanup_after_vm(), then supervisor_workflow_start() calls usb_init() → usb_build_descriptors() → common_hal_mcu_processor_get_uid(). CHECK_ESP_RESULT() should not be used there, and replacing it with ESP_ERROR_CHECK() would not be appropriate either.

Implemented in 513a55f64c. The P4 path now explicitly checks the result and clears all six UID bytes on failure, without raising or aborting. The other targets remain unchanged.

Verification passed:

  • Changed-file pre-commit, an independent source audit, and the isolated PR build for espressif_esp32p4_function_ev.
  • A host-side test of the actual corrected function, simulating success and a partially written buffer followed by failure; it checked all six output bytes and surrounding sentinel bytes.
  • Flashed the same correction in the integrated Metro firmware and verified the upload hash. Across two microcontroller.reset() calls and 1,500 UID reads, the UID remained 6055f9f990fd, Windows USB serial remained 6055F9F990FD, and the port remained COM42. The original USB host/DSI demo resumed and USB-C echo passed.

AI-assisted with Codex. The failure simulation ran on the host, not through an injected hardware eFuse error. The broader cross-target cleanup remains separate to preserve existing identifiers.

ladyada-eagleclaw and others added 2 commits October 5, 2026 10:06
UID reads also run during USB descriptor setup outside the VM. Return a fully zeroed UID if the factory MAC read fails, without raising an exception or aborting startup.

Co-authored-by: Limor Fried <ladyada@users.noreply.github.com>
@dhalbert

dhalbert commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator

to the LLM: for the UID, I would like something consistent across Espressif so that the MAC addresses are consistent, make Espressif's mfr prefix appear in the right place, etc. We have had complaints and fixes about swapped nibbles before on some ports -- I don't remember which ones, but you can search issues and PR's for that. The major version bump allows us to make an incompatible change that will normalize/canonicalize the UIDs/MAC addresses. Also should check other ports.

A single implementation for Espressif that does that right thing for all chips (and also handles 802.15.4) is desirable.

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.

5 participants