Tienes una red ESP NOW funcionando: cinco nodos con sensores en el patio, un receptor en la casa, todo afinado. Un día un nodo se moja, se quema el regulador y la placa queda inservible. Cambiarla por otra ESP32 es fácil; lo molesto viene después. Cada nodo de la red tiene registrada a la placa muerta por su dirección MAC, así que la nueva llega como una desconocida y te toca reprogramar todos los demás nodos con la dirección nueva.
Hay un atajo: hacer que la placa de reemplazo se presente con la misma MAC que tenía la dañada. En esta guía vas a aprender a leer todas las direcciones MAC de un ESP32 con ESP IDF (la de fábrica, la de WiFi en modo estación, la del punto de acceso y la de Bluetooth), a fijar una MAC personalizada desde el firmware y a usar eso para clonar un nodo sin tocar el resto de la red. Al final vas a entender también por qué no cualquier dirección sirve y en qué momento del arranque hay que fijarla para que el cambio tenga efecto.
La idea: por qué la MAC es la identidad de un nodo ESP NOW
Una dirección MAC (Media Access Control) son seis bytes que identifican a un equipo dentro de una red local. Se escribe como seis pares hexadecimales separados por dos puntos, por ejemplo 30:AE:A4:07:0D:64. Los tres primeros bytes suelen identificar al fabricante (Espressif tiene varios bloques asignados) y los tres últimos son el número de serie de cada chip.
En WiFi tradicional la MAC casi no se ve: el router la usa para asignar IP y listo. En ESP NOW, en cambio, es todo. Cuando un nodo quiere mandarle datos a otro, lo registra con esp_now_add_peer() pasándole la MAC del destino, y los mensajes que llegan se identifican por la MAC del remitente. Si cifras la comunicación, la clave local (LMK) también queda asociada a esa dirección. O sea: para la red, la MAC es el nodo.
Una MAC base y varias derivadas
El ESP32 no trae cuatro direcciones grabadas por separado. En el eFuse viene una sola MAC base de fábrica, y ESP IDF calcula las demás sumándole un número al último byte. En un ESP32 o un ESP32-S3 con la configuración por defecto queda así:
- WiFi estación (STA): la base, tal cual.
- WiFi punto de acceso (SoftAP): la base + 1.
- Bluetooth: la base + 2.
Ese detalle es clave para clonar: si cambias la MAC base, todas las derivadas se mueven con ella. Por eso basta con fijar una sola dirección para que la placa nueva tenga exactamente las mismas MAC de WiFi y Bluetooth que tenía la anterior.
Las dos funciones que vas a usar
Ambas viven en esp_mac.h:
esp_read_mac()lee la dirección del tipo que le pidas: la base de fábrica, la de estación, la de SoftAP o la de Bluetooth.esp_base_mac_addr_set()reemplaza la MAC base por una que tú le entregas.
Un punto que conviene tener claro desde el principio: el cambio no se graba en el chip. La MAC de fábrica sigue intacta en el eFuse y la personalizada vive solo en memoria mientras el programa corre. Si el firmware no vuelve a fijarla en el siguiente arranque, la placa vuelve a su dirección original. En la práctica eso significa que la llamada tiene que estar en el código que corre en cada arranque, y es una ventaja: no hay forma de "romper" la identidad de la placa de manera permanente con este método.
Hardware y software que necesitas
El montaje no lleva cables ni sensores: solo la placa conectada al computador.
- Una placa ESP32 de cualquier familia. El tutorial original usa una ESP32-S3 programada por su USB nativo (USB JTAG integrado), pero un ESP32 DevKit clásico con conversor USB serial funciona igual.
- Un cable USB de datos (no uno de solo carga, que es la causa número uno de "no aparece el puerto COM").
- Visual Studio Code con la extensión oficial ESP IDF de Espressif ya instalada. Si todavía no la tienes, sigue primero la guía de inicio de ESP IDF en VS Code (enlazada en Recursos).
Para el caso de clonar un nodo vas a necesitar además la MAC de la placa dañada. Si la placa todavía enciende, la lees con el primer programa de esta guía. Si murió del todo, la tienes que sacar de otro lado: del código de los otros nodos (donde está escrita en el arreglo del peer), de la lista de clientes del router o de una etiqueta. De ahí sale el mejor consejo de toda esta guía: cuando instales un nodo, anota su MAC y pégala en la caja.
Preparar el proyecto en VS Code
La extensión ESP IDF crea un proyecto vacío con toda la estructura de carpetas y la configuración de CMake lista. Abre la extensión desde la barra lateral, despliega el menú "Advanced", entra a "New Project Wizard" y elige la versión del framework (el original usa la rama 6.x).

