ESPマイコンのプロダクト作っていても、MACアドレスの変更までするのは稀だと思うけど、製品だとMAC見ただけで中身がESP32だとわかってしまうのもあんまり良くない。
ホビーでやっているなら関係ないけど。

ということで、独自のMACアドレスを割り振って設定する。
あ、その前に、電波出ちゃうかもしれないので、チップアンテナをむしっておく。

準備OK。
MACアドレスは機器に対して一意であることが義務付けられているので、重複して利用することは許されていない。全世界で管理されているので、勝手につけていいものではない。
MACアドレスはIEEEが管理しているので、持っていない場合は支給を受ける必要がある。
もちろん無料ではない。割り当てブロックが大中小とあって、手数料も異なる。
IEEE Registration Authorityでアカウント作成してブロックを申請し、支払いを行うと割り当て通知が連絡される。割り当てられたブロックの運用ルールは割り当てを受けた組織が独自に決めることができる。
普通はあんまり使うことはないのだけど、IEEEではローカル管理MAC(LAA)というルールも決められている。
MACのU/Lビット(ローカル管理)=1にしたアドレスは、IEEEのOUI割当を使わずに管理下で自由に使うことができる。閉域の試作であれば、これを使うのがよさそう。
LAAでユニキャストにしたいなら、先頭オクテットはbit0=0(ユニキャスト)、bit1=1(ローカル)になっていればよい。先頭が02になるという感じ。たとえば、「02:11:22:33:44:55」とかでいいかな。今回はこれを使って設定してみる。
ESP32-S3のカスタムMACについてのドキュメントはこのへん。
Burn Custom Mac - ESP32-S3 - — esptool latest documentation
https://docs.espressif.com/projects/esptool/en/latest/esp32s3/espefuse/burn-custom-mac-cmd.html
ESP-IDF/esptoolを入れた時点でeFuseの書き換えツールはインストールされている。
以下、macOSでの場合。
Windowsなどではespefuseのexeがあるかもしれない。(未確認)
オプションで渡すシリアルデバイス名は、環境に合わせて変更すること。 通常であれば、/dev/cu.usbmodem1101みたいな感じで認識している。
書き込まれたカスタムMACアドレスの確認する。未焼きなら00:00:00:00:00:00と表示される。
% espefuse.py --chip esp32s3 --port /dev/cu.usbmodem1101 get_custom_mac
espefuse.py v4.11.dev1
Connecting...
=== Run "get_custom_mac" command ===
Custom MAC Address: 00:00:00:00:00:00 (OK)
MACアドレスの書き込みをおこなう。eFuseに焼き込むので、一回だけしか書けないし、書いたあとに変更もできないので注意。
書き込み(不可逆注意):
% espefuse.py --chip esp32s3 --port /dev/cu.usbmodem1101 burn_custom_mac 02:11:22:33:44:55
espefuse.py v4.11.dev1
Connecting...
=== Run "burn_custom_mac" command ===
- 'CUSTOM_MAC' (Custom MAC) 0x000000000000 -> 0x554433221102
Check all blocks for burn...
idx, BLOCK_NAME, Conclusion
[03] BLOCK_USR_DATA is empty, will burn the new value
.
This is an irreversible operation!
Type 'BURN' (all capitals) to continue.
BURNの入力を求められるので入力する。起動時に--do-not-confirmを追加しておくと、確認を省くことができる。
BURN BLOCK3 - OK (write block == read block)
Reading updated efuses...
Custom MAC Address: 02:11:22:33:44:55 (OK)
Successful
成功したと表示されるけど、確認する。
% espefuse.py --chip esp32s3 --port /dev/cu.usbmodem1101 get_custom_mac
espefuse.py v4.11.dev1
Connecting...
=== Run "get_custom_mac" command ===
Custom MAC Address: 02:11:22:33:44:55 (OK)
書けているのが確認できた。
できないのはわかっているけど、上書きしてみる。
% espefuse.py --chip esp32s3 --port /dev/cu.usbmodem1101 burn_custom_mac 02:11:22:33:44:55 --do-not-confirm
espefuse.py v4.11.dev1
Connecting...
=== Run "burn_custom_mac" command ===
- 'CUSTOM_MAC' (Custom MAC) 0x554433221102 -> 0x554433221102
The same value for CUSTOM_MAC is already burned. Do not change the efuse.
Check all blocks for burn...
idx, BLOCK_NAME, Conclusion
Nothing to burn, see messages above.
Custom MAC Address: 02:11:22:33:44:55 (OK)
Successful
同じ値だから成功と出るのかな?
書くMACを変えてテスト。
% espefuse.py --chip esp32s3 --port /dev/cu.usbmodem1101 burn_custom_mac 02:11:22:33:44:66 --do-not-confirm
espefuse.py v4.11.dev1
Connecting...
=== Run "burn_custom_mac" command ===
- 'CUSTOM_MAC' (Custom MAC) 0x554433221102 -> 0x664433221102
A fatal error occurred: New value contains some bits that cannot be cleared (value will be 0x774433221102)
なるほど、こんなエラーになるのね。
んで、カスタムMACの使い方を調べる。
起動後に明確に指定しないとEspressifのMACがデフォルトで使われるようだ。めんどくさい。こういうのはbootloaderのほうで対処してほしいのだけど。
1. Wi-Fi/BLE初期化より前にesp_efuse_mac_get_custom()でCUSTOM_MACの値をを読む。
2. esp_base_mac_addr_set()で設定する (edited)
一応、ネイティブMACとカスタムMACを切り替えて起動してみるサンプルコードを作っておく。AIが書くのだけどw
% idf.py create-project custom_mac
Executing action: create-project
The project was created in /Users/kinneko/Downloads/esp/supermini/custom_mac
% cd custom_mac
% idf.py set-target esp32s3
% vi main/custom_mac.c
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_err.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "esp_mac.h"
#include "esp_efuse.h"
#include "esp_efuse_table.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_wifi.h"
static const char *TAG = "mac_toggle_wifi";
static void print_mac(const char *label, const uint8_t mac[6]) {
ESP_LOGI(TAG, "%s %02X:%02X:%02X:%02X:%02X:%02X",
label, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
static bool is_all_zero(const uint8_t mac[6]) {
for (int i = 0; i < 6; i++) if (mac[i] != 0) return false;
return true;
}
static void wifi_start_sta_and_print_macs(void) {
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
// STA netifを作る(イベントループが必要)
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_start());
// STAのMAC表示(baseから派生した実MAC)
uint8_t sta_mac[6] = {0};
ESP_ERROR_CHECK(esp_wifi_get_mac(WIFI_IF_STA, sta_mac));
print_mac("WIFI_STA:", sta_mac);
// ついでにAPも見たい場合は以下(不要なら消してOK)
uint8_t ap_mac[6] = {0};
ESP_ERROR_CHECK(esp_wifi_get_mac(WIFI_IF_AP, ap_mac));
print_mac("WIFI_AP :", ap_mac);
}
void app_main(void) {
// 1) NVS init(Wi-FiもNVSを使うので先に)
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ESP_ERROR_CHECK(nvs_flash_init());
} else {
ESP_ERROR_CHECK(err);
}
// 2) フラグ読込(0=NATIVE, 1=CUSTOM)
nvs_handle_t h;
ESP_ERROR_CHECK(nvs_open("maccfg", NVS_READWRITE, &h));
uint8_t use_custom = 0;
err = nvs_get_u8(h, "use_custom", &use_custom);
if (err == ESP_ERR_NVS_NOT_FOUND) {
use_custom = 0; // 初回はNATIVE
} else {
ESP_ERROR_CHECK(err);
}
// 3) eFuseからMAC取得
uint8_t native_mac[6] = {0};
uint8_t custom_mac[6] = {0};
ESP_ERROR_CHECK(esp_efuse_mac_get_default(native_mac));
ESP_ERROR_CHECK(esp_efuse_mac_get_custom(custom_mac)); // 未焼きなら 00:00:...
bool custom_available = !is_all_zero(custom_mac);
// 4) ベースMACを設定(Wi-Fi初期化より前)
const char *mode_str = "NATIVE";
if (use_custom) {
if (!custom_available) {
ESP_LOGW(TAG, "CUSTOM_MAC is not programmed; falling back to NATIVE");
ESP_ERROR_CHECK(esp_base_mac_addr_set(native_mac));
mode_str = "NATIVE";
} else {
ESP_ERROR_CHECK(esp_base_mac_addr_set(custom_mac));
mode_str = "CUSTOM";
}
} else {
ESP_ERROR_CHECK(esp_base_mac_addr_set(native_mac));
mode_str = "NATIVE";
}
// 5) 現在のベースMAC・各MACを表示
uint8_t base_mac[6] = {0};
ESP_ERROR_CHECK(esp_base_mac_addr_get(base_mac));
ESP_LOGI(TAG, "Boot mode: %s (flag use_custom=%u)", mode_str, (unsigned)use_custom);
print_mac("BASE :", base_mac);
print_mac("NATIVE :", native_mac);
print_mac("CUSTOM :", custom_mac);
// 6) Wi-Fi起動してSTAのMACを表示
wifi_start_sta_and_print_macs();
// 7) 次回起動で切り替える(2択のみ:NATIVE<->CUSTOM)
uint8_t next_use_custom = (use_custom == 0) ? 1 : 0;
ESP_ERROR_CHECK(nvs_set_u8(h, "use_custom", next_use_custom));
ESP_ERROR_CHECK(nvs_commit(h));
nvs_close(h);
ESP_LOGI(TAG, "Next boot will try: %s", next_use_custom ? "CUSTOM" : "NATIVE");
// 8) 30秒周期で再起動
while (1) {
ESP_LOGI(TAG, "Restarting in 30 seconds...");
vTaskDelay(pdMS_TO_TICKS(30000));
esp_restart();
}
}
実行。
% idf.py -p /dev/cu.usbmodem1101 build flash monitor
(snip)
I (372) mac_toggle_wifi: Boot mode: CUSTOM (flag use_custom=1)
I (372) mac_toggle_wifi: BASE : 02:11:22:33:44:55
I (372) mac_toggle_wifi: NATIVE : 20:6E:F1:B0:ED:DC
I (382) mac_toggle_wifi: CUSTOM : 02:11:22:33:44:55
(snip)
I (522) mac_toggle_wifi: WIFI_STA: 02:11:22:33:44:55
I (522) mac_toggle_wifi: WIFI_AP : 02:11:22:33:44:56
I (532) mac_toggle_wifi: Next boot will try: NATIVE
I (532) mac_toggle_wifi: Restarting in 30 seconds...
(snip)
I (372) mac_toggle_wifi: Boot mode: NATIVE (flag use_custom=0)
I (372) mac_toggle_wifi: BASE : 20:6E:F1:B0:ED:DC
I (372) mac_toggle_wifi: NATIVE : 20:6E:F1:B0:ED:DC
I (382) mac_toggle_wifi: CUSTOM : 02:11:22:33:44:55
(snip)
I (522) mac_toggle_wifi: WIFI_STA: 20:6E:F1:B0:ED:DC
I (522) mac_toggle_wifi: WIFI_AP : 20:6E:F1:B0:ED:DD
I (532) mac_toggle_wifi: Next boot will try: CUSTOM
I (532) mac_toggle_wifi: Restarting in 30 seconds...
うまくいっている様子。
オリジナル投稿:
ESP32-S3でカスタムMACを使う|kinneko|pixivFANBOX
https://kinneko.fanbox.cc/posts/11399434



