Bluetooth LE Serial terminal link, ESP32S3 to Android Phone

The Serial Bluetooth Terminal for Android app claims to support Bluetooth LE and indeed it does see an ESP32S3 that has been setup as a Server using the NimBLE library, but it wont connect, the error is ‘No serial profile found’, apparently a custom profile is required but I cannot see how you do that.

Is a serial terminal type link, ESP32S3 to Android phone even possible ?

Anyone seen details of a worked example, either with the NimBLE library or the internal ESP32 core one ?

A serial terminal link from ESP32S3 to Smartphone would be handy, without the need for a USB cable ………..

select Tools>Board then ESP32S3 Dev Module
the open File>Examples>BLE>UART
compile, link, upload and run - serial monitor should display

Waiting a client connection to notify...

on an Android phone run Serial Bluetooth Terminal
under Devices select "Bluetootrh LE"
you should see device "UART Service"
you should be able to transmit/receive serial text

e.g. serial monitor

11:03:49.325 -> Waiting a client connection to notify...
11:05:40.641 -> *********
11:05:40.641 -> Received Value:  hello from smartphone
11:05:40.641 -> *********
11:05:53.636 -> *********
11:05:53.636 -> Received Value: test 2
11:05:53.636 -> *********
11:07:31.036 -> *********
11:07:31.036 -> Received Value:  test 3
11:07:31.036 -> *********
1 Like

Well, some improvement, BLE_uart.ino does work, mostly.

I can send text such as “Hello World” from the Android device to the ESP32S3 and it displays in serial terminal.

But the Android phone terminal screen is full of continuous characters, looks like from 0 to 0xFF, as in the picture you posted. .

Changing the loop() code (of BLE_uart.ino) to something like this;

void loop() {

if (deviceConnected) {
    txValue = 0x41;
    pTxCharacteristic->setValue(&txValue, 1);
    pTxCharacteristic->notify();
    txValue++;
    pTxCharacteristic->setValue(&txValue, 1);
    pTxCharacteristic->notify();
    txValue++;
    pTxCharacteristic->setValue(&txValue, 1);
    pTxCharacteristic->notify();
    txValue++;
    txValue = 0x0D;
    pTxCharacteristic->setValue(&txValue, 1);
    pTxCharacteristic->notify();
    txValue = 0x0A;
    pTxCharacteristic->setValue(&txValue, 1);
    pTxCharacteristic->notify();
    
	delay(1000); // bluetooth stack will go into congestion, if too many packets are sent
}

Does make the Android device print;

17:30:05.125 ABC

17:30:06.125 ABC

Which is an improvement, but a function that allowed you to replace say

Serial.println(“Hello World”);

that you would use to print to the serial terminal with

BLEprintln(“Hello World”);

To print to the BLE would be good.

you probably need to implement your own smartphone app
I use Android phones therefore I use Android Studio - it is none trivial to use!
a web search for android studio alternatives will give other IDEs, e.g. MIT App Inventor which works with Android and iOS phones

I dont see how a different app on the phone would help much.

The Bluetooth Serial Terminal does appear to display characters that are sent in a serial terminal fashion.

However the mechanism to achieve that appears to be that for each character sent the ESP32S3 does a;

pTxCharacteristic->setValue(&txValue, 1);
pTxCharacteristic->notify();

So you would imagine its possible to use that to send an “ABC” etc.

What surprises me is that I had thought that a Bluetooth Serial terminal from an ESP32x device to an Android device would be useful and popular, yet no obvious examples of a solution seem to exist, at least none found so far.

I have found that Adafruit Bluefruit Connect will work as an ascii terminal program between an esp 32 and an android phone.

https://play.google.com/store/apps/details?id=com.adafruit.bluefruit.le.connect&hl=en_US

I run this program on the esp 32.


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

BLEServer *pServer = NULL;
BLECharacteristic * pTxCharacteristic;
bool deviceConnected = false;
bool oldDeviceConnected = false;
bool newMessage = false;
uint8_t txValue = 0;
bool mR = false;
//std::string rxValue = "";//esp32 BLE library syntax changed to String
String rxValue;

#define SERVICE_UUID           "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"


class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
    };

    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
    }
};

class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
      rxValue = pCharacteristic->getValue();

      if (rxValue.length() > 0) {
        Serial.println("*********");
        mR = true;
        Serial.print("Received Value: ");
        for (int i = 0; i < rxValue.length(); i++)
          Serial.print(rxValue[i]);

        Serial.println();
        newMessage = true;
        Serial.println("*********");
      }
    }
};


