ESP32 WiFi setup over BLE (ESP32 code)

Most small applications that we write have the WiFi credentials integrated in the code. But sometimes you do not want to have your WiFi credentials in the source code, specially if it is open source and maybe accessible as a repository on Github or Bitbucket. Having the WiFi credentials in the code is as well not practical if you sell your devices to others that need to setup the WiFi credentials according to their local network.

There are several solutions available like Espressifs Smartconfig or WiFiManager.

My approach is not to use WiFi to configure the WiFi credentials of an ESP32 module, but to do it over Bluetooth Low Energy (BLE).

To achieve this, the code includes a BLE server that is advertising a custom service. An Android phone or tablet (sorry, I cannot write apps for Iphones) then can connect to this BLE server and the user can setup the WiFi configuration of the phone.

So we need beside the code for the ESP32 as well an application for Android that can connect over BLE to the module. I wrote this small Android application and it is available for download from

  • Google Play for automatic installation
  • APKfiles if you do not have Google Play on your device
  • from here for manual installation
  • or you can build the Android app by yourself from the sources in my Bitbucket repo.

How does it work?

When the ESP32 is powered up it starts a BLE server and advertises a custom service to setup, check or erase the WiFi credentials. The Android app will find the ESP32 BLE server or ESP32 with active Bluetooth Serial:

By clicking on the device name the configuration screen opens. If there are already some WiFi credentials stored on the ESP32, they will be read from the device and displayed:

The configuration screen has 3 buttons to read the existing configuration, write a new configuration to the ESP32 or to erase any configuration on the device.

In case you want to use two AP settings, the switch button “Enable two AP” will show the edit boxes for the second AP:

The SSID and password for the WiFi APs should be entered in the text edit fields. Then a push on the “Send WiFi config” will send the new WiFi credentials to the ESP32.

The small text field at the bottom will show some debug messages during the process.

The source code

This post concentrates on the software that has to run on the ESP32. The complete source code is available in my Bitbucket repo.

I wrote the code with PlatformIO on Visual Studio Code, but as the code is all in one file, you can simply copy the code into a .ino file and use ArduinoIDE to test it.

The example needs to be able to encode and decode JSON objects and my favorite library for this is bblanchon‘s ArduinoJSON library. You need to install this library in your ArduinoIDE or with PlatformIO. Beside of that we need only three standard Arduino includes:

The Preferences library include is needed because I am using the preferences to store the WiFi credentials on the ESP32.

For the BLE Server I used the code from my ESP32 BLE server example and modified it for this application. We use the same include files to get access to BLE:

One difference is that instead of using “official” BLE services and characteristics I defined a custom service UUID and a custom characteristic UUID. This is to make sure that there is no collision or misunderstanding with existing BLE devices:

If you want to use different UUID’s, please note two things:

  • Do not just write some numbers and letters into the UUID but instead use an online UUID creator like Online UUID Generator
  • You need to change the Android source code and recompile it as well, because the UUIDs are hardcoded in the Android application (I put this on my TODO list already)

Next we need to define some Strings to hold the SSID’s and PW’s and pointers to the BLE server, service, advertisers and characteristic

As you can see I defined a primary and secondary set of credentials. This is because I use here the possibility to connect to one of two allowed WiFi AP’s as described in ESP8266 – Switch automatically between 2 access points. If you do not need this, you can simply change the code to delete the references to the secondary AP.

As shown in the BLE Server tutorial, we need to define some callbacks for the BLE events. The first set of callbacks is for client connection and disconnection events. If a client connects the BLE advertising of the service stops automatically. So in case a client disconnects, we start the advertising here again.

The next set of callbacks is for characteristic read and write events.

The characteristic write events receives the WiFi credentials from the Android app. The data is decoded with a simple method (XOR with a key) to make the data unreadable for sniffers. First step is to encode the received data.

The credentials are sent within a JSON object that makes it easy to parse through the data and extract the SSID names and passwords. The JSON object is in the form

The code checks that all keys are in the JSON object, extracts them and saves the values in the ESP32 preferences.

There is as well the possibility to delete the previously stored WiFi credentials by a simple JSON object containing the key “erase”.

Here is the code part for the erase:

The last part of the erase procedure is actually optional and you might want to remove it. By erasing the complete NVS partition all stored preferences are deleted, that includes as well Bluetooth pairing informations.

And there is as well the possibility to restart the ESP32

with this code part:

And the rest is some cleanup and debug output.

The characteristic read event is used to report back the current WiFi credentials to the Android app. The stored values for the two AP’s is converted into an JSON object, then decoded (XOR with a key) and then sent back to the Android App.

After defining the callbacks come the code to initialize the BLE server.

First the BLE device is initializes, then we start the BLE server and assign the callbacks for connection and reconnection of clients.

After that BLE service and the characteristic for the WiFi credentials is created.

And then the server and the advertising is started:

setup() and loop() code blocks

Inside the setup() function the first thing is to create a unique device name to distinguish between several ESP32 modules. This unique device name is created by using the MAC address of the module in the function createName() and stored in the global variable apName.

After the unique device name is created, the serial port is initialized and some debug info is send out

Next the preferences are checked to see if there are any WiFi credentials are stored already. If credentials are found, they are stored in the global variables ssidPrim, pwPrim, ssidSec and pwSec. The flag hasCredentials is set to TRUE if credentials were found or FALSE if no credentials were saved.

The next step initializes the BLE server

Then, if WiFi credentials were found, the connection to the WiFi AP is started.

The loop() function is doing nothing here than checking if a WiFi connection status change has occured. The connStatusChanged flag can be set true when

  • the BLE server has received WiFi credentials
  • a WiFi connection has been established
  • a WiFi connection has been lost

If a connection status change has been detected and the status isConnected shows that the device is connected to a WiFi network, the WiFi AP info are sent for debugging over the Serial port.

else a check for WiFi credentials is done and if true an initial connect (in case the device got new WiFi credentials) or a reconnect to WiFi is initiated

Handle WiFi connections

I use the same technique as descibed in ESP8266 – Switch automatically between 2 access points for the ESP32. Two valid AP credentials are stored in the device. The function scanWiFi() searches the available WiFi AP’s if one or two of them match the stored AP’s. If no matching AP is found, the function returns false.

Which of the stored AP’s should be used depends then on the signal strength. If there is the possibility to connect to either of the stored AP’s, the one with the stronger signal is selected.

Based on the results of scanWiFi() the function connectWiFi() then tries to connect to an AP. But before connecting, two WiFi event callbacks are initiated that will alarm the loop() if a connection change has occured.

gotIP() is called after the WiFi was assigned an IP address by the AP

lostCon() is called if the WiFi got disconnected

After initializing the callbacks, connectWiFi() checks which AP should be used and initiates the connection to this AP





  • Rico

    hi there,
    very interesting post , thanks for sharing your devs 🙂
    this technics sounds really usefull yet I have one question regarding BLE security :

    how do you handle the fact that WIFI credentials are sent in clear via the BLE charateristics?
    Am I wrong by saying that anyone connecting with a BLE app like nrfBLE can get the values too ?

    • beegee1962

      Thanks for your positive feedback and pointing out the security flaw.

      In the next version which was released today I am actually encoding the credentials before sending them. It is a simple encoding, but makes the data unreadable for sniffers or apps like nrfBLE.

      I will add today as well a new example where I use Bluetooth Serial instead of BLE. This has the encoding as well.

  • ud

    Tried to compile your code on arduino ide 1.8.5 (latest version) with included esp32 libraries. However there seems to be a problem with the preferences.h, see error listed below during compile.
    When I used #include Arduino could not find the library, even though it says the library is installed.
    When I used the #include “….. complete path of install/preferences.h” the library was indeed found, but the linker seems to have a problem.
    Can you please let me know if there is a work around..?
    Thank you for a great write up and sharing,
    ———- error messages below ———

    —- With include —-

    C:\esp32\code\arduino\esp32wifible\esp32wifible.ino:18:25: fatal error: preferences.h: No such file or directory

    compilation terminated

    —- With #include “C:\Program Files (x86)\Arduino\hardware\espressif\esp32\libraries\Preferences\src\preferences.h”

    sketch\esp32wifible.ino.cpp.o:(.literal._ZN17MyCallbackHandler7onWriteEP17BLECharacteristic[MyCallbackHandler::onWrite(BLECharacteristic*)]+0x38): undefined reference to Preferences::Preferences()'

    sketch\esp32wifible.ino.cpp.o:(.literal._ZN17MyCallbackHandler7onWriteEP17BLECharacteristic[MyCallbackHandler::onWrite(BLECharacteristic*)]+0x3c): undefined reference to Preferences::begin(char const*, bool)’

    sketch\esp32wifible.ino.cpp.o:(.literal._ZN17MyCallbackHandler7onWriteEP17BLECharacteristic[MyCallbackHandler::onWrite(BLECharacteristic*)]+0x40): undefined reference to Preferences::putString(char const*, String)'

    sketch\esp32wifible.ino.cpp.o:(.literal._ZN17MyCallbackHandler7onWriteEP17BLECharacteristic[MyCallbackHandler::onWrite(BLECharacteristic*)]+0x44): undefined reference to Preferences::putBool(char const*, bool)’

    sketch\esp32wifible.ino.cpp.o:(.literal._ZN17MyCallbackHandler7onWriteEP17BLECharacteristic[MyCallbackHandler::onWrite(BLECharacteristic*)]+0x48): undefined reference to Preferences::end()'

    sketch\esp32wifible.ino.cpp.o:(.literal._ZN17MyCallbackHandler7onWriteEP17BLECharacteristic[MyCallbackHandler::onWrite(BLECharacteristic*)]+0x4c): undefined reference to Preferences::clear()’

    sketch\esp32wifible.ino.cpp.o:(.literal._ZN17MyCallbackHandler7onWriteEP17BLECharacteristic[MyCallbackHandler::onWrite(BLECharacteristic*)]+0x50): undefined reference to Preferences::~Preferences()'

    sketch\esp32wifible.ino.cpp.o:(.literal._Z5setupv+0x1c): undefined reference to Preferences::getBool(char const*, bool)’

    sketch\esp32wifible.ino.cpp.o:(.literal._Z5setupv+0x20): undefined reference to Preferences::getString(char const*, String)'

    sketch\esp32wifible.ino.cpp.o: In function MyCallbackHandler::onWrite(BLECharacteristic*)’:

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:109: undefined reference to Preferences::Preferences()'

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:110: undefined reference to Preferences::begin(char const*, bool)’

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:111: undefined reference to Preferences::putString(char const*, String)'

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:112: undefined reference to Preferences::putString(char const*, String)’

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:113: undefined reference to Preferences::putString(char const*, String)'

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:114: undefined reference to Preferences::putString(char const*, String)’

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:115: undefined reference to Preferences::putBool(char const*, bool)'

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:116: undefined reference to Preferences::end()’

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:125: undefined reference to Preferences::Preferences()'

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:126: undefined reference to Preferences::begin(char const*, bool)’

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:127: undefined reference to Preferences::clear()'

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:128: undefined reference to Preferences::end()’

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:125: undefined reference to Preferences::~Preferences()'

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:125: undefined reference to Preferences::~Preferences()’

    sketch\esp32wifible.ino.cpp.o: In function setup()':

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:324: undefined reference to Preferences::Preferences()’

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:325: undefined reference to Preferences::begin(char const*, bool)'

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:326: undefined reference to Preferences::getBool(char const*, bool)’

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:328: undefined reference to Preferences::getString(char const*, String)'

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:329: undefined reference to Preferences::getString(char const*, String)’

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:330: undefined reference to Preferences::getString(char const*, String)'

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:331: undefined reference to Preferences::getString(char const*, String)’

    Multiple libraries were found for “WiFi.h”
    Used: C:\Program Files (x86)\Arduino\hardware\espressif\esp32\libraries\WiFi
    Not used: C:\Program Files (x86)\Arduino\libraries\WiFi
    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:347: undefined reference to Preferences::end()'

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:324: undefined reference to Preferences::~Preferences()’

    C:\esp32\code\arduino\esp32wifible/esp32wifible.ino:324: undefined reference to `Preferences::~Preferences()’

    collect2.exe: error: ld returned 1 exit status

    exit status 1
    Error compiling for board ESP32 Dev Module.

    • ud

      OK, please delete my previous post. I found that the following works:
      For compiling in Arduino ide:
      #include preferences.h
      # include Preferences.h

      Note: the left and right brackets have been left out of this text line as the web page appears to delete what ever is between them

      Thank you

      • beegee1962

        Hi UD
        Are you running Arduino IDE on Linux? That might explain the problem. I am on a Windows 7 platform and there is no case sensitivity for the library names.
        But it is a good finding and I will update the code. Thank you!

        • I’m on Windows and had the same issue with “preferences.h”. Capitalizing it to “Preferences.h” fixed the issue. The previous commenter’s large comment could be massively trimmed for readability as well.

          • beegee1962

            Code on Github and explanation here has been corrected. Sorry for causing the issue with uppercase/lowercase mismatch.

Leave a Reply

Your email address will not be published. Required fields are marked *

Free Link Directory