Use Bluetooth Low Energy

Connect to Bluetooth Low Energy (Bluetooth LE or BLE) devices from QML.

The BLE protocol provides a simple and interoperable way of describing devices and services. The QML API wraps the Qt Bluetooth Low Energy implementation. This provides you with a fast and simple way to integrate IoT, wearable and custom devices to your apps and games.

Platform support

Currently the following platforms are supported:

  • Android
  • iOS
  • macOS
  • Linux (Using the BlueZ stack 4.x/5.x)

Bluetooth LE Protocol Introduction

We will refer to the Generic Attribute Profile (GATT) provided on top of the Attribute Protocol (ATT) as “Bluetooth LE”. Even though there are multiple protocols and profiles defined in the Bluetooth 4.0+ specifications, the former is the most commonly used for wearables and custom devices.

The Bluetooth LE is a client-server protocol. Peripherals behave as servers, providing services such as Heart Rate monitors. Servers advertise themselves and control connection using the Bluetooth LE GAP protocol. Clients consume the services and are also known as central devices. Some devices can advertise but will not accept connections. One such example are beacons, providing unidirectional data broadcast.

Services

Bluetooth LE services group one or more input and output logical nodes, called Characteristics. Services are identified by a single 128bit unique identifier UUID. Short 16 bit identifier versions are used for standard services. Custom services must use full size 128 bit identifiers. Devices usually also provide a name string for the service.

Characteristics

Are the actual logical input and output nodes. Like services a 128 bits unique identifier is used, and 16 bit short versions are provided for official definitions. Data can be read, write or both as well as notified for changes from the server. Characteristics enclose descriptors that contain metadata about the characteristic and it’s value as well as properties that carry information about read and write permissions, enable notifications, among other usages.

Binary data is used as a characteristic value. It is up to the peripheral or central to make sense of the data transferred, and must know the format, size and order beforehand. The characteristic presentation format descriptor might be provided by the server to help the central parse and format the binary data.

Predefined services are provided by the Bluetooth SIG as XML representations that depict standard use cases with the corresponding configurations for services, characteristics and descriptors.

Available QML Items

BluetoothLeCharacteristic

Bluetooth Low Energy characteristics handle data read and write

BluetoothLeConnectivity

Bluetooth Lowe Energy device discovery and connection management

BluetoothLeDevice

Bluetooth Low Energy Device, discovered by the BluetoothLeConnectivity component

BluetoothLeService

Bluetooth Low Energy Service, groups Characteristics

Bluetooth LE Examples

Emulate Devices Example

You can quickly test a Bluetooth connection using two mobile devices, at least one of them an Android device. Install the BLE Peripheral Simulator app on an android device and run the Battery Service emulation.

 import Felgo 3.0
 import QtQuick 2.0

 App {
   NavigationStack {
     Page {
       title: qsTr("BLE Battery")

       Rectangle {
         anchors.centerIn: parent
         width: parent.width * 0.6
         height: parent.height * 0.8
         radius: dp(30)
         border.color: 'black'
         border.width: dp(5)

         Rectangle {
           anchors.bottom: parent.bottom
           width: parent.width
           height: parent.height * batteryCharacteristic.value / 100
           color: batteryCharacteristic.value > 80 ? 'green' : (batteryCharacteristic.value > 30 ? 'orange' : 'red')
           radius: parent.radius
         }
       }

       AppText {
         anchors.centerIn: parent
         text: batteryCharacteristic.value + '%'
         fontSize: dp(15)
       }
     }
   }

   BluetoothLeConnectivity {
     discoveryRunning: true

     BluetoothLeService {
       uuid: 0x180F // Battery Service

       BluetoothLeCharacteristic {
         id: batteryCharacteristic
         uuid: 0x2A19 // Battery Characteristic
         dataFormat: 4 // 0x04 for uint8

         onValueChanged: {
           // Value updates
           console.log('Battery Level Changed', value)
         }

         onValidChanged: {
           // Read initial value once characteristic is valid
           if (valid) {
             read()
           }
         }
       }
     }

     onDeviceDiscovered: {
       // To keep it simple, set your device reported name here
       console.log('device', device.name)
       if (device.name == 'your-device-name') {
         connect(device)
         discoveryRunning = false
       }
     }

     onConnectedChanged: {
       // Reconnect logic
       if (!connected) {
         console.log('Trying to reconnect')
         reconnect()
       }
     }
   }
 }

Set your device name in the onDeviceDiscovered callback, once your Felgo application discovers the emulated device a connection request is sent, service discovery requires an active connection, after you set the device in the connect method a service discovery starts. An automatic write to the Notify property is sent so updates are sent from the server device.

You can slide the battery control and press the notify button, immediately the BluetoothLeCharacteristic value is updated in your QML code.

ESP32 Example

