Skip to main content

SiFi Bridge Python

SiFi Bridge Python is a Python API for controlling SiFi Labs biosensor devices programmatically. Build custom applications, automate experiments, and integrate physiological data into your research workflow.

Features

  • Object-Oriented API - Clean, Pythonic interface for device control
  • Real-Time Data Streaming - Sensor packets delivered as Python dicts with per-sample timestamps
  • Flexible Configuration - Per-sensor sampling rate, filtering, and gain controlled programmatically
  • Multiple Output Formats - Stream over stdout, TCP, UDP, or Lab Streaming Layer (LSL)
  • Multi-Device Support - Manage multiple SiFi devices from a single bridge instance
  • Easy Integration - Works seamlessly with NumPy, Pandas, scikit-learn, NeuroKit2, and ML frameworks

Installation

Install via pip:

pip install sifi_bridge_py

Or from source:

git clone https://github.com/SiFiLabs/sifi-bridge-py.git
cd sifi-bridge-py
pip install -e .

See the Installation Guide for detailed instructions.

Quick Start

Connect to a device and stream ECG data in just a few lines:

import sifi_bridge_py as sbp

# Create bridge and connect to the first available SiFi device.
# Pass a DeviceType or a MAC/UUID to target a specific unit.
sb = sbp.SifiBridge()
sb.connect()

# Configure ECG sensor: 500 Hz, 0–30 Hz bandpass, 50 Hz mains notch
# (use 60 in North America).
sb.configure_ecg(state=True, fs=500, mains_notch=50,
bandpass=True, flo=0, fhi=30)
sb.start()

# Read 10 ECG packets.
for _ in range(10):
packet = sb.get_ecg()
print(f"ECG data: {packet['data']['ecg'][:5]}...")

# Cleanup.
sb.stop()
sb.disconnect()

See the Quick Start Guide for more examples.

Use Cases

Data Collection

import time
import sifi_bridge_py as sbp

sb = sbp.SifiBridge()
sb.connect()
sb.configure_sensors(ecg=True, emg=True, imu=True)
sb.start()

# Collect packets for 60 seconds and route them by packet_type.
ecg, emg, imu = [], [], []
t0 = time.time()
while time.time() - t0 < 60:
pkt = sb.get_data(timeout=1.0)
if not pkt:
continue
if pkt['packet_type'] == 'ecg':
ecg.append(pkt)
elif pkt['packet_type'] == 'emg':
emg.append(pkt)
elif pkt['packet_type'] == 'imu':
imu.append(pkt)

sb.stop()
sb.disconnect()

Real-Time Analysis

import numpy as np
import sifi_bridge_py as sbp

sb = sbp.SifiBridge()
sb.connect()
sb.configure_sensors(ecg=True)
sb.configure_ecg(state=True, fs=500)
sb.start()

while True:
packet = sb.get_ecg()
ecg_data = np.array(packet['data']['ecg'])
# Your analysis here, e.g. R-peak detection, HR estimation, ...

Lab Streaming Layer (LSL)

# Stream to LSL for integration with LabRecorder, OpenViBE, BCILAB,
# pylsl scripts, etc. Each enabled sensor becomes its own LSL outlet.
sb = sbp.SifiBridge(use_lsl=True)
sb.connect()
sb.configure_sensors(ecg=True, emg=True)
sb.start()

Network Streaming (TCP/UDP)

# Push every sensor packet as JSON over TCP. The bridge acts as a TCP
# CLIENT and connects out to the address you specify, so a TCP server
# must already be listening at host:port.
sb = sbp.SifiBridge(publishers="tcp://127.0.0.1:9001")
sb.connect()
sb.configure_sensors(ecg=True)
sb.start()

Machine Learning Integration

import pandas as pd
import sifi_bridge_py as sbp

sb = sbp.SifiBridge()
sb.connect()
sb.configure_sensors(emg=True)
sb.configure_emg(state=True, fs=2000)
sb.start()

# Collect 1000 EMG packets and turn them into a DataFrame.
data_buffer = []
for _ in range(1000):
packet = sb.get_emg()
data_buffer.extend(packet['data']['emg'])

df = pd.DataFrame({'emg': data_buffer})
# Train your model ...

Key Concepts

Device Connection

Connect to devices by type, by address, or auto-connect to the first available unit:

# Auto-connect to any SiFi device in range.
sb.connect()

# Connect to a BioPoint v1.3 specifically.
sb.connect(sbp.DeviceType.BIOPOINT_V1_3)

# Connect by MAC (Windows / Linux) or UUID (macOS).
sb.connect("AA:BB:CC:DD:EE:FF")

# List devices first, then connect.
devices = sb.list_devices(sbp.ListSources.BLE)
sb.connect(devices[0])

Sensor Configuration

Two layers: configure_sensors() is the on/off switch for each sensor family, and the per-sensor configure_* methods set sampling rate, filtering, and gain.

# Enable only the sensors you need.
sb.configure_sensors(ecg=True, emg=True, imu=True)

# ECG: 500 Hz, 0–30 Hz bandpass, 50 Hz mains notch.
sb.configure_ecg(state=True, fs=500, mains_notch=50,
bandpass=True, flo=0, fhi=30)

# EMG: 2 kHz, 20–450 Hz bandpass.
sb.configure_emg(state=True, fs=2000, mains_notch=50,
bandpass=True, flo=20, fhi=450)

# PPG: per-LED current in mA (1–50) and photodiode sensitivity.
sb.configure_ppg(state=True, fs=100, ir=7, red=7, green=9, blue=9,
sens=sbp.PpgSensitivity.MEDIUM)

# IMU: accelerometer range in g, gyroscope range in deg/s.
sb.configure_imu(state=True, fs=100, accel_range=2, gyro_range=250)

Data Acquisition

Multiple ways to get data, depending on whether you want any packet or a specific sensor:

# Get any packet (event packets, status packets, sensor packets, ...).
packet = sb.get_data()

# Get the next packet of a specific sensor type.
ecg = sb.get_ecg()
emg = sb.get_emg()
imu = sb.get_imu()
ppg = sb.get_ppg()

# Non-blocking / bounded read.
packet = sb.get_data(timeout=1.0)

Device Commands

Control LEDs, vibration motor, and event markers:

# LED control.
sb.send_command(sbp.DeviceCommand.OPEN_LED_1)
sb.send_command(sbp.DeviceCommand.CLOSE_LED_1)

# Vibration motor — set intensity (1–10), then start / stop.
sb.set_motor_intensity(5)
sb.send_command(sbp.DeviceCommand.START_MOTOR)
sb.send_command(sbp.DeviceCommand.STOP_MOTOR)

# Event markers. send_event() injects an event packet into the data
# stream — it comes back with packet_type='event' and a device-side
# timestamp, just like a sensor packet.
sb.send_event()

Comparison with CLI

FeaturePython APICLI
Use CaseCustom applications, automationInteractive exploration, quick data collection
Coding RequiredYesNo
IntegrationSeamless with NumPy, Pandas, MLExport over LSL, TCP, UDP
FlexibilityFull programmatic controlCommand-based
Best ForDevelopers, researchers with codingResearchers without coding, quick tests

Both interfaces use the same underlying library and offer the same capabilities.

Support