set(COMPONENT_ADD_INCLUDEDIRS include)
-set(COMPONENT_PRIV_INCLUDEDIRS proto-c ../protocomm/proto-c)
+set(COMPONENT_PRIV_INCLUDEDIRS src proto-c ../protocomm/proto-c)
set(COMPONENT_SRCS "src/wifi_config.c"
+ "src/manager.c"
+ "src/handlers.c"
+ "src/scheme_softap.c"
+ "src/scheme_console.c"
"proto-c/wifi_config.pb-c.c"
"proto-c/wifi_constants.pb-c.c")
-set(COMPONENT_REQUIRES lwip)
-set(COMPONENT_PRIV_REQUIRES protobuf-c protocomm)
+set(COMPONENT_REQUIRES lwip protocomm)
+set(COMPONENT_PRIV_REQUIRES protobuf-c bt mdns json)
+
+if(CONFIG_BT_ENABLED)
+ if(CONFIG_BLUEDROID_ENABLED)
+ list(APPEND COMPONENT_SRCS
+ "src/scheme_ble.c")
+ endif()
+endif()
register_component()
COMPONENT_SRCDIRS := src proto-c
COMPONENT_ADD_INCLUDEDIRS := include
-COMPONENT_PRIV_INCLUDEDIRS := proto-c ../protocomm/proto-c/
+COMPONENT_PRIV_INCLUDEDIRS := src proto-c ../protocomm/proto-c/
+
+ifneq ($(filter y, $(CONFIG_BT_ENABLED) $(CONFIG_BLUEDROID_ENABLED)),y y)
+ COMPONENT_OBJEXCLUDE := src/scheme_ble.o
+endif
--- /dev/null
+// Copyright 2019 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.
+
+#pragma once
+
+#include <esp_event_loop.h>
+#include <protocomm.h>
+
+#include "wifi_provisioning/wifi_config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Events generated by manager
+ *
+ * These events are generated in order of declaration and, for the
+ * stretch of time between initialization and de-initialization of
+ * the manager, each event is signaled only once
+ */
+typedef enum {
+ /**
+ * Emitted when the manager is initialized
+ */
+ WIFI_PROV_INIT,
+
+ /**
+ * Indicates that provisioning has started
+ */
+ WIFI_PROV_START,
+
+ /**
+ * Emitted when Wi-Fi AP credentials are received via `protocomm`
+ * endpoint `wifi_config`. The event data in this case is a pointer
+ * to the corresponding `wifi_sta_config_t` structure
+ */
+ WIFI_PROV_CRED_RECV,
+
+ /**
+ * Emitted when device fails to connect to the AP of which the
+ * credentials were received earlier on event `WIFI_PROV_CRED_RECV`.
+ * The event data in this case is a pointer to the disconnection
+ * reason code with type `wifi_prov_sta_fail_reason_t`
+ */
+ WIFI_PROV_CRED_FAIL,
+
+ /**
+ * Emitted when device successfully connects to the AP of which the
+ * credentials were received earlier on event `WIFI_PROV_CRED_RECV`
+ */
+ WIFI_PROV_CRED_SUCCESS,
+
+ /**
+ * Signals that provisioning service has stopped
+ */
+ WIFI_PROV_END,
+
+ /**
+ * Signals that manager has been de-initialized
+ */
+ WIFI_PROV_DEINIT,
+} wifi_prov_cb_event_t;
+
+typedef void (*wifi_prov_cb_func_t)(void *user_data, wifi_prov_cb_event_t event, void *event_data);
+
+/**
+ * @brief Event handler that is used by the manager while
+ * provisioning service is active
+ */
+typedef struct {
+ /**
+ * Callback function to be executed on provisioning events
+ */
+ wifi_prov_cb_func_t event_cb;
+
+ /**
+ * User context data to pass as parameter to callback function
+ */
+ void *user_data;
+} wifi_prov_event_handler_t;
+
+/**
+ * @brief Event handler can be set to none if not used
+ */
+#define WIFI_PROV_EVENT_HANDLER_NONE { \
+ .event_cb = NULL, \
+ .user_data = NULL \
+}
+
+/**
+ * @brief Structure for specifying the provisioning scheme to be
+ * followed by the manager
+ *
+ * @note Ready to use schemes are available:
+ * - wifi_prov_scheme_ble : for provisioning over BLE transport + GATT server
+ * - wifi_prov_scheme_softap : for provisioning over SoftAP transport + HTTP server
+ * - wifi_prov_scheme_console : for provisioning over Serial UART transport + Console (for debugging)
+ */
+typedef struct wifi_prov_scheme {
+ /**
+ * Function which is to be called by the manager when it is to
+ * start the provisioning service associated with a protocomm instance
+ * and a scheme specific configuration
+ */
+ esp_err_t (*prov_start) (protocomm_t *pc, void *config);
+
+ /**
+ * Function which is to be called by the manager to stop the
+ * provisioning service previously associated with a protocomm instance
+ */
+ esp_err_t (*prov_stop) (protocomm_t *pc);
+
+ /**
+ * Function which is to be called by the manager to generate
+ * a new configuration for the provisioning service, that is
+ * to be passed to prov_start()
+ */
+ void *(*new_config) (void);
+
+ /**
+ * Function which is to be called by the manager to delete a
+ * configuration generated using new_config()
+ */
+ void (*delete_config) (void *config);
+
+ /**
+ * Function which is to be called by the manager to set the
+ * service name and key values in the configuration structure
+ */
+ esp_err_t (*set_config_service) (void *config, const char *service_name, const char *service_key);
+
+ /**
+ * Function which is to be called by the manager to set a protocomm endpoint
+ * with an identifying name and UUID in the configuration structure
+ */
+ esp_err_t (*set_config_endpoint) (void *config, const char *endpoint_name, uint16_t uuid);
+
+ /**
+ * Sets mode of operation of Wi-Fi during provisioning
+ * This is set to :
+ * - WIFI_MODE_APSTA for SoftAP transport
+ * - WIFI_MODE_STA for BLE transport
+ */
+ wifi_mode_t wifi_mode;
+} wifi_prov_scheme_t;
+
+/**
+ * @brief Structure for specifying the manager configuration
+ */
+typedef struct {
+ /**
+ * Provisioning scheme to use. Following schemes are already available:
+ * - wifi_prov_scheme_ble : for provisioning over BLE transport + GATT server
+ * - wifi_prov_scheme_softap : for provisioning over SoftAP transport + HTTP server + mDNS (optional)
+ * - wifi_prov_scheme_console : for provisioning over Serial UART transport + Console (for debugging)
+ */
+ wifi_prov_scheme_t scheme;
+
+ /**
+ * Event handler required by the scheme for incorporating scheme specific
+ * behavior while provisioning manager is running. Various options may be
+ * provided by the scheme for setting this field. Use WIFI_PROV_EVENT_HANDLER_NONE
+ * when not used. When using scheme wifi_prov_scheme_ble, the following
+ * options are available:
+ * - WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM
+ * - WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BLE
+ * - WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BT
+ */
+ wifi_prov_event_handler_t scheme_event_handler;
+
+ /**
+ * Event handler that can be set for the purpose of incorporating application
+ * specific behavior. Use WIFI_PROV_EVENT_HANDLER_NONE when not used.
+ */
+ wifi_prov_event_handler_t app_event_handler;
+} wifi_prov_mgr_config_t;
+
+/**
+ * @brief Security modes supported by the Provisioning Manager.
+ *
+ * These are same as the security modes provided by protocomm
+ */
+typedef enum wifi_prov_security {
+ /**
+ * No security (plain-text communication)
+ */
+ WIFI_PROV_SECURITY_0 = 0,
+
+ /**
+ * This secure communication mode consists of
+ * X25519 key exchange
+ * + proof of possession (pop) based authentication
+ * + AES-CTR encryption
+ */
+ WIFI_PROV_SECURITY_1
+} wifi_prov_security_t;
+
+/**
+ * @brief Initialize provisioning manager instance
+ *
+ * Configures the manager and allocates internal resources
+ *
+ * Configuration specifies the provisioning scheme (transport)
+ * and event handlers
+ *
+ * Event WIFI_PROV_INIT is emitted right after initialization
+ * is complete
+ *
+ * @param[in] config Configuration structure
+ *
+ * @return
+ * - ESP_OK : Success
+ * - ESP_FAIL : Fail
+ */
+esp_err_t wifi_prov_mgr_init(wifi_prov_mgr_config_t config);
+
+/**
+ * @brief Stop provisioning (if running) and release
+ * resource used by the manager
+ *
+ * Event WIFI_PROV_DEINIT is emitted right after de-initialization
+ * is finished
+ *
+ * If provisioning service is still active when this API is called,
+ * it first stops the service, hence emitting WIFI_PROV_END, and
+ * then performs the de-initialization
+ */
+void wifi_prov_mgr_deinit(void);
+
+/**
+ * @brief Checks if device is provisioned
+ *
+ * This checks if Wi-Fi credentials are present on the NVS
+ *
+ * The Wi-Fi credentials are assumed to be kept in the same
+ * NVS namespace as used by esp_wifi component
+ *
+ * If one were to call esp_wifi_set_config() directly instead
+ * of going through the provisioning process, this function will
+ * still yield true (i.e. device will be found to be provisioned)
+ *
+ * @note Calling wifi_prov_mgr_start_provisioning() automatically
+ * resets the provision state, irrespective of what the
+ * state was prior to making the call.
+ *
+ * @param[out] provisioned True if provisioned, else false
+ *
+ * @return
+ * - ESP_OK : Retrieved provision state successfully
+ * - ESP_FAIL : Wi-Fi not initialized
+ * - ESP_ERR_INVALID_ARG : Null argument supplied
+ * - ESP_ERR_INVALID_STATE : Manager not initialized
+ */
+esp_err_t wifi_prov_mgr_is_provisioned(bool *provisioned);
+
+/**
+ * @brief Start provisioning service
+ *
+ * This starts the provisioning service according to the scheme
+ * configured at the time of initialization. For scheme :
+ * - wifi_prov_scheme_ble : This starts protocomm_ble, which internally initializes
+ * BLE transport and starts GATT server for handling
+ * provisioning requests
+ * - wifi_prov_scheme_softap : This activates SoftAP mode of Wi-Fi and starts
+ * protocomm_httpd, which internally starts an HTTP
+ * server for handling provisioning requests (If mDNS is
+ * active it also starts advertising service with type
+ * _esp_wifi_prov._tcp)
+ *
+ * Event WIFI_PROV_START is emitted right after provisioning starts without failure
+ *
+ * @note This API will start provisioning service even if device is found to be
+ * already provisioned, i.e. wifi_prov_mgr_is_provisioned() yields true
+ *
+ * @param[in] security Specify which protocomm security scheme to use :
+ * - WIFI_PROV_SECURITY_0 : For no security
+ * - WIFI_PROV_SECURITY_1 : x25519 secure handshake for session
+ * establishment followed by AES-CTR encryption of provisioning messages
+ * @param[in] pop Pointer to proof of possession string (NULL if not needed). This
+ * is relevant only for protocomm security 1, in which case it is used
+ * for authenticating secure session
+ * @param[in] service_name Unique name of the service. This translates to:
+ * - Wi-Fi SSID when provisioning mode is softAP
+ * - Device name when provisioning mode is BLE
+ * @param[in] service_key Key required by client to access the service (NULL if not needed).
+ * This translates to:
+ * - Wi-Fi password when provisioning mode is softAP
+ * - ignored when provisioning mode is BLE
+ *
+ * @return
+ * - ESP_OK : Provisioning started successfully
+ * - ESP_FAIL : Failed to start provisioning service
+ * - ESP_ERR_INVALID_STATE : Provisioning manager not initialized or already started
+ */
+esp_err_t wifi_prov_mgr_start_provisioning(wifi_prov_security_t security, const char *pop,
+ const char *service_name, const char *service_key);
+
+/**
+ * @brief Stop provisioning service
+ *
+ * If provisioning service is active, this API will initiate a process to stop
+ * the service and return. Once the service actually stops, the event WIFI_PROV_END
+ * will be emitted.
+ *
+ * If wifi_prov_mgr_deinit() is called without calling this API first, it will
+ * automatically stop the provisioning service and emit the WIFI_PROV_END, followed
+ * by WIFI_PROV_DEINIT, before returning.
+ *
+ * This API will generally be used along with wifi_prov_mgr_disable_auto_stop()
+ * in the scenario when the main application has registered its own endpoints,
+ * and wishes that the provisioning service is stopped only when some protocomm
+ * command from the client side application is received.
+ *
+ * Calling this API inside an endpoint handler, with sufficient cleanup_delay,
+ * will allow the response / acknowledgment to be sent successfully before the
+ * underlying protocomm service is stopped.
+ *
+ * Cleaup_delay is set when calling wifi_prov_mgr_disable_auto_stop().
+ * If not specified, it defaults to 1000ms.
+ *
+ * For straightforward cases, using this API is usually not necessary as
+ * provisioning is stopped automatically once WIFI_PROV_CRED_SUCCESS is emitted.
+ * Stopping is delayed (maximum 30 seconds) thus allowing the client side
+ * application to query for Wi-Fi state, i.e. after receiving the first query
+ * and sending `Wi-Fi state connected` response the service is stopped immediately.
+ */
+void wifi_prov_mgr_stop_provisioning(void);
+
+/**
+ * @brief Wait for provisioning service to finish
+ *
+ * Calling this API will block until provisioning service is stopped
+ * i.e. till event WIFI_PROV_END is emitted.
+ *
+ * This will not block if provisioning is not started or not initialized.
+ */
+void wifi_prov_mgr_wait(void);
+
+/**
+ * @brief Disable auto stopping of provisioning service upon completion
+ *
+ * By default, once provisioning is complete, the provisioning service is automatically
+ * stopped, and all endpoints (along with those registered by main application) are
+ * deactivated.
+ *
+ * This API is useful in the case when main application wishes to close provisioning service
+ * only after it receives some protocomm command from the client side app. For example, after
+ * connecting to Wi-Fi, the device may want to connect to the cloud, and only once that is
+ * successfully, the device is said to be fully configured. But, then it is upto the main
+ * application to explicitly call wifi_prov_mgr_stop_provisioning() later when the device is
+ * fully configured and the provisioning service is no longer required.
+ *
+ * @note This must be called before executing wifi_prov_mgr_start_provisioning()
+ *
+ * @param[in] cleanup_delay Sets the delay after which the actual cleanup of transport related
+ * resources is done after a call to wifi_prov_mgr_stop_provisioning()
+ * returns. Minimum allowed value is 100ms. If not specified, this will
+ * default to 1000ms.
+ *
+ * @return
+ * - ESP_OK : Success
+ * - ESP_ERR_INVALID_STATE : Manager not initialized or
+ * provisioning service already started
+ */
+esp_err_t wifi_prov_mgr_disable_auto_stop(uint32_t cleanup_delay);
+
+/**
+ * @brief Set application version and capabilities in the JSON data returned by
+ * proto-ver endpoint
+ *
+ * This function can be called multiple times, to specify information about the various
+ * application specific services running on the device, identified by unique labels.
+ *
+ * The provisioning service itself registers an entry in the JSON data, by the label "prov",
+ * containing only provisioning service version and capabilities. Application services should
+ * use a label other than "prov" so as not to overwrite this.
+ *
+ * @note This must be called before executing wifi_prov_mgr_start_provisioning()
+ *
+ * @param[in] label String indicating the application name.
+ *
+ * @param[in] version String indicating the application version.
+ * There is no constraint on format.
+ *
+ * @param[in] capabilities Array of strings with capabilities.
+ * These could be used by the client side app to know
+ * the application registered endpoint capabilities
+ *
+ * @param[in] total_capabilities Size of capabilities array
+ *
+ * @return
+ * - ESP_OK : Success
+ * - ESP_ERR_INVALID_STATE : Manager not initialized or
+ * provisioning service already started
+ * - ESP_ERR_NO_MEM : Failed to allocate memory for version string
+ * - ESP_ERR_INVALID_ARG : Null argument
+ */
+esp_err_t wifi_prov_mgr_set_app_info(const char *label, const char *version,
+ const char**capabilities, size_t total_capabilities);
+
+/**
+ * @brief Create an additional endpoint and allocate internal resources for it
+ *
+ * This API is to be called by the application if it wants to create an additional
+ * endpoint. All additional endpoints will be assigned UUIDs starting from 0xFF54
+ * and so on in the order of execution.
+ *
+ * protocomm handler for the created endpoint is to be registered later using
+ * wifi_prov_mgr_endpoint_register() after provisioning has started.
+ *
+ * @note This API can only be called BEFORE provisioning is started
+ *
+ * @note Additional endpoints can be used for configuring client provided
+ * parameters other than Wi-Fi credentials, that are necessary for the
+ * main application and hence must be set prior to starting the application
+ *
+ * @note After session establishment, the additional endpoints must be targeted
+ * first by the client side application before sending Wi-Fi configuration,
+ * because once Wi-Fi configuration finishes the provisioning service is
+ * stopped and hence all endpoints are unregistered
+ *
+ * @param[in] ep_name unique name of the endpoint
+ *
+ * @return
+ * - ESP_OK : Success
+ * - ESP_FAIL : Failure
+ */
+esp_err_t wifi_prov_mgr_endpoint_create(const char *ep_name);
+
+/**
+ * @brief Register a handler for the previously created endpoint
+ *
+ * This API can be called by the application to register a protocomm handler
+ * to any endpoint that was created using wifi_prov_mgr_endpoint_create().
+ *
+ * @note This API can only be called AFTER provisioning has started
+ *
+ * @note Additional endpoints can be used for configuring client provided
+ * parameters other than Wi-Fi credentials, that are necessary for the
+ * main application and hence must be set prior to starting the application
+ *
+ * @note After session establishment, the additional endpoints must be targeted
+ * first by the client side application before sending Wi-Fi configuration,
+ * because once Wi-Fi configuration finishes the provisioning service is
+ * stopped and hence all endpoints are unregistered
+ *
+ * @param[in] ep_name Name of the endpoint
+ * @param[in] handler Endpoint handler function
+ * @param[in] user_ctx User data
+ *
+ * @return
+ * - ESP_OK : Success
+ * - ESP_FAIL : Failure
+ */
+esp_err_t wifi_prov_mgr_endpoint_register(const char *ep_name,
+ protocomm_req_handler_t handler,
+ void *user_ctx);
+
+/**
+ * @brief Unregister the handler for an endpoint
+ *
+ * This API can be called if the application wants to selectively
+ * unregister the handler of an endpoint while the provisioning
+ * is still in progress.
+ *
+ * All the endpoint handlers are unregistered automatically when
+ * the provisioning stops.
+ *
+ * @param[in] ep_name Name of the endpoint
+ */
+void wifi_prov_mgr_endpoint_unregister(const char *ep_name);
+
+/**
+ * @brief Event handler for provisioning manager
+ *
+ * This is called from the main event handler and controls the
+ * provisioning manager's internal state machine depending on
+ * incoming Wi-Fi events
+ *
+ * @param[in] ctx Event context data
+ * @param[in] event Event info
+ *
+ * @return
+ * - ESP_OK : Event handled successfully
+ * - ESP_ERR_FAIL : This event cannot be handled
+ */
+esp_err_t wifi_prov_mgr_event_handler(void *ctx, system_event_t *event);
+
+/**
+ * @brief Get state of Wi-Fi Station during provisioning
+ *
+ * @param[out] state Pointer to wifi_prov_sta_state_t
+ * variable to be filled
+ *
+ * @return
+ * - ESP_OK : Successfully retrieved Wi-Fi state
+ * - ESP_FAIL : Provisioning app not running
+ */
+esp_err_t wifi_prov_mgr_get_wifi_state(wifi_prov_sta_state_t *state);
+
+/**
+ * @brief Get reason code in case of Wi-Fi station
+ * disconnection during provisioning
+ *
+* @param[out] reason Pointer to wifi_prov_sta_fail_reason_t
+* variable to be filled
+ *
+ * @return
+ * - ESP_OK : Successfully retrieved Wi-Fi disconnect reason
+ * - ESP_FAIL : Provisioning app not running
+ */
+esp_err_t wifi_prov_mgr_get_wifi_disconnect_reason(wifi_prov_sta_fail_reason_t *reason);
+
+/**
+ * @brief Runs Wi-Fi as Station with the supplied configuration
+ *
+ * Configures the Wi-Fi station mode to connect to the AP with
+ * SSID and password specified in config structure and sets
+ * Wi-Fi to run as station.
+ *
+ * This is automatically called by provisioning service upon
+ * receiving new credentials.
+ *
+ * If credentials are to be supplied to the manager via a
+ * different mode other than through protocomm, then this
+ * API needs to be called.
+ *
+ * Event WIFI_PROV_CRED_RECV is emitted after credentials have
+ * been applied and Wi-Fi station started
+ *
+ * @param[in] wifi_cfg Pointer to Wi-Fi configuration structure
+ *
+ * @return
+ * - ESP_OK : Wi-Fi configured and started successfully
+ * - ESP_FAIL : Failed to set configuration
+ */
+esp_err_t wifi_prov_mgr_configure_sta(wifi_config_t *wifi_cfg);
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null
+// Copyright 2019 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.
+
+#pragma once
+
+#include <protocomm.h>
+#include <protocomm_ble.h>
+
+#include "wifi_provisioning/manager.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Scheme that can be used by manager for provisioning
+ * over BLE transport with GATT server
+ */
+extern const wifi_prov_scheme_t wifi_prov_scheme_ble;
+
+/* This scheme specific event handler is to be used when application
+ * doesn't require BT and BLE after provisioning has finished */
+#define WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM { \
+ .event_cb = wifi_prov_scheme_ble_event_cb_free_btdm, \
+ .user_data = NULL \
+}
+
+/* This scheme specific event handler is to be used when application
+ * doesn't require BLE to be active after provisioning has finished */
+#define WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BLE { \
+ .event_cb = wifi_prov_scheme_ble_event_cb_free_ble, \
+ .user_data = NULL \
+}
+
+/* This scheme specific event handler is to be used when application
+ * doesn't require BT to be active after provisioning has finished */
+#define WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BT { \
+ .event_cb = wifi_prov_scheme_ble_event_cb_free_bt, \
+ .user_data = NULL \
+}
+
+void wifi_prov_scheme_ble_event_cb_free_btdm(void *user_data, wifi_prov_cb_event_t event, void *event_data);
+void wifi_prov_scheme_ble_event_cb_free_ble (void *user_data, wifi_prov_cb_event_t event, void *event_data);
+void wifi_prov_scheme_ble_event_cb_free_bt (void *user_data, wifi_prov_cb_event_t event, void *event_data);
+
+/**
+ * @brief Set the 128 bit GATT service UUID used for provisioning
+ *
+ * This API is used to override the default 128 bit provisioning
+ * service UUID, which is 0000ffff-0000-1000-8000-00805f9b34fb.
+ *
+ * This must be called before starting provisioning, i.e. before
+ * making a call to wifi_prov_mgr_start_provisioning(), otherwise
+ * the default UUID will be used.
+ *
+ * @note The data being pointed to by the argument must be valid
+ * atleast till provisioning is started. Upon start, the
+ * manager will store an internal copy of this UUID, and
+ * this data can be freed or invalidated afterwords.
+ *
+ * @param[in] uuid128 A custom 128 bit UUID
+ *
+ * @return
+ * - ESP_OK : Success
+ * - ESP_ERR_INVALID_ARG : Null argument
+ */
+esp_err_t wifi_prov_scheme_ble_set_service_uuid(uint8_t *uuid128);
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null
+// Copyright 2019 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.
+
+#pragma once
+
+#include <protocomm.h>
+#include <protocomm_console.h>
+
+#include "wifi_provisioning/manager.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Scheme that can be used by manager for provisioning
+ * over console (Serial UART)
+ */
+extern const wifi_prov_scheme_t wifi_prov_scheme_console;
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null
+// Copyright 2019 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.
+
+#pragma once
+
+#include <protocomm.h>
+#include <protocomm_httpd.h>
+
+#include "wifi_provisioning/manager.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Scheme that can be used by manager for provisioning
+ * over SoftAP transport with HTTP server
+ */
+extern const wifi_prov_scheme_t wifi_prov_scheme_softap;
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null
+// Copyright 2019 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.
+
+#include <stdio.h>
+#include <string.h>
+#include <esp_err.h>
+#include <esp_log.h>
+
+#include <esp_wifi.h>
+#include <tcpip_adapter.h>
+
+#include "wifi_provisioning/wifi_config.h"
+#include "wifi_provisioning/manager.h"
+#include "wifi_provisioning_priv.h"
+
+static const char *TAG = "wifi_prov_handlers";
+
+/* Provide definition of wifi_prov_ctx_t */
+struct wifi_prov_ctx {
+ wifi_config_t wifi_cfg;
+};
+
+static wifi_config_t *get_config(wifi_prov_ctx_t **ctx)
+{
+ return (*ctx ? & (*ctx)->wifi_cfg : NULL);
+}
+
+static wifi_config_t *new_config(wifi_prov_ctx_t **ctx)
+{
+ free(*ctx);
+ (*ctx) = (wifi_prov_ctx_t *) calloc(1, sizeof(wifi_prov_ctx_t));
+ return get_config(ctx);
+}
+
+static void free_config(wifi_prov_ctx_t **ctx)
+{
+ free(*ctx);
+ *ctx = NULL;
+}
+
+static esp_err_t get_status_handler(wifi_prov_config_get_data_t *resp_data, wifi_prov_ctx_t **ctx)
+{
+ /* Initialize to zero */
+ memset(resp_data, 0, sizeof(wifi_prov_config_get_data_t));
+
+ if (wifi_prov_mgr_get_wifi_state(&resp_data->wifi_state) != ESP_OK) {
+ ESP_LOGW(TAG, "Wi-Fi provisioning manager not running");
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ if (resp_data->wifi_state == WIFI_PROV_STA_CONNECTED) {
+ ESP_LOGD(TAG, "Got state : connected");
+
+ /* IP Addr assigned to STA */
+ tcpip_adapter_ip_info_t ip_info;
+ tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info);
+ char *ip_addr = ip4addr_ntoa(&ip_info.ip);
+ strcpy(resp_data->conn_info.ip_addr, ip_addr);
+
+ /* AP information to which STA is connected */
+ wifi_ap_record_t ap_info;
+ esp_wifi_sta_get_ap_info(&ap_info);
+ memcpy(resp_data->conn_info.bssid, (char *)ap_info.bssid, sizeof(ap_info.bssid));
+ memcpy(resp_data->conn_info.ssid, (char *)ap_info.ssid, sizeof(ap_info.ssid));
+ resp_data->conn_info.channel = ap_info.primary;
+ resp_data->conn_info.auth_mode = ap_info.authmode;
+
+ /* Tell manager to stop provisioning service */
+ wifi_prov_mgr_done();
+ } else if (resp_data->wifi_state == WIFI_PROV_STA_DISCONNECTED) {
+ ESP_LOGD(TAG, "Got state : disconnected");
+
+ /* If disconnected, convey reason */
+ wifi_prov_mgr_get_wifi_disconnect_reason(&resp_data->fail_reason);
+ } else {
+ ESP_LOGD(TAG, "Got state : connecting");
+ }
+ return ESP_OK;
+}
+
+static esp_err_t set_config_handler(const wifi_prov_config_set_data_t *req_data, wifi_prov_ctx_t **ctx)
+{
+ wifi_config_t *wifi_cfg = get_config(ctx);
+ if (wifi_cfg) {
+ free_config(ctx);
+ }
+
+ wifi_cfg = new_config(ctx);
+ if (!wifi_cfg) {
+ ESP_LOGE(TAG, "Unable to allocate Wi-Fi config");
+ return ESP_ERR_NO_MEM;
+ }
+
+ ESP_LOGD(TAG, "Wi-Fi Credentials Received");
+
+ /* 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) */
+ strncpy((char *) wifi_cfg->sta.ssid, req_data->ssid, sizeof(wifi_cfg->sta.ssid));
+
+ /* Using strlcpy allows both max passphrase length (63 bytes) and ensures null termination
+ * because size of wifi_cfg->sta.password is 64 bytes (1 extra byte for null character) */
+ strlcpy((char *) wifi_cfg->sta.password, req_data->password, sizeof(wifi_cfg->sta.password));
+ return ESP_OK;
+}
+
+static esp_err_t apply_config_handler(wifi_prov_ctx_t **ctx)
+{
+ wifi_config_t *wifi_cfg = get_config(ctx);
+ if (!wifi_cfg) {
+ ESP_LOGE(TAG, "Wi-Fi config not set");
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ esp_err_t ret = wifi_prov_mgr_configure_sta(wifi_cfg);
+ if (ret == ESP_OK) {
+ ESP_LOGD(TAG, "Wi-Fi Credentials Applied");
+ } else {
+ ESP_LOGE(TAG, "Failed to apply Wi-Fi Credentials");
+ }
+
+ free_config(ctx);
+ return ret;
+}
+
+wifi_prov_config_handlers_t get_wifi_prov_handlers(void)
+{
+ wifi_prov_config_handlers_t wifi_prov_handlers = {
+ .get_status_handler = get_status_handler,
+ .set_config_handler = set_config_handler,
+ .apply_config_handler = apply_config_handler,
+ .ctx = NULL
+ };
+ return wifi_prov_handlers;
+}
--- /dev/null
+// Copyright 2019 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.
+
+#include <string.h>
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
+#include <freertos/task.h>
+
+#include <cJSON.h>
+
+#include <esp_log.h>
+#include <esp_err.h>
+#include <esp_wifi.h>
+#include <esp_timer.h>
+
+#include <protocomm.h>
+#include <protocomm_security0.h>
+#include <protocomm_security1.h>
+
+#include "wifi_provisioning_priv.h"
+
+#define WIFI_PROV_MGR_VERSION "v1.0"
+
+static const char *TAG = "wifi_prov_mgr";
+
+typedef enum {
+ WIFI_PROV_STATE_IDLE,
+ WIFI_PROV_STATE_STARTING,
+ WIFI_PROV_STATE_STARTED,
+ WIFI_PROV_STATE_CRED_RECV,
+ WIFI_PROV_STATE_FAIL,
+ WIFI_PROV_STATE_SUCCESS,
+ WIFI_PROV_STATE_STOPPING
+} wifi_prov_mgr_state_t;
+
+/**
+ * @brief Structure for storing capabilities supported by
+ * the provisioning service
+ */
+struct wifi_prov_capabilities {
+ /* Proof of Possession is not required for establishing session */
+ bool no_pop;
+
+ /* Provisioning doesn't stop on it's own after receiving Wi-Fi credentials
+ * instead application has to explicitly call wifi_prov_mgr_stop_provisioning() */
+ bool no_auto_stop;
+};
+
+/**
+ * @brief Structure for storing miscellaneous information about
+ * provisioning service that will be conveyed to clients
+ */
+struct wifi_prov_info {
+ const char *version;
+ struct wifi_prov_capabilities capabilities;
+};
+
+/**
+ * @brief Context data for provisioning manager
+ */
+struct wifi_prov_mgr_ctx {
+ /* Provisioning manager configuration */
+ wifi_prov_mgr_config_t mgr_config;
+
+ /* State of the provisioning service */
+ wifi_prov_mgr_state_t prov_state;
+
+ /* Provisioning scheme configuration */
+ void *prov_scheme_config;
+
+ /* Protocomm handle */
+ protocomm_t *pc;
+
+ /* Type of security to use with protocomm */
+ int security;
+
+ /* Pointer to proof of possession */
+ protocomm_security_pop_t pop;
+
+ /* Handle to timer */
+ esp_timer_handle_t timer;
+
+ /* State of Wi-Fi Station */
+ wifi_prov_sta_state_t wifi_state;
+
+ /* Code for Wi-Fi station disconnection (if disconnected) */
+ wifi_prov_sta_fail_reason_t wifi_disconnect_reason;
+
+ /* Protocomm handlers for Wi-Fi configuration endpoint */
+ wifi_prov_config_handlers_t *wifi_prov_handlers;
+
+ /* Count of used endpoint UUIDs */
+ unsigned int endpoint_uuid_used;
+
+ /* Provisioning service information */
+ struct wifi_prov_info mgr_info;
+
+ /* Application related information in JSON format */
+ cJSON *app_info_json;
+
+ /* Delay after which resources will be cleaned up asynchronously
+ * upon execution of wifi_prov_mgr_stop_provisioning() */
+ uint32_t cleanup_delay;
+};
+
+/* Mutex to lock/unlock access to provisioning singleton
+ * context data. This is allocated only once on first init
+ * and never deleted as wifi_prov_mgr is a singleton */
+static SemaphoreHandle_t prov_ctx_lock = NULL;
+
+/* Pointer to provisioning context data */
+static struct wifi_prov_mgr_ctx *prov_ctx;
+
+/* This executes registered app_event_callback for a particular event
+ *
+ * NOTE : By the time this fucntion returns, it is possible that
+ * the manager got de-initialized due to a call to wifi_prov_mgr_deinit()
+ * either inside the event callbacks or from another thread. Therefore
+ * post execution of execute_event_cb(), the validity of prov_ctx must
+ * always be checked. A cleaner way, to avoid this pitfall safely, would
+ * be to limit the usage of this function to only public APIs, and that
+ * too at the very end, just before returning.
+ *
+ * NOTE: This function should be called only after ensuring that the
+ * context is valid and the control mutex is locked. */
+static void execute_event_cb(wifi_prov_cb_event_t event_id, void *event_data)
+{
+ ESP_LOGD(TAG, "execute_event_cb : %d", event_id);
+
+ if (prov_ctx) {
+ wifi_prov_cb_func_t app_cb = prov_ctx->mgr_config.app_event_handler.event_cb;
+ void *app_data = prov_ctx->mgr_config.app_event_handler.user_data;
+
+ wifi_prov_cb_func_t scheme_cb = prov_ctx->mgr_config.scheme_event_handler.event_cb;
+ void *scheme_data = prov_ctx->mgr_config.scheme_event_handler.user_data;
+
+ /* Release the mutex before executing the callbacks. This is done so that
+ * wifi_prov_mgr_event_handler() doesn't stay blocked for the duration */
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+
+ if (scheme_cb) {
+ /* Call scheme specific event handler */
+ scheme_cb(scheme_data, event_id, event_data);
+ }
+
+ if (app_cb) {
+ /* Call application specific event handler */
+ app_cb(app_data, event_id, event_data);
+ }
+
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+ }
+}
+
+esp_err_t wifi_prov_mgr_set_app_info(const char *label, const char *version,
+ const char**capabilities, size_t total_capabilities)
+{
+ if (!label || !version || !capabilities) {
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ if (!prov_ctx_lock) {
+ ESP_LOGE(TAG, "Provisioning manager not initialized");
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ esp_err_t ret = ESP_FAIL;
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+
+ if (prov_ctx && prov_ctx->prov_state == WIFI_PROV_STATE_IDLE) {
+ if (!prov_ctx->app_info_json) {
+ prov_ctx->app_info_json = cJSON_CreateObject();
+ }
+
+ cJSON *new_entry_json = cJSON_CreateObject();
+ cJSON *capabilities_json = cJSON_CreateArray();
+ cJSON_AddItemToObject(prov_ctx->app_info_json, label, new_entry_json);
+
+ /* Version ("ver") */
+ cJSON_AddStringToObject(new_entry_json, "ver", version);
+
+ /* List of capabilities ("cap") */
+ cJSON_AddItemToObject(new_entry_json, "cap", capabilities_json);
+ for (unsigned int i = 0; i < total_capabilities; i++) {
+ if (capabilities[i]) {
+ cJSON_AddItemToArray(capabilities_json, cJSON_CreateString(capabilities[i]));
+ }
+ }
+
+ } else {
+ ret = ESP_ERR_INVALID_STATE;
+ }
+
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ret;
+}
+
+static cJSON* wifi_prov_get_info_json(void)
+{
+ cJSON *full_info_json = prov_ctx->app_info_json ?
+ cJSON_Duplicate(prov_ctx->app_info_json, 1) : cJSON_CreateObject();
+ cJSON *prov_info_json = cJSON_CreateObject();
+ cJSON *prov_capabilities = cJSON_CreateArray();
+
+ /* Use label "prov" to indicate provisioning related information */
+ cJSON_AddItemToObject(full_info_json, "prov", prov_info_json);
+
+ /* Version field */
+ cJSON_AddStringToObject(prov_info_json, "ver", prov_ctx->mgr_info.version);
+
+ /* Capabilities field */
+ cJSON_AddItemToObject(prov_info_json, "cap", prov_capabilities);
+
+ /* If Proof of Possession is not used, indicate in capabilities */
+ if (prov_ctx->mgr_info.capabilities.no_pop) {
+ cJSON_AddItemToArray(prov_capabilities, cJSON_CreateString("no_pop"));
+ }
+ return full_info_json;
+}
+
+static esp_err_t wifi_prov_mgr_start_service(const char *service_name, const char *service_key)
+{
+ const wifi_prov_scheme_t *scheme = &prov_ctx->mgr_config.scheme;
+ esp_err_t ret;
+
+ /* Create new protocomm instance */
+ prov_ctx->pc = protocomm_new();
+ if (prov_ctx->pc == NULL) {
+ ESP_LOGE(TAG, "Failed to create new protocomm instance");
+ return ESP_FAIL;
+ }
+
+ ret = scheme->set_config_service(prov_ctx->prov_scheme_config, service_name, service_key);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to configure service");
+ protocomm_delete(prov_ctx->pc);
+ return ret;
+ }
+
+ /* Start provisioning */
+ ret = scheme->prov_start(prov_ctx->pc, prov_ctx->prov_scheme_config);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to start service");
+ protocomm_delete(prov_ctx->pc);
+ return ret;
+ }
+
+ /* Set version information / capabilities of provisioning service and application */
+ cJSON *version_json = wifi_prov_get_info_json();
+ ret = protocomm_set_version(prov_ctx->pc, "proto-ver", cJSON_Print(version_json));
+ cJSON_Delete(version_json);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to set version endpoint");
+ scheme->prov_stop(prov_ctx->pc);
+ protocomm_delete(prov_ctx->pc);
+ return ret;
+ }
+
+ /* Set protocomm security type for endpoint */
+ if (prov_ctx->security == 0) {
+ ret = protocomm_set_security(prov_ctx->pc, "prov-session",
+ &protocomm_security0, NULL);
+ } else if (prov_ctx->security == 1) {
+ ret = protocomm_set_security(prov_ctx->pc, "prov-session",
+ &protocomm_security1, &prov_ctx->pop);
+ } else {
+ ESP_LOGE(TAG, "Unsupported protocomm security version %d", prov_ctx->security);
+ ret = ESP_ERR_INVALID_ARG;
+ }
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to set security endpoint");
+ scheme->prov_stop(prov_ctx->pc);
+ protocomm_delete(prov_ctx->pc);
+ return ret;
+ }
+
+ prov_ctx->wifi_prov_handlers = malloc(sizeof(wifi_prov_config_handlers_t));
+ if (!prov_ctx->wifi_prov_handlers) {
+ ESP_LOGD(TAG, "Failed to allocate memory for provisioning handlers");
+ scheme->prov_stop(prov_ctx->pc);
+ protocomm_delete(prov_ctx->pc);
+ }
+ *prov_ctx->wifi_prov_handlers = get_wifi_prov_handlers();
+
+ /* Add protocomm endpoint for Wi-Fi station configuration */
+ ret = protocomm_add_endpoint(prov_ctx->pc, "prov-config",
+ wifi_prov_config_data_handler,
+ prov_ctx->wifi_prov_handlers);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to set provisioning endpoint");
+ free(prov_ctx->wifi_prov_handlers);
+ scheme->prov_stop(prov_ctx->pc);
+ protocomm_delete(prov_ctx->pc);
+ return ret;
+ }
+
+ ESP_LOGI(TAG, "Provisioning started with service name : %s ",
+ service_name ? service_name : "<NULL>");
+ return ESP_OK;
+}
+
+esp_err_t wifi_prov_mgr_endpoint_create(const char *ep_name)
+{
+ if (!prov_ctx_lock) {
+ ESP_LOGE(TAG, "Provisioning manager not initialized");
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ esp_err_t err = ESP_FAIL;
+
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+ if (prov_ctx &&
+ prov_ctx->prov_state == WIFI_PROV_STATE_IDLE) {
+ err = prov_ctx->mgr_config.scheme.set_config_endpoint(
+ prov_ctx->prov_scheme_config, ep_name,
+ prov_ctx->endpoint_uuid_used + 1);
+ }
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to create additional endpoint");
+ } else {
+ prov_ctx->endpoint_uuid_used++;
+ }
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return err;
+}
+
+esp_err_t wifi_prov_mgr_endpoint_register(const char *ep_name, protocomm_req_handler_t handler, void *user_ctx)
+{
+ if (!prov_ctx_lock) {
+ ESP_LOGE(TAG, "Provisioning manager not initialized");
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ esp_err_t err = ESP_FAIL;
+
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+ if (prov_ctx &&
+ prov_ctx->prov_state > WIFI_PROV_STATE_STARTING &&
+ prov_ctx->prov_state < WIFI_PROV_STATE_STOPPING) {
+ err = protocomm_add_endpoint(prov_ctx->pc, ep_name, handler, user_ctx);
+ }
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to register handler for endpoint");
+ }
+ return err;
+}
+
+void wifi_prov_mgr_endpoint_unregister(const char *ep_name)
+{
+ if (!prov_ctx_lock) {
+ ESP_LOGE(TAG, "Provisioning manager not initialized");
+ return;
+ }
+
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+ if (prov_ctx &&
+ prov_ctx->prov_state > WIFI_PROV_STATE_STARTING &&
+ prov_ctx->prov_state < WIFI_PROV_STATE_STOPPING) {
+ protocomm_remove_endpoint(prov_ctx->pc, ep_name);
+ }
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+}
+
+static void prov_stop_task(void *arg)
+{
+ bool is_this_a_task = (bool) arg;
+
+ wifi_prov_cb_func_t app_cb = prov_ctx->mgr_config.app_event_handler.event_cb;
+ void *app_data = prov_ctx->mgr_config.app_event_handler.user_data;
+
+ wifi_prov_cb_func_t scheme_cb = prov_ctx->mgr_config.scheme_event_handler.event_cb;
+ void *scheme_data = prov_ctx->mgr_config.scheme_event_handler.user_data;
+
+ /* This delay is so that the client side app is notified first
+ * and then the provisioning is stopped. Generally 1000ms is enough. */
+ uint32_t cleanup_delay = prov_ctx->cleanup_delay > 100 ? prov_ctx->cleanup_delay : 100;
+ vTaskDelay(cleanup_delay / portTICK_PERIOD_MS);
+
+ /* All the extra application added endpoints are also
+ * removed automatically when prov_stop is called */
+ prov_ctx->mgr_config.scheme.prov_stop(prov_ctx->pc);
+
+ /* Delete protocomm instance */
+ protocomm_delete(prov_ctx->pc);
+ prov_ctx->pc = NULL;
+
+ /* Free provisioning handlers */
+ free(prov_ctx->wifi_prov_handlers->ctx);
+ free(prov_ctx->wifi_prov_handlers);
+ prov_ctx->wifi_prov_handlers = NULL;
+
+ /* Switch device to Wi-Fi STA mode irrespective of
+ * whether provisioning was completed or not */
+ esp_wifi_set_mode(WIFI_MODE_STA);
+ ESP_LOGI(TAG, "Provisioning stopped");
+
+ if (is_this_a_task) {
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+ prov_ctx->prov_state = WIFI_PROV_STATE_IDLE;
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+
+ ESP_LOGD(TAG, "execute_event_cb : %d", WIFI_PROV_END);
+ if (scheme_cb) {
+ scheme_cb(scheme_data, WIFI_PROV_END, NULL);
+ }
+ if (app_cb) {
+ app_cb(app_data, WIFI_PROV_END, NULL);
+ }
+ vTaskDelete(NULL);
+ }
+}
+
+/* This will do one of these:
+ * 1) if blocking is false, start a task for stopping the provisioning service (returns true)
+ * 2) if blocking is true, stop provisioning service immediately (returns true)
+ * 3) if service was already in the process of termination, in blocking mode this will
+ * wait till the service is stopped (returns false)
+ * 4) if service was not running, this will return immediately (returns false)
+ *
+ * NOTE: This function should be called only after ensuring that the context
+ * is valid and the control mutex is locked
+ *
+ * NOTE: When blocking mode is selected, the event callbacks are not executed.
+ * This help with de-initialization.
+ */
+static bool wifi_prov_mgr_stop_service(bool blocking)
+{
+ if (blocking) {
+ /* Wait for any ongoing calls to wifi_prov_mgr_start_service() or
+ * wifi_prov_mgr_stop_service() from another thread to finish */
+ while (prov_ctx && (
+ prov_ctx->prov_state == WIFI_PROV_STATE_STARTING ||
+ prov_ctx->prov_state == WIFI_PROV_STATE_STOPPING)) {
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ vTaskDelay(100 / portTICK_PERIOD_MS);
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+ }
+ } else {
+ /* Wait for any ongoing call to wifi_prov_mgr_start_service()
+ * from another thread to finish */
+ while (prov_ctx &&
+ prov_ctx->prov_state == WIFI_PROV_STATE_STARTING) {
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ vTaskDelay(100 / portTICK_PERIOD_MS);
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+ }
+
+ if (prov_ctx && prov_ctx->prov_state == WIFI_PROV_STATE_STOPPING) {
+ ESP_LOGD(TAG, "Provisioning is already stopping");
+ return false;
+ }
+ }
+
+ if (!prov_ctx || prov_ctx->prov_state == WIFI_PROV_STATE_IDLE) {
+ ESP_LOGD(TAG, "Provisioning not running");
+ return false;
+ }
+
+ /* Timer not needed anymore */
+ if (prov_ctx->timer) {
+ esp_timer_stop(prov_ctx->timer);
+ esp_timer_delete(prov_ctx->timer);
+ prov_ctx->timer = NULL;
+ }
+
+ ESP_LOGD(TAG, "Stopping provisioning");
+ prov_ctx->prov_state = WIFI_PROV_STATE_STOPPING;
+
+ /* Free proof of possession */
+ if (prov_ctx->pop.data) {
+ free((void *)prov_ctx->pop.data);
+ prov_ctx->pop.data = NULL;
+ }
+
+ if (blocking) {
+ /* Run the cleanup without launching a separate task. Also the
+ * WIFI_PROV_END event is not emitted in this case */
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ prov_stop_task((void *)0);
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+ prov_ctx->prov_state = WIFI_PROV_STATE_IDLE;
+ } else {
+ /* Launch cleanup task to perform the cleanup asynchronously.
+ * It is important to do this asynchronously because, there are
+ * situations in which the transport level resources have to be
+ * released - some duration after - returning from a call to
+ * wifi_prov_mgr_stop_provisioning(), like when it is called
+ * inside a protocomm handler */
+ assert(xTaskCreate(prov_stop_task, "prov_stop_task", 4096, (void *)1,
+ tskIDLE_PRIORITY, NULL) == pdPASS);
+ ESP_LOGD(TAG, "Provisioning scheduled for stopping");
+ }
+ return true;
+}
+
+/* Task spawned by timer callback */
+static void stop_prov_timer_cb(void *arg)
+{
+ wifi_prov_mgr_stop_provisioning();
+}
+
+esp_err_t wifi_prov_mgr_disable_auto_stop(uint32_t cleanup_delay)
+{
+ if (!prov_ctx_lock) {
+ ESP_LOGE(TAG, "Provisioning manager not initialized");
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ esp_err_t ret = ESP_FAIL;
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+
+ if (prov_ctx && prov_ctx->prov_state == WIFI_PROV_STATE_IDLE) {
+ prov_ctx->mgr_info.capabilities.no_auto_stop = true;
+ prov_ctx->cleanup_delay = cleanup_delay;
+ } else {
+ ret = ESP_ERR_INVALID_STATE;
+ }
+
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ret;
+}
+
+/* Call this if provisioning is completed before the timeout occurs */
+esp_err_t wifi_prov_mgr_done(void)
+{
+ if (!prov_ctx_lock) {
+ ESP_LOGE(TAG, "Provisioning manager not initialized");
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+ /* Stop provisioning if auto stop is not disabled */
+ if (prov_ctx && !prov_ctx->mgr_info.capabilities.no_auto_stop) {
+ wifi_prov_mgr_stop_provisioning();
+ }
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_OK;
+}
+
+/* Event handler for starting/stopping provisioning.
+ * To be called from within the context of the main
+ * event handler */
+esp_err_t wifi_prov_mgr_event_handler(void *ctx, system_event_t *event)
+{
+ /* For accessing reason codes in case of disconnection */
+ system_event_info_t *info = &event->event_info;
+
+ if (!prov_ctx_lock) {
+ ESP_LOGE(TAG, "Provisioning manager not initialized");
+ return ESP_ERR_INVALID_STATE;
+ }
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+
+ /* If pointer to provisioning application data is NULL
+ * then provisioning manager is not running, therefore
+ * return with error to allow the global handler to act */
+ if (!prov_ctx) {
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ /* Only handle events when credential is received and
+ * Wi-Fi STA is yet to complete trying the connection */
+ if (prov_ctx->prov_state != WIFI_PROV_STATE_CRED_RECV) {
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_OK;
+ }
+
+ esp_err_t ret = ESP_OK;
+ switch (event->event_id) {
+ case SYSTEM_EVENT_STA_START:
+ ESP_LOGD(TAG, "STA Start");
+ /* 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 */
+ prov_ctx->wifi_state = WIFI_PROV_STA_CONNECTING;
+ break;
+
+ case SYSTEM_EVENT_STA_GOT_IP:
+ ESP_LOGD(TAG, "STA Got IP");
+ /* Station got IP. That means configuration is successful. */
+ prov_ctx->wifi_state = WIFI_PROV_STA_CONNECTED;
+ prov_ctx->prov_state = WIFI_PROV_STATE_SUCCESS;
+
+ /* If auto stop is enabled (default), schedule timer to
+ * stop provisioning app after 30 seconds. */
+ if (!prov_ctx->mgr_info.capabilities.no_auto_stop) {
+ ESP_LOGD(TAG, "Starting 30s timer for stop_prov_timer_cb()");
+ esp_timer_start_once(prov_ctx->timer, 30000 * 1000U);
+ }
+
+ /* Execute user registered callback handler */
+ execute_event_cb(WIFI_PROV_CRED_SUCCESS, NULL);
+ break;
+
+ case SYSTEM_EVENT_STA_DISCONNECTED:
+ ESP_LOGD(TAG, "STA Disconnected");
+ /* Station couldn't connect to configured host SSID */
+ prov_ctx->wifi_state = WIFI_PROV_STA_DISCONNECTED;
+ ESP_LOGD(TAG, "Disconnect reason : %d", info->disconnected.reason);
+
+ /* Set code corresponding to the reason for disconnection */
+ switch (info->disconnected.reason) {
+ case WIFI_REASON_AUTH_EXPIRE:
+ case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT:
+ case WIFI_REASON_BEACON_TIMEOUT:
+ case WIFI_REASON_AUTH_FAIL:
+ case WIFI_REASON_ASSOC_FAIL:
+ case WIFI_REASON_HANDSHAKE_TIMEOUT:
+ ESP_LOGD(TAG, "STA Auth Error");
+ prov_ctx->wifi_disconnect_reason = WIFI_PROV_STA_AUTH_ERROR;
+ break;
+ case WIFI_REASON_NO_AP_FOUND:
+ ESP_LOGD(TAG, "STA AP Not found");
+ prov_ctx->wifi_disconnect_reason = WIFI_PROV_STA_AP_NOT_FOUND;
+ break;
+ default:
+ /* If none of the expected reasons,
+ * retry connecting to host SSID */
+ prov_ctx->wifi_state = WIFI_PROV_STA_CONNECTING;
+ esp_wifi_connect();
+ }
+
+ /* In case of disconnection, update state of service and
+ * run the event handler with disconnection reason as data */
+ if (prov_ctx->wifi_state == WIFI_PROV_STA_DISCONNECTED) {
+ prov_ctx->prov_state = WIFI_PROV_STATE_FAIL;
+ wifi_prov_sta_fail_reason_t reason = prov_ctx->wifi_disconnect_reason;
+ /* Execute user registered callback handler */
+ execute_event_cb(WIFI_PROV_CRED_FAIL, (void *)&reason);
+ }
+ break;
+
+ default:
+ /* This event is not intended to be handled by this handler.
+ * Return ESP_FAIL to signal global event handler to take
+ * control */
+ ret = ESP_FAIL;
+ break;
+ }
+
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ret;
+}
+
+esp_err_t wifi_prov_mgr_get_wifi_state(wifi_prov_sta_state_t *state)
+{
+ if (!prov_ctx_lock) {
+ ESP_LOGE(TAG, "Provisioning manager not initialized");
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+ if (prov_ctx == NULL || state == NULL) {
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_FAIL;
+ }
+
+ *state = prov_ctx->wifi_state;
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_OK;
+}
+
+esp_err_t wifi_prov_mgr_get_wifi_disconnect_reason(wifi_prov_sta_fail_reason_t *reason)
+{
+ if (!prov_ctx_lock) {
+ ESP_LOGE(TAG, "Provisioning manager not initialized");
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+ if (prov_ctx == NULL || reason == NULL) {
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_FAIL;
+ }
+
+ if (prov_ctx->wifi_state != WIFI_PROV_STA_DISCONNECTED) {
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_FAIL;
+ }
+
+ *reason = prov_ctx->wifi_disconnect_reason;
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_OK;
+}
+
+static void debug_print_wifi_credentials(wifi_sta_config_t sta, const char* pretext)
+{
+ size_t passlen = strlen((const char*) sta.password);
+ ESP_LOGD(TAG, "%s Wi-Fi SSID : %.*s", pretext,
+ strnlen((const char *) sta.ssid, sizeof(sta.ssid)), (const char *) sta.ssid);
+
+ if (passlen) {
+ /* Mask password partially if longer than 3, else mask it completely */
+ memset(sta.password + (passlen > 3), '*', passlen - 2*(passlen > 3));
+ ESP_LOGD(TAG, "%s Wi-Fi Password : %s", pretext, (const char *) sta.password);
+ }
+}
+
+esp_err_t wifi_prov_mgr_is_provisioned(bool *provisioned)
+{
+ if (!provisioned) {
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ *provisioned = false;
+
+ if (!prov_ctx_lock) {
+ ESP_LOGE(TAG, "Provisioning manager not initialized");
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ /* Get Wi-Fi Station configuration */
+ wifi_config_t wifi_cfg;
+ if (esp_wifi_get_config(ESP_IF_WIFI_STA, &wifi_cfg) != ESP_OK) {
+ return ESP_FAIL;
+ }
+
+ if (strlen((const char *) wifi_cfg.sta.ssid)) {
+ *provisioned = true;
+ debug_print_wifi_credentials(wifi_cfg.sta, "Found");
+ }
+ return ESP_OK;
+}
+
+esp_err_t wifi_prov_mgr_configure_sta(wifi_config_t *wifi_cfg)
+{
+ if (!prov_ctx_lock) {
+ ESP_LOGE(TAG, "Provisioning manager not initialized");
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+ if (!prov_ctx) {
+ ESP_LOGE(TAG, "Invalid state of Provisioning app");
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_FAIL;
+ }
+ if (prov_ctx->prov_state >= WIFI_PROV_STATE_CRED_RECV) {
+ ESP_LOGE(TAG, "Wi-Fi credentials already received by provisioning app");
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_FAIL;
+ }
+ debug_print_wifi_credentials(wifi_cfg->sta, "Received");
+
+ /* Configure Wi-Fi as both AP and/or Station */
+ if (esp_wifi_set_mode(prov_ctx->mgr_config.scheme.wifi_mode) != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to set Wi-Fi mode");
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_FAIL;
+ }
+
+ /* Don't release mutex yet as it is possible that right after
+ * esp_wifi_connect() is called below, the related Wi-Fi event
+ * happens even before manager state is updated in the next
+ * few lines causing the internal event handler to miss */
+
+ /* Set Wi-Fi storage again to flash to keep the newly
+ * provided credentials on NVS */
+ if (esp_wifi_set_storage(WIFI_STORAGE_FLASH) != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to set storage Wi-Fi");
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_FAIL;
+ }
+ /* Configure Wi-Fi station with host credentials
+ * provided during provisioning */
+ if (esp_wifi_set_config(ESP_IF_WIFI_STA, wifi_cfg) != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to set Wi-Fi configuration");
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_FAIL;
+ }
+ /* (Re)Start Wi-Fi */
+ if (esp_wifi_start() != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to set Wi-Fi configuration");
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_FAIL;
+ }
+ /* Connect to AP */
+ if (esp_wifi_connect() != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to connect Wi-Fi");
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_FAIL;
+ }
+ /* This delay allows channel change to complete */
+ vTaskDelay(1000 / portTICK_PERIOD_MS);
+
+ /* Reset Wi-Fi station state for provisioning app */
+ prov_ctx->wifi_state = WIFI_PROV_STA_CONNECTING;
+ prov_ctx->prov_state = WIFI_PROV_STATE_CRED_RECV;
+ /* Execute user registered callback handler */
+ execute_event_cb(WIFI_PROV_CRED_RECV, (void *)&wifi_cfg->sta);
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+
+ return ESP_OK;
+}
+
+esp_err_t wifi_prov_mgr_init(wifi_prov_mgr_config_t config)
+{
+ if (!prov_ctx_lock) {
+ /* Create mutex if this is the first time init is being called.
+ * This is created only once and never deleted because if some
+ * other thread is trying to take this mutex while it is being
+ * deleted from another thread then the reference may become
+ * invalid and cause exception */
+ prov_ctx_lock = xSemaphoreCreateRecursiveMutex();
+ if (!prov_ctx_lock) {
+ ESP_LOGE(TAG, "Failed to create mutex");
+ return ESP_ERR_NO_MEM;
+ }
+ }
+
+ void *fn_ptrs[] = {
+ config.scheme.prov_stop,
+ config.scheme.prov_start,
+ config.scheme.new_config,
+ config.scheme.delete_config,
+ config.scheme.set_config_service,
+ config.scheme.set_config_endpoint
+ };
+
+ /* All function pointers in the scheme structure must be non-null */
+ for (int i = 0; i < sizeof(fn_ptrs)/sizeof(fn_ptrs[0]); i++) {
+ if (!fn_ptrs[i]) {
+ return ESP_ERR_INVALID_ARG;
+ }
+ }
+
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+ if (prov_ctx) {
+ ESP_LOGE(TAG, "Provisioning manager already initialized");
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ /* Allocate memory for provisioning app data */
+ prov_ctx = (struct wifi_prov_mgr_ctx *) calloc(1, sizeof(struct wifi_prov_mgr_ctx));
+ if (!prov_ctx) {
+ ESP_LOGE(TAG, "Error allocating memory for singleton instance");
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_ERR_NO_MEM;
+ }
+
+ prov_ctx->mgr_config = config;
+ prov_ctx->prov_state = WIFI_PROV_STATE_IDLE;
+ prov_ctx->mgr_info.version = WIFI_PROV_MGR_VERSION;
+
+ /* Allocate memory for provisioning scheme configuration */
+ const wifi_prov_scheme_t *scheme = &prov_ctx->mgr_config.scheme;
+ esp_err_t ret = ESP_OK;
+ prov_ctx->prov_scheme_config = scheme->new_config();
+ if (!prov_ctx->prov_scheme_config) {
+ ESP_LOGE(TAG, "failed to allocate provisioning scheme configuration");
+ ret = ESP_ERR_NO_MEM;
+ goto exit;
+ }
+
+ ret = scheme->set_config_endpoint(prov_ctx->prov_scheme_config, "prov-session", 0xFF51);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "failed to configure security endpoint");
+ goto exit;
+ }
+
+ ret = scheme->set_config_endpoint(prov_ctx->prov_scheme_config, "prov-config", 0xFF52);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "failed to configure Wi-Fi configuration endpoint");
+ goto exit;
+ }
+
+ ret = scheme->set_config_endpoint(prov_ctx->prov_scheme_config, "proto-ver", 0xFF53);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "failed to configure version endpoint");
+ goto exit;
+ }
+
+ /* Application specific custom endpoints will be assigned
+ * incremental UUIDs starting after this value */
+ prov_ctx->endpoint_uuid_used = 0xFF53;
+
+ /* This delay is so that the client side app is notified first
+ * and then the provisioning is stopped. Default is 1000ms. */
+ prov_ctx->cleanup_delay = 1000;
+
+exit:
+ if (ret != ESP_OK) {
+ if (prov_ctx->prov_scheme_config) {
+ config.scheme.delete_config(prov_ctx->prov_scheme_config);
+ }
+ free(prov_ctx);
+ } else {
+ execute_event_cb(WIFI_PROV_INIT, NULL);
+ }
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ret;
+}
+
+void wifi_prov_mgr_wait(void)
+{
+ if (!prov_ctx_lock) {
+ ESP_LOGE(TAG, "Provisioning manager not initialized");
+ return;
+ }
+
+ while (1) {
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+ if (prov_ctx &&
+ prov_ctx->prov_state != WIFI_PROV_STATE_IDLE) {
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ vTaskDelay(1000 / portTICK_PERIOD_MS);
+ continue;
+ }
+ break;
+ }
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+}
+
+void wifi_prov_mgr_deinit(void)
+{
+ if (!prov_ctx_lock) {
+ ESP_LOGE(TAG, "Provisioning manager not initialized");
+ return;
+ }
+
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+
+ /* This will do one of these:
+ * 1) if found running, stop the provisioning service (returns true)
+ * 2) if service was already in the process of termination, this will
+ * wait till the service is stopped (returns false)
+ * 3) if service was not running, this will return immediately (returns false)
+ */
+ bool service_was_running = wifi_prov_mgr_stop_service(1);
+
+ /* If service was not running, its also possible that manager
+ * was not even initialized */
+ if (!service_was_running && !prov_ctx) {
+ ESP_LOGD(TAG, "Manager already de-initialized");
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return;
+ }
+
+ if (prov_ctx->app_info_json) {
+ cJSON_Delete(prov_ctx->app_info_json);
+ }
+
+ if (prov_ctx->prov_scheme_config) {
+ prov_ctx->mgr_config.scheme.delete_config(prov_ctx->prov_scheme_config);
+ }
+
+ /* Extract the callbacks to be called post deinit */
+ wifi_prov_cb_func_t app_cb = prov_ctx->mgr_config.app_event_handler.event_cb;
+ void *app_data = prov_ctx->mgr_config.app_event_handler.user_data;
+
+ wifi_prov_cb_func_t scheme_cb = prov_ctx->mgr_config.scheme_event_handler.event_cb;
+ void *scheme_data = prov_ctx->mgr_config.scheme_event_handler.user_data;
+
+ /* Free manager context */
+ free(prov_ctx);
+ prov_ctx = NULL;
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+
+ /* If a running service was also stopped during de-initialization
+ * then WIFI_PROV_END event also needs to be emitted before deinit */
+ if (service_was_running) {
+ ESP_LOGD(TAG, "execute_event_cb : %d", WIFI_PROV_END);
+ if (scheme_cb) {
+ scheme_cb(scheme_data, WIFI_PROV_END, NULL);
+ }
+ if (app_cb) {
+ app_cb(app_data, WIFI_PROV_END, NULL);
+ }
+ }
+
+ ESP_LOGD(TAG, "execute_event_cb : %d", WIFI_PROV_DEINIT);
+
+ /* Execute deinit event callbacks */
+ if (scheme_cb) {
+ scheme_cb(scheme_data, WIFI_PROV_DEINIT, NULL);
+ }
+ if (app_cb) {
+ app_cb(app_data, WIFI_PROV_DEINIT, NULL);
+ }
+}
+
+esp_err_t wifi_prov_mgr_start_provisioning(wifi_prov_security_t security, const char *pop,
+ const char *service_name, const char *service_key)
+{
+ if (!prov_ctx_lock) {
+ ESP_LOGE(TAG, "Provisioning manager not initialized");
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+ if (!prov_ctx) {
+ ESP_LOGE(TAG, "Provisioning manager not initialized");
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ if (prov_ctx->prov_state != WIFI_PROV_STATE_IDLE) {
+ ESP_LOGE(TAG, "Provisioning service already started");
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ esp_err_t ret = ESP_OK;
+ /* Update state so that parallel call to wifi_prov_mgr_start_provisioning()
+ * or wifi_prov_mgr_stop_provisioning() or wifi_prov_mgr_deinit() from another
+ * thread doesn't interfere with this process */
+ prov_ctx->prov_state = WIFI_PROV_STATE_STARTING;
+
+ /* Change Wi-Fi storage to RAM temporarily and erase any old
+ * credentials (i.e. without erasing the copy on NVS). Also
+ * call disconnect to make sure device doesn't remain connected
+ * to the AP whose credentials were present earlier */
+ wifi_config_t wifi_cfg_empty, wifi_cfg_old;
+ memset(&wifi_cfg_empty, 0, sizeof(wifi_config_t));
+ esp_wifi_get_config(ESP_IF_WIFI_STA, &wifi_cfg_old);
+ esp_wifi_set_storage(WIFI_STORAGE_RAM);
+ esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_cfg_empty);
+ esp_wifi_disconnect();
+
+ /* Initialize app data */
+ if (pop) {
+ prov_ctx->pop.len = strlen(pop);
+ prov_ctx->pop.data = malloc(prov_ctx->pop.len);
+ if (!prov_ctx->pop.data) {
+ ESP_LOGE(TAG, "Unable to allocate PoP data");
+ ret = ESP_ERR_NO_MEM;
+ goto err;
+ }
+ memcpy((void *)prov_ctx->pop.data, pop, prov_ctx->pop.len);
+ } else {
+ prov_ctx->mgr_info.capabilities.no_pop = true;
+ }
+ prov_ctx->security = security;
+
+ /* If auto stop on completion is enabled (default) create the stopping timer */
+ if (!prov_ctx->mgr_info.capabilities.no_auto_stop) {
+ /* Create timer object as a member of app data */
+ esp_timer_create_args_t timer_conf = {
+ .callback = stop_prov_timer_cb,
+ .arg = NULL,
+ .dispatch_method = ESP_TIMER_TASK,
+ .name = "wifi_prov_mgr_tm"
+ };
+ ret = esp_timer_create(&timer_conf, &prov_ctx->timer);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to create timer");
+ free((void *)prov_ctx->pop.data);
+ goto err;
+ }
+ }
+
+ /* System APIs for BLE / Wi-Fi will be called inside wifi_prov_mgr_start_service(),
+ * which may trigger system level events. Hence, releasing the context lock will
+ * ensure that wifi_prov_mgr_event_handler() doesn't block the global event_loop
+ * handler when system events need to be handled */
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+
+ /* Start provisioning service */
+ ret = wifi_prov_mgr_start_service(service_name, service_key);
+ if (ret != ESP_OK) {
+ esp_timer_delete(prov_ctx->timer);
+ free((void *)prov_ctx->pop.data);
+ }
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+ if (ret == ESP_OK) {
+ prov_ctx->prov_state = WIFI_PROV_STATE_STARTED;
+ /* Execute user registered callback handler */
+ execute_event_cb(WIFI_PROV_START, NULL);
+ goto exit;
+ }
+
+err:
+ prov_ctx->prov_state = WIFI_PROV_STATE_IDLE;
+ esp_wifi_set_storage(WIFI_STORAGE_FLASH);
+ esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_cfg_old);
+
+exit:
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+ return ret;
+}
+
+void wifi_prov_mgr_stop_provisioning(void)
+{
+ if (!prov_ctx_lock) {
+ ESP_LOGE(TAG, "Provisioning manager not initialized");
+ return;
+ }
+
+ xSemaphoreTakeRecursive(prov_ctx_lock, portMAX_DELAY);
+
+ /* Launches task for stopping the provisioning service. This will do one of these:
+ * 1) start a task for stopping the provisioning service (returns true)
+ * 2) if service was already in the process of termination, this will
+ * wait till the service is stopped (returns false)
+ * 3) if service was not running, this will return immediately (returns false)
+ */
+ wifi_prov_mgr_stop_service(0);
+
+ xSemaphoreGiveRecursive(prov_ctx_lock);
+}
--- /dev/null
+// Copyright 2019 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.
+
+#include <string.h>
+#include <esp_log.h>
+#include <esp_err.h>
+#include <esp_bt.h>
+
+#include <protocomm.h>
+#include <protocomm_ble.h>
+
+#include "wifi_provisioning/scheme_ble.h"
+#include "wifi_provisioning_priv.h"
+
+static const char *TAG = "wifi_prov_scheme_ble";
+
+extern const wifi_prov_scheme_t wifi_prov_scheme_ble;
+
+static uint8_t *custom_service_uuid;
+
+static esp_err_t prov_start(protocomm_t *pc, void *config)
+{
+ if (!pc) {
+ ESP_LOGE(TAG, "Protocomm handle cannot be null");
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ if (!config) {
+ ESP_LOGE(TAG, "Cannot start with null configuration");
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ protocomm_ble_config_t *ble_config = (protocomm_ble_config_t *) config;
+
+ /* Start protocomm as BLE service */
+ if (protocomm_ble_start(pc, ble_config) != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to start protocomm BLE service");
+ return ESP_FAIL;
+ }
+ return ESP_OK;
+}
+
+esp_err_t wifi_prov_scheme_ble_set_service_uuid(uint8_t *uuid128)
+{
+ if (!uuid128) {
+ return ESP_ERR_INVALID_ARG;
+ }
+ custom_service_uuid = uuid128;
+ return ESP_OK;
+}
+
+static void *new_config(void)
+{
+ protocomm_ble_config_t *ble_config = calloc(1, sizeof(protocomm_ble_config_t));
+ if (!ble_config) {
+ ESP_LOGE(TAG, "Error allocating memory for new configuration");
+ return NULL;
+ }
+
+ /* The default provisioning service UUID */
+ const uint8_t service_uuid[16] = {
+ /* LSB <---------------------------------------
+ * ---------------------------------------> MSB */
+ 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+ 0x00, 0x10, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+ };
+
+ memcpy(ble_config->service_uuid, service_uuid, sizeof(ble_config->service_uuid));
+ return ble_config;
+}
+
+static void delete_config(void *config)
+{
+ if (!config) {
+ ESP_LOGE(TAG, "Cannot delete null configuration");
+ return;
+ }
+
+ protocomm_ble_config_t *ble_config = (protocomm_ble_config_t *) config;
+ for (unsigned int i = 0; i < ble_config->nu_lookup_count; i++) {
+ free((void *)ble_config->nu_lookup[i].name);
+ }
+ free(ble_config->nu_lookup);
+ free(ble_config);
+}
+
+static esp_err_t set_config_service(void *config, const char *service_name, const char *service_key)
+{
+ if (!config) {
+ ESP_LOGE(TAG, "Cannot set null configuration");
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ if (!service_name) {
+ ESP_LOGE(TAG, "Service name cannot be NULL");
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ protocomm_ble_config_t *ble_config = (protocomm_ble_config_t *) config;
+ strlcpy(ble_config->device_name, service_name, sizeof(ble_config->device_name));
+
+ /* If a custom service UUID has been provided, override the default one */
+ if (custom_service_uuid) {
+ memcpy(ble_config->service_uuid, custom_service_uuid, sizeof(ble_config->service_uuid));
+ }
+ return ESP_OK;
+}
+
+static esp_err_t set_config_endpoint(void *config, const char *endpoint_name, uint16_t uuid)
+{
+ if (!config) {
+ ESP_LOGE(TAG, "Cannot set null configuration");
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ if (!endpoint_name) {
+ ESP_LOGE(TAG, "EP name cannot be null");
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ protocomm_ble_config_t *ble_config = (protocomm_ble_config_t *) config;
+
+ char *copy_ep_name = strdup(endpoint_name);
+ if (!copy_ep_name) {
+ ESP_LOGE(TAG, "Error allocating memory for EP name");
+ return ESP_ERR_NO_MEM;
+ }
+
+ protocomm_ble_name_uuid_t *lookup_table = (
+ realloc(ble_config->nu_lookup, (ble_config->nu_lookup_count + 1) * sizeof(protocomm_ble_name_uuid_t)));
+ if (!lookup_table) {
+ ESP_LOGE(TAG, "Error allocating memory for EP-UUID lookup table");
+ return ESP_ERR_NO_MEM;
+ }
+
+ lookup_table[ble_config->nu_lookup_count].name = copy_ep_name;
+ lookup_table[ble_config->nu_lookup_count].uuid = uuid;
+ ble_config->nu_lookup = lookup_table;
+ ble_config->nu_lookup_count += 1;
+ return ESP_OK;
+}
+
+/* Used when both BT and BLE are not needed by application */
+void wifi_prov_scheme_ble_event_cb_free_btdm(void *user_data, wifi_prov_cb_event_t event, void *event_data)
+{
+ esp_err_t err;
+ switch (event) {
+ case WIFI_PROV_INIT:
+ /* Release BT memory, as we need only BLE */
+ err = esp_bt_mem_release(ESP_BT_MODE_CLASSIC_BT);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "bt_mem_release of classic BT failed %d", err);
+ } else {
+ ESP_LOGI(TAG, "BT memory released");
+ }
+ break;
+
+ case WIFI_PROV_DEINIT:
+ /* Release memory used by BLE and Bluedroid host stack */
+ err = esp_bt_mem_release(ESP_BT_MODE_BTDM);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "bt_mem_release of BTDM failed %d", err);
+ } else {
+ ESP_LOGI(TAG, "BTDM memory released");
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Used when BT is not needed by application */
+void wifi_prov_scheme_ble_event_cb_free_bt(void *user_data, wifi_prov_cb_event_t event, void *event_data)
+{
+ esp_err_t err;
+ switch (event) {
+ case WIFI_PROV_INIT:
+ /* Release BT memory, as we need only BLE */
+ err = esp_bt_mem_release(ESP_BT_MODE_CLASSIC_BT);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "bt_mem_release of classic BT failed %d", err);
+ } else {
+ ESP_LOGI(TAG, "BT memory released");
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Used when BLE is not needed by application */
+void wifi_prov_scheme_ble_event_cb_free_ble(void *user_data, wifi_prov_cb_event_t event, void *event_data)
+{
+ esp_err_t err;
+ switch (event) {
+ case WIFI_PROV_DEINIT:
+ /* Release memory used by BLE stack */
+ err = esp_bt_mem_release(ESP_BT_MODE_BLE);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "bt_mem_release of BLE failed %d", err);
+ } else {
+ ESP_LOGI(TAG, "BLE memory released");
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+const wifi_prov_scheme_t wifi_prov_scheme_ble = {
+ .prov_start = prov_start,
+ .prov_stop = protocomm_ble_stop,
+ .new_config = new_config,
+ .delete_config = delete_config,
+ .set_config_service = set_config_service,
+ .set_config_endpoint = set_config_endpoint,
+ .wifi_mode = WIFI_MODE_STA
+};
--- /dev/null
+// Copyright 2019 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.
+
+#include <string.h>
+#include <esp_log.h>
+#include <esp_err.h>
+#include <esp_wifi.h>
+
+#include <protocomm.h>
+#include <protocomm_console.h>
+
+#include "wifi_provisioning/scheme_console.h"
+#include "wifi_provisioning_priv.h"
+
+static const char *TAG = "wifi_prov_scheme_console";
+
+extern const wifi_prov_scheme_t wifi_prov_scheme_console;
+
+static esp_err_t prov_start(protocomm_t *pc, void *config)
+{
+ if (!pc) {
+ ESP_LOGE(TAG, "Protocomm handle cannot be null");
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ if (!config) {
+ ESP_LOGE(TAG, "Cannot start with null configuration");
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ protocomm_console_config_t *console_config = (protocomm_console_config_t *) config;
+
+ /* Start protocomm console */
+ esp_err_t err = protocomm_console_start(pc, console_config);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to start protocomm HTTP server");
+ return ESP_FAIL;
+ }
+ return ESP_OK;
+}
+
+static void *new_config(void)
+{
+ protocomm_console_config_t *console_config = malloc(sizeof(protocomm_console_config_t));
+ if (!console_config) {
+ ESP_LOGE(TAG, "Error allocating memory for new configuration");
+ return NULL;
+ }
+ protocomm_console_config_t default_config = PROTOCOMM_CONSOLE_DEFAULT_CONFIG();
+ memcpy(console_config, &default_config, sizeof(default_config));
+ return console_config;
+}
+
+static void delete_config(void *config)
+{
+ if (!config) {
+ ESP_LOGE(TAG, "Cannot delete null configuration");
+ return;
+ }
+ free(config);
+}
+
+static esp_err_t set_config_service(void *config, const char *service_name, const char *service_key)
+{
+ return ESP_OK;
+}
+
+static esp_err_t set_config_endpoint(void *config, const char *endpoint_name, uint16_t uuid)
+{
+ return ESP_OK;
+}
+
+const wifi_prov_scheme_t wifi_prov_scheme_console = {
+ .prov_start = prov_start,
+ .prov_stop = protocomm_console_stop,
+ .new_config = new_config,
+ .delete_config = delete_config,
+ .set_config_service = set_config_service,
+ .set_config_endpoint = set_config_endpoint,
+ .wifi_mode = WIFI_MODE_STA
+};
--- /dev/null
+// Copyright 2019 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.
+
+#include <string.h>
+#include <esp_log.h>
+#include <esp_err.h>
+#include <esp_wifi.h>
+
+#include <mdns.h>
+#include <protocomm.h>
+#include <protocomm_httpd.h>
+
+#include "wifi_provisioning/scheme_softap.h"
+#include "wifi_provisioning_priv.h"
+
+typedef struct softap_config {
+ protocomm_httpd_config_t httpd_config;
+ char ssid[33];
+ char password[65];
+} wifi_prov_softap_config_t;
+
+static const char *TAG = "wifi_prov_scheme_softap";
+
+extern const wifi_prov_scheme_t wifi_prov_scheme_softap;
+
+static esp_err_t start_wifi_ap(const char *ssid, const char *pass)
+{
+ /* Build Wi-Fi configuration for AP mode */
+ wifi_config_t wifi_config = {
+ .ap = {
+ .max_connection = 5,
+ },
+ };
+
+ strncpy((char *) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid));
+ wifi_config.ap.ssid_len = strnlen(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 {
+ strlcpy((char *) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password));
+ wifi_config.ap.authmode = WIFI_AUTH_WPA_WPA2_PSK;
+ }
+
+ /* Start Wi-Fi in AP mode with configuration built above */
+ esp_err_t err = esp_wifi_set_mode(WIFI_MODE_AP);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to set Wi-Fi mode : %d", err);
+ return err;
+ }
+ err = esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to set Wi-Fi config : %d", err);
+ return err;
+ }
+ err = esp_wifi_start();
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to start Wi-Fi : %d", err);
+ return err;
+ }
+ ESP_LOGD(TAG, "Wi-Fi SoftAP started");
+ return ESP_OK;
+}
+
+static esp_err_t prov_start(protocomm_t *pc, void *config)
+{
+ if (!pc) {
+ ESP_LOGE(TAG, "Protocomm handle cannot be null");
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ if (!config) {
+ ESP_LOGE(TAG, "Cannot start with null configuration");
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ wifi_prov_softap_config_t *softap_config = (wifi_prov_softap_config_t *) config;
+
+ protocomm_httpd_config_t *httpd_config = &softap_config->httpd_config;
+
+ /* Start protocomm server on top of HTTP */
+ esp_err_t err = protocomm_httpd_start(pc, httpd_config);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to start protocomm HTTP server");
+ return err;
+ }
+
+ /* Start Wi-Fi softAP with specified ssid and password */
+ err = start_wifi_ap(softap_config->ssid, softap_config->password);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to start Wi-Fi AP");
+ protocomm_httpd_stop(pc);
+ return err;
+ }
+
+ /* Add mDNS service for allowing discovery of provisioning
+ * service on the SoftAP network (Optional). Even though
+ * this is an http service we identify it by _esp_wifi_prov so
+ * that application is free to use _http without conflict */
+ err = mdns_service_add("Wi-Fi Provisioning Service", "_esp_wifi_prov", "_tcp",
+ softap_config->httpd_config.data.config.port, NULL, 0);
+ if (err != ESP_OK) {
+ /* mDNS is not mandatory for provisioning to work,
+ * so print warning and return without failure */
+ ESP_LOGW(TAG, "Error adding mDNS service! Check if mDNS is running");
+ } else {
+ /* Information to identify the roles of the various
+ * protocomm endpoint URIs provided by the service */
+ err |= mdns_service_txt_item_set("_esp_wifi_prov", "_tcp", "version_endpoint", "/proto-ver");
+ err |= mdns_service_txt_item_set("_esp_wifi_prov", "_tcp", "session_endpoint", "/prov-session");
+ err |= mdns_service_txt_item_set("_esp_wifi_prov", "_tcp", "config_endpoint", "/prov-config");
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Error adding mDNS service text item");
+ }
+ }
+ return ESP_OK;
+}
+
+static esp_err_t prov_stop(protocomm_t *pc)
+{
+ esp_err_t err = protocomm_httpd_stop(pc);
+ if (err != ESP_OK) {
+ ESP_LOGW(TAG, "Error occurred while stopping protocomm_httpd");
+ }
+
+ mdns_service_remove("_esp_wifi_prov", "_tcp");
+ return err;
+}
+
+static void *new_config(void)
+{
+ wifi_prov_softap_config_t *softap_config = calloc(1, sizeof(wifi_prov_softap_config_t));
+ if (!softap_config) {
+ ESP_LOGE(TAG, "Error allocating memory for new configuration");
+ return NULL;
+ }
+ protocomm_httpd_config_t default_config = {
+ .data = {
+ .config = PROTOCOMM_HTTPD_DEFAULT_CONFIG()
+ }
+ };
+ softap_config->httpd_config = default_config;
+ return softap_config;
+}
+
+static void delete_config(void *config)
+{
+ if (!config) {
+ ESP_LOGE(TAG, "Cannot delete null configuration");
+ return;
+ }
+
+ wifi_prov_softap_config_t *softap_config = (wifi_prov_softap_config_t *) config;
+ free(softap_config);
+}
+
+static esp_err_t set_config_service(void *config, const char *service_name, const char *service_key)
+{
+ if (!config) {
+ ESP_LOGE(TAG, "Cannot set null configuration");
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ if (!service_name) {
+ ESP_LOGE(TAG, "Service name cannot be NULL");
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ wifi_prov_softap_config_t *softap_config = (wifi_prov_softap_config_t *) config;
+ strlcpy(softap_config->ssid, service_name, sizeof(softap_config->ssid));
+ if (service_key) {
+ strlcpy(softap_config->password, service_key, sizeof(softap_config->password));
+ }
+ return ESP_OK;
+}
+
+static esp_err_t set_config_endpoint(void *config, const char *endpoint_name, uint16_t uuid)
+{
+ return ESP_OK;
+}
+
+const wifi_prov_scheme_t wifi_prov_scheme_softap = {
+ .prov_start = prov_start,
+ .prov_stop = prov_stop,
+ .new_config = new_config,
+ .delete_config = delete_config,
+ .set_config_service = set_config_service,
+ .set_config_endpoint = set_config_endpoint,
+ .wifi_mode = WIFI_MODE_APSTA
+};
--- /dev/null
+// Copyright 2019 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.
+
+#pragma once
+
+#include <esp_event_loop.h>
+
+#include <protocomm.h>
+#include <protocomm_security.h>
+
+#include "wifi_provisioning/manager.h"
+#include "wifi_provisioning/wifi_config.h"
+
+/**
+ * @brief Notify manager that provisioning is done
+ *
+ * Stops the provisioning. This is called by the get_status_handler()
+ * when the status is connected. This has no effect if main application
+ * has disabled auto stop on completion by calling
+ * wifi_prov_mgr_disable_auto_stop()
+ *
+ * @return
+ * - ESP_OK : Provisioning will be stopped
+ * - ESP_FAIL : Failed to stop provisioning
+ */
+esp_err_t wifi_prov_mgr_done(void);
+
+/**
+ * @brief Get protocomm handlers for wifi_config provisioning endpoint
+ *
+ * @return wifi_prov_config_handlers_t structure
+ */
+wifi_prov_config_handlers_t get_wifi_prov_handlers(void);