BluetoothLE extension very confusing (ESP32-C3 communication)

I've been trying to use the BluetoothLE extension recently and have really been struggling trying to do simple, basic things. I apparently need to use this extension for communicating with my ESP32-C3 since it uses BLE.

All I really want to do is to be able to use a ListPicker and see all available bluetooth devices with their names and addresses, connect to my ESP32-C3, and send strings to it. That's all I need this app to do. I'm finding it hard to even get started, whilst doing the same thing with the built-in Bluetooth blocks in AppInventor is effortless for me.

Could someone please get me started? There's so many blocks too, it almost hurts my eyes trying to find and choose the right ones.

I don't know what ESP32-C3 you have, but would you be willing to take another path?

You could use this extension that makes it really easy to work with BLE.

You would have to use MicroBlocks to program your ESP32-C3.

I use it almost on a daily basis and the combination is very powerful.

Thank you, this got me much much closer.

I almost got everything working, just one simple issue remains: suddenly every time I try sending a message it says "Not connected to a Bluetooth low energy device", even though it was sending messages perfectly fine a minute ago.

You see, now it's working again...

EDIT: seems like it's not connected. I added a text label to say "Connected" when successfully connected to the chosen device but it never appears

Edit 2: I managed to get it connected only twice over around an hour, I've tried everything I could think of on both sides of the whole project. Always the same errors printed by the BluetoothLE1.ConnectionFailed block: "connection timeout reached", then "connection status was set to OS code 133"

Dear @MasterfulMatthew in addition to what @Peter and @ABG have already suggested you, and if the troubles persist, you can take a look to the link below:

Recently it has been of help to another user to solve his problem of having both BLE and Classic BT working together.
Though you need only the BLE, by reading the post, and the annexed codes, you'll find that you can use only the BLE section (i.e. connecting just one ESP32 on BLE toward the AI2 app, running on the Android device).

I'm only using BLE on its own, so I shouldn't have any problems between BLE and classic Bluetooth, since classic Bluetooth isn't being used in the first place. As for the BLE section of the code, I'll have a look soon, thanks in advance.

Another little comment: with Classic BT, both used in the background with the Itoo extension (by @Kumaraswamy) and with the standard AI2 client, the communication lasts for hours without problems, while with BLE I have not tested it for the same period, but it should work anyway.
All the best.

Hello everyone,

After many hours of debugging and testing I'll just give you all the short answer to save y'all from my suffering. When resetting the ESP32-C3, pressing the reset button isn't enough for BLE. You must unplug it for 5 SECONDS or longer, since the built-in capacitor will RETAIN power to the board, just enough for the memory storage to STAY ACTIVE. If you don't hard reset the board, it'll still retain a ghost Bluetooth connection and cause all kinds of errors.

EDIT: this was not the solution WHYYYY

Hmmmm, as I was telling you, BLE is really a "nasty dog"....anyway here below the link to the ESP32-C3 manufacturer user manual with examples.
https://docs.espressif.com/projects/esp-at/en/release-v2.4.0.0/esp32c3/AT_Command_Examples/bluetooth_le_at_examples.html

It's a huge documentation, but probably by surfing it you can find something useful (I warmly wish you !).

Alright here's the solution, for me at least. (For real this time)

It was absolutely nothing wrong with AppInventor, but actually my ESP32-C3 code. I've come across many other people online with the exact same issue trying to interface AppInventor and ESP32-C3, so I better go around and share my answer quick.

With some help of ChatGPT, here's my explaination and solution:
Let's start off with behavior recorded:

  • First connection → works
  • Disconnect → phone thinks it disconnected
  • ESP32-C3 → still thinks the connection is active
  • Reconnect → OS 133 / timeout
  • Reset ESP32-C3 → works instantly

Why the ESP32-C3 can’t reconnect until reset

The ESP32-C3 uses the NimBLE BLE stack (not Bluedroid like ESP32).

When an Android device disconnects, Android sends:

LL_TERMINATE_IND

