Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Freeeeg128 #661

Closed
wants to merge 13 commits into from
30 changes: 30 additions & 0 deletions docs/SupportedBoards.rst
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,36 @@ Supported platforms:
- MacOS
- Devices like Raspberry Pi

FreeEEG128
----------

FreeEEG128
~~~~~~~~~~




To create such board you need to specify the following board ID and fields of BrainFlowInputParams object:

- :code:`BoardIds.FREEEEG128_BOARD`
- :code:`serial_port`, e.g. COM6

Initialization Example:

.. code-block:: python
params = BrainFlowInputParams()
params.serial_port = "COM6"
board = BoardShim(BoardIds.FREEEEG128_BOARD, params)
**On Unix-like systems you may need to configure permissions for serial port or run with sudo.**

Supported platforms:

- Windows
- Linux
- MacOS
- Devices like Raspberry Pi


Muse
------

Expand Down
4 changes: 4 additions & 0 deletions src/board_controller/board_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
#include "streaming_board.h"
#include "synthetic_board.h"
#include "unicorn_board.h"
#include "freeeeg128.h"

#include "json.hpp"

Expand Down Expand Up @@ -264,6 +265,9 @@ int prepare_session (int board_id, const char *json_brainflow_input_params)
case BoardIds::NTL_WIFI_BOARD:
board = std::shared_ptr<Board> (new NtlWifi (params));
break;
case BoardIds::FREEEEG128_BOARD:
board = std::shared_ptr<Board> (new FreeEEG128 (params));
break;
default:
return (int)BrainFlowExitCodes::UNSUPPORTED_BOARD_ERROR;
}
Expand Down
17 changes: 15 additions & 2 deletions src/board_controller/brainflow_boards.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ BrainFlowBoards::BrainFlowBoards()
{"48", json::object()},
{"49", json::object()},
{"50", json::object()},
{"51", json::object()}
{"51", json::object()},
{"52", json::object()}
}
}};

Expand Down Expand Up @@ -977,6 +978,18 @@ BrainFlowBoards::BrainFlowBoards()
{"emg_channels", {25, 26, 27, 28}},
{"other_channels", {29}}
};
brainflow_boards_json["boards"]["52"]["default"] =
{
{"name", "FreeEEG128"},
{"sampling_rate", 256},
{"timestamp_channel", 129},
{"marker_channel", 130},
{"package_num_channel", 0},
{"num_rows", 131},
{"eeg_channels", {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128}},
{"emg_channels", {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128}},
{"ecg_channels", {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128}}
};
}

