Skip to content

BLE provisioning turns off after failed WiFi authentication #4139

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
maikeriva opened this issue Jul 6, 2020 · 8 comments
Closed

BLE provisioning turns off after failed WiFi authentication #4139

maikeriva opened this issue Jul 6, 2020 · 8 comments
Labels
Status: Stale Issue is stale stage (outdated/stuck)

Comments

@maikeriva
Copy link

Hardware

  • Arduino IDE 1.8.12
  • Fedora 31
  • ESP32 WROOM32D

Description:

Hello!

I am trying to provision my ESP32 device with the new unified provisioning API by using BLE and the dedicated Android application. To do so I am basing my code on the Arduino sketch WiFiProv.ino found in the arduino-esp32 repository.

After selecting the network on the Android application and setting the credentials, in the case the connection attempt fails (eg. when given credentials are wrong) the device cannot be detected anymore from the application home screen. It seems that after the failed attempt the device won't be reachable via BLE anymore, and I can't find a way through the given API to change this behaviour.

Digging a little bit under the hood, I found the function wifi_prov_mgr_disable_auto_stop() and I've built a custom
WiFiProvClass :: beginBLEProvision(wifi_prov_event_handler_t scheme_event_handler, wifi_prov_security_t security, const char * pop, const char *service_name, const char *service_key, uint8_t * uuid) method which integrates it (and also which should allow to re-provision the device even when already provisioned, which is another thing I need). You can find the method here below:

WiFiProvClass :: beginBLEProvision(wifi_prov_event_handler_t scheme_event_handler, wifi_prov_security_t security, const char * pop, const char *service_name, const char *service_key, uint8_t * uuid) {
    prov_enable = true;
    wifi_prov_mgr_config_t config;
    config.scheme_event_handler = scheme_event_handler;
    config.app_event_handler = {
            .event_cb = prov_event_handler,
            .user_data = NULL
            };
    config.scheme = wifi_prov_scheme_ble;        
 
    if (wifi_prov_mgr_init(config) != ESP_OK) {
        log_e("Could not initialize provisioning manager");
    }

    WiFi.mode(WIFI_MODE_AP);
    service_key = NULL;
    if(uuid == NULL) {
        uuid=(uint8_t *)custom_service_uuid;
    }
    if (wifi_prov_scheme_ble_set_service_uuid(uuid) != ESP_OK) {
        log_e("Could not set provisioning service UUID");
    }

    if(service_name == NULL) {
        char service_name_temp[12];
        get_device_service_name(WIFI_PROV_SCHEME_BLE,service_name_temp,sizeof(service_name_temp));
        service_name = (const char *)service_name_temp;
    }

    log_i("Starting AP using BLE\n service_name : %s\n pop : %s",service_name,pop);
    
    if (wifi_prov_mgr_disable_auto_stop(1000) != ESP_OK) {
        log_e("Could not deactivate auto-stop on provisioning manager");
    }

    if (wifi_prov_mgr_start_provisioning(security,pop,service_name,service_key) != ESP_OK) {
        log_e("Could not start provisioning manager");
    }

    log_i("Provisioning manager started");
}

While with this it is possible to start the BLE provisioning operation even with WiFi credentials already present in NVS, it still doesn't solve the fact the device disappears from the Android application after a failed credentials attempt.

In a hacky attempt to solve the problem I event tried to deinit and reinit the WiFi provisioner after WIFI_PROV_CRED_FAIL events in WiFiProv.cpp :: prov_event_handler(), yet the firmware crashes and the backtrace fails to be decoded by EspExceptionDecoder.

Maybe the arduino-esp32 devs have an idea on how this could be accomplished? Is it an inherent limitation of the provisioning manager?

Sketch:

WiFiProv.ino

#include "WiFi.h"
void SysProvEvent(system_event_t *sys_event,wifi_prov_event_t *prov_event)
{
    if(sys_event) {
      switch (sys_event->event_id) {
      case SYSTEM_EVENT_STA_GOT_IP:
          Serial.print("\nConnected IP address : ");
          Serial.println(ip4addr_ntoa(&sys_event->event_info.got_ip.ip_info.ip));
          break;
      case SYSTEM_EVENT_STA_DISCONNECTED:
          Serial.println("\nDisconnected. Connecting to the AP again... ");
          break;
      default:
          break;
      }      
    }

    if(prov_event) {
        switch (prov_event->event) {
        case WIFI_PROV_START:
            Serial.println("\nProvisioning started\nGive Credentials of your access point using \" Android app \"");
            break;
        case WIFI_PROV_CRED_RECV: { 
            Serial.println("\nReceived Wi-Fi credentials");
            wifi_sta_config_t *wifi_sta_cfg = (wifi_sta_config_t *)prov_event->event_data;
            Serial.print("\tSSID : ");
            Serial.println((const char *) wifi_sta_cfg->ssid);
            Serial.print("\tPassword : ");
            Serial.println((char const *) wifi_sta_cfg->password);
            break;
        }
        case WIFI_PROV_CRED_FAIL: { 
            wifi_prov_sta_fail_reason_t *reason = (wifi_prov_sta_fail_reason_t *)prov_event->event_data;
            Serial.println("\nProvisioning failed!\nPlease reset to factory and retry provisioning\n");
            if(*reason == WIFI_PROV_STA_AUTH_ERROR) 
                Serial.println("\nWi-Fi AP password incorrect");
            else
                Serial.println("\nWi-Fi AP not found....Add API \" nvs_flash_erase() \" before beginProvision()");
            break;
        }
        case WIFI_PROV_CRED_SUCCESS:
            Serial.println("\nProvisioning Successful");
            break;
        case WIFI_PROV_END:
            Serial.println("\nProvisioning Ends");
            break;
        default:
            break;
        }      
    }
}

void setup() {
  Serial.begin(115200);
  //Sample uuid that user can pass during provisioning using BLE
  /* uint8_t uuid[16] = {0xb4, 0xdf, 0x5a, 0x1c, 0x3f, 0x6b, 0xf4, 0xbf,
                   0xea, 0x4a, 0x82, 0x03, 0x04, 0x90, 0x1a, 0x02 };*/
  WiFi.onEvent(SysProvEvent);
  WiFi.beginBLEProvision(WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM, WIFI_PROV_SECURITY_0, "abcd1234", "BLE_XXX", NULL, NULL);
  Serial.println("Hello I am alive!");
}

void loop() {
}

WiFiProv.cpp (with added beginBLEProvision() method)

/*
    WiFiProv.cpp - WiFiProv class for provisioning
    All rights reserved.
 
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.
  
    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
  
    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    
*/

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <esp_err.h>
#include <esp_wifi.h>
#include <esp_event_loop.h>
#include <esp32-hal.h>

#include <nvs_flash.h>
#include <wifi_provisioning/scheme_ble.h>
#include <wifi_provisioning/scheme_softap.h>
#include <wifi_provisioning/manager.h>
#undef IPADDR_NONE
#include "WiFi.h"

extern esp_err_t postToSysQueue(system_prov_event_t *);
static const uint8_t custom_service_uuid[16] = {  0xb4, 0xdf, 0x5a, 0x1c, 0x3f, 0x6b, 0xf4, 0xbf,
                                                  0xea, 0x4a, 0x82, 0x03, 0x04, 0x90, 0x1a, 0x02, };

#define SERV_NAME_PREFIX_BLE "BLE_"
#define SERV_NAME_PREFIX_WIFI "WIFI_"

bool WiFiProvClass::prov_enable = true;

bool WiFiProvClass::isProvEnabled()
{
    return prov_enable;
}

static void prov_event_handler(void *user_data, wifi_prov_cb_event_t event, void *event_data)
{
    if (!event) {
         return;
     }

    if (event == WIFI_PROV_CRED_FAIL) {
        /* Restart provisioner */
        /* SUPER HACKY! */
        log_i("WIFI_PROV_CRED_FAIL: Restarting provisioner");
        wifi_prov_mgr_deinit();
        vTaskDelay(pdMS_TO_TICKS(1000));
        WiFi.beginBLEProvision(WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM, WIFI_PROV_SECURITY_0, "abcd1234", "BLE_XXX", NULL, NULL);
        return;
    }

     system_prov_event_t *sys_prov = (system_prov_event_t *)malloc(sizeof(system_prov_event_t));
     if(sys_prov == NULL) {
         log_e("Malloc Failed");
         return;
     }

     sys_prov->prov_event = (wifi_prov_event_t *)malloc(sizeof(wifi_prov_event_t));
     if(sys_prov->prov_event == NULL) {
         log_e("Malloc Failed");
         free(sys_prov);
         return;
     }

     sys_prov->sys_event = (system_event_t *)malloc(sizeof(system_event_t));
     if(sys_prov->sys_event == NULL) {
         log_e("Malloc Failed");
         free(sys_prov->prov_event);
         free(sys_prov);
         return;
     }

     sys_prov->prov_event->event = event;
     sys_prov->prov_event->event_data = event_data;
     sys_prov->sys_event->event_id = SYSTEM_EVENT_MAX;
     esp_err_t check = postToSysQueue(sys_prov);
     if(check == ESP_FAIL) {
         log_e("Provisioning event not send to queue");
         free(sys_prov->sys_event);
         free(sys_prov->prov_event);
         free(sys_prov);
     }
}

static void get_device_service_name(scheme prov_scheme, char *service_name, size_t max)
{
    uint8_t eth_mac[6];
    WiFi.macAddress(eth_mac);
    if(prov_scheme == WIFI_PROV_SCHEME_BLE) {
        snprintf(service_name, max, "%s%02X%02X%02X",SERV_NAME_PREFIX_BLE, eth_mac[3], eth_mac[4], eth_mac[5]);
    } else {
         snprintf(service_name, max, "%s%02X%02X%02X",SERV_NAME_PREFIX_WIFI, eth_mac[3], eth_mac[4], eth_mac[5]);  
    }
}

void WiFiProvClass :: beginProvision(scheme prov_scheme, wifi_prov_event_handler_t scheme_event_handler, wifi_prov_security_t security, const char * pop, const char *service_name, const char *service_key, uint8_t * uuid)
{
    prov_enable = true;
    bool provisioned = false;
    wifi_prov_mgr_config_t config;
    config.scheme_event_handler = scheme_event_handler;
    config.app_event_handler = {
            .event_cb = prov_event_handler,
            .user_data = NULL
            };

    if(prov_scheme == WIFI_PROV_SCHEME_BLE) {
        config.scheme = wifi_prov_scheme_ble;        
    } else {
    	config.scheme = wifi_prov_scheme_softap;
    }
 
    wifi_prov_mgr_init(config);
    WiFi.mode(WIFI_MODE_AP);
    wifi_prov_mgr_is_provisioned(&provisioned);
    if(provisioned == false) {
        if(prov_scheme == WIFI_PROV_SCHEME_BLE) {
            service_key = NULL;
            if(uuid == NULL) {
                uuid=(uint8_t *)custom_service_uuid;
            }
            wifi_prov_scheme_ble_set_service_uuid(uuid);
        }

        if(service_name == NULL) {
            char service_name_temp[12];
            get_device_service_name(prov_scheme,service_name_temp,sizeof(service_name_temp));
            service_name = (const char *)service_name_temp;
        }

        if(prov_scheme == WIFI_PROV_SCHEME_BLE) {
            log_i("Starting AP using BLE\n service_name : %s\n pop : %s",service_name,pop);

        } else {
            if(service_key == NULL) {
               log_i("Starting AP using SOFTAP\n service_name : %s\n pop : %s",service_name,pop); 
            } else { 
               log_i("Starting AP using SOFTAP\n service_name : %s\n password : %s\n pop : %s",service_name,service_key,pop); 
            }
        }
           
        wifi_prov_mgr_start_provisioning(security,pop,service_name,service_key);

    } else {
        wifi_prov_mgr_deinit();
        WiFi.mode(WIFI_MODE_STA);
        log_i("Aleardy Provisioned, starting Wi-Fi STA");
        log_i("CONNECTING ACCESS POINT CREDENTIALS : "); 
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
        wifi_config_t conf;
        esp_wifi_get_config(WIFI_IF_STA,&conf);
        log_i("SSID : %s\n",conf.sta.ssid);
#endif
    }
}

void WiFiProvClass :: beginBLEProvision(wifi_prov_event_handler_t scheme_event_handler, wifi_prov_security_t security, const char * pop, const char *service_name, const char *service_key, uint8_t * uuid)
{
    prov_enable = true;
    wifi_prov_mgr_config_t config;
    config.scheme_event_handler = scheme_event_handler;
    config.app_event_handler = {
            .event_cb = prov_event_handler,
            .user_data = NULL
            };
    config.scheme = wifi_prov_scheme_ble;        
 
    if (wifi_prov_mgr_init(config) != ESP_OK) {
        log_e("Could not initialize provisioning manager");
    }

    WiFi.mode(WIFI_MODE_AP);
    service_key = NULL;
    if(uuid == NULL) {
        uuid=(uint8_t *)custom_service_uuid;
    }
    if (wifi_prov_scheme_ble_set_service_uuid(uuid) != ESP_OK) {
        log_e("Could not set provisioning service UUID");
    }

    if(service_name == NULL) {
        char service_name_temp[12];
        get_device_service_name(WIFI_PROV_SCHEME_BLE,service_name_temp,sizeof(service_name_temp));
        service_name = (const char *)service_name_temp;
    }

    log_i("Starting AP using BLE\n service_name : %s\n pop : %s",service_name,pop);
    
    if (wifi_prov_mgr_disable_auto_stop(1000) != ESP_OK) {
        log_e("Could not deactivate auto-stop on provisioning manager");
    }

    if (wifi_prov_mgr_start_provisioning(security,pop,service_name,service_key) != ESP_OK) {
        log_e("Could not start provisioning manager");
    }

    log_i("Provisioning manager started");
}

Debug Messages:

ets Jun  8 2016 00:22:57

rst:0x8 (TG1WDT_SYS_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:10864
load:0x40080400,len:6432
entry 0x400806b8
[D][WiFiGeneric.cpp:374] _eventCallback(): Event: 0 - WIFI_READY
[I][WiFiProv.cpp:202] beginBLEProvision(): Starting AP using BLE
 service_name : BLE_XXX
 pop : abcd1234
[D][WiFiGeneric.cpp:374] _eventCallback(): Event: 14 - AP_START
[D][WiFiGeneric.cpp:374] _eventCallback(): Event: 2 - STA_START
[D][WiFiGeneric.cpp:374] _eventCallback(): Event: 2 - STA_START

Provisioning started
Give Credentials of your access point using " Android app "
[I][WiFiProv.cpp:212] beginBLEProvision(): Provisioning manager started
[D][WiFiGeneric.cpp:374] _eventCallback(): Event: 1 - SCAN_DONE

Received Wi-Fi credentials
	SSID : FREELANCERWIFI
	Password : fbbsjjkslofi
[I][WiFiProv.cpp:60] prov_event_handler(): WIFI_PROV_CRED_FAIL: Restarting provisioner
E (51814) wifi_prov_scheme_ble: bt_mem_release of classic BT failed 259
[I][WiFiProv.cpp:202] beginBLEProvision(): Starting AP using BLE
 service_name : BLE_XXX
 pop : abcd1234
aborCtO(R)R UwPaTs  HcEaAlPl:e d at head 40 8x3f5 onec. Expe

ELF file SHA256: 0000000000000000000000000000000000000000000000000000000000000000

Backtrace: 0x4008ef74:0x3ffb2ef0 0x4008f1f1:0x3ffb2f10 0x40083155:0x3ffb2f30 0x40083279:0x3ffb2f60 0x400e060f:0x3ffb2f80 0x400dac11:0x3ffb3240 0x400d5048:0x3ffb3290 0x400951d9:0x3ffb32c0 0x400972c7:0x3ffb32f0 0x40096d73:0x3ffb3310 0x40083731:0x3ffb3330 0x4014466f:0x3ffb3350 0x401c4e21:0x3ffb3370 0x40144a53:0x3ffb33b0 0x401b7fdb:0x3ffb33d0 0x401b7bb5:0x3ffb3420 0x400ea35f:0x3ffb3470 0x400e80b1:0x3ffb3490 0x400e9370:0x3ffb34b0 0x400d2013:0x3ffb35e0 0x400d2092:0x3ffb3680 0x400e7a27:0x3ffb36c0 0x400e84b3:0x3ffb36e0 0x400d16db:0x3ffb3710 0x400d1a9b:0x3ffb3830 0x400941c1:0x3ffb3860

Rebooting...
@sweetymhaiske
Copy link
Contributor

This is currently a limitation of the underlying Wi-Fi provisioning component and the ESP IDF team is looking into how to handle it. Once the support is added there, it will be pulled into arduino

@zenius06
Copy link

Hello,
First thank you so much to do this job, for me I have an issue to compile your example, the compiler don't found : wifi_prov_mgr_event_handler, wifi_prov_mgr_init', wifi_prov_mgr_is_provisioned, wifi_prov_scheme_ble_set_service_uuid
wifi_prov_mgr_start_provisioning, wifi_prov_mgr_deinit. If I look to the "esp-idf" I can found the source code into manager.c, but if I search for arduino I don't find the source code declared into manager.h, could you help me ? In advance thanks again for your work, best regards

@stale
Copy link

stale bot commented Sep 13, 2020

[STALE_SET] This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions.

@stale stale bot added the Status: Stale Issue is stale stage (outdated/stuck) label Sep 13, 2020
@maikeriva
Copy link
Author

@zenius06 You are most likely not using the latest development version of arduino-esp32. Do not use the provided packages by espressif, rather simply use this repository's contents from its master branch.

@stale
Copy link

stale bot commented Sep 15, 2020

[STALE_CLR] This issue has been removed from the stale queue. Please ensure activity to keep it openin the future.

@stale stale bot removed the Status: Stale Issue is stale stage (outdated/stuck) label Sep 15, 2020
@stale
Copy link

stale bot commented Nov 15, 2020

[STALE_SET] This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions.

@stale stale bot added the Status: Stale Issue is stale stage (outdated/stuck) label Nov 15, 2020
@stale
Copy link

stale bot commented Nov 29, 2020

[STALE_DEL] This stale issue has been automatically closed. Thank you for your contributions.

@stale stale bot closed this as completed Nov 29, 2020
@smith-jordan-f
Copy link

smith-jordan-f commented Jan 18, 2021

It seems that the IDF may have some options for 'graceful handling' of bad credentials. See below:

Note

If the device fails to connect with the provided credentials, it won’t accept new credentials anymore, but the provisioning service will keep on running (only to convey failure to the client), until the device is restarted. Upon restart the provisioning state will turn out to be true this time (as credentials will be found in NVS), but device will again fail to connect with those same credentials (unless an AP with the matching credentials somehow does become available). This situation can be fixed by resetting the credentials in NVS or force starting the provisioning service. This has been explained above in Check Provisioning State.

If provisioning state needs to be reset, any of the following approaches may be taken :

    1. the associated part of NVS partition has to be erased manually

    2. main application must implement some logic to call esp_wifi_ APIs for erasing the credentials at runtime

    3. main application must implement some logic to force start the provisioning irrespective of the provisioning state

(https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/provisioning/wifi_provisioning.html)

I opted for option 2, by deleting credentials and restarting the device immediately if the credentials fail:

void SysProvEvent(system_event_t *sys_event,wifi_prov_event_t *prov_event)
{
    case WIFI_PROV_CRED_FAIL: { 
            wifi_prov_sta_fail_reason_t *reason = (wifi_prov_sta_fail_reason_t *)prov_event->event_data;
            Serial.println("\nProvisioning failed!\nPlease reset to factory and retry provisioning\n");
            
            WiFi.disconnect(true, true);
            ESP.restart();

This solution works for handling incorrect credentials but I don't think it's viable for handling situation where router credentials are changed. Option 3 would be my preferred method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Stale Issue is stale stage (outdated/stuck)
Projects
None yet
Development

No branches or pull requests

4 participants