Merge branch 'feature/update_wifi_provisioning_from_idf' into 'master'

feat(provisioning): update wifi provisioning from idf

See merge request sdk/ESP8266_RTOS_SDK!1501
This commit is contained in:
Dong Heng
2020-08-26 11:36:43 +08:00
63 changed files with 5608 additions and 542 deletions

View File

@ -1,6 +1,25 @@
# Provisioning Application Examples
These consist of the following examples :
This primarily consists of a single unified example wifi_prov_mgr
* wifi_prov_mgr
Abstracts out most of the complexity of Wi-Fi provisioning and allows easy switching between the SoftAP (using HTTP) and BLE transports. It also demonstrates how applications can register and use additional custom data endpoints.
Provisioning applications are available for various platforms:
* Android:
- [BLE Provisioning app on Play Store](https://play.google.com/store/apps/details?id=com.espressif.provble).
- [SoftAP Provisioning app on Play Store](https://play.google.com/store/apps/details?id=com.espressif.provsoftap).
- Source code on GitHub: [esp-idf-provisioning-android](https://github.com/espressif/esp-idf-provisioning-android).
* iOS:
- [BLE Provisioning app on app store](https://apps.apple.com/in/app/esp-ble-provisioning/id1473590141)
- [SoftAP Provisioning app on app Store](https://apps.apple.com/in/app/esp-softap-provisioning/id1474040630)
- Source code on GitHub: [esp-idf-provisioning-ios](https://github.com/espressif/esp-idf-provisioning-ios)
* For all other platforms a python based command line tool is provided under "$IDF_PATH/tools/esp_prov"
## Legacy Examples
The legacy examples require own implementation of provisioning functions and handlers. The Wi-Fi provisioning component abstracts out most of this complexity and provides a simpler interface and so, that is recommended for use. However, if you want to use lower level provisioning and protocomm APIs, you can check the these examples under legacy/ folder:
* softap_prov
Provisioning involves Wi-Fi station configuration via an HTTP server running on the device, which is initially configured to be in SoftAP mode. After provisioning, device runs in Wi-Fi station mode only and connects to the AP whose credentials were provided during provisioning.
@ -14,10 +33,4 @@ These consist of the following examples :
* custom_config
Similar to softap_prov examples, but allows for configuration of custom (device-local) information during provisioning. This is intended as an example for implementing custom provisioning schemes.
Provisioning applications are available for various platforms:
* For Android, a provisioning application along with source code is available on GitHub : [esp-idf-provisioning-android](https://github.com/espressif/esp-idf-provisioning-android)
* For iOS, a provisioning application along with source code is available on GitHub : [esp-idf-provisioning-ios](https://github.com/espressif/esp-idf-provisioning-ios)
* For all other platforms a python based command line tool is provided under "$IDF_PATH/tools/esp_prov"
Refer to the README.md files in each example directory for more information.

View File

@ -1,8 +0,0 @@
set(COMPONENT_ADD_INCLUDEDIRS include)
set(COMPONENT_PRIV_INCLUDEDIRS proto-c)
set(COMPONENT_SRCS "src/custom_config.c"
"proto-c/custom_config.pb-c.c")
set(COMPONENT_PRIV_REQUIRES protobuf-c)
register_component()

View File

@ -1,11 +0,0 @@
# Protobuf files for defining custom config-data packet structures
This is an example proto file defining custom configuration related data packet structures, namely -
1. CustomConfigRequest - for sending configuration data consisting of various fields (Info and Version)
2. CustomConfigResponse - for receiving configuration status (fail/success)
Note : These proto files are not automatically compiled during the build process.
Run "make" (Optional) to generate the respective C and Python files. The generated C files are used by protocomm itself to create, delete and manipulate transaction packets. The generated Python files can be used by python based applications for implementing client side interface to protocomm layer.
Compilation requires protoc (Protobuf Compiler) and protoc-c (Protobuf C Compiler) installed. Since the generated files are to remain the same, as long as the proto files are not modified, therefore the generated files are already available under "protocomm/proto-c" and "protocomm/python" directories, and thus running make (and installing the Protobuf compilers) is optional.

View File

@ -1,6 +0,0 @@
set(COMPONENT_SRCS "app_main.c"
"app_prov.c"
"app_prov_handlers.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -1,58 +0,0 @@
menu "Example Configuration"
config SOFTAP_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config SOFTAP_PASS
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
config USE_SEC_1
bool
default n
prompt "Use Security Version 1"
help
Security version 1 used Curve25519 key exchange for establishing
secure session between device and client during provisioning
config USE_POP
bool
depends on USE_SEC_1
default n
prompt "Use proof-of-possession"
help
Proof-of-possession can be optionally used to prove that the device is indeed
in possession of the user who is provisioning the device. This proof-of-possession
is internally used to generate the shared secret through key exchange.
config POP
string "Proof-of-possession"
default "abcd1234"
depends on USE_POP
config PROTOCOMM_HTTPD_PORT
int "Protocomm HTTP Port"
default 80
help
Port on which to run Protocomm HTTP based provisioning service
config RESET_PROVISIONED
bool
default n
prompt "Reset provisioned status of the device"
help
This erases the NVS to reset provisioned status of the device on every reboot.
Provisioned status is determined by the WiFi STA configuration, saved on the NVS.
config EXAMPLE_AP_RECONN_ATTEMPTS
int "Maximum AP connection attempts"
default 5
help
Set the maximum connection attempts to perform when connecting to a Wi-Fi AP.
endmenu

View File

@ -1,3 +0,0 @@
CONFIG_ENABLE_UNIFIED_PROVISIONING=y
CONFIG_LWIP_NETIF_LOOPBACK=y
CONFIG_LWIP_LOOPBACK_MAX_PBUFS=1

View File

@ -2,9 +2,5 @@
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(custom_config)

View File

@ -5,7 +5,5 @@
PROJECT_NAME := custom_config
EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common
include $(IDF_PATH)/make/project.mk

View File

@ -1,4 +1,6 @@
# SoftAP + HTTPD based Provisioning Example featuring Custom configuration
# SoftAP + HTTPD based Provisioning Example featuring Custom configuration (Legacy)
> Note: It is recommended to use the new `wifi_prov_mgr` example which is based on the simpler `wifi_provisioning` APIs. Check this example only if you wish to use lower level provisioning and protocomm APIs and want more control over the handlers.
(See the README.md file in the upper level 'examples' directory for more information about examples.)
@ -29,11 +31,9 @@ To provision the device running this example, the `esp_prov.py` script needs to
### Configure the project
```
make menuconfig
idf.py menuconfig
```
* Set serial port under Serial Flasher Options.
* Under Example Configuration set the following :
* SoftAP SSID (Defaults to PROV_<MACID>)
* SoftAP Password (Defaults to PROV_PASS)
@ -45,7 +45,7 @@ make menuconfig
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
@ -68,7 +68,7 @@ I (519482) tcpip_adapter: softAP assign IP to station,IP is: 192.168.4.2
In a separate terminal run the `esp_prov.py` script under `$IDP_PATH/tools/esp_prov` directory (please replace the values corresponding to the parameters `--custom_info` and `--custom_ver` with your desired values for the custom configuration). Assuming default example configuration, the script should be run as follows :
```
python esp_prov.py --ssid myssid --passphrase mypassword --sec_ver 0 --transport softap --softap_endpoint 192.168.4.1:80 --custom_config --custom_info "some string" --custom_ver 4321
python esp_prov.py --transport softap --service_name "192.168.4.1:80" --sec_ver 0 --ssid myssid --passphrase mypassword --custom_config --custom_info "some string" --custom_ver 4321
```
Above command will perform the provisioning steps, and the monitor log should display something like this :
@ -138,7 +138,7 @@ It means the Wi-Fi credentials were already set by some other application flashe
```
make erase_flash
make -j4 flash monitor
idf.py -p PORT flash monitor
```
Or, enable `Reset Provisioning` option under `Example Configuration` under menuconfig. But this will erase the saved Wi-Fi credentials every time the device boots, so this is not the preferred solution.

View File

@ -0,0 +1,5 @@
idf_component_register(SRCS "src/custom_config.c"
"proto-c/custom_config.pb-c.c"
INCLUDE_DIRS include
PRIV_INCLUDE_DIRS proto-c
PRIV_REQUIRES protobuf-c)

View File

@ -14,7 +14,6 @@
#ifndef _CUSTOM_PROV_CONFIG_H_
#define _CUSTOM_PROV_CONFIG_H_
#include <stdlib.h>
/**
* @brief Custom config data received by device

View File

@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.5)
set(PROTO_COMPILER "protoc")
set(PROTO_C_COMPILER "protoc-c")
set(C_OUT_PATH "${CMAKE_CURRENT_LIST_DIR}/../proto-c")
set(PY_OUT_PATH "${CMAKE_CURRENT_LIST_DIR}/../python")
set(PROTO_SRCS "custom_config.proto")
add_custom_target(c_proto
COMMAND ${PROTO_C_COMPILER} --c_out=${C_OUT_PATH} -I . ${PROTO_SRCS}
VERBATIM
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
)
add_custom_target(python_proto
COMMAND ${PROTO_COMPILER} --python_out=${PY_OUT_PATH} -I . ${PROTO_SRCS}
VERBATIM
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
)
add_custom_target(proto ALL
DEPENDS c_proto python_proto
VERBATIM
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
)

View File

@ -0,0 +1,27 @@
# Protobuf files for defining custom config-data packet structures
This is an example proto file defining custom configuration related data packet structures, namely -
1. CustomConfigRequest - for sending configuration data consisting of various fields (Info and Version)
2. CustomConfigResponse - for receiving configuration status (fail/success)
Note : These proto files are not automatically compiled during the build process.
# Compilation
Compilation requires protoc (Protobuf Compiler) and protoc-c (Protobuf C Compiler) installed. Since the generated files are to remain the same, as long as the proto files are not modified, therefore the generated files are already available under `examples/provisioning/custom_config/components/custom_provisioning/proto-c` and `examples/provisioning/custom_config/components/custom_provisioning/python` directories, and thus running cmake / make (and installing the Protobuf compilers) is optional.
If using `cmake` follow the below steps. If using `make`, jump to Step 2 directly.
## Step 1 (Only for cmake)
When using cmake, first create a build directory and call cmake from inside:
```
mkdir build
cd build
cmake ..
```
## Step 2
Simply run `make` to generate the respective C and Python files. The newly created files will overwrite those under `examples/provisioning/custom_config/components/custom_provisioning/proto-c` and `examples/provisioning/custom_config/components/custom_provisioning/python`

View File

@ -13,6 +13,7 @@
// limitations under the License.
#include <stdio.h>
#include <stdlib.h>
#include <esp_log.h>
#include <string.h>
#include <esp_err.h>

View File

@ -0,0 +1,4 @@
idf_component_register(SRCS "app_main.c"
"app_prov.c"
"app_prov_handlers.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,58 @@
menu "Example Configuration"
config EXAMPLE_SSID
string "Wi-Fi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config EXAMPLE_PASS
string "Wi-Fi Password"
default "mypassword"
help
Wi-Fi password (WPA or WPA2) for the example to use.
config EXAMPLE_USE_SEC_1
bool
default n
prompt "Use Security Version 1"
help
Security version 1 used Curve25519 key exchange for establishing
secure session between device and client during provisioning
config EXAMPLE_USE_POP
bool
depends on EXAMPLE_USE_SEC_1
default n
prompt "Use proof-of-possession"
help
Proof-of-possession can be optionally used to prove that the device is indeed
in possession of the user who is provisioning the device. This proof-of-possession
is internally used to generate the shared secret through key exchange.
config EXAMPLE_POP
string "Proof-of-possession"
default "abcd1234"
depends on EXAMPLE_USE_POP
config EXAMPLE_PROTOCOMM_HTTPD_PORT
int "Protocomm HTTP Port"
default 80
help
Port on which to run Protocomm HTTP based provisioning service
config EXAMPLE_RESET_PROVISIONED
bool
default n
prompt "Reset provisioned status of the device"
help
This erases the NVS to reset provisioned status of the device on every reboot.
Provisioned status is determined by the Wi-Fi STA configuration, saved on the NVS.
config EXAMPLE_AP_RECONN_ATTEMPTS
int "Maximum AP connection attempts"
default 5
help
Set the maximum connection attempts to perform when connecting to a Wi-Fi AP.
endmenu

View File

@ -8,27 +8,23 @@
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_system.h>
#include <esp_wifi.h>
#include <esp_event.h>
#include <esp_log.h>
#include <nvs_flash.h>
#include "esp_netif.h"
#include "esp_event.h"
#include "protocol_examples_common.h"
#include "nvs.h"
#include "nvs_flash.h"
#include <lwip/err.h>
#include <lwip/sys.h>
#include "app_prov.h"
static const char *TAG = "app";
#define EXAMPLE_AP_RECONN_ATTEMPTS CONFIG_EXAMPLE_AP_RECONN_ATTEMPTS
static const char *TAG = "app";
static void event_handler(void* arg, esp_event_base_t event_base,
int event_id, void* event_data)
{
@ -45,44 +41,50 @@ static void event_handler(void* arg, esp_event_base_t event_base,
ESP_LOGI(TAG,"connect to the AP fail");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "got ip:%s",
ip4addr_ntoa(&event->ip_info.ip));
ESP_LOGI(TAG, "got ip: " IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
}
}
static void wifi_init_sta()
static void wifi_init_sta(void)
{
/* Set our event handling */
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handler, NULL));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_start() );
/* Start Wi-Fi in station mode with credentials set during provisioning */
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_start());
}
void app_main()
static void start_softap_provisioning(void)
{
/* Security version */
int security = 0;
/* Proof of possession */
const protocomm_security_pop_t *pop = NULL;
#ifdef CONFIG_USE_SEC_1
#ifdef CONFIG_EXAMPLE_USE_SEC_1
security = 1;
#endif
/* Having proof of possession is optional */
#ifdef CONFIG_USE_POP
#ifdef CONFIG_EXAMPLE_USE_POP
const static protocomm_security_pop_t app_pop = {
.data = (uint8_t *) CONFIG_POP,
.len = (sizeof(CONFIG_POP)-1)
.data = (uint8_t *) CONFIG_EXAMPLE_POP,
.len = (sizeof(CONFIG_EXAMPLE_POP)-1)
};
pop = &app_pop;
#endif
ESP_ERROR_CHECK(app_prov_start_softap_provisioning(
CONFIG_EXAMPLE_SSID, CONFIG_EXAMPLE_PASS, security, pop));
}
void app_main(void)
{
/* Initialize networking stack */
tcpip_adapter_init();
ESP_ERROR_CHECK(esp_netif_init());
/* Create default event loop needed by the
* main app and the provisioning service */
@ -91,6 +93,10 @@ void app_main()
/* Initialize NVS needed by Wi-Fi */
ESP_ERROR_CHECK(nvs_flash_init());
/* Initialize Wi-Fi including netif with default config */
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
/* Check if device is provisioned */
bool provisioned;
if (app_prov_is_provisioned(&provisioned) != ESP_OK) {
@ -101,11 +107,10 @@ void app_main()
if (provisioned == false) {
/* If not provisioned, start provisioning via soft AP */
ESP_LOGI(TAG, "Starting WiFi SoftAP provisioning");
app_prov_start_softap_provisioning(CONFIG_SOFTAP_SSID, CONFIG_SOFTAP_PASS,
security, pop);
start_softap_provisioning();
} else {
/* Start WiFi station with credentials set during provisioning */
ESP_LOGI(TAG, "Starting WiFi station");
wifi_init_sta(NULL);
wifi_init_sta();
}
}

View File

@ -13,6 +13,7 @@
#include <esp_wifi.h>
#include <nvs_flash.h>
#include <nvs.h>
#include <esp_event.h>
#include <task.h>
#include <stdlib.h>
@ -28,6 +29,9 @@
static const char *TAG = "app_prov";
/* Handler for catching WiFi events */
static void app_prov_event_handler(void* handler_arg, esp_event_base_t base, int id, void* data);
/* Handlers for provisioning endpoints */
extern wifi_prov_config_handlers_t wifi_prov_handlers;
extern custom_prov_config_handler_t custom_prov_handler;
@ -117,6 +121,10 @@ static void app_prov_stop_service(void)
protocomm_httpd_stop(g_prov->pc);
/* Delete protocomm instance */
protocomm_delete(g_prov->pc);
/* Remove event handler */
esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, app_prov_event_handler);
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, app_prov_event_handler);
}
/* Task spawned by timer callback */
@ -145,33 +153,24 @@ static void _stop_prov_cb(void * arg)
xTaskCreate(&stop_prov_task, "stop_prov", 2048, NULL, tskIDLE_PRIORITY, NULL);
}
/* Event handler for starting/stopping provisioning.
* To be called from within the context of the main
* event handler.
*/
esp_err_t app_prov_event_handler(void *ctx, system_event_t *event)
/* Event handler for starting/stopping provisioning */
static void app_prov_event_handler(void* handler_arg, esp_event_base_t event_base,
int event_id, void* event_data)
{
/* For accessing reason codes in case of disconnection */
system_event_info_t *info = &event->event_info;
/* If pointer to provisioning application data is NULL
* then provisioning is not running, therefore return without
* error */
* then provisioning is not running */
if (!g_prov) {
return ESP_OK;
return;
}
switch(event->event_id) {
case SYSTEM_EVENT_STA_START:
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
ESP_LOGI(TAG, "STA Start");
/* Once configuration is received by protocomm server,
* device is restarted as both AP and Station.
* Once station starts, wait for connection to
* establish with configured host SSID and password */
/* Once configuration is received through protocomm,
* device is started as station. Once station starts,
* wait for connection to establish with configured
* host SSID and password */
g_prov->wifi_state = WIFI_PROV_STA_CONNECTING;
break;
case SYSTEM_EVENT_STA_GOT_IP:
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ESP_LOGI(TAG, "STA Got IP");
/* Station got IP. That means configuration is successful.
* Schedule timer to stop provisioning app after 30 seconds. */
@ -188,16 +187,16 @@ esp_err_t app_prov_event_handler(void *ctx, system_event_t *event)
* signaling a failure in provisioning. */
esp_timer_start_once(g_prov->timer, 30000*1000U);
}
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGE(TAG, "STA Disconnected");
/* Station couldn't connect to configured host SSID */
g_prov->wifi_state = WIFI_PROV_STA_DISCONNECTED;
ESP_LOGE(TAG, "Disconnect reason : %d", info->disconnected.reason);
wifi_event_sta_disconnected_t* disconnected = (wifi_event_sta_disconnected_t*) event_data;
ESP_LOGE(TAG, "Disconnect reason : %d", disconnected->reason);
/* Set code corresponding to the reason for disconnection */
switch (info->disconnected.reason) {
switch (disconnected->reason) {
case WIFI_REASON_AUTH_EXPIRE:
case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT:
case WIFI_REASON_BEACON_TIMEOUT:
@ -215,18 +214,13 @@ esp_err_t app_prov_event_handler(void *ctx, system_event_t *event)
/* If none of the expected reasons,
* retry connecting to host SSID */
g_prov->wifi_state = WIFI_PROV_STA_CONNECTING;
if (info->disconnected.reason == WIFI_REASON_BASIC_RATE_NOT_SUPPORT) {
if (disconnected->reason == WIFI_REASON_BASIC_RATE_NOT_SUPPORT) {
/*Switch to 802.11 bgn mode */
esp_wifi_set_protocol(ESP_IF_WIFI_STA, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N);
}
esp_wifi_connect();
}
break;
default:
break;
}
return ESP_OK;
}
esp_err_t app_prov_get_wifi_state(wifi_prov_sta_state_t* state)
@ -255,25 +249,15 @@ esp_err_t app_prov_get_wifi_disconnect_reason(wifi_prov_sta_fail_reason_t* reaso
esp_err_t app_prov_is_provisioned(bool *provisioned)
{
#ifdef CONFIG_RESET_PROVISIONED
*provisioned = false;
#ifdef CONFIG_EXAMPLE_RESET_PROVISIONED
nvs_flash_erase();
#endif
if (nvs_flash_init() != ESP_OK) {
ESP_LOGE(TAG, "Failed to init NVS");
return ESP_FAIL;
}
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
if (esp_wifi_init(&cfg) != ESP_OK) {
ESP_LOGE(TAG, "Failed to init wifi");
return ESP_FAIL;
}
/* Get WiFi Station configuration */
wifi_config_t wifi_cfg;
if (esp_wifi_get_config(ESP_IF_WIFI_STA, &wifi_cfg) != ESP_OK) {
*provisioned = false;
return ESP_FAIL;
}
@ -318,14 +302,6 @@ esp_err_t app_prov_configure_sta(wifi_config_t *wifi_cfg)
static esp_err_t start_wifi_ap(const char *ssid, const char *pass)
{
/* Initialize WiFi with default configuration */
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_err_t err = esp_wifi_init(&cfg);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to init WiFi : %d", err);
return err;
}
/* Build WiFi configuration for AP mode */
wifi_config_t wifi_config = {
.ap = {
@ -333,19 +309,18 @@ static esp_err_t start_wifi_ap(const char *ssid, const char *pass)
},
};
strncpy((char *) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid));
wifi_config.ap.ssid_len = strlen(ssid);
strlcpy((char *) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid));
if (strlen(pass) == 0) {
memset(wifi_config.ap.password, 0, sizeof(wifi_config.ap.password));
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
} else {
strncpy((char *) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password));
strlcpy((char *) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password));
wifi_config.ap.authmode = WIFI_AUTH_WPA_WPA2_PSK;
}
/* Start WiFi in AP mode with configuration built above */
err = esp_wifi_set_mode(WIFI_MODE_AP);
esp_err_t err = esp_wifi_set_mode(WIFI_MODE_AP);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set WiFi mode : %d", err);
return err;
@ -381,7 +356,7 @@ esp_err_t app_prov_start_softap_provisioning(const char *ssid, const char *pass,
return ESP_ERR_NO_MEM;
}
/* Initialise app data */
/* Initialize app data */
g_prov->pop = pop;
g_prov->security = security;
@ -398,6 +373,18 @@ esp_err_t app_prov_start_softap_provisioning(const char *ssid, const char *pass,
return err;
}
err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, app_prov_event_handler, NULL);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to register WiFi event handler");
return err;
}
err = esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, app_prov_event_handler, NULL);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to register IP event handler");
return err;
}
/* Start WiFi softAP with specified ssid and password */
err = start_wifi_ap(ssid, pass);
if (err != ESP_OK) {

View File

@ -9,7 +9,7 @@
#pragma once
#include <esp_event_loop.h>
#include <esp_event.h>
#include <protocomm_security.h>
#include <wifi_provisioning/wifi_config.h>
@ -43,21 +43,6 @@ esp_err_t app_prov_get_wifi_state(wifi_prov_sta_state_t* state);
*/
esp_err_t app_prov_get_wifi_disconnect_reason(wifi_prov_sta_fail_reason_t* reason);
/**
* @brief Event handler for provisioning app
*
* This is called from the main event handler and controls the
* provisioning application, depeding on WiFi events
*
* @param[in] ctx Event context data
* @param[in] event Event info
*
* @return
* - ESP_OK : Event handled successfully
* - ESP_FAIL : Failed to start server on event AP start
*/
esp_err_t app_prov_event_handler(void *ctx, system_event_t *event);
/**
* @brief Checks if device is provisioned
* *

View File

@ -15,7 +15,7 @@
#include <esp_log.h>
#include <esp_wifi.h>
#include <tcpip_adapter.h>
#include <esp_netif.h>
#include <wifi_provisioning/wifi_config.h>
#include <custom_provisioning/custom_config.h>
@ -110,10 +110,14 @@ static esp_err_t set_config_handler(const wifi_prov_config_set_data_t *req_data,
ESP_LOGI(TAG, "WiFi Credentials Received : \n\tssid %s \n\tpassword %s",
req_data->ssid, req_data->password);
memcpy((char *) wifi_cfg->sta.ssid, req_data->ssid,
strnlen(req_data->ssid, sizeof(wifi_cfg->sta.ssid)));
memcpy((char *) wifi_cfg->sta.password, req_data->password,
strnlen(req_data->password, sizeof(wifi_cfg->sta.password)));
/* Using strncpy allows the max SSID length to be 32 bytes (as per 802.11 standard).
* But this doesn't guarantee that the saved SSID will be null terminated, because
* wifi_cfg->sta.ssid is also 32 bytes long (without extra 1 byte for null character).
* Although, this is not a matter for concern because esp_wifi library reads the SSID
* upto 32 bytes in absence of null termination */
strncpy((char *) wifi_cfg->sta.ssid, req_data->ssid, sizeof(wifi_cfg->sta.ssid));
strlcpy((char *) wifi_cfg->sta.password, req_data->password, sizeof(wifi_cfg->sta.password));
return ESP_OK;
}

