ESP32 Bluetooth help

Hello. I am programming ESP32 development board on Arduino IDE and currently learning about the BLE. I need to be able to read some information from the ESP32 device as well as sending some information to ESP32 device to configure some settings.
Questions:

  1. I am yet not sure whether I should be using BLE_usart or BLE_server examples as a starting point for my project, would someone suggest?

  2. For example I want to be able to read the temperature sensor value on my phone (I have downloaded nRF connect app). In order for the service or characteristic to show as a "Temperature sensor" or simmilar on my APP instead of unknown service, I must choose the right UUID's is that correct? On the official BLE page I have found UUID's:
    https://btprodspecificationrefs.blob.core.windows.net/assigned-values/16-bit%20UUID%20Numbers%20Document.pdf
    For example, temperature measurement characteristic has an UUID 0x2A1C.

Unfortunately, I have not yet found a way how to set a 16-bit UUID instead of a default way Arduino defines UUID's. For example the default UUID for Service and Characteristics are as following:

#define SERVICE_UUID        "fb349b5f-8000-0080-0010-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

How can I change those to something temperature related?

I have also noticed another strange issue. I have tried to generate random UUID's using the suggested website:
https://www.uuidgenerator.net/

However, after reflashing the device and connecting through nRF connect app, I can see that the UUID's for the service and characteristic remain unchanged. Can someone explain why that happens?
my code:

/*
    Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp
    Ported to Arduino ESP32 by Evandro Copercini
    updates by chegewara
*/

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

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

//#define SERVICE_UUID        "fb349b5f-8000-0080-0010-c5c9c331914b"
//#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"


#define TSERVICE_UUID        "fa42b45a-ef63-11eb-9a03-0242ac130003"
#define TCHARACTERISTIC_UUID "006066e8-ef64-11eb-9a03-0242ac130003"

int counter=0;

void setup() {
  Serial.begin(115200);
  Serial.println("Starting BLE work!");

  BLEDevice::init("lukas123x");
  BLEServer *pServer = BLEDevice::createServer();
  BLEService *pService = pServer->createService(TSERVICE_UUID);
  BLECharacteristic *pCharacteristic = pService->createCharacteristic(
                                         TCHARACTERISTIC_UUID,
                                         BLECharacteristic::PROPERTY_READ |
                                         BLECharacteristic::PROPERTY_WRITE
                                       );

  pCharacteristic->setValue("Hello World says Lukas");
  pService->start();
  // BLEAdvertising *pAdvertising = pServer->getAdvertising();  // this still is working for backward compatibility
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(TSERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);
  BLEDevice::startAdvertising();
  Serial.println("Characteristic defined! Now you can read it in your phone!");
}

void loop() {
  counter++;
  //pCharacteristic->setValue(counter);
  // put your main code here, to run repeatedly:
  delay(2000);
}

And the screenshof of my phone app:

Notice the UUID'S remained old (default)

To get new UUIDS to show in the nrfConnect screen you have to connect to an unbonded device.

1 Like

Got it! You are totaly right on this one. Have you got any clues regarding my other few questions?

  1. How can I set the UUID so it shows as "Temperature sensor" service? Have you got any experience in programming ESP32 bluetooth? Are we allowed to use 16-bit UUID's cause I cant seem to find a way to use them.

  2. What is the real difference between BLE server and BLE uart? It seems that in both examples both devices (esp32 and phone) are able to communicate with each other, isnt that right?

Can you please explain more about what you are doing. Is your main reading/sending device an Android phone? What data will go back and forth, and how frequently?

What is the reason for the requirment of 16 bit standard UUIDS? Do you know if you can write from the phone to the standard temperature service.

Why have you chose BLE instead of classic Bluetooth for your application? The ESP32 can use either one.

Classic bluetooth uses SPP( Serial Port Protocol) which is quite simple. This can be emulated with BLE using the Nordic Uart Service but you are moving away from the advantages of BLE with the custom services and characteristics.

Thanks for the reply. I have no real reason to choose BLE over simple Bluetooth since I dont know too much about Bluetooth yet to know the real differences between the two. I have simply chosen BLE because it is more recent so I thought its going to be more advanced and better to use.