En la lista de ejemplos, elige "ESP IDF Templates" y presiona "Create project using template sample project".

Se abre un formulario con cinco datos:
- Project Name: el nombre que quieras, por ejemplo
mac-esp32. - Project Directory: la carpeta donde se guarda el proyecto. Evita carpetas sincronizadas con Google Drive, OneDrive o Dropbox: la compilación genera cientos de archivos y la sincronización la vuelve lentísima.
- ESP IDF Target: el chip de tu placa (
esp32,esp32s3,esp32c3, etc.). Con una ESP32-S3 por USB nativo, en "ESP IDF Board" se elige la configuración "ESP32-S chip (via builtin USB JTAG)". - Serial Port: con la placa conectada, el puerto que le corresponde.
- Choose Template: el botón azul que crea el proyecto.

A los pocos segundos aparece un aviso para abrir el proyecto recién creado; haz clic en "Open Project".

Si el aviso no salió, abre la carpeta a mano desde File > Open Folder y selecciona la carpeta del proyecto.

En el explorador de la izquierda, despliega la carpeta main y abre main.c. Ahí va todo el código de esta guía. (En el artículo original se cuela una mención a un archivo scan.c que viene de otro tutorial de los mismos autores; en la plantilla vacía el archivo es main.c).
Paso 1: leer todas las MAC de la placa
Este primer programa no cambia nada: solo consulta las cuatro direcciones y las imprime en el monitor serie. Úsalo para conocer la identidad de cada placa antes de instalarla, o para rescatar la MAC de un nodo que todavía enciende.
/*
Rui Santos & Sara Santos - Random Nerd Tutorials
https://RandomNerdTutorials.com/esp-idf-esp32-mac-address/
*/
#include <stdio.h>
#include "esp_mac.h"
#include "esp_log.h"
static const char *TAG = "MAC";
void app_main(void)
{
uint8_t mac[6];
esp_err_t ret;
// Base MAC Address (factory default)
ret = esp_read_mac(mac, ESP_MAC_EFUSE_FACTORY);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "Base MAC Address (Factory): %02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
// Wi-Fi Station MAC Address
ret = esp_read_mac(mac, ESP_MAC_WIFI_STA);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "Wi-Fi STA MAC Address: %02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
// Wi-Fi SoftAP MAC Address
ret = esp_read_mac(mac, ESP_MAC_WIFI_SOFTAP);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "Wi-Fi SoftAP MAC Address: %02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
// Bluetooth MAC Address
ret = esp_read_mac(mac, ESP_MAC_BT);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "Bluetooth MAC Address: %02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
}
Qué hace cada parte
Las tres cabeceras cumplen roles distintos: stdio.h es la biblioteca estándar de C, esp_mac.h trae las funciones de lectura y escritura de MAC, y esp_log.h entrega el sistema de logs de ESP IDF, que antepone a cada línea un nivel (I de info, E de error), una marca de tiempo y una etiqueta. Esa etiqueta es la constante TAG, que aquí vale "MAC" y te permite filtrar los mensajes de este módulo cuando el proyecto crezca.
El corazón del programa son cuatro llamadas casi iguales a esp_read_mac(). El primer parámetro es el arreglo de seis bytes donde se deja el resultado y el segundo dice qué dirección quieres:
ESP_MAC_EFUSE_FACTORY: la MAC base grabada de fábrica. Esta no cambia aunque fijes una personalizada, así que siempre te dice qué chip tienes en la mano.ESP_MAC_WIFI_STA: la que usa el WiFi en modo estación, que es la que ven los otros nodos ESP NOW cuando trabajas en ese modo (el caso más común).ESP_MAC_WIFI_SOFTAP: la del punto de acceso.ESP_MAC_BT: la de Bluetooth.
Cada llamada devuelve un esp_err_t, y el programa solo imprime si el resultado es ESP_OK. El formato %02X muestra cada byte como dos dígitos hexadecimales en mayúscula, con cero a la izquierda cuando hace falta.
Compilar y grabar el programa
El flujo en VS Code es siempre el mismo y todo está en la barra inferior: método de grabación, puerto, chip, compilar y grabar. Si creaste el proyecto con el asistente, lo más probable es que ya esté todo configurado; igual revísalo.
Primero, el método de grabación: haz clic en el ícono de estrella y elige UART.

