diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..87e0713 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "espnet/esp-idf"] + path = espnet/esp-idf + url = https://github.com/espressif/esp-idf.git diff --git a/espnet/ap.go b/espnet/ap.go new file mode 100644 index 0000000..872e6f4 --- /dev/null +++ b/espnet/ap.go @@ -0,0 +1,40 @@ +package espnet + +/* +#cgo CFLAGS: -DCONFIG_IDF_TARGET_ESP32C3 +#cgo CFLAGS: -Iinclude +#cgo CFLAGS: -Iesp-idf/components/esp_common/include +#cgo CFLAGS: -Iesp-idf/components/esp_event/include +#cgo CFLAGS: -Iesp-idf/components/esp_netif/include +#cgo CFLAGS: -Iesp-idf/components/esp_wifi/include + +#cgo LDFLAGS: -Lesp-idf/components/esp_wifi/lib/esp32c3 -lnet80211 -lpp -lphy -lmesh -lcore +#cgo LDFLAGS: -Tesp-idf/components/esp_rom/esp32c3/ld/esp32c3.rom.ld + +#include "esp_private/wifi.h" +#include "esp_wifi_types.h" +#include "espnet.h" +*/ +import "C" + +type ESPWiFi struct { +} + +var WiFi = &ESPWiFi{} + +type Config struct { +} + +var internalConfig = C.wifi_init_config_t{ + osi_funcs: &C.g_wifi_osi_funcs, +} + +func (wifi ESPWiFi) Configure(config Config) error { + return makeError(C.esp_wifi_init_internal(&internalConfig)) +} + +func (wifi ESPWiFi) AccessPointMAC() ([6]byte, error) { + var mac [6]byte + errCode := C.esp_wifi_get_mac(C.ESP_IF_WIFI_AP, &mac[0]) + return mac, makeError(errCode) +} diff --git a/espnet/error.go b/espnet/error.go new file mode 100644 index 0000000..7d0fff4 --- /dev/null +++ b/espnet/error.go @@ -0,0 +1,103 @@ +package espnet + +// #include +// #include +import "C" + +// Wrapper for C.esp_err_t. Don't convert a C.esp_err_t to an Error type, +// instead use makeError to handle ESP_OK. +type Error C.esp_err_t + +// makeError converts a C.esp_err_t into an error or nil depending on whether +// errCode indicates an error or not. +func makeError(errCode C.esp_err_t) error { + if errCode == C.ESP_OK { + return nil + } + return Error(errCode) +} + +func (e Error) Error() string { + switch { + case e < C.ESP_ERR_WIFI_BASE: + // esp-idf/components/esp_common/include/esp_err.h + switch e { + case C.ESP_OK: + return "OK" // not an error + case C.ESP_FAIL: + return "ESP FAIL" + case C.ESP_ERR_NO_MEM: + return "Out of memory" + case C.ESP_ERR_INVALID_ARG: + return "Invalid argument" + case C.ESP_ERR_INVALID_STATE: + return "Invalid state" + case C.ESP_ERR_INVALID_SIZE: + return "Invalid size" + case C.ESP_ERR_NOT_FOUND: + return "Requested resource not found" + case C.ESP_ERR_NOT_SUPPORTED: + return "Operation or feature not supported" + case C.ESP_ERR_TIMEOUT: + return "Operation timed out" + case C.ESP_ERR_INVALID_RESPONSE: + return "Received response was invalid" + case C.ESP_ERR_INVALID_CRC: + return "CRC or checksum was invalid" + case C.ESP_ERR_INVALID_VERSION: + return "Version was invalid" + case C.ESP_ERR_INVALID_MAC: + return "MAC address was invalid" + default: + return "Unknown error" + } + case e >= C.ESP_ERR_WIFI_BASE && e < C.ESP_ERR_MESH_BASE: + // esp-idf/components/esp_wifi/include/esp_wifi.h + switch e { + case C.ESP_ERR_WIFI_NOT_INIT: + return "WiFi driver was not installed by esp_wifi_init" + case C.ESP_ERR_WIFI_NOT_STARTED: + return "WiFi driver was not started by esp_wifi_start" + case C.ESP_ERR_WIFI_NOT_STOPPED: + return "WiFi driver was not stopped by esp_wifi_stop" + case C.ESP_ERR_WIFI_IF: + return "WiFi interface error" + case C.ESP_ERR_WIFI_MODE: + return "WiFi mode error" + case C.ESP_ERR_WIFI_STATE: + return "WiFi internal state error" + case C.ESP_ERR_WIFI_CONN: + return "WiFi internal control block of station or soft-AP error" + case C.ESP_ERR_WIFI_NVS: + return "WiFi internal NVS module error" + case C.ESP_ERR_WIFI_MAC: + return "MAC address is invalid" + case C.ESP_ERR_WIFI_SSID: + return " SSID is invalid" + case C.ESP_ERR_WIFI_PASSWORD: + return "Password is invalid" + case C.ESP_ERR_WIFI_TIMEOUT: + return "Timeout error" + case C.ESP_ERR_WIFI_WAKE_FAIL: + return "WiFi is in sleep state(RF closed) and wakeup fail" + case C.ESP_ERR_WIFI_WOULD_BLOCK: + return "The caller would block" + case C.ESP_ERR_WIFI_NOT_CONNECT: + return "Station still in disconnect status" + case C.ESP_ERR_WIFI_POST: + return "Failed to post the event to WiFi task" + case C.ESP_ERR_WIFI_INIT_STATE: + return "Invalid WiFi state when init/deinit is called" + case C.ESP_ERR_WIFI_STOP_STATE: + return "Returned when WiFi is stopping" + case C.ESP_ERR_WIFI_NOT_ASSOC: + return "The WiFi connection is not associated" + case C.ESP_ERR_WIFI_TX_DISALLOW: + return "The WiFi TX is disallowed" + default: + return "Other WiFi error" + } + default: + return "Other error" + } +} diff --git a/espnet/esp-idf b/espnet/esp-idf new file mode 160000 index 0000000..c9646ff --- /dev/null +++ b/espnet/esp-idf @@ -0,0 +1 @@ +Subproject commit c9646ff0beffc86d2c6d1bfbad34da16e328e0e3 diff --git a/espnet/espnet.c b/espnet/espnet.c new file mode 100644 index 0000000..7f1ad85 --- /dev/null +++ b/espnet/espnet.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include "espnet.h" + +// OS adapter functions. +// See: esp-idf/components/esp_wifi/include/esp_private/wifi_os_adapter.h +wifi_osi_funcs_t g_wifi_osi_funcs = { + ._version = ESP_WIFI_OS_ADAPTER_VERSION, + // TODO: define these functions. + ._magic = ESP_WIFI_OS_ADAPTER_MAGIC, +}; + +// This is a string constant that is used all over ESP-IDF and is also used by +// libnet80211.a. The main purpose is to be a fixed pointer that can be compared +// against etc. +const char *WIFI_EVENT = "WIFI_EVENT"; + +// Required by libphy.a +int phy_printf(const char *format, ...) { + va_list args; + va_start(args, format); + printf("phy: "); + int res = vprintf(format, args); + va_end(args); + return res; +} + +// Required by libpp.a +int pp_printf(const char *format, ...) { + va_list args; + va_start(args, format); + printf("pp: "); + int res = vprintf(format, args); + va_end(args); + return res; +} + +// Required by libnet80211.a +int net80211_printf(const char *format, ...) { + va_list args; + va_start(args, format); + printf("net80211: "); + int res = vprintf(format, args); + va_end(args); + return res; +} + +// Source: esp-idf/components/wpa_supplicant/src/utils/common.c +static int hex2num(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; +} + +// Source: esp-idf/components/wpa_supplicant/src/utils/common.c +int hex2byte(const char *hex) +{ + int a, b; + a = hex2num(*hex++); + if (a < 0) + return -1; + b = hex2num(*hex++); + if (b < 0) + return -1; + return (a << 4) | b; +} + +// Source: esp-idf/components/wpa_supplicant/src/utils/common.c +/** + * hexstr2bin - Convert ASCII hex string into binary data + * @hex: ASCII hex string (e.g., "01ab") + * @buf: Buffer for the binary data + * @len: Length of the text to convert in bytes (of buf); hex will be double + * this size + * Returns: 0 on success, -1 on failure (invalid hex string) + */ +int hexstr2bin(const char *hex, uint8_t *buf, size_t len) +{ + size_t i; + int a; + const char *ipos = hex; + uint8_t *opos = buf; + + for (i = 0; i < len; i++) { + a = hex2byte(ipos); + if (a < 0) + return -1; + *opos++ = a; + ipos += 2; + } + return 0; +} diff --git a/espnet/espnet.h b/espnet/espnet.h new file mode 100644 index 0000000..0a1f930 --- /dev/null +++ b/espnet/espnet.h @@ -0,0 +1,4 @@ +#include +#include "esp_private/wifi_os_adapter.h" + +extern wifi_osi_funcs_t g_wifi_osi_funcs; diff --git a/espnet/include/sdkconfig.h b/espnet/include/sdkconfig.h new file mode 100644 index 0000000..e69de29 diff --git a/examples/espnet/main.go b/examples/espnet/main.go new file mode 100644 index 0000000..9b94ed9 --- /dev/null +++ b/examples/espnet/main.go @@ -0,0 +1,20 @@ +package main + +import "tinygo.org/x/drivers/espnet" + +func main() { + err := espnet.WiFi.Configure(espnet.Config{}) + if err != nil { + println("failed to configure:", err.Error()) + } + mac, err := espnet.WiFi.AccessPointMAC() + if err != nil { + println("failed to read MAC address:", err.Error()) + return + } + print("MAC address:") + for _, b := range mac { + print(" ", b) + } + println() +}