ESP32 WiFi setup over Bluetooth Serial (ESP32 code)
In my other post I wrote about setting up the ESP32 WiFi credentials over BLE. While testing and playing around with different phones, I found that even a quite new (but as well very cheap phone) I have does not support BLE. So I sat back in front of my development IDE and wrote something similar, but this time using Bluetooth Serial as communication.
Most of the code is the same as in ESP32 WiFi setup over BLE so I skip the parts about the WiFi connection and highlight only the small changes in the setup() and loop() functions.
And as always, the full source code is available in my repository!
So we need beside the code for the ESP32 as well an application for Android that can connect over Bluetooth Serial 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 Bluetooth Serial port and listens to command for setup, check or erase the WiFi credentials. The Android app will find the ESP32 BLE server or ESP32 with active Bluetooth Serial:
In above picture you can see on ESP32 with an active Bluetooth Serial port and a second ESP32 with BLE advertising active. 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 4 buttons to read the existing configuration, write a new configuration to the ESP32, to erase any configuration on the device or to restart the ESP32.
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.
As said before, I concentrate here non the code parts for the Bluetooth Serial interface.
The code for the Bluetooth Serial is based on the Bluetooth Serial example from the Arduino ESP32 framework.
Only one include and one global class is required
1 2 3 4 5 |
// Includes for Bluetooth Serial #include "BluetoothSerial.h" // SerialBT class BluetoothSerial SerialBT; |
The initialization of the BT Serial is much simpler than for the BLE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/** * initBTSerial * Initialize Bluetooth Serial * Start BLE server and service advertising * @return <code>bool</code> * true if success * false if error occured */ bool initBTSerial() { if (!SerialBT.begin(apName)) { Serial.println("Failed to start BTSerial"); return false; } Serial.println("BTSerial active. Device name: " + String(apName)); return true; } |
It is just a call to SerialBT.begin with the device name. The creation of apName is shown in my other post.
Then the application just waits in the loop() for an incoming message over the Bluetooth Serial port. If a message arrives, readBTSerial is called
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/** * readBTSerial * read all data from BTSerial receive buffer * parse data for valid WiFi credentials */ void readBTSerial() { uint64_t startTimeOut = millis(); String receivedData; int msgSize = 0; // Read RX buffer into String while (SerialBT.available() != 0) { receivedData += (char)SerialBT.read(); msgSize++; // Check for timeout condition if ((millis()-startTimeOut) >= 5000) break; } SerialBT.flush(); Serial.println("Received message " + receivedData + " over Bluetooth"); |
First we read in all received data from the receiving buffer until the buffer is empty. I have a timeout programmed here that stops the reading after 5 seconds in case something went wrong.
The data is no longer sent as clear text, instead I added a simple encoding (XOR with a key) to make the data unreadable for sniffers
1 2 3 4 5 6 7 8 9 |
// decode the message int keyIndex = 0; for (int index = 0; index < receivedData.length(); index ++) { receivedData[index] = (char) receivedData[index] ^ (char) apName[keyIndex]; keyIndex++; if (keyIndex >= strlen(apName)) keyIndex = 0; } Serial.println("Received message " + receivedData + " over Bluetooth"); |
The key used here is the apName, which is different for each device. Not the most secure way, if you like you can add here a encryption with AES or better. But I wanted to keep the example simple.
In the next part the received data is checked if it is a valid JSON object. If true, the JSON object is searched for SSID’s and passwords
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
/** Json object for incoming data */ JsonObject& jsonIn = jsonBuffer.parseObject(receivedData); if (jsonIn.success()) { if (jsonIn.containsKey("ssidPrim") && jsonIn.containsKey("pwPrim") && jsonIn.containsKey("ssidSec") && jsonIn.containsKey("pwSec")) { ssidPrim = jsonIn["ssidPrim"].as<String>(); pwPrim = jsonIn["pwPrim"].as<String>(); ssidSec = jsonIn["ssidSec"].as<String>(); pwSec = jsonIn["pwSec"].as<String>(); Preferences preferences; preferences.begin("WiFiCred", false); preferences.putString("ssidPrim", ssidPrim); preferences.putString("ssidSec", ssidSec); preferences.putString("pwPrim", pwPrim); preferences.putString("pwSec", pwSec); preferences.putBool("valid", true); preferences.end(); Serial.println("Received over bluetooth:"); Serial.println("primary SSID: "+ssidPrim+" password: "+pwPrim); Serial.println("secondary SSID: "+ssidSec+" password: "+pwSec); connStatusChanged = true; hasCredentials = true; |
I all is found, the new WiFi credentials are stored in the devices preferences.
Next check is if the received data is a command. 3 commands are defined and can be sent from the Android app:
- read – request to receive the current stored WiFi credentials
- erase – to erase the current stored WiFi credentials from the ESP32
- reset – to restart the ESP32
If the command was read, the WiFi credentials are packed into a JSON object
1 2 3 4 5 6 7 8 9 10 11 12 13 |
} else if (jsonIn.containsKey("read")) { // {"read":"true"} Serial.println("BTSerial read request"); String wifiCredentials; jsonBuffer.clear(); /** Json object for outgoing data */ JsonObject& jsonOut = jsonBuffer.createObject(); jsonOut["ssidPrim"] = ssidPrim; jsonOut["pwPrim"] = pwPrim; jsonOut["ssidSec"] = ssidSec; jsonOut["pwSec"] = pwSec; // Convert JSON object into a string jsonOut.printTo(wifiCredentials); |
Then this JSON object is encoded
1 2 3 4 5 6 7 8 9 |
// decode the data int keyIndex = 0; Serial.println("Stored settings: " + wifiCredentials); for (int index = 0; index < wifiCredentials.length(); index ++) { wifiCredentials[index] = (char) wifiCredentials[index] ^ (char) apName[keyIndex]; keyIndex++; if (keyIndex >= strlen(apName)) keyIndex = 0; } Serial.println("Stored encrypted: " + wifiCredentials); |
and send to the requesting Android app
1 2 3 |
delay(2000); SerialBT.print(wifiCredentials); SerialBT.flush(); |
If the command was erase, then all stored credentials in the preferences are deleted
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
} else if (jsonIn.containsKey("erase")) { // {"erase":"true"} Serial.println("Received erase command"); Preferences preferences; preferences.begin("WiFiCred", false); preferences.clear(); preferences.end(); connStatusChanged = true; hasCredentials = false; ssidPrim = ""; pwPrim = ""; ssidSec = ""; pwSec = ""; int err; err=nvs_flash_init(); Serial.println("nvs_flash_init: " + err); err=nvs_flash_erase(); Serial.println("nvs_flash_erase: " + err); |
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.
In case the command was reset, we simply reboot the device
1 2 3 4 5 6 7 8 9 |
} else if (jsonIn.containsKey("reset")) { WiFi.disconnect(); esp_restart(); } } else { Serial.println("Received invalid JSON"); } jsonBuffer.clear(); } |
And at the very end of this routine there is just some cleanup and debug messages.
Different to BLE, the Bluetooth Serial library is not working with callback functions that tell us when data has arrived. So the same way you poll a normal Serial port we have to poll the Bluetooth Serial port in the loop() and act if some data has arrived
1 2 3 4 5 6 7 8 |
void loop() { ... // Check if Data over SerialBT has arrived if (SerialBT.available() != 0) { // Get and parse received data readBTSerial(); } } |
That’s all about WiFi configuration over Bluetooth Serial. If you miss some parts, check out the other post about WiFi configuration over BLE, because here I only talked about the differences between the two.







02:37
Can you please upload the android studio code for this app ? I’ll wait for your reply…
00:32
The complete source code is here: https://bitbucket.org/beegee1962/esp32_wifi_ble_android/src/master/