|
|
@@ -0,0 +1,244 @@
|
|
|
+/* Wi-Fi Provisioning Manager Example
|
|
|
+
|
|
|
+ This example code is in the Public Domain (or CC0 licensed, at your option.)
|
|
|
+
|
|
|
+ Unless required by applicable law or agreed to in writing, this
|
|
|
+ software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
|
+ CONDITIONS OF ANY KIND, either express or implied.
|
|
|
+*/
|
|
|
+
|
|
|
+#include <stdio.h>
|
|
|
+#include <string.h>
|
|
|
+
|
|
|
+#include <freertos/FreeRTOS.h>
|
|
|
+#include <freertos/task.h>
|
|
|
+#include <freertos/event_groups.h>
|
|
|
+
|
|
|
+#include <esp_log.h>
|
|
|
+#include <esp_wifi.h>
|
|
|
+#include <esp_event_loop.h>
|
|
|
+#include <nvs_flash.h>
|
|
|
+
|
|
|
+#include <wifi_provisioning/manager.h>
|
|
|
+#include <wifi_provisioning/scheme_ble.h>
|
|
|
+#include <wifi_provisioning/scheme_softap.h>
|
|
|
+
|
|
|
+static const char *TAG = "app";
|
|
|
+
|
|
|
+/* Signal Wi-Fi events on this event-group */
|
|
|
+const int WIFI_CONNECTED_EVENT = BIT0;
|
|
|
+static EventGroupHandle_t wifi_event_group;
|
|
|
+
|
|
|
+/* Event handler for catching system events */
|
|
|
+static esp_err_t event_handler(void *ctx, system_event_t *event)
|
|
|
+{
|
|
|
+ /* Pass event information to provisioning manager so that it can
|
|
|
+ * maintain its internal state depending upon the system event */
|
|
|
+ wifi_prov_mgr_event_handler(ctx, event);
|
|
|
+
|
|
|
+ /* Global event handling */
|
|
|
+ switch (event->event_id) {
|
|
|
+ case SYSTEM_EVENT_STA_START:
|
|
|
+ esp_wifi_connect();
|
|
|
+ break;
|
|
|
+ case SYSTEM_EVENT_STA_GOT_IP:
|
|
|
+ ESP_LOGI(TAG, "Connected with IP Address:%s",
|
|
|
+ ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip));
|
|
|
+ xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_EVENT);
|
|
|
+ break;
|
|
|
+ case SYSTEM_EVENT_STA_DISCONNECTED:
|
|
|
+ ESP_LOGI(TAG, "Disconnected. Connecting to the AP again...");
|
|
|
+ esp_wifi_connect();
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return ESP_OK;
|
|
|
+}
|
|
|
+
|
|
|
+/* Event handler for catching provisioning manager events */
|
|
|
+static void prov_event_handler(void *user_data,
|
|
|
+ wifi_prov_cb_event_t event, void *event_data)
|
|
|
+{
|
|
|
+ switch (event) {
|
|
|
+ case WIFI_PROV_START:
|
|
|
+ ESP_LOGI(TAG, "Provisioning started");
|
|
|
+ break;
|
|
|
+ case WIFI_PROV_CRED_RECV: {
|
|
|
+ wifi_sta_config_t *wifi_sta_cfg = (wifi_sta_config_t *)event_data;
|
|
|
+ /* If SSID length is exactly 32 bytes, null termination
|
|
|
+ * will not be present, so explicitly obtain the length */
|
|
|
+ size_t ssid_len = strnlen((const char *)wifi_sta_cfg->ssid, sizeof(wifi_sta_cfg->ssid));
|
|
|
+ ESP_LOGI(TAG, "Received Wi-Fi credentials"
|
|
|
+ "\n\tSSID : %.*s\n\tPassword : %s",
|
|
|
+ ssid_len, (const char *) wifi_sta_cfg->ssid,
|
|
|
+ (const char *) wifi_sta_cfg->password);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case WIFI_PROV_CRED_FAIL: {
|
|
|
+ wifi_prov_sta_fail_reason_t *reason = (wifi_prov_sta_fail_reason_t *)event_data;
|
|
|
+ ESP_LOGE(TAG, "Provisioning failed!\n\tReason : %s"
|
|
|
+ "\n\tPlease reset to factory and retry provisioning",
|
|
|
+ (*reason == WIFI_PROV_STA_AUTH_ERROR) ?
|
|
|
+ "Wi-Fi AP password incorrect" : "Wi-Fi AP not found");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case WIFI_PROV_CRED_SUCCESS:
|
|
|
+ ESP_LOGI(TAG, "Provisioning successful");
|
|
|
+ break;
|
|
|
+ case WIFI_PROV_END:
|
|
|
+ /* De-initialize manager once provisioning is finished */
|
|
|
+ wifi_prov_mgr_deinit();
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void wifi_init_sta()
|
|
|
+{
|
|
|
+ ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
|
|
+ ESP_ERROR_CHECK(esp_wifi_start());
|
|
|
+}
|
|
|
+
|
|
|
+static void get_device_service_name(char *service_name, size_t max)
|
|
|
+{
|
|
|
+ uint8_t eth_mac[6];
|
|
|
+ const char *ssid_prefix = "PROV_";
|
|
|
+ esp_wifi_get_mac(WIFI_IF_STA, eth_mac);
|
|
|
+ snprintf(service_name, max, "%s%02X%02X%02X",
|
|
|
+ ssid_prefix, eth_mac[3], eth_mac[4], eth_mac[5]);
|
|
|
+}
|
|
|
+
|
|
|
+void app_main()
|
|
|
+{
|
|
|
+ /* Initialize NVS partition */
|
|
|
+ esp_err_t ret = nvs_flash_init();
|
|
|
+ if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
|
|
+ /* NVS partition was truncated
|
|
|
+ * and needs to be erased */
|
|
|
+ ESP_ERROR_CHECK(nvs_flash_erase());
|
|
|
+
|
|
|
+ /* Retry nvs_flash_init */
|
|
|
+ ESP_ERROR_CHECK(nvs_flash_init());
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Initialize TCP/IP and the event loop */
|
|
|
+ tcpip_adapter_init();
|
|
|
+ ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
|
|
|
+ wifi_event_group = xEventGroupCreate();
|
|
|
+
|
|
|
+ /* Initialize Wi-Fi */
|
|
|
+ wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
|
|
+ ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
|
|
+
|
|
|
+ /* Configuration for the provisioning manager */
|
|
|
+ wifi_prov_mgr_config_t config = {
|
|
|
+ /* What is the Provisioning Scheme that we want ?
|
|
|
+ * wifi_prov_scheme_softap or wifi_prov_scheme_ble */
|
|
|
+ .scheme = wifi_prov_scheme_ble,
|
|
|
+
|
|
|
+ /* Any default scheme specific event handler that you would
|
|
|
+ * like to choose. Since our example application requires
|
|
|
+ * neither BT nor BLE, we can choose to release the associated
|
|
|
+ * memory once provisioning is complete, or not needed
|
|
|
+ * (in case when device is already provisioned). Choosing
|
|
|
+ * appropriate scheme specific event handler allows the manager
|
|
|
+ * to take care of this automatically. This can be set to
|
|
|
+ * WIFI_PROV_EVENT_HANDLER_NONE when using wifi_prov_scheme_softap*/
|
|
|
+ .scheme_event_handler = WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM,
|
|
|
+
|
|
|
+ /* Do we want an application specific handler be executed on
|
|
|
+ * various provisioning related events */
|
|
|
+ .app_event_handler = {
|
|
|
+ .event_cb = prov_event_handler,
|
|
|
+ .user_data = NULL
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ /* Initialize provisioning manager with the
|
|
|
+ * configuration parameters set above */
|
|
|
+ ESP_ERROR_CHECK(wifi_prov_mgr_init(config));
|
|
|
+
|
|
|
+ bool provisioned = false;
|
|
|
+ /* Let's find out if the device is provisioned */
|
|
|
+ ESP_ERROR_CHECK(wifi_prov_mgr_is_provisioned(&provisioned));
|
|
|
+
|
|
|
+ /* If device is not yet provisioned start provisioning service */
|
|
|
+ if (!provisioned) {
|
|
|
+ ESP_LOGI(TAG, "Starting provisioning");
|
|
|
+
|
|
|
+ /* What is the Device Service Name that we want
|
|
|
+ * This translates to :
|
|
|
+ * - Wi-Fi SSID when scheme is wifi_prov_scheme_softap
|
|
|
+ * - device name when scheme is wifi_prov_scheme_ble
|
|
|
+ */
|
|
|
+ char service_name[12];
|
|
|
+ get_device_service_name(service_name, sizeof(service_name));
|
|
|
+
|
|
|
+ /* What is the security level that we want (0 or 1):
|
|
|
+ * - WIFI_PROV_SECURITY_0 is simply plain text communication.
|
|
|
+ * - WIFI_PROV_SECURITY_1 is secure communication which consists of secure handshake
|
|
|
+ * using X25519 key exchange and proof of possession (pop) and AES-CTR
|
|
|
+ * for encryption/decryption of messages.
|
|
|
+ */
|
|
|
+ wifi_prov_security_t security = WIFI_PROV_SECURITY_1;
|
|
|
+
|
|
|
+ /* Do we want a proof-of-possession (ignored if Security 0 is selected):
|
|
|
+ * - this should be a string with length > 0
|
|
|
+ * - NULL if not used
|
|
|
+ */
|
|
|
+ const char *pop = "abcd1234";
|
|
|
+
|
|
|
+ /* What is the service key (could be NULL)
|
|
|
+ * This translates to :
|
|
|
+ * - Wi-Fi password when scheme is wifi_prov_scheme_softap
|
|
|
+ * - simply ignored when scheme is wifi_prov_scheme_ble
|
|
|
+ */
|
|
|
+ const char *service_key = NULL;
|
|
|
+
|
|
|
+ /* This step is only useful when scheme is wifi_prov_scheme_ble. This will
|
|
|
+ * set a custom 128 bit UUID which will be included in the BLE advertisement
|
|
|
+ * and will correspond to the primary GATT service that provides provisioning
|
|
|
+ * endpoints as GATT characteristics. Each GATT characteristic will be
|
|
|
+ * formed using the primary service UUID as base, with different auto assigned
|
|
|
+ * 12th and 13th bytes (assume counting starts from 0th byte). The client side
|
|
|
+ * applications must identify the endpoints by reading the User Characteristic
|
|
|
+ * Description descriptor (0x2901) for each characteristic, which contains the
|
|
|
+ * endpoint name of the characteristic */
|
|
|
+ uint8_t custom_service_uuid[] = {
|
|
|
+ /* LSB <---------------------------------------
|
|
|
+ * ---------------------------------------> MSB */
|
|
|
+ 0x21, 0x43, 0x65, 0x87, 0x09, 0xba, 0xdc, 0xfe,
|
|
|
+ 0xef, 0xcd, 0xab, 0x90, 0x78, 0x56, 0x34, 0x12
|
|
|
+ };
|
|
|
+ wifi_prov_scheme_ble_set_service_uuid(custom_service_uuid);
|
|
|
+
|
|
|
+ /* Start provisioning service */
|
|
|
+ ESP_ERROR_CHECK(wifi_prov_mgr_start_provisioning(security, pop, service_name, service_key));
|
|
|
+
|
|
|
+ /* Uncomment the following to wait for the provisioning to finish and then release
|
|
|
+ * the resources of the manager. Since in this case de-initialization is triggered
|
|
|
+ * by the configured prov_event_handler(), we don't need to call the following */
|
|
|
+ // wifi_prov_mgr_wait();
|
|
|
+ // wifi_prov_mgr_deinit();
|
|
|
+ } else {
|
|
|
+ ESP_LOGI(TAG, "Already provisioned, starting Wi-Fi STA");
|
|
|
+
|
|
|
+ /* We don't need the manager as device is already provisioned,
|
|
|
+ * so let's release it's resources */
|
|
|
+ wifi_prov_mgr_deinit();
|
|
|
+
|
|
|
+ /* Start Wi-Fi station */
|
|
|
+ wifi_init_sta();
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Wait for Wi-Fi connection */
|
|
|
+ xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_EVENT, false, true, portMAX_DELAY);
|
|
|
+
|
|
|
+ /* Start main application now */
|
|
|
+ while (1) {
|
|
|
+ ESP_LOGI(TAG, "Hello World!");
|
|
|
+ vTaskDelay(1000 / portTICK_PERIOD_MS);
|
|
|
+ }
|
|
|
+}
|