ESP32 Set Up Wi-Fi Connection Using Bluetooth

ESP32-Connected-IP-Address.png

Wi-Fi connection manager using Bluetooth serial, the Preferences library and an enum state machine.

Sometimes you need to remotely connect to an ESP32 over Wi-Fi but you don’t know the IP address or the ESP32 reconnects with a new IP address each time.

The easiest way to find the IP address for an inaccessible board is to transmit it over Bluetooth serial to a mobile phone. However Bluetooth and Wi-Fi don’t co-exist very well on the ESP32 because they share the same radio system. The Bluetooth connection needs to be closed as soon as the Wi-Fi connection starts to be used for data.

In this tutorial and example code you can see how to use use Bluetooth serial to read the IP address and then close the connection so only Wi-Fi is using the radio.

This demonstration application uses an ESP32 based camera board but it can be adapted for other projects where you need to access an ESP32 over Wi-Fi.

There are three parts to the tutorial – uploading the sketch, pairing your phone with the ESP32 and connecting using a serial Bluetooth application.

Upload the Sketch to the ESP32

Copy the code from here https://github.com/robotzero1/esp32-bluewifi into a new Sketch and upload it to your ESP32 board. If you are using the ESP32-CAM, remember to disconnect pin 0 and press reset.

Open the serial monitor so you can check the progress of the steps below.

Connecting a Mobile Phone to the ESP32

I have Android 9 but other mobiles should be very similar.

Go to Settings and select Connected devices:
Android Bluetooth Settings Link

Select Pair new device:
Pair New Device

Select robot01 from the list of Available devices:
Available Devices

Confirm pairing:
Pair Confirm

You should see the device in Currently connected:
Bluetooth Device Connected

Using the Serial Terminal

Install the Serial Bluetooth Terminal app from the Play Store – https://play.google.com/store/apps/details?id=de.kai_morich.serial_bluetooth_terminal&hl=en

Open Serial Bluetooth Terminal and select ‘Devices’ from the menu:
Serial Bluetooth Terminal Devices

Select robot01 from the list to start the Serial Terminal connection:
Choose Bluetooth Device

If this is the first time the ESP32 has connected to this Wi-Fi network or the password has changed then you will see the following output.

Serial terminal connecting to ESP32:
Bluetooth Serial Terminal Connection

ESP32 scanning for Wi-Fi networks:
Bluetooth Serial Terminal WiFi Scan

Select the number for your network SSID:
Bluetooth Serial Terminal SSID

Enter the password for this network:
Bluetooth Serial Terminal Pass

When the ESP32 connects to the Wi-Fi network, the ESP32 IP address is shown and the Bluetooth is disconnected:
Bluetooth Serial Terminal Wi-Fi IP address

If the ESP32 already has the correct connection credentials then just the IP address of the ESP32 will be displayed:
Bluetooth Serial Show IP Address

How does this work?

A few important parts of the code are explained below:

The code uses an ‘enum state machine’. An enum can be thought of as a variable with a fixed number of values such as SCAN_START, SCAN_COMPLETE, SSID_ENTERED, PASS_ENTERED, LOGIN_FAILED

These values are used in a switch statement in the main loop. Functions run or variable values are set based on the current enum value:

    case SCAN_START:
      SerialBT.println("Scanning Wi-Fi networks");
      Serial.println("Scanning Wi-Fi networks");
      scan_wifi_networks();
      SerialBT.println("Please enter the number for your Wi-Fi");
      wifi_stage = SCAN_COMPLETE;
      break;

    case SSID_ENTERED:
      SerialBT.println("Please enter your Wi-Fi password");
      Serial.println("Please enter your Wi-Fi password");
      wifi_stage = WAIT_PASS;
      break;

    case PASS_ENTERED:
      SerialBT.println("Please wait for Wi-Fi connection...");
      Serial.println("Please wait for Wi_Fi connection...");
      wifi_stage = WAIT_CONNECT;
      preferences.putString("pref_ssid", client_wifi_ssid);
      preferences.putString("pref_pass", client_wifi_password);
      if (init_wifi()) { // Connected to WiFi
        connected_string = "ESP32 IP: ";
        connected_string = connected_string + WiFi.localIP().toString();
        SerialBT.println(connected_string);
        Serial.println(connected_string);
        bluetooth_disconnect = true;
      } else { // try again
        wifi_stage = LOGIN_FAILED;
      }
      break;

    case LOGIN_FAILED:
      SerialBT.println("Wi-Fi connection failed");
      Serial.println("Wi-Fi connection failed");
      delay(2000);
      wifi_stage = SCAN_START;
      break;

The Sketch uses two Bluetooth callbacks depending on whether Wi-Fi has connected or not:

  if (!init_wifi()) { // Connect to Wi-Fi fails
    SerialBT.register_callback(callback);
  } else {
    SerialBT.register_callback(callback_show_ip);
  }

If the ESP32 cannot connect to Wi-Fi then the first callback initialised. This listens for Bluetooth data sent to the ESP32. Depending on the type of data and the current value of the enum, various variables are set and functions run:

void callback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
{
  if (event == ESP_SPP_SRV_OPEN_EVT) {
    wifi_stage = SCAN_START;
  }

  if (event == ESP_SPP_DATA_IND_EVT && wifi_stage == SCAN_COMPLETE) { // data from phone is SSID
    int client_wifi_ssid_id = SerialBT.readString().toInt();
    client_wifi_ssid = ssids_array[client_wifi_ssid_id];
    wifi_stage = SSID_ENTERED;
  }

  if (event == ESP_SPP_DATA_IND_EVT && wifi_stage == WAIT_PASS) { // data from phone is password
    client_wifi_password = SerialBT.readString();
    client_wifi_password.trim();
    wifi_stage = PASS_ENTERED;
  }

}

Alternatively, if the ESP32 has successfully connected to Wi-Fi then a second callback initialises and waits for input from the Bluetooth connection:

void callback_show_ip(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
{
  if (event == ESP_SPP_SRV_OPEN_EVT) {
    SerialBT.print("ESP32 IP: ");
    SerialBT.println(WiFi.localIP());
    bluetooth_disconnect = true;
  }
}

The final important thing to note in the code is that once the Wi-Fi IP address is known to the user then the Bluetooth should be closed. This happens either when the IP address has been displayed on the mobile phone or when the user connects to the IP in a browser. One example is shown in the code above. Another is shown below when the camera is connected over a websocket:

  if (socket_server.poll()) {
    disconnect_bluetooth();
    ...
  }

Video Demonstration

A demonstration of connecting via Bluetooth and setting the Wi-Fi access details is shown below.

22 Replies to “ESP32 Set Up Wi-Fi Connection Using Bluetooth”

  1. Donald Sweeney says:

    This saved me some time. It solved my problem. Coffee coming your way.

    My application is for a limited, amateur audience. They will find the cell phone serial terminal app somewhat awkward or confusing. Is there a more streamlined, configurable and/or simplified interface for the cell phone?

    1. WordBot says:

      I’ve not seen anything more simple. There’s lots of serial bluetooth apps but the ones I tried all worked the same. Unless you can find a serial application that you can put some sort of skin on to make it less nerdy-looking your probably need to write an app with a wizard for people to follow.

  2. Alex says:

    Hello! please help, what problem?
    Booting…
    [E][Preferences.cpp:437] getString(): nvs_get_str len fail: pref_ssid NOT_FOUND
    [E][Preferences.cpp:437] getString(): nvs_get_str len fail: pref_pass NOT_FOUND
    [D][WiFiGeneric.cpp:337] _eventCallback(): Event: 0 – WIFI_READY
    [D][WiFiGeneric.cpp:337] _eventCallback(): Event: 2 – STA_START
    [E][WiFiSTA.cpp:124] begin(): SSID too long or missing!
    …………………[D][WiFiGeneric.cpp:337] _eventCallback(): Event: 3 – STA_STOP

    1. WordBot says:

      Hi, Can you connect to the device via Bluetooth?

  3. Alex says:

    No, Bluetooth don`t work

    1. WordBot says:

      Can you see ‘robot01’ as a device under Bluetooth on your phone?

  4. Alex says:

    No don`t see i try different phones and Bluetooth scaner

    1. WordBot says:

      Did you uncomment/comment for the correct camera?
      #define CAMERA_MODEL_ESP_EYE
      //#define CAMERA_MODEL_AI_THINKER
      Also in the IDE there’s some examples to test in the File > Examples (ESP32) > Bluetooth Serial you can test with.

  5. Alex says:

    Yes, i tested example SeialToSerialBT and work good, connected

    [D][esp32-hal-psram.c:47] psramInit(): PSRAM enabled
    [I][BluetoothSerial.cpp:510] _init_bt(): device name set
    The device started, now you can pair it with bluetooth!
    [I][BluetoothSerial.cpp:225] esp_spp_cb(): ESP_SPP_INIT_EVT
    [I][BluetoothSerial.cpp:228] esp_spp_cb(): ESP_SPP_INIT_EVT: slave: start
    [I][BluetoothSerial.cpp:310] esp_spp_cb(): ESP_SPP_START_EVT
    [I][BluetoothSerial.cpp:235] esp_spp_cb(): ESP_SPP_SRV_OPEN_EVT
    test send
    [I][BluetoothSerial.cpp:247] esp_spp_cb(): ESP_SPP_CLOSE_EVT

    In you sketch i define CAMERA_MODEL_AI_THINKER, my board is ESP32-CAM on module Expressif. No errors of camera init. Strange.

    1. WordBot says:

      Which partition scheme are you using? I’ve tried here and it seems fine…

      [E][Preferences.cpp:437] getString(): nvs_get_str len fail: pref_ssid NOT_FOUND
      [E][Preferences.cpp:437] getString(): nvs_get_str len fail: pref_pass NOT_FOUND
      [D][WiFiGeneric.cpp:337] _eventCallback(): Event: 0 – WIFI_READY
      [D][WiFiGeneric.cpp:337] _eventCallback(): Event: 2 – STA_START
      [E][WiFiSTA.cpp:124] begin(): SSID too long or missing!
      ………………..[D][WiFiGeneric.cpp:337] _eventCallback(): Event: 3 – STA_STOP
      [I][BluetoothSerial.cpp:510] _init_bt(): device name set
      [I][BluetoothSerial.cpp:225] esp_spp_cb(): ESP_SPP_INIT_EVT
      [I][BluetoothSerial.cpp:228] esp_spp_cb(): ESP_SPP_INIT_EVT: slave: start
      [I][BluetoothSerial.cpp:310] esp_spp_cb(): ESP_SPP_START_EVT
      httpd_start

    1. WordBot says:

      What version of the ESP32 hardware libraries? Maybe try putting Serial.println() at every line to see where it fails?

  6. retro says:

    I am getting the below error. Any clue what could be wrong?
    ‘main_html’ was not declared in this scope

    1. WordBot says:

      Did you copy the other two camera_ files into the folder with the ino file?

  7. Alex says:

    Hello!
    ESP32 library 1.0.4. I try another board esp32-cam and it`s fail.
    Very bad in programing i learn )
    Put Serial.println, I think problem start here

    start_wifi_millis = millis();
    WiFi.begin(pref_ssid, pref_pass);
    while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(“.”);
    if (millis() – start_wifi_millis > wifi_timeout) {
    WiFi.disconnect(true, true);
    return false;
    }
    }

    1. WordBot says:

      How odd! What should happen is this part of the code:
      if (!init_wifi()) { // Connect to Wi-Fi fails
      SerialBT.register_callback(callback);
      } else {
      SerialBT.register_callback(callback_show_ip);
      }

      Tries to connect to the Wi-Fi using this function:

      bool init_wifi()
      {
      String temp_pref_ssid = preferences.getString("pref_ssid");
      String temp_pref_pass = preferences.getString("pref_pass");
      pref_ssid = temp_pref_ssid.c_str();
      pref_pass = temp_pref_pass.c_str();

      Serial.println(pref_ssid);
      Serial.println(pref_pass);

      WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);

      start_wifi_millis = millis();
      WiFi.begin(pref_ssid, pref_pass);
      while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
      if (millis() - start_wifi_millis > wifi_timeout) {
      WiFi.disconnect(true, true);
      return false;
      }
      }
      return true;
      }

      When that Wi-Fi function fails to connect it should return ‘false’ and the second callback is registered and then setup continues. Can you try putting return false like this:
      bool init_wifi()
      {
      return false

      so it doesn’t even try to connect. Don’t do anything with the Bluetooth yet if it connects because if you enter the SSID etc it will be difficult to find why it doesn’t work.

  8. Alex says:

    Tested, cycle reboot

    rst:0x1 (POWERON_RESET),boot:0x13 (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:2
    load:0x3fff0018,len:4
    load:0x3fff001c,len:1216
    ho 0 tail 12 room 4
    load:0x40078000,len:9720
    ho 0 tail 12 room 4
    load:0x40080400,len:6364
    entry 0x400806b8
    [D][esp32-hal-psram.c:47] psramInit(): PSRAM enabled
    Booting…
    [I][BluetoothSerial.cpp:510] _init_bt(): device name set
    /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/queue.c:1442 (xQueueGenericReceive)- assert failed!
    abort() was called at PC 0x4009255d on core 1

    Backtrace: 0x40096180:0x3ffce7b0 0x400963b1:0x3ffce7d0 0x4009255d:0x3ffce7f0 0x4013af4e:0x3ffce830 0x4013b22e:0x3ffce850 0x40129800:0x3ffce870 0x40129869:0x3ffce890 0x4012550a:0x3ffce8b0 0x40124004:0x3ffce8d0 0x40128820:0x3ffce910 0x400e7bf7:0x3ffce930 0x400e7e7d:0x3ffce990 0x400d1c9f:0x3ffce9c0 0x400d1df7:0x3ffcea20 0x400d910f:0x3ffcead0 0x400928c9:0x3ffceaf0

    Rebooting…

    1. WordBot says:

      Can you install this to get more info from the backtrace: https://github.com/me-no-dev/EspExceptionDecoder

  9. Alex says:

    I thought I knew a little, how wrong I was 🙂

    Decoding stack results
    0x40096180: invoke_abort at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp32/panic.c line 155
    0x400963b1: abort at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp32/panic.c line 170
    0x4009255d: xQueueGenericReceive at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/queue.c line 1442
    0x4013af4e: sys_mutex_lock at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/lwip/port/esp32/freertos/sys_arch.c line 78
    0x4013b22e: sys_arch_protect at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/lwip/port/esp32/freertos/sys_arch.c line 469
    0x40129800: do_memp_malloc_pool at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/lwip/lwip/src/core/memp.c line 302
    0x40129869: memp_malloc at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/lwip/lwip/src/core/memp.c line 398
    0x4012550a: netconn_alloc at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/lwip/lwip/src/api/api_msg.c line 742
    0x40124004: netconn_new_with_proto_and_callback at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/lwip/lwip/src/api/api_lib.c line 133
    0x40128820: lwip_socket at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/lwip/lwip/src/api/sockets.c line 1587
    0x400e7bf7: httpd_server_init at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/lwip/lwip/src/include/lwip/sockets.h line 593
    0x400e7e7d: httpd_start at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_http_server/src/httpd_main.c line 366
    0x400d1c9f: app_httpserver_init() at D:\_Alfa\_2020\_Telegram_esp32\1\_ble_connection\esp32-bluewifi-master\ESP32WebcamBluetoothWifi/ESP32WebcamBluetoothWifi.ino line 204
    0x400d1df7: setup() at D:\_Alfa\_2020\_Telegram_esp32\1\_ble_connection\esp32-bluewifi-master\ESP32WebcamBluetoothWifi/ESP32WebcamBluetoothWifi.ino line 103
    0x400d910f: loopTask(void*) at C:\Users\Admin\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\cores\esp32\main.cpp line 14
    0x400928c9: vPortTaskWrapper at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port.c line 143

    1. WordBot says:

      I thought I knew a little, how wrong I was

      Me too.. for the last three years of this hobby!

      OK.. That wasn’t going to work.. it crashes here as well.

      More things to try:
      Make sure debug is set to Verbose

      Try changing WiFi.disconnect(true, true); to WiFi.disconnect(“0”, “0”);

      Put return false in front of the while
      return false;
      while (WiFi.status() != WL_CONNECTED) {

      The only thing I can think of is that for some reason the Wi-Fi just gives up rather than times out.

  10. Alex says:

    Did not help the same errors

    1. WordBot says:

      even this one?…
      Put return false in front of the while
      return false;
      while (WiFi.status() != WL_CONNECTED) {

      It should skip trying to connect to wi-fi and go to Bluetooth setup. If that doesn’t work I’m lost!

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.

scroll to top