chore: initial commit
This commit is contained in:
@@ -0,0 +1,308 @@
|
||||
#include "wifi_portal.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "ssid_manager.h"
|
||||
#include "wifi_manager.h"
|
||||
|
||||
static const char *TAG = "wifi_portal";
|
||||
|
||||
#define WIFI_PORTAL_LOCK_TIMEOUT_MS 2000
|
||||
#define WIFI_PORTAL_DEFAULT_CONNECT_TIMEOUT_MS 10000
|
||||
#define WIFI_PORTAL_CONNECT_POLL_MS 100
|
||||
|
||||
#ifndef CONFIG_TQ_WIFI_STA_POWER_SAVE
|
||||
#define CONFIG_TQ_WIFI_STA_POWER_SAVE 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_WIFI_STA_INACTIVE_TIME_SEC
|
||||
#define CONFIG_TQ_WIFI_STA_INACTIVE_TIME_SEC 15
|
||||
#endif
|
||||
|
||||
static SemaphoreHandle_t s_portal_mutex;
|
||||
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||||
static StaticSemaphore_t s_portal_mutex_buf;
|
||||
#endif
|
||||
|
||||
static bool s_wifi_manager_initialized;
|
||||
static std::string s_config_ap_ssid_prefix = "TQ-CAT";
|
||||
static wifi_portal_behavior_hooks_t s_behavior_hooks = {};
|
||||
|
||||
static esp_err_t apply_sta_stability_policy(void);
|
||||
|
||||
struct PortalUnlockGuard {
|
||||
~PortalUnlockGuard()
|
||||
{
|
||||
if (active) {
|
||||
(void)xSemaphoreGiveRecursive(s_portal_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
bool active = true;
|
||||
};
|
||||
|
||||
static esp_err_t wifi_portal_lock(void)
|
||||
{
|
||||
if (!s_portal_mutex) {
|
||||
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||||
s_portal_mutex = xSemaphoreCreateRecursiveMutexStatic(&s_portal_mutex_buf);
|
||||
#else
|
||||
s_portal_mutex = xSemaphoreCreateRecursiveMutex();
|
||||
#endif
|
||||
if (!s_portal_mutex) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
}
|
||||
|
||||
TickType_t timeout_ticks = pdMS_TO_TICKS(WIFI_PORTAL_LOCK_TIMEOUT_MS);
|
||||
if (timeout_ticks == 0) {
|
||||
timeout_ticks = 1;
|
||||
}
|
||||
if (xSemaphoreTakeRecursive(s_portal_mutex, timeout_ticks) != pdTRUE) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static wifi_portal_behavior_hooks_t snapshot_behavior_hooks(void)
|
||||
{
|
||||
wifi_portal_behavior_hooks_t hooks = {};
|
||||
if (wifi_portal_lock() != ESP_OK) {
|
||||
return hooks;
|
||||
}
|
||||
|
||||
hooks = s_behavior_hooks;
|
||||
(void)xSemaphoreGiveRecursive(s_portal_mutex);
|
||||
return hooks;
|
||||
}
|
||||
|
||||
static void notify_keep_awake(bool keep_awake)
|
||||
{
|
||||
wifi_portal_behavior_hooks_t hooks = snapshot_behavior_hooks();
|
||||
if (hooks.set_keep_awake != NULL) {
|
||||
hooks.set_keep_awake(keep_awake);
|
||||
}
|
||||
}
|
||||
|
||||
static void wifi_manager_event_callback(WifiEvent event, const std::string &data)
|
||||
{
|
||||
switch (event) {
|
||||
case WifiEvent::Connecting:
|
||||
ESP_LOGI(TAG, "Connecting to \"%s\"", data.c_str());
|
||||
break;
|
||||
case WifiEvent::Connected:
|
||||
ESP_LOGI(TAG, "Connected to \"%s\"", data.c_str());
|
||||
(void)apply_sta_stability_policy();
|
||||
break;
|
||||
case WifiEvent::Disconnected:
|
||||
if (!data.empty()) {
|
||||
ESP_LOGW(TAG, "Wi-Fi disconnected, reason %s", data.c_str());
|
||||
}
|
||||
break;
|
||||
case WifiEvent::ConfigModeEnter:
|
||||
ESP_LOGI(TAG, "Wi-Fi configuration portal started");
|
||||
break;
|
||||
case WifiEvent::ConfigModeExit:
|
||||
ESP_LOGI(TAG, "Wi-Fi configuration portal stopped");
|
||||
notify_keep_awake(false);
|
||||
break;
|
||||
case WifiEvent::Scanning:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t ensure_wifi_manager_initialized(void)
|
||||
{
|
||||
if (s_wifi_manager_initialized) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
WifiManagerConfig config;
|
||||
config.ssid_prefix = s_config_ap_ssid_prefix;
|
||||
config.language = "zh-CN";
|
||||
|
||||
auto &manager = WifiManager::GetInstance();
|
||||
if (!manager.Initialize(config)) {
|
||||
ESP_LOGE(TAG, "Wi-Fi manager initialization failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
manager.SetEventCallback(wifi_manager_event_callback);
|
||||
s_wifi_manager_initialized = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void seed_default_credentials(const char *ssid, const char *password)
|
||||
{
|
||||
auto &manager = SsidManager::GetInstance();
|
||||
if (!manager.GetSsidList().empty()) {
|
||||
return;
|
||||
}
|
||||
if (ssid == NULL || ssid[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
manager.AddSsid(ssid, password != NULL ? password : "");
|
||||
ESP_LOGI(TAG, "Seeded Wi-Fi credentials for SSID \"%s\"", ssid);
|
||||
}
|
||||
|
||||
static int normalize_connect_timeout_ms(int timeout_ms)
|
||||
{
|
||||
return timeout_ms > 0 ? timeout_ms : WIFI_PORTAL_DEFAULT_CONNECT_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
static esp_err_t apply_sta_stability_policy(void)
|
||||
{
|
||||
wifi_ps_type_t ps_type = CONFIG_TQ_WIFI_STA_POWER_SAVE ?
|
||||
WIFI_PS_MIN_MODEM :
|
||||
WIFI_PS_NONE;
|
||||
esp_err_t err = esp_wifi_set_ps(ps_type);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Set Wi-Fi power save failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
uint16_t inactive_sec = CONFIG_TQ_WIFI_STA_INACTIVE_TIME_SEC;
|
||||
if (inactive_sec < 3U) {
|
||||
inactive_sec = 3U;
|
||||
}
|
||||
err = esp_wifi_set_inactive_time(WIFI_IF_STA, inactive_sec);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG,
|
||||
"Set Wi-Fi STA inactive time to %u s failed: %s",
|
||||
(unsigned)inactive_sec,
|
||||
esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"Wi-Fi STA stability policy: ps=%s inactive=%us",
|
||||
ps_type == WIFI_PS_NONE ? "none" : "min_modem",
|
||||
(unsigned)inactive_sec);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t wifi_portal_init_defaults(const char *seed_ssid, const char *seed_password)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(wifi_portal_lock(), TAG, "Failed to lock Wi-Fi portal");
|
||||
PortalUnlockGuard guard;
|
||||
|
||||
seed_default_credentials(seed_ssid, seed_password);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool wifi_portal_has_saved_credentials(void)
|
||||
{
|
||||
return !SsidManager::GetInstance().GetSsidList().empty();
|
||||
}
|
||||
|
||||
bool wifi_portal_sta_is_connected(void)
|
||||
{
|
||||
if (!s_wifi_manager_initialized) {
|
||||
return false;
|
||||
}
|
||||
return WifiManager::GetInstance().IsConnected();
|
||||
}
|
||||
|
||||
esp_err_t wifi_portal_start_config_ap(const char *ssid_prefix)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(wifi_portal_lock(), TAG, "Failed to lock Wi-Fi portal");
|
||||
PortalUnlockGuard guard;
|
||||
|
||||
if (ssid_prefix != NULL && ssid_prefix[0] != '\0') {
|
||||
s_config_ap_ssid_prefix = ssid_prefix;
|
||||
}
|
||||
|
||||
ESP_RETURN_ON_ERROR(ensure_wifi_manager_initialized(), TAG, "Failed to initialize Wi-Fi manager");
|
||||
WifiManager::GetInstance().StartConfigAp();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t wifi_portal_connect_sta(int timeout_ms)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(wifi_portal_lock(), TAG, "Failed to lock Wi-Fi portal");
|
||||
PortalUnlockGuard guard;
|
||||
|
||||
if (!wifi_portal_has_saved_credentials()) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
ESP_RETURN_ON_ERROR(ensure_wifi_manager_initialized(), TAG, "Failed to initialize Wi-Fi manager");
|
||||
|
||||
auto &manager = WifiManager::GetInstance();
|
||||
if (manager.IsConnected()) {
|
||||
(void)apply_sta_stability_policy();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
manager.StartStation();
|
||||
(void)apply_sta_stability_policy();
|
||||
|
||||
int effective_timeout_ms = normalize_connect_timeout_ms(timeout_ms);
|
||||
int64_t deadline_us = esp_timer_get_time() + (int64_t)effective_timeout_ms * 1000;
|
||||
while (esp_timer_get_time() < deadline_us) {
|
||||
if (manager.IsConnected()) {
|
||||
return ESP_OK;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(WIFI_PORTAL_CONNECT_POLL_MS));
|
||||
}
|
||||
|
||||
manager.StopStation();
|
||||
ESP_LOGW(TAG, "Wi-Fi connect timed out after %d ms", effective_timeout_ms);
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
esp_err_t wifi_portal_save_credentials(const char *ssid, const char *password)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(wifi_portal_lock(), TAG, "Failed to lock Wi-Fi portal");
|
||||
PortalUnlockGuard guard;
|
||||
|
||||
ESP_RETURN_ON_FALSE(ssid != NULL && ssid[0] != '\0',
|
||||
ESP_ERR_INVALID_ARG,
|
||||
TAG,
|
||||
"SSID missing");
|
||||
ESP_RETURN_ON_FALSE(strlen(ssid) <= 32, ESP_ERR_INVALID_ARG, TAG, "SSID too long");
|
||||
ESP_RETURN_ON_FALSE(password == NULL || strlen(password) <= 64,
|
||||
ESP_ERR_INVALID_ARG,
|
||||
TAG,
|
||||
"Password too long");
|
||||
|
||||
SsidManager::GetInstance().AddSsid(ssid, password != NULL ? password : "");
|
||||
ESP_LOGI(TAG, "Saved Wi-Fi credentials for SSID \"%s\"", ssid);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t wifi_portal_clear_saved_credentials(void)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(wifi_portal_lock(), TAG, "Failed to lock Wi-Fi portal");
|
||||
PortalUnlockGuard guard;
|
||||
|
||||
SsidManager::GetInstance().Clear();
|
||||
ESP_LOGI(TAG, "Cleared stored Wi-Fi credentials");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void wifi_portal_set_behavior_hooks(const wifi_portal_behavior_hooks_t *hooks)
|
||||
{
|
||||
if (wifi_portal_lock() != ESP_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hooks == NULL) {
|
||||
memset(&s_behavior_hooks, 0, sizeof(s_behavior_hooks));
|
||||
} else {
|
||||
s_behavior_hooks = *hooks;
|
||||
}
|
||||
(void)xSemaphoreGiveRecursive(s_portal_mutex);
|
||||
}
|
||||
Reference in New Issue
Block a user