Maker.io main logo

CircuitPython BLE Libraries on Any Computer

2024-03-25 | By Adafruit Industries

License: See Original Project Arduino Circuit Playground Adafruit Feather Raspberry Pi

Courtesy of Adafruit

Guide by Dan Halbert

Overview

three_1

Adafruit CircuitPython supports using Bluetooth Low Energy (BLE) ‎to communicate wirelessly with BLE devices, phones, tablets, and ‎with other CircuitPython boards. Adafruit provides many libraries to ‎make this easy and to support specific devices.‎

Now you can use those same libraries (or write your own) on any ‎host computer--Windows, Mac, or Linux--that has BLE hardware. ‎Most modern computers, especially laptops, already have Bluetooth ‎hardware built in. If not, you can plug in a USB adapter such as ‎Adafruit's Bluetooth 4.0 USB Module.‎

The Adafruit Blinka bleio library makes this possible. It is a regular ‎Python library that runs on desktop Python, not on CircuitPython ‎boards. It re-implements the _bleio module that is part of ‎CircuitPython: all our BLE libraries are ultimately based on _bleio.‎

The Blinka bleio library only supports acting in a BLE central ‎role. You can connect to peripheral devices, such as heart rate ‎monitors, pulse oximeters, bicycle sensors, etc., but you cannot act as ‎a peripheral yourself with this code. ‎

The Blinka bleio library is part of the family of "Blinka" libraries that ‎run under regular Python and implement CircuitPython functionality, ‎including Blinka and Blinka displayio.‎

This guide will explain how to get Python set up on your host ‎computer, how to install the Blinka bleio library, and then give some ‎examples of how to use it.‎

Bluetooth Capable Devices from Adafruit

Some Adafruit Microcontrollers with ‎Bluetooth

Install Python on Your Host Computer

You Need Python 3 and pip3‎

To run CircuitPython BLE libraries on a host computer, you'll need at ‎least Python 3.9, and you'll need the pip3 program to install the ‎libraries. You may have pip already, but often the pip (not pip3) ‎command installs software for Python 2, so make sure you are ‎using pip3.‎

Windows 10‎

You can install Python from https://python.org, or from the Windows ‎Store. See our guide Using Python on WIndows 10 for an easy way to ‎get Python installed.‎

If you don't use the Windows Store version, make sure you check the ‎box to add Python to your PATH when you run the installer. See the ‎screenshot below.‎

install_2

Starting Python 3 on Windows

Depending on how you install Python 3, the command to start it in a ‎command shell in Windows can be different.‎

  • Installed from the https://python.org download:‎
    • Works: python (python.exe)
    • Works:py (py.exe, the Python launcher).
    • Does not work: python3 (python3.exe). You'll get directed to the ‎Windows Store to install Python 3 yet again.‎
  • Installed from the Windows Store:
    • Works: python (python.exe)
    • Does not work: py (the Python launcher is not installed).
    • Works: python3 (python3.exe)

These differences can be confusing; see this page for detailed ‎documentation.‎

macOS

Modern macOS comes with Python 3, but it may be an older version. ‎Instead of using the system-supplied version, we recommend that ‎you use the Homebrew system to install a more recent version of ‎Python 3 and keep it up to date. The Homebrew installation will not ‎interfere with the system-installed Python, and does not replace it.‎

This article describes in detail various ways of managing Python on ‎macOS. It's worth reading to understand the issues, and to see ‎various ways of managing multiple versions of Python.‎

Bluetooth Permissions

A user has reported that, at least as of macOS Big Sur, you must add ‎your terminal application to the Bluetooth Privacy Settings ‎in System Preferences > Security & Privacy > Privacy > Bluetooth.‎

Linux

Modern versions of Linux always come with Python. They may ‎include both Python 2 and Python 3. See if the version supplied with ‎your Linux distribution is at least 3.9. If not, your distribution may ‎allow you to install additional versions that do not interfere with the ‎original system-supplied version. If you can upgrade your ‎distribution, considering doing so. For instance, Ubuntu 22.04 comes ‎with Python 3.10.‎

