#include "espwifistack.h" #include "sdkconfig.h" #define LOG_LOCAL_LEVEL CONFIG_LOG_LOCAL_LEVEL_WIFI_STACK // system includes #include #include #include #include #include // esp-idf includes #include #include #include #if LWIP_IPV6 && LWIP_IPV6_DHCP6_STATELESS #include #include #endif #ifdef CONFIG_ETH_ENABLED #include #include #include #include #if CONFIG_IDF_TARGET_ESP32 #include #include //#include //#include #endif #else #include #include #include #endif #ifdef CONFIG_ETH_ENABLED // Arduino includes #include #endif // 3rdparty lib includes #include // local includes #include "strutils.h" #include "delayedconstruction.h" #include "wrappers/event_group.h" #include "wrappers/queue.h" #include "strutils.h" #include "tickchrono.h" #include "cpputils.h" #include "cleanuphelper.h" using namespace std::chrono_literals; namespace wifi_stack { namespace { constexpr const char * const TAG = "WIFI_STACK"; std::optional lastStatus; WiFiState _wifiState; std::optional _lastStaSwitchedFromConnected; std::optional _lastStaSwitchedToConnected; std::optional lastScanStarted; espchrono::millis_clock::time_point _lastConnect; std::optional last_sta_static_ip; static_dns_config last_sta_static_dns; ap_config last_ap_config; #ifdef CONFIG_ETH_ENABLED std::optional last_eth_static_ip; static_dns_config last_eth_static_dns; #endif std::string lastWifisChecksum; bool wasReallyScanning{}; constexpr auto AP_STARTED_BIT = BIT0; constexpr auto AP_HAS_IP6_BIT = BIT1; constexpr auto AP_HAS_CLIENT_BIT = BIT2; constexpr auto STA_STARTED_BIT = BIT3; constexpr auto STA_CONNECTED_BIT = BIT4; constexpr auto STA_HAS_IP_BIT = BIT5; constexpr auto STA_HAS_IP6_BIT = BIT6; #ifdef CONFIG_ETH_ENABLED constexpr auto ETH_STARTED_BIT = BIT7; constexpr auto ETH_CONNECTED_BIT = BIT8; constexpr auto ETH_HAS_IP_BIT = BIT9; constexpr auto ETH_HAS_IP6_BIT = BIT10; #endif constexpr auto WIFI_SCANNING_BIT = BIT11; constexpr auto WIFI_SCAN_DONE_BIT= BIT12; constexpr auto WIFI_DNS_IDLE_BIT = BIT13; constexpr auto WIFI_DNS_DONE_BIT = BIT14; // generic cpputils::DelayedConstruction wifi_event_queue; cpputils::DelayedConstruction wifi_event_group; } // namespace esp_netif_t* esp_netifs[ESP_IF_MAX] = {NULL, NULL, NULL}; namespace { bool lowLevelInitDone = false; bool _esp_wifi_started = false; bool _long_range = false; wifi_ps_type_t _sleepEnabled = WIFI_PS_MIN_MODEM; // sta std::atomic _sta_status{WiFiStaStatus::WL_NO_SHIELD}; std::optional _wifiConnectFailFlag; uint8_t _wifiConnectFailCounter{}; // scan std::optional scanStarted; espchrono::milliseconds32 scanTimeout = 10s; std::optional _scanResult; bool scanResultChangedFlag{}; struct ConnectPlanItem { wifi_entry config; uint8_t channel; wifi_auth_mode_t authmode; int8_t rssi; mac_t bssid; }; std::vector connectPlan; } // namespace const WiFiState &wifiStateMachineState{_wifiState}; cpputils::Signal<> scanResultChanged{}; const std::optional &lastStaSwitchedFromConnected{_lastStaSwitchedFromConnected}; const std::optional &lastStaSwitchedToConnected{_lastStaSwitchedToConnected}; namespace { #define WifiEventIdValues(x) \ x(WIFI_READY) \ x(WIFI_SCAN_DONE) \ x(WIFI_STA_START) \ x(WIFI_STA_STOP) \ x(WIFI_STA_CONNECTED) \ x(WIFI_STA_DISCONNECTED) \ x(WIFI_STA_AUTHMODE_CHANGE) \ x(WIFI_STA_GOT_IP) \ x(WIFI_STA_GOT_IP6) \ x(WIFI_STA_LOST_IP) \ x(WIFI_AP_START) \ x(WIFI_AP_STOP) \ x(WIFI_AP_STACONNECTED) \ x(WIFI_AP_STADISCONNECTED) \ x(WIFI_AP_STAIPASSIGNED) \ x(WIFI_AP_PROBEREQRECVED) \ x(WIFI_AP_GOT_IP6) \ x(ETH_START) \ x(ETH_STOP) \ x(ETH_CONNECTED) \ x(ETH_DISCONNECTED) \ x(ETH_GOT_IP) \ x(ETH_GOT_IP6) \ x(WPS_ER_SUCCESS) \ x(WPS_ER_FAILED) \ x(WPS_ER_TIMEOUT) \ x(WPS_ER_PIN) \ x(WPS_ER_PBC_OVERLAP) \ x(MAX) //#ifdef SMARTCONFIG // x(SC_SCAN_DONE) // x(SC_FOUND_CHANNEL) // x(SC_GOT_SSID_PSWD) // x(SC_SEND_ACK_DONE) //#endif //#ifdef PROVISIONING // x(PROV_INIT) // x(PROV_DEINIT) // x(PROV_START) // x(PROV_END) // x(PROV_CRED_RECV) // x(PROV_CRED_FAIL) // x(PROV_CRED_SUCCESS) //#endif DECLARE_TYPESAFE_ENUM(WifiEventId, : int32_t, WifiEventIdValues) IMPLEMENT_TYPESAFE_ENUM(WifiEventId, : int32_t, WifiEventIdValues) static_assert(sizeof(WifiEventId) == 4); struct WifiEvent { WifiEventId event_id; union { wifi_event_sta_scan_done_t wifi_scan_done; wifi_event_sta_authmode_change_t wifi_sta_authmode_change; wifi_event_sta_connected_t wifi_sta_connected; wifi_event_sta_disconnected_t wifi_sta_disconnected; wifi_event_sta_wps_er_pin_t wps_er_pin; wifi_event_sta_wps_fail_reason_t wps_fail_reason; wifi_event_ap_probe_req_rx_t wifi_ap_probereqrecved; wifi_event_ap_staconnected_t wifi_ap_staconnected; wifi_event_ap_stadisconnected_t wifi_ap_stadisconnected; ip_event_ap_staipassigned_t wifi_ap_staipassigned; ip_event_got_ip_t got_ip; ip_event_got_ip6_t got_ip6; #ifdef SMARTCONFIG smartconfig_event_got_ssid_pswd_t sc_got_ssid_pswd; #endif #ifdef CONFIG_ETH_ENABLED esp_eth_handle_t eth_connected; #endif wifi_sta_config_t prov_cred_recv; #ifdef PROVISIONING wifi_prov_sta_fail_reason_t prov_fail_reason; #endif }; }; const char * system_event_reasons[] = { "UNSPECIFIED", "AUTH_EXPIRE", "AUTH_LEAVE", "ASSOC_EXPIRE", "ASSOC_TOOMANY", "NOT_AUTHED", "NOT_ASSOCED", "ASSOC_LEAVE", "ASSOC_NOT_AUTHED", "DISASSOC_PWRCAP_BAD", "DISASSOC_SUPCHAN_BAD", "UNSPECIFIED", "IE_INVALID", "MIC_FAILURE", "4WAY_HANDSHAKE_TIMEOUT", "GROUP_KEY_UPDATE_TIMEOUT", "IE_IN_4WAY_DIFFERS", "GROUP_CIPHER_INVALID", "PAIRWISE_CIPHER_INVALID", "AKMP_INVALID", "UNSUPP_RSN_IE_VERSION", "INVALID_RSN_IE_CAP", "802_1X_AUTH_FAILED", "CIPHER_SUITE_REJECTED", "BEACON_TIMEOUT", "NO_AP_FOUND", "AUTH_FAIL", "ASSOC_FAIL", "HANDSHAKE_TIMEOUT", "CONNECTION_FAIL" }; #define reason2str(r) ((r>176)?system_event_reasons[r-176]:system_event_reasons[r-1]) #ifdef CONFIG_ETH_ENABLED #ifndef ETH_PHY_ADDR #define ETH_PHY_ADDR 0 #endif #ifndef ETH_PHY_TYPE #define ETH_PHY_TYPE ETH_PHY_LAN8720 #endif #ifndef ETH_PHY_POWER #define ETH_PHY_POWER -1 #endif #ifndef ETH_PHY_MDC #define ETH_PHY_MDC 23 #endif #ifndef ETH_PHY_MDIO #define ETH_PHY_MDIO 18 #endif #ifndef ETH_CLK_MODE #define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN #endif enum eth_clock_mode_t { ETH_CLOCK_GPIO0_IN, ETH_CLOCK_GPIO0_OUT, ETH_CLOCK_GPIO16_OUT, ETH_CLOCK_GPIO17_OUT }; enum eth_phy_type_t { ETH_PHY_LAN8720, ETH_PHY_TLK110, ETH_PHY_RTL8201, ETH_PHY_DP83848, ETH_PHY_DM9051, ETH_PHY_KSZ8041, /* ETH_PHY_KSZ8081, */ ETH_PHY_MAX }; #define ETH_PHY_IP101 ETH_PHY_TLK110 bool eth_initialized{}; bool eth_started{}; eth_clock_mode_t eth_clock_mode{ETH_CLK_MODE}; esp_eth_handle_t eth_handle{}; #endif int wifi_set_status_bits(int bits); int wifi_clear_status_bits(int bits); int wifi_get_status_bits(); int wifi_wait_status_bits(int bits, espcpputils::ticks timeout); esp_err_t wifi_set_esp_interface_ip(esp_interface_t interface, const std::optional &ip); esp_err_t wifi_set_esp_interface_dns(esp_interface_t interface, const static_dns_config &dns); wifi_mode_t wifi_get_mode(); esp_err_t wifi_set_mode(wifi_mode_t m, const config &config); esp_err_t wifi_enable_sta(bool enable, const config &config); esp_err_t wifi_enable_ap(bool enable, const config &config); template size_t copyStrToBuf(uint8_t (&buf)[LENGTH], std::string_view str); wifi_config_t make_ap_config(const ap_config &ap_config); esp_err_t wifi_set_ap_config(const config &config, const ap_config &ap_config); void set_sta_status(WiFiStaStatus status); void wifi_scan_done(); esp_err_t wifi_sta_disconnect(const config &config, bool wifioff = false, bool eraseap = false); esp_err_t wifi_sta_begin(const config &config, const wifi_entry &sta_config, int32_t channel = 0, std::optional bssid = {}, bool connect = true); esp_err_t wifi_sta_restart(const config &config); void wifi_event_callback(const config &config, const WifiEvent &event); esp_err_t wifi_post_event(std::unique_ptr event); void wifi_event_cb(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data); esp_err_t wifi_start_network_event_task(const config &config); esp_err_t wifi_tcpip_init(const config &config); esp_err_t wifi_low_level_init(const config &config); esp_err_t wifi_start(); esp_err_t wifi_low_level_deinit(); esp_err_t wifi_stop(); wifi_mode_t wifi_get_mode(); esp_err_t wifi_set_mode(wifi_mode_t m, const config &config); esp_err_t wifi_set_ap_ip(const config &config, const static_ip_config &ip); wifi_config_t make_sta_config(std::string_view ssid, std::string_view password, int8_t min_rssi, std::optional bssid, uint8_t channel); esp_err_t wifi_sta_begin(const config &config, const wifi_entry &sta_config, int32_t channel, std::optional bssid, bool connect); esp_err_t wifi_sta_restart(const config &config); esp_err_t wifi_sta_disconnect(const config &config, bool wifioff, bool eraseap); std::string calculateWifisChecksum(const config &config); esp_err_t wifi_begin_scan(const config &config, bool show_hidden = false, bool passive = false, espchrono::milliseconds32 max_ms_per_chan = 300ms, uint8_t channel = 0); void setWifiState(WiFiState newWifiState); bool buildConnectPlan(const config &config, const scan_result &scanResult); bool buildConnectPlan(const config &config); bool buildConnectPlan(const config &config, const scan_result &scanResult); void handleWifiEvents(const config &config, TickType_t xTicksToWait); #ifdef CONFIG_ETH_ENABLED esp_err_t eth_begin(const config &config, uint8_t phy_addr = ETH_PHY_ADDR, int power = ETH_PHY_POWER, int mdc = ETH_PHY_MDC, int mdio = ETH_PHY_MDIO, eth_phy_type_t type = ETH_PHY_TYPE, eth_clock_mode_t clk_mode = ETH_CLK_MODE); esp_err_t eth_on_lowlevel_init_done(esp_eth_handle_t eth_handle); #if CONFIG_ETH_RMII_CLK_INPUT void emac_config_apll_clock(); #endif #endif } // namespace void init(const config &config) { ESP_LOGI(TAG, "called"); if (const auto result = wifi_set_mode(WIFI_MODE_APSTA, config); result != ESP_OK) { ESP_LOGE(TAG, "wifi_set_mode() failed with %s", esp_err_to_name(result)); return; } #ifdef CONFIG_ETH_ENABLED if (const auto result = eth_begin(config); result != ESP_OK) ESP_LOGE(TAG, "eth_begin() failed with %s", esp_err_to_name(result)); #endif espcpputils::delay(100ms); if (const auto result = wifi_enable_ap(true, config); result != ESP_OK) { ESP_LOGE(TAG, "wifi_enable_ap() failed with %s", esp_err_to_name(result)); return; } if (const auto result = wifi_set_ap_ip(config, config.ap.static_ip); result != ESP_OK) { ESP_LOGE(TAG, "wifi_set_ap_ip() failed with %s", esp_err_to_name(result)); return; } if (const auto result = wifi_set_ap_config(config, config.ap); result != ESP_OK) { ESP_LOGE(TAG, "wifi_set_ap_config() failed with %s", esp_err_to_name(result)); return; } last_ap_config = config.ap; _wifiState = WiFiState::None; lastWifisChecksum = calculateWifisChecksum(config); if (config.sta.enabled) { if (const auto result = begin_scan(config); result != ESP_OK) ESP_LOGE(TAG, "begin_scan() failed with %s", esp_err_to_name(result)); } else ESP_LOGW(TAG, "not connecting, because wifi is not enabled"); } void update(const config &config) { handleWifiEvents(config, 0); if (last_ap_config != config.ap) { ESP_LOGI(TAG, "AP settings changed, applying new config..."); if (config.ap.ssid != last_ap_config.ssid) ESP_LOGD(TAG, "new ap ssid=\"%s\" old ap ssid=\"%s\"", config.ap.ssid.c_str(), last_ap_config.ssid.c_str()); if (config.ap.key != last_ap_config.key) ESP_LOGD(TAG, "new ap key=\"%s\" old ap key=\"%s\"", config.ap.key.c_str(), last_ap_config.key.c_str()); if (const auto result = wifi_set_ap_ip(config, config.ap.static_ip); result != ESP_OK) ESP_LOGE(TAG, "wifi_set_ap_ip() failed with %s", esp_err_to_name(result)); if (const auto result = wifi_set_ap_config(config, config.ap); result != ESP_OK) ESP_LOGE(TAG, "wifi_set_ap_config() failed with %s", esp_err_to_name(result)); last_ap_config = config.ap; } if (scanResultChangedFlag) { scanResultChangedFlag = false; scanResultChanged(); } if (config.sta.enabled) { if (_wifiState == WiFiState::None) { if (get_sta_status() == WiFiStaStatus::WL_CONNECTED) { ESP_LOGI(TAG, "Unexpected connected!"); setWifiState(WiFiState::Connected); } else { const auto anyWifiConfigured = std::any_of(std::begin(config.sta.wifis), std::end(config.sta.wifis), [](const auto &entry){ return !entry.ssid.empty(); }); if (anyWifiConfigured) { if (!lastScanStarted || espchrono::ago(*lastScanStarted) >= 10min) { if (const auto result = begin_scan(config); result != ESP_OK) ESP_LOGE(TAG, "begin_scan() failed with %s", esp_err_to_name(result)); } else if (auto newChecksum = calculateWifisChecksum(config); newChecksum != lastWifisChecksum) { ESP_LOGI(TAG, "old wifis config: %s", lastWifisChecksum.c_str()); ESP_LOGI(TAG, "new wifis config: %s", newChecksum.c_str()); lastWifisChecksum = std::move(newChecksum); if (get_scan_status() == WiFiScanStatus::Finished) { if (const auto &scanResult = get_scan_result(); scanResult && !scanResult->entries.empty()) { ESP_LOGI(TAG, "wifi configs changed, building connect plan..."); buildConnectPlan(config, *scanResult); } else goto scanAnyways; } else { scanAnyways: ESP_LOGI(TAG, "wifi configs changed, triggering a new scan"); if (const auto result = begin_scan(config); result != ESP_OK) ESP_LOGE(TAG, "begin_scan() failed with %s", esp_err_to_name(result)); } } } } } if (_wifiState == WiFiState::Scanning) { if (const auto result = get_scan_status(); result != WiFiScanStatus::Scanning) { if (wasReallyScanning) { wasReallyScanning = false; } if (result == WiFiScanStatus::Finished) { if (const auto &scanResult = get_scan_result(); !scanResult) { ESP_LOGE(TAG, "unexpected no scan result"); setWifiState(WiFiState::None); lastStatus = std::nullopt; } else { ESP_LOGI(TAG, "Finished scan with %zd results", scanResult->entries.size()); if (get_sta_status() != WiFiStaStatus::WL_CONNECTED) { ESP_LOGI(TAG, "Not connected after scan, building connect plan..."); buildConnectPlan(config, *scanResult); } else { ESP_LOGI(TAG, "connected after scan, nothing to do..."); setWifiState(WiFiState::Connected); lastStatus = std::nullopt; } } } else { ESP_LOGE(TAG, "scan failed with %s", toString(result).c_str()); //setWifiState(WiFiState::None); // results in another scan } } } if (_wifiState == WiFiState::Connecting) { const auto status = get_sta_status(); if (status == WiFiStaStatus::WL_CONNECTED) { if (const auto interf = esp_netifs[ESP_IF_WIFI_STA]) { esp_netif_ip_info_t ip; if (const auto result = esp_netif_get_ip_info(interf, &ip); result == ESP_OK) { ESP_LOGI(TAG, "successfully connected WiFi! %s", toString(ip.ip).c_str()); } else { ESP_LOGI(TAG, "successfully connected WiFi!"); ESP_LOGW(TAG, "esp_netif_get_ip_info() for STA failed with %s", esp_err_to_name(result)); } } else { ESP_LOGI(TAG, "successfully connected WiFi!"); ESP_LOGW(TAG, "STA interface is null"); } setWifiState(WiFiState::Connected); lastStatus = std::nullopt; } else if (_wifiConnectFailFlag && espchrono::ago(*_wifiConnectFailFlag) >= 5s) { _wifiConnectFailFlag = std::nullopt; if (_wifiConnectFailCounter++ >= 10) { ESP_LOGE(TAG, "fail flag was set and fail count exceeded limit"); if (const auto result = wifi_sta_disconnect(config, false, true); result != ESP_OK) ESP_LOGE(TAG, "wifi_sta_disconnect() failed with %s", esp_err_to_name(result)); setWifiState(WiFiState::None); // results in another scan } else { ESP_LOGW(TAG, "fail flag was set, trying again %hhu", _wifiConnectFailCounter); _lastConnect = espchrono::millis_clock::now(); if (const auto result = wifi_sta_disconnect(config); result != ESP_OK) ESP_LOGE(TAG, "wifi_sta_disconnect() failed with %s", esp_err_to_name(result)); if (const auto result = wifi_sta_restart(config); result != ESP_OK) ESP_LOGE(TAG, "wifi_sta_restart() failed with %s", esp_err_to_name(result)); } } else { if (espchrono::ago(_lastConnect) >= 20s) { ESP_LOGW(TAG, "connecting timed out, building new connect plan... %s", toString(status).c_str()); if (const auto result = wifi_sta_disconnect(config, false, true); result != ESP_OK) ESP_LOGE(TAG, "wifi_sta_disconnect() failed with %s", esp_err_to_name(result)); buildConnectPlan(config); lastStatus = std::nullopt; } else if (!lastStatus || *lastStatus != status) { ESP_LOGI(TAG, "connecting: %s", toString(status).c_str()); lastStatus = status; } } } if (_wifiState == WiFiState::Connected) { const auto status = get_sta_status(); if (status != WiFiStaStatus::WL_CONNECTED) { ESP_LOGW(TAG, "lost connection: %s", toString(status).c_str()); if (const auto result = wifi_sta_disconnect(config, false, true); result != ESP_OK) ESP_LOGE(TAG, "wifi_sta_disconnect() failed with %s", esp_err_to_name(result)); lastWifisChecksum.clear(); setWifiState(WiFiState::None); // results in another scan } else if (const auto sta_info = get_sta_ap_info()) { const std::string_view connectedSSID{reinterpret_cast(sta_info->ssid)}; const auto iter = std::find_if(std::cbegin(config.sta.wifis), std::cend(config.sta.wifis), [&connectedSSID](const wifi_entry &entry){ return cpputils::stringEqualsIgnoreCase(entry.ssid, connectedSSID); }); if (iter == std::cend(config.sta.wifis)) { ESP_LOGI(TAG, "disconnecting, because cannot find ssid in config anymore"); lastWifisChecksum.clear(); if (const auto result = wifi_sta_disconnect(config, false, true); result != ESP_OK) ESP_LOGE(TAG, "wifi_sta_disconnect() failed with %s", esp_err_to_name(result)); ESP_LOGI(TAG, "status after disconnect: %s", toString(get_sta_status()).c_str()); setWifiState(WiFiState::None); // results in another scan } else { if (last_sta_static_ip != iter->static_ip || last_sta_static_dns != iter->static_dns) { ESP_LOGI(TAG, "STA static ip/dns config changed, applying new config"); if (const auto result = wifi_set_esp_interface_ip(ESP_IF_WIFI_STA, iter->static_ip); result != ESP_OK) { ESP_LOGE(TAG, "wifi_set_esp_interface_ip() for STA failed with %s", esp_err_to_name(result)); //return result; } else { last_sta_static_ip = iter->static_ip; if (const auto result = wifi_set_esp_interface_dns(ESP_IF_WIFI_STA, iter->static_dns); result != ESP_OK) { ESP_LOGE(TAG, "wifi_set_esp_interface_dns() for STA failed with %s", esp_err_to_name(result)); //return result; } else last_sta_static_dns = iter->static_dns; } } } } else ESP_LOGE(TAG, "get_sta_ap_info() failed with %.*s", sta_info.error().size(), sta_info.error().data()); } } else { if (_wifiState == WiFiState::Scanning) { if (get_scan_status() != WiFiScanStatus::Scanning) setWifiState(WiFiState::None); } if (!cpputils::is_in(get_sta_status(), WiFiStaStatus::WL_IDLE_STATUS, WiFiStaStatus::WL_DISCONNECTED) || !cpputils::is_in(_wifiState, WiFiState::None, WiFiState::Scanning)) { ESP_LOGI(TAG, "disconnecting, because wifi_enabled is false"); if (const auto result = wifi_sta_disconnect(config, false, true); result != ESP_OK) ESP_LOGE(TAG, "wifi_sta_disconnect() failed with %s", esp_err_to_name(result)); lastWifisChecksum.clear(); setWifiState(WiFiState::None); } } #ifdef XXXX if (eth_initialized) { bool justStarted{}; if (config.eth.enabled && !eth_started) { if (const auto result = esp_eth_start(eth_handle); result != ESP_OK) { ESP_LOGE(TAG, "esp_eth_start() failed with %s", esp_err_to_name(result)); // return result; } else { eth_started = true; justStarted = true; } } else if (!config.eth.enabled && eth_started) { if (const auto result = esp_eth_stop(eth_handle); result != ESP_OK) { ESP_LOGE(TAG, "esp_eth_stop() failed with %s", esp_err_to_name(result)); // return result; } else { eth_started = false; } } if (config.eth.enabled && eth_started && ( justStarted || last_eth_static_ip != config.eth.static_ip || last_eth_static_dns != config.eth.static_dns )) { if (!justStarted) ESP_LOGI(TAG, "ETH static ip/dns config changed, applying new config"); if (const auto result = wifi_set_esp_interface_ip(ESP_IF_ETH, config.eth.static_ip); result != ESP_OK) { ESP_LOGE(TAG, "wifi_set_esp_interface_ip() for ETH failed with %s", esp_err_to_name(result)); //return result; } else { last_eth_static_ip = config.eth.static_ip; if (const auto result = wifi_set_esp_interface_dns(ESP_IF_ETH, config.eth.static_dns); result != ESP_OK) { ESP_LOGE(TAG, "wifi_set_esp_interface_dns() for ETH failed with %s", esp_err_to_name(result)); //return result; } else last_eth_static_dns = config.eth.static_dns; } } } #endif } WiFiStaStatus get_sta_status() { return _sta_status.load(); } esp_err_t begin_scan(const config &config) { if (const auto result = wifi_begin_scan(config); result != ESP_OK) { ESP_LOGE(TAG, "wifi_begin_scan() failed with %s", esp_err_to_name(result)); return result; } lastScanStarted = espchrono::millis_clock::now(); setWifiState(WiFiState::Scanning); wasReallyScanning = true; return ESP_OK; } WiFiScanStatus get_scan_status() { //Check is scan was started and if the delay expired, return WIFI_SCAN_FAILED in this case if (scanStarted && espchrono::ago(*scanStarted) > scanTimeout) { wifi_clear_status_bits(WIFI_SCANNING_BIT); return WiFiScanStatus::Failed; } if (wifi_get_status_bits() & WIFI_SCAN_DONE_BIT) return WiFiScanStatus::Finished; if (wifi_get_status_bits() & WIFI_SCANNING_BIT) return WiFiScanStatus::Scanning; return WiFiScanStatus::None; } const std::optional &get_scan_result() { // TODO construct new container using esp_wifi_scan_get_ap_num() and esp_wifi_scan_get_ap_records() return _scanResult; } void delete_scan_result() { if (_scanResult) { _scanResult = std::nullopt; scanResultChanged(); } } tl::expected get_sta_ap_info() { wifi_ap_record_t info; if (const auto result = esp_wifi_sta_get_ap_info(&info); result == ESP_OK) return info; else { ESP_LOGW(TAG, "esp_wifi_sta_get_ap_info() failed with %s", esp_err_to_name(result)); return tl::make_unexpected(fmt::format("esp_wifi_sta_get_ap_info() failed with {}", esp_err_to_name(result))); } } tl::expected get_default_mac_addr() { wifi_stack::mac_t mac{}; if (const auto result = esp_efuse_mac_get_default(std::begin(mac)); result == ESP_OK) return mac; else { ESP_LOGE(TAG, "esp_efuse_mac_get_default() failed with %s", esp_err_to_name(result)); return tl::make_unexpected(fmt::format("esp_efuse_mac_get_default() failed with {}", esp_err_to_name(result))); } } tl::expected get_base_mac_addr() { wifi_stack::mac_t mac{}; if (const auto result = esp_base_mac_addr_get(std::begin(mac)); result == ESP_OK) return mac; else { ESP_LOGE(TAG, "esp_base_mac_addr_get() failed with %s", esp_err_to_name(result)); return tl::make_unexpected(fmt::format("esp_base_mac_addr_get() failed with {}", esp_err_to_name(result))); } } tl::expected set_base_mac_addr(wifi_stack::mac_t mac_addr) { if (const auto result = esp_base_mac_addr_set(std::cbegin(mac_addr)); result == ESP_OK) return {}; else { ESP_LOGE(TAG, "esp_base_mac_addr_set() failed with %s", esp_err_to_name(result)); return tl::make_unexpected(fmt::format("esp_base_mac_addr_set() failed with {}", esp_err_to_name(result))); } } tl::expected get_ip_info(tcpip_adapter_if_t tcpip_if) { tcpip_adapter_ip_info_t ip; if (const auto result = tcpip_adapter_get_ip_info(tcpip_if, &ip); result == ESP_OK) return ip; else { ESP_LOGE(TAG, "tcpip_adapter_get_ip_info() failed with %s", esp_err_to_name(result)); return tl::make_unexpected(fmt::format("tcpip_adapter_get_ip_info() failed with {}", esp_err_to_name(result))); } } namespace { int wifi_set_status_bits(int bits) { if (!wifi_event_group.constructed()) return 0; return wifi_event_group->setBits(bits); } int wifi_clear_status_bits(int bits) { if (!wifi_event_group.constructed()) return 0; return wifi_event_group->clearBits(bits); } int wifi_get_status_bits() { if (!wifi_event_group.constructed()) return 0; return wifi_event_group->getBits(); } int wifi_wait_status_bits(int bits, espcpputils::ticks timeout) { if (!wifi_event_group.constructed()) return 0; return wifi_event_group->waitBits( bits, // The bits within the event group to wait for. pdFALSE, // BIT_0 and BIT_4 should be cleared before returning. pdTRUE, // Don't wait for both bits, either bit will do. timeout.count()) & bits; // Wait a maximum of 100ms for either bit to be set. } esp_err_t wifi_set_esp_interface_ip(esp_interface_t interface, const std::optional &ip) { using wifi_stack::toString; if (ip) ESP_LOGI(TAG, "%s set STATIC ip=%s subnet=%s gateway=%s", toString(interface).c_str(), toString(ip->ip).c_str(), toString(ip->subnet).c_str(), toString(ip->gateway).c_str()); else ESP_LOGI(TAG, "%s set DYNAMIC", toString(interface).c_str()); if (interface == ESP_IF_WIFI_AP) { ESP_LOGE(TAG, "setting IP for AP must be done with wifi_set_ap_ip() instead!"); return ESP_FAIL; } esp_netif_t * const esp_netif = esp_netifs[interface]; if (!esp_netif) { ESP_LOGE(TAG, "esp_netif is invalid"); return ESP_FAIL; } { esp_netif_dhcp_status_t status; if (const auto result = esp_netif_dhcpc_get_status(esp_netif, &status); result != ESP_OK) { ESP_LOGE(TAG, "esp_netif_dhcpc_get_status() failed with %s", esp_err_to_name(result)); return result; } ESP_LOGI(TAG, "esp_netif_dhcpc_get_status() resulted in %s", toString(status).c_str()); } if (const auto result = esp_netif_dhcpc_stop(esp_netif); !cpputils::is_in(result, ESP_OK, ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED)) { ESP_LOGE(TAG, "esp_netif_dhcpc_stop() failed with %s", esp_err_to_name(result)); return result; } { esp_netif_ip_info_t info; info.ip.addr = ip ? ip->ip.value() : 0; info.netmask.addr = ip ? ip->subnet.value() : 0; info.gw.addr = ip ? ip->gateway.value() : 0; if (const auto result = esp_netif_set_ip_info(esp_netif, &info); result != ESP_OK) { ESP_LOGE(TAG, "esp_netif_set_ip_info() failed with %s", esp_err_to_name(result)); return result; } } if (!ip) { ESP_LOGI(TAG, "starting dhcpc"); if (const auto result = esp_netif_dhcpc_start(esp_netif); result != ESP_OK) { ESP_LOGE(TAG, "esp_netif_dhcpc_start() failed with %s", esp_err_to_name(result)); return result; } #if LWIP_IPV6 && LWIP_IPV6_DHCP6_STATELESS if (const auto result = dhcp6_enable_stateless(esp_netif_get_netif_impl(esp_netif)); result != ESP_OK) { ESP_LOGE(TAG, "dhcp6_enable_stateless() failed with %s", esp_err_to_name(result)); return result; } #endif } return ESP_OK; } esp_err_t wifi_set_esp_interface_dns(esp_interface_t interface, const static_dns_config &dns) { esp_netif_t *esp_netif = esp_netifs[interface]; esp_netif_dns_info_t dns_info; dns_info.ip.type = ESP_IPADDR_TYPE_V4; if (dns.main) { dns_info.ip.u_addr.ip4.addr = dns.main->value(); if (const auto result = esp_netif_set_dns_info(esp_netif, ESP_NETIF_DNS_MAIN, &dns_info); result != ESP_OK) { ESP_LOGE(TAG, "esp_netif_set_dns_info() for MAIN failed with %s", esp_err_to_name(result)); return result; } } if (dns.backup) { if (interface != ESP_IF_WIFI_AP) { dns_info.ip.u_addr.ip4.addr = dns.backup->value(); if (const auto result = esp_netif_set_dns_info(esp_netif, ESP_NETIF_DNS_BACKUP, &dns_info); result != ESP_OK) { ESP_LOGE(TAG, "esp_netif_set_dns_info() for BACKUP failed with %s", esp_err_to_name(result)); return result; } } else ESP_LOGE(TAG, "setting static backup DNS for AP is not allowed!"); } if (dns.fallback) { if (interface != ESP_IF_WIFI_AP) { dns_info.ip.u_addr.ip4.addr = dns.fallback->value(); if (const auto result = esp_netif_set_dns_info(esp_netif, ESP_NETIF_DNS_FALLBACK, &dns_info); result != ESP_OK) { ESP_LOGE(TAG, "esp_netif_set_dns_info() for FALLBACK failed with %s", esp_err_to_name(result)); return result; } } else ESP_LOGE(TAG, "setting static fallback DNS for AP is not allowed!"); } return ESP_OK; } esp_err_t wifi_enable_sta(bool enable, const config &config) { wifi_mode_t currentMode = wifi_get_mode(); bool isEnabled = currentMode & WIFI_MODE_STA; if (isEnabled == enable) return ESP_OK; const wifi_mode_t mode = enable ? (wifi_mode_t)(currentMode | WIFI_MODE_STA) : (wifi_mode_t)(currentMode & (~WIFI_MODE_STA)); if (const auto result = wifi_set_mode(mode, config); result != ESP_OK) { ESP_LOGE(TAG, "wifi_set_mode() failed with %s", esp_err_to_name(result)); return result; } return ESP_OK; } esp_err_t wifi_enable_ap(bool enable, const config &config) { wifi_mode_t currentMode = wifi_get_mode(); bool isEnabled = currentMode & WIFI_MODE_AP; if (isEnabled == enable) return ESP_OK; esp_err_t result; if (enable) result = wifi_set_mode((wifi_mode_t)(currentMode | WIFI_MODE_AP), config); else result = wifi_set_mode((wifi_mode_t)(currentMode & (~WIFI_MODE_AP)), config); if (result != ESP_OK) ESP_LOGE(TAG, "wifi_set_mode() failed with %s", esp_err_to_name(result)); return result; } template size_t copyStrToBuf(uint8_t (&buf)[LENGTH], std::string_view str) { size_t cutLength = std::min(LENGTH, str.size()); std::copy(std::begin(str), std::begin(str) + cutLength, buf); if (str.size() < LENGTH) buf[str.size()] = '\0'; return cutLength; } wifi_config_t make_ap_config(const ap_config &ap_config) { wifi_config_t wifi_config; wifi_config.ap.channel = ap_config.channel; wifi_config.ap.max_connection = ap_config.max_connection; wifi_config.ap.beacon_interval = ap_config.beacon_interval; wifi_config.ap.ssid_hidden = false; wifi_config.ap.authmode = WIFI_AUTH_OPEN; wifi_config.ap.ssid_len = 0; wifi_config.ap.ssid[0] = 0; wifi_config.ap.password[0] = 0; if (!ap_config.ssid.empty()) { auto ssidCutLength = copyStrToBuf(wifi_config.ap.ssid, ap_config.ssid); wifi_config.ap.ssid_len = ssidCutLength; if (!ap_config.key.empty()) { wifi_config.ap.authmode = ap_config.authmode; copyStrToBuf(wifi_config.ap.password, ap_config.key); } } return wifi_config; } esp_err_t wifi_set_ap_config(const config &config, const ap_config &ap_config) { if (ap_config.ssid.empty()) { ESP_LOGE(TAG, "SSID missing!"); return ESP_FAIL; } if (ap_config.ssid.size() > 32) { ESP_LOGE(TAG, "SSID too long! (size=%zd)", ap_config.ssid.size()); return ESP_FAIL; } if (!ap_config.key.empty()) { if (ap_config.key.size() < 8) { ESP_LOGE(TAG, "passphrase too short! (size=%zd)", ap_config.key.size()); return ESP_FAIL; } if (ap_config.key.size() > 64) { ESP_LOGE(TAG, "passphrase too long! (size=%zd)", ap_config.key.size()); return ESP_FAIL; } } wifi_config_t conf = make_ap_config(ap_config); wifi_config_t conf_current; if (const auto result = esp_wifi_get_config(WIFI_IF_AP, &conf_current); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_get_config() for AP failed with %s", esp_err_to_name(result)); return result; } if (!wifi_ap_config_equal(conf.ap, conf_current.ap)) { if (const auto result = esp_wifi_set_config(WIFI_IF_AP, &conf); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_set_config() for AP failed with %s", esp_err_to_name(result)); return result; } } return ESP_OK; } void set_sta_status(WiFiStaStatus status) { WiFiStaStatus oldStatus = _sta_status.exchange(status); ESP_LOGI(TAG, "%s (from %s)", toString(status).c_str(), toString(oldStatus).c_str()); if (oldStatus == WiFiStaStatus::WL_CONNECTED && status != WiFiStaStatus::WL_CONNECTED) _lastStaSwitchedFromConnected = espchrono::millis_clock::now(); else if (oldStatus != WiFiStaStatus::WL_CONNECTED && status == WiFiStaStatus::WL_CONNECTED) _lastStaSwitchedToConnected = espchrono::millis_clock::now(); } void wifi_scan_done() { _scanResult = std::nullopt; // free memory before allocating the new vector below uint16_t scanCount; if (const auto result = esp_wifi_scan_get_ap_num(&scanCount); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_scan_get_ap_num() failed with %s", esp_err_to_name(result)); goto cleanup; } if (scanCount) { scan_result newResult; newResult.entries.resize(scanCount); if (const auto result = esp_wifi_scan_get_ap_records(&scanCount, &(*std::begin(newResult.entries))); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_scan_get_ap_records() failed with %s", esp_err_to_name(result)); goto cleanup; } std::sort(std::begin(newResult.entries), std::end(newResult.entries), [](const wifi_ap_record_t &l, const wifi_ap_record_t &r){ return l.rssi > r.rssi; }); newResult.finished = espchrono::millis_clock::now(); _scanResult = std::move(newResult); } cleanup: scanResultChangedFlag = true; scanStarted = std::nullopt; //Reset after a scan is completed for normal behavior wifi_set_status_bits(WIFI_SCAN_DONE_BIT); wifi_clear_status_bits(WIFI_SCANNING_BIT); } void wifi_event_callback(const config &config, const WifiEvent &event) { ESP_LOGD(TAG, "%d %s", int(event.event_id), toString(event.event_id).c_str()); switch (event.event_id) { case WifiEventId::WIFI_SCAN_DONE: wifi_scan_done(); break; case WifiEventId::WIFI_STA_START: set_sta_status(WiFiStaStatus::WL_IDLE_STATUS); wifi_set_status_bits(STA_STARTED_BIT); if (const auto result = esp_wifi_set_ps(_sleepEnabled); result != ESP_OK) ESP_LOGE(TAG, "esp_wifi_set_ps() failed with %s", esp_err_to_name(result)); break; case WifiEventId::WIFI_STA_STOP: set_sta_status(WiFiStaStatus::WL_NO_SHIELD); wifi_clear_status_bits(STA_STARTED_BIT | STA_CONNECTED_BIT | STA_HAS_IP_BIT | STA_HAS_IP6_BIT); break; case WifiEventId::WIFI_STA_CONNECTED: set_sta_status(WiFiStaStatus::WL_IDLE_STATUS); wifi_set_status_bits(STA_CONNECTED_BIT); //esp_netif_create_ip6_linklocal(esp_netifs[ESP_IF_WIFI_STA]); break; case WifiEventId::WIFI_STA_DISCONNECTED: { const std::string_view ssid { (const char *)event.wifi_sta_disconnected.ssid, event.wifi_sta_disconnected.ssid_len }; const auto reason = event.wifi_sta_disconnected.reason; ESP_LOGW(TAG, "WIFI_STA_DISCONNECTED ssid=\"%.*s\" bssid=%s reason=%u(%s)", ssid.size(), ssid.data(), toString(mac_t{event.wifi_sta_disconnected.bssid}).c_str(), reason, reason2str(reason)); switch (reason) { case WIFI_REASON_AUTH_EXPIRE: break; case WIFI_REASON_NO_AP_FOUND: case WIFI_REASON_AUTH_FAIL: case WIFI_REASON_ASSOC_FAIL: case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: case WIFI_REASON_BEACON_TIMEOUT: case WIFI_REASON_HANDSHAKE_TIMEOUT: default: { const auto sta_status = get_sta_status(); if (sta_status != WiFiStaStatus::WL_DISCONNECTING) { ESP_LOGW(TAG, "setting fail flag"); _wifiConnectFailFlag = espchrono::millis_clock::now(); } switch (sta_status) { case WiFiStaStatus::WL_CONNECTED: set_sta_status(WiFiStaStatus::WL_CONNECTION_LOST); break; case WiFiStaStatus::WL_CONNECTING: set_sta_status(WiFiStaStatus::WL_CONNECT_FAILED); break; case WiFiStaStatus::WL_DISCONNECTING: default: set_sta_status(WiFiStaStatus::WL_DISCONNECTED); break; } break; } } wifi_clear_status_bits(STA_CONNECTED_BIT | STA_HAS_IP_BIT | STA_HAS_IP6_BIT); if (((reason == WIFI_REASON_AUTH_EXPIRE) || (reason >= WIFI_REASON_BEACON_TIMEOUT && reason != WIFI_REASON_AUTH_FAIL))) { if (const auto result = wifi_sta_disconnect(config); result != ESP_OK) ESP_LOGE(TAG, "wifi_sta_disconnect() failed with %s", esp_err_to_name(result)); if (const auto result = wifi_sta_restart(config); result != ESP_OK) ESP_LOGE(TAG, "wifi_sta_restart() failed with %s", esp_err_to_name(result)); } break; } case WifiEventId::WIFI_STA_GOT_IP: ESP_LOGI(TAG, "WIFI_STA_GOT_IP ip=%s netmask=%s gw=%s", wifi_stack::toString(event.got_ip.ip_info.ip).c_str(), wifi_stack::toString(event.got_ip.ip_info.netmask).c_str(), wifi_stack::toString(event.got_ip.ip_info.gw).c_str() ); set_sta_status(WiFiStaStatus::WL_CONNECTED); wifi_set_status_bits(STA_HAS_IP_BIT | STA_CONNECTED_BIT); break; case WifiEventId::WIFI_STA_LOST_IP: ESP_LOGW(TAG, "WIFI_STA_LOST_IP"); set_sta_status(WiFiStaStatus::WL_IDLE_STATUS); wifi_clear_status_bits(STA_HAS_IP_BIT); break; case WifiEventId::WIFI_AP_START: wifi_set_status_bits(AP_STARTED_BIT); break; case WifiEventId::WIFI_AP_STOP: wifi_clear_status_bits(AP_STARTED_BIT | AP_HAS_CLIENT_BIT); break; case WifiEventId::WIFI_AP_STACONNECTED: wifi_set_status_bits(AP_HAS_CLIENT_BIT); break; case WifiEventId::WIFI_AP_STADISCONNECTED: wifi_sta_list_t clients; if (esp_wifi_ap_get_sta_list(&clients) != ESP_OK || !clients.num) wifi_clear_status_bits(AP_HAS_CLIENT_BIT); break; case WifiEventId::ETH_START: #ifdef CONFIG_ETH_ENABLED wifi_set_status_bits(ETH_STARTED_BIT); #endif break; case WifiEventId::ETH_STOP: #ifdef CONFIG_ETH_ENABLED wifi_clear_status_bits(ETH_STARTED_BIT | ETH_CONNECTED_BIT | ETH_HAS_IP_BIT | ETH_HAS_IP6_BIT); #endif break; case WifiEventId::ETH_CONNECTED: #ifdef CONFIG_ETH_ENABLED wifi_set_status_bits(ETH_CONNECTED_BIT); #endif break; case WifiEventId::ETH_DISCONNECTED: #ifdef CONFIG_ETH_ENABLED wifi_clear_status_bits(ETH_CONNECTED_BIT | ETH_HAS_IP_BIT | ETH_HAS_IP6_BIT); #endif break; case WifiEventId::ETH_GOT_IP: #ifdef CONFIG_ETH_ENABLED ESP_LOGI(TAG, "ETH_GOT_IP ip=%s netmask=%s gw=%s", wifi_stack::toString(event.got_ip.ip_info.ip).c_str(), wifi_stack::toString(event.got_ip.ip_info.netmask).c_str(), wifi_stack::toString(event.got_ip.ip_info.gw).c_str() ); wifi_set_status_bits(ETH_CONNECTED_BIT | ETH_HAS_IP_BIT); #endif break; case WifiEventId::WIFI_STA_GOT_IP6: ESP_LOGI(TAG, "WIFI_STA_GOT_IP6 index=%d zone=%d ip=" IPV6STR, event.got_ip6.ip_index, event.got_ip6.ip6_info.ip.zone, IPV62STR(event.got_ip6.ip6_info.ip)); wifi_set_status_bits(STA_CONNECTED_BIT | STA_HAS_IP6_BIT); break; case WifiEventId::WIFI_AP_GOT_IP6: ESP_LOGI(TAG, "WIFI_AP_GOT_IP6 index=%d zone=%d ip=" IPV6STR, event.got_ip6.ip_index, event.got_ip6.ip6_info.ip.zone, IPV62STR(event.got_ip6.ip6_info.ip)); wifi_set_status_bits(AP_HAS_IP6_BIT); break; case WifiEventId::ETH_GOT_IP6: #ifdef CONFIG_ETH_ENABLED ESP_LOGI(TAG, "ETH_GOT_IP6 index=%d zone=%d ip=" IPV6STR, event.got_ip6.ip_index, event.got_ip6.ip6_info.ip.zone, IPV62STR(event.got_ip6.ip6_info.ip)); wifi_set_status_bits(ETH_CONNECTED_BIT | ETH_HAS_IP6_BIT); #endif break; #ifdef SMARTCONFIG case WifiEventId::SC_GOT_SSID_PSWD: wifi_sta_begin( (const char *)event.sc_got_ssid_pswd.ssid, (const char *)event.sc_got_ssid_pswd.password, 0, ((event.sc_got_ssid_pswd.bssid_set == true)?event.sc_got_ssid_pswd.bssid:NULL) ); break; case WifiEventId::SC_SEND_ACK_DONE: esp_smartconfig_stop(); _smartConfigDone = true; break; #endif default:; } } esp_err_t wifi_post_event(std::unique_ptr event) { if (!event) { ESP_LOGE(TAG, "invalid event"); return ESP_FAIL; } const auto ptr = event.get(); if (const auto result = wifi_event_queue->send(&ptr, portMAX_DELAY); result != pdTRUE) { ESP_LOGE(TAG, "wifi_event_queue->send() failed with %i", result); return ESP_FAIL; } event.release(); // ownership taken over by wifi_event_queue return ESP_OK; } void wifi_event_cb(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { ESP_LOGI(TAG, "event_base=%s event_id=%i", event_base, event_id); auto wifi_event = std::make_unique(); wifi_event->event_id = WifiEventId::MAX; /* * STA * */ if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "WIFI_EVENT_STA_START"); wifi_event->event_id = WifiEventId::WIFI_STA_START; } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_STOP) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "WIFI_EVENT_STA_STOP"); wifi_event->event_id = WifiEventId::WIFI_STA_STOP; } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_AUTHMODE_CHANGE) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "WIFI_EVENT_STA_AUTHMODE_CHANGE"); const wifi_event_sta_authmode_change_t &event = *(const wifi_event_sta_authmode_change_t *)event_data; ESP_LOGI(TAG, "STA Auth Mode Changed: From: %s, To: %s", wifi_stack::toString(event.old_mode).c_str(), wifi_stack::toString(event.new_mode).c_str()); wifi_event->event_id = WifiEventId::WIFI_STA_AUTHMODE_CHANGE; wifi_event->wifi_sta_authmode_change = event; } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "WIFI_EVENT_STA_CONNECTED"); const wifi_event_sta_connected_t &event = *(const wifi_event_sta_connected_t *)event_data; ESP_LOGI(TAG, "STA Connected: SSID: %s, BSSID: " MACSTR ", Channel: %u, Auth: %s", event.ssid, MAC2STR(event.bssid), event.channel, wifi_stack::toString(event.authmode).c_str()); wifi_event->event_id = WifiEventId::WIFI_STA_CONNECTED; wifi_event->wifi_sta_connected = event; } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "WIFI_EVENT_STA_CONNECTED"); const wifi_event_sta_disconnected_t &event = *(const wifi_event_sta_disconnected_t *)event_data; ESP_LOGI(TAG, "STA Disconnected: SSID: %s, BSSID: " MACSTR ", Reason: %u", event.ssid, MAC2STR(event.bssid), event.reason); wifi_event->event_id = WifiEventId::WIFI_STA_DISCONNECTED; wifi_event->wifi_sta_disconnected = event; } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "IP_EVENT_STA_GOT_IP"); const ip_event_got_ip_t &event = *(const ip_event_got_ip_t *)event_data; ESP_LOGI(TAG, "STA Got %sIP:" IPSTR, event.ip_changed?"New ":"Same ", IP2STR(&event.ip_info.ip)); wifi_event->event_id = WifiEventId::WIFI_STA_GOT_IP; wifi_event->got_ip = event; } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_LOST_IP) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "IP_EVENT_STA_LOST_IP"); wifi_event->event_id = WifiEventId::WIFI_STA_LOST_IP; } /* * SCAN * */ else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "WIFI_EVENT_SCAN_DONE"); const wifi_event_sta_scan_done_t &event = *(const wifi_event_sta_scan_done_t *)event_data; ESP_LOGI(TAG, "SCAN Done: ID: %u, Status: %u, Results: %u", event.scan_id, event.status, event.number); wifi_event->event_id = WifiEventId::WIFI_SCAN_DONE; wifi_event->wifi_scan_done = event; } /* * AP * */ else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_START) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "WIFI_EVENT_AP_START"); wifi_event->event_id = WifiEventId::WIFI_AP_START; } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STOP) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "WIFI_EVENT_AP_STOP"); wifi_event->event_id = WifiEventId::WIFI_AP_STOP; } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_PROBEREQRECVED) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "WIFI_EVENT_AP_PROBEREQRECVED"); const wifi_event_ap_probe_req_rx_t &event = *(const wifi_event_ap_probe_req_rx_t *)event_data; ESP_LOGI(TAG, "AP Probe Request: RSSI: %d, MAC: " MACSTR, event.rssi, MAC2STR(event.mac)); wifi_event->event_id = WifiEventId::WIFI_AP_PROBEREQRECVED; wifi_event->wifi_ap_probereqrecved = event; } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STACONNECTED) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "WIFI_EVENT_AP_STACONNECTED"); const wifi_event_ap_staconnected_t &event = *(const wifi_event_ap_staconnected_t *)event_data; ESP_LOGI(TAG, "AP Station Connected: MAC: " MACSTR ", AID: %d", MAC2STR(event.mac), event.aid); wifi_event->event_id = WifiEventId::WIFI_AP_STACONNECTED; wifi_event->wifi_ap_staconnected = event; } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STADISCONNECTED) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "WIFI_EVENT_AP_STADISCONNECTED"); const wifi_event_ap_stadisconnected_t &event = *(const wifi_event_ap_stadisconnected_t *)event_data; ESP_LOGI(TAG, "AP Station Disconnected: MAC: " MACSTR ", AID: %d", MAC2STR(event.mac), event.aid); wifi_event->event_id = WifiEventId::WIFI_AP_STADISCONNECTED; wifi_event->wifi_ap_stadisconnected = event; } else if (event_base == IP_EVENT && event_id == IP_EVENT_AP_STAIPASSIGNED) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "IP_EVENT_AP_STAIPASSIGNED"); const ip_event_ap_staipassigned_t &event = *(const ip_event_ap_staipassigned_t *)event_data; ESP_LOGI(TAG, "AP Station IP Assigned:" IPSTR, IP2STR(&event.ip)); wifi_event->event_id = WifiEventId::WIFI_AP_STAIPASSIGNED; wifi_event->wifi_ap_staipassigned = event; } #ifdef CONFIG_ETH_ENABLED /* * ETH * */ else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_CONNECTED) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "ETHERNET_EVENT_CONNECTED"); esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data; wifi_event->event_id = WifiEventId::ETH_CONNECTED; wifi_event->eth_connected = eth_handle; } else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_DISCONNECTED) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "ETHERNET_EVENT_DISCONNECTED"); wifi_event->event_id = WifiEventId::ETH_DISCONNECTED; } else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_START) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "ETHERNET_EVENT_START"); wifi_event->event_id = WifiEventId::ETH_START; } else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_STOP) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "ETHERNET_EVENT_STOP"); wifi_event->event_id = WifiEventId::ETH_STOP; } else if (event_base == IP_EVENT && event_id == IP_EVENT_ETH_GOT_IP) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "IP_EVENT_ETH_GOT_IP"); const ip_event_got_ip_t &event = *(const ip_event_got_ip_t *)event_data; ESP_LOGI(TAG, "Ethernet got %sip:" IPSTR, event.ip_changed?"new":"", IP2STR(&event.ip_info.ip)); wifi_event->event_id = WifiEventId::ETH_GOT_IP; wifi_event->got_ip = event; } #endif /* * IPv6 * */ else if (event_base == IP_EVENT && event_id == IP_EVENT_GOT_IP6) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "IP_EVENT_GOT_IP6"); ip_event_got_ip6_t * event = (ip_event_got_ip6_t*)event_data; esp_interface_t iface = ESP_IF_MAX; for (int i = 0; i < ESP_IF_MAX; i++) if (esp_netifs[i] && esp_netifs[i] == event->esp_netif) { iface = (esp_interface_t)i; break; } ESP_LOGV(TAG, "IF[%d] Got IPv6: IP Index: %d, Zone: %d, " IPV6STR, iface, event->ip_index, event->ip6_info.ip.zone, IPV62STR(event->ip6_info.ip)); switch (iface) { case ESP_IF_WIFI_STA: wifi_event->event_id = WifiEventId::WIFI_STA_GOT_IP6; break; case ESP_IF_WIFI_AP: wifi_event->event_id = WifiEventId::WIFI_AP_GOT_IP6; break; case ESP_IF_ETH: #ifdef CONFIG_ETH_ENABLED wifi_event->event_id = WifiEventId::ETH_GOT_IP6; #endif break; default:; } std::memcpy(&wifi_event->got_ip6, event_data, sizeof(ip_event_got_ip6_t)); // TODO replace with operator= } /* * WPS * */ else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_WPS_ER_SUCCESS) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "WIFI_EVENT_STA_WPS_ER_SUCCESS"); wifi_event->event_id = WifiEventId::WPS_ER_SUCCESS; } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_WPS_ER_FAILED) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "WIFI_EVENT_STA_WPS_ER_FAILED"); wifi_event_sta_wps_fail_reason_t * event = (wifi_event_sta_wps_fail_reason_t*)event_data; CPP_UNUSED(event) wifi_event->event_id = WifiEventId::WPS_ER_FAILED; std::memcpy(&wifi_event->wps_fail_reason, event_data, sizeof(wifi_event_sta_wps_fail_reason_t)); // TODO replace with operator= } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_WPS_ER_TIMEOUT) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "WIFI_EVENT_STA_WPS_ER_TIMEOUT"); wifi_event->event_id = WifiEventId::WPS_ER_TIMEOUT; } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_WPS_ER_PIN) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "WIFI_EVENT_STA_WPS_ER_PIN"); wifi_event_sta_wps_er_pin_t * event = (wifi_event_sta_wps_er_pin_t*)event_data; CPP_UNUSED(event) wifi_event->event_id = WifiEventId::WPS_ER_PIN; std::memcpy(&wifi_event->wps_er_pin, event_data, sizeof(wifi_event_sta_wps_er_pin_t)); // TODO replace with operator= } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_WPS_ER_PBC_OVERLAP) { ESP_LOGI(TAG, "event_base=%s event_id=%s", event_base, "WIFI_EVENT_STA_WPS_ER_PBC_OVERLAP"); wifi_event->event_id = WifiEventId::WPS_ER_PBC_OVERLAP; } #ifdef SMARTCONFIG /* * SMART CONFIG * */ else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) { ESP_LOGV(TAG, "SC Scan Done"); wifi_event->event_id = WifiEventId::SC_SCAN_DONE; } else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) { ESP_LOGV(TAG, "SC Found Channel"); wifi_event->event_id = WifiEventId::SC_FOUND_CHANNEL; } else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) { smartconfig_event_got_ssid_pswd_t *event = (smartconfig_event_got_ssid_pswd_t *)event_data; ESP_LOGV(TAG, "SC: SSID: %s, Password: %s", (const char *)event->ssid, (const char *)event->password); wifi_event->event_id = WifiEventId::SC_GOT_SSID_PSWD; std::memcpy(&wifi_event->sc_got_ssid_pswd, event_data, sizeof(smartconfig_event_got_ssid_pswd_t)); // TODO replace with operator= } else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) { ESP_LOGV(TAG, "SC Send Ack Done"); wifi_event->event_id = WifiEventId::SC_SEND_ACK_DONE; } #endif #ifdef PROVISIONING /* * Provisioning * */ else if (event_base == WIFI_PROV_EVENT && event_id == WIFI_PROV_INIT) { ESP_LOGV(TAG, "Provisioning Initialized!"); wifi_event->event_id = WifiEventId::PROV_INIT; } else if (event_base == WIFI_PROV_EVENT && event_id == WIFI_PROV_DEINIT) { ESP_LOGV(TAG, "Provisioning Uninitialized!"); wifi_event->event_id = WifiEventId::PROV_DEINIT; } else if (event_base == WIFI_PROV_EVENT && event_id == WIFI_PROV_START) { ESP_LOGV(TAG, "Provisioning Start!"); wifi_event->event_id = WifiEventId::PROV_START; } else if (event_base == WIFI_PROV_EVENT && event_id == WIFI_PROV_END) { ESP_LOGV(TAG, "Provisioning End!"); wifi_prov_mgr_deinit(); wifi_event->event_id = WifiEventId::PROV_END; } else if (event_base == WIFI_PROV_EVENT && event_id == WIFI_PROV_CRED_RECV) { wifi_sta_config_t *event = (wifi_sta_config_t *)event_data; ESP_LOGV(TAG, "Provisioned Credentials: SSID: %s, Password: %s", (const char *) event->ssid, (const char *) event->password); wifi_event->event_id = WifiEventId::PROV_CRED_RECV; std::memcpy(&wifi_event->prov_cred_recv, event_data, sizeof(wifi_sta_config_t)); // TODO replace with operator= } else if (event_base == WIFI_PROV_EVENT && event_id == WIFI_PROV_CRED_FAIL) { wifi_prov_sta_fail_reason_t *reason = (wifi_prov_sta_fail_reason_t *)event_data; ESP_LOGE(TAG, "Provisioning Failed: Reason : %s", (*reason == WIFI_PROV_STA_AUTH_ERROR)?"Authentication Failed":"AP Not Found"); wifi_event->event_id = WifiEventId::PROV_CRED_FAIL; std::memcpy(&wifi_event->prov_fail_reason, event_data, sizeof(wifi_prov_sta_fail_reason_t)); // TODO replace with operator= } else if (event_base == WIFI_PROV_EVENT && event_id == WIFI_PROV_CRED_SUCCESS) { ESP_LOGV(TAG, "Provisioning Success!"); wifi_event->event_id = WifiEventId::PROV_CRED_SUCCESS; } #endif if (wifi_event->event_id < WifiEventId::MAX) wifi_post_event(std::move(wifi_event)); } esp_err_t wifi_start_network_event_task(const config &config) { if (!wifi_event_group.constructed()) { wifi_event_group.construct(); if (!wifi_event_group->handle) { wifi_event_group.destruct(); ESP_LOGE(TAG, "Network Event Group Create Failed!"); return ESP_FAIL; } wifi_event_group->setBits(WIFI_DNS_IDLE_BIT); } if (!wifi_event_queue.constructed()) { wifi_event_queue.construct(UBaseType_t{32}, sizeof(WifiEvent*)); if (!wifi_event_queue->handle) { wifi_event_queue.destruct(); ESP_LOGE(TAG, "Network Event Queue Create Failed!"); return ESP_FAIL; } } if (const auto result = esp_event_loop_create_default(); result != ESP_OK) { ESP_LOGE(TAG, "esp_event_loop_create_default() failed with %s", esp_err_to_name(result)); if (result != ESP_ERR_INVALID_STATE) return result; } if (const auto result = esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_cb, NULL, NULL); result != ESP_OK) { ESP_LOGE(TAG, "event_handler_instance_register() for %s failed with %s", "WIFI_EVENT", esp_err_to_name(result)); return result; } if (const auto result = esp_event_handler_instance_register(IP_EVENT, ESP_EVENT_ANY_ID, &wifi_event_cb, NULL, NULL); result != ESP_OK) { ESP_LOGE(TAG, "event_handler_instance_register() for %s failed with %s", "IP_EVENT", esp_err_to_name(result)); return result; } #ifdef SMARTCONFIG if (const auto result = esp_event_handler_instance_register(SC_EVENT, ESP_EVENT_ANY_ID, &wifi_event_cb, NULL, NULL); result != ESP_OK) { ESP_LOGE(TAG, "event_handler_instance_register() for %s failed with %s", "SC_EVENT", esp_err_to_name(result)); return result; } #endif #ifdef CONFIG_ETH_ENABLED if (const auto result = esp_event_handler_instance_register(ETH_EVENT, ESP_EVENT_ANY_ID, &wifi_event_cb, NULL, NULL); result != ESP_OK) { ESP_LOGE(TAG, "event_handler_instance_register() for %s failed with %s", "ETH_EVENT", esp_err_to_name(result)); return result; } #endif #ifdef PROVISIONING if (const auto result = esp_event_handler_instance_register(WIFI_PROV_EVENT, ESP_EVENT_ANY_ID, &wifi_event_cb, NULL, NULL); result != ESP_OK) { ESP_LOGE(TAG, "event_handler_instance_register() for %s failed with %s", "WIFI_PROV_EVENT", esp_err_to_name(result)); return result; } #endif return ESP_OK; } esp_err_t wifi_tcpip_init(const config &config) { static bool initialized = false; if (initialized) return ESP_OK; #if CONFIG_IDF_TARGET_ESP32 if (const auto mac = get_default_mac_addr()) { if (const auto result = set_base_mac_addr(*mac)) { } else { ESP_LOGE(TAG, "set_base_mac_addr() failed: %.*s", result.error().size(), result.error().data()); } } else { ESP_LOGE(TAG, "get_default_mac_addr() failed: %.*s", mac.error().size(), mac.error().data()); } #endif if (const auto result = esp_netif_init(); result != ESP_OK) { ESP_LOGE(TAG, "esp_netif_init() failed with %s", esp_err_to_name(result)); return result; } if (const auto result = wifi_start_network_event_task(config); result != ESP_OK) { ESP_LOGE(TAG, "wifi_start_network_event_task() failed with %s", esp_err_to_name(result)); return result; } initialized = true; return ESP_OK; } esp_err_t wifi_low_level_init(const config &config) { if (lowLevelInitDone) return ESP_OK; if (const auto result = wifi_tcpip_init(config); result != ESP_OK) { ESP_LOGE(TAG, "wifi_tcpip_init() failed with %s", esp_err_to_name(result)); return result; } if (!esp_netifs[ESP_IF_WIFI_AP]) { esp_netifs[ESP_IF_WIFI_AP] = esp_netif_create_default_wifi_ap(); if (!esp_netifs[ESP_IF_WIFI_AP]) { ESP_LOGE(TAG, "esp_netif_create_default_wifi_ap() returned null"); return ESP_FAIL; } } if (!esp_netifs[ESP_IF_WIFI_STA]) { esp_netifs[ESP_IF_WIFI_STA] = esp_netif_create_default_wifi_sta(); if (!esp_netifs[ESP_IF_WIFI_STA]) { ESP_LOGE(TAG, "esp_netif_create_default_wifi_sta() returned null"); return ESP_FAIL; } } wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); if (const auto result = esp_wifi_init(&cfg); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_init() failed with %s", esp_err_to_name(result)); return result; } if (const auto result = esp_wifi_set_storage(WIFI_STORAGE_RAM); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_set_storage() failed with %s", esp_err_to_name(result)); return result; } lowLevelInitDone = true; return ESP_OK; } esp_err_t wifi_start() { if (_esp_wifi_started) return ESP_OK; if (const auto result = esp_wifi_start(); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_start() failed with %s", esp_err_to_name(result)); return result; } _esp_wifi_started = true; return ESP_OK; } esp_err_t wifi_low_level_deinit() { if (!lowLevelInitDone) return ESP_OK; if (const auto result = esp_wifi_deinit(); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_deinit() failed with %s", esp_err_to_name(result)); return result; } lowLevelInitDone = false; return ESP_OK; } esp_err_t wifi_stop() { if (!_esp_wifi_started) return ESP_OK; if (const auto result = esp_wifi_stop(); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_stop() failed with %s", esp_err_to_name(result)); return result; } _esp_wifi_started = false; if (const auto result = wifi_low_level_deinit(); result != ESP_OK) { ESP_LOGE(TAG, "wifi_low_level_deinit() failed with %s", esp_err_to_name(result)); return result; } return ESP_OK; } wifi_mode_t wifi_get_mode() { if (!lowLevelInitDone || !_esp_wifi_started) return WIFI_MODE_NULL; wifi_mode_t mode; if (const auto result = esp_wifi_get_mode(&mode); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_get_mode() returned %s", esp_err_to_name(result)); return WIFI_MODE_NULL; } return mode; } esp_err_t wifi_set_mode(wifi_mode_t m, const config &config) { wifi_mode_t cm = wifi_get_mode(); if (cm == m) return ESP_OK; if (!cm && m) { if (const auto result = wifi_low_level_init(config); result != ESP_OK) { ESP_LOGE(TAG, "wifi_low_level_init() failed with %s", esp_err_to_name(result)); return result; } } else if (cm && !m) { if (const auto result = wifi_stop(); result != ESP_OK) { ESP_LOGE(TAG, "wifi_stop() failed with %s", esp_err_to_name(result)); return result; } } if (m & WIFI_MODE_STA) { if (const auto result = esp_netif_set_hostname(esp_netifs[ESP_IF_WIFI_STA], config.hostname.c_str()); result != ESP_OK) { ESP_LOGE(TAG, "esp_netif_set_hostname() \"%s\" failed with %s", config.hostname.c_str(), esp_err_to_name(result)); return result; } } if (const auto result = esp_wifi_set_mode(m); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_set_mode() failed with %s", esp_err_to_name(result)); return result; } if (_long_range) { if (m & WIFI_MODE_STA) { if (const auto result = esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_LR); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_set_protocol() for STA long range failed with %s", esp_err_to_name(result)); return result; } } if (m & WIFI_MODE_AP) { if (const auto result = esp_wifi_set_protocol(WIFI_IF_AP, WIFI_PROTOCOL_LR); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_set_protocol() for AP long range failed with %s", esp_err_to_name(result)); return result; } } } if (const auto result = wifi_start(); result != ESP_OK) { ESP_LOGE(TAG, "wifi_start() failed with %s", esp_err_to_name(result)); return result; } return ESP_OK; } esp_err_t wifi_set_ap_ip(const config &config, const static_ip_config &ip) { using wifi_stack::toString; ESP_LOGI(TAG, "ip=%s subnet=%s gateway=%s", toString(ip.ip).c_str(), toString(ip.subnet).c_str(), toString(ip.gateway).c_str()); esp_netif_t * const esp_netif = esp_netifs[ESP_IF_WIFI_AP]; { esp_netif_dhcp_status_t status; if (const auto result = esp_netif_dhcps_get_status(esp_netif, &status); result != ESP_OK) { ESP_LOGE(TAG, "esp_netif_dhcps_get_status() failed with %s", esp_err_to_name(result)); return result; } ESP_LOGI(TAG, "esp_netif_dhcps_get_status() resulted in %s", toString(status).c_str()); } for (int i = 0; ; i++) { if (const auto result = esp_netif_dhcps_stop(esp_netif); result != ESP_OK) { ESP_LOGE(TAG, "esp_netif_dhcps_stop() failed with %s", esp_err_to_name(result)); if (result != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) return result; } esp_netif_ip_info_t info; info.ip.addr = ip.ip.value(); info.netmask.addr = ip.subnet.value(); info.gw.addr = ip.gateway.value(); if (const auto result = esp_netif_set_ip_info(esp_netif, &info); result == ESP_OK) break; else { ESP_LOGE(TAG, "esp_netif_set_ip_info() for AP failed with %s", esp_err_to_name(result)); if (result == ESP_ERR_ESP_NETIF_DHCP_NOT_STOPPED) { if (i < 20) { espcpputils::delay(50ms); continue; } else ESP_LOGE(TAG, "esp_netif_set_ip_info() giving up after 20 tries"); } return result; } } { dhcps_lease_t lease; lease.enable = true; lease.start_ip.addr = ip.ip.value() + (1 << 24); lease.end_ip.addr = ip.ip.value() + (11 << 24); if (const auto result = tcpip_adapter_dhcps_option(ESP_NETIF_OP_SET, ESP_NETIF_REQUESTED_IP_ADDRESS, &lease, sizeof(dhcps_lease_t)); result != ESP_OK) { ESP_LOGE(TAG, "tcpip_adapter_dhcps_option() failed with %s", esp_err_to_name(result)); return result; } } if (const auto result = esp_netif_dhcps_start(esp_netif); result != ESP_OK) { ESP_LOGE(TAG, "esp_netif_dhcps_start() failed with %s", esp_err_to_name(result)); return result; } return ESP_OK; } wifi_config_t make_sta_config(std::string_view ssid, std::string_view password, int8_t min_rssi, std::optional bssid, uint8_t channel) { wifi_config_t wifi_config; wifi_config.sta.channel = channel; wifi_config.sta.listen_interval = 0; wifi_config.sta.scan_method = WIFI_FAST_SCAN; //WIFI_ALL_CHANNEL_SCAN or WIFI_FAST_SCAN wifi_config.sta.sort_method = WIFI_CONNECT_AP_BY_SIGNAL; //WIFI_CONNECT_AP_BY_SIGNAL or WIFI_CONNECT_AP_BY_SECURITY wifi_config.sta.threshold.rssi = min_rssi; wifi_config.sta.pmf_cfg.capable = true; wifi_config.sta.pmf_cfg.required = false; wifi_config.sta.bssid_set = 0; std::fill(std::begin(wifi_config.sta.bssid), std::end(wifi_config.sta.bssid), 0); wifi_config.sta.threshold.authmode = WIFI_AUTH_OPEN; wifi_config.sta.ssid[0] = 0; wifi_config.sta.password[0] = 0; if (!ssid.empty()) { copyStrToBuf(wifi_config.sta.ssid, ssid); if (!password.empty()) { wifi_config.sta.threshold.authmode = WIFI_AUTH_WEP; copyStrToBuf(wifi_config.sta.password, password); } if (bssid) { wifi_config.sta.bssid_set = 1; bssid->copyTo(wifi_config.sta.bssid); } } return wifi_config; } esp_err_t wifi_sta_begin(const config &config, const wifi_entry &sta_config, int32_t channel, std::optional bssid, bool connect) { if (const auto result = wifi_enable_sta(true, config); result != ESP_OK) { ESP_LOGE(TAG, "wifi_enable_sta() failed with %s", esp_err_to_name(result)); return result; } if (sta_config.ssid.empty()) { ESP_LOGE(TAG, "SSID missing!"); return ESP_FAIL; } if (sta_config.ssid.size() > 32) { ESP_LOGE(TAG, "SSID too long! (size=%zd)", sta_config.ssid.size()); return ESP_FAIL; } if (!sta_config.key.empty()) { if (sta_config.key.size() < 8) { ESP_LOGE(TAG, "key too short! (size=%zd)", sta_config.key.size()); return ESP_FAIL; } if (sta_config.key.size() > 64) { ESP_LOGE(TAG, "key too long! (size=%zd)", sta_config.key.size()); return ESP_FAIL; } } wifi_config_t conf = make_sta_config(sta_config.ssid, sta_config.key, config.sta.min_rssi, bssid, channel); wifi_config_t current_conf; if (const auto result = esp_wifi_get_config(WIFI_IF_STA, ¤t_conf); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_get_config() for STA failed with %s", esp_err_to_name(result)); return result; } if (!wifi_sta_config_equal(current_conf.sta, conf.sta)) { if (const auto result = esp_wifi_disconnect(); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_disconnect() failed with %s", esp_err_to_name(result)); return result; } if (const auto result = esp_wifi_set_config(WIFI_IF_STA, &conf); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_set_config() for STA failed with %s", esp_err_to_name(result)); return result; } } else if (get_sta_status() == WiFiStaStatus::WL_CONNECTED) { ESP_LOGW(TAG, "when already connected?!"); return ESP_OK; } else { if (const auto result = esp_wifi_set_config(WIFI_IF_STA, &conf); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_set_config() for STA failed with %s", esp_err_to_name(result)); return result; } } if (const auto result = wifi_set_esp_interface_ip(ESP_IF_WIFI_STA, sta_config.static_ip); result != ESP_OK) { ESP_LOGE(TAG, "wifi_set_esp_interface_ip() for STA failed with %s", esp_err_to_name(result)); return result; } last_sta_static_ip = sta_config.static_ip; if (const auto result = wifi_set_esp_interface_dns(ESP_IF_WIFI_STA, sta_config.static_dns); result != ESP_OK) { ESP_LOGE(TAG, "wifi_set_esp_interface_dns() for STA failed with %s", esp_err_to_name(result)); return result; } last_sta_static_dns = sta_config.static_dns; if (connect) { _wifiConnectFailFlag = std::nullopt; if (const auto result = esp_wifi_connect(); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_connect() failed with %s", esp_err_to_name(result)); return result; } set_sta_status(WiFiStaStatus::WL_CONNECTING); } return ESP_OK; } esp_err_t wifi_sta_restart(const config &config) { if (const auto result = wifi_enable_sta(true, config); result != ESP_OK) { ESP_LOGE(TAG, "wifi_enable_sta() failed with %s", esp_err_to_name(result)); return result; } wifi_config_t current_conf; if (const auto result = esp_wifi_get_config(WIFI_IF_STA, ¤t_conf); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_get_config() for STA failed with %s", esp_err_to_name(result)); return result; } if (const auto result = esp_wifi_set_config(WIFI_IF_STA, ¤t_conf); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_set_config() for STA failed with %s", esp_err_to_name(result)); return result; } // TODO if (const auto result = wifi_set_esp_interface_ip(ESP_IF_WIFI_STA, /*config.sta.static_ip*/last_sta_static_ip); result != ESP_OK) { ESP_LOGE(TAG, "wifi_set_esp_interface_ip() for STA failed with %s", esp_err_to_name(result)); return result; } //last_sta_static_ip = config.sta.static_ip; if (const auto result = wifi_set_esp_interface_dns(ESP_IF_WIFI_STA, /*config.sta.static_dns*/last_sta_static_dns); result != ESP_OK) { ESP_LOGE(TAG, "wifi_set_esp_interface_dns() for STA failed with %s", esp_err_to_name(result)); return result; } //last_sta_static_dns = config.sta.static_dns; if (get_sta_status() != WiFiStaStatus::WL_CONNECTED) { _wifiConnectFailFlag = std::nullopt; if (const auto result = esp_wifi_connect(); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_connect() failed with %s", esp_err_to_name(result)); return result; } set_sta_status(WiFiStaStatus::WL_CONNECTING); } else ESP_LOGW(TAG, "when already connected?!"); return ESP_OK; } esp_err_t wifi_sta_disconnect(const config &config, bool wifioff, bool eraseap) { ESP_LOGI(TAG, "wifioff=%s eraseap=%s", wifioff?"true":"false", eraseap?"true":"false"); wifi_config_t conf = make_sta_config({}, {}, 0, {}, 0); if (!(wifi_get_mode() & WIFI_MODE_STA)) return ESP_FAIL; if (eraseap) { if (const auto result = esp_wifi_set_config(WIFI_IF_STA, &conf); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_set_config() failed with %s", esp_err_to_name(result)); return result; } } set_sta_status(WiFiStaStatus::WL_DISCONNECTING); if (const auto result = esp_wifi_disconnect(); result != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_disconnect() failed with %s", esp_err_to_name(result)); return result; } if (wifioff) { if (const auto result = wifi_enable_sta(false, config); result != ESP_OK) { ESP_LOGE(TAG, "wifi_enable_sta() failed with %s", esp_err_to_name(result)); return result; } } return ESP_OK; } std::string calculateWifisChecksum(const config &config) { std::string result; for (const auto &wifi : config.sta.wifis) { result += wifi.ssid; result += ","; result += wifi.key; result += "|"; } return result; } esp_err_t wifi_begin_scan(const config &config, bool show_hidden, bool passive, espchrono::milliseconds32 max_ms_per_chan, uint8_t channel) { if (wifi_get_status_bits() & WIFI_SCANNING_BIT) { ESP_LOGE(TAG, "while scan was still running"); return ESP_FAIL; } scanTimeout = max_ms_per_chan * 20; if (const auto result = wifi_enable_sta(true, config); result != ESP_OK) { ESP_LOGE(TAG, "wifi_enable_sta() failed with %s", esp_err_to_name(result)); return result; } delete_scan_result(); wifi_scan_config_t scan_config; scan_config.ssid = 0; scan_config.bssid = 0; scan_config.channel = channel; scan_config.show_hidden = show_hidden; if (passive) { scan_config.scan_type = WIFI_SCAN_TYPE_PASSIVE; scan_config.scan_time.passive = espchrono::milliseconds32{max_ms_per_chan}.count(); } else { scan_config.scan_type = WIFI_SCAN_TYPE_ACTIVE; scan_config.scan_time.active.min = 100; scan_config.scan_time.active.max = espchrono::milliseconds32{max_ms_per_chan}.count(); } if (const auto result = esp_wifi_scan_start(&scan_config, false) != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_scan_start() failed with %s", esp_err_to_name(result)); return result; } scanStarted = espchrono::millis_clock::now(); wifi_clear_status_bits(WIFI_SCAN_DONE_BIT); wifi_set_status_bits(WIFI_SCANNING_BIT); return ESP_OK; } void setWifiState(WiFiState newWifiState) { if (newWifiState != _wifiState) { ESP_LOGI(TAG, "state machine state changed from %s to %s", toString(_wifiState).c_str(), toString(newWifiState).c_str()); _wifiState = newWifiState; } } bool buildConnectPlan(const config &config) { const auto &scanResult = get_scan_result(); if (!scanResult) { ESP_LOGE(TAG, "no scan result available!"); return false; } return buildConnectPlan(config, *scanResult); } bool buildConnectPlan(const config &config, const scan_result &scanResult) { connectPlan.clear(); std::string foundSsids; for (const auto &entry : scanResult.entries) { std::string_view scanSSID{(const char *)entry.ssid}; // avoid duplicates if (std::any_of(std::begin(connectPlan), std::end(connectPlan), [&scanSSID](const auto &entry){ return cpputils::stringEqualsIgnoreCase(entry.config.ssid, scanSSID); })) continue; const auto iter = std::find_if(std::begin(config.sta.wifis), std::end(config.sta.wifis), [&scanSSID](const auto &entry){ return cpputils::stringEqualsIgnoreCase(entry.ssid, scanSSID); }); if (iter != std::end(config.sta.wifis)) { connectPlan.emplace_back(ConnectPlanItem{ .config = *iter, .channel = entry.primary, .authmode = entry.authmode, .rssi = entry.rssi, .bssid = mac_t{entry.bssid} }); } if (!foundSsids.empty()) foundSsids += ", "; foundSsids += scanSSID; } if (!connectPlan.empty()) { using wifi_stack::toString; const auto entry = connectPlan.front(); connectPlan.erase(std::begin(connectPlan)); ESP_LOGI(TAG, "connecting to %s (auth=%s, key=%s, channel=%i, rssi=%i, bssid=%s)", entry.config.ssid.c_str(), toString(entry.authmode).c_str(), entry.config.key.c_str(), entry.channel, entry.rssi, toString(entry.bssid).c_str() ); _lastConnect = espchrono::millis_clock::now(); _wifiConnectFailCounter = 0; if (const auto result = wifi_sta_begin(config, entry.config, entry.channel, entry.bssid); result != ESP_OK) ESP_LOGE(TAG, "wifi_sta_begin() failed with %s", esp_err_to_name(result)); setWifiState(WiFiState::Connecting); return true; } else { ESP_LOGW(TAG, "no configured ssid found"); ESP_LOGW(TAG, "found ssids: %s", foundSsids.c_str()); std::string configuredSsids; for (const auto &config : config.sta.wifis) { if (!configuredSsids.empty()) configuredSsids += ", "; configuredSsids += config.ssid; } ESP_LOGW(TAG, "configured ssids: %s", configuredSsids.c_str()); setWifiState(WiFiState::None); return false; } } void handleWifiEvents(const config &config, TickType_t xTicksToWait) { const WifiEvent *data{}; if (const auto result = wifi_event_queue->receive(&data, xTicksToWait); result != pdTRUE) { if (result != pdFALSE) ESP_LOGE(TAG, "wifi_event_queue->receive() failed with %i", result); return; } if (!data) { ESP_LOGE(TAG, "received nullptr event"); return; } std::unique_ptr event{data}; wifi_event_callback(config, *event); } #ifdef CONFIG_ETH_ENABLED esp_err_t eth_begin(const config &config, uint8_t phy_addr, int power, int mdc, int mdio, eth_phy_type_t type, eth_clock_mode_t clock_mode) { if (const auto result = wifi_tcpip_init(config); result != ESP_OK) { ESP_LOGE(TAG, "wifi_tcpip_init() failed with %s", esp_err_to_name(result)); return result; } eth_clock_mode = clock_mode; if (const auto result = tcpip_adapter_set_default_eth_handlers(); result != ESP_OK) { ESP_LOGE(TAG, "tcpip_adapter_set_default_eth_handlers() failed with %s", esp_err_to_name(result)); return result; } esp_netif_config_t cfg ESP_NETIF_DEFAULT_ETH(); esp_netifs[ESP_IF_ETH] = esp_netif_new(&cfg); if (!esp_netifs[ESP_IF_ETH]) { ESP_LOGE(TAG, "esp_netif_new() failed"); return ESP_FAIL; } if (const auto result = esp_eth_set_default_handlers(esp_netifs[ESP_IF_ETH]); result != ESP_OK) { ESP_LOGE(TAG, "esp_eth_set_default_handlers() failed with %s", esp_err_to_name(result)); return result; } esp_eth_mac_t *eth_mac{}; #if CONFIG_ETH_SPI_ETHERNET_DM9051 if (type == ETH_PHY_DM9051) { //todo ESP_LOGE(TAG, "ETH_PHY_DM9051 not implemented"); return ESP_FAIL; } else { #endif #if CONFIG_ETH_USE_ESP32_EMAC eth_mac_config_t mac_config ETH_MAC_DEFAULT_CONFIG(); mac_config.smi_mdc_gpio_num = mdc; mac_config.smi_mdio_gpio_num = mdio; mac_config.sw_reset_timeout_ms = 1000; eth_mac = esp_eth_mac_new_esp32(&mac_config); if (!eth_mac) { ESP_LOGE(TAG, "esp_eth_mac_new_esp32() failed"); return ESP_FAIL; } #endif #if CONFIG_ETH_SPI_ETHERNET_DM9051 } #endif eth_phy_config_t phy_config ETH_PHY_DEFAULT_CONFIG(); phy_config.phy_addr = phy_addr; phy_config.reset_gpio_num = power; esp_eth_phy_t *eth_phy{}; switch (type) { case ETH_PHY_LAN8720: eth_phy = esp_eth_phy_new_lan8720(&phy_config); if (!eth_phy) { ESP_LOGE(TAG, "esp_eth_phy_new_lan8720() failed"); return ESP_FAIL; } break; case ETH_PHY_TLK110: eth_phy = esp_eth_phy_new_ip101(&phy_config); if (!eth_phy) { ESP_LOGE(TAG, "esp_eth_phy_new_ip101() failed"); return ESP_FAIL; } break; case ETH_PHY_RTL8201: eth_phy = esp_eth_phy_new_rtl8201(&phy_config); if (!eth_phy) { ESP_LOGE(TAG, "esp_eth_phy_new_rtl8201() failed"); return ESP_FAIL; } break; case ETH_PHY_DP83848: eth_phy = esp_eth_phy_new_dp83848(&phy_config); if (!eth_phy) { ESP_LOGE(TAG, "esp_eth_phy_new_dp83848() failed"); return ESP_FAIL; } break; #if CONFIG_ETH_SPI_ETHERNET_DM9051 case ETH_PHY_DM9051: eth_phy = esp_eth_phy_new_dm9051(&phy_config); break; #endif case ETH_PHY_KSZ8041: eth_phy = esp_eth_phy_new_ksz8041(&phy_config); if (!eth_phy) { ESP_LOGE(TAG, "esp_eth_phy_new_ksz8041() failed"); return ESP_FAIL; } break; // case ETH_PHY_KSZ8081: // eth_phy = esp_eth_phy_new_ksz8081(&phy_config); // if (!eth_phy) // { // ESP_LOGE(TAG, "esp_eth_phy_new_ksz8081() failed"); // return ESP_FAIL; // } // break; default: ESP_LOGE(TAG, "unknown type %i", type); return ESP_FAIL; } eth_handle = {}; esp_eth_config_t eth_config ETH_DEFAULT_CONFIG(eth_mac, eth_phy); eth_config.on_lowlevel_init_done = eth_on_lowlevel_init_done; //eth_config.on_lowlevel_deinit_done = on_lowlevel_deinit_done; if (const auto result = esp_eth_driver_install(ð_config, ð_handle); result != ESP_OK) { ESP_LOGE(TAG, "esp_eth_driver_install() failed with %s", esp_err_to_name(result)); return result; } if (!eth_handle) { ESP_LOGE(TAG, "esp_eth_driver_install() invalid eth_handle"); return ESP_FAIL; } /* attach Ethernet driver to TCP/IP stack */ auto ptr = esp_eth_new_netif_glue(eth_handle); if (!ptr) { ESP_LOGE(TAG, "esp_eth_new_netif_glue() failed"); return ESP_FAIL; } if (const auto result = esp_netif_attach(esp_netifs[ESP_IF_ETH], ptr); result != ESP_OK) { ESP_LOGE(TAG, "esp_eth_driver_install() failed with %s", esp_err_to_name(result)); return result; } eth_initialized = true; if (config.eth.enabled) { if (const auto result = esp_eth_start(eth_handle); result != ESP_OK) { ESP_LOGE(TAG, "esp_eth_start() failed with %s", esp_err_to_name(result)); return result; } eth_started = true; if (const auto result = wifi_set_esp_interface_ip(ESP_IF_ETH, config.eth.static_ip); result != ESP_OK) { ESP_LOGE(TAG, "wifi_set_esp_interface_ip() for ETH failed with %s", esp_err_to_name(result)); //return result; } else { last_eth_static_ip = config.eth.static_ip; if (const auto result = wifi_set_esp_interface_dns(ESP_IF_ETH, config.eth.static_dns); result != ESP_OK) { ESP_LOGE(TAG, "wifi_set_esp_interface_dns() for ETH failed with %s", esp_err_to_name(result)); //return result; } else last_eth_static_dns = config.eth.static_dns; } } return ESP_OK; } esp_err_t eth_on_lowlevel_init_done(esp_eth_handle_t eth_handle) { #if CONFIG_IDF_TARGET_ESP32 if (eth_clock_mode > ETH_CLOCK_GPIO17_OUT) { return ESP_FAIL; } // First deinit current config if different #if CONFIG_ETH_RMII_CLK_INPUT if (eth_clock_mode != ETH_CLOCK_GPIO0_IN && eth_clock_mode != ETH_CLOCK_GPIO0_OUT) { pinMode(0, INPUT); } #endif #if CONFIG_ETH_RMII_CLK_OUTPUT #if CONFIG_ETH_RMII_CLK_OUTPUT_GPIO0 if (eth_clock_mode > ETH_CLOCK_GPIO0_OUT) { pinMode(0, INPUT); } #elif CONFIG_ETH_RMII_CLK_OUT_GPIO == 16 if (eth_clock_mode != ETH_CLOCK_GPIO16_OUT) { pinMode(16, INPUT); } #elif CONFIG_ETH_RMII_CLK_OUT_GPIO == 17 if (eth_clock_mode != ETH_CLOCK_GPIO17_OUT) { pinMode(17, INPUT); } #endif #endif // Setup interface for the correct pin #if CONFIG_ETH_PHY_INTERFACE_MII EMAC_EXT.ex_phyinf_conf.phy_intf_sel = 4; #endif if (eth_clock_mode == ETH_CLOCK_GPIO0_IN) { #ifndef CONFIG_ETH_RMII_CLK_INPUT // RMII clock (50MHz) input to GPIO0 //gpio_hal_iomux_func_sel(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_EMAC_TX_CLK); //PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[0]); pinMode(0, INPUT); pinMode(0, FUNCTION_6); EMAC_EXT.ex_clk_ctrl.ext_en = 1; EMAC_EXT.ex_clk_ctrl.int_en = 0; EMAC_EXT.ex_oscclk_conf.clk_sel = 1; #endif } else { if (eth_clock_mode == ETH_CLOCK_GPIO0_OUT) { #ifndef CONFIG_ETH_RMII_CLK_OUTPUT_GPIO0 // APLL clock output to GPIO0 (must be configured to 50MHz!) //gpio_hal_iomux_func_sel(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); //PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[0]); pinMode(0, OUTPUT); pinMode(0, FUNCTION_2); // Choose the APLL clock to output on GPIO REG_WRITE(PIN_CTRL, 6); #endif } else if (eth_clock_mode == ETH_CLOCK_GPIO16_OUT) { #if CONFIG_ETH_RMII_CLK_OUT_GPIO != 16 // RMII CLK (50MHz) output to GPIO16 //gpio_hal_iomux_func_sel(PERIPHS_IO_MUX_GPIO16_U, FUNC_GPIO16_EMAC_CLK_OUT); //PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[16]); pinMode(16, OUTPUT); pinMode(16, FUNCTION_6); #endif } else if (eth_clock_mode == ETH_CLOCK_GPIO17_OUT) { #if CONFIG_ETH_RMII_CLK_OUT_GPIO != 17 // RMII CLK (50MHz) output to GPIO17 //gpio_hal_iomux_func_sel(PERIPHS_IO_MUX_GPIO17_U, FUNC_GPIO17_EMAC_CLK_OUT_180); //PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[17]); pinMode(17, OUTPUT); pinMode(17, FUNCTION_6); #endif } #if CONFIG_ETH_RMII_CLK_INPUT EMAC_EXT.ex_clk_ctrl.ext_en = 0; EMAC_EXT.ex_clk_ctrl.int_en = 1; EMAC_EXT.ex_oscclk_conf.clk_sel = 0; emac_config_apll_clock(); EMAC_EXT.ex_clkout_conf.div_num = 0; EMAC_EXT.ex_clkout_conf.h_div_num = 0; #endif } #endif return ESP_OK; } #if CONFIG_ETH_RMII_CLK_INPUT void emac_config_apll_clock() { /* apll_freq = xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536)/((o_div + 2) * 2) */ rtc_xtal_freq_t rtc_xtal_freq = rtc_clk_xtal_freq_get(); switch (rtc_xtal_freq) { case RTC_XTAL_FREQ_40M: // Recommended /* 50 MHz = 40MHz * (4 + 6) / (2 * (2 + 2) = 50.000 */ /* sdm0 = 0, sdm1 = 0, sdm2 = 6, o_div = 2 */ rtc_clk_apll_enable(true, 0, 0, 6, 2); break; case RTC_XTAL_FREQ_26M: /* 50 MHz = 26MHz * (4 + 15 + 118 / 256 + 39/65536) / ((3 + 2) * 2) = 49.999992 */ /* sdm0 = 39, sdm1 = 118, sdm2 = 15, o_div = 3 */ rtc_clk_apll_enable(true, 39, 118, 15, 3); break; case RTC_XTAL_FREQ_24M: /* 50 MHz = 24MHz * (4 + 12 + 255 / 256 + 255/65536) / ((2 + 2) * 2) = 49.499977 */ /* sdm0 = 255, sdm1 = 255, sdm2 = 12, o_div = 2 */ rtc_clk_apll_enable(true, 255, 255, 12, 2); break; default: // Assume we have a 40M xtal rtc_clk_apll_enable(true, 0, 0, 6, 2); break; } } #endif #endif } // namespace } // namespace wifi_stack