|
|
|
|
@@ -6,38 +6,39 @@
|
|
|
|
|
#include "esp_log.h"
|
|
|
|
|
#include "freertos/semphr.h"
|
|
|
|
|
#include "common/semaphore_guard.h"
|
|
|
|
|
#include "cJSON.h"
|
|
|
|
|
|
|
|
|
|
static const char* TAG = "WifiHandler";
|
|
|
|
|
static const char* WIFI_SSID_KEY = "wifi_ssid";
|
|
|
|
|
static const char* WIFI_PASSWORD_KEY = "wifi_password";
|
|
|
|
|
static const char* WIFI_SSID_KEY = "ssid";
|
|
|
|
|
static const char* WIFI_PASSWORD_STORE_KEY = "psw";
|
|
|
|
|
|
|
|
|
|
WifiHandler::WifiHandler(
|
|
|
|
|
// this handler is used to store/retrieve WiFi credentials
|
|
|
|
|
// should have a unique namespace for WiFi credentials
|
|
|
|
|
// it will be owned by WifiHandler and deleted in its destructor
|
|
|
|
|
KVStorageHandler* kvs
|
|
|
|
|
) : kvs(kvs) {
|
|
|
|
|
std::unique_ptr<KVStorageHandler> kvs
|
|
|
|
|
) : kvs(std::move(kvs)) {
|
|
|
|
|
this->s_wifi_event_group = xEventGroupCreate();
|
|
|
|
|
if (!this->s_wifi_event_group) {
|
|
|
|
|
ESP_LOGE(TAG, "Failed to create WiFi event group");
|
|
|
|
|
}
|
|
|
|
|
this->scan_mutex = xSemaphoreCreateMutex();
|
|
|
|
|
if (!this->scan_mutex) {
|
|
|
|
|
ESP_LOGE(TAG, "Failed to create scan mutex");
|
|
|
|
|
}
|
|
|
|
|
this->connection_mutex = xSemaphoreCreateMutex();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Move constructor: transfer ownership of resources
|
|
|
|
|
WifiHandler::WifiHandler(WifiHandler&& other) noexcept
|
|
|
|
|
: initialized(other.initialized),
|
|
|
|
|
kvs(other.kvs),
|
|
|
|
|
s_wifi_event_group(other.s_wifi_event_group),
|
|
|
|
|
scan_mutex(other.scan_mutex),
|
|
|
|
|
connection_mutex(other.connection_mutex),
|
|
|
|
|
current_ssid(other.current_ssid),
|
|
|
|
|
expect_disconnected(other.expect_disconnected) {
|
|
|
|
|
other.kvs = nullptr;
|
|
|
|
|
other.initialized = false;
|
|
|
|
|
other.s_wifi_event_group = 0;
|
|
|
|
|
other.scan_mutex = nullptr;
|
|
|
|
|
other.connection_mutex = nullptr;
|
|
|
|
|
other.current_ssid = nullptr;
|
|
|
|
|
other.expect_disconnected = false;
|
|
|
|
|
if (!this->connection_mutex) {
|
|
|
|
|
ESP_LOGE(TAG, "Failed to create connection mutex");
|
|
|
|
|
}
|
|
|
|
|
this->credential_mutex = xSemaphoreCreateMutex();
|
|
|
|
|
if (!this->credential_mutex) {
|
|
|
|
|
ESP_LOGE(TAG, "Failed to create credential mutex");
|
|
|
|
|
}
|
|
|
|
|
if (this->kvs == nullptr) {
|
|
|
|
|
ESP_LOGW(TAG, "KVStorageHandler is null, WiFi credentials will not be stored");
|
|
|
|
|
} else {
|
|
|
|
|
this->kvs->init(nullptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WifiHandler::~WifiHandler() {
|
|
|
|
|
@@ -46,20 +47,23 @@ WifiHandler::~WifiHandler() {
|
|
|
|
|
// Check if it should be called
|
|
|
|
|
esp_wifi_deinit();
|
|
|
|
|
vEventGroupDelete(this->s_wifi_event_group);
|
|
|
|
|
if (this->current_ssid) {
|
|
|
|
|
delete[] this->current_ssid;
|
|
|
|
|
if (!this->current_ssid.empty()) {
|
|
|
|
|
this->current_ssid.clear();
|
|
|
|
|
}
|
|
|
|
|
vSemaphoreDelete(this->scan_mutex);
|
|
|
|
|
vSemaphoreDelete(this->connection_mutex);
|
|
|
|
|
esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &WifiHandler::wifi_event_handler);
|
|
|
|
|
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &WifiHandler::wifi_event_handler);
|
|
|
|
|
this->initialized = false;
|
|
|
|
|
// unique_ptr will automatically delete the object
|
|
|
|
|
this->kvs = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WifiHandler::init() {
|
|
|
|
|
esp_err_t WifiHandler::init() {
|
|
|
|
|
if (this->initialized) {
|
|
|
|
|
ESP_LOGW(TAG, "Already initialized, skipping");
|
|
|
|
|
return;
|
|
|
|
|
return ESP_OK;
|
|
|
|
|
}
|
|
|
|
|
esp_err_t err;
|
|
|
|
|
|
|
|
|
|
@@ -67,13 +71,13 @@ void WifiHandler::init() {
|
|
|
|
|
err = esp_netif_init();
|
|
|
|
|
if (err != ESP_OK) {
|
|
|
|
|
ESP_LOGE(TAG, "esp_netif_init failed: %s", esp_err_to_name(err));
|
|
|
|
|
return;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = esp_event_loop_create_default();
|
|
|
|
|
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
|
|
|
|
ESP_LOGE(TAG, "esp_event_loop_create_default failed: %s", esp_err_to_name(err));
|
|
|
|
|
return;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// create default WiFi station
|
|
|
|
|
@@ -84,32 +88,40 @@ void WifiHandler::init() {
|
|
|
|
|
err = esp_wifi_init(&cfg);
|
|
|
|
|
if (err != ESP_OK) {
|
|
|
|
|
ESP_LOGE(TAG, "esp_wifi_init failed: %s", esp_err_to_name(err));
|
|
|
|
|
return;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// register event handlers for WiFi and IP events
|
|
|
|
|
esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &WifiHandler::wifi_event_handler, this);
|
|
|
|
|
esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &WifiHandler::wifi_event_handler, this);
|
|
|
|
|
err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &WifiHandler::wifi_event_handler, this);
|
|
|
|
|
if (err != ESP_OK) {
|
|
|
|
|
ESP_LOGE(TAG, "esp_event_handler_register failed: %s", esp_err_to_name(err));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
err = esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &WifiHandler::wifi_event_handler, this);
|
|
|
|
|
if (err != ESP_OK) {
|
|
|
|
|
ESP_LOGE(TAG, "esp_event_handler_register failed: %s", esp_err_to_name(err));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = esp_wifi_set_mode(WIFI_MODE_STA);
|
|
|
|
|
if (err != ESP_OK) {
|
|
|
|
|
ESP_LOGE(TAG, "esp_wifi_set_mode failed: %s", esp_err_to_name(err));
|
|
|
|
|
return;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = esp_wifi_start();
|
|
|
|
|
if (err != ESP_OK) {
|
|
|
|
|
ESP_LOGE(TAG, "esp_wifi_start failed: %s", esp_err_to_name(err));
|
|
|
|
|
return;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// get WiFi credentials from KV storage if available
|
|
|
|
|
char* ssid = nullptr;
|
|
|
|
|
char* password = nullptr;
|
|
|
|
|
std::string ssid;
|
|
|
|
|
std::string password;
|
|
|
|
|
this->get_wifi_credentials(ssid, password);
|
|
|
|
|
|
|
|
|
|
if (ssid && password) {
|
|
|
|
|
ESP_LOGI(TAG, "Found stored WiFi credentials, connecting to SSID: %s", ssid);
|
|
|
|
|
if (!ssid.empty() && !password.empty()) {
|
|
|
|
|
ESP_LOGI(TAG, "Found stored WiFi credentials, connecting to SSID: %s", ssid.c_str());
|
|
|
|
|
err = this->connect(ssid, password);
|
|
|
|
|
if (err != ESP_OK) {
|
|
|
|
|
ESP_LOGE(TAG, "Failed to connect to stored WiFi credentials: %s", esp_err_to_name(err));
|
|
|
|
|
@@ -118,13 +130,11 @@ void WifiHandler::init() {
|
|
|
|
|
ESP_LOGI(TAG, "No stored WiFi credentials found, not connecting");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete[] ssid;
|
|
|
|
|
delete[] password;
|
|
|
|
|
|
|
|
|
|
initialized = true;
|
|
|
|
|
return ESP_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
esp_err_t WifiHandler::connect(const char* ssid, const char* password) {
|
|
|
|
|
esp_err_t WifiHandler::connect(const std::string& ssid, const std::string& password) {
|
|
|
|
|
SemaphoreGuard guard(this->connection_mutex);
|
|
|
|
|
// wait up to 5 seconds to take the mutex
|
|
|
|
|
if (!guard.take(5000 / portTICK_PERIOD_MS)) {
|
|
|
|
|
@@ -133,24 +143,21 @@ esp_err_t WifiHandler::connect(const char* ssid, const char* password) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
expect_disconnected = false;
|
|
|
|
|
if (this->current_ssid) {
|
|
|
|
|
delete[] this->current_ssid;
|
|
|
|
|
if (!this->current_ssid.empty()) {
|
|
|
|
|
this->current_ssid.clear();
|
|
|
|
|
}
|
|
|
|
|
size_t ssid_len = strlen(ssid);
|
|
|
|
|
this->current_ssid = new char[ssid_len + 1];
|
|
|
|
|
strncpy(this->current_ssid, ssid, ssid_len + 1);
|
|
|
|
|
this->current_ssid[ssid_len] = '\0';
|
|
|
|
|
this->current_ssid = ssid;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
wifi_config_t wifi_config = {};
|
|
|
|
|
strncpy((char*)wifi_config.sta.ssid, this->current_ssid, sizeof(wifi_config.sta.ssid));
|
|
|
|
|
strncpy((char*)wifi_config.sta.ssid, this->current_ssid.c_str(), sizeof(wifi_config.sta.ssid));
|
|
|
|
|
wifi_config.sta.ssid[sizeof(wifi_config.sta.ssid) - 1] = '\0';
|
|
|
|
|
strncpy((char*)wifi_config.sta.password, password, sizeof(wifi_config.sta.password));
|
|
|
|
|
strncpy((char*)wifi_config.sta.password, password.c_str(), sizeof(wifi_config.sta.password));
|
|
|
|
|
wifi_config.sta.password[sizeof(wifi_config.sta.password) - 1] = '\0';
|
|
|
|
|
// set auth mode to WPA2_PSK minimum
|
|
|
|
|
wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
|
|
|
|
|
|
|
|
|
|
ESP_LOGI(TAG, "Connecting to SSID: %s", this->current_ssid);
|
|
|
|
|
ESP_LOGI(TAG, "Connecting to SSID: %s", this->current_ssid.c_str());
|
|
|
|
|
esp_err_t err = esp_wifi_set_config(wifi_interface_t::WIFI_IF_STA, &wifi_config);
|
|
|
|
|
if (err != ESP_OK) {
|
|
|
|
|
ESP_LOGE(TAG, "Failed to set WiFi config: %s", esp_err_to_name(err));
|
|
|
|
|
@@ -162,40 +169,26 @@ esp_err_t WifiHandler::connect(const char* ssid, const char* password) {
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// store credentials
|
|
|
|
|
this->kvs->put(WIFI_SSID_KEY, this->current_ssid);
|
|
|
|
|
// store password under key derived from SSID
|
|
|
|
|
char* password_key = this->build_password_key(this->current_ssid);
|
|
|
|
|
this->kvs->put(password_key, password);
|
|
|
|
|
delete[] password_key;
|
|
|
|
|
|
|
|
|
|
// set connected bit on successful connection
|
|
|
|
|
xEventGroupSetBits(
|
|
|
|
|
this->s_wifi_event_group,
|
|
|
|
|
WIFI_CONNECTED_BIT
|
|
|
|
|
);
|
|
|
|
|
// store credentials after successful connection attempt
|
|
|
|
|
this->store_wifi_credentials(this->current_ssid, password);
|
|
|
|
|
|
|
|
|
|
return ESP_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
esp_err_t WifiHandler::connect(const char* ssid) {
|
|
|
|
|
char* stored_ssid = nullptr;
|
|
|
|
|
char* stored_password = nullptr;
|
|
|
|
|
esp_err_t WifiHandler::connect(const std::string& ssid) {
|
|
|
|
|
std::string stored_ssid;
|
|
|
|
|
std::string stored_password;
|
|
|
|
|
this->get_wifi_credentials(stored_ssid, stored_password);
|
|
|
|
|
if (!stored_ssid || strcmp(stored_ssid, ssid) != 0) {
|
|
|
|
|
ESP_LOGE(TAG, "No stored credentials for SSID: %s", ssid);
|
|
|
|
|
delete[] stored_ssid;
|
|
|
|
|
delete[] stored_password;
|
|
|
|
|
if (stored_ssid.empty() || stored_ssid != ssid) {
|
|
|
|
|
ESP_LOGE(TAG, "No stored credentials for SSID: %s", ssid.c_str());
|
|
|
|
|
return ESP_FAIL;
|
|
|
|
|
}
|
|
|
|
|
esp_err_t err = this->connect(stored_ssid, stored_password ? stored_password : "");
|
|
|
|
|
delete[] stored_ssid;
|
|
|
|
|
delete[] stored_password;
|
|
|
|
|
esp_err_t err = this->connect(stored_ssid, stored_password);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
esp_err_t WifiHandler::reconnect() {
|
|
|
|
|
if (!this->current_ssid) {
|
|
|
|
|
if (this->current_ssid.empty()) {
|
|
|
|
|
ESP_LOGE(TAG, "No current SSID set, cannot reconnect");
|
|
|
|
|
return ESP_FAIL;
|
|
|
|
|
}
|
|
|
|
|
@@ -270,10 +263,15 @@ void WifiHandler::wifi_event_handler(void* arg, esp_event_base_t event_base, int
|
|
|
|
|
case WIFI_EVENT_STA_START:
|
|
|
|
|
// When the station starts, attempt to connect
|
|
|
|
|
ESP_LOGI(TAG, "WIFI_EVENT_STA_START");
|
|
|
|
|
if (!self->expect_disconnected && self->current_ssid) {
|
|
|
|
|
ESP_LOGI(TAG, "Station started, attempting to connect to SSID: %s", self->current_ssid);
|
|
|
|
|
if (!self->expect_disconnected && !self->current_ssid.empty()) {
|
|
|
|
|
ESP_LOGI(TAG, "Station started, attempting to connect to SSID: %s", self->current_ssid.c_str());
|
|
|
|
|
self->reconnect();
|
|
|
|
|
}
|
|
|
|
|
// set the event bit to indicate started
|
|
|
|
|
xEventGroupSetBits(
|
|
|
|
|
self->s_wifi_event_group,
|
|
|
|
|
WIFI_STARTED_BIT
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case WIFI_EVENT_STA_DISCONNECTED:
|
|
|
|
|
ESP_LOGI(TAG, "WIFI_EVENT_STA_DISCONNECTED");
|
|
|
|
|
@@ -306,29 +304,106 @@ void WifiHandler::wifi_event_handler(void* arg, esp_event_base_t event_base, int
|
|
|
|
|
// private methods
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
char* WifiHandler::build_password_key(const char* ssid) {
|
|
|
|
|
// `{WIFI_PASSWORD_KEY}_{ssid}`
|
|
|
|
|
size_t password_key_len = strlen(WIFI_PASSWORD_KEY) + 1 + strlen(ssid) + 1;
|
|
|
|
|
char* password_key_buff = new char[password_key_len];
|
|
|
|
|
snprintf(password_key_buff, password_key_len, "%s_%s", WIFI_PASSWORD_KEY, ssid);
|
|
|
|
|
return password_key_buff;
|
|
|
|
|
void WifiHandler::store_wifi_credentials(const std::string& ssid, const std::string& password) {
|
|
|
|
|
if (!kvs) {
|
|
|
|
|
ESP_LOGW(TAG, "KVStorageHandler not set, cannot store WiFi credentials");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
SemaphoreGuard guard(this->credential_mutex);
|
|
|
|
|
// wait up to 5 seconds to take the mutex
|
|
|
|
|
if (!guard.take(5000 / portTICK_PERIOD_MS)) {
|
|
|
|
|
ESP_LOGE(TAG, "Failed to take credential mutex");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// store the password according to the JSON structure
|
|
|
|
|
std::string password_key_store = kvs->get(WIFI_PASSWORD_STORE_KEY);
|
|
|
|
|
cJSON* json = nullptr;
|
|
|
|
|
if (password_key_store.empty()) {
|
|
|
|
|
// create new JSON object
|
|
|
|
|
json = cJSON_CreateObject();
|
|
|
|
|
} else {
|
|
|
|
|
// parse existing JSON
|
|
|
|
|
json = cJSON_Parse(password_key_store.c_str());
|
|
|
|
|
if (json == nullptr) {
|
|
|
|
|
ESP_LOGE(TAG, "Failed to parse existing WiFi password JSON, creating new");
|
|
|
|
|
json = cJSON_CreateObject();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
cJSON* credentials = cJSON_GetObjectItem(json, "credentials");
|
|
|
|
|
if (credentials == nullptr || !cJSON_IsObject(credentials)) {
|
|
|
|
|
// create credentials object if it doesn't exist
|
|
|
|
|
credentials = cJSON_CreateObject();
|
|
|
|
|
cJSON_AddItemToObject(json, "credentials", credentials);
|
|
|
|
|
}
|
|
|
|
|
// create SSID object
|
|
|
|
|
cJSON* ssid_item = cJSON_CreateObject();
|
|
|
|
|
// add password field
|
|
|
|
|
cJSON_AddStringToObject(ssid_item, "password", password.c_str());
|
|
|
|
|
// add SSID object to credentials
|
|
|
|
|
cJSON_AddItemToObject(credentials, ssid.c_str(), ssid_item);
|
|
|
|
|
// store updated JSON string
|
|
|
|
|
char* updated_json_str = cJSON_PrintUnformatted(json);
|
|
|
|
|
if (updated_json_str) {
|
|
|
|
|
kvs->put(WIFI_PASSWORD_STORE_KEY, std::string(updated_json_str));
|
|
|
|
|
cJSON_free(updated_json_str);
|
|
|
|
|
}
|
|
|
|
|
cJSON_Delete(json);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WifiHandler::get_wifi_credentials(char*& ssid, char*& password) {
|
|
|
|
|
void WifiHandler::get_wifi_credentials(std::string& out_ssid, std::string& out_password) {
|
|
|
|
|
if (!kvs) {
|
|
|
|
|
ESP_LOGW(TAG, "KVStorageHandler not set, cannot get WiFi credentials");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ssid = kvs->get(WIFI_SSID_KEY).get();
|
|
|
|
|
if (!ssid) {
|
|
|
|
|
ssid = nullptr;
|
|
|
|
|
password = nullptr;
|
|
|
|
|
SemaphoreGuard guard(this->credential_mutex);
|
|
|
|
|
// wait up to 5 seconds to take the mutex
|
|
|
|
|
if (!guard.take(5000 / portTICK_PERIOD_MS)) {
|
|
|
|
|
ESP_LOGE(TAG, "Failed to take credential mutex");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
out_ssid = kvs->get(WIFI_SSID_KEY);
|
|
|
|
|
if (out_ssid.empty()) {
|
|
|
|
|
out_ssid = "";
|
|
|
|
|
out_password = "";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// password is from KV storage, may be nullptr
|
|
|
|
|
char* password_key = this->build_password_key(ssid);
|
|
|
|
|
password = kvs->get(password_key).get();
|
|
|
|
|
delete[] password_key;
|
|
|
|
|
std::string password_key_store = kvs->get(WIFI_PASSWORD_STORE_KEY);
|
|
|
|
|
if (password_key_store.empty()) {
|
|
|
|
|
out_password = "";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// parse from json
|
|
|
|
|
cJSON* json = cJSON_Parse(password_key_store.c_str());
|
|
|
|
|
if (json == nullptr) {
|
|
|
|
|
ESP_LOGE(TAG, "Failed to parse WiFi password JSON");
|
|
|
|
|
out_password = "";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
cJSON* credentials = cJSON_GetObjectItem(json, "credentials");
|
|
|
|
|
if (credentials == nullptr || !cJSON_IsObject(credentials)) {
|
|
|
|
|
ESP_LOGE(TAG, "WiFi password JSON does not contain valid 'credentials' object");
|
|
|
|
|
out_password = "";
|
|
|
|
|
cJSON_Delete(json);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// get the ssid value
|
|
|
|
|
cJSON* ssid_item = cJSON_GetObjectItem(credentials, out_ssid.c_str());
|
|
|
|
|
if (ssid_item == nullptr || !cJSON_IsObject(ssid_item)) {
|
|
|
|
|
ESP_LOGE(TAG, "WiFi password JSON does not contain valid SSID field for SSID: %s", out_ssid.c_str());
|
|
|
|
|
out_password = "";
|
|
|
|
|
cJSON_Delete(json);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
cJSON* password = cJSON_GetObjectItem(ssid_item, "password");
|
|
|
|
|
if (password == nullptr || !cJSON_IsString(password)) {
|
|
|
|
|
ESP_LOGE(TAG, "WiFi password JSON does not contain valid 'password' field for SSID: %s", out_ssid.c_str());
|
|
|
|
|
out_password = "";
|
|
|
|
|
cJSON_Delete(json);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
out_password = password->valuestring;
|
|
|
|
|
cJSON_Delete(json);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EventBits_t WifiHandler::wait_for_connection(TickType_t ticks_to_wait) {
|
|
|
|
|
|