0
0
Fork 0

feat(wifi) save credential on successful connection and auto connect to it

This commit is contained in:
kerms 2024-03-05 16:42:29 +08:00
parent a4d65c367d
commit 6fdfa76a14
21 changed files with 753 additions and 158 deletions

View File

@ -0,0 +1,7 @@
file(GLOB SOURCES *.c)
idf_component_register(
SRCS ${SOURCES}
INCLUDE_DIRS "."
)

96
components/utils/list.h Normal file
View File

@ -0,0 +1,96 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LIST_H_GUARD
#define LIST_H_GUARD
#include <stddef.h>
/**
* struct dl_list - Doubly-linked list
*/
struct dl_list {
struct dl_list *next;
struct dl_list *prev;
};
static inline void dl_list_init(struct dl_list *list)
{
list->next = list;
list->prev = list;
}
static inline void dl_list_add(struct dl_list *list, struct dl_list *item)
{
item->next = list->next;
item->prev = list;
list->next->prev = item;
list->next = item;
}
static inline void dl_list_add_tail(struct dl_list *list, struct dl_list *item)
{
dl_list_add(list->prev, item);
}
static inline void dl_list_del(struct dl_list *item)
{
item->next->prev = item->prev;
item->prev->next = item->next;
item->next = NULL;
item->prev = NULL;
}
static inline int dl_list_empty(struct dl_list *list)
{
return list->next == list;
}
static inline unsigned int dl_list_len(struct dl_list *list)
{
struct dl_list *item;
int count = 0;
for (item = list->next; item != list; item = item->next)
count++;
return count;
}
#ifndef offsetof
#define offsetof(type, member) ((long) &((type *) 0)->member)
#endif
#define dl_list_entry(item, type, member) \
((type *) ((char *) item - offsetof(type, member)))
#define dl_list_first(list, type, member) \
(dl_list_empty((list)) ? NULL : \
dl_list_entry((list)->next, type, member))
#define dl_list_last(list, type, member) \
(dl_list_empty((list)) ? NULL : \
dl_list_entry((list)->prev, type, member))
#define dl_list_for_each(item, list, type, member) \
for (item = dl_list_entry((list)->next, type, member); \
&item->member != (list); \
item = dl_list_entry(item->member.next, type, member))
#define dl_list_for_each_safe(item, n, list, type, member) \
for (item = dl_list_entry((list)->next, type, member), \
n = dl_list_entry(item->member.next, type, member); \
&item->member != (list); \
item = n, n = dl_list_entry(n->member.next, type, member))
#define dl_list_for_each_reverse(item, list, type, member) \
for (item = dl_list_entry((list)->prev, type, member); \
&item->member != (list); \
item = dl_list_entry(item->member.prev, type, member))
#define DEFINE_DL_LIST(name) \
struct dl_list name = { &(name), &(name) }
#endif //LIST_H_GUARD

View File

@ -26,13 +26,13 @@ _Noreturn void req_long_task(void *arg)
if (unlikely(xQueueReceive(long_run_queue, &req, portMAX_DELAY) != pdTRUE)) {
continue;
}
int status = req->module.helper_cb(req->module.arg);
req->status = req->module.helper_cb(req->module.arg);
/* if send out queue is busy, set status and let the cb to cancel send out
* */
if (req_queue_push_send_out(req, pdMS_TO_TICKS(20)) != 0) {
status = -1;
req->send_out.cb(req->send_out.arg, status);
req->status = -1;
req->send_out.cb(req->send_out.arg, req->status);
}
}
}
@ -42,7 +42,7 @@ _Noreturn void req_send_out_task(void *arg)
req_task_cb_t *req;
while (1) {
if (likely(xQueueReceive(send_out_queue, &req, portMAX_DELAY))) {
req->send_out.cb(req->send_out.arg, 0);
req->send_out.cb(req->send_out.arg, req->status);
}
}
}

View File

