ESP32 Arduino Bluetooth: Finding the device with Python

The objective of this tutorial is to explain how to configure the ESP32 to act as a discoverable Bluetooth device and then find it using a Python program. The tests of this ESP32 tutorial were performed using a DFRobot’s ESP-WROOM-32 device integrated in a ESP32 FireBeetle board.

Introduction

The objective of this tutorial is to explain how to configure the ESP32 to act as a discoverable Bluetooth device and then find it using a Python program.

The ESP32 will be running the Arduino core. On Python, we will be using Pybluez, a module that will allow us to use the Bluetooth functionalities of a computer that supports this protocol.

Note that the use of Pybluez was already covered in this previous tutorial. As mentioned there, you can get the .exe file from the module’s page and install it.

Since the Bluetooth functionalities have just recently arrived to the Arduino core at the time of writing, you may need to get the latest version from the Github page to be able to follow this tutorial.

On the ESP32 side, we will use some lower level IDF functions to start the Bluetooth stacks and to make the device discoverable. This was already covered in detail on this previous tutorial. You can check here the Bluetooth classic API from IDF.

The Python code

We start our Python script by importing the new library we have just installed. Note that even tough the library is called Pybluez, the actual code module we are going to use is called bluetooth.

import bluetooth

To start the discovery, we simply need to call the discover_devices function. In order for the discovery to return the names of the devices, we pass the value True in the lookup_names parameter of the function. Note that this is an optional parameter that defaults to False and without setting it to True we would only get the address of the devices.

Take in consideration that the execution of this function takes a while, in order to scan the nearby devices. By default, the discovery duration is 8 seconds, and we will not change it.

devices = bluetooth.discover_devices(lookup_names=True)

This function call will return a list that we can iterate in a for in loop, in order to print each device’s information. Each element of the list will contain both the Bluetooth address and the device name. Before this loop, we will also print the size of the list, which will correspond to the number of Bluetooth devices found during the discovery procedure.

Now that we have all the needed libraries included, we need a function to initialize both the controller and host stacks of Bluetooth. This was also covered in greater detail in this previous tutorial.

This function will receive as input a string with the name of the device, which will be seen by other Bluetooth enabled devices when discovering it. Thus, this name should be later obtained in the Python program.

bool initBluetooth(const char *deviceName) {
// Initialization code
}

In the implementation of the function, we will first call the btStart function, to initialize the Bluetooth controller stack. Them we will call the esp_bluedroid_init and esp_bluedroid_enable functions to both init and enable Bluedroid (the host stack).

We will also perform an error checking on the call of each of the previously mentioned functions, so we are sure everything has initialized correctly.

After the initialization, we will set the device name. To do it, we need to call the esp_bt_dev_set_device_name function. This function will receive as input a string with the name of the device. We will use the name which is the argument of our initBluetooth function.

esp_bt_dev_set_device_name(deviceName);

Then we need to make the device discoverable with a call to the esp_bt_gap_set_scan_mode function, passing as input the ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE enumerated value.

esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);

With this, we finish our initBluetooth function. Now we still need to declare and implement a function that will be used to retrieve the Bluetooth address of the ESP32.

We will basically reuse the same function of the already mentioned previous post to get the address. In its implementation, we first call the esp_bt_dev_get_address function to get the six bytes that compose the unique address. Then we will print them in the standard format, which corresponds to printing each byte in hexadecimal, separated by colons.

Moving on to the Arduino setup function, we will start by opening a wired serial connection, to print the results of our program. Note that the Serial object is being used by both our previously declared functions, so we need to make sure it is initialized before using it.

Serial.begin(115200);

Next we call the initBluetooth function, passing as input the name to assign to the ESP32. I’m using “ESP32 BT”, but you can use other name.

initBluetooth("ESP32 BT");

To finalize the setup function, we call the printDeviceAddress function, which will output the Bluetooth address of the ESP32. We will later be able to compare against the one obtained in the Python script.

Testing the code

The first step for testing the code is to compile it and upload it to the ESP32 using the Arduino IDE. If you run into compilation problems, then you may be using an older version of the Arduino core without support for Bluetooth, which you can easily update by following this guide.

When the procedure finishes, simply open the Arduino IDE serial monitor. You should get an output similar to figure 1, which shows the device address getting printed.

Figure 1 – Bluetooth address of the ESP32.

After this, simply run the Python script we have developed on the environment of your choice. I’m running it on IDLE, the Python IDE that comes with the language installation.

You should get an output similar to figure 1, which shows the ESP32 getting detected during the scan. Note that the address matches the one we obtained on the Arduino IDE serial monitor and the device name is the same we specified in the Arduino code.