If you have a CircuitPython development board that has Bluetooth LE (Bluetooth Low Energy, or BLE) compatibility, fairly easy to get it connected a phone or PC over a virtual UART (for simple, text-based communications.
In addition to a CircuitPython-compatible development board that supports Bluetooth LE, you'll need to have CircuitPython installed. The build of CP for your board needs to include the _bleio
library; you can check to see if you're board is supported here.
You will most likely want to install a few support libraries for working with BLE (the first one is required, the second is optional but highly recommended). You can install these with circup or get them from the CircuitPython library bundle
- Adafruit BLE - the primary API for connecting with BLE.
- Adafruit BLE Adafruit - support for Adafruit-defined BLE services, such as accelerometer data.
- Adafruit Bluefruit Connect - Pairs with the tools in the BlueFruitConnect app (see below). Can be used to send data from your mobile device to your dev board!
I have added MPY-compiled versions of these libraries compatible with CircuitPython 9.x below in the file copy_to_lib.zip, current as of 12/15/24. I recommend that you get these from one of the sources mentioned about so you'll have the latest and greatest.
I am not going into all of the detail about BLE here, but there are a couple of definitions you'll want to know about if you've not worked with it before.
- Central - a central device is responsible for making and breaking BLE connections. This will often be something like your phone or computer.
- Peripheral - a BLE peripheral is a device that typically advertises itself when nothing is connected, allows for connections and provides several services. This will usually be the dev board you're using.
- Advertising - peripheral devices can announce their presence via a broadcast message when not connected to a central device. This allows them to be found so they can be connected to and also to tell potentials users about at least come of the services available.
- Connecting - a BLE central device can start a connection with an available BLE peripheral. Once a connection is established, the central and peripheral are paired and may share data via services.
- Services - BLE uses services to exchange data. Each service has a specific definition for the data it is meant to send or recieve. The UART service is an example of a service that allows sending and receiving text data between the central and peripheral devices.
If you have all of the dependencies installed, getting BLE working with CircuitPython is fairly easy. Here's some code to set up the radio, configure the device name and UART service, then start advertising so that your device can be detected and connected from a central device (typically your phone or a computer).
from adafruit_ble import BLERadio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.nordic import UARTService
# set up the radio hardware
ble = BLERadio()
# By default, your device will have some name like CIRCUITPYxxxx; let's make that more user friendly
ble.name = "MyCPDevice"
# set up the UART service. This virtual serial port lets you send text over BLE.
uart = UARTService()
# set up advertising, so your phone or PC will know that UART service is available on your device
advertisement = ProvideServicesAdvertisement(uart)
while True:
print("Advertising BLE services")
# start advertising
ble.start_advertising(advertisement)
# keep going until we get a connection
while not ble.connected:
pass
# if we got here, we have a connection. Stop advertising!
ble.stop_advertising()
print("BLE connected")
# do some work as long as we're connected
while ble.connected:
# try reading some text from the UART, e.g. typed into the app
raw_bytes = uart.readline()
if raw_bytes:
text = raw_bytes.decode("utf-8")
print(f"Got text from central: {text}")
# reverse the text and echo it back!
flipped = "".join(reversed(text[:-1]))
uart.write(flipped.encode("utf-8"))
# we no longer have a connection, so we'll go back to the top of the loop
# and start advertising again
print("BLE disconnected")
Once you have this code running on your board, you should be able to see it advertised in your Bluetooth tool or system of choice, and can connect to it. If you use the BluefruitConnect app, or something else that support the Nordic UART service, you can send a message to the board and have it echoed back in reverse.
Adafruit's BluefruitConnect app is a nice way to connect to Bluetooth-enabled devices, including those you might build yourself using CircuitPython. The app supports basic UART service communications, as well as several tools like a plotter for data, remote controls, and other features. And Adafruit has provided libraries for CircuitPython that make it a snap to use these tools.
Bluefruit connect is available for several platforms, including iOS/iPadOS/MacOS and Android. See the link above for a guide to the app, including links to download it.
Here's another example, this time sending data from an accelerometer to the Bluefruit Connect app to plot it out.
import time
import board
from adafruit_ble import BLERadio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.nordic import UARTService
from adafruit_lsm6ds.ism330dhcx import ISM330DHCX as LSM6DS
# set up the accelerometer + gyroscope
i2c = board.I2C()
accel_gyro = LSM6DS(i2c)
# set up the radio hardware
ble = BLERadio()
# By default, your device will have some name like CIRCUITPYxxxx; let's make that more user friendly
ble.name = "MyCPDevice"
# set up the UART service. This virtual serial port lets you send text over BLE.
uart = UARTService()
# set up advertising, so your phone or PC will know that UART service is available on your device
advertisement = ProvideServicesAdvertisement(uart)
while True:
print("Advertising BLE services")
# start advertising
ble.start_advertising(advertisement)
# keep going until we get a connection
while not ble.connected:
pass
# if we got here, we have a connection. Stop advertising!
ble.stop_advertising()
print("BLE connected")
# do some work as long as we're connected
while ble.connected:
x, y, z = accel_gyro.acceleration
plot_data = f"{x}, {y}, {z}\n"
uart.write(plot_data.encode("utf-b"))
time.sleep(0.025)
# we no longer have a connection, so we'll go back to the top of the loop
# and start advertising again
print("BLE disconnected")
This will send the three axes of accelerometer data to the app in format it understands. After connnecting to the device, you can open the plotter in the app and see the data being charted.
There are several useful guides on the Adafruit Learning site for working with CircuitPython and BLE.
- Getting started with CircuitPython and Bluetooth Low Energy - focuses on using an nrf52840 development board, but is a good general introduction to BLE on CircuitPython.
- Bluefruit LE Connect for iOS and Android
I also created a gist for setting up CircuitPython on your Mac. Once you've got that done, you can use some (but not all) of the CircuitPython features from your computer; see this guide for details.