@ -22,6 +22,7 @@ typedef struct req_module_cb_t {
typedef struct req_task_cb_t {
req_module_cb_t module;
req_send_out_cb_t send_out;
int status;
} req_task_cb_t;
int request_runner_init();

View File

@ -20,7 +20,7 @@ typedef struct post_request_t {
} post_request_t;
static void async_send_out_cb(void *arg, int module_status);
static int uri_api_send_out(httpd_req_t *req, post_request_t *post_req);
static int uri_api_send_out(httpd_req_t *req, post_request_t *post_req, int err);
static esp_err_t api_post_handler(httpd_req_t *req)
@ -87,7 +87,7 @@ static esp_err_t api_post_handler(httpd_req_t *req)
}
/* api function returns something, send back to http client */
err = uri_api_send_out(req, post_req);
err = uri_api_send_out(req, post_req, 0);
goto put_buf;
end:
@ -101,19 +101,21 @@ put_buf:
return err;
}
int uri_api_send_out(httpd_req_t *req, post_request_t *post_req)
int uri_api_send_out(httpd_req_t *req, post_request_t *post_req, int err)
{
int err;
char *buf;
uint32_t buf_len;
buf = post_req->buf;
buf_len = static_buffer_get_buf_size() - sizeof(post_request_t);
httpd_resp_set_type(req, HTTPD_TYPE_JSON);
err = !cJSON_PrintPreallocated(post_req->json.out, buf, buf_len - 5, 0);
cJSON_Delete(post_req->json.out);
cJSON_Delete(post_req->json.in);
if (post_req->json.out) {
ESP_LOGI(TAG, "json out ok");
httpd_resp_set_type(req, HTTPD_TYPE_JSON);
err = !cJSON_PrintPreallocated(post_req->json.out, buf, buf_len - 5, 0);
cJSON_Delete(post_req->json.out);
}
if (unlikely(err)) {
httpd_resp_set_status(req, HTTPD_500);
return httpd_resp_send(req, NULL, 0);
@ -126,11 +128,13 @@ void async_send_out_cb(void *arg, int module_status)
{
post_request_t *req = arg;
if (module_status != API_JSON_OK) {
httpd_sess_trigger_close(req->req_out->handle,
httpd_req_to_sockfd(req->req_out->handle));
/* BUG: httpd_req_to_sockfd cause crash, fd not closed for the moment
* issue is opened on esp-idf github */
// httpd_sess_trigger_close(req->req_out->handle,
// httpd_req_to_sockfd(req->req_out->handle));
}
uri_api_send_out(req->req_out, req);
uri_api_send_out(req->req_out, req, module_status);
/* clean resources */
httpd_req_async_handler_complete(req->req_out);
@ -150,7 +154,6 @@ static const httpd_uri_t uri_api = {
static int URI_API_INIT(const httpd_uri_t **uri_conf) {
*uri_conf = &uri_api;
api_json_router_init();
return 0;
}

View File

@ -136,13 +136,11 @@ static const httpd_uri_t uri_api = {
.user_ctx = NULL,
.is_websocket = true,
.supported_subprotocol = NULL,
/* in esp-idf v5.2.0, set to false cause infinite queue_work fail on wifi disconnect when at least 1 ws client */
.handle_ws_control_frames = true,
};
static int WS_REQ_INIT(const httpd_uri_t **uri_conf) {
*uri_conf = &uri_api;
api_json_router_init();
return 0;
}

View File

@ -4,13 +4,14 @@ file(GLOB SOURCES
wifi_api_json.c
wifi_api.c
wifi_json_utils.c
wifi_storage.c
)
idf_component_register(
SRCS ${SOURCES}
INCLUDE_DIRS "."
PRIV_REQUIRES mdns esp_wifi esp_event api_router
PRIV_REQUIRES mdns esp_wifi esp_event api_router wt_storage
)
idf_component_set_property(${COMPONENT_NAME} WHOLE_ARCHIVE ON)

View File

@ -27,9 +27,6 @@ static int rssi_comp(const void *a, const void *b)
/**
* @brief blocking function
* @param number
* @param ap_info
* @return
*/
int wifi_api_get_scan_list(uint16_t *number, wifi_api_ap_info_t *ap_info)
{
@ -76,13 +73,12 @@ static void wifi_manager_scan_done(uint16_t ap_found, wifi_ap_record_t *records,
scan_done_cb(ap_found, ap_info, arg);
}
int wifi_api_trigger_scan(uint16_t *max_ap_count, wifi_api_scan_done_cb cb, void *cb_arg)
int wifi_api_connect(const char *ssid, const char *password)
{
int err;
err = wifi_manager_trigger_scan(wifi_manager_scan_done, cb_arg);
if (err) {
return err;
}
scan_done_cb = cb;
return 0;
return wifi_manager_connect(ssid, password);
}
int wifi_api_disconnect(void)
{
return wifi_manager_disconnect();
}

View File

@ -20,5 +20,9 @@ int wifi_api_trigger_scan(uint16_t *max_ap_count, wifi_api_scan_done_cb cb, void
int wifi_api_get_scan_list(uint16_t *number, wifi_api_ap_info_t *ap_info);
int wifi_api_connect(const char *ssid, const char *password);
int wifi_api_disconnect(void);
#endif //WIFI_API_H_GUARD

View File

@ -10,34 +10,44 @@ static int wifi_api_json_get_ap_info(api_json_req_t *req);
static int wifi_api_json_get_scan(api_json_req_t *req);
static int on_async_call(void *arg)
static int wifi_api_json_connect(api_json_req_t *req);
static int wifi_api_json_disconnect(api_json_req_t *req);
static int async_helper_cb(void *arg)
{
api_json_module_req_t *req = arg;
return req->func(req->arg);
}
static inline int set_async(api_json_req_t *req, api_json_module_async_t *async, int (*func)(api_json_req_t *))
{
async->module.func = func;
async->module.arg = req;
async->req_task.module.helper_cb = async_helper_cb;
async->req_task.module.arg = &async->module;
return API_JSON_ASYNC;
}
static int on_json_req(uint16_t cmd, api_json_req_t *req, api_json_module_async_t *async)
{
wifi_api_json_cmd_t wifi_cmd = cmd;
switch (wifi_cmd) {
case WIFI_API_JSON_GET_AP_INFO:
return wifi_api_json_get_ap_info(req);
case WIFI_API_JSON_CONNECT:
break;
case WIFI_API_JSON_GET_SCAN:
async->module.func = wifi_api_json_get_scan;
async->module.arg = req;
async->req_task.module.helper_cb = on_async_call;
async->req_task.module.arg = &async->module;
return API_JSON_ASYNC;
case UNKNOWN:
default:
break;
case WIFI_API_JSON_GET_AP_INFO:
return wifi_api_json_get_ap_info(req);
case WIFI_API_JSON_CONNECT:
return set_async(req, async, wifi_api_json_connect);
case WIFI_API_JSON_GET_SCAN:
return set_async(req, async, wifi_api_json_get_scan);
case WIFI_API_JSON_DISCONNECT:
return wifi_api_json_disconnect(req);
}
printf("cmd %d\n", cmd);
printf("cmd %d not executed\n", cmd);
return 0;
}
@ -70,6 +80,26 @@ static int wifi_api_json_get_scan(api_json_req_t *req)
}
int wifi_api_json_connect(api_json_req_t *req)
{
char *ssid;
char *password;
ssid = cJSON_GetStringValue(cJSON_GetObjectItem(req->in, "ssid"));
password = cJSON_GetStringValue(cJSON_GetObjectItem(req->in, "password"));
if (ssid == NULL || password == NULL) {
return 1;
}
printf("trigger connect\n");
return wifi_api_connect(ssid, password);
};
int wifi_api_json_disconnect(api_json_req_t *req)
{
return wifi_api_disconnect();
}
/* ****
* register module
* */
@ -81,6 +111,6 @@ static int wifi_api_json_init(api_json_module_cfg_t *cfg)
return 0;
}
API_JSON_MODULE_REGISTER(0x90, wifi_api_json_init);
API_JSON_MODULE_REGISTER(0x90, wifi_api_json_init)

View File

@ -6,6 +6,7 @@ typedef enum wifi_api_json_cmd_t {
WIFI_API_JSON_GET_AP_INFO,
WIFI_API_JSON_CONNECT,
WIFI_API_JSON_GET_SCAN,
WIFI_API_JSON_DISCONNECT,
} wifi_api_json_cmd_t;

View File

@ -44,42 +44,52 @@ void ip_event_handler(void *handler_arg __attribute__((unused)),
}
}
typedef struct wifi_scan_ctx_t {
wifi_ap_record_t *ap;
wifi_event_scan_done_cb cb;
uint16_t number;
} wifi_scan_ctx_t;
typedef struct wifi_event_ctx_t {
/* WIFI scan */
struct {
wifi_ap_record_t *ap;
wifi_event_scan_done_cb cb;
uint16_t number;
};
/* WIFI connect */
struct {
wifi_event_connect_done_cb cb;
void *arg;
void (*disconn_handler)(void);
uint8_t attempt;
} conn;
uint8_t is_connected;
} wifi_event_ctx_t;
static wifi_scan_ctx_t scan_ctx = {
static wifi_event_ctx_t event_ctx = {
.ap = NULL,
.is_connected = 0,
.conn.disconn_handler = NULL,
};
static void wifi_on_scan_done(wifi_event_sta_scan_done_t *event)
{
int err;
if (!scan_ctx.cb || !scan_ctx.ap) {
scan_ctx.number = 0;
if (!event_ctx.cb || !event_ctx.ap) {
event_ctx.number = 0;
} else if (event->status == 1) {
/* error */
scan_ctx.number = 0;
event_ctx.number = 0;
} else if (event->number == 0) {
/* no ap found on current channel */
scan_ctx.number = 0;
event_ctx.number = 0;
}
err = esp_wifi_scan_get_ap_records(&scan_ctx.number, scan_ctx.ap);
if (err) {
esp_wifi_clear_ap_list();
}
esp_wifi_scan_get_ap_records(&event_ctx.number, event_ctx.ap);
esp_wifi_clear_ap_list();
int i;
for (i = 0; i < scan_ctx.number; ++i) {
if (scan_ctx.ap[i].rssi < -80)
for (i = 0; i < event_ctx.number; ++i) {
if (event_ctx.ap[i].rssi < -80)
break;
}
scan_ctx.number = i;
return scan_ctx.cb(scan_ctx.number, scan_ctx.ap);
event_ctx.number = i;
return event_ctx.cb(event_ctx.number, event_ctx.ap);
}
@ -108,12 +118,20 @@ int wifi_event_trigger_scan(uint8_t channel, wifi_event_scan_done_cb cb, uint16_
return err;
}
scan_ctx.cb = cb;
scan_ctx.number = number;
scan_ctx.ap = aps;
event_ctx.cb = cb;
event_ctx.number = number;
event_ctx.ap = aps;
return 0;
}
/*
* WIFI EVENT
* */
static void reconnect_after_disco();
void event_on_connected(wifi_event_sta_connected_t *event);
void wifi_event_handler(void *handler_arg __attribute__((unused)),
esp_event_base_t event_base __attribute__((unused)),
int32_t event_id,
@ -128,13 +146,12 @@ void wifi_event_handler(void *handler_arg __attribute__((unused)),
// printf("event: WIFI_EVENT_SCAN_DONE: ok: %lu, nr: %u, seq: %u\n",
// event->status, event->number, event->scan_id);
wifi_on_scan_done(event);
scan_ctx.ap = NULL;
scan_ctx.cb = NULL;
event_ctx.ap = NULL;
event_ctx.cb = NULL;
break;
}
case WIFI_EVENT_STA_START:
printf("event: WIFI_EVENT_STA_START\n");
esp_wifi_connect();
break;
case WIFI_EVENT_STA_CONNECTED: {
wifi_event_sta_connected_t *event = event_data;
@ -145,6 +162,8 @@ void wifi_event_handler(void *handler_arg __attribute__((unused)),
#ifdef CONFIG_EXAMPLE_IPV6
tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA);
#endif
event_ctx.is_connected = 1;
event_on_connected(event);
break;
}
case WIFI_EVENT_STA_DISCONNECTED: {
@ -153,8 +172,8 @@ void wifi_event_handler(void *handler_arg __attribute__((unused)),
printf("event: WIFI_EVENT_STA_DISCONNECTED ");
printf("sta %02X:%02X:%02X:%02X:%02X:%02X disconnect reason %d\n",
m[0], m[1], m[2], m[3], m[4], m[5], event->reason);
/* auto reconnect after disconnection */
esp_wifi_connect();
event_ctx.is_connected = 0;
reconnect_after_disco();
break;
}
case WIFI_EVENT_AP_START:
@ -181,3 +200,65 @@ void wifi_event_handler(void *handler_arg __attribute__((unused)),
}
}
void wifi_event_set_disco_handler(void (*disconn_handler)(void))
{
event_ctx.conn.disconn_handler = disconn_handler;
}
int wifi_event_trigger_connect(uint8_t attempt, wifi_event_connect_done_cb cb, void *arg)
{
int err;
event_ctx.conn.attempt = attempt;
event_ctx.conn.cb = cb;
event_ctx.conn.arg = arg;
if (event_ctx.is_connected) {
err = esp_wifi_disconnect();
} else {
err = esp_wifi_connect();
}
return err;
}
void event_on_connected(wifi_event_sta_connected_t *event)
{
if (event_ctx.conn.cb) {
event_ctx.conn.cb(event_ctx.conn.arg, event);
}
event_ctx.conn.attempt = 0;
event_ctx.conn.cb = NULL;
event_ctx.conn.arg = NULL;
}
void reconnect_after_disco()
{
int err;
ESP_LOGI(TAG, "reco... atempt %d", event_ctx.conn.attempt);
if (event_ctx.conn.attempt == 0) {
goto call;
}
err = esp_wifi_connect();
if (err) {
event_ctx.conn.attempt = 0;
/* connect err: stop connect and call cb */
ESP_LOGE(TAG, "wifi connect error %s", esp_err_to_name(err));
goto call;
}
event_ctx.conn.attempt--;
return;
call:
if (event_ctx.conn.cb) {
event_ctx.conn.cb(event_ctx.conn.arg, NULL);
} else {
/* disconnected from extern environment, notify */
if (event_ctx.conn.disconn_handler) {
event_ctx.conn.disconn_handler();
}
}
event_ctx.conn.cb = NULL;
event_ctx.conn.arg = NULL;
}

View File

@ -9,10 +9,17 @@ void ip_event_handler(void *handler_arg, esp_event_base_t event_base, int32_t ev
void wifi_event_handler(void *handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
typedef void (*wifi_event_scan_done_cb)(uint16_t ap_count, wifi_ap_record_t *aps_info);
int wifi_event_trigger_scan(uint8_t channel, wifi_event_scan_done_cb cb, uint16_t number, wifi_ap_record_t *aps);
void wifi_event_set_disco_handler(void (*disconn_handler)(void));
/**
* @brief
* @param connect_status 0: SUCCESS, OTHER: ERROR
*/
typedef void (*wifi_event_connect_done_cb)(void *arg, wifi_event_sta_connected_t *event);
int wifi_event_trigger_connect(uint8_t attempt, wifi_event_connect_done_cb cb, void *arg);
#endif //WIFI_EVENT_HANDLER_H_GUARD

View File

@ -1,6 +1,7 @@
#include "wifi_manager.h"
#include "wifi_configuration.h"
#include "wifi_event_handler.h"
#include "wifi_storage.h"
#include <esp_err.h>
#include <esp_netif.h>
@ -16,26 +17,41 @@
#define TAG __FILENAME__
typedef struct wifi_scan_ctx_t {
wifi_ap_record_t *ap;
wifi_manager_scan_done_cb cb;
void *arg;
typedef struct wifi_ctx_t {
SemaphoreHandle_t lock;
TaskHandle_t task;
uint16_t total_aps;
uint16_t nr_aps_in_channel;
uint8_t status;
uint8_t max_ap;
} wifi_scan_ctx_t;
union {
struct {
wifi_ap_record_t *ap;
wifi_manager_scan_done_cb cb;
void *arg;
uint16_t total_aps;
uint16_t nr_aps_in_channel;
uint8_t max_ap;
} scan;
struct {
wifi_event_sta_connected_t *event;
uint8_t need_unlock; /* used when trigger connection from wifi_manager instead of wifi_api */
} conn;
};
uint8_t is_endless_connect:1;
uint8_t auto_reconnect:1;
uint8_t reserved:6;
} wifi_ctx_t;
static esp_netif_t *ap_netif;
static esp_netif_t *sta_netif;
static wifi_scan_ctx_t scan_ctx;
static wifi_ctx_t ctx;
static void set_sta_cred(const char *ssid, const char *password);
static void disconn_handler(void);
static int set_default_sta_cred(void);
void wifi_manager_init(void)
{
esp_err_t err;
uint8_t do_connect = 0;
ap_netif = esp_netif_create_default_wifi_ap();
assert(ap_netif);
@ -54,26 +70,21 @@ void wifi_manager_init(void)
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
wifi_config_t ap_config = {0};
{
wifi_config_t ap_config = {0};
strncpy((char *) ap_config.ap.ssid, WIFI_DEFAULT_AP_SSID, 32);
strncpy((char *) ap_config.ap.password, WIFI_DEFAULT_AP_PASS, 64);
ap_config.ap.authmode = WIFI_AUTH_WPA2_WPA3_PSK;
ap_config.ap.max_connection = 4;
ap_config.ap.channel = 6;
ap_config.ap.ssid_hidden = 0;
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_config));
strncpy((char *) ap_config.ap.ssid, WIFI_DEFAULT_AP_SSID, 32);
strncpy((char *) ap_config.ap.password, WIFI_DEFAULT_AP_PASS, 64);
ap_config.ap.authmode = WIFI_AUTH_WPA2_WPA3_PSK;
ap_config.ap.max_connection = 4;
ap_config.ap.channel = 6;
ap_config.ap.ssid_hidden = 0;
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_config));
}
// TODO: scan once and connect to known wifi
wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_DEFAULT_STA_SSID,
.password = WIFI_DEFAULT_STA_PASS,
.sort_method = WIFI_CONNECT_AP_BY_SIGNAL,
},
};
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
if (set_default_sta_cred() == 0) {
do_connect = 1;
}
/* TODO: Read from nvs */
esp_netif_ip_info_t ip_info;
@ -96,35 +107,42 @@ void wifi_manager_init(void)
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "wifi started");
esp_log_level_set("wifi", ESP_LOG_WARN);
scan_ctx.lock = xSemaphoreCreateBinary();
scan_ctx.status = 0;
xSemaphoreGive(scan_ctx.lock);
ctx.lock = xSemaphoreCreateBinary();
ctx.is_endless_connect = 0;
ctx.auto_reconnect = 1;
xSemaphoreGive(ctx.lock);
wifi_event_set_disco_handler(disconn_handler);
if (do_connect) {
disconn_handler();
}
}
static void wifi_event_scan_channel_done(uint16_t number, wifi_ap_record_t *aps)
{
scan_ctx.nr_aps_in_channel = number;
scan_ctx.total_aps += number;
if (scan_ctx.task) {
xTaskNotifyGive(scan_ctx.task);
ctx.scan.nr_aps_in_channel = number;
ctx.scan.total_aps += number;
if (ctx.task) {
xTaskNotifyGive(ctx.task);
}
}
static void scan_loop()
static int scan_loop()
{
uint32_t ret;
uint16_t number;
scan_ctx.total_aps = 0;
ctx.scan.total_aps = 0;
for (int scan_channel = 1; scan_channel <= 13; ++scan_channel) {
number = scan_ctx.max_ap - scan_ctx.total_aps;
number = ctx.scan.max_ap - ctx.scan.total_aps;
if (wifi_event_trigger_scan(scan_channel,
wifi_event_scan_channel_done, number,
&scan_ctx.ap[scan_ctx.total_aps])) {
&ctx.scan.ap[ctx.scan.total_aps])) {
ESP_LOGE(TAG, "trigger scan %d error", scan_channel);
return;
return 1;
}
/* shadow wifi_event_scan_channel_done() called */
vTaskDelay(100);
@ -133,59 +151,51 @@ static void scan_loop()
if (ret == 0) {
/* timeout */
ESP_LOGE(TAG, "scan channel %d timeout", scan_channel);
return;
return 1;
}
}
}
static void wifi_manager_scan_task(void *arg)
{
scan_loop();
free(scan_ctx.ap);
/* callback */
if (scan_ctx.cb) {
scan_ctx.cb(scan_ctx.total_aps, scan_ctx.ap, arg);
}
xSemaphoreGive(scan_ctx.lock);
vTaskDelete(NULL);
}
int wifi_manager_trigger_scan(wifi_manager_scan_done_cb cb, void *arg)
{
if (xSemaphoreTake(scan_ctx.lock, pdMS_TO_TICKS(0)) != pdTRUE) {
return 1;
}
/* TODO: let API allocate ? */
scan_ctx.ap = malloc(sizeof(wifi_scan_ctx_t) * 32);
if (scan_ctx.ap == NULL) {
return 1;
}
scan_ctx.cb = cb;
scan_ctx.arg = arg;
scan_ctx.max_ap = 32;
ulTaskNotifyTake(pdTRUE, 0);
xTaskCreatePinnedToCore(wifi_manager_scan_task, "scan task", 4 * 1024,
arg, 7, &scan_ctx.task, 0);
return 0;
}
int wifi_manager_get_scan_list(uint16_t *number, wifi_ap_record_t *aps)
{
if (xSemaphoreTake(scan_ctx.lock, pdMS_TO_TICKS(0)) != pdTRUE) {
return 1;
int err;
int broke_endless_connect = 0;
if (xSemaphoreTake(ctx.lock, pdMS_TO_TICKS(0)) != pdTRUE) {
if (ctx.is_endless_connect == 0) {
return 1;
}
ESP_LOGI(TAG, "deleting connecting %p", ctx.task);
vTaskDelete(ctx.task);
/* in case lock is released when deleting the task */
xSemaphoreTake(ctx.lock, pdMS_TO_TICKS(0));
esp_wifi_disconnect();
ctx.is_endless_connect = 0;
broke_endless_connect = 1;
} else if (ctx.is_endless_connect == 1) {
ESP_LOGI(TAG, "deleting delay %p", ctx.task);
vTaskDelete(ctx.task);
ctx.is_endless_connect = 0;
broke_endless_connect = 1;
}
scan_ctx.ap = aps;
scan_ctx.max_ap = *number;
scan_ctx.cb = NULL;
scan_ctx.task = xTaskGetCurrentTaskHandle();
ctx.scan.ap = aps;
ctx.scan.max_ap = *number;
ctx.scan.cb = NULL;
ctx.task = xTaskGetCurrentTaskHandle();
scan_loop();
xSemaphoreGive(scan_ctx.lock);
*number = scan_ctx.total_aps;
return 0;
err = scan_loop();
xSemaphoreGive(ctx.lock);
*number = ctx.scan.total_aps;
if (broke_endless_connect) {
if (set_default_sta_cred() == 0) {
disconn_handler();
}
}
return err;
}
void *wifi_manager_get_ap_netif()
@ -197,3 +207,147 @@ void *wifi_manager_get_sta_netif()
{
return sta_netif;
}
static void try_connect_done(void *arg, wifi_event_sta_connected_t *event)
{
ctx.conn.event = event;
if (ctx.task) {
xTaskNotifyGive(ctx.task);
}
if (ctx.conn.need_unlock) {
ctx.conn.need_unlock = 0;
xSemaphoreGive(ctx.lock);
}
ESP_LOGI(TAG, "event done %p", ctx.task);
}
int set_default_sta_cred()
{
int err;
wifi_credential_t credential;
err = wifi_data_get_last_conn_cred(&credential);
if (err) {
return err;
}
set_sta_cred(credential.ssid, credential.password);
return 0;
}
int wifi_manager_connect(const char *ssid, const char *password)
{
if (xSemaphoreTake(ctx.lock, pdMS_TO_TICKS(0)) != pdTRUE) {
if (ctx.is_endless_connect == 0) {
return 1;
}
vTaskDelete(ctx.task);
ctx.is_endless_connect = 0;
}
int ret;
set_sta_cred(ssid, password);
ctx.task = xTaskGetCurrentTaskHandle();
ctx.auto_reconnect = 1;
wifi_event_trigger_connect(2, try_connect_done, NULL);
ret = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(10000));
xSemaphoreGive(ctx.lock);
if (ret == 0 || ctx.conn.event == NULL) {
ESP_LOGI(TAG, "conn error");
if (set_default_sta_cred() == 0) {
disconn_handler();
}
return 1;
}
wifi_credential_t credential;
memcpy(credential.ssid, ssid, 32);
memcpy(credential.password, password, 64);
ret = wifi_save_ap_credential(&credential);
if (ret) {
ESP_LOGE(TAG, "nvs save error: %s", esp_err_to_name(ret));
}
return 0;
}
static void set_sta_cred(const char *ssid, const char *password)
{
wifi_config_t wifi_config = {
.sta = {
.threshold.rssi = -80,
.sort_method = WIFI_CONNECT_AP_BY_SIGNAL,
},
};
memcpy((char *) wifi_config.sta.ssid, ssid, 32);
memcpy((char *) wifi_config.sta.password, password, 64);
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
}
static void reconnection_task(void *arg)
{
int err;
ctx.is_endless_connect = 1;
ctx.task = xTaskGetCurrentTaskHandle();
do {
ESP_LOGI(TAG, "reco task: try connect, task %p", xTaskGetCurrentTaskHandle());
err = wifi_event_trigger_connect(0, try_connect_done, NULL);
if (err) {
ESP_LOGE(TAG, "trigger connect err: %s", esp_err_to_name(err));
break;
}
ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(20000));
if (ctx.conn.event || ctx.auto_reconnect == 0) {
/* reconnection successful or stop reconnect */
break;
}
/* long wait to not spam try to reconnect */
xSemaphoreGive(ctx.lock);
ESP_LOGI(TAG, "retry connection in 5 seconds");
vTaskDelay(pdMS_TO_TICKS(5 * 1000));
if (xSemaphoreTake(ctx.lock, pdMS_TO_TICKS(0)) != pdTRUE) {
ESP_LOGE(TAG, "reconnection failed");
break;
}
} while (1);
ctx.is_endless_connect = 0;
xSemaphoreGive(ctx.lock);
vTaskDelete(NULL);
}
static void disconn_handler(void)
{
/* disconnected
* 1. WI-FI AP is far away
* 2. WI-FI AP or AP device is closed
* 3. this device is ejected by AP
* */
if (ctx.auto_reconnect == 0) {
return;
}
if (xSemaphoreTake(ctx.lock, pdMS_TO_TICKS(0)) != pdTRUE) {
ESP_LOGE(TAG, "disconn_handler failed");
return;
}
ESP_LOGI(TAG, "start reconn task");
xTaskCreate(reconnection_task, "reconn task", 4 * 1024, NULL, 7, NULL);
}
/**
* @brief kill reconnection task
* @return
*/
int wifi_manager_disconnect(void)
{
ctx.auto_reconnect = 0;
return esp_wifi_disconnect();
}

