From 6fdfa76a1490c4baad91266274946567895f8e75 Mon Sep 17 00:00:00 2001 From: kerms Date: Tue, 5 Mar 2024 16:42:29 +0800 Subject: [PATCH] feat(wifi) save credential on successful connection and auto connect to it --- components/utils/CMakeLists.txt | 7 + components/utils/list.h | 96 ++++++ .../request_runner/request_runner.c | 8 +- .../request_runner/request_runner.h | 1 + .../web_server/uri_modules/uri_api.c | 27 +- .../web_server/uri_modules/uri_ws.c | 2 - .../wifi_manager/CMakeLists.txt | 3 +- project_components/wifi_manager/wifi_api.c | 18 +- project_components/wifi_manager/wifi_api.h | 4 + .../wifi_manager/wifi_api_json.c | 60 +++- .../wifi_manager/wifi_api_json.h | 1 + .../wifi_manager/wifi_event_handler.c | 135 ++++++-- .../wifi_manager/wifi_event_handler.h | 9 +- .../wifi_manager/wifi_manager.c | 318 +++++++++++++----- .../wifi_manager/wifi_manager.h | 4 +- .../wifi_manager/wifi_storage.c | 77 +++++ .../wifi_manager/wifi_storage.h | 20 ++ .../wifi_manager/wifi_storage_priv.h | 29 ++ project_components/wt_storage/CMakeLists.txt | 3 +- project_components/wt_storage/wt_nvs.c | 70 ++++ project_components/wt_storage/wt_nvs.h | 19 ++ 21 files changed, 753 insertions(+), 158 deletions(-) create mode 100644 components/utils/CMakeLists.txt create mode 100644 components/utils/list.h create mode 100644 project_components/wifi_manager/wifi_storage.c create mode 100644 project_components/wifi_manager/wifi_storage.h create mode 100644 project_components/wifi_manager/wifi_storage_priv.h diff --git a/components/utils/CMakeLists.txt b/components/utils/CMakeLists.txt new file mode 100644 index 0000000..5cd7ec7 --- /dev/null +++ b/components/utils/CMakeLists.txt @@ -0,0 +1,7 @@ +file(GLOB SOURCES *.c) + + +idf_component_register( + SRCS ${SOURCES} + INCLUDE_DIRS "." +) \ No newline at end of file diff --git a/components/utils/list.h b/components/utils/list.h new file mode 100644 index 0000000..233e9bb --- /dev/null +++ b/components/utils/list.h @@ -0,0 +1,96 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2009, Jouni Malinen + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef LIST_H_GUARD +#define LIST_H_GUARD + + +#include + +/** + * 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 \ No newline at end of file diff --git a/project_components/request_runner/request_runner.c b/project_components/request_runner/request_runner.c index a0a5c7f..6bc5413 100644 --- a/project_components/request_runner/request_runner.c +++ b/project_components/request_runner/request_runner.c @@ -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); } } } diff --git a/project_components/request_runner/request_runner.h b/project_components/request_runner/request_runner.h index 6878217..7bff2a1 100644 --- a/project_components/request_runner/request_runner.h +++ b/project_components/request_runner/request_runner.h @@ -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(); diff --git a/project_components/web_server/uri_modules/uri_api.c b/project_components/web_server/uri_modules/uri_api.c index 3d2cf72..519347e 100644 --- a/project_components/web_server/uri_modules/uri_api.c +++ b/project_components/web_server/uri_modules/uri_api.c @@ -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; } diff --git a/project_components/web_server/uri_modules/uri_ws.c b/project_components/web_server/uri_modules/uri_ws.c index 35ae4d6..af4541c 100644 --- a/project_components/web_server/uri_modules/uri_ws.c +++ b/project_components/web_server/uri_modules/uri_ws.c @@ -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; } diff --git a/project_components/wifi_manager/CMakeLists.txt b/project_components/wifi_manager/CMakeLists.txt index 84a8ac1..fc12dae 100644 --- a/project_components/wifi_manager/CMakeLists.txt +++ b/project_components/wifi_manager/CMakeLists.txt @@ -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) diff --git a/project_components/wifi_manager/wifi_api.c b/project_components/wifi_manager/wifi_api.c index b5328c8..310d9c6 100644 --- a/project_components/wifi_manager/wifi_api.c +++ b/project_components/wifi_manager/wifi_api.c @@ -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(); } diff --git a/project_components/wifi_manager/wifi_api.h b/project_components/wifi_manager/wifi_api.h index 6d602d2..e72aa46 100644 --- a/project_components/wifi_manager/wifi_api.h +++ b/project_components/wifi_manager/wifi_api.h @@ -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 \ No newline at end of file diff --git a/project_components/wifi_manager/wifi_api_json.c b/project_components/wifi_manager/wifi_api_json.c index a56f308..a2b2fc5 100644 --- a/project_components/wifi_manager/wifi_api_json.c +++ b/project_components/wifi_manager/wifi_api_json.c @@ -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) diff --git a/project_components/wifi_manager/wifi_api_json.h b/project_components/wifi_manager/wifi_api_json.h index 72e5ead..26dbdec 100644 --- a/project_components/wifi_manager/wifi_api_json.h +++ b/project_components/wifi_manager/wifi_api_json.h @@ -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; diff --git a/project_components/wifi_manager/wifi_event_handler.c b/project_components/wifi_manager/wifi_event_handler.c index 76a3dc3..b6cb6d9 100644 --- a/project_components/wifi_manager/wifi_event_handler.c +++ b/project_components/wifi_manager/wifi_event_handler.c @@ -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; +} diff --git a/project_components/wifi_manager/wifi_event_handler.h b/project_components/wifi_manager/wifi_event_handler.h index f885f73..c9aa1fd 100644 --- a/project_components/wifi_manager/wifi_event_handler.h +++ b/project_components/wifi_manager/wifi_event_handler.h @@ -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 \ No newline at end of file diff --git a/project_components/wifi_manager/wifi_manager.c b/project_components/wifi_manager/wifi_manager.c index 234d5c8..7311ad7 100644 --- a/project_components/wifi_manager/wifi_manager.c +++ b/project_components/wifi_manager/wifi_manager.c @@ -1,6 +1,7 @@ #include "wifi_manager.h" #include "wifi_configuration.h" #include "wifi_event_handler.h" +#include "wifi_storage.h" #include #include @@ -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(); +} diff --git a/project_components/wifi_manager/wifi_manager.h b/project_components/wifi_manager/wifi_manager.h index 5aa6082..d1a8080 100644 --- a/project_components/wifi_manager/wifi_manager.h +++ b/project_components/wifi_manager/wifi_manager.h @@ -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); diff --git a/project_components/wifi_manager/wifi_storage.c b/project_components/wifi_manager/wifi_storage.c new file mode 100644 index 0000000..2cfd0b3 --- /dev/null +++ b/project_components/wifi_manager/wifi_storage.c @@ -0,0 +1,77 @@ +#include "wifi_storage.h" +#include "wifi_storage_priv.h" +#include "wt_nvs.h" + +#include + +#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; +} diff --git a/project_components/wifi_manager/wifi_storage.h b/project_components/wifi_manager/wifi_storage.h new file mode 100644 index 0000000..2b89de6 --- /dev/null +++ b/project_components/wifi_manager/wifi_storage.h @@ -0,0 +1,20 @@ +#ifndef WIFI_STORAGE_H_GUARD +#define WIFI_STORAGE_H_GUARD + +#include + +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 \ No newline at end of file diff --git a/project_components/wifi_manager/wifi_storage_priv.h b/project_components/wifi_manager/wifi_storage_priv.h new file mode 100644 index 0000000..37e1577 --- /dev/null +++ b/project_components/wifi_manager/wifi_storage_priv.h @@ -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 \ No newline at end of file diff --git a/project_components/wt_storage/CMakeLists.txt b/project_components/wt_storage/CMakeLists.txt index 581a6fa..c705129 100644 --- a/project_components/wt_storage/CMakeLists.txt +++ b/project_components/wt_storage/CMakeLists.txt @@ -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 ) \ No newline at end of file diff --git a/project_components/wt_storage/wt_nvs.c b/project_components/wt_storage/wt_nvs.c index 805c763..ac344ab 100644 --- a/project_components/wt_storage/wt_nvs.c +++ b/project_components/wt_storage/wt_nvs.c @@ -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; +} diff --git a/project_components/wt_storage/wt_nvs.h b/project_components/wt_storage/wt_nvs.h index 3ba3712..d8556a4 100644 --- a/project_components/wt_storage/wt_nvs.h +++ b/project_components/wt_storage/wt_nvs.h @@ -2,6 +2,25 @@ #define WT_NVS_H_GUARD #include +#include + +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();