Skip to main content

Quick start

Get up and running with SiFi Bridge Python in just a few minutes. This guide covers the essential steps to connect to a device and start acquiring data.

Basic workflow

Working with SiFi Bridge Python follows this pattern:

  1. Create a SifiBridge instance
  2. Connect to a device
  3. Configure sensors
  4. Start data acquisition
  5. Read data packets
  6. Stop and disconnect

Your first connection

Here's a minimal example to connect and stream ECG data:

import time
import sifi_bridge_py as sbp

# Create bridge instance
sb = sbp.SifiBridge()

# Connect to the first available SiFi device (BioPoint or SiFi Band)
sb.connect()

# Sensors are off by default. Turn the ECG sensor on, then set its
# filter parameters (500 Hz sampling rate).
sb.configure_sensors(ecg=True)
sb.configure_ecg(fs=500)

# Start acquisition
sb.start()

# Collect for 10 seconds
start_time = time.time()
data_buffer = []

try:
while time.time() - start_time < 10:
packet = sb.get_data() # Get any packet
data_buffer.append(packet)
print(f"Received {packet['packet_type']} packet")

except KeyboardInterrupt:
print("Stopped by user")

finally:
sb.stop()
sb.disconnect()
print(f"Collected {len(data_buffer)} packets")

Understanding data packets

Sensor data has the exact same structure as SiFi Bridge's, deserialized as a dictionary:

{
'device': 'BioPointV1_3', # Device model
'id': 'device', # Device identifier
'mac': 'E9:E6:DD:2B:94:22', # Device MAC address
'packet_type': 'ecg', # Type of data (ecg, emg, imu, ppg, etc.)
'status': 'ok', # Packet status
'timestamp': 1234567890.123, # Unix timestamp of packet reception (seconds)
'sample_rate': 499.99, # Estimated sample rate of this packet (Hz)
'timestamps': [0.198, 0.2, ...], # Per-sample timestamps relative to stream start (seconds)
'data': {
'ecg': [0.001, 0.002, ...] # Sensor data arrays, one entry per timestamp
}
}

Connect to a specific device

connect() accepts either the device's name or its MAC address (Linux/Windows) / UUID (macOS):

import sifi_bridge_py as sbp
# Create bridge instance
sb = sbp.SifiBridge()
# List available devices, for each found device, you will receive the name and id (MAC/UUID) of the device
devices = sb.list_devices(sbp.ListSources.BLE)
print(f"Found devices: {devices}")

# Connect by name
sb.connect("MyBioPoint")

# Connect by id instead
sb.connect("XX:XX:XX:XX:XX:XX") # Use actual MAC/UUID ("id" field)

Use sb.rename_device("MyBioPoint") once connected to give a device a memorable name so you can target it by name later. The custom name should not have space and be 14 characters maximum

Configure multiple sensors

Enable and configure multiple sensors at once. configure_sensors() turns sensors on/off; each sensor's own configure_<sensor>() call sets its sampling rate and filter parameters:

# Enable ECG, EMG, and IMU
sb.configure_sensors(ecg=True, emg=True, imu=True)

# Set each sensor's sampling rate / filters
sb.configure_ecg(fs=500)
sb.configure_emg(fs=2000)
sb.configure_imu(fs=100)

# Start acquisition
sb.start()

# Read data from any sensor
ecg_packet = sb.get_ecg()
emg_packet = sb.get_emg()
imu_packet = sb.get_imu()

Simple data collection loop

Collect data continuously with a loop:

import sifi_bridge_py as sbp
import time

sb = sbp.SifiBridge()
sb.connect()

# Enable sensors
sb.configure_sensors(ecg=True, emg=True, imu=True)

sb.start()

# Collect for 10 seconds
start_time = time.time()
data_buffer = []

try:
while time.time() - start_time < 10:
packet = sb.get_data() # Get any packet
data_buffer.append(packet)
print(f"Received {packet['packet_type']} packet")

