diff --git a/doc/src/programmers-guide.md b/doc/src/programmers-guide.md index ed290a08e..c92ab4e25 100644 --- a/doc/src/programmers-guide.md +++ b/doc/src/programmers-guide.md @@ -968,6 +968,13 @@ The return type is a list of tuples, each of which contains the partition id (as For information about the encoding of partition types and sub-types, see the IDF SDK partition [type definitions](https://docs.espressif.com/projects/esp-idf/en/v4.4.5/esp32/api-reference/storage/spi_flash.html?highlight=esp_partition_get#id13). +* `esp:get_mac/1` + +The `esp:get_mac/1` function can be used to retrieve the network Media Access Control ([MAC](https://en.wikipedia.org/wiki/MAC_address)) address for a given interface, `wifi_sta` or `wifi_softap`. The return value is a 6-byte binary, in accordance with the [IEEE 802](https://en.wikipedia.org/wiki/IEEE_802) family of specifications. + + %% erlang + MacAddress = esp:get_mac(wifi_sta) + ## Peripherals The AtomVM virtual machine and libraries support APIs for interfacing with peripheral devices connected to the ESP32. This section provides information about these APIs. diff --git a/libs/eavmlib/src/esp.erl b/libs/eavmlib/src/esp.erl index 3be6af850..2a872bb92 100644 --- a/libs/eavmlib/src/esp.erl +++ b/libs/eavmlib/src/esp.erl @@ -40,7 +40,8 @@ partition_list/0, rtc_slow_get_binary/0, rtc_slow_set_binary/1, - freq_hz/0 + freq_hz/0, + get_mac/1 ]). -type esp_reset_reason() :: @@ -84,6 +85,9 @@ esp_partition_props() }. +-type interface() :: wifi_sta | wifi_softap. +-type mac() :: binary(). + -define(ATOMVM_NVS_NS, atomvm). %%----------------------------------------------------------------------------- @@ -287,3 +291,16 @@ rtc_slow_set_binary(Bin) when is_binary(Bin) -> -spec freq_hz() -> non_neg_integer(). freq_hz() -> erlang:nif_error(undefined). + +%%----------------------------------------------------------------------------- +%% @param Interface the ESP32 network interface +%% @returns The network MAC address of the specified interface +%% @doc Return the network MAC address of the specified interface. +%% +%% The mac address is returned as a 6-byte binary, per the +%% IEEE 802 family of specifications. +%% @end +%%----------------------------------------------------------------------------- +-spec get_mac(Interface :: interface()) -> mac(). +get_mac(_Interface) -> + erlang:nif_error(undefined). diff --git a/src/platforms/esp32/components/avm_sys/platform_nifs.c b/src/platforms/esp32/components/avm_sys/platform_nifs.c index 088caec3e..34d40f983 100644 --- a/src/platforms/esp32/components/avm_sys/platform_nifs.c +++ b/src/platforms/esp32/components/avm_sys/platform_nifs.c @@ -31,6 +31,8 @@ #include "platform_defaultatoms.h" #include "term.h" +#include "esp_log.h" +#include "esp_mac.h" #include #include #include @@ -62,6 +64,8 @@ //#define ENABLE_TRACE #include "trace.h" +#define TAG "atomvm" + #define MD5_DIGEST_LENGTH 16 static const char *const esp_rst_unknown_atom = "\xF" "esp_rst_unknown"; @@ -77,6 +81,18 @@ static const char *const esp_rst_brownout = "\x10" "esp_rst_brownout"; static const char *const esp_rst_sdio = "\xC" "esp_rst_sdio"; // 123456789ABCDEF01 +enum NetworkInterface { + WifiSTAInterface, + WifiSoftAPInterface, + InvalidInterface +}; + +static const AtomStringIntPair interface_table[] = { + { ATOM_STR("\x8", "wifi_sta"), WifiSTAInterface }, + { ATOM_STR("\xB", "wifi_softap"), WifiSoftAPInterface }, + SELECT_INT_DEFAULT(InvalidInterface) +}; + // // NIFs // @@ -450,8 +466,42 @@ static term nif_atomvm_platform(Context *ctx, int argc, term argv[]) return ESP32_ATOM; } +static term nif_esp_get_mac(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + + GlobalContext *global = ctx->global; + + int network_interface = interop_atom_term_select_int(interface_table, argv[0], global); + esp_mac_type_t interface; + switch (network_interface) { + case WifiSTAInterface: + interface = ESP_MAC_WIFI_STA; + break; + case WifiSoftAPInterface: + interface = ESP_MAC_WIFI_SOFTAP; + break; + default: + // TODO add support for BT, ETH, etc + RAISE_ERROR(BADARG_ATOM); + } + + uint8_t mac[6]; + esp_err_t err = esp_read_mac(mac, interface); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Unable to read mac. err=%i", err); + RAISE_ERROR(BADARG_ATOM); + } + + if (UNLIKELY(memory_ensure_free(ctx, term_binary_heap_size(6)) != MEMORY_GC_OK)) { + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + return term_from_literal_binary(mac, 6, &ctx->heap, ctx->global); +} + // -// NIF structures and distpatch +// NIF structures and dispatch // static const struct Nif esp_random_nif = @@ -526,6 +576,11 @@ static const struct Nif atomvm_platform_nif = .base.type = NIFFunctionType, .nif_ptr = nif_atomvm_platform }; +static const struct Nif esp_get_mac_nif = +{ + .base.type = NIFFunctionType, + .nif_ptr = nif_esp_get_mac +}; const struct Nif *platform_nifs_get_nif(const char *nifname) { @@ -591,6 +646,10 @@ const struct Nif *platform_nifs_get_nif(const char *nifname) TRACE("Resolved platform nif %s ...\n", nifname); return &atomvm_platform_nif; } + if (strcmp("esp:get_mac/1", nifname) == 0) { + TRACE("Resolved platform nif %s ...\n", nifname); + return &esp_get_mac_nif; + } const struct Nif *nif = nif_collection_resolve_nif(nifname); if (nif) { return nif;