void setup() {
  Serial.begin(115200);
  //setup LED
  pinMode(16, OUTPUT); //onboard led LOW to turn on
  digitalWrite(16, HIGH);

  // Create the BLE Device
  BLEDevice::init("UART Service");

  // Create the BLE Server
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  // Create the BLE Service
  BLEService *pService = pServer->createService(SERVICE_UUID);

  // Create a BLE Characteristic
  pTxCharacteristic = pService->createCharacteristic(
                        CHARACTERISTIC_UUID_TX,
                        BLECharacteristic::PROPERTY_NOTIFY
                      );

  pTxCharacteristic->addDescriptor(new BLE2902());

  BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
      CHARACTERISTIC_UUID_RX,
      BLECharacteristic::PROPERTY_WRITE);

  pRxCharacteristic->setCallbacks(new MyCallbacks());

  // Start the service
  pService->start();

 // Start advertising
  pServer->getAdvertising()->start();
  Serial.println("Waiting a client connection to notify...");
}

//void respond(std::string  send_message) {//esp32 BLE library syntax changed to String
void respond(String  send_message) {
  pTxCharacteristic->setValue(send_message);
  pTxCharacteristic->notify();
}

void loop() {

  if (Serial.available() > 0)
  {
    char monitorInput[50] = "";
    byte len = Serial.readBytesUntil('\n', monitorInput, 49);
    monitorInput[len] = '\0';
    Serial.println("Sending message input from Serial Monitor");
    Serial.println(monitorInput); //echo back to monitor
    Serial.println("*********");
    respond("Received from monitor\n");//shows in phone
    respond(monitorInput);//send on BLE UART
  }

  if (deviceConnected && newMessage) {
    newMessage = false;
    /*
    if (rxValue == String("hello\n").c_str()) {
      digitalWrite(16, LOW);
      delay(1000);
      digitalWrite(16, HIGH);
      respond(String("world\n").c_str());
    }
    */

    respond("Confirm received from phone\n");
    respond(rxValue);
  }

  // disconnecting
  if (!deviceConnected && oldDeviceConnected) {
    delay(500); // give the bluetooth stack the chance to get things ready
    pServer->startAdvertising(); // restart advertising
    Serial.println("start advertising");
    oldDeviceConnected = deviceConnected;
  }
  // connecting
  if (deviceConnected && !oldDeviceConnected) {
    // do stuff here on connecting
    oldDeviceConnected = deviceConnected;
  }
}

Wow……

One of the reasons why I regularly study this forum is for the intellectual exercise in finding a thread like this… I am sure it has never happened before, and this should go down in history.

So, am I correct in understanding that Cattledog’s code in post #6 is the equivalent of srnet’s mythical

BLEprintln(“Hello World”);

?

found this version of the BLE UART program which transmits/receives text using Android Serial Bluetooth app

// ESP32 BLE terminal transmit/receive text

// Android Serial Bluetooth/BLE terminal
//    https://play.google.com/store/apps/details?id=de.kai_morich.serial_bluetooth_terminal&hl=en-US

/*
    Video: https://www.youtube.com/watch?v=oCMOYS71NIU
    Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
    Ported to Arduino ESP32 by Evandro Copercini

   Create a BLE server that, once we receive a connection, will send periodic notifications.
   The service advertises itself as: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E
   Has a characteristic of: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E - used for receiving data with "WRITE" 
   Has a characteristic of: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E - used to send data with  "NOTIFY"

   The design of creating the BLE server is:
   1. Create a BLE Server
   2. Create a BLE Service
   3. Create a BLE Characteristic on the Service
   4. Create a BLE Descriptor on the characteristic
   5. Start the service.
   6. Start advertising.

   In this example rxValue is the data received (only accessible inside that function).
   And txValue is the data to be sent, in this example just a byte incremented every second. 
*/
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

BLEServer *pServer = NULL;
BLECharacteristic *pTxCharacteristic;
bool deviceConnected = false;
bool oldDeviceConnected = false;
uint8_t txValue = 0;

// See the following for generating UUIDs:
// https://www.uuidgenerator.net/

#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"  // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"


class MyServerCallbacks : public BLEServerCallbacks {
  void onConnect(BLEServer *pServer) {
    deviceConnected = true;
  };

  void onDisconnect(BLEServer *pServer) {
    deviceConnected = false;
  }
};

class MyCallbacks : public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *pCharacteristic) {
    String rxValue = pCharacteristic->getValue();

