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 import adafruit_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 = adafruit_pcf8523.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!
Text editor powered by tinymce.