What I am trying to do is quite simple. My ESP32 will be a controller and is going to control various relays and send data to the server regularly. I want to be able to change the parameters through the Bluetooth.

For example by default, the ESP32 will send data to the server every 1 minute. Through the bluetooth, I want to be able to change this time for example to 1 hour.

Also, I want to be able to toggle the GPIO. For example, I send 0xA1 - that will turn on GPIO1. I send 0XB1 that will turn OFF the GPIO1.

I can hardcode these commands to the ESP32 for example 0xAA - send to server every 1 minute. 0xBB - send to server every 5 minutes. 0xCC - send to server every 10 minutes and so on. Im just trying to determine what is the best way to communicate between the two.

How is the ESP32 doing that? WiFi? Is it connecting to a webpage? Blynk? What is happening with the data being sent? Can you not talk to the ESP32 from the server?

Both Wifi and Bluetooth use the same radio on the ESP32, and I do not believe that they can work at the same time. You may need to have scheduled update times when the "server" connection is broken and the bluetooth is enabled.

Please explain more about the "server" connection?

Through the bluetooth, I want to be able to change this time for example to 1 hour.

Also, I want to be able to toggle the GPIO. For example, I send 0xA1 - that will turn on GPIO1. I send 0XB1 that will turn OFF the GPIO1.

I would certainly use bluetooth classic for this task.

The data will be sent thorugh the GSM. I can talk to the ESP32 thorugh the GSM and the SMS but I also want to have an option to talk to the ESP32 through the BLE.

I havent created server solution yet so I cant explain more but I just need some server storage to be able to hold all the required information that I want to send and keep the history. It can be any server really.

Will I need to develop a custom BLE app for this or I can use NRF connect or something simmilar to send the required bytes of data?

This sounds like you will be using an separate modem for sending the data, and the bluetooth radio section of the ESP32 will be available for bluetooth.

For your application, I would use bluetooth classic and a serial terminal phone app. I highly recommend Kai Morich's Serial Bluetooth Terminal.

On the ESP side you will use the built in library
#include "BluetoothSerial.h"

When you select the ESP32 board, there is an example in the ide File>Examples>Bluetooth Serial.

Here's an link to a tutorial
https://randomnerdtutorials.com/esp32-bluetooth-classic-arduino-ide/

Bluetooth Serial is like any other serial communication. I would recommend a review of Robin2's excellent tutorial Serial Input Basics

With the ESP32, you could use String methods for receiving messages if you are more familiar with those.

For BLE SIG defined characteristics and services to be working, you need to use 16-bit standard UUID's and the format defined for the characteristics.

Here is an example for the Environmental Sensing Service for the ESP32 including links to the documents describing the characteristics.

/*
  This example creates a BLE peripheral with a Environmental Sensing Service (ESS)
  that contains a temperature, humidity and pressure characteristic.

  The circuit:
  - ESP32-WROOM-32E

  BME280 - SDA connected to GPIO21
  BME280 - SCL connected to GPIO22

  You can use a generic BLE central app, like LightBlue (iOS and Android) or
  nRF Connect (Android), to interact with the services and characteristics
  created in this sketch.
  Silicon Labs EFR Connect can decode ESS.


  This example code is in the public domain.
*/

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

#include <Adafruit_BME280.h>

//----------------------------------------------------------------------------------------------------------------------
// BLE UUIDs
//----------------------------------------------------------------------------------------------------------------------

// https://www.bluetooth.com/specifications/assigned-numbers/environmental-sensing-service-characteristics/
// https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.temperature.xml
// https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.humidity.xml
// https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.pressure.xml

#define BLE_UUID_ENVIRONMENTAL_SENSING_SERVICE    "181A"
#define BLE_UUID_TEMPERATURE                      "2A6E"
#define BLE_UUID_HUMIDITY                         "2A6F"
#define BLE_UUID_PRESSURE                         "2A6D"

#define deviceName                                "ESP32-WROOM-32E"

