Bluetooth Low Energy (BLE)

Bluetooth Low Energy also works on the same 2.4 GHz ISM frequency band. What this means is that a single antenna can be used for Wi-Fi and both the versions of Bluetooth.

A BLE device can have 5 possible states:

  • Standby
  • Advertising
  • Scanning
  • Initiating
  • Connected

Important Terms in BLE

Let us briefly see some of the important terms associated with BLE.

  • GATT: It is short for Generic Attribute Profile. It defines the specifications for data transfer between BLE devices using Service and Characteristics.
  • Characteristic: Characteristic is a group of information called Attribute and Attribute is a group of information transferred between devices. A characteristic usually contains the following attributes:
  • Value: Data value of the characteristic
  • Declaration: Properties of the characteristic (location, type like read, write, notify, indicate etc.)
  • Description: ASCII String describing the characteristic.
  • Service: A collection of characteristics is called a Service. Each Service has a unique 16-bit or 128-bit ID called UUID.
  • UUID: Universally Unique Identifier is a 128-bit ID given to each service and characteristic in a profile. Use the website UUIDGenerator to generate unique IDs. Each service and characteristic has a unique 16-bit or 128-bit ID called UUID. A sample UUID looks something like this:
    •  583f8b30-74b4-4757-8143-56048fd88b25 

ESP32 BLE Server

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

#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

BLEDevice::init("ESP32-BLE-Server");
BLEServer *pServer = BLEDevice::createServer();
BLEService *pService = pServer->createService(SERVICE_UUID);
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
                CHARACTERISTIC_UUID,
                BLECharacteristic::PROPERTY_READ |
                BLECharacteristic::PROPERTY_WRITE
                                       );

  pCharacteristic->setValue("Hello, World!");
  pService->start();
  //BLEAdvertising *pAdvertising = pServer->getAdvertising();
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);
  BLEDevice::startAdvertising();

  //std::string value = pCharacteristic->getValue();

Callbacks

Characteristic Callbacks

class BLECharacteristicCallbacks {
public:
	typedef enum {
		SUCCESS_INDICATE,
		SUCCESS_NOTIFY,
		ERROR_INDICATE_DISABLED,
		ERROR_NOTIFY_DISABLED,
		ERROR_GATT,
		ERROR_NO_CLIENT,
		ERROR_INDICATE_TIMEOUT,
		ERROR_INDICATE_FAILURE
	}Status;

	virtual ~BLECharacteristicCallbacks();
	virtual void onRead(BLECharacteristic* pCharacteristic);
	virtual void onWrite(BLECharacteristic* pCharacteristic);
	virtual void onNotify(BLECharacteristic* pCharacteristic);
	virtual void onStatus(BLECharacteristic* pCharacteristic, Status s, uint32_t code);
};

class MyCallbacks: public BLECharacteristicCallbacks
{
  void onWrite(BLECharacteristic *pCharacteristic) override
  {
    std::string value = pCharacteristic->getValue();

    if (value.length() > 0)
    {
      Serial.print("New value: ");
      for (int i = 0; i < value.length(); i++)
      {
        Serial.print(value[i]);
      }
      Serial.println();
    }
  }
  
  void onRead(BLECharacteristic *pCharacteristic)override{
    Serial.print("client read value : ");
    Serial.println(pCharacteristic->getValue().c_str());
  }
};

pCharacteristic->setCallbacks(new MyCallbacks());

server callbacks

class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      Serial.println("device connected");
      deviceConnected = true;
    };

    void onDisconnect(BLEServer* pServer) {
      Serial.println("device disconnected");
      deviceConnected = false;
    }
};

pServer->setCallbacks(new MyServerCallbacks());

Descriptors

GATT characteristic descriptors (commonly called simply descriptors) are mostly used to provide the client with metadata (additional information about the characteristic and its value). They are always placed within the characteristic definition and after the characteristic value attribute. Descriptors are always made of a single attribute, the characteristic descriptor declaration, whose UUID is always the descriptor type and whose value contains whatever is defined by that particular descriptor type.

descriptor=new BLEDescriptor("CADE");
descriptor->setValue("new descriptor");
pCharacteristic->addDescriptor(descriptor);

BLE notify

BLE standard defines two ways to transfer data for the server to the client: notification and indication. Notifications and indications are initiated by the Server but enabled by the Client. Notification doesn’t need to be acknowledged, so they are faster and an efficient way to read data continuously. Hence, a server does not know if the message reaches to the client. Indication needs to be acknowledged for communicating. The client sent a confirmation message back to the server, this way server knows that message reached the client. Notification process similar to the reading of data.

class MyCallbacks: public BLECharacteristicCallbacks
{
  void onStatus(BLECharacteristic* pCharacteristic, Status s, uint32_t code)override{
    Serial.print("character status changed : ");
    pCharacteristic->getDescriptorByUUID("")->setCallbacks(new BLEDescriptorCallbacks());
    switch (s)
    {
    case Status::SUCCESS_INDICATE:
      Serial.println("Status::SUCCESS_INDICATE");
      break;
      case Status::ERROR_INDICATE_FAILURE:
      Serial.println("Status::ERROR_INDICATE_FAILURE");
      break;
      case Status::ERROR_INDICATE_TIMEOUT:
      Serial.println("Status::ERROR_INDICATE_TIMEOUT");
      break;
      case Status::ERROR_INDICATE_DISABLED:
      Serial.println("Status::ERROR_INDICATE_DISABLED");
      break;
    }
  } 
};
pCharacteristic = pService->createCharacteristic(
                      CHARACTERISTIC_UUID,
                      BLECharacteristic::PROPERTY_READ   |
                      BLECharacteristic::PROPERTY_WRITE  |
                      BLECharacteristic::PROPERTY_NOTIFY |
                      BLECharacteristic::PROPERTY_INDICATE
                    );
pCharacteristic->addDescriptor(new BLE2902());//add indications and notifications
pCharacteristic->setCallback(new MyCallbacks());
if (deviceConnected) {

   pCharacteristic->setValue(&value, 1); 
   pCharacteristic->notify(); 
   //pCharacteristic->indicate(); 
   value++; 
}