47
47
previous_sample_number = None # Store the previous sample number for detecting missing samples
48
48
missing_samples = 0 # Count of missing samples due to packet loss
49
49
buffer = bytearray () # Buffer for storing incoming raw data from Arduino
50
- data = np .zeros ((6 , 2000 )) # 2D array to store data for real-time plotting (6 channels, 2000 data points)
51
50
samples_per_second = 0 # Number of samples received per second
52
51
retry_limit = 4
53
52
54
53
# Initialize gloabal variables for Arduino Board
55
54
board = "" # Variable for Connected Arduino Board
56
- supported_boards = {"UNO-R3" :250 , "UNO-R4" :500 } #Supported boards and their sampling rate
55
+ supported_boards = {
56
+ "UNO-R3" : {"sampling_rate" : 250 , "Num_channels" : 6 },
57
+ "UNO-CLONE" : {"sampling_rate" : 250 , "Num_channels" : 6 },
58
+ "UNO-R4" : {"sampling_rate" : 500 , "Num_channels" : 6 },
59
+ "RPI-PICO-RP2040" : {"sampling_rate" : 500 , "Num_channels" : 3 },
60
+ }
57
61
58
62
# Initialize gloabal variables for Incoming Data
59
- PACKET_LENGTH = 16 # Expected length of each data packet
60
63
SYNC_BYTE1 = 0xc7 # First byte of sync marker
61
64
SYNC_BYTE2 = 0x7c # Second byte of sync marker
62
65
END_BYTE = 0x01 # End byte marker
63
- NUM_CHANNELS = 6 #Number of Channels being received
64
66
HEADER_LENGTH = 3 #Length of the Packet Header
65
67
66
68
## Initialize gloabal variables for Output
69
71
csv_filename = None # Store CSV filename
70
72
csv_file = None
71
73
ser = None
74
+ packet_length = None
75
+ num_channels = None
72
76
73
77
def connect_hardware (port , baudrate , timeout = 1 ):
74
78
try :
@@ -80,9 +84,13 @@ def connect_hardware(port, baudrate, timeout=1):
80
84
response = ser .readline ().strip ().decode () # Try reading from the port
81
85
retry_counter += 1
82
86
if response in supported_boards : # If response is received, assume it's the Arduino
83
- global board
87
+ global board , sampling_rate , data , num_channels , packet_length
84
88
board = response # Set board type
85
89
print (f"{ response } detected at { port } " ) # Notify the user
90
+ sampling_rate = supported_boards [board ]["sampling_rate" ]
91
+ num_channels = supported_boards [board ]["Num_channels" ]
92
+ packet_length = (2 * num_channels ) + HEADER_LENGTH + 1
93
+ data = np .zeros ((num_channels , 2000 )) # 2D array to store data for real-time plotting (num_channels, 2000 data points)
86
94
if ser is not None :
87
95
return ser # Return the port name
88
96
ser .close () # Close the port if no response
@@ -114,24 +122,24 @@ def send_command(ser, command):
114
122
def read_arduino_data (ser , csv_writer = None , inverted = False ):
115
123
global total_packet_count , cumulative_packet_count , previous_sample_number , missing_samples , buffer , data
116
124
117
- max_value = 2 ** 14 if board == "UNO-R4 " else 2 ** 10
125
+ max_value = 2 * 10 if board == "UNO-R3 " else 2 * 14
118
126
min_value = 0
119
127
mid_value = (max_value - 1 ) / 2
120
128
121
129
raw_data = ser .read (ser .in_waiting or 1 ) # Read available data from the serial port
122
130
if raw_data == b'' :
123
131
send_command (ser , 'START' )
124
132
buffer .extend (raw_data ) # Add received data to the buffer
125
- while len (buffer ) >= PACKET_LENGTH : # Continue processing if the buffer contains at least one full packet
133
+ while len (buffer ) >= packet_length : # Continue processing if the buffer contains at least one full packet
126
134
sync_index = buffer .find (bytes ([SYNC_BYTE1 , SYNC_BYTE2 ])) # Search for the sync marker
127
135
128
136
if sync_index == - 1 : # If sync marker not found, clear the buffer
129
137
buffer .clear ()
130
138
continue
131
139
132
- if len (buffer ) >= sync_index + PACKET_LENGTH : # Check if a full packet is available
133
- packet = buffer [sync_index :sync_index + PACKET_LENGTH ] # Extract the packet
134
- if len (packet ) == PACKET_LENGTH and packet [0 ] == SYNC_BYTE1 and packet [1 ] == SYNC_BYTE2 and packet [- 1 ] == END_BYTE :
140
+ if len (buffer ) >= sync_index + packet_length : # Check if a full packet is available
141
+ packet = buffer [sync_index :sync_index + packet_length ] # Extract the packet
142
+ if len (packet ) == packet_length and packet [0 ] == SYNC_BYTE1 and packet [1 ] == SYNC_BYTE2 and packet [- 1 ] == END_BYTE :
135
143
if (start_time is None ):
136
144
start_timer () # Start timers for logging
137
145
@@ -150,7 +158,7 @@ def read_arduino_data(ser, csv_writer=None, inverted=False):
150
158
151
159
# Extract channel data (6 channels, 2 bytes per channel)
152
160
channel_data = []
153
- for channel in range (NUM_CHANNELS ): # Loop through channel data bytes
161
+ for channel in range (num_channels ): # Loop through channel data bytes
154
162
high_byte = packet [2 * channel + HEADER_LENGTH ]
155
163
low_byte = packet [2 * channel + HEADER_LENGTH + 1 ]
156
164
value = (high_byte << 8 ) | low_byte # Combine high and low bytes
@@ -174,7 +182,7 @@ def read_arduino_data(ser, csv_writer=None, inverted=False):
174
182
data = np .roll (data , - 1 , axis = 1 ) # Shift data to the left
175
183
data [:, - 1 ] = channel_data # Add new channel data to the right end of the array
176
184
177
- del buffer [:sync_index + PACKET_LENGTH ] # Remove the processed packet from the buffer
185
+ del buffer [:sync_index + packet_length ] # Remove the processed packet from the buffer
178
186
else :
179
187
del buffer [:sync_index + 1 ] # If the packet is invalid, remove only the sync marker
180
188
@@ -202,7 +210,7 @@ def log_ten_minute_data(verbose=False):
202
210
print (f"Total data count after 10 minutes: { cumulative_packet_count } " ) # Print cumulative data count
203
211
sampling_rate = cumulative_packet_count / (10 * 60 ) # Calculate sampling rate
204
212
print (f"Sampling rate: { sampling_rate :.2f} samples/second" ) # Print sampling rate
205
- expected_sampling_rate = supported_boards [board ] # Expected sampling rate
213
+ expected_sampling_rate = supported_boards [board ][ "sampling_rate" ] # Expected sampling rate
206
214
drift = ((sampling_rate - expected_sampling_rate ) / expected_sampling_rate ) * 3600 # Calculate drift
207
215
print (f"Drift: { drift :.2f} seconds/hour" ) # Print drift
208
216
cumulative_packet_count = 0 # Reset cumulative packet count
@@ -217,7 +225,7 @@ def parse_data(ser, lsl_flag=False, csv_flag=False, verbose=False, run_time=None
217
225
218
226
# Start LSL streaming if requested
219
227
if lsl_flag :
220
- lsl_stream_info = StreamInfo ('BioAmpDataStream' , 'EXG' , 6 , supported_boards [board ], 'float32' , 'UpsideDownLabs' ) # Define LSL stream info
228
+ lsl_stream_info = StreamInfo ('BioAmpDataStream' , 'EXG' , num_channels , supported_boards [board ][ "sampling_rate" ], 'float32' , 'UpsideDownLabs' ) # Define LSL stream info
221
229
lsl_outlet = StreamOutlet (lsl_stream_info ) # Create LSL outlet
222
230
print ("LSL stream started" ) # Notify user
223
231
@@ -230,7 +238,7 @@ def parse_data(ser, lsl_flag=False, csv_flag=False, verbose=False, run_time=None
230
238
if csv_file :
231
239
csv_writer = csv .writer (csv_file ) # Create CSV writer
232
240
csv_writer .writerow ([f"Arduino Board: { board } " ])
233
- csv_writer .writerow ([f"Sampling Rate (samples per second): { supported_boards [board ]} " ])
241
+ csv_writer .writerow ([f"Sampling Rate (samples per second): { supported_boards [board ][ "sampling_rate" ] } " ])
234
242
csv_writer .writerow ([]) # Blank row for separation
235
243
csv_writer .writerow (['Counter' , 'Channel1' , 'Channel2' , 'Channel3' , 'Channel4' , 'Channel5' , 'Channel6' ]) # Write header
236
244
0 commit comments