ESP32 Bluetooth Low Energy (BLE) using Arduino IDE

In this tutorial, we will learn about the Bluetooth low energy commonly known as BLE with ESP32, its introduction, working mechanism, architecture, how to configure it with ESP32 BLE server and BLE client. In the end, we will look at an example of an ESP32 BLE server to communicate with an Android app using Arduino IDE.

ESP32 Bluetooth Low Energy (BLE) using Arduino IDE

In this tutorial, we will learn the followings:

ESP32 chip, not only comes with WiFi but also contains Bluetooth and Bluetooth Low energy support.

Bluetooth is known as one of the most commonly used wireless data transfer protocols among devices. It is used by a multitude of devices including wireless headphones, cellphones, laptops, and smartwatches to transfer data. SIG supports two Bluetooth specifications:

  • Classic Bluetooth also known as the common model
  • Bluetooth Low energy also commonly known as BLE or smart Bluetooth

The ESP32 development board comes up with integrated chips which include Bluetooth and supports both of the connections. In this user guide, we will focus on the BLE connection.

You may like to read ESP32 Bluetooth Classic tutorial:

Link to article: Use ESP32 Bluetooth Classic with Arduino IDE

Bluetooth Low Energy (BLE)

Bluetooth Low Energy or simply BLE is designed as a low power consumption version of the generic classic Bluetooth connection. Instead of focusing on high data rates, BLE is optimized for reduced energy consumption and is widely used in applications such as fitness trackers, smartwatches, and battery-operated devices. That is why BLE has become one of the prominent technologies in the internet of things.

Although BLE is suitable for applications where we need to transfer smaller amounts of data over a shorter distance. But its power consumption is approximately 10 times lower than that of classic Bluetooth. This is because BLE activates radio only whenever it is want to transmit or receive data to/from another device otherwise it is in standby mode.

It is important to point out the major differences between BT classic and BLE in terms of electrical specifications. You can view the table below to see some of the differences between the two modes of Bluetooth connections.

Bluetooth ClassicBluetooth Low Energy / BLE
Data Transfer Rate2-3 Mbps1 Mbps
Range~10-100m~50m
Operating Frequency 79 RF 40 RF 
Peak Current Consumption~30mA<15mA
Power Consumption1W0.01-0.5W
Total time to send data100ms3ms
ApplicationsAudio straming or files transferSensor data, Control commands, etc

BLE operates at 2.4 GHz ISM (Industrial, Scientific, and Medical) band and designed to target applications which need to run on batteries for longer periods of time—months, and even year.

Key features

  • Low Power consumption
  • Cost-Effective
  • Faster connection
  • Compact size
  • Secure & reliable

BLE Client and Server

In BLE, there are two types of devices known as the server (Peripheral) and the client (Central). A client can have many simultaneous connections with multiple servers. But a server can have only one connection at a time. Therefore, BLE communication can be either one-to-one or one-to-many communication.

Apart from point-to-point communication, BLE also supports mesh networks and broadcast mode. In broadcast mode, multiple devices connect to a single server. It is a point to a multipoint data communication or one to many. Whereas in a mesh network all the devices are connected so it acts as a multipoint-to-multipoint or many to many communication.

As shown in the figure below, one ESP32 acts as a client (central), and the other ESP32 boards act as a server.

  • The BLE server announces its presence by sending radio advertising packets and ready to accept connection from a client.
  • The BLE client discovers and listens to BLE devices that are advertising. That means the BLE client initiates the process of establishing a connection to the BLE servers.
esp32 ble client server

Note: ESP32 can act both as a server or client. Most importantly, the BLE client can hold connections with multiple servers at the same time.

The client has two modes such as scanning and connected. Similarly, the server also has two modes such as advertising and connected.

Now let’s see how the BLE server and client establised a connection through advertising, scanning and conneting steps.

Advertising

A server advertises on the radio frequency by advertising connectable advertisement packets, its device name, and unique UUID then on another in a process known as frequency hopping. In doing so, it reduces radio interference created from reflected signals or other devices.

Scanning

A client listens to incoming advertisements on the same radio frequency and scans the nearby devices. An advertising event from a server may consist of multiple events. When a client finds a connectable event in an advertising packet, it sends a connection request to a server with a CONNECT_IND packet.

Connecting

In the end, the BLE clients connect to a server by performing several types of handshaking steps.

Note: While connected to a client, the server may not connect to any other device.

Communication

