In this tutorial, you’ll learn how to set up Wi-Fi provisioning via BLE on the ESP32. The Wi-Fi provisioning service allows you to configure Wi-Fi credentials over Bluetooth Low Energy. This is a great option for IoT projects that require Wi-Fi credentials to connect to the internet, without the need to hard-code them on the Arduino sketch while developing.
Table of Contents:
- Wi-Fi Provisioning
- Provisioning over BLE
- Wi-Fi Provisioning App
- ESP32 WiFi Provisioning via BLE – Code
- Instructions for Uploading the Code
- Testing the Code
- How to Apply the Wi-Fi Provisioning to Your Wi-Fi Projects
Wi-Fi Provisioning
Wi-Fi provisioning is the process of connecting a new Wi-Fi device (station) to a Wi-Fi network (access point). In this case, we want to connect an ESP32 to a Wi-Fi network. The provisioning process involves loading the ESP32 with the name of the network (SSID) and password that we want to connect to.
The ESP32 supports Wi-Fi provisioning over SoftAP (access point) or via Bluetooth Low Energy. In this tutorial, we’ll cover Wi-Fi provisioning via Bluetooth.
Provisioning over BLE
To provision the ESP32 via BLE, we need to use another BLE-enabled device, usually a smartphone to connect to the ESP32 via BLE and send the Wi-Fi credentials. We can use an Android or iOS app or a Web Bluetooth app.
Espressif developed Android and iOS apps that support Wi-Fi provisioning for its devices like the ESP32 boards. Alternatively, you can create your own BLE app.
After the provisioning process, the ESP32 can connect to the desired Wi-Fi network with the provided network credentials.
Here’s a quick summary of how it works:
- You have a new device that you want to connect to a known network. In our case, an ESP32.
- You connect to the ESP32 via BLE. On the Wi-Fi provisioning app, you enter and send the SSID and password of the network you want your ESP32 to connect to.
- Finally, the ESP32 connects to the network using the provided credentials. From this moment, it can do any other Wi-Fi-related tasks it needs.
Wi-Fi Provisioning App
To connect and send the credentials to the ESP32 via BLE, we’ll use the Wi-Fi Provisioning App developed by Espressif (alternatively, you can create your own BLE app).
Installing the Wi-Fi Provisioning App
Before proceeding, make sure you install the Wi-Fi provisioning app on your smartphone:
- Wi-Fi Provisioning App for Android devices – (App source code)
- Wi-Fi Provisioning App for iOS devices – (App source code)
ESP32 WiFi Provisioning via BLE – Code
The following code will prepare the ESP32 for Wi-Fi provisioning via BLE. This is the minimal code required to do that. We adapted this code slightly from the official example that also supports WiFi provisioning via an access point.
Copy the following code to the Arduino IDE.
/*********
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/esp32-wi-fi-provisioning-ble-arduino/
Please read README.md file in this folder, or on the web: https://github.com/espressif/arduino-esp32/tree/master/libraries/WiFiProv/examples/WiFiProv
Note: This sketch takes up a lot of space for the app and may not be able to flash with default setting on some chips.
If you see Error like this: "Sketch too big"
In Arduino IDE go to: Tools > Partition scheme > chose anything that has more than 1.4MB APP
- for example "No OTA (2MB APP/2MB SPIFFS)"
*********/
#include "WiFiProv.h"
#include "WiFi.h"
const char * pop = "abcd1234"; // Proof of possession - otherwise called a PIN - string provided by the device, entered by the user in the phone app
const char * service_name = "PROV_123"; // Name of your device (the Espressif apps expects by default device name starting with "Prov_")
const char * service_key = NULL; // Password used for SofAP method (NULL = no password needed)
bool reset_provisioned = true; // When true the library will automatically delete previously provisioned data.
// WARNING: SysProvEvent is called from a separate FreeRTOS task (thread)!
void SysProvEvent(arduino_event_t *sys_event){
switch (sys_event->event_id) {
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
Serial.print("\nConnected IP address : ");
Serial.println(IPAddress(sys_event->event_info.got_ip.ip_info.ip.addr));
break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
Serial.println("\nDisconnected. Connecting to the AP again... ");
break;
case ARDUINO_EVENT_PROV_START:
Serial.println("\nProvisioning started\nGive Credentials of your access point using smartphone app");
break;
case ARDUINO_EVENT_PROV_CRED_RECV: {
Serial.println("\nReceived Wi-Fi credentials");
Serial.print("\tSSID : ");
Serial.println((const char *) sys_event->event_info.prov_cred_recv.ssid);
Serial.print("\tPassword : ");
Serial.println((char const *) sys_event->event_info.prov_cred_recv.password);
break;
}
case ARDUINO_EVENT_PROV_CRED_FAIL: {
Serial.println("\nProvisioning failed!\nPlease reset to factory and retry provisioning\n");
if(sys_event->event_info.prov_fail_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 ARDUINO_EVENT_PROV_CRED_SUCCESS:
Serial.println("\nProvisioning Successful");
break;
case ARDUINO_EVENT_PROV_END:
Serial.println("\nProvisioning Ends");
break;
default:
break;
}
}
void setup() {
Serial.begin(115200);
WiFi.onEvent(SysProvEvent);
Serial.println("Begin Provisioning using BLE");
// 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 };
WiFiProv.beginProvision(WIFI_PROV_SCHEME_BLE, WIFI_PROV_SCHEME_HANDLER_FREE_BTDM, WIFI_PROV_SECURITY_1, pop, service_name, service_key, uuid, reset_provisioned);
log_d("ble qr");
WiFiProv.printQR(service_name, pop, "ble");
}
void loop() {
}
How the Code Works
Let’s take a quick look at how the code works (or skip to the next section).
Including Libraries
First, you include the required libraries, the WiFiProv.h for Wi-Fi provisioning and the WiFi.h for Wi-Fi related functions.
#include "WiFiProv.h"
#include "WiFi.h"
Provisioning Details
Then, we declare some variables to hold details for the provisioning service.
The pop variable is the proof of possession, it’s like a password so that you can connect the smartphone to this specific device. For testing, we’ll use the default value.
const char * pop = "abcd1234"; // Proof of possession - otherwise called a PIN - string provided by the device, entered by the user in the phone app
The service_name is the name of our BLE device. If you’re using the Espressif app, like we’ll do in this tutorial, the service name must start with the prefix PROV_.
const char * service_name = "PROV_123"; // Name of your device (the Espressif apps expects by default device name starting with "Prov_")
The service_key is used when using Wi-Fi Provisioning via Access Point (not included in this guide). Set it to NULL because it’s not needed.
const char * service_key = NULL; // Password used for SofAP method (NULL = no password needed)
The reset_provisioned is a boolean variable indicating whether the library should delete previously provisioned data.
bool reset_provisioned = true; // When true the library will automatically delete previously provisioned data.
Wi-Fi Event Callback Function
In the setup(), set a function that will run when a Wi-Fi event occurs. In this case, it will call the SysProvEvent() function when any Wi-Fi event occurs. Then, on the SysProvEvent() function we’ll decide what happens according to the Wi-Fi event detected.
WiFi.onEvent(SysProvEvent);
You may also like: ESP32 Useful Wi-Fi Library Functions (Arduino IDE)
Starting WiFi Provisioning
Starting the Wi-Fi provisioning process is as easy as using WiFiProv.beginProvision() with the proper arguments.
WiFiProv.beginProvision(WIFI_PROV_SCHEME_BLE, WIFI_PROV_SCHEME_HANDLER_FREE_BTDM, WIFI_PROV_SECURITY_1, pop, service_name, service_key, uuid, reset_provisioned);
Print QR Code
We also print the BLE provision details in a QR code that can be detected by the Wi-Fi Provisioning app.
log_d("ble qr");
WiFiProv.printQR(service_name, pop, "ble");
Detecting WiFi Events
The different Wi-Fi events will be passed to the SysProvEenvent().
ARDUINO_EVENT_WIFI_STA_GOT_IP
When the ESP32 connects to a network and is assigned an IP address, it detects the ARDUINO_EVENT_WIFI_STA_GOT_IP event. When this happens, we print the ESP32 IP address (you can run other tasks as needed, or add a flag variable to indicate that the ESP32 is connected to WiFi and that we can run wifi-related tasks in the loop()).
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
Serial.print("\nConnected IP address : ");
Serial.println(IPAddress(sys_event->event_info.got_ip.ip_info.ip.addr));
break;
ARDUINO_EVENT_PROV_START
The ARDUINO_EVENT_PROV_START event happens when the provisioning process starts. This happens when we call the WiFiProv.beginProvision() function in the setup().
case ARDUINO_EVENT_PROV_START:
Serial.println("\nProvisioning started\nGive Credentials of your access point using smartphone app");
break;
ARDUINO_EVENT_PROV_CRED_RECV
This event is triggered when the board receives the credentials via Wi-Fi provisioning. In this case, we print the received SSID and password. Alternatively, you can save the credentials permanently to a file if that’s useful for your project.
case ARDUINO_EVENT_PROV_CRED_RECV: {
Serial.println("\nReceived Wi-Fi credentials");
Serial.print("\tSSID : ");
Serial.println((const char *) sys_event->event_info.prov_cred_recv.ssid);
Serial.print("\tPassword : ");
Serial.println((char const *) sys_event->event_info.prov_cred_recv.password);
break;
}
ARDUINO_EVENT_PROV_CRED_FAIL
This event will run if the credentials provided via Wi-Fi provisioning will fail to connect to the network.
case ARDUINO_EVENT_PROV_CRED_FAIL: {
Serial.println("\nProvisioning failed!\nPlease reset to factory and retry provisioning\n");
if(sys_event->event_info.prov_fail_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;
}
ARDUINO_EVENT_PROV_CRED_SUCCESS
The ARDUINO_EVEN_PROV_CRED_SUCCESS event will run if the ESP32 successfully connects to the network using the credentials provided via Wi-Fi provisioning.
case ARDUINO_EVENT_PROV_CRED_SUCCESS:
Serial.println("\nProvisioning Successful");
break;
ARDUINO_EVENT_PROV_END
Finally, when the provisioning process ends, the ARDUINO_EVENT_PROV_END is triggered.
case ARDUINO_EVENT_PROV_END:
Serial.println("\nProvisioning Ends");
break;
Instructions for Uploading the Code
This code takes up a lot of space and may not be able to flash with the default settings on some chips.
If you see an Error like this: “Sketch too big” during the uploading process, in Arduino IDE go to Tools > Partition scheme > choose anything that has more than 1.4MB APP, for example: “Huge APP (3MB No OTA/1MB SPIFFS“.
If you don’t have the partition scheme option on your Tools menu, you need to select a different ESP32 board model—for example, the ESP32 Wrover Module.
Testing the Code
After uploading the code to the ESP32, open the Serial Monitor at a baud rate of 115200. Press the ESP32 RST button, so it starts running the code.
Note: you may get a PSRAM error. You can ignore it.
You’ll get something similar on your Serial Monitor.
Begin Provisioning using BLE
Provisioning started
Give Credentials of your access point using smartphone app
Now, open the Espressif WiFi Provisioning App on your smartphone and click on Provision Device. It has the option to scan the QR code, but I couldn’t make it work. Click on “I don’t have a QR code“.
It will list all Bluetooth Devices within its range with the “PROV_” prefix. Our code sets the ESP32 as a BLE Device called “PROV_123“—click on that device.
Then, you need to enter the proof of possession—it must be the same used in the code. We’re using the default values, so it will be abcd1234. After, it will list all Wi-Fi networks within its range (if it doesn’t list your network, you may need to enter it manually by clicking on “Join Other Network”).
Finally, enter the password for your Wi-Fi network and click Connect. The Wi-Fi credentials should be sent to the ESP32 after a few seconds.
On the Serial monitor, you can see that the ESP32 received the Wi-Fi credentials and connected successfully to the network (it printed its IP address on the network it connected to).
That’s it. Wi-Fi provisioning was successfully implemented on the ESP32.
How to Apply the Wi-Fi Provisioning to Your Wi-Fi Projects
To add this feature to your projects that use Wi-Fi, I suggest creating a global variable that indicates whether the ESP32 is already connected to the internet. That variable will change to true when the ARDUINO_EVENT_WIFI_STA_GOT_IP is triggered.
Then, in the loop(), check the state of that variable. If it’s true, run your Wi-Fi-related tasks. If not, simply wait for the board to establish a connection.
We’ll create a web server example with this feature soon, so stay tuned.
Wrapping Up
In this tutorial, we’ve shown you how to add Wi-Fi Provisioning via BLE to your ESP32 devices. Using this feature is simple and easy thanks to the WiFiProv library and the Espressif Provisioning app.
This is a great option for IoT projects that require Wi-Fi credentials to connect to the internet, without the need to hard-code them while developing, which makes the project much more versatile.
We’ll create more tutorials about this subject soon, so stay tuned. You may also like reading:
- ESP32: Create a Wi-Fi Manager (AsyncWebServer library)
- ESP32 WiFiMulti: Connect to the Strongest Wi-Fi Network (from a list of networks)
- ESP32 Useful Wi-Fi Library Functions (Arduino IDE)
We hope you’ve found this tutorial useful. Learn more about the ESP32 with our resources.
- Learn ESP32 with Arduino IDE (eBook)
- SMART HOME with Raspberry Pi, ESP32, and ESP8266
- Build Web Servers with ESP32 and ESP8266 (eBook)
- Free ESP32 Projects and Tutorials…
What WiFiProv.h library is needed? WiFiProvisioner of Santari Lindfors? If other please provide URL.
Hi Hans,
In the “Wrapping up” section there is mention of the “WiFiProv library” that was provided by Espressif. I have not tried this process yet so I can only work from the information I read above. Let us know how you went.
Hi.
It is included by default.
You don’t need to install any library.
Just make sure the ESP32 boards are updated in the Arduino IDE.
Regards,
Sara
/*
Please read README.md file in this folder, or on the web:
https://github.com/espressif/arduino-esp32/tree/master/libraries/WiFiProv/examples/WiFiProv
Note: This sketch takes up a lot of space for the app and may not be able to flash with default setting on some chips.
If you see Error like this: “Sketch too big”
In Arduino IDE go to: Tools > Partition scheme > chose anything that has more than 1.4MB APP
– for example “No OTA (2MB APP/2MB SPIFFS)”
*/
Hi.
You don’t need to install any library.
Just make sure you have the ESP32 boards updated in your Arduino IDE.
Regards,
Sara
Good Morning, I have this errors messages …..
C:\Users\Efrem\AppData\Local\Temp.arduinoIDE-unsaved2024311-8452-11tz2gj.7kaj\sketch_apr11a\sketch_apr11a.ino:21:19: error: ‘arduino_event_t’ was not declared in this scope
C:\Users\Efrem\AppData\Local\Temp.arduinoIDE-unsaved2024311-8452-11tz2gj.7kaj\sketch_apr11a\sketch_apr11a.ino:21:36: error: ‘sys_event’ was not declared in this scope
void SysProvEvent(arduino_event_t *sys_event){
^
C:\Users\Efrem\AppData\Local\Temp.arduinoIDE-unsaved2024311-8452-11tz2gj.7kaj\sketch_apr11a\sketch_apr11a.ino:21:19: error: variable or field ‘SysProvEvent’ declared void
void SysProvEvent(arduino_event_t *sys_event){
^
C:\Users\Efrem\AppData\Local\Temp.arduinoIDE-unsaved2024311-8452-11tz2gj.7kaj\sketch_apr11a\sketch_apr11a.ino:21:19: error: ‘arduino_event_t’ was not declared in this scope
C:\Users\Efrem\AppData\Local\Temp.arduinoIDE-unsaved2024311-8452-11tz2gj.7kaj\sketch_apr11a\sketch_apr11a.ino:21:36: error: ‘sys_event’ was not declared in this scope
void SysProvEvent(arduino_event_t *sys_event){
Hi.
What version of the ESP32 do you have installed in the arduino IDE?
Go to Tools > Boards > Boards Manager, search for ESP32 and check the version. You may need to update.
Regards,
Sara
Now it is ok, I have the 1.0.5 version nd the last is 2.0.15 …..Compile ok !!
Hi, the problem in using the Espressif app is that it only supports provisioning using a dynamic IP address. What can I do if I also want to set a static IP address during the provisioning process? If the app does not support this, you wrote that I can develop my own process/app. Are there any instructions on how to do that? Thanks!
Hi.
No. At the moment, we don’t have any instructions for that specific application.
But, we have a tutorial for a web BLE app that you can use as inspiration: https://randomnerdtutorials.com/esp32-web-bluetooth/
Regards,
Sara
You can configure your DHCP server to always give the same IP address to a specific MAC, in this case the MAC of your ESP board
Awesome tutorial. I like this idea over storing a boot config on sd card. Definitely will cut cost and boost realibity.
Very interesting but the memory use is a bit of a downer as it doesnt leave much for other, possibly more interesting stuff, like OTA or anything else a code could do. Long time ago I wrote a code to set WiFi credentials via Bluetooth Serial with a simple general phone Bluetooth app. I’ll see if I can dig that up again.
Yes of course, you are right!
What shell I do with this great feature, when I can not write an application, because less memory?
Hi, The QR code works if you copy and paste it into a notepad ( so the formatting is correct). Also you don’t need the PROV_.. you can just remove that from the prefix at the top in the provisioning app if you don’t want that.
Will this also work with the ESP8266?
Hi.
No.
The ESP8266 doesn’t support BLE.
Regards,
Sara
This ESP32 Wi-Fi provisioning tutorial with BLE is a game-changer for IoT projects!
Hi Sara I’m getting to the part where it says “Provisioning Successful” but then I get this:
E (52249) wifi_prov_scheme_ble: bt_mem_release of BTDM failed 259
-> Provisioning Ends
and the wifi connection fails
8*(