Make sure you have pip3 installed as well, by trying to run it. If it's not ‎installed, install it in the appropriate way for your Linux distribution. ‎For instance, for Ubuntu and Debian, do:‎

Download File

Copy Code
sudo apt install python3-pip

Raspberry Pi OS

Raspberry Pi OS (the new name for Raspian), which is based on ‎Debian Linux, also comes with Python. The latest version as of this ‎writing (April 2023), bullseye, comes with Python 3.9, invoked with ‎the python3 command.‎

If you are using Raspberry Pi OS Lite, it may not come with pip3. You'll ‎need to install it by doing: ‎

Download File

Copy Code
sudo apt install python3-pip

Do not use sudo when running pip3.‎

Do Not Use sudo When ‎Running pip3

You may see Internet advice to install libraries using sudo pip3 on Linux ‎or MacOS. This is in general a bad idea because you can damage the ‎libraries that the underlying system depends on, and in general end ‎up trashing your system in mysterious ways. Always install ‎using pip3 without sudo. If your pip3 is old, you may need to specify pip3 --‎user, but these days --user is often the default.‎

Here's a detailed discussion of why sudo pip3 is a bad idea.‎

Install BLE Libraries

Once you have installed Python 3 and pip3, you are ready to install the ‎Blinka bleio library and the base CircuitPython BLE library. In your ‎shell, enter this command to install both:‎

Download File

Copy Code
pip3 install --upgrade adafruit-blinka-bleio adafruit-circuitpython-ble

The --upgrade will ensure that you get the latest versions, even if either ‎library was previously installed. The adafruit-blinka-bleio library depends ‎on a number of other libraries, which will be installed automatically, ‎and upgraded if necessary.‎‎ ‎

Linux and Raspberry Pi: Add User ‎to bluetooth Group

On Linux, including on Raspberry Pi, you must also add your user to ‎the bluetooth group. Reboot after doing this to ensure your user is ‎added to the group and also to ensure that ~/.local/bin is added to ‎your path (after doing pip3 above). To add yourself to the group, do ‎this:‎

Download File

Copy Code
sudo usermod -a -G bluetooth $USER
sudo reboot

Raspberry Pi 3B+ and 4B Firmware ‎Fix

Raspberry Pi 3B+ and 4B boards use different hardware for BLE and ‎WiFi than Raspberry Pi 3B and Pi Zero W boards. There was a bug in ‎the firmware for the 3B+ and 4B boards, in the bluez-‎firmware package. bluez-firmware-1.2-4+rpt2 worked, but ‎versions rpt3, rpt4, rpt5, and rpt6 did not. Make sure your version is at ‎least rpt8, as rpt8 includes a security fix does not present in rpt7.‎

Download File

Copy Code
$ apt list bluez-firmware
Listing... Done
bluez-firmware/testing,now 1.2-4+rpt8 all [installed,automatic]
N: There is 1 additional version. Please use the '-a' switch to see it

If you need to upgrade bluez-firmware, do:‎

Download File

Copy Code
$ sudo apt upgrade bluez-firmware

Pulse Oximeter, Heart Rate Monitor, and ‎BBQ Thermometer

Once the base BLE libraries are installed, you can install helper ‎libraries for various third-party BLE peripherals, and access them ‎directly. We'll show examples of peripherals accessed from several ‎different host computers.‎

Pulse Oximeter

This example uses the BerryMed Pulse Oximeter sold by Adafruit. Install ‎the pulse oximeter library by typing the pip3 command below into ‎your shell. Then download the example program below.‎

pulse_3

Download File

Copy Code
pip3 install adafruit-circuitpython-ble-berrymed-pulse-oximeter

Download Project Bundle

Copy Code
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT

"""
Read data from a BerryMed pulse oximeter, model BM1000C, BM1000E, etc.
"""