…but sometimes NimBLE never receives the termination packet.

This leaves the ESP32 stuck in “ghost connection” state:

Phone: disconnected.
ESP32-C3: still connected.

So when I try to connect again:

  • Android thinks the device is available
  • ESP32 still thinks someone is connected
  • The GATT server won’t accept a new connection
  • Android returns timeout / OS133

The fix: Must implement onDisconnect and restart advertising properly in the ESP32-C3 code.

My code didn't have ServerCallbacks, so the ESP32 never realized the client left unless it received the BLE-level termination packet (which Android sometimes drops).

Add this:

class ServerCallbacks : public BLEServerCallbacks {
  void onConnect(BLEServer* pServer) override {
    Serial.println("Client connected");
  }

  void onDisconnect(BLEServer* pServer) override {
    Serial.println("Client disconnected");
    delay(100); // NimBLE needs a small pause
    BLEDevice::startAdvertising();  // critical!
  }
};

And in setup:

server->setCallbacks(new ServerCallbacks());

This single change forces NimBLE to:

:heavy_check_mark: Mark the connection as closed
:heavy_check_mark: Clean up GATT state
:heavy_check_mark: Re-enable advertising
:heavy_check_mark: Allow a new connection

Example code showcasing my main point of using ServerCallbacks:

#include <WiFi.h>
#include "BLEDevice.h"

#define RX_UUID       "6e400002-b5a3-f393-e0a9-e50e24dcca9e"
#define SERVICE_UUID  "6e400001-b5a3-f393-e0a9-e50e24dcca9e"

BLECharacteristic *rxChar;
BLEServer *server;

// --------------------------------------------------
//      BLE Write Callback — Just prints data
// --------------------------------------------------
class RXCallback : public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *characteristic) override {
    String incoming = String(characteristic->getValue().c_str());
    Serial.print("BLE Received: ");
    Serial.println(incoming);
  }
};

// --------------------------------------------------
//      ServerCallbacks — CRITICAL for proper BLE
//      reconnection, especially on Android
// --------------------------------------------------
class ServerCallbacks : public BLEServerCallbacks {
  void onConnect(BLEServer* pServer) override {
    Serial.println("Client connected");
  }

  void onDisconnect(BLEServer* pServer) override {
    Serial.println("Client disconnected");

    // Without this delay + restart,
    // Android often fails to reconnect.
    delay(200);

    BLEAdvertising *adv = pServer->getAdvertising();
    adv->start();

    Serial.println("Advertising restarted");
  }
};

// --------------------------------------------------
//      Setup
// --------------------------------------------------
void setup() {
  Serial.begin(115200);
  delay(200);

  // Reset BLE state (fixes C3/Android stale handles)
  BLEDevice::deinit(true);
  delay(200);

  BLEDevice::init("ESP32C3-DEMO");
  server = BLEDevice::createServer();
  server->setCallbacks(new ServerCallbacks());

  BLEService *service = server->createService(SERVICE_UUID);

  rxChar = service->createCharacteristic(
      RX_UUID,
      BLECharacteristic::PROPERTY_WRITE
  );
  rxChar->setCallbacks(new RXCallback());
  service->start();

  BLEAdvertising *adv = server->getAdvertising();
  adv->addServiceUUID(SERVICE_UUID);
  adv->setScanResponse(true);

  // Recommended values for NimBLE on C3
  adv->setMinPreferred(0x06);
  adv->setMinPreferred(0x12);

  adv->start();

  Serial.println("BLE Ready — Send text to RX characteristic.");
}

void loop() {}

EDIT: Here's my AppInventor code by the way,

1 Like

Dear @MasterfulMatthew really many thanks for your solution sharing.
I believe this will be very helpful in the future for other Users.
The lesson learned is (as many other times..) *** Never Give-Up ***. :muscle: :muscle: :muscle:
Cheers.

1 Like

You're very welcome, I highly enjoy giving back to all of these great communities with any newfound knowledge I may have come across through my various projects!

1 Like

(added to FAQ)

1 Like