11.Bluetooth low energy (BLE) serial port pass-through

Bluetooth Low Energy (BLE, Bluetooth Low Energy) serial port pass-through is widely used. On Apple's iOS platform, Classic Bluetooth requires MFi certification to connect with Apple's iOS devices. The Bluetooth low energy device does not have this restriction.

The protocol stack and principle of Bluetooth low energy will not be repeated here, there are many related articles and videos. In short, the bluetooth service is provided in the form of a profile, and there are N characters with independent IDs (UUID) under the profile of each service. Each character has different permissions (read, write, notify, indicate). After the user defines the character and combines it with the authority, a complete service can be provided.

What BLE pass-through is actually to establish a BLE Service, and there are 2 characters under this profile.

#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"

One for TX (transmit data) and one for RX (receive data). For this they have different permissions. The following code is to create a new service and character:

// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pTxCharacteristic = pService->createCharacteristic(
pTxCharacteristic->addDescriptor(new BLE2902());
BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(

Next are two callback functions, which are performed when there is a connection and when there is a write RX character:

class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
std::string rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0) {
Serial.print("Received Value: ");
for (int i = 0; i < rxValue.length(); i++)

Finally, the main loop is the control of the connection, which determines whether there is a connection and whether it is disconnected.

if (deviceConnected) {
pTxCharacteristic->setValue(&txValue, 1);
delay(10); // bluetooth stack will go into congestion, if too many packets are sent
// 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;

For the complete code, see the example of the official library: ble_uart, and the debugging tool can use LightBlue.