XNeuroSDK 1.0.6.27

dotnet add package XNeuroSDK --version 1.0.6.27                
NuGet\Install-Package XNeuroSDK -Version 1.0.6.27                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="XNeuroSDK" Version="1.0.6.27" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add XNeuroSDK --version 1.0.6.27                
#r "nuget: XNeuroSDK, 1.0.6.27"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install XNeuroSDK as a Cake Addin
#addin nuget:?package=XNeuroSDK&version=1.0.6.27

// Install XNeuroSDK as a Cake Tool
#tool nuget:?package=XNeuroSDK&version=1.0.6.27                

Overview

Neurosdk is a powerful tool for working with neuro-sensors BrainBit, BrainBitBlack, Callibri and Kolibri. All these devices work with BLE 4.2+ technology. Xamarin SDK is designed for Android and iOS platforms. SDK allows you to connect, read the parameters of devices, as well as receive signals of various types from the selected device.

Getting Started

Android:

Only for Android you need to add library initialization:

        public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder();
#if ANDROID
            builder.UseNeurosdk2();
#endif
            return builder.Build();
        }

For Android you need request permissions to location and bluetooth:

#if ANDROID
        private async void requestPermissions()
        {
            if(Build.VERSION.SdkInt >= BuildVersionCodes.S)
            {
                PermissionStatus locationStatus = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();
                PermissionStatus BTStatus = await Permissions.CheckStatusAsync<Permissions.Bluetooth>();
            }
            else
            {
                PermissionStatus BTstatus = await Permissions.CheckStatusAsync<Permissions.Bluetooth>();
            }

        }
#endif

iOS

For iOS you need to add a key to Info.plist:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>Explanation to using bluetooth</string>

Windows

You need to open package manifest and add enable capability "Bluetooth".

Another way is add this capability manually:

  1. Go into Platforms/Windows/ folder
  2. Open Package.appxmanifest file
  3. Into Capabilities section add: <DeviceCapability Name="bluetooth"/>

Windows DotNet

You don't need to configure anything.

Usage

Scanner

The scanner works like this:

  1. Create scanner. When creating a scanner, you need to specify the type of device to search. It can be either one device or several. Here is example for two type of devices - BrainBit and Callibri.
Scanner scanner = new Scanner(SensorFamily.SensorLECallibri, SensorFamily.SensorLEBrainBit, SensorFamily.SensorLEBrainBit2);
  1. During the search, you can get a list of found devices using a callback. To do this, you need to subscribe to receive the event, and unsubscribe after the search is completed:
private void Scanner_Founded(IScanner scanner, IReadOnlyList<SensorInfo> sensors)
{
    foreach(SensorInfo sensorInfo in sensors){
        Console.WriteLine(sensorInfo.Name + ": " + sensorInfo.Address);
    }
}

scanner.EventSensorsChanged += Scanner_Founded;
...
scanner.EventSensorsChanged -= Scanner_Founded;
  1. Start and stop search
scanner.Start();
...
scanner.Stop();
  1. Additionally, a list of found devices can be obtained using a separate method.
IReadOnlyList<SensorInfo> sensors = scanner.Sensors;

SensorInfo contains information about device:

Field Type Description
Name string the name of device
Address string MAC address of device (UUID for iOS/MacOS)
SerialNumber string device's serial number
SensFamily SensorFamily type of device
SensModel byte numerical value of the device model
PairingRequired bool whether the device needs to be paired or not
RSSI short current signal strength in dBm. The valid range is [-127, 126]
  1. After you finish working with the scanner, you need to clean up the resources used.
scanner.Dispose();

Sensor

You need to create a device using a scanner. All manipulations with the device will be performed without errors only if the device is connected.

// BrainBit
BrainBitSensor sensor = scanner.CreateSensor(sensorInfo) as BrainBitSensor;

// Callibri
CallibriSensor sensor = scanner.CreateSensor(sensorInfo) as CallibriSensor;

// BrainBit2
BrainBit2Sensor sensor = scanner.CreateSensor(sensorInfo) as BrainBit2Sensor;

Device creation is a blocking method, so it must be called from separate thread.

Manage connection state

Connection status can be obtained in two ways. The first one is using the sensor property State.

The second way is in real time using a callback:

 private void EventSensorStateChanged(ISensor sensor, SensorState sensorState)
{
    Console.WriteLine(sensorState);
}
...
sensor.EventSensorStateChanged += EventSensorStateChanged;
...
sensor.EventSensorStateChanged -= EventSensorStateChanged;

A connection can be in two states: connected (InRange) and disconnected (OutOfRange).

Important! The state change callback will not come after the device is created, only after disconnecting (device lost from the scope or using a method) and then connected. The device does not automatically reconnect.

You can connect and disconnect from device manually by methods Connect() and Disconnect(). To receive connection state in real time you need to subscribe to stateChanged event. Also you can get connection state by sensor's property.

sensor.Disconnect();
...
sensor.Connect();

Battery

Also, you can get power value from each device by sensor property BattPower or by callback in real time:

private void EventBatteryChanged(ISensor sensor, int battPower)
{
    Console.WriteLine("Power: " + battPower);
}
...
sensor.EventBatteryChanged += EventBatteryChanged;
...
sensor.EventBatteryChanged -= EventBatteryChanged;

Parameters

Each device has its own settings, and some of them can be configured as you need. Not all settings can be changed and not every device supports all settings.

Firstly you need to find out what parameters the device supports and whether they can be changed:

var parameters = sensor.Parameters;

Info about parameter includes two fields:

  • the name of the parameter, represented by an enumeration
  • parameter availability for manipulation. Can be one of three values:
    • read - read-only
    • read and write - parameter can be changed
    • read and notify - parameter is updated over time

You can also check if the parameter is supported, for example Gain:

if(sensor.IsSupportedParameter(SensorParameter.ParameterGain)){
...
}

Each device has a specific set of modules. You can find out which modules the device has using the property Feature:

var features = sensor.Features;

You can also check if the feature is supported, for example Signal:

if(sensor.IsSupportedFeature(SensorFeature.FeatureSignal)){
...
}

The device can execute certain commands. The list of supported commands can be obtained as follows:

var commands = sensor.Commands;

And also check if the device can execute the desired command:

if(sensor.IsSupportedCommand(SensorCommand.StartSignal)){
...
}

BrainBit, BrainBitBlack

The BrainBit and BrainBitBlack is a headband with 4 electrodes and 4 data channels - O1, O2, T3, T4. The device has a frequency of 250 Hz, which means that data on each of the channels will come at a frequency of 250 samples per second. The parameters of this device, such as gain, data offset and the other, cannot be changed, if you try to do this, an exception will appear.

sensor.Gain = SensorGain.SensorGain1; // <- This throw an exeption!

You can distinguish BrainBit device from Flex by the firmware version number: if the SensorVersion.FwMajor is more than 100 - it's Flex, if it's less than BrainBit.

BrainBitBlack, unlike BrainBit, requires pairing with a PC/mobile device. So, before connecting to the BBB, you must put it into pairing mode. SDK starts the pairing process automatically.

To use the BrainBit features, you need to bring the sensor type to a BrainBit or BrainBitBlack device type. The sensor as such does not provide signal or resistance values.

BrainBitSensor sensor = scanner.CreateSensor(info) as BrainBitSensor;
// or
BrainBitBlackSensor sensor = scanner.CreateSensor(info) as BrainBitBlackSensor;
// or
var sensor = = scanner.CreateSensor(info);
// or
Sensor sensor = scanner.CreateSensor(info);
BrainBitSensor BBsensor = sensor as BrainBitSensor;
Receiving signal

To receive signal data, you need to subscribe to the corresponding callback. The values will be received as a packet from four channels at once, which will avoid desynchronization between them. The values come in volts. In order for the device to start transmitting data, you need to start a signal using the execute command. This method is also recommended to be run in an separate thread.

private void onBrainBitSignalDataRecived(ISensor sensor, BrainBitSignalData[] data)
{
    Console.WriteLine("Data: " + data);
}
...
sensor.EventBrainBitSignalDataRecived += onBrainBitSignalDataRecived;
sensor.ExecCommand(SensorCommand.CommandStartSignal);
...
sensor.EventBrainBitSignalDataRecived -= onBrainBitSignalDataRecived;
sensor.ExecCommand(SensorCommand.CommandStopSignal);
Recieving resistance

BrainBit and BrainBitBlack also allow you to get resistance values. With their help, you can determine the quality of the electrodes to the skin.

private void onBrainBitResistDataRecived(ISensor sensor, BrainBitResistData data)
{
    Console.WriteLine("Data: " + data);
}
...
sensor.EventBrainBitResistDataRecived += onBrainBitResistDataRecived;
sensor.ExecCommand(SensorCommand.CommandStartResist);
...
sensor.EventBrainBitResistDataRecived -= onBrainBitResistDataRecived;
sensor.ExecCommand(SensorCommand.CommandStopResist);

BrainBit 2/Flex/Pro

The BrainBit2 class is designed to work with several device families: BrainBit 2, BrainBit Pro, BrainBit Flex, BrainBit Flex Pro. All devices have a sampling frequency of 250Hz. All devices can work in two modes - signal and resistance separately. These devices have different number of channels - BrainBit2, BrainBit Flex have 4 channels each and BrainBitPro, BrainBit FlexPro have 8 channels each. The main difference from BraibBit of the first version is that they do not support gain property, but have the ability to set gain for each channel separately using BrainBit2AmplifierParam structure. Also, these types of devices support the ping command.

Info about channels

The device can have 4 or 8 channels. The number of channels can be determined as follows:

IReadOnlyList<EEGChannelInfo> channels = sensor.SupportedChannelsBrainBit2;

EEGChannelInfo contains some info:

C#
Field Type Description
Id EEGChannelId physical location of the channel. You will receive the values O1, O2, T3, T4 or Unknown. Unknown means that the position of a specific electrode is free.
ChType EEGChannelType type of channel, possible values A1, A2, Differential or Referent
Name string channel name
Num byte channel number. By this number the channel will be located in the array of signal or resistance values
Amplifier parameters

You can configure each channel and whole device settings by setting amplifier parameters.

BrainBit2AmplifierParam ampParam = sensor.AmplifierParamBrainBit2;
for (int i = 0; i < ampParam.ChSignalMode.Length; ++i)
{
    ampParam.ChSignalMode[i] = BrainBit2ChannelMode.ChModeNormal;
    ampParam.ChGain[i] = SensorGain.SensorGain3;
    ampParam.ChResistUse[i] = true;
}
ampParam.Current = GenCurrent.GenCurr6nA;
sensor.AmplifierParamBrainBit2 = ampParam;

BrainBit2AmplifierParam contains:

Field Type Description
ChSignalMode BrainBit2ChannelMode[] input type
ChResistUse bool[] dont used for current version
ChGain SensorGain[] gain of an ADC signal for each channel
Current GenCurrent setting parameters of the probe current generator

Possible values for Current:

  • 0nA
  • 6nA
  • 12nA
  • 18nA
  • 24nA

Signal modes:

  • Short - shorted input
  • Normal - bipolar input mode (used for EEG)

Possible Gain values:

  • 1
  • 2
  • 3
  • 4
  • 6
  • 8
  • 12
Receiving signal

To receive signal data, you need to subscribe to the corresponding callback. The values come in volts. In order for the device to start transmitting data, you need to start a signal using the execute command. This method is also recommended to be run in an separate thread.

private static void SensorEventBrainBit2SignalDataRecived(ISensor sensor, SignalChannelsData[] data)
{
    foreach (var it in data)
    {
        Console.WriteLine($"[{it.PackNum}][{it.Marker}][{it.Samples[0]}][{it.Samples[1]}][{it.Samples[2]}][{it.Samples[3]}]");
    }
}
...
sensor.EventBrainBit2SignalDataRecived += SensorEventBrainBit2SignalDataRecived;
sensor.ExecCommand(SensorCommand.CommandStartSignal);
...
sensor.EventBrainBit2SignalDataRecived -= SensorEventBrainBit2SignalDataRecived;
sensor.ExecCommand(SensorCommand.CommandStopSignal);

You get signal values as a list of samples (SignalChannelsData), each containing:

Field Type Description
PackNum uint number for each packet
Marker byte marker of sample
Samples double[] array of samples in V. Each sample number into array consistent with Num value of EEGChannelInfo from getSupportedChannels() method.
Receiving resistance

BrainBit2 also allow you to get resistance values. With their help, you can determine the quality of the electrodes to the skin. Initial resistance values are infinity. The values change when the device is on the head.

private static void SensorEventBrainBit2ResistDataRecived(ISensor sensor, ResistRefChannelsData[] data)
{
    if (data.Length < 1) return;
    ResistRefChannelsData lastData = data[data.Length - 1];
    Console.WriteLine($"0: {lastData.Samples[0]} 1: {lastData.Samples[1]} 2: {lastData.Samples[2]} 3: {lastData.Samples[3]}");
}
...
sensor.EventBrainBit2ResistDataRecived += SensorEventBrainBit2ResistDataRecived;
sensor.ExecCommand(SensorCommand.CommandStartResist);
...
sensor.EventBrainBit2ResistDataRecived -= SensorEventBrainBit2ResistDataRecived;
sensor.ExecCommand(SensorCommand.CommandStopResist);

You get resistance values structure of samples ResistRefChannelsData for each channel:

Field Type Description
PackNum uint number for each packet
Samples double[] array of samples in V. Each sample number into array consistent with Num value of EEGChannelInfo from SupportedChannelsBrainBit2 property.
Referents double[] array of values for referents channels. For BrainBit2 sensor is empty now.

Callibri MF, Kolibri

The Callibri family of devices has a wide range of built-in modules. For each of these modules, the SDK contains its own processing area. It is recommended before using any of the modules to check if the module is supported by the device using one of the methods IsSupportedFeature, IsSupportedCommand or IsSupportedParameter

To use the Callibri features, you need to bring the sensor type to a Callibri device type. The sensor as such does not provide signal or electrodes state values.

CallibriSensor sensor = scanner.CreateSensor(info) as CallibriSensor;
// or
var sensor = = scanner.CreateSensor(info);
// or
Sensor sensor = scanner.CreateSensor(info);
CallibriSensor sensor = sensor as CallibriSensor;
Receiving signal

To receive signal data, you need to subscribe to the corresponding callback. The values come in volts. In order for the device to start transmitting data, you need to start a signal using the execute command. This method is also recommended to be run in an separate thread.

private void onCallibriSignalDataRecived(ISensor sensor, CallibriSignalData[] data)
{
    Console.WriteLine("Data: " + data);
}
...
sensor.EventCallibriSignalDataRecived += onCallibriSignalDataRecived;
sensor.ExecCommand(SensorCommand.CommandStartSignal);
...
sensor.EventCallibriSignalDataRecived -= onCallibriSignalDataRecived;
sensor.ExecCommand(SensorCommand.CommandStopSignal);

The sampling rate can be controlled using the SamplingFrequency property. For example, at a frequency of 1000 Hz, the device will send 1000 samples per second. Supports frequencies 125/250/500/1000/2000/4000 Hz. You can also adjust the signal offset (DataOffset) and signal power (Gain).

sensor.Gain = SensorGain.SensorGain6;
sensor.DataOffset = SensorDataOffset.DataOffset3;
sensor.SamplingFrequency = SamplingFrequency.FrequencyHz1000;
Check electrodes state

Allows you to determine the presence of electrical contact of the device electrodes with human skin. It can be in three states:

  • Normal - The electrodes have normal skin contact. Low electrical resistance between electrodes. The expected state when working with physiological signals.
  • Detached - High electrical resistance between electrodes. High probability of interference in the physiological signal.
  • HighResistance - There is no electrical contact between the electrodes of the device.

To receive data, you need to subscribe to the corresponding callback and start signal pickup.

private void onCallibriElectrodeStateChanged(ISensor sensor, CallibriElectrodeState elState)
{
    Console.WriteLine(elState);
}
...
sensor.EventCallibriElectrodeStateChanged += onCallibriElectrodeStateChanged;
sensor.ExecCommand(SensorCommand.CommandStartSignal);
...
sensor.EventCallibriElectrodeStateChanged -= onCallibriElectrodeStateChanged;
sensor.ExecCommand(SensorCommand.CommandStopSignal);
Receiving envelope

To get the values of the envelope, you need to subscribe to a specific event and start pickup. The channel must be configured in the same way as for a normal signal, and all parameters work the same way. Then the signal is filtered and decimated at 20 Hz.

private void onCallibriEnvelopeDataRecived(ISensor sensor, CallibriEnvelopeData[] data)
{
    Console.WriteLine("Data: " + data);
}
...
sensor.CallibriEnvelopeDataRecived += onCallibriEnvelopeDataRecived;
sensor.ExecCommand(SensorCommand.CommandStartEnvelope);
...
sensor.CallibriEnvelopeDataRecived -= onCallibriEnvelopeDataRecived;
sensor.ExecCommand(SensorCommand.CommandStopEnvelope);
MEMS