    if (rxValue.length() > 0) {
      Serial.print("BLE Received: ");
      for (int i = 0; i < rxValue.length(); i++)
        Serial.print(rxValue[i]);
      Serial.println();
    }
  }
};


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

  // Create the BLE Device
  BLEDevice::init("UART Service");

  // Create the BLE Server
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  // Create the BLE Service
  BLEService *pService = pServer->createService(SERVICE_UUID);

  // Create a BLE Characteristic
  pTxCharacteristic = pService->createCharacteristic(
    CHARACTERISTIC_UUID_TX,
    BLECharacteristic::PROPERTY_NOTIFY);

  pTxCharacteristic->addDescriptor(new BLE2902());

  BLECharacteristic *pRxCharacteristic = pService->createCharacteristic(
    CHARACTERISTIC_UUID_RX,
    BLECharacteristic::PROPERTY_WRITE);

  pRxCharacteristic->setCallbacks(new MyCallbacks());

  // Start the service
  pService->start();

  // Start advertising
  pServer->getAdvertising()->start();
  Serial.println("Waiting a client connection to notify...");
}

void loop() {
  // if available read string from serial - transmit over BLE
  if (deviceConnected) {
    if (Serial.available()) {
      String s = Serial.readStringUntil('\n') + "\n";  // read line of text
      Serial.printf("BLE transmit: %s\n", s.c_str());
      pTxCharacteristic->setValue((uint8_t *)s.c_str(), strlen(s.c_str()));
      pTxCharacteristic->notify();  // transmit line of text
    }
  }
  // disconnecting
  if (!deviceConnected && oldDeviceConnected) {
    delay(500);                   // give the bluetooth stack the chance to get things ready
    pServer->startAdvertising();  // restart advertising
    Serial.println("start advertising");
    oldDeviceConnected = deviceConnected;
  }
  // connecting
  if (deviceConnected && !oldDeviceConnected) {
    // do stuff here on connecting
    oldDeviceConnected = deviceConnected;
  }
}

ESP32 serial monitor

Waiting a client connection to notify...
BLE transmit: test1 from ESP32

BLE transmit: test2 from ESP32 0123456789

BLE Received: test1 from android

BLE Received: test1 from android

BLE Received: test2 android 2345677

BLE Received: test2 android 2345677

BLE transmit: test3 from esp32 abcdef

Android phone terminal output

I can find no differences between the what you have posted and the code in Post #6.

I can not get either of them to connect to the Morich Serial Bluetooth Terminal app in BLE.

I am using Android 15 on a Galaxy A54 5G.

looks very similar - looking at the date on the .ino file of post 8 it appears to have been implemented (using UART example as a start) about 5 years ago

it defaults to Bluetooth Classic - under devices did you select Bluetooth LE and UART Service

using a Huawei P30 Pro

The use of the respond() function does provide a mechanism for printing to the Bluetooth Terminal app on my Android device, so a significant improvement.

Replacing the loop code with;

char buff[] = "00000";                //the message to send
uint16_t MessageCount = 0;

void loop() {

  Serial.print(F("BLE Sending > "));

  buff[0] = MessageCount / 10000 + '0';
  buff[1] = ((MessageCount % 10000) / 1000) + '0';
  buff[2] = ((MessageCount % 1000) / 100) + '0';
  buff[3] = ((MessageCount % 100) / 10) + '0';
  buff[4] = MessageCount % 10 + '0';

  for (uint8_t index = 0; index < sizeof(buff); index++) {
    Serial.write(buff[index]);
  }

  Serial.println();

  //now send the BLE stuff
    respond("BLE ");
    respond(buff);
    respond(String("\n"));
    MessageCount++;

    delay(1000);
}

Results in this on the Android;

BLE 00621

BLE 00622

BLE 00623

Note that the code you posted does not compile on the older Expressif ESP32 Cores, such as 2.0.14 but OK on 3.2.0.

About to try the Adafruit Bluefruit Connect app.

Cattledog’s code is Workable, yes.

However, a function that was able to use the Stream class whould be a lot easier to use.

Yes. It's a mystery to me why I can't connect with SerialBluetoothTerminal and get a "Connection failed custom service not found" error message.

The phone works fine with BLE using LightBlue or nrfConnect and the Adafruit terminal program linked above.

EDIT: I am finding that the Morich terminal app connects and communicates with a Nordic uart service on an Nano 33 Ble using the Arduino library #include <ArduinoBLE.h> instead of the esp32 library.

Indeed if I use the ArduinoBLE.h library with an esp32 I can connect and communicate with the Morich app.

It appears that I have something wrong in my esp 32 environment when using these libraries and their syntax.

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