# Protocol defined here:
# https://github.com/zh2x/BCI_Protocol
# Thanks as well to:
# https://github.com/ehborisov/BerryMed-Pulse-Oximeter-tool
# https://github.com/ScheindorfHyenetics/berrymedBluetoothOxymeter
#
# The sensor updates the readings at 100Hz.

import _bleio
import adafruit_ble
from adafruit_ble.advertising.standard import Advertisement
from adafruit_ble.services.standard.device_info import DeviceInfoService
from adafruit_ble_berrymed_pulse_oximeter import BerryMedPulseOximeterService

# CircuitPython <6 uses its own ConnectionError type. So, is it if available. Otherwise,
# the built in ConnectionError is used.
connection_error = ConnectionError
if hasattr(_bleio, "ConnectionError"):
connection_error = _bleio.ConnectionError

# PyLint can't find BLERadio for some reason so special case it here.
ble = adafruit_ble.BLERadio() # pylint: disable=no-member

pulse_ox_connection = None

while True:
print("Scanning...")
for adv in ble.start_scan(Advertisement, timeout=5):
name = adv.complete_name
if not name:
continue
# "BerryMed" devices may have trailing nulls on their name.
if name.strip("\x00") == "BerryMed":
pulse_ox_connection = ble.connect(adv)
print("Connected")
break

# Stop scanning whether or not we are connected.
ble.stop_scan()
print("Stopped scan")

try:
if pulse_ox_connection and pulse_ox_connection.connected:
print("Fetch connection")
if DeviceInfoService in pulse_ox_connection:
dis = pulse_ox_connection[DeviceInfoService]
try:
manufacturer = dis.manufacturer
except AttributeError:
manufacturer = "(Manufacturer Not specified)"
try:
model_number = dis.model_number
except AttributeError:
model_number = "(Model number not specified)"
print("Device:", manufacturer, model_number)
else:
print("No device information")
pulse_ox_service = pulse_ox_connection[BerryMedPulseOximeterService]
while pulse_ox_connection.connected:
print(pulse_ox_service.values)
except connection_error:
try:
pulse_ox_connection.disconnect()
except connection_error:
pass
pulse_ox_connection = None

View on GitHub

Run the example program, and turn on the pulse oximeter, reading ‎from your finger. Here's a screenshot from running it on Windows, in ‎PowerShell. In this example, I typed ctrl-C after getting a few ‎readings.‎

Pulse Oximeter

Heart Rate Monitor

This is a Schoshe Heart Rate Monitor, which transmits data using the ‎standard BLE Heart Rate Monitor service. Install the heart rate library ‎by typing the pip3 command below into your shell. Then download ‎the example program below.‎

heart_5

Download File

Copy Code
pip3 install adafruit-circuitpython-ble-heart-rate

Download Project Bundle

Copy Code
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT

"""
Read heart rate data from a heart rate peripheral using the standard BLE
Heart Rate service.
"""

import time

import adafruit_ble
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.standard.device_info import DeviceInfoService
from adafruit_ble_heart_rate import HeartRateService

# PyLint can't find BLERadio for some reason so special case it here.
ble = adafruit_ble.BLERadio() # pylint: disable=no-member

hr_connection = None

while True:
print("Scanning...")
for adv in ble.start_scan(ProvideServicesAdvertisement, timeout=5):
if HeartRateService in adv.services:
print("found a HeartRateService advertisement")
hr_connection = ble.connect(adv)
print("Connected")
break

# Stop scanning whether or not we are connected.
ble.stop_scan()
print("Stopped scan")

if hr_connection and hr_connection.connected:
print("Fetch connection")
if DeviceInfoService in hr_connection:
dis = hr_connection[DeviceInfoService]
try:
manufacturer = dis.manufacturer
except AttributeError:
manufacturer = "(Manufacturer Not specified)"
try:
model_number = dis.model_number
except AttributeError:
model_number = "(Model number not specified)"
print("Device:", manufacturer, model_number)
else:
print("No device information")
hr_service = hr_connection[HeartRateService]
print("Location:", hr_service.location)
while hr_connection.connected:
print(hr_service.measurement_values)
time.sleep(1)

