Skip to content

Latest commit

 

History

History
1061 lines (747 loc) · 42.7 KB

README.md

File metadata and controls

1061 lines (747 loc) · 42.7 KB

Join the chat at https://gitter.im/OpenBCI/OpenBCI_NodeJS Build Status codecov Dependency Status npm

OpenBCI Node.js Ganglion SDK

A Node.js module for OpenBCI.

We are proud to support all functionality of the Ganglion (4 channel).

The purpose of this module is to get connected and start streaming as fast as possible.

Table of Contents:


  1. TL;DR
  2. Prerequisites
  3. Installation
  4. Ganglion
  5. General Overview
  6. SDK Reference Guide
  7. Interfacing With Other Tools
  8. Developing
  9. Testing
  10. Contribute
  11. License
  12. Roadmap

TL;DR:

Get connected and start streaming right now

const Ganglion = require("@openbci/ganglion");
const ganglion = new Ganglion();
ganglion.once("ganglionFound", peripheral => {
  // Stop searching for BLE devices once a ganglion is found.
  ganglion.searchStop();
  ganglion.on("sample", sample => {
    /** Work with sample */
    console.log(sample.sampleNumber);
    for (let i = 0; i < ganglion.numberOfChannels(); i++) {
      console.log(
        "Channel " +
          (i + 1) +
          ": " +
          sample.channelData[i].toFixed(8) +
          " Volts."
      );
    }
  });
  ganglion.once("ready", () => {
    ganglion.streamStart();
  });
  ganglion.connect(peripheral);
});
// Start scanning for BLE devices
ganglion.searchStart();

Prerequisites:

Please ensure Python 2.7 is installed for all OS.

macOS

Linux

  • Kernel version 3.6 or above
  • libbluetooth-dev
  • libudev-dev

Running without sudo

In order to stream data on Linux without root/sudo access, you may need to give the node binary privileges to start and stop BLE advertising.

sudo setcap cap_net_raw+eip $(eval readlink -f `which node`)

Note: this command requires setcap to be installed. Install it with the libcap2-bin package.

sudo apt-get install libcap2-bin

Windows 8+

See @don's set up guide on Bluetooth LE with Node.js and Noble on Windows.

Installation:

Install from npm:

npm install @openbci/ganglion

About:

The Ganglion driver used by OpenBCI's Processing GUI and Electron Hub.

Check out the automatic tests written for it!

General Overview:

Initialization

Initializing the board:

const Ganglion = require("@openbci/ganglion");
const ganglion = new Ganglion();

For initializing with options, such as verbose print outs:

const Ganglion = require("@openbci/ganglion");
const ourBoard = new Ganglion({
  verbose: true
});

For initializing with callback, such as to catch errors on noble startup:

const Ganglion = require("@openbci/ganglion");
const ourBoard = new Ganglion(error => {
  if (error) {
    console.log("error", error);
  } else {
    console.log("no error");
  }
});

For initializing with options and callback, such as verbose and to catch errors on noble startup:

const Ganglion = require("@openbci/ganglion");
const ourBoard = new Ganglion(
  {
    verbose: true
  },
  error => {
    if (error) {
      console.log("error", error);
    } else {
      console.log("no error");
    }
  }
);

'ready' event

You MUST wait for the 'ready' event to be emitted before streaming/talking with the board. The ready happens asynchronously so installing the 'sample' listener and writing before the ready event might result in... nothing at all.

const Ganglion = require("@openbci/ganglion");
const ourBoard = new Ganglion();
ourBoard
  .connect(portName)
  .then(function(boardSerial) {
    ourBoard.on("ready", function() {
      /** Start streaming, reading registers, what ever your heart desires  */
    });
  })
  .catch(function(err) {
    /** Handle connection errors */
  });

Sample properties:

  • sampleNumber (a Number between 0-255)
  • channelData (channel data indexed at 0 filled with floating point Numbers in Volts)
  • accelData (Array with X, Y, Z accelerometer values when new data available)
  • timeStamp (Number the boardTime plus the NTP calculated offset)

The power of this module is in using the sample emitter, to be provided with samples to do with as you wish.

To get a 'sample' event, you need to:

  1. Call .connect(localName | peripheral)
  2. Install the 'ready' event emitter on resolved promise
  3. In callback for 'ready' emitter, call streamStart()
  4. Install the 'sample' event emitter
