Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 39 additions & 78 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,98 +1,63 @@
Zepyhr BioHarness LSL Integration
Zephyr BioHarness LSL Integration
=================================

This is an LSL adapter for Medtronic / Zephyr BioModule and BioHarness
Bluetooth devices. If you encounter any issues with this program, please use the
GitHub issue tracker for this project to report them.
This LSL adapter facilitates the integration of Medtronic / Zephyr BioModule and BioHarness Bluetooth devices. If you encounter any issues with this program, please report them using the GitHub issue tracker for this project.

Prerequisites
============
=============

- **Linux:** make sure that you have Bluetooth development headers installed
(e.g. on Ubuntu 20.04: `sudo apt install libbluetooth-dev`)
- **Every Platform:** make sure that you have Miniconda installed and that the
`conda` command-line interface is on your path
- **Linux:** Ensure that you have Bluetooth development headers installed
(e.g., on Ubuntu 20.04: `sudo apt install libbluetooth-dev`).
- **Every Platform:** Ensure that you have Miniconda installed and that the `conda` command-line interface is in your system's path.

Since the Zephyr uses Bluetooth Low Energy (BLE), your machine needs to have the
necessary wireless hardware installed (either built in as with many recent laptops,
or using a BLE-capable USB dongle).
Since the Zephyr uses Bluetooth Low Energy (BLE), your machine needs to have the necessary wireless hardware installed (either built-in, as with many recent laptops, or by using a BLE-capable USB dongle).

Installing/Running
==================

- **Windows:** Invoke the script `run.cmd`, which will, if necessary, create a fresh Python
environment and install the necessary dependencies into it
- **Linux/MacOS:** Invoke the script `run.sh`, which will, if necessary, create a fresh Python
environment and install the necessary dependencies into it
- **Alternative manual install:** you can also follow the instructions in
`conda-environment.yml` to install a Python environment yourself or to add the
necessary requirements to an existing environment, and then you can use that
interpreter to run `main.py`

- **Windows:** Execute the script `run.cmd`, which will, if necessary, create a fresh Python environment and install the required dependencies into it.
- **Linux/MacOS:** Execute the script `run.sh`, which will, if necessary, create a fresh Python environment and install the required dependencies into it.
- **Alternative manual install:** Alternatively, you can follow the instructions in `conda-environment.yml` to install a Python environment yourself or to add the necessary requirements to an existing environment. Then, use that interpreter to run `examples/sender.py`.

Usage
=====

1. Switch on the sensor using the button (it should either blink orange or
have a constant orange LED).
2. Optionally configure the device using the tools provided with the device
(e.g., set subject info, device clock, configure logging, and so on).
3. Optionally check that you can access the device from a vendor-provided
desktop software (e.g., the one used to retrieve log files), particularly if
this program appears unable to connect by itself.
4. Run this application, optionally with command-line arguments, to stream
real-time data over LSL. No other software is necessary to interface with the
device.
- if you know the Bluetooth MAC address of the device (e.g., from a prior run),
you can specify that via the command line as in `--address=12:34:56:78:9A` to
speed up the startup time
- you can additionally override what modalities you want to stream using the
`--stream` argument (using fewer than all might extend the device's battery
running time). See Emitted Streams for a brief summary, and the vendor
documentation for the full details. By default, everything is streamed,
which is equivalent to the argument
`--stream=ecg,respiration,accel100mg,accel,rtor,events,summary,general`.
- if you have multiple devices, it can be a good idea to use the
`--streamprefix` argument to prefix the stream name with a string of
your choice (e.g., `--streamprefix=Subject1`) to disambiguate multiple
devices.
- additional command-line arguments are available to further customize the
program's behavior, for further information on these, run the program
with the `--help` argument

