Skip to content

Commit 75db782

Browse files
authored
NeuroPawn bug fix + emulation + docs (#748)
add neuropawn board
1 parent 75d55cd commit 75db782

File tree

19 files changed

+634
-7
lines changed

19 files changed

+634
-7
lines changed

.github/workflows/run_unix.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,9 @@ jobs:
234234
env:
235235
LD_LIBRARY_PATH: ${{ github.workspace }}/installed/lib
236236
- name: Cyton Daisy Python
237-
run: sudo -H python3 $GITHUB_WORKSPACE/emulator/brainflow_emulator/cyton_linux.py python3 $GITHUB_WORKSPACE/python_package/examples/tests/brainflow_get_data.py --board-id 2 --serial-port
237+
run: sudo -H python3 $GITHUB_WORKSPACE/emulator/brainflow_emulator/cyton_linux.py python3 $GITHUB_WORKSPACE/python_package/examples/tests/brainflow_get_data.py --board-id 2 --serial-port
238+
- name: KnightBoard Python
239+
run: sudo -H python3 $GITHUB_WORKSPACE/emulator/brainflow_emulator/knightboard_linux.py python3 $GITHUB_WORKSPACE/python_package/examples/tests/brainflow_get_data.py --board-id 57 --serial-port
238240
- name: Cyton Daisy Python Markers
239241
run: sudo -H python3 $GITHUB_WORKSPACE/emulator/brainflow_emulator/cyton_linux.py python3 $GITHUB_WORKSPACE/python_package/examples/tests/markers.py --board-id 2 --serial-port
240242
- name: Galea Cpp

.github/workflows/run_windows.yml

+3
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ jobs:
183183
- name: FreeEEG32 Python Test
184184
run: python %GITHUB_WORKSPACE%\emulator\brainflow_emulator\freeeeg32_windows.py python %GITHUB_WORKSPACE%\python_package\examples\tests\brainflow_get_data.py --board-id 17 --serial-port
185185
shell: cmd
186+
- name: KnightBoard Windows Python Test
187+
run: python %GITHUB_WORKSPACE%\emulator\brainflow_emulator\knightboard_windows.py python %GITHUB_WORKSPACE%\python_package\examples\tests\brainflow_get_data.py --board-id 57 --serial-port
188+
shell: cmd
186189
# Signal Processing Testing
187190
- name: Serialization Rust Test
188191
run: |

csharp_package/brainflow/brainflow/board_controller_library.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ public enum BoardIds
115115
AAVAA_V3_BOARD = 53,
116116
EXPLORE_PLUS_8_CHAN_BOARD = 54,
117117
EXPLORE_PLUS_32_CHAN_BOARD = 55,
118-
PIEEG_BOARD = 56
118+
PIEEG_BOARD = 56,
119+
NEUROPAWN_KNIGHT_BOARD = 57
119120
};
120121

121122

docs/SupportedBoards.rst

