Esp32 wifi provisioning with ble

Hello,

Setup :

Mcu: esp32 s3 mini

Ide : arduino

Board manager : esp32 2.0.10

Question :slight_smile:

I am on a project involving changing Wi-Fi credentials using ble and I have tried using a proof of concept by GPT and Gemini and I came out with a good result where I can start my world with connecting to Wi-Fi from memory nvs. Whether the connection was successful or not i can press at anytime a button to open ble port on esp32 to connect with my phone and change share ssid and password with ESP. Tge mcu will use this data to connect to Wi-Fi after closing ble socket. whether the connection was successful or not i must to be able to enter ble mode again at any given time on the using a button to give new credentials of Wi-Fi. Currently the code that I have can do this cycle only ones, on the second session it doesn't complete the full process and I have only a print of โ€œstarting bleโ€ but nothing happens. my only solution currently is to manage a sequence of sharing data with resetting the ESP, which i don't like.

do you have any suggestions of how to fix this code?

#include <WiFi.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

#define BUTTON_PIN 0  // ืฉื ื” ืœืคื™ ื”ื—ื™ื‘ื•ืจ ื”ืคื™ื–ื™ ืฉืœืš

// ืžืฉืชื ื™ื ื’ืœื•ื‘ืœื™ื™ื
String ssid = "";
String password = "";
bool credentialsReceived = false;
bool buttonPressed = false;
bool bleActive = false;

BLECharacteristic *ssidChar;
BLECharacteristic *passChar;
BLEAdvertising *pAdvertising = nullptr;
BLEServer *pServer = nullptr;

// Callback ืœื›ืชื™ื‘ืช SSID
class SSIDCallback : public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *pChar) override {
    ssid = pChar->getValue().c_str();
    Serial.println("[BLE] Received SSID: " + ssid);
  }
};

// Callback ืœื›ืชื™ื‘ืช Password
class PassCallback : public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *pChar) override {
    password = pChar->getValue().c_str();
    credentialsReceived = true;
    Serial.println("[BLE] Received Password");
  }
};

void startBLE() {
  if (bleActive) {
    Serial.println("[BLE] Already running.");
    return;
  }

  Serial.println("[BLE] Starting BLE...");
  BLEDevice::init("ESP32-WiFi-Setup");

  pServer = BLEDevice::createServer();
  BLEService *pService = pServer->createService("12345678-1234-1234-1234-123456789abc");

  ssidChar = pService->createCharacteristic(
    "12345678-1234-1234-1234-123456789001",
    BLECharacteristic::PROPERTY_WRITE
  );

  passChar = pService->createCharacteristic(
    "12345678-1234-1234-1234-123456789002",
    BLECharacteristic::PROPERTY_WRITE
  );

  ssidChar->setCallbacks(new SSIDCallback());
  passChar->setCallbacks(new PassCallback());

  pService->start();

  pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->start();

  bleActive = true;
  Serial.println("[BLE] Waiting for SSID and Password...");
}

void stopBLE() {
  if (!bleActive) return;

  Serial.println("[BLE] Stopping BLE...");

  if (pAdvertising) {
    pAdvertising->stop();
  }

  if (pServer) {
    for (int i = 0; i < 4; i++) {
      pServer->disconnect(i);
      delay(50);
    }
  }

  delay(200);
  BLEDevice::deinit(true);
  bleActive = false;
  pAdvertising = nullptr;
  pServer = nullptr;

  delay(500); // ื—ืฉื•ื‘ ืœื”ืžืชื™ืŸ ืœืื™ืคื•ืก ืคื ื™ืžื™
}

void connectToWiFi() {
  Serial.println("[Wi-Fi] Connecting to new credentials...");
  WiFi.disconnect(true);
  delay(500);
  WiFi.begin(ssid.c_str(), password.c_str());

  int attempt = 0;
  while (WiFi.status() != WL_CONNECTED && attempt++ < 20) {
    delay(500);
    Serial.print(".");
  }

  delay(200); // ืชืŸ ื–ืžืŸ ืœื”ืชืขื“ื›ืŸ

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\n[Wi-Fi] Connected to: " + WiFi.SSID());
    Serial.println("[Wi-Fi] IP Address: " + WiFi.localIP().toString());
  } else {
    Serial.println("\n[Wi-Fi] Failed to connect.");
  }
}

void setup() {
  Serial.begin(115200);
  delay(1000);

  pinMode(BUTTON_PIN, INPUT_PULLUP);

  Serial.println("[SYSTEM] Booting with default Wi-Fi...");

  WiFi.mode(WIFI_STA);
  WiFi.begin("defaultSSID", "defaultPassword");

  int attempt = 0;
  while (WiFi.status() != WL_CONNECTED && attempt++ < 20) {
    delay(500);
    Serial.print(".");
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\n[Wi-Fi] Connected!");
    Serial.println("[Wi-Fi] IP Address: " + WiFi.localIP().toString());
  } else {
    Serial.println("\n[Wi-Fi] Connection failed.");
  }
}

void loop() {
  if (digitalRead(BUTTON_PIN) == LOW && !buttonPressed) {
    buttonPressed = true;
    Serial.println("[BTN] Button pressed. Switching to BLE mode...");

    WiFi.mode(WIFI_OFF);
    delay(200);
    startBLE();

    // ืžืžืชื™ืŸ ืœืงื‘ืœืช ื ืชื•ื ื™ื ืžื”BLE
    while (!credentialsReceived) {
      delay(100);
    }

    stopBLE();
    delay(500);

    WiFi.mode(WIFI_STA);
    connectToWiFi();

    // ืืคืก ื“ื’ืœื™ื
    credentialsReceived = false;
    buttonPressed = false;
  }

  delay(100);
}

would you consider changing Wi-Fi credentials using WiFi ? if so, check out https://docs.arduino.cc/libraries/wifimanager/

PS: to your question, I remember reading the BLE implementation has known issues with multiple init() / deinit() cycles.If that's what is biting you, Instead of destroying and reinitializing BLE every time, start BLE once in setup() , and then only stop and start advertising when you need to enter or leave configuration mode. You can keep the server and service objects alive in memory for the lifetime of the program or explore with NimBLE-Arduino which seems to be better at init/deinit.