Skip to main content

Python library for Berry Pulse Oximeter data collection via Bluetooth LE

Project description

Berry Oximeter 🫀

Banner

PyPI version Python 3.10+ License: MIT

A simple Python library for real-time data collection from BerryMed pulse oximeters via Bluetooth LE. Get heart rate, SpO2, and perfusion index readings with just a few lines of code!

[!WARNING] This library is for educational and research purposes only. It is not intended for medical diagnosis or treatment. Always consult with a qualified healthcare provider for medical advice.

Features ✨

  • 🔌 Auto-discovery - Automatically finds and connects to your BerryMed device
  • 📊 Real-time streaming - Get live readings with customizable callbacks
  • 📈 Data collection - Collect readings over time for analysis
  • 💾 CSV logging - Built-in data logging to CSV files
  • 🎯 Signal filtering - Filter readings by signal quality
  • 🐍 Pythonic API - Simple, intuitive interface
  • 🧪 Type hints - Full type hint support for better IDE experience

Installation 📦

Install via pip:

pip install berry-oximeter

Or using uv:

uv add berry-oximeter

Quick Start 🚀

Basic Usage

from berry_oximeter import BerryOximeter

# Create instance and connect
oximeter = BerryOximeter()
oximeter.connect()

# Enable console output
oximeter.log_to_console(True)

# Let it run for 30 seconds
import time

time.sleep(30)

# Disconnect
oximeter.disconnect()

Callback-based Streaming

def handle_reading(reading):
    if reading.is_valid:
        print(f"♥ SpO2: {reading.spo2}%, Pulse: {reading.pulse_rate} BPM")
    else:
        print(f"Status: {reading.status}")


oximeter = BerryOximeter()
oximeter.connect()
oximeter.start_streaming(handle_reading)

# Stream for 60 seconds
time.sleep(60)

oximeter.stop_streaming()
oximeter.disconnect()

Data Collection & Analysis

# Collect 60 seconds of data
oximeter = BerryOximeter()
oximeter.connect()

readings = oximeter.get_readings(duration_seconds=60)

# Analyze the data
valid_readings = [r for r in readings if r.is_valid]
avg_spo2 = sum(r.spo2 for r in valid_readings) / len(valid_readings)
avg_pulse = sum(r.pulse_rate for r in valid_readings) / len(valid_readings)

print(f"Average SpO2: {avg_spo2:.1f}%")
print(f"Average Pulse: {avg_pulse:.1f} BPM")

oximeter.disconnect()

CSV Logging

oximeter = BerryOximeter()
oximeter.connect()

# Start logging (auto-generates filename with timestamp)
filename = oximeter.start_logging()
print(f"Logging to: {filename}")

# Or specify your own filename
# oximeter.start_logging("patient_123.csv")

# Collect data
oximeter.log_to_console(True)
time.sleep(300)  # 5 minutes

# Stop logging
oximeter.stop_logging()
oximeter.disconnect()

Context Manager (Auto-cleanup)

with BerryOximeter() as oximeter:
    oximeter.connect()
    oximeter.log_to_console(True)
    time.sleep(30)
    # Automatically disconnects when done

API Reference 📚

BerryOximeter Class

The main interface for interacting with the pulse oximeter.

Connection Methods

  • connect(device_address=None, timeout=10.0) - Connect to device
  • disconnect() - Disconnect from device
  • is_connected - Property to check connection status

Data Access Methods

  • start_streaming(callback) - Start streaming with callback function
  • stop_streaming() - Stop streaming
  • get_reading(timeout=5.0) - Get a single reading
  • get_readings(duration_seconds) - Collect readings for specified duration

Logging Methods

  • start_logging(filename=None) - Start CSV logging
  • stop_logging() - Stop logging and return filename
  • log_to_console(enabled=True) - Enable/disable console output

Configuration

  • set_filter(min_signal_strength=None) - Filter by signal quality (0-8)

OximeterReading Object

Each reading contains:

  • timestamp - When the reading was taken
  • spo2 - Oxygen saturation (%) or None
  • pulse_rate - Heart rate (BPM) or None
  • pleth - Plethysmograph value or None
  • signal_strength - Signal quality (0-8)
  • is_valid - True if SpO2 and pulse are valid
  • status - Human-readable status ("reading", "no_finger", "searching", etc.)

