What the chip says about itself
Cores, clock, flash size, PSRAM, MAC address and free heap — eight lines that answer the questions you would otherwise argue about with your own board three hours later.
Ask the board, not the listing
Boards are sold with a chip name and a set of numbers, and the numbers are
frequently the family's rather than the board's. Eight lines in setup() settle
it, and they are the same eight lines forever.
The three that change what you can build
Cores. The classic ESP32 and the S3 have two, the C3 and C6 have one. Everything you will read about pinning a task to core 1 assumes two, and on a single-core chip the call succeeds and pins it to the only one there is.
PSRAM. External RAM, off by default, absent on most boards. A camera frame buffer or a full-colour display buffer needs it, and "ESP32-S3" on the silkscreen does not promise it.
Flash. Decides how much room there is for your sketch, for OTA's second copy of it, and for a filesystem — and the split between those three is fixed before you upload.
The MAC is a free device id
ESP.getEfuseMac() is burned in at the factory and unique to the chip. It is
the right answer whenever you were about to invent a device id: a hostname, an
MQTT client id, the last four hex digits in an access point's name. It costs no
storage, survives an erase, and cannot collide with the board next to it.
While you are there, drop the clock
setCpuFrequencyMhz(80) roughly halves idle current and most projects never
notice. It is the cheapest power saving there is, and unlike sleep it changes
nothing about how your code is structured.
The code
Paste this into the top of every new project. It costs nothing, prints once, and settles what you are actually holding.
void setup() {
Serial.begin(115200);
delay(200);
Serial.printf("chip %s rev %d\n", ESP.getChipModel(), ESP.getChipRevision());
Serial.printf("cores %d @ %d MHz\n", ESP.getChipCores(), getCpuFrequencyMhz());
Serial.printf("flash %u bytes\n", ESP.getFlashChipSize());
Serial.printf("psram %u bytes\n", ESP.getPsramSize());
Serial.printf("heap %u free\n", ESP.getFreeHeap());
Serial.printf("mac %012llx\n", ESP.getEfuseMac());
}
void loop() {}getFlashChipSize reports what the board was configured for, not what is soldered on. If it says 4 MB on a 16 MB board, the Tools → Flash Size setting is wrong.
The same facts, from three modules. unique_id() is the same eFuse MAC the C++ call returns, in bytes.
import machine, esp, gc, ubinascii
print("freq ", machine.freq() // 1_000_000, "MHz")
print("flash ", esp.flash_size(), "bytes")
print("id ", ubinascii.hexlify(machine.unique_id()).decode())
gc.collect()
print("free ", gc.mem_free(), "bytes")gc.mem_free() is not comparable to the C++ heap figure — it is the MicroPython heap, which is a slice of the same RAM with the interpreter already taken out of it.
When it does not work
The board setting, not the board. Tools → Flash Size in the IDE, or board_upload.flash_size in platformio.ini. Uploading with the wrong value also means the partition table is wrong, so half the chip is simply unused.
PSRAM is off unless enabled. Tools → PSRAM in the IDE. Until it is on, every large allocation quietly comes out of the 300-odd kB of internal heap and fails at the worst moment.
The eFuse value is a base address. Wi-Fi station, soft-AP, BLE and Ethernet each derive their own from it by adding a small offset, so four different addresses are normal and only one of them is on your router.
Wi-Fi takes about 50 kB the moment it starts, and a TLS handshake takes another 40 while it runs. Print the heap before and after WiFi.begin and the number stops being alarming.
Enough about the board. The next chapter is the first pin actually doing something — high, low, and what happens in between.
Digital I/O →Edit this page — content/esp32/whats-inside-the-chip.mdx
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.