const Ganglion = require("@openbci/ganglion");
const ourBoard = new Ganglion();
ourBoard
  .connect(localName)
  .then(function() {
    ourBoard.on("ready", function() {
      ourBoard.streamStart();
      ourBoard.on("sample", function(sample) {
        /** Work with sample */
      });
    });
  })
  .catch(function(err) {
    /** Handle connection errors */
  });

Close the connection with .streamStop() and disconnect with .disconnect()

const Ganglion = require("@openbci/ganglion");
const ourBoard = new Ganglion();
ourBoard.streamStop().then(ourBoard.disconnect());

See Reference Guide for a complete list of impedance tests.

SDK Reference Guide:


Classes

Ganglion

Typedefs

InitializationObject : Object

Board optional configurations.

BLED112AttributeValue : Object
BLED112AttributeWrite : Object
BLEDConnection : Object
BLED112FindInformationFound : Object
BLED112GapConnectDirect : Object
BLED112GroupService : Object
BLED112ParseRawAttributeValue : Object
BLED112ParseRawHeadTail : Object
BLED112ParseRawWord : Object
BLED112Peripheral : Object
BLED112RspGroupType : Object

Ganglion

Kind: global class
Author: AJ Keller (@pushtheworldllc)

new Ganglion(options, callback)

The initialization method to call first, before any other method.

Param Type Description
options InitializationObject (optional) - Board optional configurations.
callback function (optional) - A callback function used to determine if the noble module was able to be started. This can be very useful on Windows when there is no compatible BLE device found.

ganglion.options : InitializationObject

Kind: instance property of Ganglion

ganglion._accelArray

Private Properties (keep alphabetical)

Kind: instance property of Ganglion

ganglion._bled112WriteCharacteristic : BLED112FindInformationFound

Kind: instance property of Ganglion

ganglion.buffer

Public Properties (keep alphabetical)

Kind: instance property of Ganglion

ganglion.accelStart() ⇒ Promise

Used to enable the accelerometer. Will result in accelerometer packets arriving 10 times a second. Note that the accelerometer is enabled by default.

Kind: instance method of Ganglion

ganglion.accelStop() ⇒ Promise

Used to disable the accelerometer. Prevents accelerometer data packets from arriving.

Kind: instance method of Ganglion

ganglion.autoReconnect()

Used to start a scan if power is on. Useful if a connection is dropped.

Kind: instance method of Ganglion

ganglion.channelOff(channelNumber) ⇒ Promise.<T>

Send a command to the board to turn a specified channel off

Kind: instance method of Ganglion
Author: AJ Keller (@pushtheworldllc)

Param
channelNumber

ganglion.channelOn(channelNumber) ⇒ Promise.<T> | *

Send a command to the board to turn a specified channel on

Kind: instance method of Ganglion
Author: AJ Keller (@pushtheworldllc)

Param
channelNumber

ganglion.cleanupEmitters()

Used to clean up emitters

Kind: instance method of Ganglion

ganglion.connect(id) ⇒ Promise

The essential precursor method to be called initially to establish a ble connection to the OpenBCI ganglion board.

Kind: instance method of Ganglion
Returns: Promise - If the board was able to connect.
Author: AJ Keller (@pushtheworldllc)

Param Type Description
id String | Object a string local name or peripheral object

ganglion.destroyNoble()

Destroys the noble!

Kind: instance method of Ganglion

ganglion.destroyBLED112()

Destroys the noble!

Kind: instance method of Ganglion

ganglion.destroyMultiPacketBuffer()

Destroys the multi packet buffer.

Kind: instance method of Ganglion

ganglion.disconnect(stopStreaming) ⇒ Promise

Closes the connection to the board. Waits for stop streaming command to be sent if currently streaming.

Kind: instance method of Ganglion
Returns: Promise - - fulfilled by a successful close, rejected otherwise.
Author: AJ Keller (@pushtheworldllc)

Param Type Description
stopStreaming Boolean (optional) - True if you want to stop streaming before disconnecting.

ganglion.getLocalName() ⇒ null | String

Return the local name of the attached Ganglion device.

Kind: instance method of Ganglion

