Brainy Pi

Available to select audience (currently beta)

BLE GATT
If you’re developing for the Internet of Things (IoT) or robotics projects, chances are you’ll need to work with Bluetooth Low Energy (BLE) protocols. In this tutorial, we’ll walk you through creating a BLE GATT client with Brainy Pi and the Python library simplepyble. This code is intended for developers who are already familiar with Python and BLE protocols.

What is a BLE GATT Client?

A GATT client is a device that connects to a BLE peripheral device and reads or writes data to its characteristics. The Generic Attribute Profile (GATT) is a standard way of defining the structure of data exchanged between BLE devices. BrainyPi is a Raspberry Pi-compatible board designed for IoT and robotics projects. It has built-in Wi-Fi and Bluetooth, making it ideal for BLE development.

Prerequisites

Before you begin, you’ll need the following:
  • A Brainy Pi board
  • Rbian OS installed on the Brainy Pi board
  • The simplepyble library installed on the BrainyPi board
You can install simplepyble by running the following command:
pip install simplepyble

Writing Code for a BLE GATT client

Initializing the Bluetooth Adapter on BrainyPi

The first step is to get the Bluetooth adapter available on Brainy Pi and initialize it. The following code imports simplepyble and initializes the Bluetooth adapter:
import simplepyble

DEFAULT_BRAINYPI_ADAPTER_IDX = 0

def on_scan_start():
    """Callback function which runs when Scan is started.
    """
    print("Scan started.")

def on_scan_stopped():
    """Callback function which runs when Scan is stopped.
    """
    print("Scan complete.")

def on_device_found(peripheral):
    """Callback function which runs when peripheral device is found while scanning.
    """
    print(f"Found {peripheral.identifier()} [{peripheral.address()}]")

def main():
    # List all Bluetooth Adapters on the device
    adapters = simplepyble.Adapter.get_adapters()

    if len(adapters) == 0:
        print("No adapters found")

    # Choose the default brainypi adapter
    adapter = adapters[DEFAULT_BRAINYPI_ADAPTER_IDX]

    print(f"Selected adapter: {adapter.identifier()} [{adapter.address()}]")

    # Set callback functions for Scanning
    adapter.set_callback_on_scan_start(on_scan_start)
    adapter.set_callback_on_scan_stop(on_scan_stopped)
    adapter.set_callback_on_scan_found(on_device_found)

Scan for BLE Peripheral Devices

The next step is to scan for BLE peripheral devices. The following code scans for five seconds and lists all the BLE peripheral devices found:
# Scan for 5 seconds
    adapter.scan_for(5000)
    peripherals = adapter.scan_get_results()

Connect to a BLE Peripheral Device

Optionally, you can ask the user to select a peripheral to connect. If you know the MAC address of the device you are connecting to, you can omit this step. The following code connects to the selected BLE peripheral device:
# Ask the user to pick a device to connect to
    print("Please select a peripheral:")
    for i, peripheral in enumerate(peripherals):
        print(f"{i}: {peripheral.identifier()} [{peripheral.address()}]")

    choice = int(input("Enter choice: "))

    # Connect to the selected peripheral device
    peripheral = peripherals[choice]
    print(f"Connecting to: {peripheral.identifier()} [{peripheral.address()}]")
    peripheral.connect()

Get Services and Characteristics of the BLE Device

Once connected to the BLE peripheral device, you can retrieve the services and characteristics it offers. The following code retrieves all the services:
# Get all the services
    services = peripheral.services()
Optionally, you can ask the user to choose a service to interact with. If you know the UUID of the service and characteristic you want to work with, you can omit this step. The following code prompts the user to select a service and characteristic pair:
# Create a list of service and characteristic pairs
    service_characteristic_pair = []
    for service in services:
        for characteristic in service.characteristics():
            service_characteristic_pair.append((service.uuid(), characteristic.uuid()))

    # Ask the user to pick a service/characteristic pair
    print("Please select a service/characteristic pair:")
    for i, (service_uuid, characteristic) in enumerate(service_characteristic_pair):
        print(f"{i}: {service_uuid} {characteristic}")

    choice = int(input("Enter choice: "))
    service_uuid, characteristic_uuid = service_characteristic_pair[choice]