1. Turn on the sensor using the button (it should either blink orange or have a constant orange LED).
2. Optionally, configure the device using the tools provided with the device (e.g., setting subject info, device clock, configuring logging, etc.).
3. Optionally, verify that you can access the device using vendor-provided desktop software (e.g., software used to retrieve log files), especially if this program seems unable to connect on its own.
4. Run the `sender.py` file from the examples folder, optionally with command-line arguments, to stream real-time data over LSL. No other software is necessary to interface with the device.
- If you know the Bluetooth MAC address of the device (e.g., from a prior run), you can specify it via the command line as in `--address=12:34:56:78:9A` to expedite the startup time.
- You can also override which modalities you want to stream using the `--stream` argument (streaming fewer modalities might extend the device's battery life). See Emitted Streams for a summary and the vendor documentation for complete details. By default, all data is streamed, equivalent to the argument `--stream=ecg,respiration,accel100mg,accel,rtor,events,summary,general`.
- If you have multiple devices, it's advisable to use the `--streamprefix` argument to prefix the stream name with a string of your choice (e.g., `--streamprefix=Subject1`) to distinguish between multiple devices.
- Additional command-line arguments are available to further customize the program's behavior. For more information on these, run the program with the `--help` argument.
5. Optionally, you can also run the `all_in_one.py` file from the examples folder. This will connect and stream the device data and also display it in real-time using matplotlib animation. Currently, only ECG and respiration data are enabled for real-time viewing, as they are the only 1D data streams live from the device.
6. Optionally, you can run `sender.py` in one instance and then run `receiver.py` in a different instance if you want more flexibility in running the project.

Important Concepts
==================

This app transmits Zephyr device data over a pylsl object. You can read about it at (https://github.com/chkothe/pylsl). In brief, we create a separate data stream for each data channel in the `sender.py` file and access it from the `receiver.py` file. You are encouraged to examine the animate function in the `receiver.py` file to understand how it works better.

Emitted Streams
===============

The following streams can be generated by the application (assuming here the Zephyr
stream name prefix). See vendor manual for full details.

* **ZephyrECG** the raw ECG waveform, in mV at 250 Hz (1 channel).
* **ZephyrResp** the raw respiration (breathing) waveform, measuring chest
expansion, at ca. 17.8 Hz, in unscaled units.
* **ZephyrAccel100mg** a 3-channel stream with acceleration along X, Y, and Z
axes (coordinate system is configurable via vendor tools) at 50Hz, in units of
g (earth acceleration).
* **ZephyrAccel** same as Accel100mg, but higher numeric precision (>2x),
in unscaled units.
* **ZephyrRtoR** the interval between the most recent two ECG R-waves, in ms, at
ca. 17.8 Hz. The value is held constant until the next R-wave is detected, and
alternates the sign with each new incoming R-wave.
* **ZephyrMarkers** a marker stream with some device events (e.g., button pressed,
battery low, worn status changed and a handful of others, along with some
event-specific numeric payload).
* **ZephyrSummary** summary metrics calculated at 1Hz. This has over 60 channels,
including things like heart rate, respiration rate (beats/breaths per minute),
HRV, various accelerometer-derived measures, confidence measures, and some
system status channels, among others.
* **ZephyrGeneral** an alternative set of summary metrics (subset of summary,
plus a handful of channels of limited utility).

The application can generate the following streams (assuming the Zephyr stream name prefix). Refer to the vendor manual for complete details.

* **ZephyrECG:** The raw ECG waveform, in mV at 250 Hz (1 channel).
* **ZephyrResp:** The raw respiration (breathing) waveform, measuring chest expansion, at approximately 17.8 Hz, in unscaled units.
* **ZephyrAccel100mg:** A 3-channel stream with acceleration along the X, Y, and Z axes (coordinate system is configurable via vendor tools) at 50Hz, in units of g (earth acceleration).
* **ZephyrAccel:** Same as Accel100mg, but with higher numeric precision (>2x), in unscaled units.
* **ZephyrRtoR:** The interval between the most recent two ECG R-waves, in ms, at approximately 17.8 Hz. The value remains constant until the next R-wave is detected and alternates its sign with each new incoming R-wave.
* **ZephyrMarkers:** A marker stream with some device events (e.g., button pressed, battery low, worn status changed, and others), along with some event-specific numeric payload.
* **ZephyrSummary:** Summary metrics calculated at 1Hz. This stream includes over 60 channels, such as heart rate, respiration rate (beats/breaths per minute), HRV, various accelerometer-derived measures, confidence measures, and system status channels, among others.
* **ZephyrGeneral:** An alternative set of summary metrics (subset of summary, plus some channels of limited utility).

Acknowledgements
================

This open-source LSL application was developed and is maintained by Intheon (www.intheon.io).

For support inquiries please file a GitHub issue on this repo (preferred) or contact [email protected].
For support inquiries, please file a GitHub issue on this repo (preferred) or contact [email protected].

Copyright (C) 2020-2021 Syntrogi Inc. dba Intheon.

Expand All @@ -101,8 +66,4 @@ This work was funded by the Army Research Laboratory under Cooperative Agreement
Known Issues
============

Python's Bluetooth (LE) support on current Mac OS appears to be in an incomplete and/or
broken state at this point, and this application is known to not run on Mac OS
Big Sur, neither with conda's Python 3.7 nor the default Python 3.7, or with PyBlueZ 0.23
or its current GitHub version (PyBlueZ 0.30). Older Mac OS versions might work but have
not been tested.
Python's Bluetooth (LE) support on current macOS versions appears to be incomplete or broken, and this application is known not to run on macOS Big Sur, neither with conda's Python 3.7, the default Python 3.7, PyBlueZ 0.23, nor its current GitHub version (PyBlueZ 0.30). Older macOS versions might work but have not been tested.
9 changes: 4 additions & 5 deletions conda-environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ channels:
- conda-forge
- defaults
dependencies:
- python=3.7
- python
- matplotlib
- numpy
- pip
- pip:
- cbitstruct
- pybluez ; platform_system != "Windows"
- https://s3.amazonaws.com/resources.neuropype.io/wheels/PyBluez-0.22-cp37-cp37m-win_amd64.whl ; platform_system == "Windows"
- pylsl==1.13.1
- -r file:requirements.txt
2 changes: 1 addition & 1 deletion core/bluetooth.py → core/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

class BioharnessIO:

def __init__(self, address='', port=1, lifesign_interval=2, reconnect=True,
def __init__(self, address='', port=5, lifesign_interval=2, reconnect=True,
daemon=False):
"""Handle message-level communication with a Bioharness device via BLE.

Expand Down
2 changes: 1 addition & 1 deletion core/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import asyncio
from concurrent import futures

from .bluetooth import BioharnessIO
from .connection import BioharnessIO
from .protocol import Message, MI, periodic_messages, transmit_state2data_packet

logger = logging.getLogger(__name__)
Expand Down
Loading