The ESP32 is a powerful Microcontroller Unit that incorporates WiFI and Bluetooth LE radio and stack. You can use the ESP32 Arduino libraries to test Characteristic read, notification and write with your Felgo apps.

Notify Example

Use the BLE_notify example for the ESP32 from the Arduino file menu, upload your code to the device. Run the following example in your Felgo QML application. Once connected a value notification is sent every 3 ms which shows the capabilities of the protocol.

 import Felgo 3.0
 import QtQuick 2.0

 App {
   NavigationStack {

     Page {
       title: qsTr("ESP32 Notify")

       AppText {
         anchors.centerIn: parent
         text: notifyCharacteristic.value
         fontSize: dp(15)
       }
     }
   }

   BluetoothLeConnectivity {
     discoveryRunning: true

     BluetoothLeService {
       uuid: '{4fafc201-1fb5-459e-8fcc-c5c9c331914b}' // Custom notify Service

       BluetoothLeCharacteristic {
         id: notifyCharacteristic
         uuid: '{beb5483e-36e1-4688-b7f5-ea07361b26a8}' // Value
         dataFormat: 8 // For uint32
       }
     }

     onDeviceDiscovered: {
       // To keep it simple, set your device reported name here
       if (device.name == 'ESP32') {
         connect(device)
         discoveryRunning = false
       }
     }

     onConnectedChanged: {
       // Reconnect logic
       if (!connected) {
         console.log('Trying to reconnect')
         reconnect()
       }
     }
   }
 }

Read & Write

This example reads a string from the ESP32 device and writes a string to the same characteristic. A formatting function shows how to parse a string to a Javascript ArrayBuffer. Writing sends raw binary data to the BLE device. After writing a read is triggered to update the value, since notifications are not supported in the custom device. Inspecting the ESP32 serial output confirms the write is successful.

 import Felgo 3.0
 import QtQuick 2.0

 App {
   NavigationStack {

     Page {
       title: qsTr("ESP32 Read & Write")
       Column {
         anchors.centerIn: parent
         spacing: dp(15)

         AppText {
           anchors.horizontalCenter: parent.horizontalCenter
           text: stringCharacteristic.value
           fontSize: dp(15)
         }

         AppTextField {
           anchors.horizontalCenter: parent.horizontalCenter
           id: textField
           text: 'Hello from Felgo!'
           width: dp(200)
         }

         AppButton {
           anchors.horizontalCenter: parent.horizontalCenter
           text: 'Write'
           enabled: stringCharacteristic.valid && bleConnectivity.connected
           onClicked: {
             textField.focus = false
             stringCharacteristic.formatWrite(textField.text)
             stringCharacteristic.read()
           }
         }
       }
     }
   }

   BluetoothLeConnectivity {
     id: bleConnectivity
     discoveryRunning: true

     BluetoothLeService {
       uuid: '{4fafc201-1fb5-459e-8fcc-c5c9c331914b}' // Custom read/write Service

       BluetoothLeCharacteristic {
         id: stringCharacteristic
         uuid: '{beb5483e-36e1-4688-b7f5-ea07361b26a8}' // Value
         dataFormat: 0x19 // For uint32

         function formatWrite(text) {
           let data = new Uint8Array(text.length)
           for (var i = 0; i < text.length; i++) {
             data[i] = text.charCodeAt(i)
           }
           write(data.buffer)
         }

         onValidChanged: {
           // Read initial value once characteristic is valid
           if (valid) {
             read()
           }
         }
       }
     }

     onDeviceDiscovered: {
       // To keep it simple, set your device reported name here
       console.log(device.name)
       if (device.name == 'MyESP32') {
         connect(device)
         discoveryRunning = false
       }
     }

     onConnectedChanged: {
       // Reconnect logic
       if (!connected) {
         console.log('Trying to reconnect')
         reconnect()
       }
     }
   }
 }

Considerations

  • Android
    • The ACCESS_FINE_LOCATION permission must be included in your manifest and accepted by the user.
    • The service list for devices is cached, a BT stack reset might be required to refresh discovered device services.
    • All services are presented as primary, there’s no information to identify secondary services from the OS.
  • iOS
    • Bluetooth on/off state is not provided by the OS, it’s determined from events provided by the underlying API.
    • Device addresses are not accessible, device dependent UUIDs are provided instead.

Further Reading

Voted #1 for:

  • Easiest to learn
  • Most time saving
  • Best support

Develop Cross-Platform Apps and Games 50% Faster!

  • Voted the best supported, most time-saving and easiest to learn cross-platform development tool
  • Based on the Qt framework, with native performance and appearance on all platforms including iOS and Android
  • Offers a variety of plugins to monetize, analyze and engage users
FREE!
create apps
create games
cross platform
native performance
3rd party services
game network
multiplayer
level editor
easiest to learn
biggest time saving
best support