XNeuroSDK 1.0.6.27
dotnet add package XNeuroSDK --version 1.0.6.27
NuGet\Install-Package XNeuroSDK -Version 1.0.6.27
<PackageReference Include="XNeuroSDK" Version="1.0.6.27" />
paket add XNeuroSDK --version 1.0.6.27
#r "nuget: XNeuroSDK, 1.0.6.27"
// 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:
- Go into
Platforms/Windows/
folder - Open
Package.appxmanifest
file - Into
Capabilities
section add:<DeviceCapability Name="bluetooth"/>
Windows DotNet
You don't need to configure anything.
Usage
Scanner
The scanner works like this:
- 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);
- 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;
- Start and stop search
scanner.Start();
...
scanner.Stop();
- 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] |
- 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 | Versions 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. |
-
.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.