View File

@ -0,0 +1 @@
CONFIG_ENABLE_UNIFIED_PROVISIONING=y

View File

@ -2,9 +2,5 @@
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(softap_prov)

View File

@ -5,7 +5,5 @@
PROJECT_NAME := softap_prov
EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common
include $(IDF_PATH)/make/project.mk

View File

@ -1,4 +1,6 @@
# SoftAP + HTTPD based Provisioning Example
# SoftAP + HTTPD based Provisioning Example (Legacy)
> Note: It is recommended to use the new `wifi_prov_mgr` example which is based on the simpler `wifi_provisioning` APIs. Check this example only if you wish to use lower level provisioning and protocomm APIs and want more control over the handlers.
(See the README.md file in the upper level 'examples' directory for more information about examples.)
@ -42,11 +44,9 @@ To provision the device running this example, the `esp_prov.py` script needs to
### Configure the project
```
make menuconfig
idf.py menuconfig
```
* Set serial port under Serial Flasher Options.
* Under Example Configuration set the following :
* SoftAP SSID (Defaults to PROV_<MACID>)
* SoftAP Password (Defaults to PROV_PASS)
@ -58,7 +58,7 @@ make menuconfig
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
@ -81,7 +81,7 @@ I (519482) tcpip_adapter: softAP assign IP to station,IP is: 192.168.4.2
In a separate terminal run the `esp_prov.py` script under `$IDP_PATH/tools/esp_prov` directory (please replace `myssid` and `mypassword` with the credentials of the AP to which the device is supposed to connect to after provisioning). The SoftAP endpoint corresponds to the IP and port of the device on the SoftAP network, but this is usually same as the default value and may be left out. Assuming default example configuration, the script should be run as follows :
```
python esp_prov.py --ssid myssid --passphrase mypassword --sec_ver 1 --pop abcd1234 --transport softap --softap_endpoint 192.168.4.1:80
python esp_prov.py --transport softap --service_name "192.168.4.1:80" --sec_ver 1 --pop abcd1234 --ssid myssid --passphrase mypassword
```
Above command will perform the provisioning steps, and the monitor log should display something like this :
@ -145,7 +145,7 @@ It means the Wi-Fi credentials were already set by some other application flashe
```
make erase_flash
make -j4 flash monitor
idf.py -p PORT flash monitor
```
Or, enable `Reset Provisioning` option under `Example Configuration` under menuconfig. But this will erase the saved Wi-Fi credentials every time the device boots, so this is not the preferred solution.

