Skip to Content
This is the Beta version of our new Learning Paths approach. Please email feedback.

Bluetooth

Bluetooth is a short-range wireless technology standard used for exchanging data between fixed and mobile devices over short distances using ultra-high frequency (UHF) radio waves, from 2.402 GHz to 2.48 GHz. Bluetooth uses low-power transmission which gives it a very short range of up to 10 metres but with the benefit of low battery power consumption.

Bluetooth and Wi-Fi are similar in that both are used to exchange data between devices. The difference is that Wi-Fi is access point-centered, with all traffic routed through the access point, while Bluetooth is symmetrical, with data exchanged directly between two Bluetooth devices. Bluetooth serves well in simple applications where two devices need to connect without a dependency on additional devices such as those required for a WiFi access point.

While Ethernet, WiFi, and cellular connections are networking protocols and can be used for data transfer across large networks, Bluetooth is a device-to-device communication protocol, not a networking protocol.

Bluetooth is widely used to exchange files between two devices and connect smartphones to audio playback systems such as headphones, eliminating the need for wires. Bluetooth is also used to connect smartphones to Arduino circuits and IoT appliances to send control signals over a very short distance.

Bluetooth Classic Illustration

There are two variants of Bluetooth:

Classic Bluetooth

This is the original version of Bluetooth with a limitation that it can only have two devices connected and communicating with each other at one time.

To overcome these limitations and to improve its capability a newer version of Bluetooth (BLE) was introduced which is soon likely to become the de-facto standard.

Bluetooth Low Energy (BLE)

Bluetooth Low Energy is called so because it is a variant of Bluetooth that conserves energy by remaining in sleep mode constantly except for when a connection is initiated, unlike classic Bluetooth that is always on. This makes BLE particularly suitable for IoT applications where the sensors and devices sending data need a longer battery life.

BLE is not simply an upgrade to Bluetooth Classic, it is a different protocol altogether. It is meant for scenarios where you need to exchange small packets of data infrequently (such as sending sensor data to an app at periodic intervals), as against exchanging large amounts of data over a coninuous connection (such as streaming music from your smartphone to a speaker), which is better achieved by Bluetooth Classic.

BLE also supports broadcast communication in addition to point-to-point communication.

BLE Illustration

Central and Peripheral Devices

BLE devices operate and interact with each other in one of two roles: Central or Peripheral.

  • Central Device: Central devices are generally the processing devices with higher computing power, more memory, and more battery power. Central devices are responsible for initiating connections to peripheral devices. For example, your smartphone is often a central device which connects to and reads data from BLE peripherals such as sensors connected to a BLE-enabled Arduino board.

  • Peripheral Device: Peripheral devices advertise their presence and availability to send data to central devices and wait for a connection to be initiated by a central device. Peripheral devices generally have lesser resources (computing power, memory, battery power) compared to central devices

A Central Device can connect to multiple Peripherals simultaneously and a Peripheral can connect to multiple Central Devices simultaneously.

Peripheral Services and Characteristics

BLE uses a concept of Services and Characteristics to manage data exchange between devices.

A service is a logical grouping of related data points provided by a peripheral device. For example, a weather monitoring sensor (peripheral) could have a service that provides temperature, humidity, and wind speed data. All peripherals could have a service that provides data related to battery level and energy consumption.

Each service has one or more characteristics, which are the individual data points provided by the service. In the weather monitoring service, temperature, humidity, and wind speed would be the characteristics. And in the battery information service, battery level and energy consumption would be the characteristics. Data is read from or written to a characteristic.

There are some standard services and characteristics that many commonly used devices use so that any central device can connect to it and know what data it can expect. You can also specify custom services and characteristics.

Universally Unique Identifiers (UUIDs)

Services and characteristics have what are known as Universally Unique Identifiers (UUIDs). A UUID is a universally unique 128-bit number which has a specific format as shown below:

55072829-bc9e-4c53-938a-74a6d4c78776

If your solution is expected to work in the public domain, you will have to ensure the UUIDs you use are indeed unique. If your solution is expected to work in a private domain you can generate UUIDs using an online generator.

