diff --git a/csharp_package/brainflow/brainflow/board_controller_library.cs b/csharp_package/brainflow/brainflow/board_controller_library.cs index b2cf103cd..64be6691b 100644 --- a/csharp_package/brainflow/brainflow/board_controller_library.cs +++ b/csharp_package/brainflow/brainflow/board_controller_library.cs @@ -104,7 +104,9 @@ public enum BoardIds BRAINALIVE_BOARD = 40, MUSE_2016_BOARD = 41, MUSE_2016_BLED_BOARD = 42, - PIEEG_BOARD = 43 + PIEEG_BOARD = 43, + EXPLORE_4_CHAN_BOARD = 44, + EXPLORE_8_CHAN_BOARD = 45 }; diff --git a/docs/SupportedBoards.rst b/docs/SupportedBoards.rst index c752b4428..090caea45 100644 --- a/docs/SupportedBoards.rst +++ b/docs/SupportedBoards.rst @@ -32,6 +32,7 @@ Supported platforms: - Windows >= 8.1 - Linux - MacOS +- Devices like Raspberry Pi By default it generates new timestamps and stops at the end of the file. You can override it using commands: @@ -79,6 +80,8 @@ Supported platforms: - Windows >= 8.1 - Linux - MacOS +- Devices like Raspberry Pi +- Android In methods like: @@ -110,6 +113,7 @@ Supported platforms: - Windows >= 8.1 - Linux - MacOS +- Devices like Raspberry Pi - Android OpenBCI @@ -144,6 +148,7 @@ Supported platforms: - Windows >= 8.1 - Linux - MacOS +- Devices like Raspberry Pi **On MacOS there are two serial ports for each device: /dev/tty..... and /dev/cu..... You HAVE to specify /dev/cu.....** @@ -179,6 +184,7 @@ Supported platforms: - Windows >= 8.1 - Linux - MacOS +- Devices like Raspberry Pi **On MacOS there are two serial ports for each device: /dev/tty..... and /dev/cu..... You HAVE to specify /dev/cu.....** @@ -201,6 +207,7 @@ Supported platforms: - Windows >= 8.1 - Linux - MacOS +- Devices like Raspberry Pi **On MacOS there are two serial ports for each device: /dev/tty..... and /dev/cu..... You HAVE to specify /dev/cu.....** @@ -254,6 +261,7 @@ Supported platforms: - Windows >= 8.1 - Linux - MacOS +- Devices like Raspberry Pi - Android CytonDaisy with WIFI Shield @@ -279,6 +287,7 @@ Supported platforms: - Windows >= 8.1 - Linux - MacOS +- Devices like Raspberry Pi - Android NeuroMD @@ -430,6 +439,7 @@ Supported platforms: - Windows - Linux - MacOS +- Devices like Raspberry Pi *Note: On Windows you may need to disable firewall to allow broadcast messages.* @@ -455,6 +465,7 @@ Supported platforms: - Windows - Linux - MacOS +- Devices like Raspberry Pi *Note: On Windows you may need to disable firewall to allow broadcast messages.* @@ -479,6 +490,7 @@ Supported platforms: - Windows - Linux - MacOS +- Devices like Raspberry Pi *Note: On Windows you may need to disable firewall to allow broadcast messages.* @@ -554,6 +566,7 @@ Supported platforms: - Windows - Linux - MacOS +- Devices like Raspberry Pi Muse ------ @@ -822,6 +835,10 @@ Supported platforms: - Windows - Linux +Available commands: + +- Set sampling rate: :code:`board.config_board("sampling_rate:500")`, for available values check docs from Ant Neuro. + For more information about Ant Neuro boards please refer to their User Manual. @@ -852,6 +869,7 @@ Supported platforms: - Windows - Linux - MacOS +- Devices like Raspberry Pi Steps to find MAC address: @@ -898,3 +916,95 @@ Supported platforms: - MacOS 10.15+ - Linux, compilation from source code probably will be needed - Devices like Raspberry Pi + + +Mentalab +--------- + +.. csv-table:: Required inputs + :header: "Board", "Board Id", "serial_port", "mac_address", "ip_address", "ip_port", "ip_protocol", "other_info", "timeout", "serial_number", "file", "preset", "master_board" + + "Explore4Channels", "BoardIds.EXPLORE_4_CHAN_BOARD (44)", "-", "Optional: MAC adress", "-", "-", "-", "-", "-", "-", "-", "-", "-" + "Explore8Channels", "BoardIds.EXPLORE_8_CHAN_BOARD (45)", "-", "Optional: MAC adress", "-", "-", "-", "-", "-", "-", "-", "-", "-" + +Explore 4 Channels Board +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. image:: https://live.staticflickr.com/65535/52349031632_51bc8ea56c.jpg" + :width: 500px + :height: 334px + +`Mentalab website `_ + +To choose this board in BoardShim constructor please specify: + +- board_id: 44 +- Optional: mac address field of BrainFlowInputParams structure + +Supported platforms: + +- Windows +- Linux +- MacOS +- Devices like Raspberry Pi + +**On Linux in order to compile and use it you may need to install :code:`libbluetooth-dev` for Debian like systems and :code:`bluez-libs-devel` for Fedora like.** + +Available :ref:`presets-label`: + +- *BrainFlowPresets.DEFAULT_PRESET* contains EEG data +- *BrainFlowPresets.AUXILIARY_PRESET* contains Gyro and Accel data +- *BrainFlowPresets.ANCILLARY_PRESET* contains battery and temperature data + +Steps to find MAC address: + +- On Windows: open device manager, navigate to explore device, click properties and select Bluetooth Address +- On Linux: install bluez-utils and run :code:`bluetoothctl paired-devices` +- On MacOS: run :code:`system_profiler SPBluetoothDataType` + +Steps to connect: + +- Enable device and Pair it with your laptop using bluetooth settings +- Ensure that blue LED is blinking before calling :code:`board.prepare_session()` +- If you see green LED probably you need to reboot a devce + +Explore 8 Channels Board +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. image:: https://live.staticflickr.com/65535/52349031632_51bc8ea56c.jpg" + :width: 500px + :height: 334px + +`Mentalab website `_ + +To choose this board in BoardShim constructor please specify: + +- board_id: 44 +- Optional: mac address field of BrainFlowInputParams structure + +Supported platforms: + +- Windows +- Linux +- MacOS +- Devices like Raspberry Pi + +**On Linux in order to compile and use it you may need to install :code:`libbluetooth-dev` for Debian like systems and :code:`bluez-libs-devel` for Fedora like.** + +Available :ref:`presets-label`: + +- *BrainFlowPresets.DEFAULT_PRESET* contains EEG data +- *BrainFlowPresets.AUXILIARY_PRESET* contains Gyro and Accel data +- *BrainFlowPresets.ANCILLARY_PRESET* contains battery and temperature data + +Steps to find MAC address: + +- On Windows: open device manager, navigate to explore device, click properties and select Bluetooth Address +- On Linux: install bluez-utils and run :code:`bluetoothctl paired-devices` +- On MacOS: run :code:`system_profiler SPBluetoothDataType` + +Steps to connect: + +- Enable device and Pair it with your laptop using bluetooth settings +- Ensure that blue LED is blinking before calling :code:`board.prepare_session()` +- If you see green LED probably you need to reboot a devce diff --git a/java_package/brainflow/src/main/java/brainflow/BoardIds.java b/java_package/brainflow/src/main/java/brainflow/BoardIds.java index 0e7ecad96..a523e9050 100644 --- a/java_package/brainflow/src/main/java/brainflow/BoardIds.java +++ b/java_package/brainflow/src/main/java/brainflow/BoardIds.java @@ -54,7 +54,9 @@ public enum BoardIds BRAINALIVE_BOARD (40), MUSE_2016_BOARD (41), MUSE_2016_BLED_BOARD (42), - PIEEG_BOARD (43); + PIEEG_BOARD (43), + EXPLORE_4_CHAN_BOARD (44), + EXPLORE_8_CHAN_BOARD (45); 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 49bcb264f..1d7464685 100644 --- a/julia_package/brainflow/src/board_shim.jl +++ b/julia_package/brainflow/src/board_shim.jl @@ -50,6 +50,8 @@ export BrainFlowInputParams MUSE_2016_BOARD = 41 MUSE_2016_BLED_BOARD = 42 PIEEG_BOARD = 43 + EXPLORE_4_CHAN_BOARD = 44 + EXPLORE_8_CHAN_BOARD = 45 end diff --git a/matlab_package/brainflow/BoardIds.m b/matlab_package/brainflow/BoardIds.m index 0e010ec68..eec6e9de1 100644 --- a/matlab_package/brainflow/BoardIds.m +++ b/matlab_package/brainflow/BoardIds.m @@ -48,5 +48,7 @@ MUSE_2016_BOARD (41) MUSE_2016_BLED_BOARD (42) PIEEG_BOARD (43) + EXPLORE_4_CHAN_BOARD (44) + EXPLORE_8_CHAN_BOARD (45) end end \ No newline at end of file diff --git a/python_package/brainflow/board_shim.py b/python_package/brainflow/board_shim.py index 1a223dd0c..1a8f537bb 100644 --- a/python_package/brainflow/board_shim.py +++ b/python_package/brainflow/board_shim.py @@ -64,6 +64,8 @@ class BoardIds(enum.IntEnum): MUSE_2016_BOARD = 41 #: MUSE_2016_BLED_BOARD = 42 #: PIEEG_BOARD = 43 #: + EXPLORE_4_CHAN_BOARD = 44 #: + EXPLORE_8_CHAN_BOARD = 45 #: class IpProtocolTypes(enum.IntEnum): diff --git a/python_package/examples/tests/explore.py b/python_package/examples/tests/explore.py new file mode 100644 index 000000000..8d11d125d --- /dev/null +++ b/python_package/examples/tests/explore.py @@ -0,0 +1,38 @@ +import argparse +import time + +from brainflow.board_shim import BoardShim, BrainFlowInputParams, BrainFlowPresets, BoardIds +from brainflow.data_filter import DataFilter + + +def main(): + BoardShim.enable_dev_board_logger() + parser = argparse.ArgumentParser() + parser.add_argument('--mac-address', type=str, help='mac address', required=False, default='') + args = parser.parse_args() + + params = BrainFlowInputParams() + params.mac_address = args.mac_address + board = BoardShim(BoardIds.EXPLORE_4_CHAN_BOARD, params) + board.prepare_session() + board.start_stream() + # important: for explore device config board has to be after start stream + # board.config_board('sampling_rate:500') + # board.config_board('test_signal:1') # 1 is a bitmask represented as int not channel num + time.sleep(20) + data_eeg = board.get_board_data(preset=BrainFlowPresets.DEFAULT_PRESET) + data_orn = board.get_board_data(preset=BrainFlowPresets.AUXILIARY_PRESET) + data_env = board.get_board_data(preset=BrainFlowPresets.ANCILLARY_PRESET) + board.stop_stream() + board.release_session() + + print(data_eeg) + print(data_orn) + print(data_env) + DataFilter.write_file(data_eeg, 'data_eeg.csv', 'w') + DataFilter.write_file(data_orn, 'data_orn.csv', 'w') + DataFilter.write_file(data_env, 'data_env.csv', 'w') + + +if __name__ == "__main__": + main() diff --git a/rust_package/brainflow/src/ffi/constants.rs b/rust_package/brainflow/src/ffi/constants.rs index 6aa2c3d94..d771f50f4 100644 --- a/rust_package/brainflow/src/ffi/constants.rs +++ b/rust_package/brainflow/src/ffi/constants.rs @@ -32,7 +32,7 @@ impl BoardIds { pub const FIRST: BoardIds = BoardIds::PlaybackFileBoard; } impl BoardIds { - pub const LAST: BoardIds = BoardIds::PieegBoard; + pub const LAST: BoardIds = BoardIds::Explore8ChanBoard; } #[repr(i32)] #[derive(FromPrimitive, ToPrimitive, Debug, Copy, Clone, Hash, PartialEq, Eq)] @@ -84,6 +84,8 @@ pub enum BoardIds { Muse2016Board = 41, Muse2016BledBoard = 42, PieegBoard = 43, + Explore4ChanBoard = 44, + Explore8ChanBoard = 45, } #[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 61ce49d5d..ea763ac6d 100644 --- a/src/board_controller/board_controller.cpp +++ b/src/board_controller/board_controller.cpp @@ -32,6 +32,7 @@ #include "cyton_daisy_wifi.h" #include "cyton_wifi.h" #include "enophone.h" +#include "explore.h" #include "freeeeg32.h" #include "galea.h" #include "galea_serial.h" @@ -241,6 +242,12 @@ int prepare_session (int board_id, const char *json_brainflow_input_params) case BoardIds::PIEEG_BOARD: board = std::shared_ptr (new PiEEG (params)); break; + case BoardIds::EXPLORE_4_CHAN_BOARD: + board = std::shared_ptr (new Explore (board_id, params)); + break; + case BoardIds::EXPLORE_8_CHAN_BOARD: + board = std::shared_ptr (new Explore (board_id, 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 1f552d9ac..578128e5f 100644 --- a/src/board_controller/brainflow_boards.cpp +++ b/src/board_controller/brainflow_boards.cpp @@ -63,7 +63,9 @@ BrainFlowBoards::BrainFlowBoards() {"40", json::object()}, {"41", json::object()}, {"42", json::object()}, - {"43", json::object()} + {"43", json::object()}, + {"44", json::object()}, + {"45", json::object()} } }}; @@ -768,6 +770,80 @@ BrainFlowBoards::BrainFlowBoards() {"emg_channels", {1, 2, 3, 4, 5, 6, 7, 8}}, {"ecg_channels", {1, 2, 3, 4, 5, 6, 7, 8}} }; + brainflow_boards_json["boards"]["44"]["default"] = + { + {"name", "Explore4Channels"}, + {"sampling_rate", 250}, + {"timestamp_channel", 6}, + {"marker_channel", 7}, + {"package_num_channel", 0}, + {"num_rows", 8}, + {"eeg_channels", {1, 2, 3, 4}}, + {"emg_channels", {1, 2, 3, 4}}, + {"ecg_channels", {1, 2, 3, 4}}, + {"other_channels", {5}} // data status + }; + brainflow_boards_json["boards"]["44"]["auxiliary"] = + { + {"name", "Explore4Channels"}, + {"sampling_rate", 20}, + {"timestamp_channel", 10}, + {"marker_channel", 11}, + {"package_num_channel", 0}, + {"num_rows", 12}, + {"accel_channels", {1, 2, 3}}, + {"gyro_channels", {4, 5, 6}}, + {"other_channels", {7, 8, 9}} + }; + brainflow_boards_json["boards"]["44"]["ancillary"] = + { + {"name", "Explore4Channels"}, + {"sampling_rate", 1}, + {"timestamp_channel", 4}, + {"marker_channel", 5}, + {"package_num_channel", 0}, + {"num_rows", 6}, + {"temperature_channels", {1}}, + {"battery_channel", 2}, + {"other_channels", {3}} + }; + brainflow_boards_json["boards"]["45"]["default"] = + { + {"name", "Explore8Channels"}, + {"sampling_rate", 250}, + {"timestamp_channel", 10}, + {"marker_channel", 11}, + {"package_num_channel", 0}, + {"num_rows", 12}, + {"eeg_channels", {1, 2, 3, 4, 5, 6, 7, 8}}, + {"emg_channels", {1, 2, 3, 4, 5, 6, 7, 8}}, + {"ecg_channels", {1, 2, 3, 4, 5, 6, 7, 8}}, + {"other_channels", {9}} // data status + }; + brainflow_boards_json["boards"]["45"]["auxiliary"] = + { + {"name", "Explore8Channels"}, + {"sampling_rate", 20}, + {"timestamp_channel", 10}, + {"marker_channel", 11}, + {"package_num_channel", 0}, + {"num_rows", 12}, + {"accel_channels", {1, 2, 3}}, + {"gyro_channels", {4, 5, 6}}, + {"other_channels", {7, 8, 9}} + }; + brainflow_boards_json["boards"]["45"]["ancillary"] = + { + {"name", "Explore8Channels"}, + {"sampling_rate", 1}, + {"timestamp_channel", 4}, + {"marker_channel", 5}, + {"package_num_channel", 0}, + {"num_rows", 6}, + {"temperature_channels", {1}}, + {"battery_channel", 2}, + {"other_channels", {3}} + }; } BrainFlowBoards boards_struct; \ No newline at end of file diff --git a/src/board_controller/bt_lib_board.cpp b/src/board_controller/bt_lib_board.cpp index ec6afac81..93e117193 100644 --- a/src/board_controller/bt_lib_board.cpp +++ b/src/board_controller/bt_lib_board.cpp @@ -68,9 +68,8 @@ int BTLibBoard::prepare_session () if (params.ip_port <= 0) { params.ip_port = 1; - safe_logger (spdlog::level::warn, "Port for Bluetooth is not provided, default is: {}", - params.ip_port); } + safe_logger (spdlog::level::info, "Use bluetooth port: {}", params.ip_port); if ((params.mac_address.empty ()) && (return_res == (int)BrainFlowExitCodes::STATUS_OK)) { safe_logger ( @@ -125,7 +124,13 @@ int BTLibBoard::release_session () int BTLibBoard::config_board (std::string config, std::string &response) { - return bluetooth_write_data (config.c_str (), (int)strlen (config.c_str ())); + int res = bluetooth_write_data (config.c_str (), (int)strlen (config.c_str ())); + if (res != (int)strlen (config.c_str ())) + { + safe_logger (spdlog::level::err, "failed to config device, res: {}", res); + return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; + } + return (int)BrainFlowExitCodes::STATUS_OK; } int BTLibBoard::bluetooth_open_device () @@ -179,12 +184,7 @@ int BTLibBoard::bluetooth_write_data (const char *command, int len) int res = func_config ( const_cast (command), len, const_cast (params.mac_address.c_str ())); - if (res != (int)SocketBluetoothReturnCodes::STATUS_OK) - { - safe_logger (spdlog::level::err, "failed to config board: {}", res); - return (int)BrainFlowExitCodes::BOARD_NOT_READY_ERROR; - } - return (int)BrainFlowExitCodes::STATUS_OK; + return res; } int BTLibBoard::bluetooth_get_data (char *data, int len) @@ -193,7 +193,7 @@ int BTLibBoard::bluetooth_get_data (char *data, int len) (int (*) (char *, int, char *))dll_loader->get_address ("bluetooth_get_data"); if (func_get == NULL) { - safe_logger (spdlog::level::err, "failed to get function address for bluetooth_write_data"); + safe_logger (spdlog::level::err, "failed to get function address for bluetooth_get_data"); return (int)BrainFlowExitCodes::GENERAL_ERROR; } diff --git a/src/board_controller/build.cmake b/src/board_controller/build.cmake index cc06c7966..5e423e008 100644 --- a/src/board_controller/build.cmake +++ b/src/board_controller/build.cmake @@ -65,6 +65,7 @@ SET (BOARD_CONTROLLER_SRC ${CMAKE_HOME_DIRECTORY}/src/board_controller/neuromd/callibri_ecg.cpp ${CMAKE_HOME_DIRECTORY}/src/board_controller/neuromd/callibri_emg.cpp ${CMAKE_HOME_DIRECTORY}/src/board_controller/neurosity/notion_osc.cpp + ${CMAKE_HOME_DIRECTORY}/src/board_controller/mentalab/explore.cpp ${CMAKE_HOME_DIRECTORY}/src/board_controller/oymotion/gforce_pro.cpp ${CMAKE_HOME_DIRECTORY}/src/board_controller/oymotion/gforce_dual.cpp ${CMAKE_HOME_DIRECTORY}/src/board_controller/hackerbci/ironbci.cpp @@ -130,6 +131,7 @@ target_include_directories ( ${CMAKE_HOME_DIRECTORY}/src/board_controller/enophone/inc ${CMAKE_HOME_DIRECTORY}/third_party/SimpleBLE/include ${CMAKE_HOME_DIRECTORY}/src/board_controller/brainalive/inc + ${CMAKE_HOME_DIRECTORY}/src/board_controller/mentalab/inc ) target_compile_definitions(${BOARD_CONTROLLER_NAME} PRIVATE NOMINMAX BRAINFLOW_VERSION=${BRAINFLOW_VERSION}) diff --git a/src/board_controller/enophone/enophone.cpp b/src/board_controller/enophone/enophone.cpp index 1f7633ad8..437ff5235 100644 --- a/src/board_controller/enophone/enophone.cpp +++ b/src/board_controller/enophone/enophone.cpp @@ -20,11 +20,20 @@ Enophone::~Enophone () release_session (); } +int Enophone::prepare_session () +{ + if (params.ip_port <= 0) + { + params.ip_port = 1; // default for enophone + } + return BTLibBoard::prepare_session (); +} + int Enophone::start_stream (int buffer_size, const char *streamer_params) { if (!initialized) { - safe_logger (spdlog::level::err, "You need to call prepare_session before config_board"); + safe_logger (spdlog::level::err, "You need to call prepare_session before start_stream"); return (int)BrainFlowExitCodes::BOARD_NOT_CREATED_ERROR; } if (keep_alive) diff --git a/src/board_controller/enophone/inc/enophone.h b/src/board_controller/enophone/inc/enophone.h index bbf575bb3..c9eb3fca7 100644 --- a/src/board_controller/enophone/inc/enophone.h +++ b/src/board_controller/enophone/inc/enophone.h @@ -25,6 +25,7 @@ class Enophone : public BTLibBoard Enophone (struct BrainFlowInputParams params); ~Enophone (); + int prepare_session (); int start_stream (int buffer_size, const char *streamer_params); int stop_stream (); int release_session (); diff --git a/src/board_controller/mentalab/explore.cpp b/src/board_controller/mentalab/explore.cpp new file mode 100644 index 000000000..8289c428c --- /dev/null +++ b/src/board_controller/mentalab/explore.cpp @@ -0,0 +1,495 @@ +#include +#include + +#include "explore.h" + +#include "custom_cast.h" +#include "get_dll_dir.h" +#include "timestamp.h" + +Explore::Explore (int board_id, struct BrainFlowInputParams params) : BTLibBoard (board_id, params) +{ + keep_alive = false; + last_eeg_timestamp = -1; + state = (int)BrainFlowExitCodes::SYNC_TIMEOUT_ERROR; +} + +Explore::~Explore () +{ + skip_logs = true; + release_session (); +} + +int Explore::prepare_session () +{ + if (params.ip_port <= 0) + { + params.ip_port = 5; // default for explore + } + return BTLibBoard::prepare_session (); +} + +int Explore::config_board (std::string config, std::string &response) +{ + bool prefix_found = false; + constexpr int command_len = 14; + unsigned char command[command_len]; + memset (command, 0, command_len); + // pid + command[0] = 0xA0; + // payload len + command[2] = 10; + command[3] = 0; + // timestamp, doesnt really matter since we dont use it and it's wrong even in explorepy, it + // gets upper part from unix timestamp and this part does not change + command[4] = 0x17; + command[5] = 0x15; + command[6] = 0xEC; + command[7] = 0x1E; + // fletcher + command[10] = 0xAF; + command[11] = 0xBE; + command[12] = 0xAD; + command[13] = 0xDE; + + std::string sps_prefix = "sampling_rate:"; + if (config.find (sps_prefix) != std::string::npos) + { + prefix_found = true; + int new_sampling_rate = 0; + std::string value = config.substr (sps_prefix.size ()); + try + { + new_sampling_rate = std::stoi (value); + } + catch (...) + { + safe_logger (spdlog::level::err, "format is '{}value'", sps_prefix.c_str ()); + return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR; + } + if ((new_sampling_rate != 250) && (new_sampling_rate != 500) && (new_sampling_rate != 1000)) + { + safe_logger (spdlog::level::err, + "invalid sampling rate provided, possible values are 250, 500, 1000"); + return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR; + } + command[8] = 0xA1; // op code + if (new_sampling_rate == 250) + command[9] = 0x01; + if (new_sampling_rate == 500) + command[9] = 0x02; + if (new_sampling_rate == 1000) + command[9] = 0x03; + } + std::string test_sig_prefix = "test_signal:"; + if (config.find (test_sig_prefix) != std::string::npos) + { + prefix_found = true; + int mask = 0; + std::string value = config.substr (test_sig_prefix.size ()); + try + { + mask = std::stoi (value); + } + catch (...) + { + safe_logger (spdlog::level::err, "format is '{}value'", test_sig_prefix.c_str ()); + return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR; + } + if ((mask < 0) || (mask > 255)) + { + safe_logger (spdlog::level::err, "invalid mask provided, should be between 1 and 255"); + return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR; + } + command[8] = 0xAA; // op code + command[9] = (unsigned char)mask; + } + + // todo, it changes package format, also need to change parsing somehow + /* + std::string chan_prefix = "channels:"; + if (config.find (chan_prefix) != std::string::npos) + { + prefix_found = true; + int mask = 0; + std::string value = config.substr (chan_prefix.size ()); + try + { + mask = std::stoi (value); + } + catch (...) + { + safe_logger (spdlog::level::err, "format is '{}value'", chan_prefix.c_str ()); + return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR; + } + if ((mask < 0) || (mask > 255)) + { + safe_logger (spdlog::level::err, "invalid mask provided, should be between 1 and 255"); + return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR; + } + command[8] = 0xA2; // op code + command[9] = (unsigned char)mask; + } + */ + + if (!prefix_found) + { + safe_logger (spdlog::level::err, + "invalid config format, supported formats are sampling_rate:value and " + "test_signal:value"); + return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR; + } + + std::string command_str = ""; + for (int i = 0; i < command_len; i++) + command_str += std::to_string (command[i]) + " "; + safe_logger (spdlog::level::info, "sending command {} to device", command_str.c_str ()); + + int res = bluetooth_write_data ((char *)command, command_len); + if (res != command_len) + { + safe_logger (spdlog::level::err, "failed to config device, res: {}", res); + return (int)BrainFlowExitCodes::BOARD_WRITE_ERROR; + } + return (int)BrainFlowExitCodes::STATUS_OK; +} + +int Explore::start_stream (int buffer_size, const char *streamer_params) +{ + if (!initialized) + { + safe_logger (spdlog::level::err, "You need to call prepare_session before start_stream"); + return (int)BrainFlowExitCodes::BOARD_NOT_CREATED_ERROR; + } + if (keep_alive) + { + 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; + } + + res = bluetooth_open_device (); + if (res != (int)BrainFlowExitCodes::STATUS_OK) + { + return res; + } + + keep_alive = true; + streaming_thread = std::thread ([this] { this->read_thread (); }); + // wait for the 1st package received + std::unique_lock lk (this->m); + auto sec = std::chrono::seconds (1); + int num_secs = 7; + if (cv.wait_for (lk, num_secs * sec, + [this] { return this->state != (int)BrainFlowExitCodes::SYNC_TIMEOUT_ERROR; })) + { + return state; + } + else + { + safe_logger (spdlog::level::err, "no data received in {} sec, stopping thread", num_secs); + stop_stream (); + return (int)BrainFlowExitCodes::BOARD_NOT_READY_ERROR; + } +} + +int Explore::stop_stream () +{ + if (keep_alive) + { + keep_alive = false; + streaming_thread.join (); + state = (int)BrainFlowExitCodes::SYNC_TIMEOUT_ERROR; + last_eeg_timestamp = -1.0; + return bluetooth_close_device (); + } + else + { + return (int)BrainFlowExitCodes::STREAM_THREAD_IS_NOT_RUNNING; + } +} + +int Explore::release_session () +{ + if (initialized) + { + stop_stream (); + free_packages (); + } + return BTLibBoard::release_session (); +} + +void Explore::read_thread () +{ + // exg data + int num_exg_rows = board_descr["default"]["num_rows"]; + double *package_exg = new double[num_exg_rows]; + memset (package_exg, 0, sizeof (double) * num_exg_rows); + // orientation data + int num_aux_rows = board_descr["auxiliary"]["num_rows"]; + double *package_aux = new double[num_aux_rows]; + memset (package_aux, 0, sizeof (double) * num_aux_rows); + // env data + int num_anc_rows = board_descr["ancillary"]["num_rows"]; + double *package_anc = new double[num_anc_rows]; + memset (package_anc, 0, sizeof (double) * num_anc_rows); + + unsigned char payload_buffer[UINT16_MAX]; + + while (keep_alive) + { + struct ExploreHeader header; + int res = bluetooth_get_data ((char *)&header, sizeof (header)); + if (res < 0) + { + safe_logger (spdlog::level::err, "error reading data from bluetooth"); + } + if (res != 8) + { + continue; + } + // notify main thread that 1st byte received + if (state != (int)BrainFlowExitCodes::STATUS_OK) + { + { + std::lock_guard lk (m); + state = (int)BrainFlowExitCodes::STATUS_OK; + } + cv.notify_one (); + safe_logger (spdlog::level::debug, "received first package"); + } + res = 0; + // safe_logger (spdlog::level::trace, "header pid counter size timestamp: {} {} {} {}", + // header.pid, header.counter, header.payload_size, header.timestamp); + + header.payload_size -= 4; // its because of timestamp which moved to header from the package + if (header.payload_size < 1) + { + safe_logger (spdlog::level::err, "negative size for payload"); + continue; + } + + while ((res != header.payload_size) && (keep_alive)) + { + res = bluetooth_get_data ((char *)payload_buffer, header.payload_size); + } + if (!keep_alive) + { + break; + } + + switch (header.pid) + { + case 0x0d: + parse_orientation_data (&header, package_aux, payload_buffer); + break; + case 0x90: // eeg94 + parse_eeg_data (&header, package_exg, payload_buffer, 2.4, 33); + break; + case 0xD0: // eeg94r + parse_eeg_data (&header, package_exg, payload_buffer, 2.4, 33); + break; + case 0x92: // eeg98 + parse_eeg_data (&header, package_exg, payload_buffer, 2.4, 16); + break; + case 0xD2: // eeg98r + parse_eeg_data (&header, package_exg, payload_buffer, 2.4, 16); + break; + case 0x3e: // eeg99 + parse_eeg_data (&header, package_exg, payload_buffer, 4.5, 16); + break; + case 0x1e: // eeg99s + parse_eeg_data (&header, package_exg, payload_buffer, 4.5, 16); + break; + case 0x13: // env + parse_env_data (&header, package_anc, payload_buffer); + break; + default: + safe_logger (spdlog::level::trace, "received header: {}", header.pid); + break; + } + } + delete[] package_anc; + delete[] package_exg; + delete[] package_aux; +} + +std::string Explore::get_name_selector () +{ + return "Explore"; +} + +void Explore::parse_orientation_data ( + const ExploreHeader *header, double *package, unsigned char *payload) +{ + int payload_size = header->payload_size; + if ((payload[payload_size - 4] != 0xAF) || (payload[payload_size - 3] != 0xBE) || + (payload[payload_size - 2] != 0xAD) || (payload[payload_size - 1] != 0xDE)) + { + safe_logger (spdlog::level::warn, "checksum failed, {} {} {} {}", payload[payload_size - 4], + payload[payload_size - 3], payload[payload_size - 2], payload[payload_size - 1]); + return; + } + payload_size = payload_size - 4; + + std::vector accel_channels = board_descr["auxiliary"]["accel_channels"]; + std::vector gyro_channels = board_descr["auxiliary"]["gyro_channels"]; + std::vector other_channels = board_descr["auxiliary"]["other_channels"]; + + if (payload_size % 2 != 0) // 2 is int16 + { + safe_logger (spdlog::level::warn, "Invalid payload size for Orn package: {}"); + return; + } + int num_datapoints = payload_size / 2; + + for (int i = 0; i < num_datapoints; i++) + { + double data = (double)cast_16bit_to_int32_swap_order ((unsigned char *)(payload + 2 * i)); + if (i < 3) + { + package[accel_channels[i]] = 0.061 * data; + } + else if (i < 6) + { + package[gyro_channels[i - 3]] = 8.750 * data; + } + else + { + if (i == 6) + { + data *= -1; // no idea why, copypaste, maybe bug in fw + } + package[other_channels[i - 6]] = 1.52 * data; + } + } + package[board_descr["auxiliary"]["timestamp_channel"].get ()] = get_timestamp (); + package[board_descr["auxiliary"]["package_num_channel"].get ()] = header->counter; + push_package (package, (int)BrainFlowPresets::AUXILIARY_PRESET); +} + +void Explore::parse_eeg_data (const ExploreHeader *header, double *package, unsigned char *payload, + double vref, int n_packages) +{ + int payload_size = header->payload_size; + if ((payload[payload_size - 4] != 0xAF) || (payload[payload_size - 3] != 0xBE) || + (payload[payload_size - 2] != 0xAD) || (payload[payload_size - 1] != 0xDE)) + { + safe_logger (spdlog::level::warn, "checksum failed, {} {} {} {}", payload[payload_size - 4], + payload[payload_size - 3], payload[payload_size - 2], payload[payload_size - 1]); + return; + } + double current_timestamp = get_timestamp (); + payload_size = payload_size - 4; + std::vector eeg_channels = board_descr["default"]["eeg_channels"]; + if ((payload_size % n_packages != 0) || (payload_size % 3 != 0)) // 3 is int24 format + { + safe_logger (spdlog::level::warn, + "Invalid payload size for EEG package: {}, n_packages: {}", payload_size, n_packages); + return; + } + if (payload_size % (eeg_channels.size () + 1) != 0) // 1 for data status, maybe its reference + { + safe_logger (spdlog::level::warn, "Invalid payload size for num_eeg_channels: {}, {}", + payload_size, eeg_channels.size ()); + return; + } + + int num_datapoints = payload_size / 3; + // convert to uV + std::vector data; + for (int i = 0; i < num_datapoints; i++) + { + double datapoint = + (double)cast_24bit_to_int32_swap_order ((unsigned char *)(payload + i * 3)); + data.push_back (datapoint); + } + int num_total_channels = (int)eeg_channels.size () + 1; + int other_channel = board_descr["default"]["other_channels"][0]; + // submit packages + if (last_eeg_timestamp > 0.0) + { + double step = (current_timestamp - last_eeg_timestamp) / n_packages; + for (int i = 0; i < n_packages; i++) + { + for (int j = 0; j < num_total_channels; j++) + { + if (j == 0) + { + package[other_channel] = data[i * num_total_channels + j]; + } + else + { + double gain = 1.E-6 * (pow (2, 23) - 1) * 6.0; + package[eeg_channels[j - 1]] = data[i * num_total_channels + j] * vref / gain; + } + } + package[board_descr["default"]["timestamp_channel"].get ()] = last_eeg_timestamp + + step * (i + 1); // todo improve timestamps, use data from device + package[board_descr["default"]["package_num_channel"].get ()] = header->counter; + push_package (package, (int)BrainFlowPresets::DEFAULT_PRESET); + } + } + last_eeg_timestamp = current_timestamp; +} + +void Explore::parse_env_data (const ExploreHeader *header, double *package, unsigned char *payload) +{ + int payload_size = header->payload_size; + if ((payload[payload_size - 4] != 0xAF) || (payload[payload_size - 3] != 0xBE) || + (payload[payload_size - 2] != 0xAD) || (payload[payload_size - 1] != 0xDE)) + { + safe_logger (spdlog::level::warn, "checksum failed, {} {} {} {}", payload[payload_size - 4], + payload[payload_size - 3], payload[payload_size - 2], payload[payload_size - 1]); + return; + } + payload_size = payload_size - 4; + if (payload_size < 5) + { + safe_logger (spdlog::level::warn, "invalid size for env package: {}", payload_size); + return; + } + int temperature_channel = board_descr["ancillary"]["temperature_channels"][0]; + int battery_channel = board_descr["ancillary"]["battery_channel"]; + int other_channel = board_descr["ancillary"]["other_channels"][0]; + + uint16_t battery_raw = 0; + memcpy (&battery_raw, payload + 3, 2); + double battery = (16.8 / 6.8) * (1.8 / 2457) * battery_raw; + package[temperature_channel] = (double)payload[0]; + package[battery_channel] = get_battery_percentage (battery); + uint16_t light_raw = 0; + memcpy (&light_raw, payload + 1, 2); + package[other_channel] = (1000.0 / 4095.0) * light_raw; + package[board_descr["ancillary"]["timestamp_channel"].get ()] = get_timestamp (); + package[board_descr["ancillary"]["package_num_channel"].get ()] = header->counter; + push_package (package, (int)BrainFlowPresets::ANCILLARY_PRESET); +} + +double Explore::get_battery_percentage (double battery) +{ + double percentage = 0.0; + if (battery < 3.1) + percentage = 1; + else if (battery < 3.5) + percentage = 1 + (battery - 3.1) / .4 * 10; + else if (battery < 3.8) + percentage = 10 + (battery - 3.5) / .3 * 40; + else if (battery < 3.9) + percentage = 40 + (battery - 3.8) / .1 * 20; + else if (battery < 4.) + percentage = 60 + (battery - 3.9) / .1 * 15; + else if (battery < 4.1) + percentage = 75 + (battery - 4.) / .1 * 15; + else if (battery < 4.2) + percentage = 90 + (battery - 4.1) / .1 * 10; + else if (battery > 4.2) + percentage = 100; + + return percentage; +} \ No newline at end of file diff --git a/src/board_controller/mentalab/inc/explore.h b/src/board_controller/mentalab/inc/explore.h new file mode 100644 index 000000000..f12cb44bf --- /dev/null +++ b/src/board_controller/mentalab/inc/explore.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include + +#include "bt_lib_board.h" + + +#pragma pack(push, 1) +struct ExploreHeader +{ + unsigned char pid; + unsigned char counter; + uint16_t payload_size; + uint32_t timestamp; + + ExploreHeader () + { + pid = 0; + payload_size = 0; + counter = 0; + timestamp = 0; + } +}; +#pragma pack(pop) + +class Explore : public BTLibBoard +{ + +protected: + volatile bool keep_alive; + volatile int state; + std::thread streaming_thread; + std::mutex m; + std::condition_variable cv; + double last_eeg_timestamp; + + void read_thread (); + std::string get_name_selector (); + void parse_eeg_data (const ExploreHeader *header, double *package, unsigned char *payload, + double vref, int n_packages); + void parse_orientation_data ( + const ExploreHeader *header, double *package, unsigned char *payload); + void parse_env_data (const ExploreHeader *header, double *package, unsigned char *payload); + double get_battery_percentage (double battery); + +public: + Explore (int board_id, struct BrainFlowInputParams params); + ~Explore (); + + int prepare_session (); + int config_board (std::string config, std::string &response); + int start_stream (int buffer_size, const char *streamer_params); + int stop_stream (); + int release_session (); +}; diff --git a/src/tests/utils/bluetooth/bluetooth_functions_unittest.cpp b/src/tests/utils/bluetooth/bluetooth_functions_unittest.cpp index bc4a51fbd..0c4010228 100644 --- a/src/tests/utils/bluetooth/bluetooth_functions_unittest.cpp +++ b/src/tests/utils/bluetooth/bluetooth_functions_unittest.cpp @@ -88,20 +88,6 @@ TEST (BluetoothTest, CloseDevice_AddressNull_ParameterErrorReturned) EXPECT_EQ (result, (int)SocketBluetoothReturnCodes::PARAMETER_ERROR); } -TEST (BluetoothTest, GetData_AllRequestedBytesAvailable_NumberOfRequestedBytesReturned) -{ - char mac[] = BRAINFLOW_TEST_BLUETOOTH_VALID_MAC; - bluetooth_open_device (BRAINFLOW_TEST_BLUETOOTH_ALL_BYTES_AVAILABLE_PORT, mac); - - char buffer[BRAINFLOW_TEST_BLUETOOTH_EXPECTED_BYTES] {0}; - int n_bytes = bluetooth_get_data (buffer, BRAINFLOW_TEST_BLUETOOTH_EXPECTED_BYTES, mac); - - EXPECT_EQ (n_bytes, BRAINFLOW_TEST_BLUETOOTH_EXPECTED_BYTES); - EXPECT_THAT (buffer, Each (BRAINFLOW_TEST_BLUETOOTH_DATA_CHAR)); - - bluetooth_close_device (mac); -} - TEST (BluetoothTest, GetData_SomeRequestedBytesAvailable_NumberBytesReadReturned) { char mac[] = BRAINFLOW_TEST_BLUETOOTH_VALID_MAC; @@ -110,11 +96,7 @@ TEST (BluetoothTest, GetData_SomeRequestedBytesAvailable_NumberBytesReadReturned char buffer[BRAINFLOW_TEST_BLUETOOTH_EXPECTED_BYTES] {0}; int n_bytes = bluetooth_get_data (buffer, BRAINFLOW_TEST_BLUETOOTH_EXPECTED_BYTES, mac); - EXPECT_GT (n_bytes, 0); - EXPECT_LT (n_bytes, BRAINFLOW_TEST_BLUETOOTH_EXPECTED_BYTES); - - EXPECT_THAT (buffer, Contains (BRAINFLOW_TEST_BLUETOOTH_DATA_CHAR).Times (n_bytes)); - + EXPECT_EQ (n_bytes, 0); bluetooth_close_device (mac); } diff --git a/src/tests/utils/bluetooth/socket_bluetooth_test.cpp b/src/tests/utils/bluetooth/socket_bluetooth_test.cpp index 90725cbb1..83e2b1024 100644 --- a/src/tests/utils/bluetooth/socket_bluetooth_test.cpp +++ b/src/tests/utils/bluetooth/socket_bluetooth_test.cpp @@ -38,28 +38,27 @@ int SocketBluetooth::recv (char *data, int size) return 0; } - int n_bytes = bytes_available (); - - // Fill the data buffer - for (auto i = 0; i < n_bytes; i++) - { - data[i] = BRAINFLOW_TEST_BLUETOOTH_DATA_CHAR; - } - - return n_bytes; -} - -int SocketBluetooth::bytes_available () -{ + int n_bytes = 0; switch (port) { case BRAINFLOW_TEST_BLUETOOTH_ALL_BYTES_AVAILABLE_PORT: - return BRAINFLOW_TEST_BLUETOOTH_EXPECTED_BYTES; + n_bytes = BRAINFLOW_TEST_BLUETOOTH_EXPECTED_BYTES; case BRAINFLOW_TEST_BLUETOOTH_SOME_BYTES_AVAILABLE_PORT: - return BRAINFLOW_TEST_BLUETOOTH_EXPECTED_BYTES / 2; + n_bytes = BRAINFLOW_TEST_BLUETOOTH_EXPECTED_BYTES / 2; default: - return 0; + n_bytes = 0; + } + + if (n_bytes >= size) + { + for (auto i = 0; i < size; i++) + { + data[i] = BRAINFLOW_TEST_BLUETOOTH_DATA_CHAR; + } + return size; } + + return 0; } int SocketBluetooth::close () diff --git a/src/utils/bluetooth/inc/socket_bluetooth.h b/src/utils/bluetooth/inc/socket_bluetooth.h index 22bc5395e..2e95d7df6 100644 --- a/src/utils/bluetooth/inc/socket_bluetooth.h +++ b/src/utils/bluetooth/inc/socket_bluetooth.h @@ -15,9 +15,9 @@ #ifdef __APPLE__ #include "pipe.h" -#include #endif +#include #include #include #include @@ -37,7 +37,10 @@ class SocketBluetooth int connect (); int send (const char *data, int size); int recv (char *data, int size); - int bytes_available (); + int bytes_available () + { + return (int)temp_buffer.size (); + } int close (); static std::pair discover (char *device_selector); @@ -45,12 +48,13 @@ class SocketBluetooth private: std::string mac_addr; int port; + std::queue temp_buffer; #ifdef _WIN32 SOCKET socket_bt; #elif defined(__linux__) int socket_bt; + int rep[2]; #elif defined(__APPLE__) pipe_consumer_t *consumer; - std::queue temp_buffer; #endif }; diff --git a/src/utils/bluetooth/socket_bluetooth_linux.cpp b/src/utils/bluetooth/socket_bluetooth_linux.cpp index 9550b1d2b..9ab10ff95 100644 --- a/src/utils/bluetooth/socket_bluetooth_linux.cpp +++ b/src/utils/bluetooth/socket_bluetooth_linux.cpp @@ -23,10 +23,18 @@ SocketBluetooth::SocketBluetooth (std::string mac_addr, int port) this->mac_addr = mac_addr; this->port = port; socket_bt = -1; + rep[0] = -1; + rep[1] = -1; } int SocketBluetooth::connect () { + if (pipe (rep) == -1) + { + return (int)SocketBluetoothReturnCodes::OS_SPECIFIC_ERROR; + } + int flags = fcntl (rep[0], F_GETFL, 0); + fcntl (rep[0], F_SETFL, flags | O_NONBLOCK); struct sockaddr_rc addr; memset (&addr, 0, sizeof (addr)); @@ -63,40 +71,34 @@ int SocketBluetooth::recv (char *data, int size) { return -1; } - // waiting for exact amount of bytes - int e = bytes_available (); - if (e < size) - { - return 0; - } fd_set set; FD_ZERO (&set); FD_SET (socket_bt, &set); + FD_SET (rep[0], &set); + int nfds = (socket_bt > rep[0]) ? socket_bt : rep[0]; - int res = -1; - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 0; - if (select (socket_bt + 1, &set, NULL, NULL, &timeout) >= 0) + if (pselect (nfds + 1, &set, NULL, NULL, NULL, NULL) >= 0) { if (FD_ISSET (socket_bt, &set)) { - res = ::recv (socket_bt, data, size, 0); + int res = ::recv (socket_bt, data, size, 0); + for (int i = 0; i < res; i++) + { + temp_buffer.push (data[i]); + } } } - return res; -} - -int SocketBluetooth::bytes_available () -{ - if (socket_bt < 0) + if ((int)temp_buffer.size () < size) { - return -1; + return 0; + } + for (int i = 0; i < size; i++) + { + data[i] = temp_buffer.front (); + temp_buffer.pop (); } - int count; - ioctl (socket_bt, FIONREAD, &count); - return count; + return size; } int SocketBluetooth::close () diff --git a/src/utils/bluetooth/socket_bluetooth_macos.mm b/src/utils/bluetooth/socket_bluetooth_macos.mm index 041a98882..fb0539d86 100644 --- a/src/utils/bluetooth/socket_bluetooth_macos.mm +++ b/src/utils/bluetooth/socket_bluetooth_macos.mm @@ -104,11 +104,6 @@ return res; } -int SocketBluetooth::bytes_available () -{ - return 0; -} - std::pair SocketBluetooth::discover (char *selector) { // somehow it leads to connection reset and audio stops, todo - fix, dont use autodiscovery on macos diff --git a/src/utils/bluetooth/socket_bluetooth_win.cpp b/src/utils/bluetooth/socket_bluetooth_win.cpp index d747ff7c4..4f86756c1 100644 --- a/src/utils/bluetooth/socket_bluetooth_win.cpp +++ b/src/utils/bluetooth/socket_bluetooth_win.cpp @@ -93,38 +93,32 @@ int SocketBluetooth::recv (char *data, int size) { return -1; } - // waiting for exact amount of bytes - int e = bytes_available (); - if (e < size) - { - return 0; - } fd_set set; FD_ZERO (&set); FD_SET (socket_bt, &set); - int res = -1; - timeval timeout {0, 0}; if (select (1, &set, nullptr, nullptr, &timeout) >= 0) { if (FD_ISSET (socket_bt, &set)) { - res = ::recv (socket_bt, data, size, 0); + int res = ::recv (socket_bt, data, size, 0); + for (int i = 0; i < res; i++) + { + temp_buffer.push (data[i]); + } } } - return res; -} - -int SocketBluetooth::bytes_available () -{ - if (socket_bt == INVALID_SOCKET) + if ((int)temp_buffer.size () < size) { - return -1; + return 0; + } + for (int i = 0; i < size; i++) + { + data[i] = temp_buffer.front (); + temp_buffer.pop (); } - u_long count; - ioctlsocket (socket_bt, FIONREAD, &count); - return count; + return size; } int SocketBluetooth::close () diff --git a/src/utils/inc/brainflow_constants.h b/src/utils/inc/brainflow_constants.h index b6e96c1b2..e809fdd8e 100644 --- a/src/utils/inc/brainflow_constants.h +++ b/src/utils/inc/brainflow_constants.h @@ -77,9 +77,11 @@ enum class BoardIds : int MUSE_2016_BOARD = 41, MUSE_2016_BLED_BOARD = 42, PIEEG_BOARD = 43, + EXPLORE_4_CHAN_BOARD = 44, + EXPLORE_8_CHAN_BOARD = 45, // use it to iterate FIRST = PLAYBACK_FILE_BOARD, - LAST = PIEEG_BOARD + LAST = EXPLORE_8_CHAN_BOARD }; enum class IpProtocolTypes : int diff --git a/src/utils/inc/custom_cast.h b/src/utils/inc/custom_cast.h index 38459d437..28d82c8de 100644 --- a/src/utils/inc/custom_cast.h +++ b/src/utils/inc/custom_cast.h @@ -19,6 +19,15 @@ inline int32_t cast_24bit_to_int32 (unsigned char *byte_array) return (prefix << 24) | (byte_array[0] << 16) | (byte_array[1] << 8) | byte_array[2]; } +inline int32_t cast_24bit_to_int32_swap_order (unsigned char *byte_array) +{ + unsigned char swapped[3]; + swapped[0] = byte_array[2]; + swapped[1] = byte_array[1]; + swapped[2] = byte_array[0]; + return cast_24bit_to_int32 (swapped); +} + inline int32_t cast_16bit_to_int32 (unsigned char *byte_array) { int prefix = 0; @@ -29,6 +38,14 @@ inline int32_t cast_16bit_to_int32 (unsigned char *byte_array) return (prefix << 16) | (byte_array[0] << 8) | byte_array[1]; } +inline int32_t cast_16bit_to_int32_swap_order (unsigned char *byte_array) +{ + unsigned char swapped[2]; + swapped[0] = byte_array[1]; + swapped[1] = byte_array[0]; + return cast_16bit_to_int32 (swapped); +} + inline void uchar_to_bits (unsigned char c, unsigned char *bits) { for (int i = sizeof (unsigned char) * 8; i; c >>= 1) @@ -93,4 +110,4 @@ inline int32_t swap_endians (int32_t value) rightmost_byte <<= 0; result = (leftmost_byte | left_middle_byle | right_middle_byte | rightmost_byte); return result; -} \ No newline at end of file +}