How esp_flasher Reads and Displays Chip Properties: MAC Address and CPU Frequency
esp_flasher retrieves MAC addresses and CPU frequencies by communicating directly with the ESP ROM bootloader through a custom own_esptool.py module, then aggregates these properties into human-readable formats via the read_chip_info() helper.
The jason2866/esp_flasher repository provides a Python-based tool for flashing ESP8266 and ESP32 devices. Understanding how esp_flasher reads and displays chip properties like MAC address and CPU frequency requires examining its three-layer architecture: serial detection, ROM communication, and data presentation.
Understanding the Detection Architecture
The process follows a clear pipeline from hardware connection to console output. First, esp_flasher establishes a serial connection and identifies the specific chip family. Next, it queries the ROM bootloader for raw hardware registers and feature flags. Finally, it transforms these low-level values into structured data classes and formatted console output.
This architecture separates concerns between transport layer (own_esptool.py), business logic (common.py), and user interface (__main__.py).
Step 1: Detecting the Chip and Opening Serial Connection
The entry point begins in esp_flasher/__main__.py with the detect_chip() function. This helper instantiates the appropriate ROM loader class based on the detected chip family.
from esp_flasher import common
# Detect creates ESP32ROM, ESP8266ROM, etc. based on chip family
chip = common.detect_chip("/dev/ttyUSB0")
The function returns a chip-specific object (such as ESP32ROM or ESP8266ROM) defined in esp_flasher/own_esptool.py. These classes inherit from base ROM handlers that manage the serial protocol and register access.
Step 2: Reading Raw Properties from the ROM
Once the ROM loader is active, esp_flasher queries specific hardware attributes through low-level register reads and ROM function calls.
Retrieving the MAC Address
The MAC address is read via the read_mac() method implemented in own_esptool.py. For ESP8266, this method resides at lines 1507-1520; for ESP32-family chips, it appears around lines 1815-1830.
The method reads OTP registers (ESP_OTP_MAC0, ESP_OTP_MAC1, ESP_OTP_MAC3) to extract the Organizationally Unique Identifier (OUI) and device-specific bytes. It returns a 6-byte tuple representing the hardware MAC.
# Inside ESP32ROM or ESP8266ROM class
mac_bytes = chip.read_mac() # Returns (0x30, 0xAE, 0xA4, 0x12, 0x34, 0x56)
Determining CPU Frequency
The CPU frequency is not stored in a single register. Instead, esp_flasher queries the ROM for a list of chip features via get_chip_features(). For ESP32ROM, this method appears around line 1482 in own_esptool.py.
The ROM returns feature strings such as "80MHz", "160MHz", or "240MHz". The read_chip_info() function in common.py parses this list against an ordered frequency hierarchy:
# From esp_flasher/common.py
FREQUENCIES = ("400MHz", "240MHz", "160MHz", "120MHz", "80MHz")
The function selects the first matching frequency from this tuple, ensuring the highest supported frequency is reported as the maximum CPU frequency.
Step 3: Aggregating and Displaying the Data
The read_chip_info() function in esp_flasher/common.py (lines 29-64) serves as the aggregation layer. It accepts a chip object and returns a structured data class (ESP32ChipInfo or ESP8266ChipInfo).
from esp_flasher.common import read_chip_info
info = read_chip_info(chip)
print(f"MAC: {info.mac}") # Formatted as 30:AE:A4:12:34:56
print(f"CPU Frequency: {info.cpu_frequency}") # e.g., 240MHz
The function converts the raw MAC tuple into a colon-separated hexadecimal string using ":".join(f"{x:02X}" for x in mac). It bundles the parsed CPU frequency along with other attributes like core count, Bluetooth support, and flash configuration into the info object.
The CLI entry point in esp_flasher/__main__.py (lines 33-48) simply prints these fields:
print(f" - MAC Address: {info.mac}")
print(f" - Max CPU Frequency: {info.cpu_frequency}")
Practical Code Examples
Programmatic Usage in Python
You can integrate esp_flasher's chip detection into your own Python scripts to retrieve hardware properties before flashing:
from esp_flasher import common
from esp_flasher.common import read_chip_info
def get_hardware_info(port="/dev/ttyUSB0"):
# Step 1: Detect chip type and open connection
chip = common.detect_chip(port)
# Step 2: Read aggregated properties
info = read_chip_info(chip)
# Step 3: Access structured data
return {
"mac": info.mac,
"cpu_frequency": info.cpu_frequency,
"cores": info.num_cores,
"chip_model": info.chip_model
}
# Usage
properties = get_hardware_info()
print(f"Device MAC: {properties['mac']}")
Command-Line Interface
For manual inspection or scripting in shell environments, use the CLI to display chip properties before flashing firmware:
# Display chip information without flashing
$ python -m esp_flasher -p /dev/ttyUSB0 --chip-info
Chip Info:
- Chip Family: ESP32
- Chip Model: ESP32-C6 (revision v1.0)
- Number of Cores: 2
- Max CPU Frequency: 240MHz
- Has Bluetooth: YES
- MAC Address: 30:AE:A4:12:34:56
Key Source Files and Their Roles
| File | Role |
|---|---|
esp_flasher/own_esptool.py |
Low-level ROM loader implementation; defines read_mac() and get_chip_features() for each chip family (ESP8266, ESP32, etc.). |
esp_flasher/common.py |
High-level aggregation layer containing read_chip_info() (lines 29-64), detect_chip(), and data class definitions (ESP32ChipInfo, ESP8266ChipInfo). |
esp_flasher/__main__.py |
Command-line entry point; orchestrates detection and prints formatted output using the aggregated chip information. |
Summary
- esp_flasher queries chip properties through a custom
own_esptool.pymodule that communicates directly with the ESP ROM bootloader. - The MAC address is read from OTP registers via
read_mac()methods specific to each chip family (ESP8266 at lines 1507-1520, ESP32 at lines 1815-1830). - CPU frequency is determined by parsing feature strings from
get_chip_features()against a prioritized frequency tuple incommon.py. - The
read_chip_info()function incommon.py(lines 29-64) aggregates raw data into structured objects and formats the MAC as a colon-separated hexadecimal string. - Both programmatic Python APIs and CLI interfaces expose these properties for integration into flashing workflows.
Frequently Asked Questions
How does esp_flasher determine which chip family is connected?
The detect_chip() function in esp_flasher/common.py establishes a serial connection and attempts to synchronize with the ROM bootloader. Based on the response and magic numbers returned by the chip, it instantiates the appropriate ROM class (such as ESP32ROM, ESP8266ROM, or ESP32C3ROM) from own_esptool.py to handle family-specific register layouts and commands.
Why does CPU frequency detection rely on feature strings instead of a direct register read?
The ESP ROM bootloader does not expose a single register containing the CPU frequency value. Instead, it provides a list of supported capabilities via get_chip_features(), which includes strings like "240MHz" or "80MHz". The read_chip_info() function parses this list and matches it against an ordered tuple of frequencies to determine the maximum supported clock speed, ensuring compatibility across different ESP32 variants and ESP8266 devices.
Can I retrieve chip properties without flashing firmware using esp_flasher?
Yes, you can use the Python API to query hardware information independently of the flashing process. Import detect_chip and read_chip_info from esp_flasher.common, open a connection to the serial port, and call these functions to retrieve the MAC address, CPU frequency, core count, and other properties without writing any binary to the device flash.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →