ESP32 Arduino Serial over Bluetooth: Receiving data

In this tutorial we will check how to receive data on a serial connection operating over Bluetooth classic. The tests of this ESP32 tutorial were performed using a DFRobot’s ESP-WROOM-32 device integrated in a ESP32 FireBeetle board.

 

Introduction

In this tutorial we will check how to receive data on a serial connection operating over Bluetooth classic.

This tutorial will be very simple since we are going to use the BluetoothSerial library for the Arduino core, which exposes a very high level API much similar to the regular wired serial interface.

Note that, at the time of writing, the code of the mentioned library had just been merged to the Arduino Master branch, so you may need to get the latest changes from there. You can check here how to update your Arduino core version.

The tutorial shown here was based on the Arduino core BluetoothSerial library example, which can be see seen here. It’s a really good example to get started, which I encourage you to try.

If you want to know a little bit more of the lower level Bluetooth functionalities, you can check the Related Posts section at the end of this tutorial, which includes some tutorials on how to use the IDF Bluetooth API on the Arduino core.

The tests of this ESP32 tutorial were performed using a DFRobot’s ESP-WROOM-32 device integrated in a ESP32 FireBeetle board.


The code

We start our code by including the BluetoothSerial.h library, which makes available the Bluetooth related functionalities we will need.

#include "BluetoothSerial.h"

Next we will need an object of class BluetoothSerial. We will use this object to initialize the Bluetooth stacks (controller and host) and to established the serial communication over Bluetooth.

This object works very similarly to the Serial extern variable we use to establish a regular wired serial communication.

BluetoothSerial SerialBT;

Moving on to the Arduino setup function, we start by opening a wired serial connection, so we can print the content we receive via Bluetooth.

Serial.begin(115200);

Next we call the begin method of the BluetoothSerial object, to initialize the Bluetooth interface. This method will handle all the lower level initialization, so we can use the Bluetooth functionality without having to worry about what happens under the hood.

As input, the begin method receives the name we want to assign to the ESP32, which will be shown to other Bluetooth enabled devices when performing a scan.

As output, the method returns a Boolean value indicating if the initialization was correctly performed. Thus, to confirm everything executed fine, we will do an error check wrapping the begin method call.

Note that we are not going to take in consideration the result of this call in the main loop function, which will assume that everything went fine. Nonetheless, for a final robust code, you should handle error situations and act accordingly.

if(!SerialBT.begin("ESP32")){
    Serial.println("An error occurred initializing Bluetooth");
}

Now, on the Arduino main loop, we will handle the received data. Our flow will be very simple and will consist on reading available bytes one by one from the Bluetooth connection and printing them to the wired serial connection.

To check if there are bytes available, we can use the available function of the BluetoothSerial object. This function behaves like the Arduino serial available function, which returns the number of bytes available for reading.

For curiosity, the current implementation of the BluetoothSerial.h uses a FreeRTOS queue under the hood to store the received bytes, which can be seen by analyzing the source code.

Thus, the available method calls the uxQueueMessagesWaiting function, which returns the number of messages available on the queue, which correspond to the number of bytes. If you want to learn more about this FreeRTOS function and how to use it, please check here.

Also, take in consideration that the size of FreeRTOS queues is specified at their creation and in the case of this library it is initialized with 256.

So, in order to get the bytes available, we will start by polling the BluetoothSerial object with the mentioned available method. Since we are going to read the bytes one by one and the ESP32 may have received more, we will poll it in a while loop.

while(SerialBT.available()){
// Handling code here
}

In order to get a byte, if it is available, we simply need to call the read method of the BluetoothSerial object. This will return the value of the byte as an integer.

Note: At the time of writing and by analyzing the source code, this method call is returning 0 when no data is available. Nonetheless, for the wired serial connection implementation, this function returns -1. I’ve opened this GitHub issue to track the correction.

In its implementation, the read method calls the FreeRTOS xQueueReceive function You can also check a tutorial for this functionality here.

SerialBT.read()

In order to write a byte to the wired serial connection, we simply need to call the write method, passing as input the byte to write. To avoid having to declare an intermediate variable, we can directly pass to the write method the return of the BluetoothSerial read method.

while(SerialBT.available()){
    Serial.write(SerialBT.read());
}

To finalize, we make a small delay between each iteration of the Arduino loop, so we are not constantly polling for incoming bytes. You can check the full source code below.

#include "BluetoothSerial.h"

BluetoothSerial SerialBT;

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

  if(!SerialBT.begin("ESP32")){
    Serial.println("An error occurred initializing Bluetooth");
  }
}

void loop() {

  while(SerialBT.available()){
    Serial.write(SerialBT.read());
  }

  delay(50);
}

 

Testing the code

To test the code, simply compile it and upload it to your ESP32 using the Arduino IDE. Then, when the procedure finishes, open the serial monitor using the COM port for the wired connection.

If you see no error message, then the Bluetooth was correctly initialized. If you start a Bluetooth scan with your computer, you should see the ESP32. Pair with it if you haven’t already done so.

Once the pairing finishes, you should have a new COM port available. On Windows 8, it is possible to check it on the Device Manager. If you need more help with the pairing procedure, please check this previous post.

Now, in order to establish the serial communication with the ESP32 over Bluetooth, we will need an additional serial monitor, so we can send the data to the device.

Although we can open a new instance of the Arduino IDE and open a new terminal for the Bluetooth emulated COM port, I’ve been experiencing some crashes with it, so I will be using Putty. Amongst many other features, Putty allows to establish serial connections.

You can give it a try with using two instances of the Arduino IDE since it may be related to my local environment, but if you run into crashes my recommendation is to change to Putty to send the data.

After downloading and opening putty, configure it like is shown in figure 1, on the left. As can be seen, we need to select “Serial” in the connection type radio. After that, put the COM port detected by your computer for the Serial over Bluetooth and select a speed of 115200. Note that other speeds will also work.

ESP32 Arduino Bluetooth Serial PuTTY.png

Figure 1 – Connection using Putty and the Arduino IDE serial monitor.

By default, Putty will not echo the characters inserted in the serial interface and will send them as soon as they are clicked. If you want a behavior more similar to the Arduino IDE where we can type some characters and only send them after clicking enter, then use the configurations of figure 2, before establishing the connection.

As shown, you need to go to the Terminal tab and select “Force On” in both “Local echo” and “Local line editing” options.

Putty activate serial terminal echo.png

Figure 2 – Changing Putty serial configurations to echo inputted chars and wait for enter to send them.

After that you can start sending data to the Bluetooth serial connection, which should be printed in the wired serial connection, as shown in figure 3.

ESP32 Arduino Bluetooth Serial receiving data

Figure 3 – Echoing the Serial over Bluetooth received data.

 

Related posts

 

21 thoughts on “ESP32 Arduino Serial over Bluetooth: Receiving data”

  1. I do not know where to find BluetoothSerial.h for installation into Arduino IDE and use with ESP 32 ???
    Github shows the BluetoothSerial.h but no download etc… no ccp-file…
    Where do I get it ?

  2. I do not know where to find BluetoothSerial.h for installation into Arduino IDE and use with ESP 32 ???
    Github shows the BluetoothSerial.h but no download etc… no ccp-file…
    Where do I get it ?

  3. I tried to compile your sample, there are some libraries needed, do you know where can I find them? i.e. esp_bt_main.h,esp_bt_device.h,esp_bt_defs.h,esp_err,esp_bt_main.h

    1. Hi!

      Unless something has changed in the lower layers, if you are using the latest version of the Arduino core, everything needed should already be there.

      So you should not need to install additional libraries for that, just update to the latest version of the Arduino core.

      Here are the instructions on how to update to the latest version (for Windows):
      https://github.com/espressif/arduino-esp32/blob/master/docs/arduino-ide/windows.md#how-to-update-to-the-latest-code

      Let me know if it helps 🙂

      Best regards,
      Nuno Santos

  4. I tried to compile your sample, there are some libraries needed, do you know where can I find them? i.e. esp_bt_main.h,esp_bt_device.h,esp_bt_defs.h,esp_err,esp_bt_main.h

    1. Hi!
      Unless something has changed in the lower layers, if you are using the latest version of the Arduino core, everything needed should already be there.
      So you should not need to install additional libraries for that, just update to the latest version of the Arduino core.
      Here are the instructions on how to update to the latest version (for Windows):
      https://github.com/espressif/arduino-esp32/blob/master/docs/arduino-ide/windows.md#how-to-update-to-the-latest-code
      Let me know if it helps 🙂
      Best regards,
      Nuno Santos

  5. Nikos Mouratidis

    Hi! Thanks for the detailed tutorials that go beyond the surface! Info on using BT through ESP32 Arduino core is still so scarce and techtutorialsx is like an oasis! 🙂

    I have an issue and I would appreciate if you could offer any info about it.

    I’m using an ESP32 on my project, which is a word clock. You can see it in action here if you’d like: https://youtu.be/dCYm4Cdy3jY

    I’m developing a smartphone app that connects to the ESP32 through BT to control it’s options. On the ESP32 side, I’m using the BluetoothSerial library of the ESP32 Arduino Core.

    Everything works fine in normal situations. Smartphone connects to ESP32, sends commands through BT SPP, ESP32 receives commands, executes them and sends an execution acknowledgement back to the phone. Let’s say for example that the smartphone sends the command “SET COLOR BLUE”, ESP32 gets it, it changes the display color of the clock and responds back with “COLOR SET BLUE”. (it’s much more complicated than that in reality but let me describe it like this for simplicity reasons).

    Now, let’s suppose that while one smartphone is connected to ESP32, a second phone tries to pair with it. Surprisingly enough, ESP32 allows a second pairing to proceed, even if it’s already connected to another device at that time. Not only that, but it allows the second phone to also open a serial interface with it. When that happens, both phones can send data to the ESP32 at the same time. But when ESP32 replies, the reply is only received by the second phone. So, if phone 1 sends a command, it’s received by ESP32, but the response is only received from the second phone. If phone 2 sends a command, everything works OK.

    Do you know if there is a way to limit simultaneous connections to just 1? I’m quite sure there is no such function on BluetoothSerial library but I was hoping there is another more low level way….

    Any ideas are welcome! Thanks again!

    1. Hi!

      Thank you very much for your feedback, I’m very happy to know that you are finding the tutorials useful 🙂

      Indeed, Bluetooth is not very well documented yet and IDF offers a lot more functionalities that are not yet exposed in the Arduino core.

      I’ve not been working a while with the ESP32 Bluetooth, but much likely, in order to solve your problem and with the current Arduino Interface, you will need to change the BluetoothSerial.cpp file:
      https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/src/BluetoothSerial.cpp

      I’ve taken a quick look at the current implementation, and the problem you describe seems to result from the following:
      – When a client connects, the identifier of that client is written on the _spp_client variable.
      – When a new client connects with an already existing client, there’s no check for that and the _spp_client variable is updated with the identifier of the second client (you can check this on the ESP_SPP_SRV_OPEN_EVT handling)
      – When sending data in the _spp_send_buffer function, the current _spp_client variable value is used, so the data is always sent do the last client that has connected.
      – In the ESP_SPP_DATA_IND_EVT handling, it seems that there is no validation to who sent the data, which is why I think you can still receive data from the first client.

      This is my understanding of what is happening under the hood from a quick analysis.

      In theory, you could add a validation if the _spp_client already has a value, then don’t override it.

      Nonetheless, I’m not sure if there’s some way of “rejecting” the second connection, so the client also cannot send commands.

      Also, there may be some kind of parameter to limit the number of simultaneous connections allowed, that I’m not aware of.

      My suggestion is to take a look at the IDF Bluetooth Classic API:
      https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/bluetooth/classic_bt.html

      If you are going to try to change the lib yourself, one good place to ask around is on the IDF GitHub page.

      Nonetheless, I would also recommend you to open an issue in the Arduino core because it could be interesting to parametrize the maximum number of connections using the Arduino APIs, since most likely this is a use case other users might have.

      If you find a solution or some answers to this, please let us know since it will definitely be very interesting for other users. Also, If I come across some solution I’ll make sure to share 🙂

      Btw, very nice project! 🙂

      Hope this helps.

      Best regards,
      Nuno Santos

      1. Man… how can I buy you a beer?

        Your pointers lead me to a workaround in 10 minutes!

        By pointing me to the ESP_SPP_DATA_IND_EVT handling, I did the first modification of the library by changing this:
        case ESP_SPP_SRV_OPEN_EVT://Server connection open
        _spp_client = param->open.handle;
        log_i(“ESP_SPP_SRV_OPEN_EVT”);
        break;

        to this:
        case ESP_SPP_SRV_OPEN_EVT://Server connection open
        if (!_spp_client){
        _spp_client = param->open.handle;
        }
        log_i(“ESP_SPP_SRV_OPEN_EVT”);
        break;

        This first workaround already made half the fix. Even if a second device tried to hijack the BT SPP connection by connecting to the ESP32, all ESP32 BT SPP output was directed to the first connected device.

        Then, I followed your second advice and checked the IDF Bluetooth Classic API. While I didn’t find something limiting the incoming connections, I found esp_spp_disconnect which is used to disconnect a connected device.

        So, finally, I replaced these:
        case ESP_SPP_CLOSE_EVT://Client connection closed
        _spp_client = 0;
        log_i(“ESP_SPP_CLOSE_EVT”);
        break;
        case ESP_SPP_SRV_OPEN_EVT://Server connection open
        _spp_client = param->open.handle;
        log_i(“ESP_SPP_SRV_OPEN_EVT”);
        break;

        With these:
        case ESP_SPP_CLOSE_EVT://Client connection closed
        if(itWasASecondConnection) {
        itWasASecondConnection = false;
        } else {
        _spp_client = 0;
        }
        log_i(“ESP_SPP_CLOSE_EVT”);
        break;
        case ESP_SPP_SRV_OPEN_EVT://Server connection open
        if (!_spp_client){
        _spp_client = param->open.handle;
        } else {
        itWasASecondConnection = true;
        esp_spp_disconnect(param->open.handle);
        }
        log_i(“ESP_SPP_SRV_OPEN_EVT”);
        break;

        Of course I also needed to declare at the top:
        static boolean itWasASecondConnection;

        Now, any attempt for a second connection is immediately rejected.

        You are the man!

        1. I’m very happy to know it worked and your issue is solved. And thanks for sharing your changes to the code, so others are aware of the procedure if they need to do something similar 🙂

          The Bluetooth API from IDF is very rich, and it has many functionalities that are not exposed in the Arduino core.

          It’s usually the best place to look for functionalities that are not available in the BluetoothSerial.h library.

          Nonetheless, they are also not trivial to work with, since we need to handle a lot of the lower level details.

          Besides the IDF API documentation, a very good place to get information about how the BT API works is the IDF Bluetooth examples folder:
          https://github.com/espressif/esp-idf/tree/master/examples/bluetooth

          If you find more interesting functionalities during your work with Bluetooth classic, please let us know since the information about it is still very limited.

          Wish you the best luck with your project 🙂

          Best regards,
          Nuno Santos

  6. Nikos Mouratidis

    Hi! Thanks for the detailed tutorials that go beyond the surface! Info on using BT through ESP32 Arduino core is still so scarce and techtutorialsx is like an oasis! 🙂
    I have an issue and I would appreciate if you could offer any info about it.
    I’m using an ESP32 on my project, which is a word clock. You can see it in action here if you’d like: https://youtu.be/dCYm4Cdy3jY
    I’m developing a smartphone app that connects to the ESP32 through BT to control it’s options. On the ESP32 side, I’m using the BluetoothSerial library of the ESP32 Arduino Core.
    Everything works fine in normal situations. Smartphone connects to ESP32, sends commands through BT SPP, ESP32 receives commands, executes them and sends an execution acknowledgement back to the phone. Let’s say for example that the smartphone sends the command “SET COLOR BLUE”, ESP32 gets it, it changes the display color of the clock and responds back with “COLOR SET BLUE”. (it’s much more complicated than that in reality but let me describe it like this for simplicity reasons).
    Now, let’s suppose that while one smartphone is connected to ESP32, a second phone tries to pair with it. Surprisingly enough, ESP32 allows a second pairing to proceed, even if it’s already connected to another device at that time. Not only that, but it allows the second phone to also open a serial interface with it. When that happens, both phones can send data to the ESP32 at the same time. But when ESP32 replies, the reply is only received by the second phone. So, if phone 1 sends a command, it’s received by ESP32, but the response is only received from the second phone. If phone 2 sends a command, everything works OK.
    Do you know if there is a way to limit simultaneous connections to just 1? I’m quite sure there is no such function on BluetoothSerial library but I was hoping there is another more low level way….
    Any ideas are welcome! Thanks again!

    1. Hi!
      Thank you very much for your feedback, I’m very happy to know that you are finding the tutorials useful 🙂
      Indeed, Bluetooth is not very well documented yet and IDF offers a lot more functionalities that are not yet exposed in the Arduino core.
      I’ve not been working a while with the ESP32 Bluetooth, but much likely, in order to solve your problem and with the current Arduino Interface, you will need to change the BluetoothSerial.cpp file:
      https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/src/BluetoothSerial.cpp
      I’ve taken a quick look at the current implementation, and the problem you describe seems to result from the following:
      – When a client connects, the identifier of that client is written on the _spp_client variable.
      – When a new client connects with an already existing client, there’s no check for that and the _spp_client variable is updated with the identifier of the second client (you can check this on the ESP_SPP_SRV_OPEN_EVT handling)
      – When sending data in the _spp_send_buffer function, the current _spp_client variable value is used, so the data is always sent do the last client that has connected.
      – In the ESP_SPP_DATA_IND_EVT handling, it seems that there is no validation to who sent the data, which is why I think you can still receive data from the first client.
      This is my understanding of what is happening under the hood from a quick analysis.
      In theory, you could add a validation if the _spp_client already has a value, then don’t override it.
      Nonetheless, I’m not sure if there’s some way of “rejecting” the second connection, so the client also cannot send commands.
      Also, there may be some kind of parameter to limit the number of simultaneous connections allowed, that I’m not aware of.
      My suggestion is to take a look at the IDF Bluetooth Classic API:
      https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/bluetooth/classic_bt.html
      If you are going to try to change the lib yourself, one good place to ask around is on the IDF GitHub page.
      Nonetheless, I would also recommend you to open an issue in the Arduino core because it could be interesting to parametrize the maximum number of connections using the Arduino APIs, since most likely this is a use case other users might have.
      If you find a solution or some answers to this, please let us know since it will definitely be very interesting for other users. Also, If I come across some solution I’ll make sure to share 🙂
      Btw, very nice project! 🙂
      Hope this helps.
      Best regards,
      Nuno Santos

      1. Man… how can I buy you a beer?
        Your pointers lead me to a workaround in 10 minutes!
        By pointing me to the ESP_SPP_DATA_IND_EVT handling, I did the first modification of the library by changing this:
        case ESP_SPP_SRV_OPEN_EVT://Server connection open
        _spp_client = param->open.handle;
        log_i(“ESP_SPP_SRV_OPEN_EVT”);
        break;
        to this:
        case ESP_SPP_SRV_OPEN_EVT://Server connection open
        if (!_spp_client){
        _spp_client = param->open.handle;
        }
        log_i(“ESP_SPP_SRV_OPEN_EVT”);
        break;
        This first workaround already made half the fix. Even if a second device tried to hijack the BT SPP connection by connecting to the ESP32, all ESP32 BT SPP output was directed to the first connected device.
        Then, I followed your second advice and checked the IDF Bluetooth Classic API. While I didn’t find something limiting the incoming connections, I found esp_spp_disconnect which is used to disconnect a connected device.
        So, finally, I replaced these:
        case ESP_SPP_CLOSE_EVT://Client connection closed
        _spp_client = 0;
        log_i(“ESP_SPP_CLOSE_EVT”);
        break;
        case ESP_SPP_SRV_OPEN_EVT://Server connection open
        _spp_client = param->open.handle;
        log_i(“ESP_SPP_SRV_OPEN_EVT”);
        break;
        With these:
        case ESP_SPP_CLOSE_EVT://Client connection closed
        if(itWasASecondConnection) {
        itWasASecondConnection = false;
        } else {
        _spp_client = 0;
        }
        log_i(“ESP_SPP_CLOSE_EVT”);
        break;
        case ESP_SPP_SRV_OPEN_EVT://Server connection open
        if (!_spp_client){
        _spp_client = param->open.handle;
        } else {
        itWasASecondConnection = true;
        esp_spp_disconnect(param->open.handle);
        }
        log_i(“ESP_SPP_SRV_OPEN_EVT”);
        break;
        Of course I also needed to declare at the top:
        static boolean itWasASecondConnection;
        Now, any attempt for a second connection is immediately rejected.
        You are the man!

        1. I’m very happy to know it worked and your issue is solved. And thanks for sharing your changes to the code, so others are aware of the procedure if they need to do something similar 🙂
          The Bluetooth API from IDF is very rich, and it has many functionalities that are not exposed in the Arduino core.
          It’s usually the best place to look for functionalities that are not available in the BluetoothSerial.h library.
          Nonetheless, they are also not trivial to work with, since we need to handle a lot of the lower level details.
          Besides the IDF API documentation, a very good place to get information about how the BT API works is the IDF Bluetooth examples folder:
          https://github.com/espressif/esp-idf/tree/master/examples/bluetooth
          If you find more interesting functionalities during your work with Bluetooth classic, please let us know since the information about it is still very limited.
          Wish you the best luck with your project 🙂
          Best regards,
          Nuno Santos

  7. Pingback: ESP32 Arduino Serial over Bluetooth: Client connection event – techtutorialsx

  8. Pingback: ESP32 Arduino Serial over Bluetooth: Client connection event – techtutorialsx

  9. Pingback: ESP32 Arduino Serial over Bluetooth: Client disconnection event – techtutorialsx

  10. Pingback: ESP32 Arduino Serial over Bluetooth: Client disconnection event – techtutorialsx

Leave a Reply