View File

@ -13,9 +13,9 @@ void *wifi_manager_get_ap_netif();
void *wifi_manager_get_sta_netif();
typedef void (*wifi_manager_scan_done_cb)(uint16_t ap_found, wifi_ap_record_t *record, void *arg);
int wifi_manager_scan(uint16_t *number, wifi_ap_record_t *records, uint8_t is_async);
int wifi_manager_trigger_scan(wifi_manager_scan_done_cb cb, void *arg);
int wifi_manager_get_scan_list(uint16_t *number, wifi_ap_record_t *aps);
int wifi_manager_connect(const char *ssid, const char *password);
int wifi_manager_disconnect(void);

View File

@ -0,0 +1,77 @@
#include "wifi_storage.h"
#include "wifi_storage_priv.h"
#include "wt_nvs.h"
#include <stdio.h>
#define NVS_NAMESPACE "wifi"
int wifi_data_get_last_conn_cred(wifi_credential_t *ap_credential)
{
uint32_t ap_bitmap = 0;
nvs_handle_t handle;
int err;
err = wt_nvs_open(NVS_NAMESPACE, &handle);
if (err) {
return WT_NVS_ERR;
}
err = wt_nvs_get(handle, KEY_WIFI_STA_AP_BITMAP, &ap_bitmap, sizeof(ap_bitmap));
if (err || ap_bitmap == 0) {
return WT_NVS_ERR_NOT_FOUND;
}
err = wt_nvs_get(handle, KEY_WIFI_STA_LAST_AP_CRED,
ap_credential, sizeof(wifi_credential_t));
if (err) {
return WT_NVS_ERR;
}
wt_nvs_close(handle);
return WT_NVS_OK;
}
/*
* Called when connect to an AP,
*/
int wifi_save_ap_credential(wifi_credential_t *ap_credential)
{
uint32_t ap_bitmap;
int err;
nvs_handle_t handle;
err = wt_nvs_open(NVS_NAMESPACE, &handle);
if (err) {
return err;
}
err = wt_nvs_get(handle, KEY_WIFI_STA_AP_BITMAP, &ap_bitmap, sizeof(ap_bitmap));
if (err) {
if (err != ESP_ERR_NVS_NOT_FOUND) {
return err;
}
ap_bitmap = 0;
}
if (ap_bitmap == 0) {
ap_bitmap = 1;
err = wt_nvs_set(handle, KEY_WIFI_STA_AP_BITMAP, &ap_bitmap, sizeof(ap_bitmap));
}
wifi_credential_t credential;
err = wt_nvs_get(handle, KEY_WIFI_STA_LAST_AP_CRED, &credential, sizeof(ap_bitmap));
if (err) {
if (err != ESP_ERR_NVS_NOT_FOUND) {
return err;
}
}
err |= wt_nvs_set(handle, KEY_WIFI_STA_LAST_AP_CRED, ap_credential, sizeof(wifi_credential_t));
if (err) {
return err;
}
wt_nvs_close(handle);
return WT_NVS_OK;
}