ganglion.getMutliPacketBuffer() ⇒ null | Buffer

Get's the multi packet buffer.

Kind: instance method of Ganglion
Returns: null | Buffer - - Can be null if no multi packets received.

ganglion.impedanceStart() ⇒ global.Promise | Promise

Call to start testing impedance.

Kind: instance method of Ganglion

ganglion.impedanceStop() ⇒ global.Promise | Promise

Call to stop testing impedance.

Kind: instance method of Ganglion

ganglion.initDriver() ⇒ Promise.<any>

Initialize the drivers

Kind: instance method of Ganglion

ganglion.isConnected() ⇒ boolean

Checks if the driver is connected to a board.

Kind: instance method of Ganglion
Returns: boolean - - True if connected.

ganglion.isNobleReady() ⇒ boolean

Checks if bluetooth is powered on.

Kind: instance method of Ganglion
Returns: boolean - - True if bluetooth is powered on.

ganglion.isSearching() ⇒ boolean

Checks if noble is currently scanning.

Kind: instance method of Ganglion
Returns: boolean - - True if streaming.

ganglion.isStreaming() ⇒ boolean

Checks if the board is currently sending samples.

Kind: instance method of Ganglion
Returns: boolean - - True if streaming.

ganglion.numberOfChannels() ⇒ Number

This function is used as a convenience method to determine how many channels the current board is using.

Kind: instance method of Ganglion
Returns: Number - A number Note: This is dependent on if you configured the board correctly on setup options
Author: AJ Keller (@pushtheworldllc)

ganglion.printRegisterSettings() ⇒ Promise.<T> | *

To print out the register settings to the console

Kind: instance method of Ganglion
Author: AJ Keller (@pushtheworldllc)

ganglion.sampleRate() ⇒ Number

Get the the current sample rate is.

Kind: instance method of Ganglion
Returns: Number - The sample rate Note: This is dependent on if you configured the board correctly on setup options

ganglion.searchStart(`maxSearchTime`) ⇒ Promise

List available peripherals so the user can choose a device when not automatically found.

Kind: instance method of Ganglion
Returns: Promise - - If scan was started

Param Type Description
maxSearchTime Number The amount of time to spend searching. (Default is 20 seconds)

ganglion.searchStop() ⇒ global.Promise | Promise

Called to end a search.

Kind: instance method of Ganglion

ganglion.softReset() ⇒ Promise

Sends a soft reset command to the board

Kind: instance method of Ganglion
Returns: Promise - - Fulfilled if the command was sent to board.
Author: AJ Keller (@pushtheworldllc)

ganglion.streamStart() ⇒ Promise

Sends a start streaming command to the board.

Kind: instance method of Ganglion
Returns: Promise - indicating if the signal was able to be sent. Note: You must have successfully connected to an OpenBCI board using the connect method. Just because the signal was able to be sent to the board, does not mean the board will start streaming.
Author: AJ Keller (@pushtheworldllc)

ganglion.streamStop() ⇒ Promise

Sends a stop streaming command to the board.

Kind: instance method of Ganglion
Returns: Promise - indicating if the signal was able to be sent. Note: You must have successfully connected to an OpenBCI board using the connect method. Just because the signal was able to be sent to the board, does not mean the board stopped streaming.
Author: AJ Keller (@pushtheworldllc)

ganglion.syntheticEnable() ⇒ Promise

Puts the board in synthetic data generation mode. Must call streamStart still.

Kind: instance method of Ganglion
Returns: Promise - indicating if the signal was able to be sent.
Author: AJ Keller (@pushtheworldllc)

ganglion.syntheticDisable() ⇒ Promise

Takes the board out of synthetic data generation mode. Must call streamStart still.

Kind: instance method of Ganglion
Returns: Promise - - fulfilled if the command was sent.
Author: AJ Keller (@pushtheworldllc)

ganglion.write(data) ⇒ Promise

Used to send data to the board.

Kind: instance method of Ganglion
Returns: Promise - - fulfilled if command was able to be sent
Author: AJ Keller (@pushtheworldllc)

Param Type Description
data Array | Buffer | Number | String The data to write out

Example

Sends a single character command to the board.

// ourBoard has fulfilled the promise on .connect() and 'ready' has been observed previously
ourBoard.write("a");