except KeyboardInterrupt:
print("Stopped by user")

finally:
sb.stop()
sb.disconnect()
print(f"Collected {len(data_buffer)} packets")

Using threading

Since get_data() blocks, use threading for responsive applications:

import time
import threading
import sifi_bridge_py as sbp

def data_collection_thread(sb):
"""Background thread for data collection"""
while running:
packet = sb.get_data(timeout=1.0)
if packet:
# Process packet
print(f"Got {packet['packet_type']}")

# Setup
sb = sbp.SifiBridge()
sb.connect()
sb.configure_sensors(ecg=True)
sb.start()

# Start background thread
running = True
thread = threading.Thread(target=data_collection_thread, args=(sb,))
thread.start()

# Do other work in main thread
time.sleep(30)

# Cleanup
running = False
thread.join()
sb.stop()
sb.disconnect()

Common patterns

Check connection status

info() raises SifiBridgeError if no device is currently connected, rather than returning a "not connected" status:

try:
info = sb.info()
print(f"Connected to {info['device']}")
except sbp.SifiBridgeError:
print("Not connected")

Retry connection

import time

max_retries = 5
for attempt in range(max_retries):
if sb.connect():
print("Connected!")
break
print(f"Retry {attempt + 1}/{max_retries}")
time.sleep(2)

Wait for a packet with a timeout

By default the get_* methods block indefinitely. Pass timeout (seconds) to give up and get {} back instead:

ecg_packet = sb.get_ecg(timeout=5.0)
if not ecg_packet:
print("No ECG packet received within 5 seconds")

Error handling

Always include error handling for robust applications:

import sifi_bridge_py as sbp

try:
sb = sbp.SifiBridge()

# Try to connect
if not sb.connect():
raise ConnectionError("Failed to connect to device")

sb.configure_sensors(ecg=True)
sb.configure_ecg(fs=500)
sb.start()

# Collect data
for _ in range(100):
packet = sb.get_ecg()
# Process packet...

except ConnectionError as e:
# Also raised by connect() itself if Bluetooth is off
print(f"Connection error: {e}")
except KeyboardInterrupt:
print("Interrupted by user")
finally:
# Always cleanup. stop()/disconnect() block until the device
# confirms, so no extra sleep is needed here.
try:
sb.stop()
sb.disconnect()
except Exception:
pass

Quick reference

# Import
import time
import sifi_bridge_py as sbp

# Create bridge
sb = sbp.SifiBridge()

try:
# Connection
sb.connect() # Connect to the first available device
sb.connect("MAC_OR_UUID") # Connect to a specific device by address
sb.disconnect() # Disconnect
# Configuration
sb.configure_sensors(ecg=True, emg=True) # Enable sensors (off by default)
sb.configure_ecg(fs=500, dc_notch=True, mains_notch=50, bandpass=True, flo=0, fhi=30) # Set ECG sampling rate / filters
sb.configure_emg(fs=2000, dc_notch=True, mains_notch=50, bandpass=True, flo=20, fhi=450) # Set EMG sampling rate / filters
sb.set_low_latency_mode(True) # Toggle low-latency mode (on by default, tradeoff between latency and bandwidth use)

# Control
sb.start() # Start acquisition
sb.stop() # Stop acquisition

# Data
packet = sb.get_data() # Get the next packet of any type
ecg = sb.get_ecg() # Get the next ECG packet
emg = sb.get_emg() # Get the next EMG packet
imu = sb.get_imu() # Get the next IMU packet

# Device commands
sb.set_led(1, True) # Turn LED 1 on
sb.set_motor(True) # Start the vibration motor
time.sleep(1)
sb.set_motor_intensity(10) # Set vibration motor intensity to 10
time.sleep(1)
sb.set_motor_intensity(5) # Set vibration motor intensity to 5
time.sleep(1)
sb.set_motor(False) # Stop the vibration motor
sb.set_led(1, False) # Turn LED 1 off
finally:
sb.stop()
sb.disconnect()