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 Replies to “ESP32 Arduino Serial over Bluetooth: Receiving data”

  1. Thank you for your tutorials. I’ve been working on integrating a bluetooth serial port profile on a project and this has been a great help.
    I have a quick question about this: does the BluetoothSerial library handle SDP advertising? And if not, is it possible to use btstack alongside this library to do so? (Or to do it some other way)

    Although I can get this example to work fine, I have a desktop application where I want to be able to scan for available services, but it’s not able to list services offered by the ESP32 (while it does see other devices’ services just fine).

    I am a beginner in all things bluetooth and it is possible that my issue is due to the desktop-side libraries used (Qt and its bluetooth backend on Linux, which I believe is BlueZ), but it seems more likely to me that I am missing something about SDP on the ESP32 side.

    Thanks for any advice you may have!

    1. Hi! Thank you very much for the feedback 🙂

      I’m not sure if the library is handling the advertisement, I haven’t yet checked it from any client discovering services.

      Nonetheless, from the architecture document below, it seems that currently some profiles are already using the SDP functionalities, but there is no API for us to use:
      https://www.espressif.com/sites/default/files/documentation/esp32_bluetooth_architecture_en.pdf

      Unfortunately, they don’t specifically mention SPP (Serial Port Profile, the one used by the BluetoothSerial library) as using it.

      Nonetheless, in IDF, there seems to be an API in the GAP profile to get the remote services of a device (didn’t tested it yet tough):
      http://esp-idf.readthedocs.io/en/latest/api-reference/bluetooth/esp_gap_bt.html?highlight=sdp#_CPPv230esp_bt_gap_get_remote_services13esp_bd_addr_t

      So hopefully, it is a matter of time until there are APIs for us to advertise services.

      I’m not aware on how to make BTStack working on the ESP32, on the Arduino core. But even if it possible, my guess is that it both Bluedroid (the stack used in IDF and consequently on the Arduino core) wouldn’t be able to cohexist, but it is just a guess.

      Nonetheless, one thing you can do is asking around the ESP32 arduino core GitHub if someone has successfully been able to work with BTstack on the Arduino core.

      You can do a quick test with another desktop library to make sure the problem is on the ESP32. In this tutorial, on the Python section, you can check a very simple code using the PyBluez library:
      https://techtutorialsx.com/2017/07/17/esp32-bluetooth-advertising-a-spp-service-with-sdp/

      Hope this helps getting you on the right track 🙂 If you discover something more, please share with us, I’m also interested in SDP working on the Arduino core.

      Best regards,
      Nuno Santos

      1. Thank you so much for your detailed reply!
        I suppose I will be patient, and hope that an API for advertisement is added to IDF. From there it seems like it would be easy to add it to the Arduino library.

        For now I can at least detect the ESP’s address and connect to it (knowing the port to connect to), so I have at least a working demo to work on while I wait for the API to be expanded 🙂

        1. You’re welcome 🙂 Yes after things land in IDF, they usually quickly get to the Arduino core.

          I will also be checking IDF and try to do some tutorials as soon as they launch the new APIs for advertisement.

          Best regards,
          Nuno Santos

  2. Hello, Thank you for this techtutorialsx
    But I can not pair. Windows 10 needs a pin code, on the serial : I have
    [0;31mE (59526) BT: btm_sec_conn_req
    [0m
    [0;31mE (59684) BT: btm_sec_connected
    [0m
    ASSERT_WARN(1 9), in lc_task.c at line 5054

    And when I try the code #1442, I have
    [0;31mE (99708) BT: SDP – Rcvd L2CAP disc cfm, unknown CID: 0x40
    [0m
    [0;31mE (103696) BT: bta_gattc_mark_bg_conn unable to find the bg connection mask for: 00:00:3a:69:af:e2[0m
    (update already done in ..Arduino\hardware\espressif\esp32\tools)
    Can you help me?

    1. Hi! You’re welcome 🙂

      Unfortunately I cannot be of much assistance since I never experienced that issue and I don’t have any windows 10 machine to test.

      At the time I wrote these tutorials, I didn’t find any way to set a pair pin o the ESP32, neither on the Arduino core or IDF.

      My suggestion is that you post your issue on the Arduino core GitHub page, since most likely someone else already tested it on Windows 10 and may have a solution.

      I hope that the support for setting a pairing pin arrives quickly since it is a very useful feature.

      Hope this helps getting you in the right track.

      Best regards,
      Nuno Santos

  3. Hi, can I use esp32 bluetooth serial library to connect with hc-06 bluetooth module?
    Thanks.

    1. Hi!

      I’m not sure, unfortunately never tried to do it.

      I think the HC.06 works as slave in the communication and it is not possible to change it. So, in theory, if you can make the ESP32 operate as master, you may be able to connect both of them.

      Nonetheless, I’ve never explored these roles on the ESP32 and if we can configure them.

      My suggestion is to ask around the ESP32 Arduino core git hub page, so someone there may already been able to do it.

      Let us know if you find any solution, it would be very interesting 🙂

      Best regards,
      Nuno Santos

  4. 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 ?

  5. 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

  6. 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

Leave a Reply