+34
Original file line numberDiff line numberDiff line change
@@ -1305,3 +1305,37 @@ Supported platforms:
13051305
**Note**: Ensure that you have the necessary permissions to access the serial port on your operating system. For Unix-like systems, you may need to configure permissions for the serial port or run with sudo.
13061306
13071307
**To use this board you need to compile BrainFlow from the source code right on your Raspbery Pi device with flag --build-periphery(build.py) or with -DBUILD_PERIPHERY=ON(CMake) and install desired bindings using local libraries.**
1308+
1309+
NeuroPawn
1310+
--------
1311+
1312+
Knight Board
1313+
~~~~~~~~~~~~~
1314+
1315+
.. image:: https://drive.google.com/file/d/192dUfIXKmOqcTIBr7PYJJ8VUdWCuzeIv/view?usp=sharing
1316+
:width: 400px
1317+
:height: 225px
1318+
1319+
Visit us `here <https://www.neuropawn.tech/>`_
1320+
1321+
To create such board you need to specify the following board ID and fields of BrainFlowInputParams object:
1322+
1323+
- :code:`BoardIds.NEUROPAWN_KNIGHT_BOARD`
1324+
- :code:`serial_port`, e.g. COM3, /dev/tty.*
1325+
1326+
Initialization Example:
1327+
1328+
.. code-block:: python
1329+
1330+
params = BrainFlowInputParams()
1331+
params.serial_port = "COM3"
1332+
board = BoardShim(BoardIds.NEUROPAWN_KNIGHT_BOARD, params)
1333+
1334+
**On Unix-like systems you may need to configure permissions for serial port or run with sudo.**
1335+
1336+
Supported platforms:
1337+
1338+
- Windows
1339+
- Linux
1340+
- MacOS
1341+
- Devices like Raspberry Pi
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import logging
2+
import threading
3+
import time
4+
from random import randint
5+
6+
class Listener(threading.Thread):
7+
8+
def __init__(self, port, write, read):
9+
# for windows write and read are methods from Serial object, for linux - os.read/write it doesnt work otherwise
10+
threading.Thread.__init__(self)
11+
self.port = port
12+
self.writer_process = None
13+
self.write = write
14+
self.read = read
15+
self.need_stop = False
16+
17+
def run(self):
18+
self.writer_process = KnightBoardWriter(self.port, 0.005, self.write)
19+
self.writer_process.daemon = True
20+
self.writer_process.start()
21+
time.sleep(10)
22+
self.writer_process.need_data = False
23+
self.writer_process.join()
24+
25+
26+
class KnightBoardWriter(threading.Thread):
27+
28+
def __init__(self, port, delay, write):
29+
threading.Thread.__init__(self)
30+
self.port = port
31+
self.write = write
32+
self.delay = delay
33+
self.package_size = 21
34+
self.package_num = 0
35+
self.need_data = True
36+
37+
def run(self):
38+
while self.need_data:
39+
if self.package_num % 256 == 0:
40+
self.package_num = 0
41+
42+
package = list()
43+
package.append(0xA0)
44+
package.append(self.package_num)
45+
for i in range(2, self.package_size - 1):
46+
package.append(randint(0, 255))
47+
package.append(0xC0)
48+
logging.info(bytes(package))
49+
self.write(self.port, bytes(package))
50+
51+
self.package_num = self.package_num + 1
52+
time.sleep(self.delay)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import logging
2+
import os
3+
import pty
4+
import subprocess
5+
import sys
6+
7+
from brainflow_emulator.emulate_common import TestFailureError, log_multilines
8+
from brainflow_emulator.knightboard_emulator import Listener
9+
10+
11+
def write(port, data):
12+
return os.write(port, data)
13+
14+
15+
def read(port, num_bytes):
16+
return os.read(port, num_bytes)
17+
18+
19+
def get_ports_pty():
20+
master, slave = pty.openpty()
21+
s_name = os.ttyname(slave)
22+
return master, slave, s_name
23+
24+
25+
def test_serial(cmd_list, master, slave, s_name):
26+
listen_thread = Listener(master, write, read)
27+
listen_thread.daemon = True
28+
listen_thread.start()
29+
30+
cmd_to_run = cmd_list + [s_name]
31+
logging.info('Running %s' % ' '.join([str(x) for x in cmd_to_run]))
32+
process = subprocess.Popen(cmd_to_run, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
33+
stdout, stderr = process.communicate()
34+
35+
log_multilines(logging.info, stdout)
36+
log_multilines(logging.info, stderr)
37+
38+
if process.returncode != 0:
39+
raise TestFailureError('Test failed with exit code %s' % str(process.returncode), process.returncode)
40+
41+
return stdout, stderr
42+
43+
44+
def main(cmd_list):
45+
if not cmd_list:
46+
raise Exception('No command to execute')
47+
master, slave, s_name = get_ports_pty()
48+
test_serial(cmd_list, master, slave, s_name)
49+
50+
51+
if __name__ == '__main__':
52+
logging.basicConfig(level=logging.INFO)
53+
main(sys.argv[1:])
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import logging
2+
import os
3+
import subprocess
4+
import sys
5+
import time
6+
7+
import pkg_resources
8+
from brainflow_emulator.emulate_common import TestFailureError, log_multilines
9+
from brainflow_emulator.knightboard_emulator import Listener
10+
from serial import Serial
11+
12+
13+
def write(port, data):
14+
return port.write(data)
15+
16+
17+
def read(port, num_bytes):
18+
return port.read(num_bytes)
19+
20+
21+
def get_isntaller():
22+
return pkg_resources.resource_filename(__name__, os.path.join('com0com', 'setup_com0com_W7_x64_signed.exe'))
23+
24+
25+
def install_com0com():
26+
this_directory = os.path.abspath(os.path.dirname(__file__))
27+
directory = os.path.join(this_directory, 'com0com')
28+
if not os.path.exists(directory):
29+
os.makedirs(directory)
30+
cmds = [get_isntaller(), '/NCRC', '/S', '/D=%s' % directory]
31+
logging.info('running %s' % ' '.join(cmds))
32+
p = subprocess.Popen(cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
33+
out, err = p.communicate()
34+
if p.returncode != 0:
35+
logging.error('stdout is %s' % out)
36+
logging.error('stderr is %s' % err)
37+
raise Exception('com0com installation failure')
38+
logging.info('Sleeping a few second, it doesnt work in appveyour without it')
39+
time.sleep(10)
40+
return directory
41+
42+
43+
def get_ports_windows():
44+
directory = install_com0com()
45+
# remove ports from previous run if any
46+
p = subprocess.Popen([os.path.join(directory, 'setupc.exe'), 'remove', '0'],
47+
stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=directory)
48+
stdout, stderr = p.communicate()
49+
logging.info('remove stdout is %s' % stdout)
50+
logging.info('remove stderr is %s' % stderr)
51+
52+
m_name = 'COM14'
53+
s_name = 'COM15'
54+
55+
p = subprocess.Popen(
56+
[os.path.join(directory, 'setupc.exe'), 'install', 'PortName=%s' % m_name, 'PortName=%s' % s_name],
57+
stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=directory)
58+
stdout, stderr = p.communicate()
59+
logging.info('install stdout is %s' % stdout)
60+
logging.info('install stderr is %s' % stderr)
61+
62+
if p.returncode != 0:
63+
raise Exception('com0com failure')
64+
logging.info('Sleeping a few second, it doesnt work in appveyour without it')
65+
time.sleep(10)
66+
return m_name, s_name
67+
68+
69+
def test_serial(cmd_list, m_name, s_name):
70+
master = Serial('\\\\.\\%s' % m_name, timeout=0)
71+
listen_thread = Listener(master, write, read)
72+
listen_thread.daemon = True
73+
listen_thread.start()
74+
75+
cmd_to_run = cmd_list + [s_name]
76+
logging.info('Running %s' % ' '.join([str(x) for x in cmd_to_run]))
77+
process = subprocess.Popen(cmd_to_run, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
78+
stdout, stderr = process.communicate()
79+
80+
log_multilines(logging.info, stdout)
81+
log_multilines(logging.info, stderr)
82+
83+
master.close()
84+
if process.returncode != 0:
85+
raise TestFailureError('Test failed with exit code %s' % str(process.returncode), process.returncode)
86+
87+
return stdout, stderr
88+
89+
90+
def main(cmd_list):
91+
if not cmd_list:
92+
raise Exception('No command to execute')
93+
94+
m_name, s_name = get_ports_windows()
95+
test_serial(cmd_list, m_name, s_name)
96+
97+
98+
if __name__ == '__main__':
99+
logging.basicConfig(level=logging.INFO)
100+
main(sys.argv[1:])

java_package/brainflow/src/main/java/brainflow/BoardIds.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ public enum BoardIds
6565
AAVAA_V3_BOARD(53),
6666
EXPLORE_PLUS_8_CHAN_BOARD(54),
6767
EXPLORE_PLUS_32_CHAN_BOARD(55),
68-
PIEEG_BOARD(56);
68+
PIEEG_BOARD(56),
69+
NEUROPAWN_KNIGHT_BOARD(57);
6970

7071
private final int board_id;
7172
private static final Map<Integer, BoardIds> bi_map = new HashMap<Integer, BoardIds> ();

julia_package/brainflow/src/board_shim.jl

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export BrainFlowInputParams
6161
EXPLORE_PLUS_8_CHAN_BOARD = 54
6262
EXPLORE_PLUS_32_CHAN_BOARD = 55
6363
PIEEG_BOARD = 56
64+
NEUROPAWN_KNIGHT_BOARD = 57
6465

6566
end
6667

matlab_package/brainflow/BoardIds.m

+1
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,6 @@
5959
EXPLORE_PLUS_8_CHAN_BOARD(54)
6060
EXPLORE_PLUS_32_CHAN_BOARD(55)
6161
PIEEG_BOARD(56)
62+
NEUROPAWN_KNIGHT_BOARD(57)
6263
end
6364
end

nodejs_package/brainflow/brainflow.types.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ export enum BoardIds {
6868
ANT_NEURO_EE_511_BOARD = 51,
6969
EXPLORE_PLUS_8_CHAN_BOARD = 54,
7070
EXPLORE_PLUS_32_CHAN_BOARD = 55,
71-
PIEEG_BOARD = 56
71+
PIEEG_BOARD = 56,
72+
NEUROPAWN_KNIGHT_BOARD = 57
7273
}
7374

7475
export enum IpProtocolTypes {

python_package/brainflow/board_shim.py

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class BoardIds(enum.IntEnum):
7474
EXPLORE_PLUS_8_CHAN_BOARD = 54 #:
7575
EXPLORE_PLUS_32_CHAN_BOARD = 55 #:
7676
PIEEG_BOARD = 56 #:
77+
NEUROPAWN_KNIGHT_BOARD = 57 #:
7778

7879

7980
class IpProtocolTypes(enum.IntEnum):

rust_package/brainflow/src/ffi/constants.rs

+1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ pub enum BoardIds {
9494
AavaaV3Board = 53,
9595
ExplorePlus8ChanBoard = 54,
9696
ExplorePlus32ChanBoard = 55,
97+
NeuroPawnKnightBoard = 57
9798
}
9899
#[repr(i32)]
99100
#[derive(FromPrimitive, ToPrimitive, Debug, Copy, Clone, Hash, PartialEq, Eq)]

src/board_controller/board_controller.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
#include "ganglion_wifi.h"
4646
#include "gforce_dual.h"
4747
#include "gforce_pro.h"
48+
#include "json.hpp"
49+
#include "knight.h"
4850
#include "muse.h"
4951
#include "muse_bled.h"
5052
#include "notion_osc.h"
@@ -55,8 +57,6 @@
5557
#include "synthetic_board.h"
5658
#include "unicorn_board.h"
5759

58-
#include "json.hpp"
59-
6060
using json = nlohmann::json;
6161

6262

@@ -281,6 +281,10 @@ int prepare_session (int board_id, const char *json_brainflow_input_params)
281281
case BoardIds::PIEEG_BOARD:
282282
board = std::shared_ptr<Board> (new PIEEGBoard (board_id, params));
283283
break;
284+
case BoardIds::NEUROPAWN_KNIGHT_BOARD:
285+
board =
286+
std::shared_ptr<Board> (new Knight ((int)BoardIds::NEUROPAWN_KNIGHT_BOARD, params));
287+
break;
284288
default:
285289
return (int)BrainFlowExitCodes::UNSUPPORTED_BOARD_ERROR;
286290
}

src/board_controller/brainflow_boards.cpp

+13-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ BrainFlowBoards::BrainFlowBoards()
7474
{"53", json::object()},
7575
{"54", json::object()},
7676
{"55", json::object()},
77-
{"56", json::object()}
77+
{"56", json::object()},
78+
{"57", json::object()}
7879
}
7980
}};
8081

@@ -1095,6 +1096,17 @@ BrainFlowBoards::BrainFlowBoards()
10951096
{"eeg_channels", {1, 2, 3, 4, 5, 6, 7, 8}},
10961097
{"eeg_names", "Fp1,Fp2,C3,C4,P7,P8,O1,O2"}
10971098
};
1099+
brainflow_boards_json["boards"]["57"]["default"] =
1100+
{
1101+
{"name", "Knight"},
1102+
{"sampling_rate", 125},
1103+
{"timestamp_channel", 11},
1104+
{"marker_channel",12},
1105+
{"package_num_channel", 0},
1106+
{"num_rows", 13},
1107+
{"eeg_channels", {1, 2, 3, 4, 5, 6, 7, 8}},
1108+
{"other_channels", {9, 10}}
1109+
};
10981110
}
10991111

11001112
BrainFlowBoards boards_struct;

0 commit comments

Comments
 (0)