//----------------------------------------------------------------------------------------------------------------------
// BLE
//----------------------------------------------------------------------------------------------------------------------

BLEServer* pServer = NULL;
BLECharacteristic* pTemperatureCharacteristic = NULL;
BLECharacteristic* pHumidityCharacteristic = NULL;
BLECharacteristic* pPressureCharacteristic = NULL;
BLEService *pService = NULL;

bool deviceConnected = false;
bool previousDeviceConnected = false;

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

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


//----------------------------------------------------------------------------------------------------------------------
// BME280
//----------------------------------------------------------------------------------------------------------------------

#define SEALEVELPRESSURE_HPA                      (1013.25)
#define SENSOR_UPDATE_INTERVAL                    (10000)

Adafruit_BME280 bme; // I2C

typedef struct __attribute__( ( packed ) )
{
  float temperature;
  float humidity;
  float pressure;
  bool updated = false;
  bool simulate = false;
} sensor_data_t;

sensor_data_t sensorData;



void setup()
{
  Serial.begin( 115200 );
  Serial.println( "ESP32 BLE Environmental Sensing Service Example" );

  if ( !bme.begin( BME280_ADDRESS_ALTERNATE ) )
  {
    Serial.println( "BME280 not found. Data simulation enabled" );
    sensorData.simulate = true;
  }

  if ( setupBleMode() )
  {
    Serial.println( "Waiting for clients to connect" );
  }
}


void loop()
{
  bleTask();
  if ( sensorTask() )
  {
    printTask();
  }
}


void printTask()
{
  Serial.print( "Temperature: " );
  Serial.print( sensorData.temperature, 1 );
  Serial.println( " °C" );

  Serial.print( "Humidity:    " );
  Serial.print( sensorData.humidity, 1 );
  Serial.println( " %" );

  Serial.print( "Pressure:   " );
  Serial.print( sensorData.pressure / 100, 1 );
  Serial.println( " hPa" );
}


bool sensorTask()
{
  static uint32_t previousMillis = 0;

  uint32_t currentMillis = millis();
  if ( currentMillis - previousMillis < SENSOR_UPDATE_INTERVAL )
  {
    return false;
  }
  previousMillis = currentMillis;

  if ( !sensorData.simulate )
  {
    sensorData.temperature = bme.readTemperature();
    sensorData.humidity = bme.readHumidity();
    sensorData.pressure = bme.readPressure();
    sensorData.updated = true;
  }
  else
  {
    sensorData.temperature = 21.0 + random(50)/10.0;
    sensorData.humidity = 20.0 + random(300)/10.0;
    sensorData.pressure = 97000.0 + random(40000)/10.0;
    sensorData.updated = true;
  }
  return true;
}


bool setupBleMode()
{
  BLEDevice::init( deviceName );
  pServer = BLEDevice::createServer();
  pServer->setCallbacks( new MyServerCallbacks() );

  // BLE add service
  pService = pServer->createService( BLE_UUID_ENVIRONMENTAL_SENSING_SERVICE );

  // BLE add characteristics
  pTemperatureCharacteristic = pService->createCharacteristic(
                                 BLE_UUID_TEMPERATURE,
                                 BLECharacteristic::PROPERTY_READ |
                                 BLECharacteristic::PROPERTY_NOTIFY
                               );

  // BLE add Descriptor (UUID 0x2902), this is required for notifications to work
  pTemperatureCharacteristic->addDescriptor( new BLE2902() );

  pHumidityCharacteristic = pService->createCharacteristic(
                              BLE_UUID_HUMIDITY,
                              BLECharacteristic::PROPERTY_READ |
                              BLECharacteristic::PROPERTY_NOTIFY
                            );
  pHumidityCharacteristic->addDescriptor( new BLE2902() );

  pPressureCharacteristic = pService->createCharacteristic(
                              BLE_UUID_PRESSURE,
                              BLECharacteristic::PROPERTY_READ |
                              BLECharacteristic::PROPERTY_NOTIFY
                            );
  pPressureCharacteristic->addDescriptor( new BLE2902() );

  // start service
  pService->start();

  // start advertising
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID( BLE_UUID_ENVIRONMENTAL_SENSING_SERVICE );
  pAdvertising->setScanResponse( true );
  pAdvertising->setMinPreferred( 0x06 ); // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred( 0x12 );
  BLEDevice::startAdvertising();

  return true;
}