Advertising and Connection Modes

  • A peripheral starts off in the advertising mode making it discoverable by central devices. In the advertising mode, the peripheral device periodically broadcasts advertising packets. These packets contain information about the peripheral’s identity, services, and characteristics. Central devices continuously scan for these advertising packets to discover nearby peripherals. In advertising mode, the peripheral device is not actively connected to any central device. It remains in a low-power state while broadcasting advertising packets, waiting for a central device to establish a connection.

  • The peripheral goes into the connection mode once a central device successfully establishes a connection with it. In this mode, central devices can read data from and write data to the peripheral device. The peripheral goes back to the advertising mode when the central device disconnects from it.

Notify

The BLE specification includes a mechanism known as notify. When notify on a characteristic is enabled and the sender (peripheral) writes to it, the new value is automatically sent to the receiver (central device), without the receiver explicitly issuing a read command. There’s a variation on this specification called indicate which works similarly, but in the indicate specification, the receiver sends an acknowledgment of the pushed data.

The client-server structure of BLE, with the central device acting as the client and the peripheral acting as the server, combined with the notify characteristic, is referred to as a publish-and-subscribe model. This is simlar to the concept used in MQTT or other messaging architectures.

In this project we will learn to program a board to use Bluetooth Low Energy (BLE) to connect with and exchange data with a BLE mobile app. We will use one of several open source apps on Android and iOS are available to communicate via BLE.

While there is a BLE module that connects to an Arduino board (the HM-10) it is not very easy to configure. Therefore, for BLE we will use the ESP32 which has built-in support for BLE.

Components
ComponentPurpose
ESP32The microcontroller.
Circuit Diagram
nano_serial_bluetooth_fritzing
Connections

No assembly required. Simply connect the ESP32 to your computer running the Arduino IDE.

