From b0a8e19f7e0a48a34b967ed5f22a0cd56617f952 Mon Sep 17 00:00:00 2001 From: NeuroPawn Admin Date: Mon, 16 Feb 2026 18:31:09 -0800 Subject: [PATCH] Imu (#5) Co-authored-by: Kevin Co-authored-by: Kevin-Xue01 <59587075+Kevin-Xue01@users.noreply.github.com> Co-authored-by: aa217 <125326241+aa217@users.noreply.github.com> Co-authored-by: abdallah --- README.md | 101 +++++ docs/SupportedBoards.rst | 7 +- python_package/brainflow/board_shim.py | 1 + src/board_controller/board_controller.cpp | 5 + src/board_controller/brainflow_boards.cpp | 12 + src/board_controller/build.cmake | 1 + src/board_controller/neuropawn/inc/knight.h | 5 + .../neuropawn/inc/knightimu.h | 45 +++ src/board_controller/neuropawn/knight.cpp | 45 ++- src/board_controller/neuropawn/knightimu.cpp | 382 ++++++++++++++++++ src/utils/inc/brainflow_constants.h | 1 + 11 files changed, 602 insertions(+), 3 deletions(-) create mode 100644 src/board_controller/neuropawn/inc/knightimu.h create mode 100644 src/board_controller/neuropawn/knightimu.cpp diff --git a/README.md b/README.md index 9c33e7ab7..3283f91e9 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,107 @@ It provides a uniform SDK to work with biosensors with a primary focus on neuroi * Powerful CI/CD system which runs integrations tests for each commit automatically using BrainFlow's Emulator * Simplified process to add new boards and methods +## NeuroPawn Knight Board Docs + +### [Python] Brainflow Simple Setup +``` +import brainflow as bf +import time + +from brainflow.board_shim import BoardShim, BrainFlowInputParams, BoardIds + +class KnightBoard: + def __init__(self, serial_port: str, num_channels: int): + """Initialize and configure the Knight Board.""" + self.params = BrainFlowInputParams() + self.params.serial_port = serial_port + self.num_channels = num_channels + + # Initialize board + self.board_shim = BoardShim(BoardIds.NEUROPAWN_KNIGHT_BOARD.value, self.params) + self.board_id = self.board_shim.get_board_id() + self.eeg_channels = self.board_shim.get_exg_channels(self.board_id) + self.sampling_rate = self.board_shim.get_sampling_rate(self.board_id) + + def start_stream(self, buffer_size: int = 450000): + """Start the data stream from the board.""" + self.board_shim.prepare_session() + self.board_shim.start_stream(buffer_size) + print("Stream started.") + time.sleep(2) + for x in range(1, self.num_channels + 1): + time.sleep(0.5) + cmd = f"chon_{x}_12" + self.board_shim.config_board(cmd) + print(f"sending {cmd}") + time.sleep(1) + rld = f"rldadd_{x}" + self.board_shim.config_board(rld) + print(f"sending {rld}") + time.sleep(0.5) + + def stop_stream(self): + """Stop the data stream and release resources.""" + self.board_shim.stop_stream() + self.board_shim.release_session() + print("Stream stopped and session released.") + +Knight_board = KnightBoard("COM3", 8) +Knight_board.start_stream() + +while True: + data = Knight_board.board_shim.get_board_data() + # do stuff with data + + if keyboard.is_pressed('q'): + Knight_board.stop_stream() + break +``` +### BrainFlow Configuration Commands + +To configure the NeuroPawn Knight board with BrainFlow, pass the commands as strings into: + + board_shim.config_board() + +#### Command 1: Enable EEG Channel / Set Gain +**Purpose**: Enables a specified channel with a specified gain, starting data acquisition on that channel. If the channel is already enabled, it will remain enabled, but will still update its gain. + + f"chon_{channel}_{gain_value}" + +##### Parameters: + +- channel: The channel number to start the data acquisition. Replace this with the actual number of the channel you want to configure. One-indexed. + +- gain: Specifies the gain value for the channel to be enabled. Allowable gain values are: [1, 2, 3, 4, 6, 8, 12 (recommended)]. The gain value controls the amplification level of the EEG signal on the specified channel. + +#### Command 2: Disable EEG Channel +**Purpose**: Disables a specified channel, stopping data acquisition on that channel. + + f"choff_{channel_number}" + +##### Parameters: + +- channel_number: The channel number to **stop** the data acquisition. This is appended to *'choff'* to construct the configuration command. One-indexed. + +#### Command 3: Toggle on RLD +**Purpose**: Toogle **on** right leg drive for the specified channel. + + f"rldadd_{channel_number}" + +##### Parameters: + +- channel_number: The channel number to toggle **on** the right leg drive. This number is converted to a string and appended to *'rldadd'* to create the configuration command. One-indexed. + + +### Command 4: Toggle off RLD +**Purpose**: Toogle **off** right leg drive for the specified channel. + + f"rldremove_{channel}" + +#### Parameters: + +- channel_number: The channel number to toggle **off** the right leg drive. This number is converted to a string and appended to *'rldremove'* to create the configuration command. One-indexed. + ## Resources * [***BrainFlow Docs, Dev and User guides and other information***](https://brainflow.readthedocs.io) diff --git a/docs/SupportedBoards.rst b/docs/SupportedBoards.rst index 465730371..cc3b96b5d 100644 --- a/docs/SupportedBoards.rst +++ b/docs/SupportedBoards.rst @@ -1425,7 +1425,7 @@ Knight Board To create such board you need to specify the following board ID and fields of BrainFlowInputParams object: -- :code:`BoardIds.NEUROPAWN_KNIGHT_BOARD` +- :code:`BoardIds.NEUROPAWN_KNIGHT_BOARD` or :code:`BoardIds.NEUROPAWN_KNIGHT_BOARD_IMU` - :code:`serial_port`, e.g. COM3, /dev/tty.* Initialization Example: @@ -1434,7 +1434,10 @@ Initialization Example: params = BrainFlowInputParams() params.serial_port = "COM3" - board = BoardShim(BoardIds.NEUROPAWN_KNIGHT_BOARD, params) + params.other_info = '{"gain": 6}' # optional: set gain to allowed values: 1, 2, 3, 4, 6, 8, 12 (default) + + board = BoardShim(BoardIds.NEUROPAWN_KNIGHT_BOARD, params) # standard Knight Board + board = BoardShim(BoardIds.NEUROPAWN_KNIGHT_BOARD_IMU, params) # Knight Board IMU **On Unix-like systems you may need to configure permissions for serial port or run with sudo.** diff --git a/python_package/brainflow/board_shim.py b/python_package/brainflow/board_shim.py index c3c61888e..faf57da7e 100644 --- a/python_package/brainflow/board_shim.py +++ b/python_package/brainflow/board_shim.py @@ -80,6 +80,7 @@ class BoardIds(enum.IntEnum): OB3000_24_CHANNELS_BOARD = 63 #: BIOLISTENER_BOARD = 64 #: IRONBCI_32_BOARD = 65 #: + NEUROPAWN_KNIGHT_BOARD_IMU = 66 #: class IpProtocolTypes(enum.IntEnum): diff --git a/src/board_controller/board_controller.cpp b/src/board_controller/board_controller.cpp index f39d1fa57..d4ab199fb 100644 --- a/src/board_controller/board_controller.cpp +++ b/src/board_controller/board_controller.cpp @@ -45,6 +45,7 @@ #include "gforce_pro.h" #include "json.hpp" #include "knight.h" +#include "knightimu.h" #include "muse.h" #include "muse_bled.h" #include "notion_osc.h" @@ -299,6 +300,10 @@ int prepare_session (int board_id, const char *json_brainflow_input_params) case BoardIds::BIOLISTENER_BOARD: board = std::shared_ptr (new BioListener<8> (board_id, params)); break; + case BoardIds::NEUROPAWN_KNIGHT_BOARD_IMU: + board = std::shared_ptr ( + new KnightIMU ((int)BoardIds::NEUROPAWN_KNIGHT_BOARD_IMU, 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 69e042b5b..5d86d6a59 100644 --- a/src/board_controller/brainflow_boards.cpp +++ b/src/board_controller/brainflow_boards.cpp @@ -84,6 +84,7 @@ BrainFlowBoards::BrainFlowBoards() {"63", json::object()}, {"64", json::object()}, {"65", json::object()}, + {"66", json::object()} } }}; @@ -1147,6 +1148,17 @@ BrainFlowBoards::BrainFlowBoards() {"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}}, {"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}} }; + brainflow_boards_json["boards"]["66"]["default"] = + { + {"name", "KnightIMU"}, + {"sampling_rate", 125}, + {"timestamp_channel", 20}, + {"marker_channel", 21}, + {"package_num_channel", 0}, + {"num_rows", 22}, + {"eeg_channels", {1, 2, 3, 4, 5, 6, 7, 8}}, + {"other_channels", {9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}} + }; } BrainFlowBoards boards_struct; diff --git a/src/board_controller/build.cmake b/src/board_controller/build.cmake index a6d4f15f5..1a5548eb0 100644 --- a/src/board_controller/build.cmake +++ b/src/board_controller/build.cmake @@ -84,6 +84,7 @@ SET (BOARD_CONTROLLER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/pieeg/pieeg_board.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/synchroni/synchroni_board.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/neuropawn/knight.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/neuropawn/knightimu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/board_controller/biolistener/biolistener.cpp ) diff --git a/src/board_controller/neuropawn/inc/knight.h b/src/board_controller/neuropawn/inc/knight.h index 7edd88356..f024655e0 100644 --- a/src/board_controller/neuropawn/inc/knight.h +++ b/src/board_controller/neuropawn/inc/knight.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "board.h" @@ -17,6 +18,7 @@ class Knight : public Board Serial *serial; int min_package_size; + int gain; virtual int send_to_board (const char *msg); virtual int send_to_board (const char *msg, std::string &response); @@ -25,6 +27,9 @@ class Knight : public Board int set_port_settings (); void read_thread (); +private: + static const std::set allowed_gains; + public: Knight (int board_id, struct BrainFlowInputParams params); ~Knight (); diff --git a/src/board_controller/neuropawn/inc/knightimu.h b/src/board_controller/neuropawn/inc/knightimu.h new file mode 100644 index 000000000..4b9fbb429 --- /dev/null +++ b/src/board_controller/neuropawn/inc/knightimu.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +#include "board.h" +#include "board_controller.h" +#include "serial.h" + +class KnightIMU : public Board +{ + +protected: + volatile bool keep_alive; + bool initialized; + bool is_streaming; + std::thread streaming_thread; + Serial *serial; + + int min_package_size; + int gain; + + 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 (); + +private: + static const std::set allowed_gains; + +public: + KnightIMU (int board_id, struct BrainFlowInputParams params); + ~KnightIMU (); + + 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 index 137db4113..6c173a386 100644 --- a/src/board_controller/neuropawn/knight.cpp +++ b/src/board_controller/neuropawn/knight.cpp @@ -3,12 +3,16 @@ #include #include "custom_cast.h" +#include "json.hpp" #include "knight.h" #include "serial.h" #include "timestamp.h" +using json = nlohmann::json; + constexpr int Knight::start_byte; constexpr int Knight::end_byte; +const std::set Knight::allowed_gains = {1, 2, 3, 4, 6, 8, 12}; Knight::Knight (int board_id, struct BrainFlowInputParams params) : Board (board_id, params) { @@ -16,6 +20,45 @@ Knight::Knight (int board_id, struct BrainFlowInputParams params) : Board (board is_streaming = false; keep_alive = false; initialized = false; + gain = 12; // default gain value + + // Parse gain from other_info if provided + if (!params.other_info.empty ()) + { + try + { + json j = json::parse (params.other_info); + if (j.contains ("gain")) + { + int parsed_gain = j["gain"]; + // Validate gain is one of allowed values + if (allowed_gains.count (parsed_gain)) + { + gain = parsed_gain; + safe_logger (spdlog::level::info, "Knight board gain set to {}", gain); + } + else + { + safe_logger (spdlog::level::warn, + "Invalid gain value {} in other_info, using default 12", parsed_gain); + } + } + else + { + safe_logger (spdlog::level::info, "No gain field in other_info, using default 12"); + } + } + catch (json::parse_error &e) + { + safe_logger (spdlog::level::warn, + "Failed to parse JSON from other_info: {}, using default gain 12", e.what ()); + } + catch (json::exception &e) + { + safe_logger (spdlog::level::warn, + "JSON exception while parsing other_info: {}, using default gain 12", e.what ()); + } + } } Knight::~Knight () @@ -136,7 +179,7 @@ void Knight::read_thread () int res; unsigned char b[20] = {0}; - float eeg_scale = 4 / float ((pow (2, 23) - 1)) / 12 * 1000000.; + float eeg_scale = 4 / float ((pow (2, 15) - 1)) / gain * 1000000.; int num_rows = board_descr["default"]["num_rows"]; double *package = new double[num_rows]; for (int i = 0; i < num_rows; i++) diff --git a/src/board_controller/neuropawn/knightimu.cpp b/src/board_controller/neuropawn/knightimu.cpp new file mode 100644 index 000000000..25e64b057 --- /dev/null +++ b/src/board_controller/neuropawn/knightimu.cpp @@ -0,0 +1,382 @@ +#include +#include +#include +#include +#include + +#include "custom_cast.h" +#include "json.hpp" +#include "knightimu.h" +#include "serial.h" +#include "timestamp.h" + +using json = nlohmann::json; + +constexpr int KnightIMU::start_byte; +constexpr int KnightIMU::end_byte; +const std::set KnightIMU::allowed_gains = {1, 2, 3, 4, 6, 8, 12}; + +KnightIMU::KnightIMU (int board_id, struct BrainFlowInputParams params) : Board (board_id, params) +{ + serial = NULL; + is_streaming = false; + keep_alive = false; + initialized = false; + gain = 12; // default gain value + + // Parse gain from other_info if provided + if (!params.other_info.empty ()) + { + try + { + json j = json::parse (params.other_info); + if (j.contains ("gain")) + { + int parsed_gain = j["gain"]; + // Validate gain is one of allowed values + if (allowed_gains.count (parsed_gain)) + { + gain = parsed_gain; + safe_logger (spdlog::level::info, "KnightIMU board gain set to {}", gain); + } + else + { + safe_logger (spdlog::level::warn, + "Invalid gain value {} in other_info, using default 12", parsed_gain); + } + } + else + { + safe_logger (spdlog::level::info, "No gain field in other_info, using default 12"); + } + } + catch (json::parse_error &e) + { + safe_logger (spdlog::level::warn, + "Failed to parse JSON from other_info: {}, using default gain 12", e.what ()); + } + catch (json::exception &e) + { + safe_logger (spdlog::level::warn, + "JSON exception while parsing other_info: {}, using default gain 12", e.what ()); + } + } +} + +KnightIMU::~KnightIMU () +{ + skip_logs = true; + release_session (); +} + +int KnightIMU::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 KnightIMU::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 KnightIMU::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 KnightIMU::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 KnightIMU::read_thread () +{ + /* + Frame format (Arduino): + [0] 1 Byte : START (0xA0) + [1] 1 Byte : counter + [2..17]16 Bytes: 8x EXG int16 (little-endian) + [18] 1 Byte : LOFF STATP + [19] 1 Byte : LOFF STATN + [20..55]36 Bytes: 9x float32 IMU, little-endian: ax,ay,az,gx,gy,gz,mx,my,mz + [56] 1 Byte : END (0xC0) + */ + + int res; + constexpr int exg_channels_count = 8; + constexpr int imu_channels_count = 9; + constexpr int loff_bytes = 2; + constexpr int frame_payload_size = 1 /*counter*/ + (exg_channels_count * 2) + loff_bytes + + (imu_channels_count * 4) + 1 /*end*/; + + unsigned char b[frame_payload_size] = {0}; + + float eeg_scale = 4 / float ((pow (2, 15) - 1)) / 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; + } + + 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 + unsigned char start = 0; + res = serial->read_from_serial_port (&start, 1); + if (res != 1) + { + safe_logger (spdlog::level::debug, "unable to read 1 byte, {}"); + continue; + } + if (start != KnightIMU::start_byte) + { + continue; + } + + int remaining_bytes = frame_payload_size; + int pos = 0; + while ((remaining_bytes > 0) && (keep_alive)) + { + res = serial->read_from_serial_port (b + pos, remaining_bytes); + if (res > 0) + { + remaining_bytes -= res; + pos += res; + } + } + + if (!keep_alive) + { + break; + } + + if (b[frame_payload_size - 1] != KnightIMU::end_byte) + { + safe_logger (spdlog::level::warn, "Wrong end byte {}", b[frame_payload_size - 1]); + continue; + } + + // package number / counter + package[board_descr["default"]["package_num_channel"].get ()] = (double)b[0]; + + // exg data retrieval + const int exg_offset = 1; + for (unsigned int i = 0; i < eeg_channels.size () && i < exg_channels_count; i++) + { + package[eeg_channels[i]] = eeg_scale * cast_16bit_to_int32 (b + exg_offset + 2 * i); + } + + // other channel data retrieval (keep old behavior) + const int loff_offset = exg_offset + (exg_channels_count * 2); + package[other_channels[0]] = (double)b[loff_offset]; // LOFF STATP + package[other_channels[1]] = (double)b[loff_offset + 1]; // LOFF STATN + + // IMU float32, little-endian + const int imu_offset = loff_offset + loff_bytes; + for (int i = 0; i < imu_channels_count && (2 + i) < (int)other_channels.size (); i++) + { + uint32_t u = (uint32_t)b[imu_offset + 4 * i] | + ((uint32_t)b[imu_offset + 4 * i + 1] << 8) | + ((uint32_t)b[imu_offset + 4 * i + 2] << 16) | + ((uint32_t)b[imu_offset + 4 * i + 3] << 24); + float f = 0.0f; + static_assert (sizeof (float) == 4, "float must be 4 bytes"); + std::memcpy (&f, &u, sizeof (f)); + package[other_channels[2 + i]] = (double)f; + } + + // time stamp channel + package[board_descr["default"]["timestamp_channel"].get ()] = get_timestamp (); + + push_package (package); + } + delete[] package; +} + +int KnightIMU::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 KnightIMU::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 KnightIMU::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 KnightIMU::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 KnightIMU::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 KnightIMU::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 f120e42b3..f6883168c 100644 --- a/src/utils/inc/brainflow_constants.h +++ b/src/utils/inc/brainflow_constants.h @@ -94,6 +94,7 @@ enum class BoardIds : int OB3000_24_CHANNELS_BOARD = 63, BIOLISTENER_BOARD = 64, IRONBCI_32_BOARD = 65, + NEUROPAWN_KNIGHT_BOARD_IMU = 66, // use it to iterate FIRST = PLAYBACK_FILE_BOARD, LAST = IRONBCI_32_BOARD