Merge branch 'feature/add_esptouch_v2' into 'master'

WiFi: add ESPTouch v2

Closes WIFI-942 and WIFI-2619

See merge request espressif/esp-idf!10093
This commit is contained in:
Jiang Jiang Jian 2021-01-15 19:11:47 +08:00
commit 9ff1609a20
6 changed files with 80 additions and 32 deletions

View File

@ -28,6 +28,7 @@ typedef enum {
SC_TYPE_ESPTOUCH = 0, /**< protocol: ESPTouch */ SC_TYPE_ESPTOUCH = 0, /**< protocol: ESPTouch */
SC_TYPE_AIRKISS, /**< protocol: AirKiss */ SC_TYPE_AIRKISS, /**< protocol: AirKiss */
SC_TYPE_ESPTOUCH_AIRKISS, /**< protocol: ESPTouch and AirKiss */ SC_TYPE_ESPTOUCH_AIRKISS, /**< protocol: ESPTouch and AirKiss */
SC_TYPE_ESPTOUCH_V2, /**< protocol: ESPTouch v2*/
} smartconfig_type_t; } smartconfig_type_t;
/** Smartconfig event declarations */ /** Smartconfig event declarations */
@ -54,11 +55,15 @@ typedef struct {
/** Configure structure for esp_smartconfig_start */ /** Configure structure for esp_smartconfig_start */
typedef struct { typedef struct {
bool enable_log; /**< Enable smartconfig logs. */ bool enable_log; /**< Enable smartconfig logs. */
bool esp_touch_v2_enable_crypt; /**< Enable ESPTouch v2 crypt. */
char *esp_touch_v2_key; /**< ESPTouch v2 crypt key, len should be 16. */
} smartconfig_start_config_t; } smartconfig_start_config_t;
#define SMARTCONFIG_START_CONFIG_DEFAULT() { \ #define SMARTCONFIG_START_CONFIG_DEFAULT() { \
.enable_log = false \ .enable_log = false, \
.esp_touch_v2_enable_crypt = false,\
.esp_touch_v2_key = NULL \
}; };
/** /**
@ -139,6 +144,18 @@ esp_err_t esp_smartconfig_set_type(smartconfig_type_t type);
*/ */
esp_err_t esp_smartconfig_fast_mode(bool enable); esp_err_t esp_smartconfig_fast_mode(bool enable);
/**
* @brief Get reserved data of ESPTouch v2.
*
* @param rvd_data reserved data
* @param len length of reserved data
*
* @return
* - ESP_OK: succeed
* - others: fail
*/
esp_err_t esp_smartconfig_get_rvd_data(uint8_t *rvd_data, uint8_t len);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -51,19 +51,19 @@ typedef enum {
} esp_crypto_cipher_alg_t; } esp_crypto_cipher_alg_t;
/* /*
* This structure is about the algorithm when do crypto_hash operation, for detail, * This structure is about the algorithm when do crypto_hash operation, for detail,
* please reference to the structure crypto_hash. * please reference to the structure crypto_hash.
*/ */
typedef struct crypto_hash esp_crypto_hash_t; typedef struct crypto_hash esp_crypto_hash_t;
/* /*
* This structure is about the algorithm when do crypto_cipher operation, for detail, * This structure is about the algorithm when do crypto_cipher operation, for detail,
* please reference to the structure crypto_cipher. * please reference to the structure crypto_cipher.
*/ */
typedef struct crypto_cipher esp_crypto_cipher_t; typedef struct crypto_cipher esp_crypto_cipher_t;
/** /**
* @brief The AES callback function when do WPS connect. * @brief The AES callback function when do WPS connect.
* *
* @param key Encryption key. * @param key Encryption key.
* @param iv Encryption IV for CBC mode (16 bytes). * @param iv Encryption IV for CBC mode (16 bytes).
@ -73,7 +73,7 @@ typedef struct crypto_cipher esp_crypto_cipher_t;
typedef int (*esp_aes_128_encrypt_t)(const unsigned char *key, const unsigned char *iv, unsigned char *data, int data_len); typedef int (*esp_aes_128_encrypt_t)(const unsigned char *key, const unsigned char *iv, unsigned char *data, int data_len);
/** /**
* @brief The AES callback function when do WPS connect. * @brief The AES callback function when do WPS connect.
* *
* @param key Decryption key. * @param key Decryption key.
* @param iv Decryption IV for CBC mode (16 bytes). * @param iv Decryption IV for CBC mode (16 bytes).
@ -106,7 +106,7 @@ typedef int (*esp_aes_wrap_t)(const unsigned char *kek, int n, const unsigned ch
typedef int (*esp_aes_unwrap_t)(const unsigned char *kek, int n, const unsigned char *cipher, unsigned char *plain); typedef int (*esp_aes_unwrap_t)(const unsigned char *kek, int n, const unsigned char *cipher, unsigned char *plain);
/** /**
* @brief The SHA256 callback function when do WPS connect. * @brief The SHA256 callback function when do WPS connect.
* *
* @param key Key for HMAC operations. * @param key Key for HMAC operations.
* @param key_len Length of the key in bytes. * @param key_len Length of the key in bytes.
@ -127,7 +127,7 @@ typedef int (*esp_hmac_sha256_vector_t)(const unsigned char *key, int key_len, i
* @param label A unique label for each purpose of the PRF. * @param label A unique label for each purpose of the PRF.
* @param data Extra data to bind into the key. * @param data Extra data to bind into the key.
* @param data_len Length of the data. * @param data_len Length of the data.
* @param buf Buffer for the generated pseudo-random key. * @param buf Buffer for the generated pseudo-random key.
* @param buf_len Number of bytes of key to generate. * @param buf_len Number of bytes of key to generate.
* *
*/ */
@ -136,7 +136,7 @@ typedef int (*esp_sha256_prf_t)(const unsigned char *key, int key_len, const cha
/** /**
* @brief HMAC-MD5 over data buffer (RFC 2104)' * @brief HMAC-MD5 over data buffer (RFC 2104)'
* *
* @key: Key for HMAC operations * @key: Key for HMAC operations
* @key_len: Length of the key in bytes * @key_len: Length of the key in bytes
* @data: Pointers to the data area * @data: Pointers to the data area
@ -144,12 +144,12 @@ typedef int (*esp_sha256_prf_t)(const unsigned char *key, int key_len, const cha
* @mac: Buffer for the hash (16 bytes) * @mac: Buffer for the hash (16 bytes)
* Returns: 0 on success, -1 on failure * Returns: 0 on success, -1 on failure
*/ */
typedef int (*esp_hmac_md5_t)(const unsigned char *key, unsigned int key_len, const unsigned char *data, typedef int (*esp_hmac_md5_t)(const unsigned char *key, unsigned int key_len, const unsigned char *data,
unsigned int data_len, unsigned char *mac); unsigned int data_len, unsigned char *mac);
/** /**
* @brief HMAC-MD5 over data vector (RFC 2104) * @brief HMAC-MD5 over data vector (RFC 2104)
* *
* @key: Key for HMAC operations * @key: Key for HMAC operations
* @key_len: Length of the key in bytes * @key_len: Length of the key in bytes
* @num_elem: Number of elements in the data vector * @num_elem: Number of elements in the data vector
@ -163,7 +163,7 @@ typedef int (*esp_hmac_md5_vector_t)(const unsigned char *key, unsigned int key_
/** /**
* @brief HMAC-SHA1 over data buffer (RFC 2104) * @brief HMAC-SHA1 over data buffer (RFC 2104)
* *
* @key: Key for HMAC operations * @key: Key for HMAC operations
* @key_len: Length of the key in bytes * @key_len: Length of the key in bytes
* @data: Pointers to the data area * @data: Pointers to the data area
@ -171,12 +171,12 @@ typedef int (*esp_hmac_md5_vector_t)(const unsigned char *key, unsigned int key_
* @mac: Buffer for the hash (20 bytes) * @mac: Buffer for the hash (20 bytes)
* Returns: 0 on success, -1 of failure * Returns: 0 on success, -1 of failure
*/ */
typedef int (*esp_hmac_sha1_t)(const unsigned char *key, unsigned int key_len, const unsigned char *data, typedef int (*esp_hmac_sha1_t)(const unsigned char *key, unsigned int key_len, const unsigned char *data,
unsigned int data_len, unsigned char *mac); unsigned int data_len, unsigned char *mac);
/** /**
* @brief HMAC-SHA1 over data vector (RFC 2104) * @brief HMAC-SHA1 over data vector (RFC 2104)
* *
* @key: Key for HMAC operations * @key: Key for HMAC operations
* @key_len: Length of the key in bytes * @key_len: Length of the key in bytes
* @num_elem: Number of elements in the data vector * @num_elem: Number of elements in the data vector
@ -190,7 +190,7 @@ typedef int (*esp_hmac_sha1_vector_t)(const unsigned char *key, unsigned int key
/** /**
* @brief SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) * @brief SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1)
* *
* @key: Key for PRF * @key: Key for PRF
* @key_len: Length of the key in bytes * @key_len: Length of the key in bytes
* @label: A unique label for each purpose of the PRF * @label: A unique label for each purpose of the PRF
@ -208,19 +208,19 @@ typedef int (*esp_sha1_prf_t)(const unsigned char *key, unsigned int key_len, co
/** /**
* @brief SHA-1 hash for data vector * @brief SHA-1 hash for data vector
* *
* @num_elem: Number of elements in the data vector * @num_elem: Number of elements in the data vector
* @addr: Pointers to the data areas * @addr: Pointers to the data areas
* @len: Lengths of the data blocks * @len: Lengths of the data blocks
* @mac: Buffer for the hash * @mac: Buffer for the hash
* Returns: 0 on success, -1 on failure * Returns: 0 on success, -1 on failure
*/ */
typedef int (*esp_sha1_vector_t)(unsigned int num_elem, const unsigned char *addr[], const unsigned int *len, typedef int (*esp_sha1_vector_t)(unsigned int num_elem, const unsigned char *addr[], const unsigned int *len,
unsigned char *mac); unsigned char *mac);
/** /**
* @brief SHA1-based key derivation function (PBKDF2) for IEEE 802.11i * @brief SHA1-based key derivation function (PBKDF2) for IEEE 802.11i
* *
* @passphrase: ASCII passphrase * @passphrase: ASCII passphrase
* @ssid: SSID * @ssid: SSID
* @ssid_len: SSID length in bytes * @ssid_len: SSID length in bytes
@ -238,7 +238,7 @@ typedef int (*esp_pbkdf2_sha1_t)(const char *passphrase, const char *ssid, unsig
/** /**
* @brief XOR RC4 stream to given data with skip-stream-start * @brief XOR RC4 stream to given data with skip-stream-start
* *
* @key: RC4 key * @key: RC4 key
* @keylen: RC4 key length * @keylen: RC4 key length
* @skip: number of bytes to skip from the beginning of the RC4 stream * @skip: number of bytes to skip from the beginning of the RC4 stream
@ -255,7 +255,7 @@ typedef int (*esp_rc4_skip_t)(const unsigned char *key, unsigned int keylen, uns
/** /**
* @brief MD5 hash for data vector * @brief MD5 hash for data vector
* *
* @num_elem: Number of elements in the data vector * @num_elem: Number of elements in the data vector
* @addr: Pointers to the data areas * @addr: Pointers to the data areas
* @len: Lengths of the data blocks * @len: Lengths of the data blocks
@ -267,7 +267,7 @@ typedef int (*esp_md5_vector_t)(unsigned int num_elem, const unsigned char *addr
/** /**
* @brief Encrypt one AES block * @brief Encrypt one AES block
* *
* @ctx: Context pointer from aes_encrypt_init() * @ctx: Context pointer from aes_encrypt_init()
* @plain: Plaintext data to be encrypted (16 bytes) * @plain: Plaintext data to be encrypted (16 bytes)
* @crypt: Buffer for the encrypted data (16 bytes) * @crypt: Buffer for the encrypted data (16 bytes)
@ -276,7 +276,7 @@ typedef void (*esp_aes_encrypt_t)(void *ctx, const unsigned char *plain, unsigne
/** /**
* @brief Initialize AES for encryption * @brief Initialize AES for encryption
* *
* @key: Encryption key * @key: Encryption key
* @len: Key length in bytes (usually 16, i.e., 128 bits) * @len: Key length in bytes (usually 16, i.e., 128 bits)
* Returns: Pointer to context data or %NULL on failure * Returns: Pointer to context data or %NULL on failure
@ -285,14 +285,14 @@ typedef void * (*esp_aes_encrypt_init_t)(const unsigned char *key, unsigned int
/** /**
* @brief Deinitialize AES encryption * @brief Deinitialize AES encryption
* *
* @ctx: Context pointer from aes_encrypt_init() * @ctx: Context pointer from aes_encrypt_init()
*/ */
typedef void (*esp_aes_encrypt_deinit_t)(void *ctx); typedef void (*esp_aes_encrypt_deinit_t)(void *ctx);
/** /**
* @brief Decrypt one AES block * @brief Decrypt one AES block
* *
* @ctx: Context pointer from aes_encrypt_init() * @ctx: Context pointer from aes_encrypt_init()
* @crypt: Encrypted data (16 bytes) * @crypt: Encrypted data (16 bytes)
* @plain: Buffer for the decrypted data (16 bytes) * @plain: Buffer for the decrypted data (16 bytes)
@ -301,7 +301,7 @@ typedef void (*esp_aes_decrypt_t)(void *ctx, const unsigned char *crypt, unsigne
/** /**
* @brief Initialize AES for decryption * @brief Initialize AES for decryption
* *
* @key: Decryption key * @key: Decryption key
* @len: Key length in bytes (usually 16, i.e., 128 bits) * @len: Key length in bytes (usually 16, i.e., 128 bits)
* Returns: Pointer to context data or %NULL on failure * Returns: Pointer to context data or %NULL on failure
@ -310,7 +310,7 @@ typedef void * (*esp_aes_decrypt_init_t)(const unsigned char *key, unsigned int
/** /**
* @brief Deinitialize AES decryption * @brief Deinitialize AES decryption
* *
* @ctx: Context pointer from aes_encrypt_init() * @ctx: Context pointer from aes_encrypt_init()
*/ */
typedef void (*esp_aes_decrypt_deinit_t)(void *ctx); typedef void (*esp_aes_decrypt_deinit_t)(void *ctx);
@ -385,6 +385,8 @@ typedef struct {
esp_aes_decrypt_t aes_decrypt; esp_aes_decrypt_t aes_decrypt;
esp_aes_decrypt_init_t aes_decrypt_init; esp_aes_decrypt_init_t aes_decrypt_init;
esp_aes_decrypt_deinit_t aes_decrypt_deinit; esp_aes_decrypt_deinit_t aes_decrypt_deinit;
esp_aes_128_encrypt_t aes_128_encrypt;
esp_aes_128_decrypt_t aes_128_decrypt;
esp_omac1_aes_128_t omac1_aes_128; esp_omac1_aes_128_t omac1_aes_128;
esp_ccmp_decrypt_t ccmp_decrypt; esp_ccmp_decrypt_t ccmp_decrypt;
esp_ccmp_encrypt_t ccmp_encrypt; esp_ccmp_encrypt_t ccmp_encrypt;

@ -1 +1 @@
Subproject commit dd9b0bf4812b47195ae67a26a194c23694f0dada Subproject commit 8017558a14d29ce5f7fa8631afdecad3e86b2a93

View File

@ -34,12 +34,14 @@
#define SC_ACK_TASK_PRIORITY 2 /*!< Priority of sending smartconfig ACK task */ #define SC_ACK_TASK_PRIORITY 2 /*!< Priority of sending smartconfig ACK task */
#define SC_ACK_TASK_STACK_SIZE 2048 /*!< Stack size of sending smartconfig ACK task */ #define SC_ACK_TASK_STACK_SIZE 2048 /*!< Stack size of sending smartconfig ACK task */
#define SC_ACK_TOUCH_SERVER_PORT 18266 /*!< ESP touch UDP port of server on cellphone */ #define SC_ACK_TOUCH_DEVICE_PORT 7001 /*!< ESPTouch UDP port of server on device */
#define SC_ACK_TOUCH_SERVER_PORT 18266 /*!< ESPTouch UDP port of server on cellphone */
#define SC_ACK_TOUCH_V2_SERVER_PORT(i) (18266+i*10000) /*!< ESPTouch v2 UDP port of server on cellphone */
#define SC_ACK_AIRKISS_SERVER_PORT 10000 /*!< Airkiss UDP port of server on cellphone */ #define SC_ACK_AIRKISS_SERVER_PORT 10000 /*!< Airkiss UDP port of server on cellphone */
#define SC_ACK_AIRKISS_DEVICE_PORT 10001 /*!< Airkiss UDP port of server on device */ #define SC_ACK_AIRKISS_DEVICE_PORT 10001 /*!< Airkiss UDP port of server on device */
#define SC_ACK_AIRKISS_TIMEOUT 1500 /*!< Airkiss read data timout millisecond */ #define SC_ACK_AIRKISS_TIMEOUT 1500 /*!< Airkiss read data timout millisecond */
#define SC_ACK_TOUCH_LEN 11 /*!< Length of ESP touch ACK context */ #define SC_ACK_TOUCH_LEN 11 /*!< Length of ESPTouch ACK context */
#define SC_ACK_AIRKISS_LEN 7 /*!< Length of Airkiss ACK context */ #define SC_ACK_AIRKISS_LEN 7 /*!< Length of Airkiss ACK context */
#define SC_ACK_MAX_COUNT 30 /*!< Maximum count of sending smartconfig ACK */ #define SC_ACK_MAX_COUNT 30 /*!< Maximum count of sending smartconfig ACK */
@ -77,7 +79,6 @@ static void sc_ack_send_task(void *pvParameters)
esp_netif_ip_info_t local_ip; esp_netif_ip_info_t local_ip;
uint8_t remote_ip[4]; uint8_t remote_ip[4];
memcpy(remote_ip, ack->ctx.ip, sizeof(remote_ip)); memcpy(remote_ip, ack->ctx.ip, sizeof(remote_ip));
int remote_port = (ack->type == SC_TYPE_ESPTOUCH) ? SC_ACK_TOUCH_SERVER_PORT : SC_ACK_AIRKISS_SERVER_PORT;
struct sockaddr_in server_addr; struct sockaddr_in server_addr;
socklen_t sin_size = sizeof(server_addr); socklen_t sin_size = sizeof(server_addr);
int send_sock = -1; int send_sock = -1;
@ -88,6 +89,19 @@ static void sc_ack_send_task(void *pvParameters)
int err; int err;
int ret; int ret;
int remote_port = 0;
if (ack->type == SC_TYPE_ESPTOUCH) {
remote_port = SC_ACK_TOUCH_SERVER_PORT;
} else if (ack->type == SC_TYPE_ESPTOUCH_V2) {
uint8_t port_bit = ack->ctx.token;
if(port_bit > 3) {
port_bit = 0;
}
remote_port = SC_ACK_TOUCH_V2_SERVER_PORT(port_bit);
} else {
remote_port = SC_ACK_AIRKISS_SERVER_PORT;
}
bzero(&server_addr, sizeof(struct sockaddr_in)); bzero(&server_addr, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET; server_addr.sin_family = AF_INET;
memcpy(&server_addr.sin_addr.s_addr, remote_ip, sizeof(remote_ip)); memcpy(&server_addr.sin_addr.s_addr, remote_ip, sizeof(remote_ip));
@ -101,7 +115,7 @@ static void sc_ack_send_task(void *pvParameters)
/* Get local IP address of station */ /* Get local IP address of station */
ret = esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &local_ip); ret = esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &local_ip);
if ((ESP_OK == ret) && (local_ip.ip.addr != INADDR_ANY)) { if ((ESP_OK == ret) && (local_ip.ip.addr != INADDR_ANY)) {
/* If ESP touch, smartconfig ACK contains local IP address. */ /* If ESPTouch, smartconfig ACK contains local IP address. */
if (ack->type == SC_TYPE_ESPTOUCH) { if (ack->type == SC_TYPE_ESPTOUCH) {
memcpy(ack->ctx.ip, &local_ip.ip.addr, 4); memcpy(ack->ctx.ip, &local_ip.ip.addr, 4);
} }
@ -128,7 +142,11 @@ static void sc_ack_send_task(void *pvParameters)
bzero(&from, sizeof(struct sockaddr_in)); bzero(&from, sizeof(struct sockaddr_in));
local_addr.sin_family = AF_INET; local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = INADDR_ANY; local_addr.sin_addr.s_addr = INADDR_ANY;
local_addr.sin_port = htons(SC_ACK_AIRKISS_DEVICE_PORT); if (ack->type == SC_TYPE_AIRKISS) {
local_addr.sin_port = htons(SC_ACK_AIRKISS_DEVICE_PORT);
} else {
local_addr.sin_port = htons(SC_ACK_TOUCH_DEVICE_PORT);
}
bind(send_sock, (struct sockaddr *)&local_addr, sockadd_len); bind(send_sock, (struct sockaddr *)&local_addr, sockadd_len);
setsockopt(send_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); setsockopt(send_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));

View File

@ -50,6 +50,8 @@ const wpa_crypto_funcs_t g_wifi_default_wpa_crypto_funcs = {
.aes_decrypt = (esp_aes_decrypt_t)aes_decrypt, .aes_decrypt = (esp_aes_decrypt_t)aes_decrypt,
.aes_decrypt_init = (esp_aes_decrypt_init_t)aes_decrypt_init, .aes_decrypt_init = (esp_aes_decrypt_init_t)aes_decrypt_init,
.aes_decrypt_deinit = (esp_aes_decrypt_deinit_t)aes_decrypt_deinit, .aes_decrypt_deinit = (esp_aes_decrypt_deinit_t)aes_decrypt_deinit,
.aes_128_encrypt = (esp_aes_128_encrypt_t)aes_128_cbc_encrypt,
.aes_128_decrypt = (esp_aes_128_decrypt_t)aes_128_cbc_decrypt,
.omac1_aes_128 = (esp_omac1_aes_128_t)omac1_aes_128, .omac1_aes_128 = (esp_omac1_aes_128_t)omac1_aes_128,
.ccmp_decrypt = (esp_ccmp_decrypt_t)ccmp_decrypt, .ccmp_decrypt = (esp_ccmp_decrypt_t)ccmp_decrypt,
.ccmp_encrypt = (esp_ccmp_encrypt_t)ccmp_encrypt .ccmp_encrypt = (esp_ccmp_encrypt_t)ccmp_encrypt

View File

@ -54,6 +54,7 @@ static void event_handler(void* arg, esp_event_base_t event_base,
wifi_config_t wifi_config; wifi_config_t wifi_config;
uint8_t ssid[33] = { 0 }; uint8_t ssid[33] = { 0 };
uint8_t password[65] = { 0 }; uint8_t password[65] = { 0 };
uint8_t rvd_data[33] = { 0 };
bzero(&wifi_config, sizeof(wifi_config_t)); bzero(&wifi_config, sizeof(wifi_config_t));
memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid)); memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
@ -67,6 +68,14 @@ static void event_handler(void* arg, esp_event_base_t event_base,
memcpy(password, evt->password, sizeof(evt->password)); memcpy(password, evt->password, sizeof(evt->password));
ESP_LOGI(TAG, "SSID:%s", ssid); ESP_LOGI(TAG, "SSID:%s", ssid);
ESP_LOGI(TAG, "PASSWORD:%s", password); ESP_LOGI(TAG, "PASSWORD:%s", password);
if (evt->type == SC_TYPE_ESPTOUCH_V2) {
ESP_ERROR_CHECK( esp_smartconfig_get_rvd_data(rvd_data, sizeof(rvd_data)) );
ESP_LOGI(TAG, "RVD_DATA:");
for (int i=0; i<33; i++) {
printf("%02x ", rvd_data[i]);
}
printf("\n");
}
ESP_ERROR_CHECK( esp_wifi_disconnect() ); ESP_ERROR_CHECK( esp_wifi_disconnect() );
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );