1.2.0 • Published 5 years ago

openbci-ganglion v1.2.0

Weekly downloads
5
License
MIT
Repository
github
Last release
5 years ago

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

Typedefs

Ganglion

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

new Ganglion(options, callback)

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

ParamTypeDescription
optionsInitializationObject(optional) - Board optional configurations.
callbackfunction(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)

ParamTypeDescription
idString | Objecta 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)

ParamTypeDescription
stopStreamingBoolean(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

ParamTypeDescription
maxSearchTimeNumberThe 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)

ParamTypeDescription
dataArray | Buffer | Number | StringThe 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)

ParamTypeDescription
dataBuffer | Buffer2The 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

NameTypeDescription
bled112BooleanWhether to use bled112 as bluetooth driver or default to first available. (Default false)
debugBooleanPrint out a raw dump of bytes sent and received. (Default false)
driverAutoInitBooleanUsed to auto start either noble or the bled112 drivers (Default true)
nobleAutoStartBooleanAutomatically initialize noble. Subscribes to blue tooth state changes and such. (Default true)
nobleScanOnPowerOnBooleanStart scanning for Ganglion BLE devices as soon as power turns on. (Default true)
sendCountsBooleanSend integer raw counts instead of scaled floats. (Default false)
simulateBoolean(IN-OP) Full functionality, just mock data. (Default false)
simulatorBoardFailureBoolean(IN-OP) Simulates board communications failure. This occurs when the RFduino on the board is not polling the RFduino on the dongle. (Default false)
simulatorHasAccelerometerBooleanSets simulator to send packets with accelerometer data. (Default true)
simulatorInjectAlphaBooleanInject a 10Hz alpha wave in Channels 1 and 2 (Default true)
simulatorInjectLineNoiseStringInjects line noise on channels. 3 Possible Options: 60Hz - 60Hz line noise (Default) America 50Hz - 50Hz line noise Europe none - Do not inject line noise.
simulatorSampleRateNumberThe 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)
BooleanPrint out useful debugging events. (Default false)

BLED112AttributeValue : Object

Kind: global typedef
Properties

NameTypeDescription
characteristicHandleNumber
characteristicHandleRawBufferThe string of the advertisement data, not the full ad data
connectionNumberThe connection the info is from
typeNumberThe type, where 0x01 is data?
valueBufferThe value from device

BLED112AttributeWrite : Object

Kind: global typedef
Properties

NameTypeDescription
characteristicHandleRawBufferBuffer of length 2 for the service number in the att database
connectionNumberWhich connection is being used
valueString | BufferThe value to send to the device

BLEDConnection : Object

Kind: global typedef
Properties

NameType
addressTypeNumber
bondingNumber
connectionNumber
connectionIntervalNumber
flagsNumber
latencyNumber
senderBuffer
timeoutNumber

BLED112FindInformationFound : Object

Kind: global typedef
Properties

NameTypeDescription
characteristicHandleNumber
characteristicHandleRawBufferThe string of the advertisement data, not the full ad data
connectionNumberThe entire end of ad data
typeNumberThe type, where 0x02 is short uuid and 0x10 is long, it's hex for length
uuidBuffer

BLED112GapConnectDirect : Object

Kind: global typedef
Properties

NameType
connectionNumber
resultBuffer

BLED112GroupService : Object

Kind: global typedef
Properties

NameType
connectionnumber
endnumber
endRawBuffer
startnumber
startRawBuffer
uuidBuffer

BLED112ParseRawAttributeValue : Object

Kind: global typedef
Properties

NameTypeDescription
bufferBuffer | Buffer2The raw data buffer to parse
ignoreNumberThe position to ignore in the word
lengthNumberThe length of raw you want to extract
lengthPositionNumberThe position of the byte that stores the length of the value
verifyObject
verify.comparePositionNumberThe value to compare with position
verify.differenceNumberThe difference between position and comparePostion
verify.ignoreNumberThe difference between position and comparePostion
verify.positionNumberThe position of the verification byte
wordBuffer | Buffer2The 4 byte word to search for, ignore byte in postion 1

BLED112ParseRawHeadTail : Object

Kind: global typedef
Properties

NameTypeDescription
bufferBuffer | Buffer2The raw data buffer to parse
headNumberThe head byte to search for
lengthNumberThe length of raw you want to extract
tailNumberThe tail byte to search for

BLED112ParseRawWord : Object

Kind: global typedef
Properties

NameTypeDescription
bufferBuffer | Buffer2The raw data buffer to parse
lengthNumberThe length of raw you want to extract
verifyObject
verify.positionNumberThe position of the verification byte
verify.valueNumberThe value of the verification byte
wordBuffer | Buffer2The 4 byte word to search for

BLED112Peripheral : Object

Kind: global typedef
Properties

NameTypeDescription
addressTypeNumber
advertisementObject
advertisement.localNameStringSame as advertisementDataString but mimics what noble outputs
advertisementDataStringStringThe string of the advertisement data, not the full ad data
advertisementDataRawBuffer | Buffer2The entire end of ad data
bondNumber
packetTypeNumber-
rssiNumberThe RSSI which stands for receive signal strength indicator and is in db so it's negative, and lower the better.
senderBuffer | Buffer2The mac address

BLED112RspGroupType : Object

Kind: global typedef
Properties

NameType
connectionNumber
resultBuffer

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

1.2.0

5 years ago

1.1.12

6 years ago

1.1.11

6 years ago

1.1.10

6 years ago

1.1.9

6 years ago

1.1.8

6 years ago

1.1.7

6 years ago

1.1.6

6 years ago

1.1.5

6 years ago

1.1.4

6 years ago

1.1.3

6 years ago

1.1.2

6 years ago

1.1.1

6 years ago

1.1.0

6 years ago

1.0.0

7 years ago

0.4.3

7 years ago

0.4.2

7 years ago

0.4.1

7 years ago

0.4.0

7 years ago

0.3.8

7 years ago

0.3.7

7 years ago

0.3.6

7 years ago

0.3.5

7 years ago

0.3.4

7 years ago

0.3.3

7 years ago

0.3.2

7 years ago

0.3.1

7 years ago

0.3.0

7 years ago

0.2.0

7 years ago

0.1.1

7 years ago

0.1.0

7 years ago