MicroPython ubluetooth Example: A Comprehensive Guide

MicroPython is a lean and efficient implementation of the Python 3 programming language that includes a small subset of the Python standard library and is optimised to run on microcontrollers and constrained systems. The ubluetooth module in MicroPython provides support for Bluetooth Low Energy (BLE) functionality, allowing microcontrollers to communicate wirelessly with other BLE devices such as smartphones, tablets, and other microcontrollers. In this blog post, we will explore the fundamental concepts, usage methods, common practices, and best practices of using the ubluetooth module in MicroPython through practical examples.

Table of Contents#

  1. Fundamental Concepts
  2. Usage Methods
  3. Common Practices
  4. Best Practices
  5. Conclusion
  6. References

Fundamental Concepts#

Bluetooth Low Energy (BLE)#

BLE is a wireless personal area network technology designed for short-range communication with low power consumption. It operates in the 2.4 GHz ISM band and uses a connection-oriented or connectionless communication model. BLE devices can act as either a central device (e.g., a smartphone) or a peripheral device (e.g., a sensor).

GATT (Generic Attribute Profile)#

GATT is a framework for sending and receiving short pieces of data known as "attributes" over a BLE link. It defines how data is structured and exchanged between BLE devices. GATT uses a hierarchical structure consisting of services, characteristics, and descriptors.

  • Service: A collection of related characteristics. For example, a heart rate service may contain a characteristic for the current heart rate value.
  • Characteristic: A data value within a service. It has a unique UUID (Universally Unique Identifier) and can have properties such as read, write, and notify.
  • Descriptor: Additional information about a characteristic, such as its format or measurement units.

UUID#

A UUID is a 128-bit number used to uniquely identify services, characteristics, and descriptors in the BLE protocol. There are standard UUIDs defined by the Bluetooth SIG (Special Interest Group) for common services and characteristics, as well as custom UUIDs that can be used for proprietary applications.

Usage Methods#

Initializing the Bluetooth Module#

The first step in using the ubluetooth module is to initialize the Bluetooth controller. Here is an example of how to do this:

import ubluetooth
 
# Initialize Bluetooth
ble = ubluetooth.BLE()
ble.active(True)  # Turn on Bluetooth

Advertising as a Peripheral#

To make a MicroPython device discoverable by other BLE devices, you need to start advertising. Here is an example of advertising a simple service:

import ubluetooth
import ustruct
 
# Initialize Bluetooth
ble = ubluetooth.BLE()
ble.active(True)
 
# Generate a custom UUID for the service and characteristic
SERVICE_UUID = ubluetooth.UUID("12345678-1234-5678-1234-56789abcdef0")
CHAR_UUID = ubluetooth.UUID("12345678-1234-5678-1234-56789abcdef1")
 
# Define the characteristic with read and notify properties
CHAR = (CHAR_UUID, ubluetooth.FLAG_READ | ubluetooth.FLAG_NOTIFY,)
SERVICE = (SERVICE_UUID, (CHAR,),)
SERVICES = (SERVICE,)
 
# Register the service and characteristic with the Bluetooth stack
((char_handle,),) = ble.gatts_register_services(SERVICES)
 
# Set the advertising payload
adv = bytearray()
adv.extend(ustruct.pack("BB", 2, 1, 0x06))  # Advertising flags
adv.extend(ustruct.pack("BB", len(SERVICE_UUID) + 1, 0x03, bytes(SERVICE_UUID)))  # Service UUID
 
# Start advertising
ble.gap_advertise(100, adv_data=adv)

Connecting as a Central Device#

If you want to connect to a peripheral device, you need to scan for available devices and then establish a connection. Here is an example:

import ubluetooth
 
# Initialize Bluetooth
ble = ubluetooth.BLE()
ble.active(True)
 
