Embedded System #7 — Bluetooth Connections in ESP32
Hi, it’s me again, Syafiq Ziyadul Arifin, also known as Safiq. In this seventh project (you can get the list here), I am excited to show you how to use Bluetooth to pass data from your ESP32 to your smartphone. Psst, did you know that there are two types of Bluetooth distinguished by their power consumption?
Components Used:
- ESP32 Devkit V1
- Android smartphone
- Data cable (USB type A to Micro B)
- BMP280
- External LED
- 330 Ω resistor
- Jumper cables (male to male and male to female)
- Breadboard
The Differences
There are several types of Bluetooth technology, including Classic Bluetooth and Bluetooth Low Energy (BLE). Classic Bluetooth is the traditional type of Bluetooth and is suitable for applications that require high data transfer rates, such as audio streaming. It operates on the 2.4 GHz frequency band and has a range of approximately 30 feet.
BLE, also known as Bluetooth Smart or Bluetooth 4.0, is designed for low-power applications that require long battery life, such as wearables and IoT devices. BLE operates on the same 2.4 GHz frequency band as Classic Bluetooth but has a lower power consumption and a shorter range of approximately 100 feet.
There is also Bluetooth 5, which is an upgrade to both Classic Bluetooth and BLE. Bluetooth 5 offers improved data transfer rates, increased range, and better coexistence with other wireless technologies. It also provides support for new Bluetooth applications, such as location-based services and indoor navigation. But I will not talk about Bluetooth 5 here right now, maybe on another chance?
In summary, while Classic Bluetooth is better suited for applications that require high data transfer rates and longer range, BLE is ideal for low-power devices that need to run for a long time on a small battery.
Subproject #1 — Classic Bluetooth
Serial Bluetooth Terminal
You need a Serial Bluetooth Terminal app from Google Play Store to connect a Bluetooth serial from your phone to your ESP32 because the app allows you to establish a wireless Bluetooth connection between your phone and the ESP32 board. This allows you to communicate and exchange data between your phone and the ESP32 board without the need for wired connections.
The Serial Bluetooth Terminal app acts as a serial terminal emulator, providing a graphical user interface (GUI) that allows you to send and receive data over the Bluetooth connection. This makes it easy to interact with your ESP32 board wirelessly using your smartphone.
Circuit
Because this is a simple project and doesn’t require much wiring, I won’t use a simulator such as Wokwi. The images below demonstrate how to construct the circuit.
Once we have verified that everything is accurate, we can proceed to the code for this project.
Code
Below is the code that I have written. In case you don’t have the BMP280 library, you can refer to my other project where I have provided an installation guide for it (here).
#include "BluetoothSerial.h"
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
// constants won't change. They're used here to set pin numbers, etc.
const int ledPin = 23;
const long interval = 10000; // interval at which to publish sensor readings
// variables will change:
unsigned long previousMillis = 0; // stores last time temperature was published
String message = "";
char incomingChar;
float temperature;
float pressure;
float altitude;
// define the BMP sensor and BluetoothSerial
Adafruit_BMP280 bmp;
BluetoothSerial SerialBT;
// check if Bluetooth configs are enabled
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
void setup() {
pinMode(ledPin, OUTPUT);
// initialize Serial and BMP sensor
Serial.begin(115200);
bmp.begin(0x76); // [0x76, 0x77]
SerialBT.begin("ESP32"); // param is the name of the Bluetooth device
Serial.println("The device started, now you can pair it with Bluetooth!");
}
void loop() {
unsigned long currentMillis = millis();
// send temperature readings
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
SerialBT.println("Temperature: " + String(bmp.readTemperature()) + " °C");
SerialBT.println("Pressure: " + String(bmp.readPressure()) + " Pa");
SerialBT.println("Altitude: " + String(bmp.readAltitude()) + " m");
}
// read received messages (LED control command)
if (SerialBT.available()) {
char incomingChar = SerialBT.read();
if (incomingChar != '\n') message += String(incomingChar);
else message = "";
Serial.write(incomingChar);
}
// check received message and control output accordingly
if (message =="led_on") digitalWrite(ledPin, HIGH);
else if (message =="led_off") digitalWrite(ledPin, LOW);
delay(20);
}
The code starts by including necessary libraries such as “BluetoothSerial.h”, “Adafruit_Sensor.h”, and “Adafruit_BMP280.h”. It then declares and initializes various constants, variables, and objects. The ledPin
constant specifies the pin number of an LED connected to the microcontroller.
The code initializes a BMP280 sensor object and a BluetoothSerial
object for Bluetooth communication. It then sets up the Serial communication with a baud rate of 115200 and prints a message to indicate that the device is ready for Bluetooth pairing.
In the loop()
function, the code reads the temperature, pressure, and altitude from the BMP280 sensor and sends the readings via Bluetooth at an interval of 10 seconds. It also checks for incoming messages from the Bluetooth connection and controls the LED output based on the message received. If the message is “led_on”, the LED is turned on, and if the message is “led_off”, the LED is turned off.
The delay()
function is used to ensure a short delay between loop iterations to avoid overloading the system with too many iterations in a short period of time.
How to Use
First, turn on Bluetooth on your smartphone and open the Serial Bluetooth Terminal app. Next, click on the menu icon located on the upper left-hand corner of the app and select “Devices”. Here, you will find the Bluetooth device name of your ESP32. Click on it, and you will be directed to the Terminal page where your Bluetooth connection will start connecting. Once it shows “Connected,” you will be able to view the temperature, pressure, and altitude data from your BMP280 on your smartphone. Since the interval is set to 10000 milliseconds, the sensor readings will be displayed every 10 seconds. You can also turn on and off the LED by typing “led_on” and “led_off,” respectively.
Here is the circuit’s point of view when I type “led_on” and “led_off,” respectively.
Subproject #2 — Bluetooth Low Energy
nRF Connect for Mobile
When working with BLE on the ESP32, you will need to advertise the characteristics of your device so that it can be detected by other devices that are looking for it. To do this, you will need to use an app on your smartphone that can detect BLE devices.
One such app is nRF Connect for Mobile, which is available on the Google Play Store. This app is developed by Nordic Semiconductor, a company that specializes in Bluetooth technology, and it is a powerful tool for developers who are working with BLE.
With nRF Connect for Mobile, you can scan for nearby BLE devices, connect to them, and explore their characteristics. This is particularly useful when you are developing your own BLE devices, as it allows you to test and debug your code and ensure that everything is working correctly.
Circuit
I’m going to be using the same circuit that I used for subproject #1, so if you don’t mind scrolling back a bit, you can take a look at it.
Code
This is the code I have created.
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
// constants won't change. They're used here to set pin numbers, etc.
#define SERVICE_UUID "eac68f42-6151-4835-bae7-b8b0be6f9282"
#define CHARACTERISTIC_UUID_1 "9b9ae509-c7e4-493e-a37a-5f92bdfe98e9"
#define CHARACTERISTIC_UUID_2 "517bafe1-7712-471b-8dbc-e460f5ddd3ba"
#define CHARACTERISTIC_UUID_3 "4582c723-1898-4671-a086-504e5a1e6683"
// generated from https://www.uuidgenerator.net/
const int ledPin = 23;
// variables will change:
String lastInput;
String sensorInput;
// define the BMP sensor and BLE characteristics
Adafruit_BMP280 bmp;
BLECharacteristic *pCharacteristic1, *pCharacteristic2, *pCharacteristic3;
void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(115200);
bmp.begin(0x76); // [0x76, 0x77]
Serial.println("Starting BLE work!");
BLEDevice::init("ESP32"); // param is the name of the BLE device
BLEServer *pServer = BLEDevice::createServer();
BLEService *pService = pServer->createService(SERVICE_UUID);
pCharacteristic1 = pService->createCharacteristic(
CHARACTERISTIC_UUID_1,
BLECharacteristic::PROPERTY_READ
); // use only PROPERTY_READ to make the value immutable
pCharacteristic1->setValue("Medium @syafiqza");
pCharacteristic2 = pService->createCharacteristic(
CHARACTERISTIC_UUID_2,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
); // use both PROPERTY_READ and PROPERTY_WRITE to make it mutable
pCharacteristic3 = pService->createCharacteristic(
CHARACTERISTIC_UUID_3,
BLECharacteristic::PROPERTY_READ
);
pService->start();
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
pAdvertising->setMinPreferred(0x12);
BLEDevice::startAdvertising();
Serial.println("Characteristic defined! Now you can read it in your phone!");\
}
void loop() {
// turn on the LED if the value is set to "led_on"
if(pCharacteristic2->getValue() == "led_on") digitalWrite(ledPin, HIGH);
// turn off the LED if the value is set to "led_off"
else if (pCharacteristic2->getValue() == "led_off") digitalWrite(ledPin, LOW);
else pCharacteristic2->setValue("Input command here");
lastInput = pCharacteristic2->getValue().c_str();
sensorInput = "Temperature: " + String(bmp.readTemperature()) + " °C\n" + "Pressure: " + String(bmp.readPressure()) + " Pa\n" + "Altitude: " + String(bmp.readAltitude()) + " m";
pCharacteristic3->setValue(std::string(sensorInput.c_str()));
delay(2000);
}
Let’s break down the code.
The SERVICE_UUID and CHARACTERISTIC_UUIDs are used to define the UUIDs for the BLE service and characteristics, respectively. The ledPin
variable is the pin number for the LED. The lastInput
and sensorInput
variables store the last input value and sensor readings, respectively.
The setup()
function sets up the program by initializing the LED pin and the BMP280 sensor, as well as initializing the BLE device and creating a BLE server and service. The three BLE characteristics are also defined here. The startAdvertising()
function is used to advertise the BLE service UUID and set other parameters for iPhone compatibility. Lastly, the function prints a message to the serial monitor to indicate that the characteristic is now defined.
The loop()
function is where the main program logic occurs. First, it checks the value of the second characteristic (pCharacteristic2
) and turns on or off the LED accordingly. If the value is not “led_on” or “led_off”, it sets the value to “Input command here”. The last input value is then stored in the lastInput
variable.
Next, the BMP280 sensor readings are stored in the sensorInput
variable as a string. This string is then used to set the value of the third characteristic (pCharacteristic3
).
Finally, there is a delay of 2000 milliseconds before the loop repeats again. This delay is used to prevent the program from overwhelming the BLE connection with too many updates.
How to Use
To access the ESP32’s services, you’ll need to open the nRF Connect for Mobile app on your smartphone and ensure that your Bluetooth and location are enabled. Once you’ve done that, click the “Scan” button in the upper right-hand corner of the page. If the app says “No devices found,” you’ll need to click the EN button on your ESP32.
After that, your ESP32 should show up on the app, and you can click “Connect” to establish a connection. There will be three services available, with the one we created being the least service (named “Unknown Service”). Click on it to proceed.
Within the service, there are three characteristics available. The first characteristic is used to credit the author’s Medium (which is mine XD), the second is used to take input for the LED light, and the third shows the sensor read. To access the values of each characteristic, click the download/read icon on the right side of the characteristic*.
To make the characteristics more readable, hold each one and click the “Edit” icon on the top of the page. Rename the characteristics to “My Medium,” “Input Command,” and “Sensor Read,” respectively, and change the value format to “TEXT.”
To write a value to turn the LED on or off, click the upload icon on the second characteristic, and enter a value of “led_on” or “led_off.” Don’t forget to change the value format to “TEXT.”
Congratulations! You have successfully finished your seventh project using the ESP32 and the Arduino IDE. You are now able to transfer data using Classic Bluetooth and BLE technology. Take advantage of this feature to send and receive information wirelessly. If you have any questions, feel free to reach out to me. Happy tinkering!
(*) Trivia:
- To update the value of the characteristics, you will need to click on the read icon each time. While this may seem like a minor inconvenience, it’s actually an important aspect of how BLE works. The read icon is what triggers the device to send the current value of the characteristic to the client, and doing so only when requested helps to conserve power and reduce unnecessary data transfer.