void bleTask()
{
  if ( sensorData.updated )
  {
    // BLE defines Temperature UUID 2A6E Type sint16 ( see XML links )
    // Unit is in degrees Celsius with a resolution of 0.01 degrees Celsius
    int16_t temperature = round( sensorData.temperature * 100.0 );
    pTemperatureCharacteristic->setValue( ( uint8_t* )&temperature, 2 );
    pTemperatureCharacteristic->notify();

    // BLE defines Humidity UUID 2A6F Type uint16
    // Unit is in percent with a resolution of 0.01 percent
    uint16_t humidity = round( sensorData.humidity * 100.0 );
    pHumidityCharacteristic->setValue( ( uint8_t* )&humidity, 2 );
    pHumidityCharacteristic->notify();

    // BLE defines Pressure UUID 2A6D Type uint32
    // Unit is in Pascal with a resolution of 0.1 Pa
    uint32_t pressure = round( sensorData.pressure * 10.0 );
    pPressureCharacteristic->setValue( ( uint8_t* )&pressure, 4 );
    pPressureCharacteristic->notify();

    sensorData.updated = false;
  }

  // disconnecting
  if ( !deviceConnected && previousDeviceConnected )
  {
    Serial.println( "Client disconnected." );
    pServer->startAdvertising();
    previousDeviceConnected = deviceConnected;
  }
  // connecting
  if ( deviceConnected && !previousDeviceConnected )
  {
    // do stuff here on connecting
    previousDeviceConnected = deviceConnected;
    Serial.println( "Client connected." );
  }
}

I used a BOSCH BME280 sensor and the Adafruit_BME280 library for that. When no sensor is found the data will be simulated. Changing the code to another sensor should be easy. Just store the data in the sensorData structure and set the flag. The BLE task will format the data before writing it to the characeristic.

I recommend the Silicon Labs EFR Connect app. It decodes the environmental sensing service data.

You can write to characteristics with WRITE attribute from a generic BLE app, but for the best user experience you will need to write your own.

Here is @Klaus_K 's great BLE environmental services sketch roughly converted to use bluetooth classic with BluetoothSerial.h. The output is to the previously referenced Kai Morich Bluetooth Serial Terminal. I have included reading some input to change the data reporting interval.

I'm still not clear about the overall picture of what you are trying to achieve and how the bluetooth fits in for both input and output. I'm certain there is a preferred path but you have been shown how to use either BLE and BTSerial to send data to an android phone.

#include <BluetoothSerial.h>
BluetoothSerial SerialBT;
#define deviceName "ESP32-WROOM-32E"

#include <Adafruit_BME280.h>
#define SEALEVELPRESSURE_HPA (1013.25)
unsigned long  sensorUpdateInterval = 10000;

Adafruit_BME280 bme; // I2C

typedef struct __attribute__( ( packed ) )
{
  float temperature;
  float humidity;
  float pressure;
  bool updated = false;
  bool simulate = false;
} sensor_data_t;

sensor_data_t sensorData;

void setup()
{
  Serial.begin(115200);
  SerialBT.begin(deviceName); //Bluetooth device name
  delay(5000);
  Serial.println("ESP32 BLE Environmental Sensing BTClassic Example" );
  Serial.println("The device started, now you can pair it with bluetooth!");
  pinMode(16, OUTPUT); //red dual color led
  pinMode(17, OUTPUT); //green dual color led

  if (!bme.begin( BME280_ADDRESS_ALTERNATE ) )
  {
    Serial.println( "BME280 not found. Data simulation enabled" );
    sensorData.simulate = true;
  }
}