# Scan for devices
def scan_callback(event, data):
    if event == ubluetooth.SCAN_RESULT:
        addr_type, addr, adv_type, rssi, adv_data = data
        print("Found device:", bytes(addr))
 
ble.gap_scan(2000, 30000, 30000, scan_callback)

Common Practices#

Handling Characteristic Read and Write Operations#

When a central device reads or writes a characteristic on a peripheral device, the peripheral device needs to handle these operations. Here is an example of how to handle a characteristic read operation:

import ubluetooth
import ustruct
 
# Initialize Bluetooth
ble = ubluetooth.BLE()
ble.active(True)
 
# Generate a custom UUID for the service and characteristic
SERVICE_UUID = ubluetooth.UUID("12345678-1234-5678-1234-56789abcdef0")
CHAR_UUID = ubluetooth.UUID("12345678-1234-5678-1234-56789abcdef1")
 
# Define the characteristic with read and notify properties
CHAR = (CHAR_UUID, ubluetooth.FLAG_READ | ubluetooth.FLAG_NOTIFY,)
SERVICE = (SERVICE_UUID, (CHAR,),)
SERVICES = (SERVICE,)
 
# Register the service and characteristic with the Bluetooth stack
((char_handle,),) = ble.gatts_register_services(SERVICES)
 
# Set the value of the characteristic
value = 42
ble.gatts_write(char_handle, ustruct.pack("<h", value))
 
# Handle characteristic read events
def bt_irq(event, data):
    if event == ubluetooth.CENTRAL_CONNECT:
        print("Central connected")
    elif event == ubluetooth.CENTRAL_DISCONNECT:
        print("Central disconnected")
    elif event == ubluetooth.GATTS_READ_REQUEST:
        conn_handle, attr_handle = data
        print("Read request received")
 
ble.irq(bt_irq)

Sending Notifications#

If a characteristic has the notify property, the peripheral device can send notifications to the central device when the characteristic value changes. Here is an example:

import ubluetooth
import ustruct
import time
 
# Initialize Bluetooth
ble = ubluetooth.BLE()
ble.active(True)
 
# Generate a custom UUID for the service and characteristic
SERVICE_UUID = ubluetooth.UUID("12345678-1234-5678-1234-56789abcdef0")
CHAR_UUID = ubluetooth.UUID("12345678-1234-5678-1234-56789abcdef1")
 
# Define the characteristic with read and notify properties
CHAR = (CHAR_UUID, ubluetooth.FLAG_READ | ubluetooth.FLAG_NOTIFY,)
SERVICE = (SERVICE_UUID, (CHAR,),)
SERVICES = (SERVICE,)
 
# Register the service and characteristic with the Bluetooth stack
((char_handle,),) = ble.gatts_register_services(SERVICES)
 
# Set the initial value of the characteristic
value = 0
ble.gatts_write(char_handle, ustruct.pack("<h", value))
 
# Send notifications every second
while True:
    value += 1
    ble.gatts_write(char_handle, ustruct.pack("<h", value))
    ble.gatts_notify(0, char_handle)
    time.sleep(1)

Best Practices#

Use Standard UUIDs#

Whenever possible, use standard UUIDs defined by the Bluetooth SIG for common services and characteristics. This makes your application more interoperable with other BLE devices.

Optimize Power Consumption#

BLE is designed for low power consumption, but you can further optimize power usage by reducing the advertising interval, minimizing the amount of data transmitted, and using sleep modes when the device is idle.

Error Handling#

Implement proper error handling in your code to handle situations such as connection failures, disconnections, and invalid data. This will make your application more robust and reliable.

Conclusion#

The ubluetooth module in MicroPython provides a powerful and flexible way to add Bluetooth Low Energy functionality to microcontrollers. By understanding the fundamental concepts, usage methods, common practices, and best practices, you can develop robust and efficient BLE applications. Whether you are building a sensor network, a smart home device, or a wearable device, MicroPython and ubluetooth can help you achieve your goals.

References#