View File

@ -0,0 +1,4 @@
idf_component_register(SRCS "app_main.c"
"app_prov.c"
"app_prov_handlers.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,59 @@
menu "Example Configuration"
config EXAMPLE_SSID_SET_MAC
bool "Use MAC as SSID"
default y
help
Set SoftAP SSID as PROV_<MAC>.
config EXAMPLE_SSID
string "Wi-Fi SSID"
default "PROV_SSID"
depends on !EXAMPLE_SSID_SET_MAC
help
SSID (network name) for the example to connect to.
config EXAMPLE_PASS
string "Wi-Fi Password"
default "PROV_PASS"
help
Wi-Fi password (WPA or WPA2) for the example to use.
config EXAMPLE_USE_SEC_1
bool
default y
prompt "Use Security Version 1"
help
Security version 1 used Curve25519 key exchange for establishing
secure session between device and client during provisioning
config EXAMPLE_USE_POP
bool
depends on EXAMPLE_USE_SEC_1
default y
prompt "Use proof-of-possession"
help
Proof-of-possession can be optionally used to prove that the device is indeed
in possession of the user who is provisioning the device. This proof-of-possession
is internally used to generate the shared secret through key exchange.
config EXAMPLE_POP
string "Proof-of-possession"
default "abcd1234"
depends on EXAMPLE_USE_POP
config EXAMPLE_RESET_PROVISIONED
bool
default n
prompt "Reset provisioned status of the device"
help
This erases the NVS to reset provisioned status of the device on every reboot.
Provisioned status is determined by the Wi-Fi STA configuration, saved on the NVS.
config EXAMPLE_AP_RECONN_ATTEMPTS
int "Maximum AP connection attempts"
default 5
help
Set the maximum connection attempts to perform when connecting to a Wi-Fi AP.
endmenu

View File

@ -12,19 +12,19 @@
#include <freertos/task.h>
#include <esp_system.h>
#include <esp_wifi.h>
#include <esp_event_loop.h>
#include <esp_event.h>
#include <esp_log.h>
#include <nvs_flash.h>
#include "esp_netif.h"
#include <lwip/err.h>
#include <lwip/sys.h>
#include "app_prov.h"
static const char *TAG = "app";
#define EXAMPLE_AP_RECONN_ATTEMPTS CONFIG_EXAMPLE_AP_RECONN_ATTEMPTS
static const char *TAG = "app";
static void event_handler(void* arg, esp_event_base_t event_base,
int event_id, void* event_data)
{
@ -41,66 +41,46 @@ static void event_handler(void* arg, esp_event_base_t event_base,
ESP_LOGI(TAG,"connect to the AP fail");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "got ip:%s",
ip4addr_ntoa(&event->ip_info.ip));
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
}
}
static void wifi_init_sta()
static void wifi_init_sta(void)
{
/* Set our event handling */
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handler, NULL));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_start() );
/* Start Wi-Fi in station mode with credentials set during provisioning */
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_start());
}
void app_main()
static void start_softap_provisioning(void)
{
/* Security version */
int security = 0;
/* Proof of possession */
const protocomm_security_pop_t *pop = NULL;
#ifdef CONFIG_USE_SEC_1
#ifdef CONFIG_EXAMPLE_USE_SEC_1
security = 1;
#endif
/* Having proof of possession is optional */
#ifdef CONFIG_USE_POP
#ifdef CONFIG_EXAMPLE_USE_POP
const static protocomm_security_pop_t app_pop = {
.data = (uint8_t *) CONFIG_POP,
.len = (sizeof(CONFIG_POP)-1)
.data = (uint8_t *) CONFIG_EXAMPLE_POP,
.len = (sizeof(CONFIG_EXAMPLE_POP)-1)
};
pop = &app_pop;
#endif
/* Initialize networking stack */
tcpip_adapter_init();
/* Create default event loop needed by the
* main app and the provisioning service */
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* Initialize NVS needed by Wi-Fi */
ESP_ERROR_CHECK(nvs_flash_init());
/* Check if device is provisioned */
bool provisioned;
if (app_prov_is_provisioned(&provisioned) != ESP_OK) {
ESP_LOGE(TAG, "Error getting device provisioning state");
return;
}
if (provisioned == false) {
/* If not provisioned, start provisioning via soft AP */
ESP_LOGI(TAG, "Starting WiFi SoftAP provisioning");
const char *ssid = NULL;
#ifdef CONFIG_SOFTAP_SSID
ssid = CONFIG_SOFTAP_SSID;
#ifdef CONFIG_EXAMPLE_SSID
ssid = CONFIG_EXAMPLE_SSID;
#else
uint8_t eth_mac[6];
esp_wifi_get_mac(WIFI_IF_STA, eth_mac);
@ -112,11 +92,40 @@ void app_main()
ssid = ssid_with_mac;
#endif
app_prov_start_softap_provisioning(ssid, CONFIG_SOFTAP_PASS,
security, pop);
ESP_ERROR_CHECK(app_prov_start_softap_provisioning(
ssid, CONFIG_EXAMPLE_PASS, security, pop));
}
void app_main(void)
{
/* Initialize networking stack */
ESP_ERROR_CHECK(esp_netif_init());
/* Create default event loop needed by the
* main app and the provisioning service */
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* Initialize NVS needed by Wi-Fi */
ESP_ERROR_CHECK(nvs_flash_init());
/* Initialize Wi-Fi including netif with default config */
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
/* Check if device is provisioned */
bool provisioned;
if (app_prov_is_provisioned(&provisioned) != ESP_OK) {
ESP_LOGE(TAG, "Error getting device provisioning state");
return;
}
if (provisioned == false) {
/* If not provisioned, start provisioning via soft AP */
ESP_LOGI(TAG, "Starting WiFi SoftAP provisioning");
start_softap_provisioning();
} else {
/* Start WiFi station with credentials set during provisioning */
ESP_LOGI(TAG, "Starting WiFi station");
wifi_init_sta(NULL);
wifi_init_sta();
}
}

View File

@ -13,6 +13,7 @@
#include <esp_wifi.h>
#include <nvs_flash.h>
#include <nvs.h>
#include <esp_event.h>
#include <task.h>
#include <stdlib.h>
@ -27,6 +28,9 @@
static const char *TAG = "app_prov";
/* Handler for catching WiFi events */
static void app_prov_event_handler(void* handler_arg, esp_event_base_t base, int id, void* data);
/* Handlers for wifi_config provisioning endpoint */
extern wifi_prov_config_handlers_t wifi_prov_handlers;
@ -103,6 +107,10 @@ static void app_prov_stop_service(void)
protocomm_httpd_stop(g_prov->pc);
/* Delete protocomm instance */
protocomm_delete(g_prov->pc);
/* Remove event handler */
esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, app_prov_event_handler);
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, app_prov_event_handler);
}
/* Task spawned by timer callback */
@ -131,33 +139,24 @@ static void _stop_prov_cb(void * arg)
xTaskCreate(&stop_prov_task, "stop_prov", 2048, NULL, tskIDLE_PRIORITY, NULL);
}
/* Event handler for starting/stopping provisioning.
* To be called from within the context of the main
* event handler.
*/
esp_err_t app_prov_event_handler(void *ctx, system_event_t *event)
/* Event handler for starting/stopping provisioning */
static void app_prov_event_handler(void* handler_arg, esp_event_base_t event_base,
int event_id, void* event_data)
{
/* For accessing reason codes in case of disconnection */
system_event_info_t *info = &event->event_info;
/* If pointer to provisioning application data is NULL
* then provisioning is not running, therefore return without
* error */
* then provisioning is not running */
if (!g_prov) {
return ESP_OK;
return;
}
switch(event->event_id) {
case SYSTEM_EVENT_STA_START:
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
ESP_LOGI(TAG, "STA Start");
/* Once configuration is received by protocomm server,
* device is restarted as both AP and Station.
* Once station starts, wait for connection to
* establish with configured host SSID and password */
/* Once configuration is received through protocomm,
* device is started as station. Once station starts,
* wait for connection to establish with configured
* host SSID and password */
g_prov->wifi_state = WIFI_PROV_STA_CONNECTING;
break;
case SYSTEM_EVENT_STA_GOT_IP:
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ESP_LOGI(TAG, "STA Got IP");
/* Station got IP. That means configuration is successful.
* Schedule timer to stop provisioning app after 30 seconds. */
@ -174,16 +173,16 @@ esp_err_t app_prov_event_handler(void *ctx, system_event_t *event)
* signaling a failure in provisioning. */
esp_timer_start_once(g_prov->timer, 30000*1000U);
}
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGE(TAG, "STA Disconnected");
/* Station couldn't connect to configured host SSID */
g_prov->wifi_state = WIFI_PROV_STA_DISCONNECTED;
ESP_LOGE(TAG, "Disconnect reason : %d", info->disconnected.reason);
wifi_event_sta_disconnected_t* disconnected = (wifi_event_sta_disconnected_t*) event_data;
ESP_LOGE(TAG, "Disconnect reason : %d", disconnected->reason);
/* Set code corresponding to the reason for disconnection */
switch (info->disconnected.reason) {
switch (disconnected->reason) {
case WIFI_REASON_AUTH_EXPIRE:
case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT:
case WIFI_REASON_BEACON_TIMEOUT:
@ -198,21 +197,16 @@ esp_err_t app_prov_event_handler(void *ctx, system_event_t *event)
g_prov->wifi_disconnect_reason = WIFI_PROV_STA_AP_NOT_FOUND;
break;
default:
if (info->disconnected.reason == WIFI_REASON_BASIC_RATE_NOT_SUPPORT) {
/*Switch to 802.11 bgn mode */
esp_wifi_set_protocol(ESP_IF_WIFI_STA, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N);
}
/* If none of the expected reasons,
* retry connecting to host SSID */
g_prov->wifi_state = WIFI_PROV_STA_CONNECTING;
if (disconnected->reason == WIFI_REASON_BASIC_RATE_NOT_SUPPORT) {
/*Switch to 802.11 bgn mode */
esp_wifi_set_protocol(ESP_IF_WIFI_STA, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N);
}
esp_wifi_connect();
}
break;
default:
break;
}
return ESP_OK;
}
esp_err_t app_prov_get_wifi_state(wifi_prov_sta_state_t* state)
@ -243,21 +237,10 @@ esp_err_t app_prov_is_provisioned(bool *provisioned)
{
*provisioned = false;
#ifdef CONFIG_RESET_PROVISIONED
#ifdef CONFIG_EXAMPLE_RESET_PROVISIONED
nvs_flash_erase();
#endif
if (nvs_flash_init() != ESP_OK) {
ESP_LOGE(TAG, "Failed to init NVS");
return ESP_FAIL;
}
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
if (esp_wifi_init(&cfg) != ESP_OK) {
ESP_LOGE(TAG, "Failed to init wifi");
return ESP_FAIL;
}
/* Get WiFi Station configuration */
wifi_config_t wifi_cfg;
if (esp_wifi_get_config(ESP_IF_WIFI_STA, &wifi_cfg) != ESP_OK) {
@ -305,14 +288,6 @@ esp_err_t app_prov_configure_sta(wifi_config_t *wifi_cfg)
static esp_err_t start_wifi_ap(const char *ssid, const char *pass)
{
/* Initialize WiFi with default configuration */
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_err_t err = esp_wifi_init(&cfg);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to init WiFi : %d", err);
return err;
}
/* Build WiFi configuration for AP mode */
wifi_config_t wifi_config = {
.ap = {
@ -320,19 +295,18 @@ static esp_err_t start_wifi_ap(const char *ssid, const char *pass)
},
};
strncpy((char *) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid));
wifi_config.ap.ssid_len = strlen(ssid);
strlcpy((char *) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid));
if (strlen(pass) == 0) {
memset(wifi_config.ap.password, 0, sizeof(wifi_config.ap.password));
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
} else {
strncpy((char *) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password));
strlcpy((char *) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password));
wifi_config.ap.authmode = WIFI_AUTH_WPA_WPA2_PSK;
}
/* Start WiFi in AP mode with configuration built above */
err = esp_wifi_set_mode(WIFI_MODE_AP);
esp_err_t err = esp_wifi_set_mode(WIFI_MODE_AP);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set WiFi mode : %d", err);
return err;
@ -385,6 +359,18 @@ esp_err_t app_prov_start_softap_provisioning(const char *ssid, const char *pass,
return err;
}
err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, app_prov_event_handler, NULL);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to register WiFi event handler");
return err;
}
err = esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, app_prov_event_handler, NULL);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to register IP event handler");
return err;
}
/* Start WiFi softAP with specified ssid and password */
err = start_wifi_ap(ssid, pass);
if (err != ESP_OK) {

View File

@ -9,7 +9,7 @@
#pragma once
#include <esp_event_loop.h>
#include <esp_event.h>
#include <protocomm_security.h>
#include <wifi_provisioning/wifi_config.h>
@ -42,21 +42,6 @@ esp_err_t app_prov_get_wifi_state(wifi_prov_sta_state_t* state);
*/
esp_err_t app_prov_get_wifi_disconnect_reason(wifi_prov_sta_fail_reason_t* reason);
/**
* @brief Event handler for provisioning app
*
* This is called from the main event handler and controls the
* provisioning application, depeding on WiFi events
*
* @param[in] ctx Event context data
* @param[in] event Event info
*
* @return
* - ESP_OK : Event handled successfully
* - ESP_FAIL : Failed to start server on event AP start
*/
esp_err_t app_prov_event_handler(void *ctx, system_event_t *event);
/**
* @brief Checks if device is provisioned
* *

View File

@ -15,7 +15,7 @@
#include <esp_log.h>
#include <esp_wifi.h>
#include <tcpip_adapter.h>
#include <esp_netif.h>
#include <wifi_provisioning/wifi_config.h>
@ -98,10 +98,14 @@ static esp_err_t set_config_handler(const wifi_prov_config_set_data_t *req_data,
ESP_LOGI(TAG, "WiFi Credentials Received : \n\tssid %s \n\tpassword %s",
req_data->ssid, req_data->password);
memcpy((char *) wifi_cfg->sta.ssid, req_data->ssid,
strnlen(req_data->ssid, sizeof(wifi_cfg->sta.ssid)));
memcpy((char *) wifi_cfg->sta.password, req_data->password,
strnlen(req_data->password, sizeof(wifi_cfg->sta.password)));
/* Using strncpy allows the max SSID length to be 32 bytes (as per 802.11 standard).
* But this doesn't guarantee that the saved SSID will be null terminated, because
* wifi_cfg->sta.ssid is also 32 bytes long (without extra 1 byte for null character).
* Although, this is not a matter for concern because esp_wifi library reads the SSID
* upto 32 bytes in absence of null termination */
strncpy((char *) wifi_cfg->sta.ssid, req_data->ssid, sizeof(wifi_cfg->sta.ssid));
strlcpy((char *) wifi_cfg->sta.password, req_data->password, sizeof(wifi_cfg->sta.password));
return ESP_OK;
}