View File

@ -0,0 +1,20 @@
#ifndef WIFI_STORAGE_H_GUARD
#define WIFI_STORAGE_H_GUARD
#include <stdint-gcc.h>
typedef struct nvs_wifi_credential_t {
char ssid[32];
char password[64];
} wifi_credential_t;
int wifi_data_get_last_conn_cred(wifi_credential_t *ap_credential);
int wifi_get_ap_password_of_ssid(wifi_credential_t *ap_credential);
int wifi_save_ap_credential(wifi_credential_t *ap_credential);
int wifi_rm_ap_credential(const char *ssid);
void wifi_cache_enable(uint8_t en);
#endif //WIFI_STORAGE_H_GUARD

View File

@ -0,0 +1,29 @@
#ifndef WIFI_STORAGE_PRIV_H_GUARD
#define WIFI_STORAGE_PRIV_H_GUARD
#define WIFI_MAX_AP_CRED_RECORD 1
typedef struct w_cache_t {
uint32_t ap_bitmap;
wifi_credential_t ap_creds[WIFI_MAX_AP_CRED_RECORD];
} w_cache_t;
typedef enum wt_wifi_key_enum {
KEY_WIFI_RESERVED = 0x000,
/* WIFI */
KEY_WIFI_AP_SSID,
KEY_WIFI_AP_PASSWORD,
/* TODO: should have 1 for each AP */
KEY_WIFI_STA_USE_STATIC, /* bit[0:31]=[IP, MASK, GATEWAY, DNS] */
KEY_WIFI_STA_STATIC_IP, /* 4B */
KEY_WIFI_STA_STATIC_MASK, /* 4B */
KEY_WIFI_STA_STATIC_GATEWAY, /* 4B */
KEY_WIFI_STA_STATIC_DNS, /* 4B */
/* AP's information */
KEY_WIFI_STA_LAST_AP_CRED, /*!< ssid[32] + password[64] */
KEY_WIFI_STA_AP_BITMAP,
} wt_wifi_key;
#endif //WIFI_STORAGE_PRIV_H_GUARD

View File

@ -3,5 +3,6 @@ file(GLOB SOURCES wt_nvs.c wt_storage.c)
idf_component_register(
SRCS ${SOURCES}
INCLUDE_DIRS "."
PRIV_REQUIRES nvs_flash
PRIV_REQUIRES
REQUIRES nvs_flash
)

View File

@ -14,3 +14,73 @@ void wt_nvs_init()
}
ESP_ERROR_CHECK(err);
}
int wt_nvs_open(const char* namespace, nvs_handle_t *out_handle)
{
return nvs_open(namespace, NVS_READWRITE, out_handle);
}
void wt_nvs_close(nvs_handle_t handle)
{
ESP_ERROR_CHECK(nvs_commit(handle));
nvs_close(handle);
}
int wt_nvs_get(nvs_handle_t handle, const uint32_t key, void *data, uint32_t data_size)
{
switch (data_size) {
case 0: {
uint8_t tmp;
return nvs_get_u8(handle, (const char *) &key, &tmp);
}
case 1:
return nvs_get_u8(handle, (const char *) &key, data);
case 2:
return nvs_get_u16(handle, (const char *) &key, data);
case 4:
return nvs_get_u32(handle, (const char *) &key, data);
case 8:
return nvs_get_u64(handle, (const char *) &key, data);
default:
return nvs_get_blob(handle, (const char *) &key, data,
(size_t *) &data_size);
}
}
int wt_nvs_set(nvs_handle_t handle, const uint32_t key, void *data, uint32_t data_size)
{
switch (data_size) {
case 0: {
uint8_t tmp = 0xFF;
return nvs_set_u8(handle, (const char *) &key, tmp);
}
case 1:
return nvs_set_u8(handle, (const char *) &key, *(uint8_t *)data);
case 2:
return nvs_set_u16(handle, (const char *) &key, *(uint16_t *)data);
case 4:
return nvs_set_u32(handle, (const char *) &key, *(uint32_t *)data);
case 8:
return nvs_set_u64(handle, (const char *) &key, *(uint64_t *)data);
default:
return nvs_set_blob(handle, (const char *) &key, data, data_size);
}
}
int wt_nvs_get_once(const char* namespace, const uint32_t key, void *data, uint32_t data_size)
{
nvs_handle_t handle;
int err;
err = wt_nvs_open(namespace, &handle);
if (err) {
return err;
}
err = wt_nvs_get(handle, key, data, data_size);
if (err) {
return err;
}
wt_nvs_close(handle);
return 0;
}

View File

@ -2,6 +2,25 @@
#define WT_NVS_H_GUARD
#include <stdint.h>
#include <nvs.h>
typedef enum wt_nvs_error_enum {
WT_NVS_OK = 0,
WT_NVS_ERR,
WT_NVS_NO_MEM,
WT_NVS_ERR_NOT_FOUND,
} wt_nvs_key_error;
int wt_nvs_open(const char* namespace, nvs_handle_t *out_handle);
void wt_nvs_close(nvs_handle_t handle);
int wt_nvs_get(nvs_handle_t handle, uint32_t key, void *data, uint32_t data_size);
int wt_nvs_set(nvs_handle_t handle, uint32_t key, void *data, uint32_t data_size);
int wt_nvs_flush(nvs_handle_t handle);
void wt_nvs_init();