View on GitHub

Turn on your heart rate monitor and run the example program. ‎reading from your finger. Here's a screenshot from running it on ‎Ubuntu Linux. In this example, I typed ctrl-C after getting a few ‎readings.‎

Heart rate monitor example program

iBBQ Thermometer

Here's a BLE-enabled food thermometer, which can take two probes. ‎It and similar models are readily available under the "nutrichef" ‎brand name and identify as "iBBQ" in their BLE advertisements.‎

Install the iBBQ library by typing the pip3 command below into your ‎shell. Then download the example program below.‎

thermometer_7

Download File

Copy Code
pip3 install adafruit-circuitpython-ble-ibbq

Download Project Bundle

Copy Code
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT

import time

import adafruit_ble
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble_ibbq import IBBQService

# PyLint can't find BLERadio for some reason so special case it here.
ble = adafruit_ble.BLERadio() # pylint: disable=no-member

ibbq_connection = None

while True:
print("Scanning...")
for adv in ble.start_scan(ProvideServicesAdvertisement, timeout=5):
if IBBQService in adv.services:
print("found an IBBq advertisement")
ibbq_connection = ble.connect(adv)
print("Connected")
break

# Stop scanning whether or not we are connected.
ble.stop_scan()

if ibbq_connection and ibbq_connection.connected:
ibbq_service = ibbq_connection[IBBQService]
ibbq_service.init()
while ibbq_connection.connected:
print(
"Temperatures:",
ibbq_service.temperatures,
"; Battery:",
ibbq_service.battery_level,
)
time.sleep(2)

View on GitHub

Turn on your thermometer and run the example program. Here's a ‎screenshot from running it on MacOS. In this example, I typed ctrl-C ‎after getting a few readings.‎

Thermometer example program

BLE UART

One simple way of communicating between two BLE devices is to ‎use a simulated "UART". A UART provides a bi-directional byte ‎stream, so that both ends of a connection can transmit and receive ‎bytes with each other.‎

There are standard BLE UART services, such as the Nordic UART ‎Service (NUS). The Adafruit Bluefruit Connect app uses NUS to talk to ‎BLE boards.‎

Once you get the UART service working, it's easy to invent your ‎own ad hoc protocol that sends and receives commands and data ‎over the serial stream.‎

BLE UART Python eval() Example

Here's a simple example that uses BLE UART to send a text string ‎from a host computer to a CircuitPython board over BLE. The board ‎calls the Python function eval() on the string, to evaluate it as a ‎Python expression, and sends the result back as a string to the host ‎computer. For instance, the host might send 2+2, and the board will ‎send back 4.‎

To try this example, first install this library from the latest library ‎bundle on your BLE-capable CircuitPython board, such as a Feather ‎nRF52840 or a Circuit Playground Bluefruit:‎

  • adafruit_ble

Then copy the program below to CIRCUITPY on your CircuitPython ‎board as code.py:‎

Download Project Bundle

Copy Code
# SPDX-FileCopyrightText: 2020 Dan Halbert for Adafruit Industries
#
# SPDX-License-Identifier: MIT

# Provide an "eval()" service over BLE UART.

from adafruit_ble import BLERadio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.nordic import UARTService

ble = BLERadio()
uart = UARTService()
advertisement = ProvideServicesAdvertisement(uart)

while True:
ble.start_advertising(advertisement)
print("Waiting to connect")
while not ble.connected:
pass
print("Connected")
while ble.connected:
s = uart.readline()
if s:
try:
result = str(eval(s))
except Exception as e:
result = repr(e)
uart.write(result.encode("utf-8"))

View on GitHub

Now copy the second program to your host computer and run it. ‎Wait for it to connect to your board, and then type some Python ‎expressions at the Eval: prompt. ‎