void loop()
{
  if (SerialBT.available()>0)//incoming from phone
  {
    char command = (SerialBT.read());
    if(command == 'A')sensorUpdateInterval = 10000;
    if(command == 'B')sensorUpdateInterval = 5000;
  }

  if (SerialBT.connected())
  {
    digitalWrite(17, LOW);//turn on
    digitalWrite(16, HIGH);//turn off
  }
  else
  {
    digitalWrite(17, HIGH);//turn off
    digitalWrite(16, LOW); //turn on
  }

  sensorTask(); //read BME280
  if (sensorData.updated)
  {
    sensorData.updated = false;
    sendData();
  }
}

void sendData()
{
  SerialBT.print( "Temperature: " );
  SerialBT.print( sensorData.temperature, 1 );
  SerialBT.println( " °C" );

  SerialBT.print( "Humidity:    " );
  SerialBT.print( sensorData.humidity, 1 );
  SerialBT.println( " %" );

  SerialBT.print( "Pressure:   " );
  SerialBT.print( sensorData.pressure / 100, 1 );
  SerialBT.println( " hPa" );
  
  SerialBT.println();
}


bool sensorTask()
{
  static uint32_t previousMillis = 0;

  uint32_t currentMillis = millis();
  if ( currentMillis - previousMillis < sensorUpdateInterval )
  {
    return false;
  }
  previousMillis = currentMillis;

  if (!sensorData.simulate )
  {
    sensorData.temperature = bme.readTemperature();
    sensorData.humidity = bme.readHumidity();
    sensorData.pressure = bme.readPressure();
    sensorData.updated = true;
  }
  else
  {
    sensorData.temperature = 21.0 + random(50) / 10.0;
    sensorData.humidity = 20.0 + random(300) / 10.0;
    sensorData.pressure = 97000.0 + random(40000) / 10.0;
    sensorData.updated = true;
  }
  return true;
}

Hey thanks all for the responses and the explanation. So the difference between BLE and Bluetooth classic is that BLE is using services and characteristics to send data and the Bluetooth classic is using something simmilar to serial communications? I assume BLE is better when it comes to energy saving right? Another reason why I have initially selected BLE is due to its power saving capabilities since the device I am programming will be battery powered

Yes, it is in the name. BLE was designed for coin cell powered sensors. Bluetooth Classic devices usually need to be re-charged daily.

The Serial Port Profile (SPP) is one profile in Bluetooth Classic. There are others e.g., for audio streaming, ... .

In general, yes, but there are specific issues with the esp32 and it will consume more power than other BLE solutions.

Actual Power Consumption of ESP32 using Only BLE

BLE taking too much power

You mentioned a GPS modem as the main data sending device. What is the power budget for that?

If you only need bluetooth for reconfiguring a few parameters it's not clear to me that talking to the ESP32 through the GSM and the SMS isn't a better way to go than implementing either bluetooth method as an add on.

Hey thanks all for the responses! I have been further reading about the BLE and bluetooth and got one more question for you guys.

Lets say for this project, I only want to be able to send some hexadecimal command using BT serial app to the device and receive some sort of response.

I can achieve this by using bluetooth classic simple bluetooth example, or I can also achieve the same by using BLE_uart example from arduino.

I do not see any reasons why I wouldnt want to choose BLE over bluetooth simply due to being newer standard and maybe saving more power?

One reason BLE is more power efficient is GATT.

  • First the data is stored as raw data aka a 16-bit int (2 bytes) for the temperature in my example. When you store it as a String ( e.g., 21.5 -> 4 bytes) you need to transfer more bytes.
  • With a BLE UART implementation you additionally need a serial protocol to differentiate the values. This adds more bytes e.g., "Temperature : 21.5".
  • The client can choose which characteristic it wants to read. e.g., if you have a display unit that can show only temperature it can ignore humidity and pressure. With a BLE UART implementation you will always have to send all data values because you do not know what characteristic the client wants to read.

When you are happy to send just a few commands (less than 255) as hex value. You can simply create a characteristic with WRITE property and just store one byte in it. You can use any generic BLE app for that. Create a second characteristic with READ property and write your response code to it.

Thank you very much for the response. This is exactly what I am trying to achieve in my other forum post that you have recently replied regarding the HEX values and strings