Skip to content

Commit

Permalink
Adding support for multiple emotibits
Browse files Browse the repository at this point in the history
Signed-off-by: Andrey Parfenov <a1994ndrey@gmail.com>
  • Loading branch information
Andrey1994 committed Nov 24, 2024
1 parent 134d538 commit 07f04cb
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 30 deletions.
1 change: 1 addition & 0 deletions docs/SupportedBoards.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
62 changes: 62 additions & 0 deletions python_package/examples/tests/two_emotibits.py
Original file line number Diff line number Diff line change
@@ -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()
100 changes: 72 additions & 28 deletions src/board_controller/emotibit/emotibit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <stdint.h>
#include <string.h>

#include "broadcast_server.h"
#include "custom_cast.h"
#include "json.hpp"
#include "network_interfaces.h"
Expand All @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -493,6 +494,13 @@ std::vector<std::string> 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<std::string> package = split_string (package_string, PAYLOAD_DELIMITER);
if (package.size () >= HEADER_LENGTH)
Expand All @@ -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
{
Expand Down Expand Up @@ -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);
Expand All @@ -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 (
Expand All @@ -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<std::string> splitted_packages =
Expand All @@ -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
Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
7 changes: 5 additions & 2 deletions src/board_controller/emotibit/inc/emotibit.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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;
Expand All @@ -45,6 +46,8 @@ class Emotibit : public Board
std::vector<std::string> 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<std::string> get_payload (const std::string &package_string, int data_len);

int create_adv_connection ();
Expand Down

0 comments on commit 07f04cb

Please sign in to comment.