Después, el puerto: con la placa conectada, haz clic en el ícono del enchufe y selecciona el puerto que le corresponde.

Luego el chip: el ícono de chip en la misma barra. Elige el de tu placa.

Con una ESP32-S3 programada por su USB nativo, falta elegir la configuración "ESP32-S chip (via builtin USB JTAG)".

Ahora compila con el ícono de llave inglesa (Build Project).

La primera compilación tarda bastante más que las siguientes, porque arma todo el framework desde cero. Cuando termina, la terminal muestra el resumen y el mensaje "Build Successfully".

Por último, graba la placa con el ícono del rayo ("Flash Device"). Según el modelo, puede que tengas que mantener apretado el botón BOOT para que entre en modo de grabación. Al terminar aparece el aviso "Flash Done".

Probar la lectura
Abre el monitor con el ícono de pantalla ("Monitor Device") y presiona RESET en la placa. Vas a ver algo así:
MAC: Base MAC Address (Factory): 68:B6:B3:22:9E:60
MAC: Wi-Fi STA MAC Address: 68:B6:B3:22:9E:60
MAC: Wi-Fi SoftAP MAC Address: 68:B6:B3:22:9E:61
MAC: Bluetooth MAC Address: 68:B6:B3:22:9E:62

Fíjate en el patrón: la base y la de estación son idénticas, la de SoftAP termina en 61 y la de Bluetooth en 62. Es justo la regla de "base + 1, base + 2" de más arriba, y es la prueba de que basta con mover la base para mover todas las demás.
Paso 2: fijar una MAC personalizada (y clonar la de un nodo dañado)
El segundo programa reemplaza la MAC base al arrancar y después lee la de estación para comprobar que el cambio se aplicó.
/*
Rui Santos & Sara Santos - Random Nerd Tutorials
https://RandomNerdTutorials.com/esp-idf-esp32-mac-address/
*/
#include <stdio.h>
#include "esp_mac.h"
#include "esp_log.h"
#include "nvs_flash.h"
static const char *TAG = "CUSTOM_MAC";
// REPLACE THE NEXT VARIABLE WITH YOUR CUSTOM MAC ADDRESS
uint8_t custom_mac[6] = {0x32, 0xA1, 0xB2, 0xC3, 0xD4, 0xE5};
void app_main(void)
{
// Set custom MAC Address
esp_err_t err = esp_base_mac_addr_set(custom_mac);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Custom MAC address set successfully!");
} else {
ESP_LOGE(TAG, "Failed to set custom MAC address: %s", esp_err_to_name(err));
}
uint8_t read_mac[6];
// Read new custom Wi-Fi STA MAC Address
err = esp_read_mac(read_mac, ESP_MAC_WIFI_STA);
if (err == ESP_OK) {
ESP_LOGI(TAG, "[NEW] Wi-Fi STA MAC Address: %02X:%02X:%02X:%02X:%02X:%02X",
read_mac[0], read_mac[1], read_mac[2],
read_mac[3], read_mac[4], read_mac[5]);
}
}
La dirección nueva se define en una sola línea:
uint8_t custom_mac[6] = {0x32, 0xA1, 0xB2, 0xC3, 0xD4, 0xE5};
Para clonar un nodo, aquí va la MAC de estación de la placa dañada. Si su MAC era 24:6F:28:A1:B2:C3, la línea queda {0x24, 0x6F, 0x28, 0xA1, 0xB2, 0xC3}. Como la de estación es igual a la base, al fijarla como base la placa nueva hereda también la de SoftAP y la de Bluetooth del nodo original.
Después viene la llamada que hace el trabajo, con su manejo de error:
esp_err_t err = esp_base_mac_addr_set(custom_mac);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Custom MAC address set successfully!");
} else {
ESP_LOGE(TAG, "Failed to set custom MAC address: %s", esp_err_to_name(err));
}
Si la dirección es inválida, esp_err_to_name() traduce el código de error a texto legible en el log, lo que ahorra buscar números en la documentación.
Qué direcciones sirven y cuáles no
ESP IDF rechaza una MAC cuyo primer byte tenga el bit 0 en 1. Ese bit marca las direcciones de multidifusión (multicast), que identifican a un grupo de equipos y no a uno solo. Por eso 1A:XX:XX:XX:XX:XX es válida (0x1A termina en 0 en binario) y 15:XX:XX:XX:XX:XX no (0x15 termina en 1). Un truco rápido: el primer byte tiene que ser par.
Hay un segundo bit que conviene conocer aunque ESP IDF no lo exija. El bit 1 del primer byte indica si la dirección es administrada localmente (inventada por ti) o universal (asignada por un fabricante). El ejemplo del original usa 0x32, que en binario es 00110010: bit 0 en cero y bit 1 en uno, o sea una dirección local válida. Si inventas MAC para tus propios nodos, sigue esa convención (primer byte como 02, 06, 0A, 32...) y nunca vas a chocar con un equipo comercial de tu red. Para clonar, en cambio, usas la MAC de fábrica de la placa muerta, que es universal y también válida.
El momento justo: antes de iniciar el WiFi
esp_base_mac_addr_set() tiene que ejecutarse antes de inicializar cualquier interfaz de red, es decir, antes de esp_netif_init() y esp_wifi_init(). El driver de WiFi copia la MAC al iniciarse; si la cambias después, el radio sigue transmitiendo con la vieja y el log te dice que todo salió bien, lo que confunde bastante. En un nodo ESP NOW real, entonces, el orden de app_main() queda así:
- Fijar la MAC base con
esp_base_mac_addr_set(). - Inicializar NVS (
nvs_flash_init()), que el WiFi necesita. - Inicializar
esp_netify el loop de eventos. esp_wifi_init(), modo estación yesp_wifi_start().esp_now_init()y el registro de peers.
Ese orden también explica la cabecera nvs_flash.h que el programa incluye sin usarla todavía: es la que vas a necesitar en cuanto sumes el WiFi.
Probar el cambio
Graba el programa con el mismo procedimiento del paso 1, abre el monitor y reinicia la placa. Deberías ver:
CUSTOM_MAC: Custom MAC address set successfully!
CUSTOM_MAC: [NEW] Wi-Fi STA MAC Address: 32:A1:B2:C3:D4:E5

Clonar un nodo ESP NOW de principio a fin
Con las dos piezas anteriores, el reemplazo de una placa queda así:
- Recupera la MAC del nodo dañado. Si enciende, grábale el programa del paso 1 y anota la línea "WiFi STA". Si no, búscala en el código de los otros nodos, en tu registro o en la etiqueta de la caja.
- Retira la placa vieja de la red. Dos equipos con la misma MAC encendidos al mismo tiempo se pisan: los acuses de recibo de ESP NOW llegan a cualquiera de los dos y las entregas fallan de forma intermitente. Si la placa dañada todavía funciona a medias, desconéctala antes de seguir.
- Agrega la MAC al firmware del nodo. En el código que ya usabas, pon la llamada a
esp_base_mac_addr_set()como primera línea deapp_main(), antes de todo el arranque del WiFi. - Graba la placa nueva y verifica en el monitor que la MAC de estación sea la de la placa original.
- Comprueba desde el receptor. Sin tocar su código, debería empezar a recibir los mensajes del nodo reemplazado como si nada hubiera pasado. Si usas el callback de envío,
ESP_NOW_SEND_SUCCESSen el emisor confirma que el receptor lo reconoce.
Si la red usa cifrado con LMK, no tienes que hacer nada más: la clave está asociada a la MAC y la MAC es la misma.
Errores comunes y cómo resolverlos
- El log dice que la MAC se fijó, pero los otros nodos no la reconocen. Casi siempre es el orden: la llamada quedó después de
esp_wifi_init(). Muévela al principio deapp_main(). ESP_ERR_INVALID_ARGal fijar la MAC. El primer byte es impar (bit de multidifusión en 1). Revísalo en binario o usa uno par.- Comunicación intermitente después del reemplazo. Probablemente la placa vieja sigue encendida en algún lado con la misma MAC.
- El receptor recibe, pero el emisor no. Revisa que el receptor esté en el mismo canal WiFi que el emisor; la MAC no tiene nada que ver con el canal, pero es un problema que aparece seguido justo después de cambiar una placa.
- No aparece el puerto al conectar la placa. Cambia el cable por uno de datos; en placas con USB nativo, prueba entrando a modo de grabación con BOOT + RESET.
Variantes y mejoras
- Un solo firmware para todos los nodos. En vez de escribir la MAC en el código, guárdala en NVS (para eso está
nvs_flash.h) y léela al arrancar. Así compilas una sola vez y cada placa conserva su identidad, aunque la reemplaces: basta con escribir la MAC del nodo viejo en la NVS de la placa nueva desde un comando por el puerto serie. - Cambiar solo la MAC de una interfaz. Si quieres que el WiFi tenga la MAC del nodo viejo pero el Bluetooth conserve la suya, usa
esp_wifi_set_mac(WIFI_IF_STA, mac)después deesp_wifi_init()y antes deesp_wifi_start(), oesp_iface_mac_addr_set()en las versiones recientes de ESP IDF. Aplican las mismas reglas de bits. - Grabarla para siempre en eFuse, con mucho cuidado. El ESP32 tiene un bloque de eFuse para una MAC personalizada que se graba con
espefuse.py burn_custom_macy se lee conesp_efuse_mac_get_custom(). Es irreversible: si te equivocas en un byte, esa placa queda así para siempre. Úsalo solo en producción en serie, nunca para pruebas.
Personalización para Chile
En Chile consigues lo necesario para replicar y mantener esta red en MechatronicStore:
- Placa de desarrollo ESP32-S3: es la que usa el tutorial original, con USB nativo y grabación por USB JTAG integrado. Sirve tanto para el nodo de pruebas como para la placa de reemplazo.
- Placa ESP32 DevKit (ESP32-WROOM-32): si tu red ESP NOW ya está armada con ESP32 clásicos, mejor reemplazar con el mismo modelo. El código de esta guía funciona sin cambios en las dos familias.
- Cable USB-C de datos: para grabar y monitorear la placa. Si tu DevKit es de los de micro USB, usa el cable que corresponda.
Un consejo para quienes mantienen redes de sensores: ten al menos una placa de repuesto del mismo modelo en el cajón. Con la MAC anotada y este procedimiento, cambiar un nodo dañado toma diez minutos en lugar de una tarde reprogramando la red entera.
Recursos
- Tutorial original (inglés): ESP IDF: Read and Set ESP32 MAC Address, de Rui Santos y Sara Santos en Random Nerd Tutorials. Esta versión está basada en él, con el caso de uso de ESP NOW agregado.
- Código: los dos programas completos (lectura y MAC personalizada) están en esta página, listos para copiar en
main.c. - Documentación de Espressif: Misc System APIs (MAC Address) y ESP NOW.
Versión chilena con componentes en stock local en MechatronicStore.