Exceptions

  • DeviceNotFoundError - No BerryMed device found
  • ConnectionError - Failed to connect to device
  • NoDataError - No data received within timeout

Advanced Usage 🔧

Filtering Low-Quality Signals

# Only accept readings with signal strength >= 5
oximeter.set_filter(min_signal_strength=5)


def quality_callback(reading):
    print(f"High quality: SpO2 {reading.spo2}%, Signal: {reading.signal_strength}/8")


oximeter.start_streaming(quality_callback)

Custom Logging

import csv


def custom_logger(reading):
    if reading.is_valid:
        with open('custom_log.csv', 'a', newline='') as f:
            writer = csv.writer(f)
            writer.writerow([
                reading.timestamp,
                reading.spo2,
                reading.pulse_rate,
                reading.pleth,
                reading.signal_strength
            ])


oximeter.start_streaming(custom_logger)

Async Integration

Since the library runs BLE operations in a separate thread, it works well with async code:

import asyncio


async def monitor_patient(patient_id, duration):
    oximeter = BerryOximeter()
    oximeter.connect()
    oximeter.start_logging(f"patient_{patient_id}.csv")

    await asyncio.sleep(duration)

    oximeter.stop_logging()
    oximeter.disconnect()


# Run multiple monitors concurrently
async def main():
    await asyncio.gather(
        monitor_patient("001", 300),
        monitor_patient("002", 300)
    )

Troubleshooting 🔍

Device Not Found

  • Make sure your BerryMed oximeter is turned on
  • Check that Bluetooth is enabled on your computer
  • The device name should be "BerryMed" - if yours is different, you'll need to modify the code
  • On Linux, you may need to run with sudo or add your user to the bluetooth group

Connection Issues

  • Ensure the device isn't already connected to another application
  • Try moving closer to the device
  • Some devices may need to be in pairing mode

No Readings

  • Make sure your finger is properly inserted
  • Keep your hand still - movement affects readings
  • Warm up cold fingers first
  • Check the signal strength indicator

Supported Devices 📱

This library is tested with:

  • BerryMed BM1000C
  • Other BerryMed pulse oximeters using the BCI protocol

The library should work with any Berry device that:

  • Advertises as "BerryMed" over Bluetooth LE
  • Uses the standard BCI protocol (5-byte packets)

Contributing 🤝

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

Acknowledgments 🙏

  • Thanks to the Bleak project for the excellent BLE library
  • Inspired by the need for simple, reliable pulse oximeter data collection in research settings

Qatar University Machine Learning Group

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

berry_oximeter-0.0.1.tar.gz (886.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

berry_oximeter-0.0.1-py3-none-any.whl (10.9 kB view details)

Uploaded Python 3

File details

Details for the file berry_oximeter-0.0.1.tar.gz.

File metadata

  • Download URL: berry_oximeter-0.0.1.tar.gz
  • Upload date:
  • Size: 886.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for berry_oximeter-0.0.1.tar.gz
Algorithm Hash digest
SHA256 7e4465f9f3e2f99947bd92ca9dfcd7aa638d5dd9bc748c4cd3bea3cf48e421e9
MD5 5c044c2665b9105a175f6da364a7b003
BLAKE2b-256 65fb13f0a197106f5740833fad317de4ac449691b491ef9a9bb11fa871e93fa4

See more details on using hashes here.

Provenance

The following attestation bundles were made for berry_oximeter-0.0.1.tar.gz:

Publisher: cd.yml on atick-faisal/berry-oximeter

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file berry_oximeter-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: berry_oximeter-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 10.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for berry_oximeter-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 ccf0ca20ea549909992a26c164c3b9978f7160076e48789fae16e40f2fb78d69
MD5 f7a484877b9a53dccd6aaf414abe2920
BLAKE2b-256 8dcacede115851b2014bab7c9243c9fa0fa6039d6eb9b0be7272428ec4558a46

See more details on using hashes here.

Provenance

The following attestation bundles were made for berry_oximeter-0.0.1-py3-none-any.whl:

Publisher: cd.yml on atick-faisal/berry-oximeter

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page