In this tutorial, we will learn how to use Bluetooth Classic on ESP32 with ESP-IDF. We’ll establish bidirectional data communication between the ESP32 and an Android phone over Bluetooth Classic. We will see an example of controlling the onboard LED of the ESP32 by sending ON and OFF commands from an Android app.
We will program ESP32 with ESP-IDF in VS Code. Before we move ahead, make sure you have the latest version of VS Code installed on your system with the ESP-IDF extension configured.
Bluetooth Classic
Bluetooth Classic, also referred to as “Bluetooth BR/EDR” (Basic Rate/Enhanced Data Rate), stands as one of the primary Bluetooth communication protocols alongside Bluetooth Low Energy (BLE). With its inception in 1999, Bluetooth Classic emerged as the original version of Bluetooth, offering a reliable solution for applications that necessitate faster data transfer rates.
Range
Bluetooth Classic is a wireless communication technology that functions in the 2.4 GHz ISM (Industrial, Scientific, and Medical) band. It facilitates reliable connectivity between devices within a close range, usually no more than 10 meters. With a maximum data transfer speed of up to 3 Mbps, Bluetooth Classic is ideal for various applications like streaming audio, transferring files, and connecting wireless peripherals such as keyboards, mice, and speakers.
Advantages
Bluetooth Classic offers backward compatibility with older Bluetooth devices, enabling seamless connectivity between different generations of Bluetooth-enabled products. This crucial feature has led to its widespread acceptance across a wide range of industries, including consumer electronics, automotive systems, and industrial applications.
Bluetooth Classic, which was once the prevailing wireless communication protocol, is now being increasingly overshadowed by Bluetooth Low Energy. While Bluetooth Classic excels in applications requiring higher data rates and continuous data streaming, the advent of Bluetooth Low Energy has prompted developers to consider this more energy-efficient option. With the ever-evolving technology landscape, it is essential for developers to weigh the specific requirements of their projects when choosing between Bluetooth Classic and Bluetooth Low Energy, as each protocol caters to different use cases in wireless communication.
ESP32 Bluetooth Classic
The ESP32 microcontroller is incredibly versatile and powerful. One of its standout features is its ability to function both as a Bluetooth Classic device (using Bluetooth Basic Rate/Enhanced Data Rate) and a Bluetooth Low Energy (BLE) device.
Using Bluetooth Classic on the ESP32 allows for seamless connections with other Bluetooth Classic devices like smartphones, computers, and peripherals. This enables the exchange of data over short distances, making it possible to send and receive information such as audio, files, and commands. Bluetooth Classic is particularly useful for applications that require higher data transfer rates, making it perfect for tasks like audio streaming or sending large amounts of data quickly.
In addition to Bluetooth Classic, the ESP32 also supports BLE, which operates with lower power consumption. BLE is well-suited for wireless sensor applications and projects where energy efficiency is a priority. Combined with the ESP32’s Bluetooth Classic capabilities, developers have the freedom to choose the most suitable Bluetooth technology for their specific project requirements.
The ESP32’s support for both Bluetooth Classic and BLE offers a wide range of possibilities for wireless applications. Whether it’s in the Internet of Things (IoT), home automation, or smart devices field, the ESP32 empowers developers to create innovative and functional solutions.
Android Smart Phone Terminal Application
Today, we will explore the process of connecting an ESP32 development board to an Android smartphone. This exciting venture requires two essential components: an Android phone and a Bluetooth terminal application. By utilizing a Bluetooth terminal app, we can establish a seamless connection between these devices, enabling efficient communication. Let’s get started by ensuring you have both an Android phone and a Bluetooth terminal app readily available for the upcoming steps.
To access the Serial Bluetooth Terminal app, simply head over to the Play Store and download it. You can find the app by searching for its name or by using the following link: Serial Bluetooth Terminal. Once installed, you can launch the app and start using its features.
Create ESP32 ESP-IDF Project for Bluetooth Classic
In this section, let’s create an ESP-IDF project to use Bluetooth Classic of ESP32.
Open your VS Code and head over to View > Command Palette. Type ESP-IDF: New Project in the search bar and press enter.
Specify the project name and directory. For the ESP-IDF board, we have chosen the custom board option. For ESP-IDF target, choose ESP32 module. Click ‘Choose Template’ button to proceed forward.
In the Extension, select the ESP-IDF option:
We will click the ‘sample_project’ under the get-started tab. Now click ‘Create a project using template sample_project.’
You will get a notification that the project has been created. To open the project in a new window, click ‘Yes.’
This opens the project that we created inside the EXPLORER tab. There are several folders inside our project folder. This is the same for every project which you will create through ESP-IDF Explorer.
Code
This code uses Bluetooth classic of ESP32 to establish a Serial Port Profile (SPP) server. The program sets up an SPP server, which can receive and send data over a Bluetooth connection. It uses the ESP-IDF framework to configure and manage Bluetooth communication.
The Bluetooth Serial Port Profile (SPP) server is a Bluetooth profile that emulates the behavior of a traditional serial port (RS-232) over a wireless Bluetooth connection. It allows for the wireless transfer of data between devices as if they were connected via a physical serial cable. This profile is commonly used in applications where a wireless serial communication link is needed.
The code initializes a GPIO LED on pin 2 and responds to Bluetooth data received over the SPP profile. When data is received, it checks if the data is “ON” or “OFF” and toggles the LED accordingly. Additionally, it handles Bluetooth events, such as initialization, connection, and data reception. The application can be controlled remotely over Bluetooth using a client that sends commands like “ON” or “OFF” to control the LED’s state.
It also handles Bluetooth authentication and security settings. If a client tries to connect to the ESP32 device, the code specifies how authentication should occur, such as using a fixed PIN or other methods.
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include "nvs.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_gap_bt_api.h"
#include "esp_bt_device.h"
#include "esp_spp_api.h"
#include "driver/gpio.h"
#include "time.h"
#include "sys/time.h"
#define SPP_SERVER_NAME "SPP_SERVER"
#define EXAMPLE_DEVICE_NAME "ESP32test"
#define SPP_SHOW_DATA 0
#define SPP_SHOW_SPEED 1
#define SPP_SHOW_MODE SPP_SHOW_DATA /*Choose show mode: show data or speed*/
#define LED_PIN 2
const char *tag = "Bluetooth";
static const esp_spp_mode_t esp_spp_mode = ESP_SPP_MODE_CB;
static const esp_spp_sec_t sec_mask = ESP_SPP_SEC_AUTHENTICATE;
static const esp_spp_role_t role_slave = ESP_SPP_ROLE_SLAVE;
static void init_led(void)
{
gpio_reset_pin(LED_PIN);
//gpio_pad_select_gpio(LED_PIN);
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
ESP_LOGI(tag, "Init led completed");
}
static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
{
switch (event)
{
case ESP_SPP_INIT_EVT:
ESP_LOGI(tag, "ESP_SPP_INIT_EVT");
esp_spp_start_srv(sec_mask, role_slave, 0, SPP_SERVER_NAME);
break;
case ESP_SPP_DISCOVERY_COMP_EVT:
ESP_LOGI(tag, "ESP_SPP_DISCOVERY_COMP_EVT");
break;
case ESP_SPP_OPEN_EVT:
ESP_LOGI(tag, "ESP_SPP_OPEN_EVT");
break;
case ESP_SPP_CLOSE_EVT:
ESP_LOGI(tag, "ESP_SPP_CLOSE_EVT");
break;
case ESP_SPP_START_EVT:
ESP_LOGI(tag, "ESP_SPP_START_EVT");
esp_bt_dev_set_device_name(EXAMPLE_DEVICE_NAME);
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
break;
case ESP_SPP_CL_INIT_EVT:
ESP_LOGI(tag, "ESP_SPP_CL_INIT_EVT");
break;
case ESP_SPP_DATA_IND_EVT:
ESP_LOGI(tag, "ESP_SPP_DATA_IND_EVT len=%d handle=%d",
param->data_ind.len, param->data_ind.handle);
esp_log_buffer_hex("", param->data_ind.data, param->data_ind.len);
char received_data[5];
snprintf(received_data, sizeof(received_data), "%s", param->data_ind.data);
if (strncmp(received_data, "ON", 2) == 0)
{
gpio_set_level(LED_PIN, 1); // turn on the LED
ESP_LOGI(tag, "Setting LED to ON");
}
else if (strncmp(received_data, "OFF", 3) == 0)
{
gpio_set_level(LED_PIN, 0); // turn off the LED
ESP_LOGI(tag, "Setting LED to OFF");
}
esp_spp_write(param->data_ind.handle, param->data_ind.len, param->data_ind.data);
break;
case ESP_SPP_CONG_EVT:
ESP_LOGI(tag, "ESP_SPP_CONG_EVT");
break;
case ESP_SPP_WRITE_EVT:
ESP_LOGI(tag, "ESP_SPP_WRITE_EVT");
break;
case ESP_SPP_SRV_OPEN_EVT:
ESP_LOGI(tag, "ESP_SPP_SRV_OPEN_EVT");
break;
case ESP_SPP_SRV_STOP_EVT:
ESP_LOGI(tag, "ESP_SPP_SRV_STOP_EVT");
break;
case ESP_SPP_UNINIT_EVT:
ESP_LOGI(tag, "ESP_SPP_UNINIT_EVT");
break;
default:
break;
}
}
void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
{
switch (event)
{
case ESP_BT_GAP_AUTH_CMPL_EVT:
{
if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS)
{
ESP_LOGI(tag, "authentication success: %s", param->auth_cmpl.device_name);
esp_log_buffer_hex(tag, param->auth_cmpl.bda, ESP_BD_ADDR_LEN);
}
else
{
ESP_LOGE(tag, "authentication failed, status:%d", param->auth_cmpl.stat);
}
break;
}
case ESP_BT_GAP_PIN_REQ_EVT:
{
ESP_LOGI(tag, "ESP_BT_GAP_PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit);
if (param->pin_req.min_16_digit)
{
ESP_LOGI(tag, "Input pin code: 0000 0000 0000 0000");
esp_bt_pin_code_t pin_code = {0};
esp_bt_gap_pin_reply(param->pin_req.bda, true, 16, pin_code);
}
else
{
ESP_LOGI(tag, "Input pin code: 1234");
esp_bt_pin_code_t pin_code;
pin_code[0] = '1';
pin_code[1] = '2';
pin_code[2] = '3';
pin_code[3] = '4';
esp_bt_gap_pin_reply(param->pin_req.bda, true, 4, pin_code);
}
break;
}
#if (CONFIG_BT_SSP_ENABLED == true)
case ESP_BT_GAP_CFM_REQ_EVT:
ESP_LOGI(tag, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %d", param->cfm_req.num_val);
esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true);
break;
case ESP_BT_GAP_KEY_NOTIF_EVT:
ESP_LOGI(tag, "ESP_BT_GAP_KEY_NOTIF_EVT passkey:%d", param->key_notif.passkey);
break;
case ESP_BT_GAP_KEY_REQ_EVT:
ESP_LOGI(tag, "ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!");
break;
#endif
case ESP_BT_GAP_MODE_CHG_EVT:
ESP_LOGI(tag, "ESP_BT_GAP_MODE_CHG_EVT mode:%d", param->mode_chg.mode);
break;
default:
{
ESP_LOGI(tag, "event: %d", event);
break;
}
}
return;
}
void app_main(void)
{
init_led();
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE));
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK)
{
ESP_LOGE(tag, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
if ((ret = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK)
{
ESP_LOGE(tag, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
if ((ret = esp_bluedroid_init()) != ESP_OK)
{
ESP_LOGE(tag, "%s initialize bluedroid failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
if ((ret = esp_bluedroid_enable()) != ESP_OK)
{
ESP_LOGE(tag, "%s enable bluedroid failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
if ((ret = esp_bt_gap_register_callback(esp_bt_gap_cb)) != ESP_OK)
{
ESP_LOGE(tag, "%s gap register failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
if ((ret = esp_spp_register_callback(esp_spp_cb)) != ESP_OK)
{
ESP_LOGE(tag, "%s spp register failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
if ((ret = esp_spp_init(esp_spp_mode)) != ESP_OK)
{
ESP_LOGE(tag, "%s spp init failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
#if (CONFIG_BT_SSP_ENABLED == true)
/* Set default parameters for Secure Simple Pairing */
esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE;
esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO;
esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));
#endif
/*
* Set default parameters for Legacy Pairing
* Use variable pin, input pin code when pairing
*/
esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_VARIABLE;
esp_bt_pin_code_t pin_code;
esp_bt_gap_set_pin(pin_type, 0, pin_code);
}
Download the complete project from this link:
Demonstration
In this section, we will see a demo. First, let’s flash the code to ESP32. To flash your chip, type the following command in the serial terminal. Remember to replace the COM port with the one through which your board is connected.
idf.py -p COMX flash monitor
After the code flashes successfully, you can view all the informational logs.
Now go to your mobile app which you installed earlier.
Enable Bluetooth in the settings of your smartphone and then install the ‘Serial Bluetooth Terminal’ app. Once installed, open the app and tap the three horizontal bars located at the top-left corner of the screen.
Proceed by selecting the Devices tab.
Please choose the ‘ESP32test’ option from the list of available devices in the ‘available devices’ section.
Once you have chosen your ESP32 module, simply click on the ‘link’ icon located at the top of the screen. Initially, you will receive a message saying ‘Connecting to ESP32test.’ Once the connection is established successfully, you will be notified with a message saying ‘Connected.’
Now send ON and OFF messages from the Android app as shown below, you will see that the onboard LED of ESP32 will also turn on and turn off according to these commands:
As you can also see the on serial console, messages are getting displayed about the LED getting on and off.
Conclusion
In summary, we learned how to establish two-way communication with ESP32 and Android via Bluetooth Classic using esp-idf framework.
You may also like to read:
- ESP32 ESP-IDF Send Email Notifications with IFTTT
- ESP32 Internal Temperature Sensor with ESP-IDF
- ESP32 ESP-IDF OpenWeatherMap API Sensorless Weather Station
- ESP32 ESP-IDF Send SMS with Twilio
- ESP32 ESP-IDF Send Sensor Readings to ThingSpeak Cloud Platform
We are a team of experienced Embedded Software Developers with skills in developing Embedded Linux, RTOS, and IoT products from scratch to deployment with a demonstrated history of working in the embedded software industry. Contact us for your projects: admin@esp32tutorials.com
I can’t build it. i have fatal error: esp_bt.h: no such file or directory. If I use BT example, i can build it. Please could you explain what i have wrong?
Rename the file `sdkconfig.old` to `sdkconfig`
The IDF creates its default config with BT turned off. The existing file `sdkconfig.old` contains the correct setting, but is incorrectly named and IDF does not pick it up.
I have opened a PR to fix this: https://github.com/ESP32Tutorials/esp32-bluetooth-classic-esp-idf/pull/1
Hi guys, I tried your example project but I have an issue. I receive an event 10 message on my terminal. I can see the device in on my phone in Bluetooth devices but no in the app. Pls. See what I get in my VS Code terminal:
I (592) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (602) gpio: GPIO[2]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (612) Bluetooth: Init led completed
I (632) BTDM_INIT: BT controller compile version [2c56073]
I (632) system_api: Base MAC address is not set
I (632) system_api: read default base MAC address from EFUSE
I (642) phy_init: phy_version 4670,719f9f6,Feb 18 2021,17:07:07
I (1122) Bluetooth: ESP_SPP_INIT_EVT
I (1122) Bluetooth: ESP_SPP_START_EVT
I (1132) Bluetooth: event: 10
perfect tutorial, i need lvgl tutorial please with tft display
is it possible to do with MIT APP Inventor2
ESP-IDF Bluetooth classic using MIT APP Inventor