Read Data from the Characteristic

Mow, to read data from a characteristic, you can use the read method. The following code reads the contents of a characteristic:
# Read the contents of the characteristic
    contents = peripheral.read(service_uuid, characteristic_uuid)
    print(f"Contents: {contents}")

Write Data to the Characteristic

Now, to write data to a characteristic, you can use the write_request method. The following code prompts the user to enter the content to write and sends it to the characteristic:
# Ask the user for the content to write
    content = input("Enter content to write: ")

    # Write the content to the characteristic
    peripheral.write_request(service_uuid, characteristic_uuid, str.encode(content))

Notify Characteristic Data Changes

If the characteristic supports notifications, you can subscribe to it and receive notifications when the data changes. The following code subscribes to the characteristic and waits for notifications for five seconds:
def on_notification(data):
        print(f"Notification: {data}")

    # Subscribe to the notification characteristic
    peripheral.notify(service_uuid, characteristic_uuid, on_notification)

    # Wait for notification from the device
    import time
    time.sleep(5)

Example Code

Here’s an example code that puts everything together:
import simplepyble
import time

DEFAULT_BRAINYPI_ADAPTER_IDX = 0

def on_scan_start():
    print("Scan started.")

def on_scan_stopped():
    print("Scan complete.")

def on_device_found(peripheral):
    print(f"Found {peripheral.identifier()} [{peripheral.address()}]")

def on_notification(data):
    print(f"Notification: {data}")

def main():
    adapters = simplepyble.Adapter.get_adapters()

    if len(adapters) == 0:
        print("No adapters found")

    adapter = adapters[DEFAULT_BRAINYPI_ADAPTER_IDX]
    print(f"Selected adapter: {adapter.identifier()} [{adapter.address()}]")

    adapter.set_callback_on_scan_start(on_scan_start)
    adapter.set_callback_on_scan_stop(on_scan_stopped)
    adapter.set_callback_on_scan_found(on_device_found)

    adapter.scan_for(5000)
    peripherals = adapter.scan_get_results()

    print("Please select a peripheral:")
    for i, peripheral in enumerate(peripherals):
        print(f"{i}: {peripheral.identifier()} [{peripheral.address()}]")

    choice = int(input("Enter choice: "))
    peripheral = peripherals[choice]

    print(f"Connecting to: {peripheral.identifier()} [{peripheral.address()}]")
    peripheral.connect()

    services = peripheral.services()

    service_characteristic_pair = []
    for service in services:
        for characteristic in service.characteristics():
            service_characteristic_pair.append((service.uuid(), characteristic.uuid()))

    print("Please select a service/characteristic pair:")
    for i, (service_uuid, characteristic) in enumerate(service_characteristic_pair):
        print(f"{i}: {service_uuid} {characteristic}")

    choice = int(input("Enter choice: "))
    service_uuid, characteristic_uuid = service_characteristic_pair[choice]

    contents = peripheral.read(service_uuid, characteristic_uuid)
    print(f"Contents: {contents}")

    content = input("Enter content to write: ")
    peripheral.write_request(service_uuid, characteristic_uuid, str.encode(content))

    peripheral.notify(service_uuid, characteristic_uuid, on_notification)

    time.sleep(5)


if __name__ == "__main__":
    main()

Running the code

Lets run the code
python3 ble_gatt_client.py

Screenshot_from_2023-05-06_08-48-16

Conclusion

Congratulations! You’ve learned how to create a BLE GATT client with Brainy Pi and Python. You can now scan for BLE peripheral devices, connect to a device, read and write data to characteristics, and receive notifications when the data changes. This knowledge will be valuable for developing IoT and robotics projects that involve BLE communication.
Remember to ensure that you have the necessary hardware and software prerequisites, such as the Brainy Pi board with Rbian OS and the simplepyble library. Feel free to modify the code to suit your specific requirements and explore more advanced features of BLE communication.
Happy coding and have fun building innovative IoT and robotics projects with Brainy Pi and BLE!
0 Comments

Leave a reply

Your email address will not be published. Required fields are marked *

*