Skip to content

Commit 96b4179

Browse files
authored
Adding support for multiple emotibits (#749)
Signed-off-by: Andrey Parfenov <a1994ndrey@gmail.com>
1 parent 134d538 commit 96b4179

File tree

4 files changed

+140
-30
lines changed

4 files changed

+140
-30
lines changed

docs/SupportedBoards.rst

+1
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,7 @@ To create such board you need to specify the following board ID and fields of Br
12481248

12491249
- :code:`BoardIds.EMOTIBIT_BOARD`
12501250
- *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.
1251+
- *optional:* :code:`serial_number`, recommended you if have multiple boards in the same network.
12511252

12521253
Initialization Example:
12531254

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import argparse
2+
import time
3+
4+
from brainflow.board_shim import BoardShim, BrainFlowInputParams, BoardIds, BrainFlowPresets
5+
from brainflow.data_filter import DataFilter
6+
7+
8+
def main():
9+
BoardShim.enable_dev_board_logger()
10+
11+
parser = argparse.ArgumentParser()
12+
parser.add_argument('--id1', type=str, help='id for first emotibit', required=True)
13+
parser.add_argument('--id2', type=str, help='id for second emotibit', required=True)
14+
args = parser.parse_args()
15+
16+
params1 = BrainFlowInputParams()
17+
params1.serial_number = args.id1
18+
params2 = BrainFlowInputParams()
19+
params2.serial_number = args.id2
20+
board_id = BoardIds.EMOTIBIT_BOARD.value
21+
22+
presets = BoardShim.get_board_presets(board_id)
23+
print (presets)
24+
25+
# Init both boards
26+
board1 = BoardShim(board_id, params1)
27+
board2 = BoardShim(board_id, params2)
28+
board1.prepare_session()
29+
board2.prepare_session()
30+
31+
# Start streaming for both
32+
board1.start_stream()
33+
board2.start_stream()
34+
time.sleep(10)
35+
36+
# Get data from both
37+
data_default1 = board1.get_board_data(preset=BrainFlowPresets.DEFAULT_PRESET)
38+
data_aux1 = board1.get_board_data(preset=BrainFlowPresets.AUXILIARY_PRESET)
39+
data_anc1 = board1.get_board_data(preset=BrainFlowPresets.ANCILLARY_PRESET)
40+
data_default2 = board2.get_board_data(preset=BrainFlowPresets.DEFAULT_PRESET)
41+
data_aux2 = board2.get_board_data(preset=BrainFlowPresets.AUXILIARY_PRESET)
42+
data_anc2 = board1.get_board_data(preset=BrainFlowPresets.ANCILLARY_PRESET)
43+
44+
# Stop streaming for both
45+
board1.stop_stream()
46+
board2.stop_stream()
47+
48+
# Release both boards
49+
board1.release_session()
50+
board2.release_session()
51+
52+
# Write data from both
53+
DataFilter.write_file(data_default1, 'default1.csv', 'w')
54+
DataFilter.write_file(data_aux1, 'aux1.csv', 'w')
55+
DataFilter.write_file(data_anc1, 'anc1.csv', 'w')
56+
DataFilter.write_file(data_default2, 'default2.csv', 'w')
57+
DataFilter.write_file(data_aux2, 'aux2.csv', 'w')
58+
DataFilter.write_file(data_anc2, 'anc2.csv', 'w')
59+
60+
61+
if __name__ == "__main__":
62+
main()

src/board_controller/emotibit/emotibit.cpp

+72-28
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <stdint.h>
66
#include <string.h>
77

8+
#include "broadcast_server.h"
89
#include "custom_cast.h"
910
#include "json.hpp"
1011
#include "network_interfaces.h"
@@ -20,7 +21,7 @@ Emotibit::Emotibit (struct BrainFlowInputParams params)
2021
{
2122
data_socket = NULL;
2223
control_socket = NULL;
23-
advertise_socket_server = NULL;
24+
adv_socket = NULL;
2425
keep_alive = false;
2526
initialized = false;
2627
control_port = -1;
@@ -70,10 +71,10 @@ int Emotibit::prepare_session ()
7071

7172
if (res != (int)BrainFlowExitCodes::STATUS_OK)
7273
{
73-
if (advertise_socket_server != NULL)
74+
if (adv_socket != NULL)
7475
{
75-
delete advertise_socket_server;
76-
advertise_socket_server = NULL;
76+
delete adv_socket;
77+
adv_socket = NULL;
7778
}
7879
if (data_socket != NULL)
7980
{
@@ -165,11 +166,11 @@ int Emotibit::release_session ()
165166
delete control_socket;
166167
control_socket = NULL;
167168
}
168-
if (advertise_socket_server)
169+
if (adv_socket)
169170
{
170-
advertise_socket_server->close ();
171-
delete advertise_socket_server;
172-
advertise_socket_server = NULL;
171+
adv_socket->close ();
172+
delete adv_socket;
173+
adv_socket = NULL;
173174
}
174175
control_port = -1;
175176
data_port = -1;
@@ -493,6 +494,13 @@ std::vector<std::string> Emotibit::split_string (const std::string &package, cha
493494

494495
bool Emotibit::get_header (
495496
const std::string &package_string, int *package_num, int *data_len, std::string &type_tag)
497+
{
498+
std::string serial_number = "";
499+
return get_header (package_string, package_num, data_len, type_tag, serial_number);
500+
}
501+
502+
bool Emotibit::get_header (const std::string &package_string, int *package_num, int *data_len,
503+
std::string &type_tag, std::string &serial_number)
496504
{
497505
std::vector<std::string> package = split_string (package_string, PAYLOAD_DELIMITER);
498506
if (package.size () >= HEADER_LENGTH)
@@ -518,6 +526,13 @@ bool Emotibit::get_header (
518526
if (package.at (3) != "")
519527
{
520528
type_tag = package.at (3);
529+
if (type_tag == HELLO_HOST)
530+
{
531+
if (package.size () > 9)
532+
{
533+
serial_number = package.at (9);
534+
}
535+
}
521536
}
522537
else
523538
{
@@ -582,14 +597,14 @@ int Emotibit::create_adv_connection ()
582597

583598
for (std::string broadcast_address : broadcast_addresses)
584599
{
600+
BroadCastServer *advertise_socket =
601+
new BroadCastServer (broadcast_address.c_str (), WIFI_ADVERTISING_PORT);
585602
res = (int)BrainFlowExitCodes::STATUS_OK;
586603
safe_logger (
587604
spdlog::level::info, "trying broadcast address: {}", broadcast_address.c_str ());
588605
if (res == (int)BrainFlowExitCodes::STATUS_OK)
589606
{
590-
advertise_socket_server =
591-
new BroadCastServer (broadcast_address.c_str (), WIFI_ADVERTISING_PORT);
592-
int init_res = advertise_socket_server->init ();
607+
int init_res = advertise_socket->init ();
593608
if (init_res != (int)BroadCastServerReturnCodes::STATUS_OK)
594609
{
595610
safe_logger (spdlog::level::err, "failed to init broadcast server socket: {}", res);
@@ -600,7 +615,7 @@ int Emotibit::create_adv_connection ()
600615
{
601616
std::string package = create_package (HELLO_EMOTIBIT, 0, "", 0);
602617
safe_logger (spdlog::level::info, "sending package: {}", package.c_str ());
603-
int bytes_send = advertise_socket_server->send (package.c_str (), (int)package.size ());
618+
int bytes_send = advertise_socket->send (package.c_str (), (int)package.size ());
604619
if (bytes_send != (int)package.size ())
605620
{
606621
safe_logger (
@@ -618,8 +633,8 @@ int Emotibit::create_adv_connection ()
618633
double start_time = get_timestamp ();
619634
for (int i = 0; (i < 100) && (!found); i++)
620635
{
621-
int bytes_recv = advertise_socket_server->recv (
622-
recv_data, max_size, emotibit_ip, max_ip_addr_size);
636+
int bytes_recv =
637+
advertise_socket->recv (recv_data, max_size, emotibit_ip, max_ip_addr_size);
623638
if (bytes_recv > 0)
624639
{
625640
std::vector<std::string> splitted_packages =
@@ -630,13 +645,21 @@ int Emotibit::create_adv_connection ()
630645
int package_num = 0;
631646
int data_len = 0;
632647
std::string type_tag = "";
633-
if (get_header (recv_package, &package_num, &data_len, type_tag))
648+
std::string serial_number = "";
649+
if (get_header (
650+
recv_package, &package_num, &data_len, type_tag, serial_number))
634651
{
635652
safe_logger (spdlog::level::info, "received {} package", type_tag);
636-
if ((type_tag == HELLO_HOST) || (type_tag == PONG))
653+
if (type_tag == HELLO_HOST)
637654
{
638-
found = true;
639-
ip_address = emotibit_ip;
655+
safe_logger (
656+
spdlog::level::info, "Found emotibit: {}", serial_number);
657+
if (params.serial_number.empty () ||
658+
(params.serial_number == serial_number))
659+
{
660+
found = true;
661+
ip_address = emotibit_ip;
662+
}
640663
}
641664
}
642665
else
@@ -657,19 +680,40 @@ int Emotibit::create_adv_connection ()
657680
res = (int)BrainFlowExitCodes::BOARD_NOT_READY_ERROR;
658681
}
659682
}
660-
if (res != (int)BrainFlowExitCodes::STATUS_OK)
683+
if (advertise_socket != NULL)
661684
{
662-
if (advertise_socket_server != NULL)
663-
{
664-
delete advertise_socket_server;
665-
advertise_socket_server = NULL;
666-
}
685+
delete advertise_socket;
686+
advertise_socket = NULL;
667687
}
668-
else
688+
if (res == (int)BrainFlowExitCodes::STATUS_OK)
669689
{
670690
break;
671691
}
672692
}
693+
// we dont need broadcast anymore, replace it with normal socket
694+
if (res == (int)BrainFlowExitCodes::STATUS_OK)
695+
{
696+
// ugly but makes it safer
697+
#ifdef _WIN32
698+
Sleep (1000);
699+
#else
700+
usleep (1000000);
701+
#endif
702+
adv_socket = new SocketClientUDP (ip_address.c_str (), WIFI_ADVERTISING_PORT);
703+
if (adv_socket->connect () != ((int)SocketClientUDPReturnCodes::STATUS_OK))
704+
{
705+
safe_logger (spdlog::level::err, "Failed to bind adv_socket");
706+
res = (int)BrainFlowExitCodes::SET_PORT_ERROR;
707+
adv_socket->close ();
708+
delete adv_socket;
709+
adv_socket = NULL;
710+
}
711+
else
712+
{
713+
safe_logger (spdlog::level::debug, "adv_connection established, ip: {}, port: {}",
714+
ip_address, WIFI_ADVERTISING_PORT);
715+
}
716+
}
673717
return res;
674718
}
675719

@@ -749,7 +793,7 @@ int Emotibit::send_connect_msg ()
749793
safe_logger (spdlog::level::info, "sending connect package: {}", package.c_str ());
750794

751795
int res = (int)BrainFlowExitCodes::STATUS_OK;
752-
int bytes_send = advertise_socket_server->send (package.c_str (), (int)package.size ());
796+
int bytes_send = adv_socket->send (package.c_str (), (int)package.size ());
753797
if (bytes_send != (int)package.size ())
754798
{
755799
safe_logger (spdlog::level::err, "failed to send connect package, res is {}", bytes_send);
@@ -795,7 +839,7 @@ int Emotibit::wait_for_connection ()
795839
}
796840
else
797841
{
798-
int max_attempts = 15;
842+
int max_attempts = 20;
799843
for (int i = 0; i < max_attempts; i++)
800844
{
801845
safe_logger (spdlog::level::trace, "waiting for accept {}/{}", i, max_attempts);
@@ -838,7 +882,7 @@ void Emotibit::ping_thread ()
838882
payload.push_back (std::to_string (data_port));
839883
std::string package = create_package (PING, package_num++, payload);
840884
// safe_logger (spdlog::level::trace, "sending package: {}", package.c_str ());
841-
int bytes_send = advertise_socket_server->send (package.c_str (), (int)package.size ());
885+
int bytes_send = adv_socket->send (package.c_str (), (int)package.size ());
842886
if (bytes_send != (int)package.size ())
843887
{
844888
safe_logger (spdlog::level::err, "failed to send adv package, res is {}", bytes_send);

src/board_controller/emotibit/inc/emotibit.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
#include "board.h"
88
#include "board_controller.h"
99

10-
#include "broadcast_server.h"
1110
#include "socket_client_udp.h"
1211
#include "socket_server_tcp.h"
1312

@@ -24,7 +23,9 @@ class Emotibit : public Board
2423
std::thread connection_thread;
2524
SocketClientUDP *data_socket;
2625
SocketServerTCP *control_socket;
27-
BroadCastServer *advertise_socket_server;
26+
// there is one for broadcast but its used only to find ip, after
27+
// that its replaced by normal udp socket
28+
SocketClientUDP *adv_socket;
2829
std::mutex m;
2930
std::condition_variable cv;
3031
int control_port;
@@ -45,6 +46,8 @@ class Emotibit : public Board
4546
std::vector<std::string> split_string (const std::string &package, char delim);
4647
bool get_header (
4748
const std::string &package_string, int *package_num, int *data_len, std::string &type_tag);
49+
bool get_header (const std::string &package_string, int *package_num, int *data_len,
50+
std::string &type_tag, std::string &serial_number);
4851
std::vector<std::string> get_payload (const std::string &package_string, int data_len);
4952

5053
int create_adv_connection ();

0 commit comments

Comments
 (0)