diff --git a/.github/workflows/run_unix.yml b/.github/workflows/run_unix.yml index 5d50a781d..8458078b1 100644 --- a/.github/workflows/run_unix.yml +++ b/.github/workflows/run_unix.yml @@ -234,7 +234,9 @@ jobs: env: LD_LIBRARY_PATH: ${{ github.workspace }}/installed/lib - name: Cyton Daisy Python - run: sudo -H python3 $GITHUB_WORKSPACE/emulator/brainflow_emulator/cyton_linux.py python3 $GITHUB_WORKSPACE/python_package/examples/tests/brainflow_get_data.py --board-id 2 --serial-port + run: sudo -H python3 $GITHUB_WORKSPACE/emulator/brainflow_emulator/cyton_linux.py python3 $GITHUB_WORKSPACE/python_package/examples/tests/brainflow_get_data.py --board-id 2 --serial-port + - name: KnightBoard Python + run: sudo -H python3 $GITHUB_WORKSPACE/emulator/brainflow_emulator/knightboard_linux.py python3 $GITHUB_WORKSPACE/python_package/examples/tests/brainflow_get_data.py --board-id 57 --serial-port - name: Cyton Daisy Python Markers run: sudo -H python3 $GITHUB_WORKSPACE/emulator/brainflow_emulator/cyton_linux.py python3 $GITHUB_WORKSPACE/python_package/examples/tests/markers.py --board-id 2 --serial-port - name: Galea Cpp diff --git a/.github/workflows/run_windows.yml b/.github/workflows/run_windows.yml index 519f1a371..1fa6b4174 100644 --- a/.github/workflows/run_windows.yml +++ b/.github/workflows/run_windows.yml @@ -183,6 +183,9 @@ jobs: - name: FreeEEG32 Python Test run: python %GITHUB_WORKSPACE%\emulator\brainflow_emulator\freeeeg32_windows.py python %GITHUB_WORKSPACE%\python_package\examples\tests\brainflow_get_data.py --board-id 17 --serial-port shell: cmd + - name: KnightBoard Windows Python Test + run: python %GITHUB_WORKSPACE%\emulator\brainflow_emulator\knightboard_windows.py python %GITHUB_WORKSPACE%\python_package\examples\tests\brainflow_get_data.py --board-id 57 --serial-port + shell: cmd # Signal Processing Testing - name: Serialization Rust Test run: | diff --git a/csharp_package/brainflow/brainflow/board_controller_library.cs b/csharp_package/brainflow/brainflow/board_controller_library.cs index ca545b3aa..5e0ce5c14 100644 --- a/csharp_package/brainflow/brainflow/board_controller_library.cs +++ b/csharp_package/brainflow/brainflow/board_controller_library.cs @@ -115,7 +115,8 @@ public enum BoardIds AAVAA_V3_BOARD = 53, EXPLORE_PLUS_8_CHAN_BOARD = 54, EXPLORE_PLUS_32_CHAN_BOARD = 55, - PIEEG_BOARD = 56 + PIEEG_BOARD = 56, + NEUROPAWN_KNIGHT_BOARD = 57 }; diff --git a/docs/SupportedBoards.rst b/docs/SupportedBoards.rst index 3b92875c1..2942b5dab 100644 --- a/docs/SupportedBoards.rst +++ b/docs/SupportedBoards.rst @@ -1305,3 +1305,37 @@ Supported platforms: **Note**: Ensure that you have the necessary permissions to access the serial port on your operating system. For Unix-like systems, you may need to configure permissions for the serial port or run with sudo. **To use this board you need to compile BrainFlow from the source code right on your Raspbery Pi device with flag --build-periphery(build.py) or with -DBUILD_PERIPHERY=ON(CMake) and install desired bindings using local libraries.** + +NeuroPawn +-------- + +Knight Board +~~~~~~~~~~~~~ + +.. image:: https://drive.google.com/file/d/192dUfIXKmOqcTIBr7PYJJ8VUdWCuzeIv/view?usp=sharing + :width: 400px + :height: 225px + +Visit us `here `_ + +To create such board you need to specify the following board ID and fields of BrainFlowInputParams object: + +- :code:`BoardIds.NEUROPAWN_KNIGHT_BOARD` +- :code:`serial_port`, e.g. COM3, /dev/tty.* + +Initialization Example: + +.. code-block:: python + + params = BrainFlowInputParams() + params.serial_port = "COM3" + board = BoardShim(BoardIds.NEUROPAWN_KNIGHT_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 \ No newline at end of file diff --git a/emulator/brainflow_emulator/knightboard_emulator.py b/emulator/brainflow_emulator/knightboard_emulator.py new file mode 100644 index 000000000..becc6cb07 --- /dev/null +++ b/emulator/brainflow_emulator/knightboard_emulator.py @@ -0,0 +1,52 @@ +import logging +import threading +import time +from random import randint + +class Listener(threading.Thread): + + def __init__(self, port, write, read): + # for windows write and read are methods from Serial object, for linux - os.read/write it doesnt work otherwise + threading.Thread.__init__(self) + self.port = port + self.writer_process = None + self.write = write + self.read = read + self.need_stop = False + + def run(self): + self.writer_process = KnightBoardWriter(self.port, 0.005, self.write) + self.writer_process.daemon = True + self.writer_process.start() + time.sleep(10) + self.writer_process.need_data = False + self.writer_process.join() + + +class KnightBoardWriter(threading.Thread): + + def __init__(self, port, delay, write): + threading.Thread.__init__(self) + self.port = port + self.write = write + self.delay = delay + self.package_size = 21 + self.package_num = 0 + self.need_data = True + + def run(self): + while self.need_data: + if self.package_num % 256 == 0: + self.package_num = 0 + + package = list() + package.append(0xA0) + package.append(self.package_num) + for i in range(2, self.package_size - 1): + package.append(randint(0, 255)) + package.append(0xC0) + logging.info(bytes(package)) + self.write(self.port, bytes(package)) + + self.package_num = self.package_num + 1 + time.sleep(self.delay) diff --git a/emulator/brainflow_emulator/knightboard_linux.py b/emulator/brainflow_emulator/knightboard_linux.py new file mode 100644 index 000000000..926a271e2 --- /dev/null +++ b/emulator/brainflow_emulator/knightboard_linux.py @@ -0,0 +1,53 @@ +import logging +import os +import pty +import subprocess +import sys + +from brainflow_emulator.emulate_common import TestFailureError, log_multilines +from brainflow_emulator.knightboard_emulator import Listener + + +def write(port, data): + return os.write(port, data) + + +def read(port, num_bytes): + return os.read(port, num_bytes) + + +def get_ports_pty(): + master, slave = pty.openpty() + s_name = os.ttyname(slave) + return master, slave, s_name + + +def test_serial(cmd_list, master, slave, s_name): + listen_thread = Listener(master, write, read) + listen_thread.daemon = True + listen_thread.start() + + cmd_to_run = cmd_list + [s_name] + logging.info('Running %s' % ' '.join([str(x) for x in cmd_to_run])) + process = subprocess.Popen(cmd_to_run, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = process.communicate() + + log_multilines(logging.info, stdout) + log_multilines(logging.info, stderr) + + if process.returncode != 0: + raise TestFailureError('Test failed with exit code %s' % str(process.returncode), process.returncode) + + return stdout, stderr + + +def main(cmd_list): + if not cmd_list: + raise Exception('No command to execute') + master, slave, s_name = get_ports_pty() + test_serial(cmd_list, master, slave, s_name) + + +if __name__ == '__main__': + logging.basicConfig(level=logging.INFO) + main(sys.argv[1:]) diff --git a/emulator/brainflow_emulator/knightboard_windows.py b/emulator/brainflow_emulator/knightboard_windows.py new file mode 100644 index 000000000..c63cdceb4 --- /dev/null +++ b/emulator/brainflow_emulator/knightboard_windows.py @@ -0,0 +1,100 @@ +import logging +import os +import subprocess +import sys +import time + +import pkg_resources +from brainflow_emulator.emulate_common import TestFailureError, log_multilines +from brainflow_emulator.knightboard_emulator import Listener +from serial import Serial + + +def write(port, data): + return port.write(data) + + +def read(port, num_bytes): + return port.read(num_bytes) + + +def get_isntaller(): + return pkg_resources.resource_filename(__name__, os.path.join('com0com', 'setup_com0com_W7_x64_signed.exe')) + + +def install_com0com(): + this_directory = os.path.abspath(os.path.dirname(__file__)) + directory = os.path.join(this_directory, 'com0com') + if not os.path.exists(directory): + os.makedirs(directory) + cmds = [get_isntaller(), '/NCRC', '/S', '/D=%s' % directory] + logging.info('running %s' % ' '.join(cmds)) + p = subprocess.Popen(cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = p.communicate() + if p.returncode != 0: + logging.error('stdout is %s' % out) + logging.error('stderr is %s' % err) + raise Exception('com0com installation failure') + logging.info('Sleeping a few second, it doesnt work in appveyour without it') + time.sleep(10) + return directory + + +def get_ports_windows(): + directory = install_com0com() + # remove ports from previous run if any + p = subprocess.Popen([os.path.join(directory, 'setupc.exe'), 'remove', '0'], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=directory) + stdout, stderr = p.communicate() + logging.info('remove stdout is %s' % stdout) + logging.info('remove stderr is %s' % stderr) + + m_name = 'COM14' + s_name = 'COM15' + + p = subprocess.Popen( + [os.path.join(directory, 'setupc.exe'), 'install', 'PortName=%s' % m_name, 'PortName=%s' % s_name], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=directory) + stdout, stderr = p.communicate() + logging.info('install stdout is %s' % stdout) + logging.info('install stderr is %s' % stderr) + + if p.returncode != 0: + raise Exception('com0com failure') + logging.info('Sleeping a few second, it doesnt work in appveyour without it') + time.sleep(10) + return m_name, s_name + + +def test_serial(cmd_list, m_name, s_name): + master = Serial('\\\\.\\%s' % m_name, timeout=0) + listen_thread = Listener(master, write, read) + listen_thread.daemon = True + listen_thread.start() + + cmd_to_run = cmd_list + [s_name] + logging.info('Running %s' % ' '.join([str(x) for x in cmd_to_run])) + process = subprocess.Popen(cmd_to_run, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = process.communicate() + + log_multilines(logging.info, stdout) + log_multilines(logging.info, stderr) + + master.close() + if process.returncode != 0: + raise TestFailureError('Test failed with exit code %s' % str(process.returncode), process.returncode) + + return stdout, stderr + + +def main(cmd_list): + if not cmd_list: + raise Exception('No command to execute') + + m_name, s_name = get_ports_windows() + test_serial(cmd_list, m_name, s_name) + + +if __name__ == '__main__': + logging.basicConfig(level=logging.INFO) + main(sys.argv[1:]) diff --git a/java_package/brainflow/src/main/java/brainflow/BoardIds.java b/java_package/brainflow/src/main/java/brainflow/BoardIds.java index 6377c20af..a460d98bc 100644 --- a/java_package/brainflow/src/main/java/brainflow/BoardIds.java +++ b/java_package/brainflow/src/main/java/brainflow/BoardIds.java @@ -65,7 +65,8 @@ public enum BoardIds AAVAA_V3_BOARD(53), EXPLORE_PLUS_8_CHAN_BOARD(54), EXPLORE_PLUS_32_CHAN_BOARD(55), - PIEEG_BOARD(56); + PIEEG_BOARD(56), + NEUROPAWN_KNIGHT_BOARD(57); private final int board_id; private static final Map bi_map = new HashMap (); diff --git a/julia_package/brainflow/src/board_shim.jl b/julia_package/brainflow/src/board_shim.jl index c21b94c83..c455c2fbd 100644 --- a/julia_package/brainflow/src/board_shim.jl +++ b/julia_package/brainflow/src/board_shim.jl @@ -61,6 +61,7 @@ export BrainFlowInputParams EXPLORE_PLUS_8_CHAN_BOARD = 54 EXPLORE_PLUS_32_CHAN_BOARD = 55 PIEEG_BOARD = 56 + NEUROPAWN_KNIGHT_BOARD = 57 end diff --git a/matlab_package/brainflow/BoardIds.m b/matlab_package/brainflow/BoardIds.m index ceb601a86..4c4d69a90 100644 --- a/matlab_package/brainflow/BoardIds.m +++ b/matlab_package/brainflow/BoardIds.m @@ -59,5 +59,6 @@ EXPLORE_PLUS_8_CHAN_BOARD(54) EXPLORE_PLUS_32_CHAN_BOARD(55) PIEEG_BOARD(56) + NEUROPAWN_KNIGHT_BOARD(57) end end \ No newline at end of file diff --git a/nodejs_package/brainflow/brainflow.types.ts b/nodejs_package/brainflow/brainflow.types.ts index c95ec7d1c..b0730b561 100644 --- a/nodejs_package/brainflow/brainflow.types.ts +++ b/nodejs_package/brainflow/brainflow.types.ts @@ -68,7 +68,8 @@ export enum BoardIds { ANT_NEURO_EE_511_BOARD = 51, EXPLORE_PLUS_8_CHAN_BOARD = 54, EXPLORE_PLUS_32_CHAN_BOARD = 55, - PIEEG_BOARD = 56 + PIEEG_BOARD = 56, + NEUROPAWN_KNIGHT_BOARD = 57 } export enum IpProtocolTypes { diff --git a/python_package/brainflow/board_shim.py b/python_package/brainflow/board_shim.py index ced6ec9d5..0b8f5707b 100644 --- a/python_package/brainflow/board_shim.py +++ b/python_package/brainflow/board_shim.py @@ -74,6 +74,7 @@ class BoardIds(enum.IntEnum): EXPLORE_PLUS_8_CHAN_BOARD = 54 #: EXPLORE_PLUS_32_CHAN_BOARD = 55 #: PIEEG_BOARD = 56 #: + NEUROPAWN_KNIGHT_BOARD = 57 #: class IpProtocolTypes(enum.IntEnum): diff --git a/rust_package/brainflow/src/ffi/constants.rs b/rust_package/brainflow/src/ffi/constants.rs index 6e8d43c8d..1d295216d 100644 --- a/rust_package/brainflow/src/ffi/constants.rs +++ b/rust_package/brainflow/src/ffi/constants.rs @@ -94,6 +94,7 @@ pub enum BoardIds { AavaaV3Board = 53, ExplorePlus8ChanBoard = 54, ExplorePlus32ChanBoard = 55, + NeuroPawnKnightBoard = 57 } #[repr(i32)] #[derive(FromPrimitive, ToPrimitive, Debug, Copy, Clone, Hash, PartialEq, Eq)] diff --git a/src/board_controller/board_controller.cpp b/src/board_controller/board_controller.cpp index 623499463..cb55e2e88 100644 --- a/src/board_controller/board_controller.cpp +++ b/src/board_controller/board_controller.cpp @@ -45,6 +45,8 @@ #include "ganglion_wifi.h" #include "gforce_dual.h" #include "gforce_pro.h" +#include "json.hpp" +#include "knight.h" #include "muse.h" #include "muse_bled.h" #include "notion_osc.h" @@ -55,8 +57,6 @@ #include "synthetic_board.h" #include "unicorn_board.h" -#include "json.hpp" - using json = nlohmann::json; @@ -281,6 +281,10 @@ int prepare_session (int board_id, const char *json_brainflow_input_params) case BoardIds::PIEEG_BOARD: board = std::shared_ptr (new PIEEGBoard (board_id, params)); break; + case BoardIds::NEUROPAWN_KNIGHT_BOARD: + board = + std::shared_ptr (new Knight ((int)BoardIds::NEUROPAWN_KNIGHT_BOARD, params)); + break; default: return (int)BrainFlowExitCodes::UNSUPPORTED_BOARD_ERROR; } diff --git a/src/board_controller/brainflow_boards.cpp b/src/board_controller/brainflow_boards.cpp index f22ec341c..884e2efb5 100644 --- a/src/board_controller/brainflow_boards.cpp +++ b/src/board_controller/brainflow_boards.cpp @@ -74,7 +74,8 @@ BrainFlowBoards::BrainFlowBoards() {"53", json::object()}, {"54", json::object()}, {"55", json::object()}, - {"56", json::object()} + {"56", json::object()}, + {"57", json::object()} } }}; @@ -1095,6 +1096,17 @@ BrainFlowBoards::BrainFlowBoards() {"eeg_channels", {1, 2, 3, 4, 5, 6, 7, 8}}, {"eeg_names", "Fp1,Fp2,C3,C4,P7,P8,O1,O2"} }; + brainflow_boards_json["boards"]["57"]["default"] = + { + {"name", "Knight"}, + {"sampling_rate", 125}, + {"timestamp_channel", 11}, + {"marker_channel",12}, + {"package_num_channel", 0}, + {"num_rows", 13}, + {"eeg_channels", {1, 2, 3, 4, 5, 6, 7, 8}}, + {"other_channels", {9, 10}} + }; } BrainFlowBoards boards_struct; diff --git a/src/board_controller/build.cmake b/src/board_controller/build.cmake index aeeedbaa7..9cb994390 100644 --- a/src/board_controller/build.cmake +++ b/src/board_controller/build.cmake @@ -85,6 +85,7 @@ SET (BOARD_CONTROLLER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/ntl/ntl_wifi.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/aavaa/aavaa_v3.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/pieeg/pieeg_board.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/neuropawn/knight.cpp ) include (${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/ant_neuro/build.cmake) @@ -142,6 +143,7 @@ target_include_directories ( ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/ntl/inc ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/aavaa/inc ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/pieeg/inc + ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/neuropawn/inc ) target_compile_definitions(${BOARD_CONTROLLER_NAME} PRIVATE NOMINMAX BRAINFLOW_VERSION=${BRAINFLOW_VERSION}) diff --git a/src/board_controller/neuropawn/inc/knight.h b/src/board_controller/neuropawn/inc/knight.h new file mode 100644 index 000000000..7edd88356 --- /dev/null +++ b/src/board_controller/neuropawn/inc/knight.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include "board.h" +#include "board_controller.h" +#include "serial.h" + +class Knight : public Board +{ + +protected: + volatile bool keep_alive; + bool initialized; + bool is_streaming; + std::thread streaming_thread; + Serial *serial; + + int min_package_size; + + virtual int send_to_board (const char *msg); + virtual int send_to_board (const char *msg, std::string &response); + virtual std::string read_serial_response (); + int open_port (); + int set_port_settings (); + void read_thread (); + +public: + Knight (int board_id, struct BrainFlowInputParams params); + ~Knight (); + + 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; +}; \ No newline at end of file diff --git a/src/board_controller/neuropawn/knight.cpp b/src/board_controller/neuropawn/knight.cpp new file mode 100644 index 000000000..137db4113 --- /dev/null +++ b/src/board_controller/neuropawn/knight.cpp @@ -0,0 +1,317 @@ +#include +#include +#include + +#include "custom_cast.h" +#include "knight.h" +#include "serial.h" +#include "timestamp.h" + +constexpr int Knight::start_byte; +constexpr int Knight::end_byte; + +Knight::Knight (int board_id, struct BrainFlowInputParams params) : Board (board_id, params) +{ + serial = NULL; + is_streaming = false; + keep_alive = false; + initialized = false; +} + +Knight::~Knight () +{ + skip_logs = true; + release_session (); +} + +int Knight::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 Knight::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 Knight::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 Knight::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 Knight::read_thread () +{ + /* + [0] 1 Byte: Start byte + [1] 2 Byte: Sample Number + [2-3] 3-4 Bytes: EXG channel 1 + [4-5] 5-6 Bytes: Data value for EXG channel 2 + [6-7] 7-8 Bytes: Data value for EXG channel 3 + [8-9] 9-10 Bytes: Data value for EXG channel 4 + [10-11] 11-12 Bytes: Data value for EXG channel 5 + [12-13] 13-14 Bytes: Data value for EXG channel 6 + [14-15] 15-16 Bytes: Data value for EXG channel 7 + [16-17] 17-18 Bytes: Data value for EXG channel 8 + [18] 19 Byte: Data LOFF STATP + [19] 20 Byte: Data LOFF STATN + [20] 21 Byte: End byte + */ + + int res; + unsigned char b[20] = {0}; + float eeg_scale = 4 / float ((pow (2, 23) - 1)) / 12 * 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 eeg_channels = board_descr["default"]["eeg_channels"]; + std::vector other_channels = board_descr["default"]["other_channels"]; + + while (keep_alive) + { + // checking the start byte + res = serial->read_from_serial_port (b, 1); + if (res != 1) + { + safe_logger (spdlog::level::debug, "unable to read 1 byte, {}"); + continue; + } + if (b[0] != Knight::start_byte) + { + continue; + } + + int remaining_bytes = 20; + int pos = 0; + while ((remaining_bytes > 0) && (keep_alive)) + { + res = serial->read_from_serial_port (b + pos, remaining_bytes); + remaining_bytes -= res; + pos += res; + } + + if (!keep_alive) + { + break; + } + + if (b[19] != Knight::end_byte) + { + safe_logger (spdlog::level::warn, "Wrong end byte {}", b[19]); + continue; + } + + // package number CHANGE TO 1 if not working + package[board_descr["default"]["package_num_channel"].get ()] = (double)b[0]; + + // exg data retrieval + for (unsigned int i = 0; i < eeg_channels.size (); i++) + { + package[eeg_channels[i]] = + eeg_scale * cast_16bit_to_int32 (b + 1 + 2 * i); // CHANGE TO 2+2*i if not working + } + + // other channel data retrieval + package[other_channels[0]] = (double)b[17]; // LOFF STATP + package[other_channels[1]] = (double)b[18]; // LOFF STATN + + // time stamp channel + package[board_descr["default"]["timestamp_channel"].get ()] = get_timestamp (); + + push_package (package); + } + delete[] package; +} + +int Knight::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 Knight::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); + return (int)BrainFlowExitCodes::SET_PORT_ERROR; + } + res = serial->set_custom_baudrate (115200); + if (res < 0) + { + safe_logger (spdlog::level::err, "Unable to set custom baud rate, res is {}", res); + return (int)BrainFlowExitCodes::SET_PORT_ERROR; + } + safe_logger (spdlog::level::trace, "set port settings"); + return (int)BrainFlowExitCodes::STATUS_OK; +} + +int Knight::config_board (std::string config, std::string &response) +{ + if (!initialized) + { + return (int)BrainFlowExitCodes::BOARD_NOT_READY_ERROR; + } + int res = (int)BrainFlowExitCodes::STATUS_OK; + if (is_streaming) + { + safe_logger (spdlog::level::warn, + "You are changing board params during streaming, it may lead to sync mismatch between " + "data acquisition thread and device"); + res = send_to_board (config.c_str ()); + } + else + { + // read response if streaming is not running + res = send_to_board (config.c_str (), response); + } + + return res; +} + +int Knight::send_to_board (const char *msg) +{ + int length = (int)strlen (msg); + safe_logger (spdlog::level::debug, "sending {} to the board", msg); + int res = serial->send_to_serial_port ((const void *)msg, length); + if (res != length) + { + return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; + } + + return (int)BrainFlowExitCodes::STATUS_OK; +} + +int Knight::send_to_board (const char *msg, std::string &response) +{ + int length = (int)strlen (msg); + safe_logger (spdlog::level::debug, "sending {} to the board", msg); + int res = serial->send_to_serial_port ((const void *)msg, length); + if (res != length) + { + response = ""; + return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; + } + response = read_serial_response (); + + return (int)BrainFlowExitCodes::STATUS_OK; +} + +std::string Knight::read_serial_response () +{ + constexpr int max_tmp_size = 4096; + unsigned char tmp_array[max_tmp_size]; + unsigned char tmp; + int tmp_id = 0; + while (serial->read_from_serial_port (&tmp, 1) == 1) + { + if (tmp_id < max_tmp_size) + { + tmp_array[tmp_id] = tmp; + tmp_id++; + } + else + { + serial->flush_buffer (); + break; + } + } + tmp_id = (tmp_id == max_tmp_size) ? tmp_id - 1 : tmp_id; + tmp_array[tmp_id] = '\0'; + + return std::string ((const char *)tmp_array); +} \ No newline at end of file diff --git a/src/utils/inc/brainflow_constants.h b/src/utils/inc/brainflow_constants.h index 26034af5b..4dfea5ddf 100644 --- a/src/utils/inc/brainflow_constants.h +++ b/src/utils/inc/brainflow_constants.h @@ -88,6 +88,7 @@ enum class BoardIds : int EXPLORE_PLUS_8_CHAN_BOARD = 54, EXPLORE_PLUS_32_CHAN_BOARD = 55, PIEEG_BOARD = 56, + NEUROPAWN_KNIGHT_BOARD = 57, // use it to iterate FIRST = PLAYBACK_FILE_BOARD, LAST = PIEEG_BOARD