Once your Feather nRF52840 is set up with CircuitPython, you'll also need to add some libraries. Follow this page for information on how to download and add libraries to your Feather.
From the library bundle you downloaded in that guide page, transfer the following libraries onto the Feather's /lib directory:
- adafruit_ble
- adafruit_ble_berrymed_pulse_oximeter
- adafruit_bus_device
- adafruit_register
- neopixel.mpy
- adafruit_pcf8523.mpy
Text Editor
Adafruit recommends using the Mu editor for using your CircuitPython code with the Feather boards. You can get more info in this guide.
Alternatively, you can use any text editor that saves files.
# SPDX-FileCopyrightText: 2020 John Park for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
Read data from a BerryMed pulse oximeter, model BM1000C, BM1000E, etc.
Run this on Feather nRF52840
Log data to SD card on Autologger FeatherWing
"""
# 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 time
import sdcardio
import board
import busio
import storage
from adafruit_pcf8523.pcf8523 import PCF8523
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
# Logging setup
SD_CS = board.D10
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
sd_card = sdcardio.SDCard(spi, SD_CS)
vfs = storage.VfsFat(sd_card)
storage.mount(vfs, "/sd_card")
log_interval = 2 # you can adjust this to log at a different rate
# RTC setup
I2C = busio.I2C(board.SCL, board.SDA)
rtc = PCF8523(I2C)
days = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
set_time = False
if set_time: # change to True if you want to write the time!
# year, mon, date, hour, min, sec, wday, yday, isdst
t = time.struct_time((2020, 4, 21, 18, 13, 0, 2, -1, -1))
# you must set year, mon, date, hour, min, sec and weekday
# yearday not supported, isdst can be set but we don't use it at this time
print("Setting time to:", t) # uncomment for debugging
rtc.datetime = t
print()
# 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:
t = rtc.datetime
print("Scanning for Pulse Oximeter...")
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:
values = pulse_ox_service.values
if values is not None:
# unpack the message to 'values' list
valid, spo2, pulse_rate, pleth, finger = values
if not valid:
continue
if (
pulse_rate == 255
): # device sends 255 as pulse until it has a valid read
continue
print(
"SpO2: {}% | ".format(spo2),
"Pulse Rate: {} BPM | ".format(pulse_rate),
"Pleth: {}".format(pleth),
)
# print((pleth,)) # uncomment to see graph on Mu plotter
try: # logging to SD card
with open("/sd_card/log.txt", "a") as sdc:
t = rtc.datetime
sdc.write(
"{} {}/{}/{} {}:{}:{}, ".format(
days[t.tm_wday],
t.tm_mday,
t.tm_mon,
t.tm_year,
t.tm_hour,
t.tm_min,
t.tm_sec
)
)
sdc.write(
"{}, {}, {:.2f}\n".format(
spo2, pulse_rate, pleth
)
)
time.sleep(log_interval)
except OSError:
pass
except RuntimeError:
pass
except _bleio.ConnectionError:
try:
pulse_ox_connection.disconnect()
except _bleio.ConnectionError:
pass
pulse_ox_connection = None
How it Works
The code does the following things:
- Imports necessary libraries, including adafruit_ble_berrymed_pulse_oximeter library
- Sets up SD card interface to use as a file system
- Sets up the real-time clock (RTC) on the I2C bus for setting and then keeping track of the day, date, and time
- Scan for and connect to a Bluetooth LE peripheral advertising the BerryMed pulse oximeter service
- Receive pulse oximeter service messages and unpack their values into SpO2%, pulse rate, and pleth (pulse strength graph)
- When a valid pulse oximeter message is received, log the values along with the day, date, and time from the RTC onto the SD card in a file called
log.txt - Repeat this process after the specified
log_interval(a couple of seconds for example) - If/when the connection is dropped, go back to BLE search mode
Now, let's try it out!
Page last edited January 21, 2025
Text editor powered by tinymce.