The MEMS microcircuit is optional on request. Its presence can be checked using the IsSupportedFeature method. This means that the device contains an accelerometer and a gyroscope. Contains information about the position of the device in space. Channel sampling frequency is 100 Hz.

MEMS data is a structure:

  • PackNum - number for each packet
  • Accelerometer - accelerometer data. Contains:
    • X - Abscissa Acceleration
    • Y - Y-axis acceleration
    • Z - Acceleration along the applicate axis
  • Gyroscope - gyroscope data
    • X - The angle of inclination along the abscissa axis
    • Y - Inclination angle along the ordinate axis
    • Z - Angle of inclination along the axis of the applicate

Quaternion data is a structure:

  • PackNum - number for each packet
  • W - Rotation component
  • X - Vector abscissa coordinate
  • Y - Vector coordinate along the ordinate axis
  • Z - The coordinate of the vector along the axis of the applicate

It is recommended to perform calibration on a flat, horizontal non-vibrating surface before starting work using the CalibrateMEMS command. Calibration state can be checked using the MEMSCalibrateStateCallibri property, it can take only two values: calibrated (true), not calibrated (false).

MEMS and quaternion available only to signal Callibri/Kolibri!

private void Sensor_EventQuaternionDataRecived(ISensor sensor, QuaternionData[] data)
{
    Console.WriteLine(data.ToString());
}

private void Sensor_EventMEMSDataRecived(ISensor sensor, MEMSData[] data)
{
    Console.WriteLine(data.ToString());
}
...
// Calibration
sensor.ExecCommand(SensorCommand.CommandCalibrateMEMS);
bool isCalibrated = sensor.MEMSCalibrateStateCallibri;

// For receiving MEMS
sensor.EventMEMSDataRecived += Sensor_EventMEMSDataRecived;
sensor.ExecCommand(SensorCommand.CommandStartMEMS);
...
sensor.EventMEMSDataRecived -= Sensor_EventMEMSDataRecived;
sensor.ExecCommand(SensorCommand.CommandStopMEMS);

// For quaternion
sensor.EventQuaternionDataRecived += Sensor_EventQuaternionDataRecived; 
sensor.ExecCommand(SensorCommand.CommandStartAngle);
...
sensor.EventQuaternionDataRecived -= Sensor_EventQuaternionDataRecived; 
sensor.ExecCommand(SensorCommand.CommandStopAngle);
Motion counter

Represents a motion counter. It can be configured using the CallibriMotionCounterParam property, in it:

  • InsensThreshmG – Threshold of the algorithm's deadness in amplitude (in mg). The maximum value is 500mg. The minimum value is 0.
  • InsensThreshSamp - Threshold of the algorithm's insensitivity in time (in samples with the MEMS sampling rate). The maximum value is 500 samples. The minimum value is 0.

You can find out the current number of movements using the MotionCounterCallibri property. You can reset the counter with the ResetMotionCounter command. No additional commands are needed to start the counter, it will be incremented all the time until the reset command is executed.

if(sensor.IsSupportedParameter(SensorParameter.ParameterMotionCounter))
{
    sensor.MotionCounterParamCallibri = new CallibriMotionCounterParam() { 
                    InsenseThresholdMG = 250,
                    InsenseThresholdSample = 250
                    };
    int motionCount = sensor.MotionCounterCallibri;
    sensor.ExecCommand(SensorCommand.CommandResetMotionCounter);
}
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-android34.0 is compatible.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-ios13.0 is compatible.  net8.0-maccatalyst was computed.  net8.0-maccatalyst14.0 is compatible.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net8.0-windows10.0.19041 is compatible. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net8.0-android34.0

    • No dependencies.
  • net8.0-ios13.0

    • No dependencies.
  • net8.0-maccatalyst14.0

    • No dependencies.
  • net8.0-windows10.0.19041

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.6.27 96 11/13/2024
1.0.6.26 205 9/11/2024
1.0.6.25 188 3/25/2024
1.0.6.16 176 12/18/2023
1.0.4 307 4/20/2023
1.0.3.3 335 12/19/2022
1.0.3.2 301 12/19/2022