A client can request data from a server through something called a “Characteristic.” Characteristics are provided by the server for the client to read and write data. A Characteristic may have one or more properties, for example, READ or WRITE. Each Characteristic belongs to a Service, which is like a container for Characteristics. This paradigm is called the Bluetooth Generic Attribute Profile (GATT)

GATT Protocol

BLE clients and servers devices transmit and receive data through Generic Attribute Profile (GATT). In this protocol, the central devices act as clients and any peripheral device is the server. The server has Characteristics, Services, and a particular ATT table that is used in the transmission and reception of data. The GATT protocol consists of a hierarchy in which there are different sections known as Profiles, Services, and Characteristics. The figure below shows these different sections in their particular order and positions.

BLE GATT Protocol
GATT Protocol

Profile

At the top of the hierarchy, you can find the Profile which consists of one or more Services. These are the set of Services that are defined by the designers or by Bluetooth Special Interest Group (SIG).

Services

These are sets of simple data for example readings from a sensor and are divided into subcategories known as Characteristics. Pre-defined Services by Bluetooth SIG are available such as reading heart rate, Weight Scale, Blood pressure, etc. To view all the official services, click here.

UUID or Universally Unique Identifier is the unique ID code of a particular Service. It can be 16 or 128 bits depending on the service. We can use the preset UUIDs or customize our own for a particular service. To obtain a unique UUID go to the online website UUIDGenerator.

An example of a UUID obtained from this generator is: c31cb7af-34e2-42eb-8e7e-198230848b4a

Characteristics

Every service has its own Characteristics. These are broken down into a single Characteristic or several Characteristics. They also have unique UUIDs consisting of 16/128 bits. These contain all the useful information in its ‘value’ section and declaration which supplies metadata.

The descriptor is optional and is used in describing the Characteristic value. They contain a different type of metadata through which they expand the Characteristic. The Properties exercise the role of permitting operations including read, notify, write, indicate, or broadcast. These are the functions of a particular value that is defined by property. This property is then linked with a particular Characteristic.

BLE with ESP32 using Arduino IDE

Apart from Wi-Fi which is one of the major features, ESP32 also supports Bluetooth as a dual-mode system. This means that we can configure both the classic Bluetooth as well as the low energy Bluetooth (BLE) in the ESP32 board. In BLE mode, the ESP32 can act as a server as well as a client. Let us look at a few examples in Arduino IDE in either case. Arduino IDE already comes up with the built-in BLE library after we install the ESP32 module in the IDE.

In your Arduino IDE, click on File > Examples > ESP32 BLE Arduino
This will open up several examples to choose from for example BLE_client, BLE_notify, BLE_server etc.

BLE Arduino examples

In this article, we will discuss the examples: BLE_server and then BLE_scan. First, we will set up our ESP32 as a server using the BLE_server example and then we will use the BLE_scan example sketch to detect the scanner.

ESP32 BLE Server

In your Arduino IDE, click Tools > Board and select ESP32. Then click Tools > Port and select the port number through which the ESP32 board is connected. Now go to File > Examples > ESP32 BLE Arduino. You will find a list of examples. Select BLE_server. A new file will open up named ‘BLE_server’ which will contain the program code. I am copying that code down below so that we can understand it step by step.

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

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

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

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

  BLEDevice::init(""Microcontrollerslab ESP32"");
  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 says Neil");
  pService->start();
  // BLEAdvertising *pAdvertising = pServer->getAdvertising();  // this still is working for backward compatibility
  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();
  Serial.println("Characteristic defined! Now you can read it in your phone!");
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(2000);
}

Working Process

This example sketch will follow a series of steps in order which is shown in the process diagram below:

BLE working process
Working Process

How the Code Works?

The code starts off by including all the libraries which are necessary for the BLE server communication, connection and advertisement.

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

As we already know, the BLE service and characteristic have a unique UUID so the next lines define that. The UUIDs which are defined below are the ones that were set in default. You can keep them as it is or generate your own by using the website which was mentioned above.

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

In the setup() function, serial.begin() is used to establish the serial connection between the development board at a baud rate of 115200.

Serial.begin(115200);

Next, define your ESP32 as the BLE device by replacing “Long name works now” with “Microcontrollerslab ESP32” or the name of your choice in the init command.

BLEDevice::init("Microcontrollerslab ESP32");       //Replace with any name

Then create a server by using the createServer() function for the ESP32 board which we specified before.

BLEService *pService = pServer->createService(SERVICE_UUID);

After that, we will create the Characteristic on our server (ESP32). In the createCharacteristic(), we will pass the properties which are PROPERTY_READ and PROPERTY_WRITE. This will grant access to the client connected to the server to read and write the characteristic.