Sends an array of bytes

// ourBoard has fulfilled the promise on .connect() and 'ready' has been observed previously
ourBoard.write(["x", "0", "1", "0", "0", "0", "0", "0", "0", "X"]);

Call crazy? Go for it...

ourBoard.write("t");
ourBoard.write("a");
ourBoard.write("c");
ourBoard.write("o");

ganglion._bled112WriteAndDrain(data) ⇒ Promise

Should be used to send data to the board

Kind: instance method of Ganglion
Returns: Promise - if signal was able to be sent
Author: AJ Keller (@pushtheworldllc)

Param Type Description
data Buffer | Buffer2 The data to write out

Ganglion~o

Configuring Options

Kind: inner property of Ganglion

kOBCIBLED112ParsingConnectDirect

Used in parsing incoming serial data

Kind: global constant

InitializationObject : Object

Board optional configurations.

Kind: global typedef
Properties

Name Type Description
bled112 Boolean Whether to use bled112 as bluetooth driver or default to first available. (Default false)
debug Boolean Print out a raw dump of bytes sent and received. (Default false)
driverAutoInit Boolean Used to auto start either noble or the bled112 drivers (Default true)
nobleAutoStart Boolean Automatically initialize noble. Subscribes to blue tooth state changes and such. (Default true)
nobleScanOnPowerOn Boolean Start scanning for Ganglion BLE devices as soon as power turns on. (Default true)
sendCounts Boolean Send integer raw counts instead of scaled floats. (Default false)
simulate Boolean (IN-OP) Full functionality, just mock data. (Default false)
simulatorBoardFailure Boolean (IN-OP) Simulates board communications failure. This occurs when the RFduino on the board is not polling the RFduino on the dongle. (Default false)
simulatorHasAccelerometer Boolean Sets simulator to send packets with accelerometer data. (Default true)
simulatorInjectAlpha Boolean Inject a 10Hz alpha wave in Channels 1 and 2 (Default true)
simulatorInjectLineNoise String Injects line noise on channels. 3 Possible Options: 60Hz - 60Hz line noise (Default) [America] 50Hz - 50Hz line noise [Europe] none - Do not inject line noise.
simulatorSampleRate Number The sample rate to use for the simulator. Simulator will set to 125 if simulatorDaisyModuleAttached is set true. However, setting this option overrides that setting and this sample rate will be used. (Default is 250)
Boolean Print out useful debugging events. (Default false)

BLED112AttributeValue : Object

Kind: global typedef
Properties

Name Type Description
characteristicHandle Number
characteristicHandleRaw Buffer The string of the advertisement data, not the full ad data
connection Number The connection the info is from
type Number The type, where 0x01 is data?
value Buffer The value from device

BLED112AttributeWrite : Object

Kind: global typedef
Properties

Name Type Description
characteristicHandleRaw Buffer Buffer of length 2 for the service number in the att database
connection Number Which connection is being used
value String | Buffer The value to send to the device

BLEDConnection : Object

Kind: global typedef
Properties

Name Type
addressType Number
bonding Number
connection Number
connectionInterval Number
flags Number
latency Number
sender Buffer
timeout Number

BLED112FindInformationFound : Object

Kind: global typedef
Properties

Name Type Description
characteristicHandle Number
characteristicHandleRaw Buffer The string of the advertisement data, not the full ad data
connection Number The entire end of ad data
type Number The type, where 0x02 is short uuid and 0x10 is long, it's hex for length
uuid Buffer

BLED112GapConnectDirect : Object

Kind: global typedef
Properties

Name Type
connection Number
result Buffer

BLED112GroupService : Object

Kind: global typedef
Properties

Name Type
connection number
end number
endRaw Buffer
start number
startRaw Buffer
uuid Buffer

BLED112ParseRawAttributeValue : Object

Kind: global typedef
Properties

Name Type Description
buffer Buffer | Buffer2 The raw data buffer to parse
ignore Number The position to ignore in the word
length Number The length of raw you want to extract
lengthPosition Number The position of the byte that stores the length of the value
verify Object
verify.comparePosition Number The value to compare with position
verify.difference Number The difference between position and comparePostion
verify.ignore Number The difference between position and comparePostion
verify.position Number The position of the verification byte
word Buffer | Buffer2 The 4 byte word to search for, ignore byte in postion 1

