diff --git a/docs/SupportedBoards.rst b/docs/SupportedBoards.rst index b5696add3..8ede7cbd6 100644 --- a/docs/SupportedBoards.rst +++ b/docs/SupportedBoards.rst @@ -1248,6 +1248,7 @@ To create such board you need to specify the following board ID and fields of Br - :code:`BoardIds.EMOTIBIT_BOARD` - *optional:* :code:`ip_address`, you can provide *broadcast* ip address of the network with EmotiBit device, e.g. 192.168.178.255. If not provided BrainFlow will try to autodiscover the network and it may take a little longer. +- *optional:* :code:`serial_number`, recommended you if have multiple boards in the same network. Initialization Example: diff --git a/python_package/examples/tests/two_emotibits.py b/python_package/examples/tests/two_emotibits.py new file mode 100644 index 000000000..54e4eaa76 --- /dev/null +++ b/python_package/examples/tests/two_emotibits.py @@ -0,0 +1,62 @@ +import argparse +import time + +from brainflow.board_shim import BoardShim, BrainFlowInputParams, BoardIds, BrainFlowPresets +from brainflow.data_filter import DataFilter + + +def main(): + BoardShim.enable_dev_board_logger() + + parser = argparse.ArgumentParser() + parser.add_argument('--id1', type=str, help='id for first emotibit', required=True) + parser.add_argument('--id2', type=str, help='id for second emotibit', required=True) + args = parser.parse_args() + + params1 = BrainFlowInputParams() + params1.serial_number = args.id1 + params2 = BrainFlowInputParams() + params2.serial_number = args.id2 + board_id = BoardIds.EMOTIBIT_BOARD.value + + presets = BoardShim.get_board_presets(board_id) + print (presets) + + # Init both boards + board1 = BoardShim(board_id, params1) + board2 = BoardShim(board_id, params2) + board1.prepare_session() + board2.prepare_session() + + # Start streaming for both + board1.start_stream() + board2.start_stream() + time.sleep(10) + + # Get data from both + data_default1 = board1.get_board_data(preset=BrainFlowPresets.DEFAULT_PRESET) + data_aux1 = board1.get_board_data(preset=BrainFlowPresets.AUXILIARY_PRESET) + data_anc1 = board1.get_board_data(preset=BrainFlowPresets.ANCILLARY_PRESET) + data_default2 = board2.get_board_data(preset=BrainFlowPresets.DEFAULT_PRESET) + data_aux2 = board2.get_board_data(preset=BrainFlowPresets.AUXILIARY_PRESET) + data_anc2 = board1.get_board_data(preset=BrainFlowPresets.ANCILLARY_PRESET) + + # Stop streaming for both + board1.stop_stream() + board2.stop_stream() + + # Release both boards + board1.release_session() + board2.release_session() + + # Write data from both + DataFilter.write_file(data_default1, 'default1.csv', 'w') + DataFilter.write_file(data_aux1, 'aux1.csv', 'w') + DataFilter.write_file(data_anc1, 'anc1.csv', 'w') + DataFilter.write_file(data_default2, 'default2.csv', 'w') + DataFilter.write_file(data_aux2, 'aux2.csv', 'w') + DataFilter.write_file(data_anc2, 'anc2.csv', 'w') + + +if __name__ == "__main__": + main() diff --git a/src/board_controller/emotibit/emotibit.cpp b/src/board_controller/emotibit/emotibit.cpp index 68ed8bc6a..e7ac913eb 100644 --- a/src/board_controller/emotibit/emotibit.cpp +++ b/src/board_controller/emotibit/emotibit.cpp @@ -5,6 +5,7 @@ #include #include +#include "broadcast_server.h" #include "custom_cast.h" #include "json.hpp" #include "network_interfaces.h" @@ -20,7 +21,7 @@ Emotibit::Emotibit (struct BrainFlowInputParams params) { data_socket = NULL; control_socket = NULL; - advertise_socket_server = NULL; + adv_socket = NULL; keep_alive = false; initialized = false; control_port = -1; @@ -70,10 +71,10 @@ int Emotibit::prepare_session () if (res != (int)BrainFlowExitCodes::STATUS_OK) { - if (advertise_socket_server != NULL) + if (adv_socket != NULL) { - delete advertise_socket_server; - advertise_socket_server = NULL; + delete adv_socket; + adv_socket = NULL; } if (data_socket != NULL) { @@ -165,11 +166,11 @@ int Emotibit::release_session () delete control_socket; control_socket = NULL; } - if (advertise_socket_server) + if (adv_socket) { - advertise_socket_server->close (); - delete advertise_socket_server; - advertise_socket_server = NULL; + adv_socket->close (); + delete adv_socket; + adv_socket = NULL; } control_port = -1; data_port = -1; @@ -493,6 +494,13 @@ std::vector Emotibit::split_string (const std::string &package, cha bool Emotibit::get_header ( const std::string &package_string, int *package_num, int *data_len, std::string &type_tag) +{ + std::string serial_number = ""; + return get_header (package_string, package_num, data_len, type_tag, serial_number); +} + +bool Emotibit::get_header (const std::string &package_string, int *package_num, int *data_len, + std::string &type_tag, std::string &serial_number) { std::vector package = split_string (package_string, PAYLOAD_DELIMITER); if (package.size () >= HEADER_LENGTH) @@ -518,6 +526,13 @@ bool Emotibit::get_header ( if (package.at (3) != "") { type_tag = package.at (3); + if (type_tag == HELLO_HOST) + { + if (package.size () > 9) + { + serial_number = package.at (9); + } + } } else { @@ -582,14 +597,14 @@ int Emotibit::create_adv_connection () for (std::string broadcast_address : broadcast_addresses) { + BroadCastServer *advertise_socket = + new BroadCastServer (broadcast_address.c_str (), WIFI_ADVERTISING_PORT); res = (int)BrainFlowExitCodes::STATUS_OK; safe_logger ( spdlog::level::info, "trying broadcast address: {}", broadcast_address.c_str ()); if (res == (int)BrainFlowExitCodes::STATUS_OK) { - advertise_socket_server = - new BroadCastServer (broadcast_address.c_str (), WIFI_ADVERTISING_PORT); - int init_res = advertise_socket_server->init (); + int init_res = advertise_socket->init (); if (init_res != (int)BroadCastServerReturnCodes::STATUS_OK) { safe_logger (spdlog::level::err, "failed to init broadcast server socket: {}", res); @@ -600,7 +615,7 @@ int Emotibit::create_adv_connection () { std::string package = create_package (HELLO_EMOTIBIT, 0, "", 0); safe_logger (spdlog::level::info, "sending package: {}", package.c_str ()); - int bytes_send = advertise_socket_server->send (package.c_str (), (int)package.size ()); + int bytes_send = advertise_socket->send (package.c_str (), (int)package.size ()); if (bytes_send != (int)package.size ()) { safe_logger ( @@ -618,8 +633,8 @@ int Emotibit::create_adv_connection () double start_time = get_timestamp (); for (int i = 0; (i < 100) && (!found); i++) { - int bytes_recv = advertise_socket_server->recv ( - recv_data, max_size, emotibit_ip, max_ip_addr_size); + int bytes_recv = + advertise_socket->recv (recv_data, max_size, emotibit_ip, max_ip_addr_size); if (bytes_recv > 0) { std::vector splitted_packages = @@ -630,13 +645,21 @@ int Emotibit::create_adv_connection () int package_num = 0; int data_len = 0; std::string type_tag = ""; - if (get_header (recv_package, &package_num, &data_len, type_tag)) + std::string serial_number = ""; + if (get_header ( + recv_package, &package_num, &data_len, type_tag, serial_number)) { safe_logger (spdlog::level::info, "received {} package", type_tag); - if ((type_tag == HELLO_HOST) || (type_tag == PONG)) + if (type_tag == HELLO_HOST) { - found = true; - ip_address = emotibit_ip; + safe_logger ( + spdlog::level::info, "Found emotibit: {}", serial_number); + if (params.serial_number.empty () || + (params.serial_number == serial_number)) + { + found = true; + ip_address = emotibit_ip; + } } } else @@ -657,19 +680,40 @@ int Emotibit::create_adv_connection () res = (int)BrainFlowExitCodes::BOARD_NOT_READY_ERROR; } } - if (res != (int)BrainFlowExitCodes::STATUS_OK) + if (advertise_socket != NULL) { - if (advertise_socket_server != NULL) - { - delete advertise_socket_server; - advertise_socket_server = NULL; - } + delete advertise_socket; + advertise_socket = NULL; } - else + if (res == (int)BrainFlowExitCodes::STATUS_OK) { break; } } + // we dont need broadcast anymore, replace it with normal socket + if (res == (int)BrainFlowExitCodes::STATUS_OK) + { + // ugly but makes it safer +#ifdef _WIN32 + Sleep (1000); +#else + usleep (1000000); +#endif + adv_socket = new SocketClientUDP (ip_address.c_str (), WIFI_ADVERTISING_PORT); + if (adv_socket->connect () != ((int)SocketClientUDPReturnCodes::STATUS_OK)) + { + safe_logger (spdlog::level::err, "Failed to bind adv_socket"); + res = (int)BrainFlowExitCodes::SET_PORT_ERROR; + adv_socket->close (); + delete adv_socket; + adv_socket = NULL; + } + else + { + safe_logger (spdlog::level::debug, "adv_connection established, ip: {}, port: {}", + ip_address, WIFI_ADVERTISING_PORT); + } + } return res; } @@ -749,7 +793,7 @@ int Emotibit::send_connect_msg () safe_logger (spdlog::level::info, "sending connect package: {}", package.c_str ()); int res = (int)BrainFlowExitCodes::STATUS_OK; - int bytes_send = advertise_socket_server->send (package.c_str (), (int)package.size ()); + int bytes_send = adv_socket->send (package.c_str (), (int)package.size ()); if (bytes_send != (int)package.size ()) { safe_logger (spdlog::level::err, "failed to send connect package, res is {}", bytes_send); @@ -795,7 +839,7 @@ int Emotibit::wait_for_connection () } else { - int max_attempts = 15; + int max_attempts = 20; for (int i = 0; i < max_attempts; i++) { safe_logger (spdlog::level::trace, "waiting for accept {}/{}", i, max_attempts); @@ -838,7 +882,7 @@ void Emotibit::ping_thread () payload.push_back (std::to_string (data_port)); std::string package = create_package (PING, package_num++, payload); // safe_logger (spdlog::level::trace, "sending package: {}", package.c_str ()); - int bytes_send = advertise_socket_server->send (package.c_str (), (int)package.size ()); + int bytes_send = adv_socket->send (package.c_str (), (int)package.size ()); if (bytes_send != (int)package.size ()) { safe_logger (spdlog::level::err, "failed to send adv package, res is {}", bytes_send); diff --git a/src/board_controller/emotibit/inc/emotibit.h b/src/board_controller/emotibit/inc/emotibit.h index e639e822a..c32c4163b 100644 --- a/src/board_controller/emotibit/inc/emotibit.h +++ b/src/board_controller/emotibit/inc/emotibit.h @@ -7,7 +7,6 @@ #include "board.h" #include "board_controller.h" -#include "broadcast_server.h" #include "socket_client_udp.h" #include "socket_server_tcp.h" @@ -24,7 +23,9 @@ class Emotibit : public Board std::thread connection_thread; SocketClientUDP *data_socket; SocketServerTCP *control_socket; - BroadCastServer *advertise_socket_server; + // there is one for broadcast but its used only to find ip, after + // that its replaced by normal udp socket + SocketClientUDP *adv_socket; std::mutex m; std::condition_variable cv; int control_port; @@ -45,6 +46,8 @@ class Emotibit : public Board std::vector split_string (const std::string &package, char delim); bool get_header ( const std::string &package_string, int *package_num, int *data_len, std::string &type_tag); + bool get_header (const std::string &package_string, int *package_num, int *data_len, + std::string &type_tag, std::string &serial_number); std::vector get_payload (const std::string &package_string, int data_len); int create_adv_connection ();