BLECharacteristic *pCharacteristic = pService->createCharacteristic(
                                     CHARACTERISTIC_UUID,
                                     BLECharacteristic::PROPERTY_READ |
                                     BLECharacteristic::PROPERTY_WRITE
                                     );

By using the setValue() method, we will set the ‘Value’ of the characteristic with the text: “Hello World says Neil.” You can change this value according to your liking.

pCharacteristic->setValue("Hello World says Neil");

We will launch the server, using the start() command as shown below.

pService->start();

After the service has started, we will use getAdvertising() to initiate the advertising process so that nearby devices would be able to spot our server and connect with it.

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

In this example code, the loop() function is left empty as we are just initializing the ESP32 as a server and starting its advertising process. Further code can be added inside the loop() function to cater to what happens after a client connects with the ESP32 BLE server.

Upload the above code to first ESP32 board and open serial monitor of Arduino IDE. After that, click enable button of ESP32. You will see that BLE server will start advertising:

ESP32 BLE Server serial console output

ESP32 BLE Scanner

After we have seen how to set up the ESP32 module as a BLE server. Let’s go a little further and see how to scan for nearby BLE server devices with ESP32 BLE scanner. Again, we will use a sample example code from the ESP32 BLE Arduino library.

Click on File > Examples > ESP32 BLE Arduino. You will find a list of examples. Select BLE_scan. A new file will open up named as ‘BLE_server’ which will contain the program code. You can view the code down below:

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>

int scanTime = 5; //In seconds
BLEScan* pBLEScan;

class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
    }
};

void setup() {
  Serial.begin(115200);
  Serial.println("Scanning...");

  BLEDevice::init("");
  pBLEScan = BLEDevice::getScan(); //create new scan
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
  pBLEScan->setInterval(100);
  pBLEScan->setWindow(99);  // less or equal setInterval value
}

void loop() {
  // put your main code here, to run repeatedly:
  BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
  Serial.print("Devices found: ");
  Serial.println(foundDevices.getCount());
  Serial.println("Scan done!");
  pBLEScan->clearResults();   // delete results fromBLEScan buffer to release memory
  delay(2000);
}

Demonstration

In this example code, we will be requiring two ESP32 development boards. One acts as the server and the other device acts as the client. We initialize the first ESP32 as a BLE server which is uploaded with BLE_server code which we discussed above.

ESP32 BLE scan servers

The second ESP32 module is uploaded with the BLE_scan code. Make sure both of your ESP32 boards are powered on and each has a separate sketch uploaded on it. This can be achieved by powering the boards separately. Now, press the ENABLE button on the second ESP32 board which has BLE_scan uploaded to it. It will scan for all the devices which are close by. After a few moments, you will see the following devices which it scanned printed on the serial monitor below:

ESP32 BLE scanner serial console output

Testing ESP32 BLE Server with a Android App

To perform this testing functionality of the ESP32 BLE server on a cell phone we will need a phone which supports BLE connection. After you have got your hands on one, open the Play Store and search for ‘nRF Connect for Mobile.’ This is a free app that will help us to check for the connection. Install the app on your cell phone.

BLE app

Before proceeding further, make sure to change your Bluetooth settings by enabling the Bluetooth adapter in your cell phone as shown in the figure.

BLE bluetooth enable

Press the ENABLE button on your ESP32 board and open the serial monitor.

In this case, the cell phone acts as the client and the ESP32 board which has the BLE_server uploaded on it is the server.

BLE serial monitor demo
Serial Monitor

Now, open the nRF Connect for Mobile application and tap the Scan button. This will scan for all the devices which are close to the cell phone. Thus, our ESP32 will also get scanned and you will be able to see ‘Microcontrollerslab ESP32’ written among the scanned devices as shown below:

BLE app ESP32 demo

Now, tap the Connect button and the following appears on the cell phone screen.

BLE app demo

The Service and the Characteristic which we had defined in the program code can be viewed here. It displays “Hello World says Neil” which we set as our characteristic value and both the service and the characteristic UUIDs are also visible with the properties which we set to Read and write (highlighted in the blue rectangle) in the BLE_server example code.

We can write any message as shown below:

BLE app demo2

Conclusion

Through this article, we learnt about Bluetooth Low Energy and some simple examples in Arduino IDE to get us started with BLE in ESP32.

If you find this ESP32 tutorial useful, you may like to check our other projects and tutorials:

Leave a Comment