View File

@ -0,0 +1 @@
CONFIG_ENABLE_UNIFIED_PROVISIONING=y

View File

@ -0,0 +1,116 @@
#!/usr/bin/env python
#
# Copyright 2018 Espressif Systems (Shanghai) PTE LTD
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import print_function
import re
import os
import time
import ttfw_idf
import esp_prov
import wifi_tools
# Have esp_prov throw exception
esp_prov.config_throw_except = True
@ttfw_idf.idf_example_test(env_tag="Example_WIFI_BT")
def test_examples_provisioning_softap(env, extra_data):
# Acquire DUT
dut1 = env.get_dut("softap_prov", "examples/provisioning/legacy/softap_prov", dut_class=ttfw_idf.ESP32DUT)
# Get binary file
binary_file = os.path.join(dut1.app.binary_path, "softap_prov.bin")
bin_size = os.path.getsize(binary_file)
ttfw_idf.log_performance("softap_prov_bin_size", "{}KB".format(bin_size // 1024))
ttfw_idf.check_performance("softap_prov_bin_size", bin_size // 1024, dut1.TARGET)
# Upload binary and start testing
dut1.start_app()
# Parse IP address of STA
dut1.expect("Starting WiFi SoftAP provisioning", timeout=60)
[ssid, password] = dut1.expect(re.compile(r"SoftAP Provisioning started with SSID '(\S+)', Password '(\S+)'"), timeout=30)
iface = wifi_tools.get_wiface_name()
if iface is None:
raise RuntimeError("Failed to get Wi-Fi interface on host")
print("Interface name : " + iface)
print("SoftAP SSID : " + ssid)
print("SoftAP Password : " + password)
ctrl = wifi_tools.wpa_cli(iface, reset_on_exit=True)
print("Connecting to DUT SoftAP...")
ip = ctrl.connect(ssid, password)
got_ip = dut1.expect(re.compile(r"DHCP server assigned IP to a station, IP is: (\d+.\d+.\d+.\d+)"), timeout=30)[0]
if ip != got_ip:
raise RuntimeError("SoftAP connected to another host! " + ip + "!=" + got_ip)
print("Connected to DUT SoftAP")
print("Starting Provisioning")
verbose = False
protover = "V0.1"
secver = 1
pop = "abcd1234"
provmode = "softap"
ap_ssid = "myssid"
ap_password = "mypassword"
softap_endpoint = ip.split('.')[0] + "." + ip.split('.')[1] + "." + ip.split('.')[2] + ".1:80"
print("Getting security")
security = esp_prov.get_security(secver, pop, verbose)
if security is None:
raise RuntimeError("Failed to get security")
print("Getting transport")
transport = esp_prov.get_transport(provmode, softap_endpoint)
if transport is None:
raise RuntimeError("Failed to get transport")
print("Verifying protocol version")
if not esp_prov.version_match(transport, protover):
raise RuntimeError("Mismatch in protocol version")
print("Starting Session")
if not esp_prov.establish_session(transport, security):
raise RuntimeError("Failed to start session")
print("Sending Wifi credential to DUT")
if not esp_prov.send_wifi_config(transport, security, ap_ssid, ap_password):
raise RuntimeError("Failed to send Wi-Fi config")
print("Applying config")
if not esp_prov.apply_wifi_config(transport, security):
raise RuntimeError("Failed to send apply config")
success = False
while True:
time.sleep(5)
print("Wi-Fi connection state")
ret = esp_prov.get_wifi_config(transport, security)
if (ret == 1):
continue
elif (ret == 0):
print("Provisioning was successful")
success = True
break
if not success:
raise RuntimeError("Provisioning failed")
if __name__ == '__main__':
test_examples_provisioning_softap()

View File

@ -1,6 +0,0 @@
set(COMPONENT_SRCS "app_main.c"
"app_prov.c"
"app_prov_handlers.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -1,59 +0,0 @@
menu "Example Configuration"
config SOFTAP_SSID_SET_MAC
bool "Use MAC as SSID"
default y
help
Set SoftAP SSID as PROV_<MAC>.
config SOFTAP_SSID
string "WiFi SSID"
default "PROV_SSID"
depends on !SOFTAP_SSID_SET_MAC
help
SSID (network name) for the example to connect to.
config SOFTAP_PASS
string "WiFi Password"
default "PROV_PASS"
help
WiFi password (WPA or WPA2) for the example to use.
config USE_SEC_1
bool
default y
prompt "Use Security Version 1"
help
Security version 1 used Curve25519 key exchange for establishing
secure session between device and client during provisioning
config USE_POP
bool
depends on USE_SEC_1
default y
prompt "Use proof-of-possession"
help
Proof-of-possession can be optionally used to prove that the device is indeed
in possession of the user who is provisioning the device. This proof-of-possession
is internally used to generate the shared secret through key exchange.
config POP
string "Proof-of-possession"
default "abcd1234"
depends on USE_POP
config RESET_PROVISIONED
bool
default n
prompt "Reset provisioned status of the device"
help
This erases the NVS to reset provisioned status of the device on every reboot.
Provisioned status is determined by the WiFi STA configuration, saved on the NVS.
config EXAMPLE_AP_RECONN_ATTEMPTS
int "Maximum AP connection attempts"
default 5
help
Set the maximum connection attempts to perform when connecting to a Wi-Fi AP.
endmenu

View File

@ -1,3 +0,0 @@
CONFIG_ENABLE_UNIFIED_PROVISIONING=y
CONFIG_LWIP_NETIF_LOOPBACK=y
CONFIG_LWIP_LOOPBACK_MAX_PBUFS=1

View File

@ -1,98 +0,0 @@
# Copyright 2018 Espressif Systems (Shanghai) PTE LTD
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import dbus
import dbus.mainloop.glib
import netifaces
import time
def get_wiface_name():
for iface in netifaces.interfaces():
if iface.startswith('w'):
return iface
return None
def get_wiface_IPv4(iface):
try:
[info] = netifaces.ifaddresses(iface)[netifaces.AF_INET]
return info['addr']
except KeyError:
return None
class wpa_cli:
def __init__(self, iface, reset_on_exit = False):
self.iface_name = iface
self.iface_obj = None
self.iface_ifc = None
self.old_network = None
self.new_network = None
self.connected = False
self.reset_on_exit = reset_on_exit
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus()
service = dbus.Interface(bus.get_object("fi.w1.wpa_supplicant1", "/fi/w1/wpa_supplicant1"), "fi.w1.wpa_supplicant1")
paths = service.Get("fi.w1.wpa_supplicant1", "Interfaces", dbus_interface='org.freedesktop.DBus.Properties')
iface_path = service.GetInterface(self.iface_name)
self.iface_obj = bus.get_object("fi.w1.wpa_supplicant1", iface_path)
self.iface_ifc = dbus.Interface(self.iface_obj, "fi.w1.wpa_supplicant1.Interface")
if self.iface_ifc == None:
raise RuntimeError('supplicant : Failed to fetch interface')
self.old_network = self.iface_obj.Get("fi.w1.wpa_supplicant1.Interface", "CurrentNetwork", dbus_interface='org.freedesktop.DBus.Properties')
if self.old_network == '/':
self.old_network = None
else:
self.connected = True
def connect(self, ssid, password):
if self.connected == True:
self.iface_ifc.Disconnect()
self.connected = False
if self.new_network != None:
self.iface_ifc.RemoveNetwork(self.new_network)
self.new_network = self.iface_ifc.AddNetwork({"ssid": ssid, "psk": password})
self.iface_ifc.SelectNetwork(self.new_network)
ip = None
retry = 10
while retry > 0:
time.sleep(5)
ip = get_wiface_IPv4(self.iface_name)
if ip != None:
self.connected = True
return ip
retry -= 1
self.reset()
raise RuntimeError('wpa_cli : Connection failed')
def reset(self):
if self.iface_ifc != None:
if self.connected == True:
self.iface_ifc.Disconnect()
self.connected = False
if self.new_network != None:
self.iface_ifc.RemoveNetwork(self.new_network)
self.new_network = None
if self.old_network != None:
self.iface_ifc.SelectNetwork(self.old_network)
self.old_network = None
def __del__(self):
if self.reset_on_exit == True:
self.reset()