ESP-IDF: читаем и меняем MAC-адрес ESP32 — заводской, Wi-Fi и Bluetooth

Перевод статьи ESP-IDF: Read and Set ESP32 MAC Address — Rui Santos & Sara Santos, Random Nerd Tutorials.
У ESP32 не один MAC-адрес, а сразу несколько: заводской базовый, записанный в одноразовую память eFuse, и вычисляемые из него адреса Wi-Fi-станции, точки доступа и Bluetooth. В этом руководстве мы выведем все четыре в монитор последовательного порта, а заодно научимся подменять базовый MAC-адрес собственным.
Зачем это нужно на практике? Чтобы настроить фильтрацию по MAC на роутере, найти плату в таблице ARP, различать несколько одинаковых ESP32 в сети или отлаживать сетевые проблемы. Работать будем в VS Code с официальным расширением ESP-IDF.
Что такое MAC-адрес#
MAC-адрес (Media Access Control) — уникальный 48-битный (6 байт) идентификатор сетевого интерфейса. Записывается он шестью парами шестнадцатеричных цифр, например 24:0A:C4:11:22:33. Адрес закрепляется за интерфейсом производителем и служит для адресации на канальном уровне — в Wi-Fi и Ethernet.
У чипа ESP32 MAC-адресов несколько: свой есть у Wi-Fi-станции (Station), у точки доступа (SoftAP) и у Bluetooth. Заводской базовый адрес (base MAC) хранится в eFuse — одноразовой памяти конфигурации чипа, — а остальные адреса ESP-IDF вычисляет из него с фиксированными смещениями. Поэтому на практике они идут почти подряд и различаются последними байтами.
Что нужно знать заранее#
Понадобится настроенная среда ESP-IDF. Установить её можно тремя способами:
- расширением ESP-IDF для VS Code — рекомендуемый вариант, его же мы используем ниже;
- установщиком ESP-IDF для Windows;
- плагином Espressif IoT Development Framework для Eclipse.
Подробно установку и настройку мы разбирали в руководстве «Программирование ESP32 на ESP-IDF в VS Code». А если вы пришли в ESP-IDF из Arduino, полезно сначала прочитать про ключевые концепции фреймворка — они сильно экономят время при переходе.
Создаём проект из шаблона#
Расширение ESP-IDF умеет генерировать готовый каркас проекта. Нажмите F1, наберите ESP-IDF: Open ESP-IDF Extension Settings и убедитесь, что выбрана нужная версия фреймворка — у нас это v6.0.

Проверяем версию ESP-IDF в настройках расширения.
Затем снова нажмите F1 и выполните команду ESP-IDF: New Project Wizard.

Мастер New Project Wizard.
В окне мастера заполните поля:
- Project name — имя проекта;
- Project directory — каталог, в котором он будет создан;
- Board — ваша плата (мы взяли ESP32 DevKitS);
- Template — стартовый шаблон кода; выберите sample project.

Имя проекта, каталог, плата и шаблон sample project.
Нажмите Create project using template E — и расширение создаст каркас проекта с минимальным примером.
Открываем проект в VS Code#
Нажмите F1, наберите ESP-IDF: Open VS Code Folder и нажмите Enter. То же самое можно сделать через меню File > Open Folder.

Открываем каталог со свежесозданным проектом.

Или через меню File > Open Folder.
Укажите каталог проекта и нажмите Select Folder (или Open):

Выбираем папку проекта.
Если VS Code спросит, доверяете ли вы авторам файлов, нажмите Yes, I trust the authors.
Код приложения лежит в файле main/main.c — откройте его в проводнике слева:

Нас интересует main/main.c.
Код: чтение MAC-адреса ESP32#
Сотрите содержимое main.c и вставьте следующее:
/*
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]);
}
}
Как работает код#
Подключаемые заголовки#
stdio.h нужна для стандартного вывода, esp_mac.h — для функций работы с MAC-адресами, esp_log.h — для журналирования в монитор порта.
#include <stdio.h>
#include "esp_mac.h"
#include "esp_log.h"
Тег для логирования#
Тег TAG будет предварять каждую строку наших сообщений в мониторе — так их легко отличать от остального вывода.
static const char *TAG = "MAC";
app_main()#
В ESP-IDF точка входа — не setup()/loop(), а функция app_main().
void app_main(void)
Внутри неё мы заводим буфер mac из шести байт (как раз размер MAC-адреса) и переменную ret для кодов возврата.
uint8_t mac[6];
esp_err_t ret;
Базовый (заводской) MAC-адрес#
Первым делом читаем заводской адрес из eFuse — тип ESP_MAC_EFUSE_FACTORY.
// 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]);
}
Функция esp_read_mac() принимает указатель на буфер и тип интерфейса, а кладёт в буфер шесть байт адреса. Выводим их макросом ESP_LOGI (уровень Info), форматируя каждый байт как пару шестнадцатеричных цифр — %02X. Именно из этого адреса ESP32 производит остальные.
MAC-адрес Wi-Fi-станции#
Тип ESP_MAC_WIFI_STA — адрес, под которым плата представляется роутеру в режиме клиента Wi-Fi.
// 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]);
}
MAC-адрес точки доступа (SoftAP)#
Тип ESP_MAC_WIFI_SOFTAP — адрес, который ESP32 использует, когда сам раздаёт Wi-Fi в режиме точки доступа.
// 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]);
}
MAC-адрес Bluetooth#
Наконец, ESP_MAC_BT — адрес Bluetooth-интерфейса.
// 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]);
}
Сборка и прошивка#
Перед сборкой проверьте настройки в строке состояния внизу окна VS Code: метод прошивки, COM-порт и целевое устройство.

Метод прошивки, порт и целевая плата — в строке состояния.
Кликните по полю метода прошивки и выберите UART.

Прошиваем через UART.
Затем укажите COM-порт, к которому подключена плата.

Выбираем правильный COM-порт.
И целевое устройство: для классического ESP32 — пункт ESP32, для плат на ESP32-S3 — соответствующая позиция.

Указываем целевой чип.
Плата на ESP32-S3 с прошивкой через встроенный USB-JTAG выбирается отдельным пунктом.

Вариант для S3 со встроенным USB-JTAG.
Теперь нажмите значок сборки (Build).

Собираем проект.
После успешной сборки появится сообщение о том, что образ для платы создан.

Сборка прошла успешно.
Подключите ESP32 по USB и нажмите значок прошивки (Flash) — расширение запишет бинарник в плату.

Прошиваем плату.

Прошивка завершена.
Наконец, откройте монитор последовательного порта — значок в той же строке состояния. Именно в него наш код выводит MAC-адреса.

Открываем монитор порта.
Демонстрация#
В мониторе появятся четыре строки: заводской адрес из eFuse и три адреса, которые ESP-IDF производит от базового.

Вывод программы: базовый MAC и три производных адреса.
Обратите внимание: SoftAP- и Bluetooth-адреса отличаются от адреса станции на фиксированные смещения — потому они и идут почти подряд.
Код: подмена MAC-адреса своим#
Второй пример — установим собственный базовый MAC-адрес. Сначала предупреждение из оригинала: два устройства с одинаковым MAC-адресом в одной сети — это конфликт, поэтому не задавайте адрес, который может оказаться занят.
Впишите в переменную custom_mac нужный адрес и замените содержимое main.c на следующий код:
/*
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]);
}
}
Как работает код#
Собственный адрес#
Массив из шести байт — это и есть новый MAC-адрес, записанный в привычном порядке: слева старший байт.
// REPLACE THE NEXT VARIABLE WITH YOUR CUSTOM MAC ADDRESS
uint8_t custom_mac[6] = {0x32, 0xA1, 0xB2, 0xC3, 0xD4, 0xE5};
Установка базового адреса#
Функция esp_base_mac_addr_set() задаёт новый базовый MAC-адрес. Все производные от него адреса — Wi-Fi-станции, SoftAP, Bluetooth — будут вычисляться уже от нового значения.
// 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));
}
Вызвать её нужно как можно раньше — до инициализации Wi-Fi и Bluetooth, поэтому в примере это первая же инструкция в app_main(); иначе функция вернёт ошибку.
Важный момент: новый адрес живёт только в оперативной памяти и на время работы прошивки. Заводской MAC в eFuse не перезаписывается — после сброса питания плата вернётся к родному адресу.
Проверка результата#
Осталось прочитать адрес Wi-Fi-станции ещё раз и убедиться, что он сменился.
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]);
}
Демонстрация#
Прошейте плату собранным проектом и откройте монитор порта.

Станция теперь представляется заданным нами адресом.
Wi-Fi-станция показывает адрес 32:A1:B2:C3:D4:E5 — ровно тот, что мы записали в массив custom_mac.
Итоги#
В этом руководстве вы научились читать все MAC-адреса ESP32 в ESP-IDF: заводской базовый из eFuse (ESP_MAC_EFUSE_FACTORY), а также адреса Wi-Fi-станции, точки доступа и Bluetooth через esp_read_mac(). Разобрали и обратную задачу — подмену базового адреса функцией esp_base_mac_addr_set(), не забывая, что изменение живёт только до перезагрузки платы.
Что почитать дальше:
- Веб-сервер на ESP32 в ESP-IDF: управляем GPIO из браузера — наш перевод подробного руководства;
- ESP-IDF: WiFi Scanner with ESP32 — сканирование ближайших Wi-Fi-сетей;
- ESP-IDF: ADC with ESP32 — читаем аналоговые входы;
- ESP-IDF: PWM with ESP32 (LEDC) — широтно-импульсная модуляция.