Skip to content

BLE Link

The BLE link is included in the zeloscloud package and requires the ble extra to be installed.

It is designed to streamline communication with Bluetooth Low Energy (BLE) devices. It simplifies interactions by abstracting the complexity of connecting, sending, receiving, and subscribing to notifications over BLE. We currently support a concrete implementation based on the Bleak library.

Read these!

To get a full understanding of this, please read through the bleak documentation and BLE Link API documentation.

Quick Start Guide

This section will walk through creating and using a BleakBleLink.

Basic Pytest Setup

To set up a basic BLE link, you need to initialize it with a name and a configuration dictionary. Here's a quick example to get you started:

conftest.py

import pytest
from zeloscloud.links.ble import BleakBleLink
from zeloscloud.task import Task

@pytest.fixture(scope="session")
def task():
    """Initialize and open a task manager."""
    with Task("task-manager") as task:
        yield task

@pytest.fixture(scope="session")
def ble_link(task):
    """Initialize and open a BLE link."""
    link_config = {
        "address": "AA:BB:CC:11:22:33",
        "timeout": 60.0
    }
    with BleakBleLink(name="ble-link", link_config=link_config, task=task) as ble_link:
        yield ble_link

Pytest Fixtures

We are utilizing pytest fixtures to inject dependencies into our tests and setups. This method ensures that our components are modular and can be tested independently. The ble_link fixture can also be used as a standalone component.

Example Configuration

The link_config dictionary provides configuration options for the BLE link. Below are descriptions and examples for each option. The only required argument is the address of the device you want to connect to; everything else is optional.

link_config = {
    "address": "AA:BB:CC:11:22:33",  # The Bluetooth address of the BLE device to connect to.
    "timeout": 60.0,  # Connection timeout in seconds. Example: 60.0
    "adapter": None,  # The Bluetooth adapter to use. Set to None to use the default adapter. Example: "hci0"
    "mtu": None,  # The maximum transmission unit size. Set to None to use the default MTU. Example: 256
    "disconnected_callback": None,  # A callback function to handle disconnections. Example: my_disconnected_callback
    "scan_filter": {}  # A filter to apply when scanning for devices. Example: {"UUIDs": ["1234"]}
}
  • address: The Bluetooth address of the BLE device to connect to. This should be a string in the format "XX:XX:XX:XX:XX:XX".
  • timeout: The connection timeout in seconds. This should be a floating-point number. For example, 60.0 sets the timeout to 60 seconds.
  • adapter: The Bluetooth adapter to use. If you have multiple Bluetooth adapters, you can specify which one to use. For example, "hci0" specifies the first adapter. Setting this to None will use the default adapter.
  • mtu: The maximum transmission unit size. This sets the maximum size of the data packets sent over the BLE connection. For example, 256 sets the MTU to 256 bytes. Setting this to None will use the default MTU size.
  • disconnected_callback: A callback function to handle disconnections. This should be a function that takes a single argument, the BleakClient instance. For example, my_disconnected_callback is a user-defined function to handle disconnections.
  • scan_filter: A filter to apply when scanning for devices. This is a dictionary that can contain various filtering options. For example, {"UUIDs": ["1234"]} filters the scan results to only include devices advertising the specified UUID.

Example Usage

After setting up the link with the configuration, you can use it in your application to connect to a BLE device, read/write characteristics, and subscribe to notifications. Here's an example showing how to interact with the BleakBleLink (assuming you already have the conftest.py from above).

def test_nrf52_device(ble_link, check)
    # Read temperature characteristic
    temperature_data = ble_link.read_characteristic("1234")
    temperature_value = int.from_bytes(temperature_data, byteorder='little')

    check.that(temperature_value, "is equal to", 25.0)

    # Callback for LED rising edge notification
    def notification_cb(sender, data):
        check.that(data, "is equal to", 1)

    # Set LED to blink and notify on rising edge
    ble_link.write_characteristic("5678", b'\x01\x02\x03')

    # Subscribe to notifications
    ble_link.subscribe_to_notifications("5678", notification_cb)

    # Wait for 5 seconds to receive notifications
    time.sleep(5)

Troubleshooting

Permissions

If you encounter permission issues when using the BLE link, you might need to adjust your system's Bluetooth settings. On Linux, you can use bluetoothctl to manage Bluetooth devices and connections.

If you already have bluetooth started, trying giving it a restart:

sudo systemctl restart bluetooth

If you don't have bluetooth enabled, turn it on:

sudo systemctl enable bluetooth
sudo systemctl start bluetooth

Once this is working, you can try enabling power/agent/scan/discovery with these:

sudo bluetoothctl
[bluetooth] power on
[bluetooth] agent on
[bluetooth] default-agent
[bluetooth] scan on
[bluetooth] discoverable on

Debugging

If you need to debug the BLE connection, you can use the bluetoothctl tool to interact with Bluetooth devices.

bluetoothctl list
bluetoothctl connect <device_address>
[bluetooth] pair <device_address>
[bluetooth] trust <device_address>
bluetoothctl info <device_address>

You can find additional information on setting up here: Managing Bluetooth on Linux with Bluetoothctl

API Reference

See zeloscloud.links.ble in the API Reference.