Download Project Bundle

Copy Code
# SPDX-FileCopyrightText: 2020 Dan Halbert for Adafruit Industries
#
# SPDX-License-Identifier: MIT

# Connect to an "eval()" service over BLE UART.

from adafruit_ble import BLERadio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.nordic import UARTService

ble = BLERadio()

uart_connection = None

while True:
if not uart_connection:
print("Trying to connect...")
for adv in ble.start_scan(ProvideServicesAdvertisement):
if UARTService in adv.services:
uart_connection = ble.connect(adv)
print("Connected")
break
ble.stop_scan()

if uart_connection and uart_connection.connected:
uart_service = uart_connection[UARTService]
while uart_connection.connected:
s = input("Eval: ")
uart_service.write(s.encode("utf-8"))
uart_service.write(b'\n')
print(uart_service.readline().decode("utf-8"))

View on GitHub

Here's an example of running the ble_eval_client.py program on a ‎Raspberry Pi Zero W, talking to a Circuit Playground Bluefruit.‎

Note that errors, like division by zero, are caught and reported. Also ‎note you can only type Python expressions, not statements. ‎So, a = 3 doesn't work.‎

Python expressions

Troubleshooting

Reset Bluetooth

Sometimes the Bluetooth system software on the host computer ‎becomes stuck, confused, or in a bad state. This can cause Blinka ‎bleio programs to stop working. Often a simple fix is just to turnBluetooth off and then back on, on the host computer.‎

Windows

There are two ways to cycle Bluetooth off and on in Windows. In ‎Settings, find the Bluetooth setting page, and turn Bluetooth off and ‎then back on:‎

Windows Bluetooth

Or select the Notification area by clicking the Notification icon in the ‎taskbar (1), and then toggle Bluetooth off and then back on (2):‎

notification_11

macOS

Go to System Preferences and choose Bluetooth (1). Then toggle ‎Bluetooth off and on (2). If you check the box that says, "Show ‎Bluetooth in menu bar" (3), you can toggle Bluetooth more quickly ‎‎(4).‎

system_12

system_13

system_14

Linux and Raspberry Pi

Depending on which Linux distribution and desktop software you're ‎using, there might be a Bluetooth item in the task bar or menu bar ‎for your desktop that will allow to you cycle Bluetooth off and back ‎on easily. For instance, the default Raspberry Pi desktop has a ‎Bluetooth icon in the top menu bar; you can turn Bluetooth off and ‎then back on from there.

bluetooth_15

If you don't have a desktop icon to use, you can cycle Bluetooth from ‎the command line. For Raspberry Pi OS, Debian, and Ubuntu, type ‎these commands into your shell:‎

Download File

Copy Code
rfkill block bluetooth
rfkill unblock bluetooth

You may find it convenient to create a script to do this. For instance, ‎you can put these commands into a file called btcycle, make it ‎executable, and put it in some directory on your PATH:‎

Download File

Copy Code
#!/bin/bash
# Cycle Bluetooth on and off to reset it.
rfkill block bluetooth
rfkill unblock bluetooth
# Wait for bluetooth to come back on. You may need to change this delay.
sleep 1.5
制造商零件编号 1327
BLUETOOTH 4.0 USB MODULE 2.1COMP
Adafruit Industries LLC
¥97.27
Details
制造商零件编号 4500
CLUE NRF52840 EXPRESS
Adafruit Industries LLC
¥365.90
Details
制造商零件编号 4333
CIRCUIT PLAYGROUND BLUEFRUIT BLE
Adafruit Industries LLC
¥203.09
Details
制造商零件编号 4062
ADAFRUIT FEATHER NRF52840 EXPRES
Adafruit Industries LLC
¥203.09
Details
制造商零件编号 4516
FEATHER NRF52840 SENSE
Adafruit Industries LLC
¥321.53
Details
Add all DigiKey Parts to Cart
TechForum

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.

Visit TechForum