EDIT: I think the issue for me was that I had some custom services enabled in the Morich Terminal app, and did not set it back to the predefined services.

Here is some updated code, uses IDE 2.3.6 with ESP32 Core 3.2.0.

Works with the Android Serial Bluetooth Terminal app, an this appears on the Android screen

00012 float,1.23 int,-123 uint,234

00013 float,1.23 int,-123 uint,234

So you can print char buffer,text,float and integers.

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

BLEServer *pServer = NULL;
BLECharacteristic *pTxCharacteristic;
bool deviceConnected = false;
bool oldDeviceConnected = false;
bool newMessage = false;
uint8_t txValue = 0;
bool mR = false;
//std::string rxValue = "";//esp32 BLE library syntax changed to String
String rxValue;

#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"  // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"

class MyServerCallbacks : public BLEServerCallbacks {
    void onConnect(BLEServer *pServer) {
      deviceConnected = true;
    };

    void onDisconnect(BLEServer *pServer) {
      deviceConnected = false;
    }
};

class MyCallbacks : public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
      rxValue = pCharacteristic->getValue();

      if (rxValue.length() > 0) {
        Serial.println("*********");
        mR = true;
        Serial.print("Received Value: ");
        for (int i = 0; i < rxValue.length(); i++)
          Serial.print(rxValue[i]);

        Serial.println();
        newMessage = true;
        Serial.println("*********");
      }
    }
};

void setup() {
  Serial.begin(115200);
  //setup LED
  pinMode(16, OUTPUT);  //onboard led LOW to turn on
  digitalWrite(16, HIGH);

  // Create the BLE Device
  BLEDevice::init("UART Service");

  // Create the BLE Server
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  // Create the BLE Service
  BLEService *pService = pServer->createService(SERVICE_UUID);

  // Create a BLE Characteristic
  pTxCharacteristic = pService->createCharacteristic(
                        CHARACTERISTIC_UUID_TX,
                        BLECharacteristic::PROPERTY_NOTIFY);

  pTxCharacteristic->addDescriptor(new BLE2902());

  BLECharacteristic *pRxCharacteristic = pService->createCharacteristic(
      CHARACTERISTIC_UUID_RX,
      BLECharacteristic::PROPERTY_WRITE);

  pRxCharacteristic->setCallbacks(new MyCallbacks());

  // Start the service
  pService->start();

  // Start advertising
  pServer->getAdvertising()->start();
  Serial.println("Waiting a client connection to notify...");
}


//void respond(std::string  send_message) {//esp32 BLE library syntax changed to String
void respond(String send_message) {
  pTxCharacteristic->setValue(send_message);
  pTxCharacteristic->notify();
}

void respond_int(int32_t value) {
  String mystring;
  mystring = String(value);
  pTxCharacteristic->setValue(mystring);
  pTxCharacteristic->notify();
}

void respond_uint(uint32_t value) {
  String mystring;
  mystring = String(value);
  pTxCharacteristic->setValue(mystring);
  pTxCharacteristic->notify();
}

void respond_float(float f, int places ) {
  String mystring;
  mystring = String(f, places);
  pTxCharacteristic->setValue(mystring);
  pTxCharacteristic->notify();
}

char buff[] = "00000";  //the message to send
uint16_t MessageCount = 0;

void loop() {

  Serial.print(F("BLE Sending > "));

  buff[0] = MessageCount / 10000 + '0';
  buff[1] = ((MessageCount % 10000) / 1000) + '0';
  buff[2] = ((MessageCount % 1000) / 100) + '0';
  buff[3] = ((MessageCount % 100) / 10) + '0';
  buff[4] = MessageCount % 10 + '0';

  for (uint8_t index = 0; index < sizeof(buff); index++) {
    Serial.write(buff[index]);
  }

  Serial.println();

  respond(buff);

  respond(" float,");
  respond_float(1.2345678, 2);

  int8_t intval = -123;
  respond(" int,");
  respond_int(intval);

  uint8_t uintval = 234;
  respond(" uint,");
  respond_uint(uintval);

  respond("\n");

  MessageCount++;

  delay(1000);
}

The compiled code takes around 66% of the ESP32S3 4MB of Flash.

The nimBLE library similar example compiles to around 36%, but there is no connect from the Android app.

Can you please post a link to the library you are using.

I would think that there is a way to configure the uuids to match the Morich requirements for the nordic nrf uuids

SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"  // UART service UUID CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"

https://github.com/h2zero/NimBLE-Arduino

That libraries basic ‘NimBLE_Server.ino” example gives the Flash memory useage I quoted.