BrainFlowBoards boards_struct;
BrainFlowBoards boards_struct;
2 changes: 2 additions & 0 deletions src/board_controller/build.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ SET (BOARD_CONTROLLER_SRC
${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/oymotion/gforce_pro.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/oymotion/gforce_dual.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/freeeeg32/freeeeg32.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/freeeeg128/freeeeg128.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/neuromd/brainbit_bled.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/muse/muse_bled.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/ant_neuro/ant_neuro.cpp
Expand Down Expand Up @@ -130,6 +131,7 @@ target_include_directories (
${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/neurosity/inc
${CMAKE_CURRENT_SOURCE_DIR}/third_party/gForceSDKCXX/src/inc
${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/freeeeg32/inc
${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/freeeeg128/inc
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ant_neuro
${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/ant_neuro/inc
${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/enophone/inc
Expand Down
232 changes: 232 additions & 0 deletions src/board_controller/freeeeg128/freeeeg128.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
#include <math.h>
#include <string.h>
#include <vector>

#include "custom_cast.h"
#include "freeeeg128.h"
#include "serial.h"
#include "timestamp.h"


constexpr int FreeEEG128::start_byte;
constexpr int FreeEEG128::end_byte;
constexpr double FreeEEG128::ads_gain;
constexpr double FreeEEG128::ads_vref;


FreeEEG128::FreeEEG128 (struct BrainFlowInputParams params)
: Board ((int)BoardIds::FREEEEG128_BOARD, params)
{
serial = NULL;
is_streaming = false;
keep_alive = false;
initialized = false;
}

FreeEEG128::~FreeEEG128 ()
{
skip_logs = true;
release_session ();
}

int FreeEEG128::prepare_session ()
{
if (initialized)
{
safe_logger (spdlog::level::info, "Session already prepared");
return (int)BrainFlowExitCodes::STATUS_OK;
}
if (params.serial_port.empty ())
{
safe_logger (spdlog::level::err, "serial port is empty");
return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR;
}
serial = Serial::create (params.serial_port.c_str (), this);
int port_open = open_port ();
if (port_open != (int)BrainFlowExitCodes::STATUS_OK)
{
delete serial;
serial = NULL;
return port_open;
}

int set_settings = set_port_settings ();
if (set_settings != (int)BrainFlowExitCodes::STATUS_OK)
{
delete serial;
serial = NULL;
return set_settings;
}

initialized = true;
return (int)BrainFlowExitCodes::STATUS_OK;
}

int FreeEEG128::start_stream (int buffer_size, const char *streamer_params)
{
if (is_streaming)
{
safe_logger (spdlog::level::err, "Streaming thread already running");
return (int)BrainFlowExitCodes::STREAM_ALREADY_RUN_ERROR;
}
int res = prepare_for_acquisition (buffer_size, streamer_params);
if (res != (int)BrainFlowExitCodes::STATUS_OK)
{
return res;
}

serial->flush_buffer ();

keep_alive = true;
streaming_thread = std::thread ([this] { this->read_thread (); });
is_streaming = true;
return (int)BrainFlowExitCodes::STATUS_OK;
}

int FreeEEG128::stop_stream ()
{
if (is_streaming)
{
keep_alive = false;
is_streaming = false;
if (streaming_thread.joinable ())
{
streaming_thread.join ();
}
return (int)BrainFlowExitCodes::STATUS_OK;
}
else
{
return (int)BrainFlowExitCodes::STREAM_THREAD_IS_NOT_RUNNING;
}
}

int FreeEEG128::release_session ()
{
if (initialized)
{
if (is_streaming)
{
stop_stream ();
}
free_packages ();
initialized = false;
}
if (serial)
{
serial->close_serial_port ();
delete serial;
serial = NULL;
}
return (int)BrainFlowExitCodes::STATUS_OK;
}

void FreeEEG128::read_thread ()
{
int res;
constexpr int max_size = 200; // random value bigger than package size which is unknown
unsigned char b[max_size] = {0};
// dont know exact package size and it can be changed with new firmware versions, its >=
// min_package_size and we can check start\stop bytes
constexpr int min_package_size = 1 + 128 * 3;
float eeg_scale =
FreeEEG128::ads_vref / float ((pow (2, 23) - 1)) / FreeEEG128::ads_gain * 1000000.;
int num_rows = board_descr["default"]["num_rows"];
double *package = new double[num_rows];
for (int i = 0; i < num_rows; i++)
{
package[i] = 0.0;
}
bool first_package_received = false;

std::vector<int> eeg_channels = board_descr["default"]["eeg_channels"];

while (keep_alive)
{
int pos = 0;
bool complete_package = false;
while ((keep_alive) && (pos < max_size - 2))
{
res = serial->read_from_serial_port (b + pos, 1);
int prev_id = (pos <= 0) ? 0 : pos - 1;
if ((b[pos] == FreeEEG128::start_byte) && (b[prev_id] == FreeEEG128::end_byte) &&
(pos >= min_package_size))
{
complete_package = true;
break;
}
pos += res;
}
if (complete_package)
{
// handle the case that we start reading in the middle of data stream
if (!first_package_received)
{
first_package_received = true;
continue;
}
package[board_descr["default"]["package_num_channel"].get<int> ()] = (double)b[0];
for (unsigned int i = 0; i < eeg_channels.size (); i++)
{
package[eeg_channels[i]] = (double)eeg_scale * cast_24bit_to_int32 (b + 1 + 3 * i);
}
package[board_descr["default"]["timestamp_channel"].get<int> ()] = get_timestamp ();
push_package (package);
}
else
{
safe_logger (
spdlog::level::trace, "stopped with pos: {}, keep_alive: {}", pos, keep_alive);
}
}
delete[] package;
}

int FreeEEG128::open_port ()
{
if (serial->is_port_open ())
{
safe_logger (spdlog::level::err, "port {} already open", serial->get_port_name ());
return (int)BrainFlowExitCodes::PORT_ALREADY_OPEN_ERROR;
}

safe_logger (spdlog::level::info, "openning port {}", serial->get_port_name ());
int res = serial->open_serial_port ();
if (res < 0)
{
return (int)BrainFlowExitCodes::UNABLE_TO_OPEN_PORT_ERROR;
}
safe_logger (spdlog::level::trace, "port {} is open", serial->get_port_name ());
return (int)BrainFlowExitCodes::STATUS_OK;
}

int FreeEEG128::set_port_settings ()
{
int res = serial->set_serial_port_settings (1000, false);
if (res < 0)
{
safe_logger (spdlog::level::err, "Unable to set port settings, res is {}", res);
#ifndef _WIN32
return (int)BrainFlowExitCodes::SET_PORT_ERROR;
#endif
}
res = serial->set_custom_baudrate (921600);
if (res < 0)
{
safe_logger (spdlog::level::err, "Unable to set custom baud rate, res is {}", res);
#ifndef _WIN32
// Setting the baudrate may return an error on Windows for some serial drivers.
// We do not throw an exception, because it will still work with USB.
// Optical connection will fail, though.
return (int)BrainFlowExitCodes::SET_PORT_ERROR;
#endif
}
safe_logger (spdlog::level::trace, "set port settings");
return (int)BrainFlowExitCodes::STATUS_OK;
}

int FreeEEG128::config_board (std::string config, std::string &response)
{
safe_logger (spdlog::level::err, "FreeEEG128 doesn't support board configuration.");
return (int)BrainFlowExitCodes::UNSUPPORTED_BOARD_ERROR;
}
38 changes: 38 additions & 0 deletions src/board_controller/freeeeg128/inc/freeeeg128.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#pragma once

#include <thread>

#include "board.h"
#include "board_controller.h"
#include "serial.h"


class FreeEEG128 : public Board
{

protected:
volatile bool keep_alive;
bool initialized;
bool is_streaming;
std::thread streaming_thread;
Serial *serial;

int open_port ();
int set_port_settings ();
void read_thread ();

public:
FreeEEG128 (struct BrainFlowInputParams params);
~FreeEEG128 ();

int prepare_session ();
int start_stream (int buffer_size, const char *streamer_params);
int stop_stream ();
int release_session ();
int config_board (std::string config, std::string &response);

static constexpr int start_byte = 0xA0;
static constexpr int end_byte = 0xC0;
static constexpr double ads_gain = 8.0;
static constexpr double ads_vref = 2.5;
};
3 changes: 2 additions & 1 deletion src/utils/inc/brainflow_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ enum class BoardIds : int
GALEA_SERIAL_BOARD_V4 = 49,
NTL_WIFI_BOARD = 50,
ANT_NEURO_EE_511_BOARD = 51,
FREEEEG128_BOARD = 52,
// use it to iterate
FIRST = PLAYBACK_FILE_BOARD,
LAST = ANT_NEURO_EE_511_BOARD
Expand Down Expand Up @@ -240,4 +241,4 @@ enum class WaveletTypes : int
// to iterate and check sizes
FIRST_WAVELET = HAAR,
LAST_WAVELET = SYM10
};
};
Loading