In this tutorial we will learn how to detect the client connection event when using the Bluetooth Serial library of the Arduino core.
As covered here, this library allows to establish a serial connection over Bluetooth, leveraging the Serial Port Profile (SPP).
This library offers a class called BluetoothSerial that behaves very similarly to the Serial object we use on wired serial communication. This means that we also have methods such as the available, to check if the client sent data, or the read, to read data.
So, a very simple way of implementing the serial communication is polling for incoming data with the available method and then reading the content when it’s available. Nonetheless, polling is not a very optimized approach, since it wastes CPU cycles that, in many cases, could have been used for other useful computation.
Another important thing to take in consideration is that the BluetoothSerial.h library is implemented on top of the IDF Bluetooth APIs, which are event based. An event based approach can lead to a more efficient implementation of our programs.
So, an option could be using these IDF APIs directly on the Arduino core, since we can access them on our programs. Nonetheless, they are much lower level and harder to use, which means we would loose the simplicity of the BluetoothSerial.h library, which makes it really easy to use Bluetooth Classic.
So, we can leverage a new method from the BluetoothSerial class that allows to register a callback function that is triggered when a SPP event occurs. Note that, at the time of writing, this method was recently added, so you should pull the latest changes of the Arduino core Git repository, if you haven’t yet done it.
Note that, in the implementation of the BluetoothSerial.h library, there’s already an internal callback function handling these SPP events. When an event occurs (ex: new client connected, data received, etc..) this function is responsible for handling it. Nonetheless, this function is internal and not exposed to the user of the BluetoothSerial.h API.
So, in the current implementation, at the end of this internal handling function and after the received event is processed, if there’s a user callback function registered, it is called, receiving as arguments the event type and a data structure with some additional parameters for that event. You can check here the implementation file for the BluetoothSerial.h.
This user defined callback function is naturally our responsibility to implement, which gives great flexibility to what we can do.
Note however that both the event type and the mentioned data structure that are passed to our callback are lower level constructs from IDF’s Bluetooth APIs. But, although this makes us looking a little bit to the lower level, it offers a lot of flexibility by allowing us to receive the lower level events without having to do polling, while keeping the rest of the very simple and easy to use BluetoothSerial.h interface.
In our case, one of the events we will look for is the client connection, although there are others that we can leverage, as shown here.
The tests were performed using a DFRobot’s ESP32 module integrated in a ESP32 development board.
We will start our code by including the BluetoothSerial.h library, which exposes the functionality we will need to establish the serial connection over Bluetooth.
#include "BluetoothSerial.h"
Then we will need an object of class BluetoothSerial. We will interact with this object to initialize the Bluetooth interface and to configure the callback function that will listen to the client connection event.
BluetoothSerial SerialBT;
Moving on to the Arduino setup, we will open a regular (wired) serial connection, to later print some messages from our program.
Serial.begin(115200);
After that, we will call the register_callback method on our BluetoothSerial object, in order to register a callback for the Bluetooth related SPP events. Note that we can do this before initializing the Bluetooth interface.
As input, this method receives the callback function that will be executed when a Bluetooth SPP event occurs. This function needs to have the signature defined by the esp_spp_cb_t type, which can be found here.
We will define the callback function below and, for now, we will assume it will be called callback, as shown below.
SerialBT.register_callback(callback);
Next we will initialize the Bluetooth interface by calling the begin method on the BluetoothSerial object. This method receives as input a string with the name we want to assign to the ESP32. This name will be seen by other Bluetooth enabled devices when performing the discovery procedure.
Since this method call returns as output a Boolean value indicating if the initialization was successful, we will use that value for error checking.
if(!SerialBT.begin("ESP32")){ Serial.println("An error occurred initializing Bluetooth"); }else{ Serial.println("Bluetooth initialized"); }
The full setup function can be seen below.
void setup() { Serial.begin(115200); SerialBT.register_callback(callback); if(!SerialBT.begin("ESP32")){ Serial.println("An error occurred initializing Bluetooth"); }else{ Serial.println("Bluetooth initialized"); } }
To finalize, we need to declare our callback handling function which, as already mentioned, needs to follow the signature specified by the esp_spp_cb_t type.
So, this function should return void, receive as first parameter a variable of type esp_spp_cb_event_t and as second parameter a variable of type esp_spp_cb_param_t *.
The first parameter corresponds to an enumerated value that indicates the type of SPP event that has occurred. The second argument, which we will not use on this tutorial, is a pointer to a data structure that contains additional parameters regarding the event that occurred.
void callback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param){ // Callback function implementation }
Note that this function will be called for all the SPP related events. So, it’s our responsibility to choose which events we want to handle and which events we want to ignore. The simplest way of doing it is with IF conditions.
So, since we are going to print a message when detecting the client connection, we need to look for the corresponding SPP event. The list of existing events can be seen here.
In our case, we are going to check for the ESP_SPP_SRV_OPEN_EVT event, which is triggered when a new client connects. When we receive this event, we will simply print a message indicating the client has connected.
if(event == ESP_SPP_SRV_OPEN_EVT){ Serial.println("Client Connected"); }
The full callback function can be seen below. Since we are not interested in any other event type, we don’t any other IF condition.
void callback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param){ if(event == ESP_SPP_SRV_OPEN_EVT){ Serial.println("Client Connected"); } }
The final complete code is shown below.
#include "BluetoothSerial.h" BluetoothSerial SerialBT; void callback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param){ if(event == ESP_SPP_SRV_OPEN_EVT){ Serial.println("Client Connected"); } } void setup() { Serial.begin(115200); SerialBT.register_callback(callback); if(!SerialBT.begin("ESP32")){ Serial.println("An error occurred initializing Bluetooth"); }else{ Serial.println("Bluetooth initialized"); } } void loop() {}
To test the code, simply compile it and upload it to your ESP32 using the Arduino IDE. When the procedure finishes, open the serial monitor using the wired connection serial port and wait for the “Bluetooth initialized” message to be printed.
After this, the ESP32 should become discoverable for other Bluetooth enabled devices. So, pair with the device from a Bluetooth enabled computer.
Once the pairing procedure finishes, a new COM port should be available for connection. On Windows, you can use the device manager to check which COM ports are operating over Bluetooth, as explained in more detail at the end of this post.
Note that for programs that can establish serial connections, such as the Arduino IDE, this serial port should appear as any other regular wired serial port, so you can connect to it like you would do for a wired connection.
So, use a serial program of your choice to connect to the Bluetooth COM port. Note that you should leave the wired serial connection with the ESP32 opened to see the results from the program.
This means that, if you want to use the Arduino serial monitor to also establish the connection with the Bluetooth COM port, you need to open another instance of the IDE and connect from there. Alternatively, you can use other tool such as Putty, which is detailed at the end of this post.
After the Serial over Bluetooth connection is established, go back to the wired connection opened first. You should see a result similar to figure 1, which shows the message corresponding to the detection of the client connection event.
Figure 1 – Detection of the client connection event.