BLED112ParseRawHeadTail : Object

Kind: global typedef
Properties

Name Type Description
buffer Buffer | Buffer2 The raw data buffer to parse
head Number The head byte to search for
length Number The length of raw you want to extract
tail Number The tail byte to search for

BLED112ParseRawWord : Object

Kind: global typedef
Properties

Name Type Description
buffer Buffer | Buffer2 The raw data buffer to parse
length Number The length of raw you want to extract
verify Object
verify.position Number The position of the verification byte
verify.value Number The value of the verification byte
word Buffer | Buffer2 The 4 byte word to search for

BLED112Peripheral : Object

Kind: global typedef
Properties

Name Type Description
addressType Number
advertisement Object
advertisement.localName String Same as advertisementDataString but mimics what noble outputs
advertisementDataString String The string of the advertisement data, not the full ad data
advertisementDataRaw Buffer | Buffer2 The entire end of ad data
bond Number
packetType Number -
rssi Number The RSSI which stands for receive signal strength indicator and is in db so it's negative, and lower the better.
sender Buffer | Buffer2 The mac address

BLED112RspGroupType : Object

Kind: global typedef
Properties

Name Type
connection Number
result Buffer

Events:

.on('accelerometer', callback)

Emitted when the module receives accelerometer data.

Returns an object with properties:

accelData {Array}

Array of floats for each dimension in g's.

NOTE: Only present if sendCounts is true.

accelDataCounts {Array}

Array of integers for each dimension in counts.

NOTE: Only present if sendCounts is false.

Example (if sendCounts is false):

{
  "accelData": [0.0, 0.0, 0.0, 0.0]
}

Example (if sendCounts is true):

{
  "accelDataCounts": [0, 0, 0, 0]
}

.on('droppedPacket', callback)

Emitted when a packet (or packets) are dropped. Returns an array.

.on('error', callback)

Emitted when there is an on the serial port.

.on('impedance', callback)

Emitted when there is a new impedance available.

Returns an object with properties:

channelNumber {Number}

The channel number: 1, 2, 3, 4 respectively and 0 for reference.

impedanceValue {Number}

The impedance in ohms.

Example:

{
  "channelNumber": 0,
  "impedanceValue": 0
}

.on('rawDataPacket', callback)

Emitted when there is a new raw data packet available.

.on('ready', callback)

Emitted when the board is in a ready to start streaming state.

.on('sample', callback)

Emitted when there is a new sample available.

Returns an object with properties:

channelData {Array}

Array of floats for each channel in volts..

NOTE: Only present if sendCounts is true.

channelDataCounts {Array}

Array of integers for each channel in counts.

NOTE: Only present if sendCounts is false.

sampleNumber {Number}

The sample number. Only goes up to 254.

timeStamp {Number}

The time the sample is packed up. Not accurate for ERP.

Example (if sendCounts is false):

{
  "channelData": [0.0, 0.0, 0.0, 0.0],
  "sampleNumber": 0,
  "timeStamp": 0
}

Example (if sendCounts is true):

{
  "channelDataCounts": [0, 0, 0, 0],
  "sampleNumber": 0,
  "timeStamp": 0
}

.on('scanStart', callback)

Emitted when a noble scan is started.

.on('scanStop', callback)

Emitted when a noble scan is stopped.

Interfacing With Other Tools:

LabStreamingLayer

LabStreamingLayer is a tool for streaming or recording time-series data. It can be used to interface with Matlab, Python, Unity, and many other programs.

To use LSL with the NodeJS SDK, go to our labstreaminglayer example, which contains code that is ready to start an LSL stream of OpenBCI data.

Follow the directions in the readme to get started.

Developing:

Running:

npm install

Testing:

npm test

Contribute:

  1. Fork it!
  2. Branch off of development: git checkout development
  3. Create your feature branch: git checkout -b my-new-feature
  4. Make changes
  5. If adding a feature, please add test coverage.
  6. Ensure tests all pass. (npm test)
  7. Commit your changes: git commit -m 'Add some feature'
  8. Push to the branch: git push origin my-new-feature
  9. Submit a pull request. Make sure it is based off of the development branch when submitting! :D

License:

MIT