Code
/* This is a combination of the example code for Notify and Write. Notify is to send data from the BLE board (this code) to the app. Write is to receive data from the app to the BLE board (this code). Video: https://www.youtube.com/watch?v=oCMOYS71NIU Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp Ported to Arduino ESP32 by Evandro Copercini updated by chegewara Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleWrite.cpp Ported to Arduino ESP32 by Evandro Copercini Create a BLE server that, once we receive a connection, will send periodic notifications. The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8 The design of creating the BLE server is: 1. Create a BLE Server 2. Create a BLE Service 3. Create a BLE Characteristic on the Service 4. Create a BLE Descriptor on the characteristic 5. Start the service. 6. Start advertising. A connect handler associated with the server starts a background task that performs notification every couple of seconds. // See the following for generating UUIDs: // https://www.uuidgenerator.net/ The UUIDs will be generated on the app for each BLE board (service and characteristics). Use the same UUIDs here. There will be a SENSOR_DATA service which will send sensor data to the app. and a CONTROL_SIGNAL service which will receive data from the app. Each sensor and controlled device will be a characteristic. */ // BLE Library is installed when the ESP32 board is added to the IDE #include <BLEDevice.h> #include <BLEServer.h> #include <BLEUtils.h> #include <BLE2902.h> #include <BLE2901.h> BLEServer *pServer = NULL; BLECharacteristic *sensor1_characteristic = NULL; BLE2901 *descriptor_2901 = NULL; bool deviceConnected = false; bool oldDeviceConnected = false; uint32_t value = 0; uint32_t rand_value = 0; // See the following for generating UUIDs: // https://www.uuidgenerator.net/ #define PERIPHERAL_NAME "SmartHome Comm" // Number of services and characteristics per service are both limited to 7 // To be updated with the app generated UUIDs #define SENSOR_DATA_SERVICE "49b8a4c2-35db-46a5-804e-e24aa49f38d9" #define SENSOR_1_CHARACTERISTIC "7eaab413-63e5-47b9-a37c-315b382ab396" #define CONTROL_SIGNAL_SERVICE "d7b470a7-a590-44e8-8c5e-6045fff9d345" #define DEVICE_1_CHARACTERISTIC "ebf06ac0-9674-479b-ac3b-f05c9b32670a" class MyServerCallbacks : public BLEServerCallbacks { void onConnect(BLEServer *pServer) { deviceConnected = true; }; void onDisconnect(BLEServer *pServer) { deviceConnected = false; } }; class ControlSignalCallbacks : public BLECharacteristicCallbacks { void onWrite(BLECharacteristic *pCharacteristic) { Serial.println("*** Value received ***"); String value = pCharacteristic->getValue(); Serial.print(value.toInt()); if (value.length() > 0) { Serial.println("*********"); Serial.print("New value: "); for (int i = 0; i < value.length(); i++) { Serial.print(value[i], DEC); } Serial.println(); Serial.println("*********"); } } }; void setup() { Serial.begin(115200); // Create the BLE Device with the name that will be broadcast. BLEDevice::init(PERIPHERAL_NAME); // Create the BLE Server pServer = BLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks()); // Create the SENSOR_DATA BLE Service which will send data to the app using the notify function, called when sensor data is recieved. BLEService *sensor_data_service = pServer->createService(SENSOR_DATA_SERVICE); // Create a BLE Characteristic sensor1_characteristic = sensor_data_service->createCharacteristic( SENSOR_1_CHARACTERISTIC, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_INDICATE ); // Creates BLE Descriptor 0x2902: Client Characteristic Configuration Descriptor (CCCD) sensor1_characteristic->addDescriptor(new BLE2902()); // Adds also the Characteristic User Description - 0x2901 descriptor descriptor_2901 = new BLE2901(); descriptor_2901->setDescription("Temperature Sensor Data."); descriptor_2901->setAccessPermissions(ESP_GATT_PERM_READ); // enforce read only - default is Read|Write sensor1_characteristic->addDescriptor(descriptor_2901); // A value set using setValue is read by the app using BleClient.read(...) // But a more practical approach is to use notifications in a loop. sensor1_characteristic->setValue("Ready to start sending data..."); // Start the service sensor_data_service->start(); // Start advertising BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); pAdvertising->addServiceUUID(SENSOR_DATA_SERVICE); pAdvertising->setScanResponse(true); pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter BLEDevice::startAdvertising(); Serial.println("Waiting a client connection to notify..."); // Create the CONTROL_SIGNAL BLE service. BLEService *control_signal_service = pServer->createService(CONTROL_SIGNAL_SERVICE); BLECharacteristic *pCharacteristic = control_signal_service->createCharacteristic(DEVICE_1_CHARACTERISTIC, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE); pCharacteristic->setCallbacks(new ControlSignalCallbacks()); pCharacteristic->setValue("Listening for control signals..."); control_signal_service->start(); // BLEAdvertising *pAdvertising = pServer->getAdvertising(); // pAdvertising->start(); } void loop() { // notify changed value if (deviceConnected) { // print a random number from 10 to 19 rand_value = random(0, 120); Serial.println(rand_value); // sensor1_characteristic->setValue((uint8_t *)&value, 4); // Unsigned 8-bit integer, 4-byte buffer // sensor1_characteristic->notify(); // Simply sending the value without any casting and size parameter works. // Will allow 32 bit unsigned integers since value has been declared of type uint32_t // uint16_t should also be enough, uint8_t will only allow 256 values. sensor1_characteristic->setValue(rand_value); sensor1_characteristic->notify(); // value=value+100; delay(10000); } // disconnecting if (!deviceConnected && oldDeviceConnected) { delay(500); // give the bluetooth stack the chance to get things ready pServer->startAdvertising(); // restart advertising Serial.println("start advertising"); oldDeviceConnected = deviceConnected; } // connecting if (deviceConnected && !oldDeviceConnected) { // do stuff here on connecting oldDeviceConnected = deviceConnected; } delay(10000); }

Test with an app

You can use one of several BLE Test Apps for Android and iOS to test your code.

⚠️

We do not recommend any specific app, rather we leave it to you to do your research and find a reliable and safe app.