From 355769e2336271941111678d6932dcfec58a4061 Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Sat, 25 Jan 2025 16:19:45 +0530 Subject: [PATCH 01/25] Updated requirements --- app_requirements.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app_requirements.txt b/app_requirements.txt index 9741be3..a589385 100644 --- a/app_requirements.txt +++ b/app_requirements.txt @@ -7,4 +7,6 @@ neurokit2==0.2.10 plotly==5.24.1 pandas==2.2.3 tk==0.1.0 -PyAutoGUI==0.9.54 \ No newline at end of file +PyAutoGUI==0.9.54 +Flask==3.1.0 +psutil==6.1.1 \ No newline at end of file From e5a59a8b030102eb38eabd0367191b3f1ea7675c Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Sat, 25 Jan 2025 18:36:54 +0530 Subject: [PATCH 02/25] Updated script to prioritize baud rate from argparse; defaults to trying 230400 first, then 115200 if not provided. --- chords.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/chords.py b/chords.py index 3e940de..cce50c1 100644 --- a/chords.py +++ b/chords.py @@ -55,9 +55,11 @@ supported_boards = { "UNO-R3": {"sampling_rate": 250, "Num_channels": 6}, "UNO-CLONE": {"sampling_rate": 250, "Num_channels": 6}, # Baud Rate 115200 + "GENUINO-UNO": {"sampling_rate": 250, "Num_channels": 6}, "UNO-R4": {"sampling_rate": 500, "Num_channels": 6}, "RPI-PICO-RP2040": {"sampling_rate": 500, "Num_channels": 3}, "NANO-CLONE": {"sampling_rate": 250, "Num_channels": 8}, # Baud Rate 115200 + "BLACK-PILL": {"sampling_rate": 500, "Num_channels": 10}, } # Initialize gloabal variables for Incoming Data @@ -80,14 +82,18 @@ def connect_hardware(port, baudrate, timeout=1): ser = serial.Serial(port, baudrate=baudrate, timeout=timeout) # Try opening the port response = None retry_counter = 0 - while response is None or retry_counter < retry_limit: + while response not in supported_boards and retry_counter < retry_limit: ser.write(b'WHORU\n') # Check board type - response = ser.readline().strip().decode() # Try reading from the port + try: + response = ser.readline().strip().decode() # Attempt to decode the response + except UnicodeDecodeError as e: + print(f"Decode error: {e}. Ignoring this response.") + response = None retry_counter += 1 if response in supported_boards: # If response is received, assume it's the Arduino global board, sampling_rate, data, num_channels, packet_length board = response # Set board type - print(f"{response} detected at {port}") # Notify the user + print(f"{response} detected at {port} with baudrate {baudrate}.") # Notify the user sampling_rate = supported_boards[board]["sampling_rate"] num_channels = supported_boards[board]["Num_channels"] packet_length = (2 * num_channels) + HEADER_LENGTH + 1 @@ -97,17 +103,19 @@ def connect_hardware(port, baudrate, timeout=1): ser.close() # Close the port if no response except (OSError, serial.SerialException): # Handle exceptions if the port can't be opened pass - print("Unable to connect to any hardware!") # Notify if no Arduino is found + print(f"Unable to connect to any hardware at baudrate {baudrate}") # Notify if no Arduino is found return None # Return None if not found -# Function to automatically detect the Arduino's serial port -def detect_hardware(baudrate, timeout=1): +def detect_hardware(baudrate=None, timeout=1): ports = serial.tools.list_ports.comports() # List available serial ports ser = None + baudrates = [baudrate] if baudrate else [230400, 115200] for port in ports: # Iterate through each port - ser = connect_hardware(port.device, baudrate) - if ser is not None: - return ser + for baud_rate in baudrates: # Iterate through all baud rates + print(f"Trying {port.device} at Baudrate {baud_rate}...") + ser = connect_hardware(port.device, baud_rate, timeout) + if ser is not None: + return ser print("Unable to detect hardware!") # Notify if no Arduino is found return None # Return None if not found @@ -319,7 +327,7 @@ def main(): global verbose,ser parser = argparse.ArgumentParser(description="Upside Down Labs - Chords-Python Tool",allow_abbrev = False) # Create argument parser parser.add_argument('-p', '--port', type=str, help="Specify the COM port") # Port argument - parser.add_argument('-b', '--baudrate', type=int, default=230400, help="Set baud rate for the serial communication") # Baud rate + parser.add_argument('-b', '--baudrate', type=int, help="Set baud rate for the serial communication") # Baud rate parser.add_argument('--csv', action='store_true', help="Create and write to a CSV file") # CSV logging flag parser.add_argument('--lsl', action='store_true', help="Start LSL stream") # LSL streaming flag parser.add_argument('-v', '--verbose', action='store_true', help="Enable verbose output with statistical data") # Verbose flag @@ -337,6 +345,8 @@ def main(): parser.print_help() # Print help if no options are selected return + baudrate = args.baudrate # Get baud rate from arguments + if args.port: print("trying to connect to port:", args.port) ser = connect_hardware(port=args.port, baudrate=args.baudrate) From 2ede52f1abca9cedc11cf349ea79531ad48661e7 Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Sat, 25 Jan 2025 18:39:05 +0530 Subject: [PATCH 03/25] Implemented Moving Window concept: Buffer updates by adding 50 new samples and removing the previous 50 samples before calculating FFT. --- ffteeg.py | 47 ++++++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/ffteeg.py b/ffteeg.py index 60b9ef0..2b700c0 100644 --- a/ffteeg.py +++ b/ffteeg.py @@ -27,7 +27,7 @@ def __init__(self): self.eeg_plot_widget.showGrid(x=True, y=True) self.eeg_plot_widget.setLabel('bottom', 'EEG Plot') self.eeg_plot_widget.setYRange(-5000, 5000, padding=0) - self.eeg_plot_widget.setXRange(0, 2, padding=0) + self.eeg_plot_widget.setXRange(0, 4, padding=0) self.eeg_plot_widget.setMouseEnabled(x=False, y=True) # Disable zoom self.main_layout.addWidget(self.eeg_plot_widget) @@ -39,9 +39,9 @@ def __init__(self): self.fft_plot.setBackground('w') self.fft_plot.showGrid(x=True, y=True) self.fft_plot.setLabel('bottom', 'FFT') - # self.fft_plot.setYRange(0, 25000, padding=0) + # self.fft_plot.setYRange(0, 500, padding=0) self.fft_plot.setXRange(0, 50, padding=0) # Set x-axis to 0 to 50 Hz - self.fft_plot.setMouseEnabled(x=False, y=False) # Disable zoom + # self.fft_plot.setMouseEnabled(x=False, y=False) # Disable zoom self.fft_plot.setAutoVisible(y=True) # Allow y-axis to autoscale self.bottom_layout.addWidget(self.fft_plot) @@ -74,13 +74,8 @@ def __init__(self): print(f"Sampling rate: {self.sampling_rate} Hz") # Data and Buffers - self.one_second_buffer = deque(maxlen=self.sampling_rate) # 1-second buffer - self.buffer_size = self.sampling_rate * 10 - self.moving_window_size = self.sampling_rate * 2 # 2-second window - - self.eeg_data = np.zeros(self.buffer_size) - self.time_data = np.linspace(0, 10, self.buffer_size) - self.current_index = 0 + self.eeg_data = deque(maxlen=500) # Initialize moving window with 500 samples + self.moving_window = deque(maxlen=500) # 500 samples for FFT and power calculation (sliding window) self.b_notch, self.a_notch = iirnotch(50, 30, self.sampling_rate) self.b_band, self.a_band = butter(4, [0.5 / (self.sampling_rate / 2), 48.0 / (self.sampling_rate / 2)], btype='band') @@ -93,7 +88,7 @@ def __init__(self): self.timer.timeout.connect(self.update_plot) self.timer.start(20) - self.eeg_curve = self.eeg_plot_widget.plot(self.time_data, self.eeg_data, pen=pg.mkPen('b', width=1)) #EEG Colour is blue + self.eeg_curve = self.eeg_plot_widget.plot(pen=pg.mkPen('b', width=1)) self.fft_curve = self.fft_plot.plot(pen=pg.mkPen('r', width=1)) # FFT Colour is red def update_plot(self): @@ -106,29 +101,23 @@ def update_plot(self): band_filtered, self.zi_band = lfilter(self.b_band, self.a_band, notch_filtered, zi=self.zi_band) band_filtered = band_filtered[-1] # Get the current filtered point - # Update the EEG plot - self.eeg_data[self.current_index] = band_filtered - self.current_index = (self.current_index + 1) % self.buffer_size + # Update EEG data buffer + self.eeg_data.append(band_filtered) - if self.current_index == 0: - plot_data = self.eeg_data + if len(self.moving_window) < 500: + self.moving_window.append(band_filtered) else: - plot_data = np.concatenate((self.eeg_data[self.current_index:], self.eeg_data[:self.current_index])) + self.process_fft_and_brainpower() - recent_data = plot_data[-self.moving_window_size:] - recent_time = np.linspace(0, len(recent_data) / self.sampling_rate, len(recent_data)) - self.eeg_curve.setData(recent_time, recent_data) + self.moving_window = deque(list(self.moving_window)[50:] + [band_filtered], maxlen=500) - self.one_second_buffer.append(band_filtered) # Add the filtered point to the 1-second buffer - if len(self.one_second_buffer) == self.sampling_rate: # Process FFT and brainwave power - self.process_fft_and_brainpower() - self.one_second_buffer.clear() - - def process_fft_and_brainpower(self): - window = np.hanning(len(self.one_second_buffer)) # Apply Hanning window to the buffer - buffer_windowed = np.array(self.one_second_buffer) * window + plot_data = np.array(self.eeg_data) + time_axis = np.linspace(0, 4, len(plot_data)) + self.eeg_curve.setData(time_axis, plot_data) - # Perform FFT + def process_fft_and_brainpower(self): + window = np.hanning(len(self.moving_window)) + buffer_windowed = np.array(self.moving_window) * window fft_result = np.abs(fft(buffer_windowed))[:len(buffer_windowed) // 2] fft_result /= len(buffer_windowed) freqs = np.fft.fftfreq(len(buffer_windowed), 1 / self.sampling_rate)[:len(buffer_windowed) // 2] From eb8ba6100a127a7f6f5e4a86420c1c5c2aa67a1e Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Sat, 25 Jan 2025 18:40:19 +0530 Subject: [PATCH 04/25] beetle game- requires testing --- test/ball.py | 100 ------------------------------------ test/beetle.jpg | Bin 0 -> 6495 bytes test/beetle.py | 131 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 100 deletions(-) delete mode 100644 test/ball.py create mode 100644 test/beetle.jpg create mode 100644 test/beetle.py diff --git a/test/ball.py b/test/ball.py deleted file mode 100644 index 28ac5bf..0000000 --- a/test/ball.py +++ /dev/null @@ -1,100 +0,0 @@ -import pygame -import pylsl -import numpy as np -import time -from pylsl import StreamInlet, resolve_stream - -pygame.init() - -# Screen setup -screen = pygame.display.set_mode((800, 600)) -pygame.display.set_caption('Focus Game') - -# Ball properties -ball_x, ball_y = 400, 550 -ball_radius = 20 -focus_speed_upward = 8 # Speed when moving upward -focus_speed_downward = 4 # Speed when moving downward -focus_timeout = 2 # Time in seconds to stabilize focus -focus_threshold = 1000000000 # Threshold for beta power - -# LSL stream setup -streams = resolve_stream('name', 'BioAmpDataStream') -if not streams: - print("No LSL stream found!") - pygame.quit() - exit() - -inlet = StreamInlet(streams[0]) -print(" LSL Stream Started") - -# Buffer for EEG data and Focus tracking variables -buffer = [] -buffer_size = 500 -focus_timer = 0 -last_focus_time = time.time() -last_time = time.time() - -def calculate_focus_level(eeg_data): - fft_result = np.fft.fft(eeg_data) - freqs = np.fft.fftfreq(len(eeg_data), 1 / 500) - positive_freqs = freqs[:len(freqs) // 2] - fft_magnitude = np.abs(fft_result[:len(freqs) // 2]) - - # Extract beta band power (13-30 Hz) - beta_band = np.where((positive_freqs >= 13) & (positive_freqs <= 30)) - beta_power = np.sum(fft_magnitude[beta_band] ** 2) - print(f"Beta Power: {beta_power}") - - return beta_power - -def update_ball_position(focus_level, is_focus_stable): - global ball_y - if is_focus_stable: - ball_y = max(0, ball_y - focus_speed_upward) # Move upward, bounded by top - else: - ball_y = min(600 - ball_radius, ball_y + focus_speed_downward) # Move downward, bounded by bottom - -# Game loop -running = True -while running: - try: - for event in pygame.event.get(): - if event.type == pygame.QUIT: - running = False - - # Read EEG data from the LSL stream - sample, _ = inlet.pull_sample(timeout=0.1) - if sample: - buffer.append(sample[:1]) # Append new data to buffer - - current_time = time.time() - if current_time - last_time >= 1: - last_time = current_time - release_count = int(len(buffer) * 0.2) # Remove oldest 20% - buffer = buffer[release_count:] # Trim buffer - - if len(buffer) >= buffer_size: # Process EEG data when the buffer is full - eeg_data = np.array(buffer)[:, 0] # Use only the first channel - buffer = [] # Clear the buffer - - focus_level = calculate_focus_level(eeg_data) - - if focus_level > focus_threshold: - focus_timer = min(focus_timeout, focus_timer + (current_time - last_focus_time)) - else: - focus_timer = max(0, focus_timer - (current_time - last_focus_time)) - - is_focus_stable = focus_timer >= focus_timeout - update_ball_position(focus_level, is_focus_stable) - last_focus_time = current_time - - screen.fill((0, 0, 0)) - pygame.draw.circle(screen, (255, 0, 0), (ball_x, ball_y), ball_radius) # Draw the ball - pygame.display.update() - - except KeyboardInterrupt: - print("Exiting gracefully...") - running = False - -pygame.quit() \ No newline at end of file diff --git a/test/beetle.jpg b/test/beetle.jpg new file mode 100644 index 0000000000000000000000000000000000000000..90e5f28650f2e199e630fb86b9bd321c57ff17fe GIT binary patch literal 6495 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 z z(uVQ#E>G@pJ%$tnG+0Evu-#r!kwU2VNvWuIHO;Qy0yL-dl*h(mB!3J9bUetWpCim{oHHca+UIZQsr(P(j&)cR5SHizep z0f0#BAA|`4;l2!mv?wsmU(}8LRKWrZj}Ki_hwD{ft}U3PhcyVreXqy^jswcG{%Jya zROEW=ztx1(aj)bcEkwgDw>WUmKQ1z|n?Ynf2Autn;^{0vlmir*&}U3i@#wpi;}?+6 zGOOPQ=2~k+r};K9vW!;5s#;TrQ0+&2l&gnJE3Xp6f^~V*?%2&`=^N}ZM-Xk1+udzK zUa1WrzJVK3;2CVp4YkaTkM?!KEwy|DbCC!n+un&35)+o1jXI#bf@4OVHxj89fBhIh zU6vkB6wy$QO|60Y zK>NA?z53`cKgVqs&b#`)`IyQQPMr=Y*4!a12BJSM9zX#lT8{|;Jn{dW9Eu!tma$bi zF>9dK9~OQG#N{5SqO<-YxGP2=D+6{?a*Cn#!KZ-56W%KIc1V-^=V{Kbb7ZwfYoEIg zru%sKY+s~o*C)vDj)>%!wI*F5Q5ysigtAbJJIW4_W3&1X#3yl6YRYk5&4OiF0r~%T zCwAO80s!Z~#6UlXHK?)ga~SkLG_T`7$4*Gh@rSqmfv$4lny)@9(82$88+1GSKWk3! AI{*Lx literal 0 HcmV?d00001 diff --git a/test/beetle.py b/test/beetle.py new file mode 100644 index 0000000..cbf91aa --- /dev/null +++ b/test/beetle.py @@ -0,0 +1,131 @@ +import pygame +import pylsl +import numpy as np +import time +from pylsl import StreamInlet, resolve_stream +from scipy.signal import iirnotch, butter, lfilter +import math + +pygame.init() + +# Screen setup +screen = pygame.display.set_mode((800, 600)) +pygame.display.set_caption('Beetle Game') + +# Beetle properties +beetle_x, beetle_y = 380, 530 +focus_speed_upward = 8 # Speed when moving upward +focus_speed_downward = 4 # Speed when moving downward +focus_timeout = 2 # Time in seconds to stabilize focus +focus_threshold = 5 # Threshold for beta power + +# LSL stream setup +streams = resolve_stream('name', 'BioAmpDataStream') +if not streams: + print("No LSL stream found!") + pygame.quit() + exit() + +inlet = StreamInlet(streams[0]) +print("LSL Stream Started") +sampling_rate = int(inlet.info().nominal_srate()) +print(f"Sampling rate: {sampling_rate} Hz") + +b_notch, a_notch = iirnotch(50.0 / (500 / 2), 30.0) +b_band, a_band = butter(4, [0.5/ (sampling_rate / 2), 48.0 / (sampling_rate / 2)], btype='band') + +# Buffer for EEG data and Focus tracking variables +buffer = [] +buffer_size = 500 +focus_timer = 0 +last_focus_time = time.time() +last_time = time.time() + +# Load the beetle image +beetle_image = pygame.image.load('beetle.jpg') +beetle_image = pygame.transform.scale(beetle_image, (80, 80)) + +# Function to apply filters +def apply_filters(eeg_point): + filtered = lfilter(b_notch, a_notch, [eeg_point]) + filtered_point = lfilter(b_band, a_band, filtered) + return filtered_point[0] + +def calculate_focus_level(eeg_data, sampling_rate=500): + window = np.hanning(len(eeg_data)) # Apply a Hanning window + eeg_data_windowed = eeg_data * window + fft_data = np.abs(np.fft.fft(eeg_data_windowed))[:len(eeg_data_windowed) // 2] + fft_data /= len(eeg_data_windowed) + freqs = np.fft.fftfreq(len(eeg_data_windowed), d=1 / sampling_rate)[:len(eeg_data_windowed) // 2] + + # Compute power in different bands + delta_power = math.sqrt(np.sum((fft_data[(freqs >= 0.5) & (freqs <= 4)]) ** 2)) + theta_power = math.sqrt(np.sum((fft_data[(freqs >= 4) & (freqs <= 8)]) ** 2)) + alpha_power = math.sqrt(np.sum((fft_data[(freqs >= 8) & (freqs <= 13)]) ** 2)) + beta_power = math.sqrt(np.sum((fft_data[(freqs >= 13) & (freqs <= 30)]) ** 2)) + gamma_power = math.sqrt(np.sum((fft_data[(freqs >= 30) & (freqs <= 45)]) ** 2)) + + power = (beta_power + gamma_power) / (theta_power + alpha_power) + print(power) + return power + +def update_beetle_position(focus_level, is_focus_stable): + global beetle_y + if is_focus_stable: + beetle_y = max(10 + beetle_image.get_height() // 2, beetle_y - focus_speed_upward) # Prevent moving above border + else: + beetle_y = min(580 - beetle_image.get_height() // 2, beetle_y + focus_speed_downward) # Prevent moving below border + +# Game loop +running = True +while running: + try: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + + # Read EEG data from the LSL stream + sample, _ = inlet.pull_sample(timeout=0.1) + if sample: + filtered_sample = apply_filters(sample[0]) + buffer.append(filtered_sample) + + current_time = time.time() + if current_time - last_time >= 1: + last_time = current_time + release_count = int(len(buffer) * 0.2) # Remove oldest 20% + buffer = buffer[release_count:] # Trim buffer + + if len(buffer) >= buffer_size: # Process EEG data when the buffer is full + eeg_data = np.array(buffer) # Use filtered data + buffer = [] # Clear the buffer + + focus_level = calculate_focus_level(eeg_data) + + if focus_level > focus_threshold: + focus_timer = min(focus_timeout, focus_timer + (current_time - last_focus_time)) + is_focus_stable = focus_timer >= focus_timeout + update_beetle_position(focus_level, is_focus_stable) + + elif 7 <= focus_level <= 8: # No movement of the beetle + print("Beetle remains stationary.") + + else: + focus_timer = max(0, focus_timer - (current_time - last_focus_time)) + is_focus_stable = focus_timer >= focus_timeout + update_beetle_position(focus_level, is_focus_stable) + + last_focus_time = current_time + + # Clear the screen and draw the beetle + screen.fill("#FFFFFF") + pygame.draw.rect(screen, (0, 0, 0), (10, 10, 780, 580), 5) # Draw border + screen.blit(beetle_image, (beetle_x - beetle_image.get_width() // 2, beetle_y - beetle_image.get_height() // 2)) + + pygame.display.update() + + except KeyboardInterrupt: + print("Exiting gracefully...") + running = False + +pygame.quit() \ No newline at end of file From 6d35d0e30f1b27fe493173e3d7bca2cae8c7df7e Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Tue, 28 Jan 2025 11:01:04 +0530 Subject: [PATCH 05/25] Remove unused variables --- chords.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/chords.py b/chords.py index cce50c1..8e1f074 100644 --- a/chords.py +++ b/chords.py @@ -345,8 +345,6 @@ def main(): parser.print_help() # Print help if no options are selected return - baudrate = args.baudrate # Get baud rate from arguments - if args.port: print("trying to connect to port:", args.port) ser = connect_hardware(port=args.port, baudrate=args.baudrate) From 81261c2e2df4169d1af6bf62043ab545f15809cd Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Tue, 28 Jan 2025 13:52:04 +0530 Subject: [PATCH 06/25] Readme Updated --- README.md | 52 ++++++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 73a863b..ec17947 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ To use the script, run it from the command line with various options: ### Options - `-p`, `--port` : Specify the serial port to use (e.g., COM5, /dev/ttyUSB0). -- `-b`, `--baudrate` : Set the baud rate for serial communication (default is 230400). +- `-b`, `--baudrate` : Set the baud rate for serial communication. The script will first attempt to use 230400, and if that fails, it will automatically fallback to 115200. - `--csv`: Enable CSV logging. Data will be saved to a timestamped file. - `--lsl`: Enable LSL streaming. Sends data to an LSL outlet. - `-v`, `--verbose`: Enable verbose output with detailed statistics and error reporting. @@ -74,30 +74,8 @@ To use the script, run it from the command line with various options: - **Sampling Rate**: `UNO-R3 : 250 Hz` , `UNO-R4 : 500 Hz` - **Data Format**: `float32` - -### Script Functions - -`auto_detect_arduino(baudrate, timeout=1)`: Detects an Arduino connected via serial port. Returns the port name if detected. - -`read_arduino_data(ser, csv_writer=None)`: Reads and processes data from the Arduino. Writes data to CSV and/or LSL stream if enabled. - -`start_timer()`: Initializes timers for 1-second and 10-minute intervals. - -`log_one_second_data(verbose=False)`: Logs and resets data for the 1-second interval. - -`log_ten_minute_data(verbose=False)`: Logs data and statistics for the 10-minute interval. - -`parse_data(port,baudrate,lsl_flag=False,csv_flag=False,verbose=False)`: Parses data from Arduino and manages logging, streaming, and GUI updates. - -`cleanup()`: Handles all the cleanup tasks. - -`main()`: Handles command-line argument parsing and initiates data processing. - ## Applications -> [!IMPORTANT] - Before using the below Applications make sure you are in application folder. - ### GUI - `python gui.py`: Enable the real-time data plotting GUI. @@ -108,7 +86,7 @@ To use the script, run it from the command line with various options: ### HEART RATE -- `python heartbeat.ecg.py`:Enable a GUI with real-time ECG and heart rate. +- `python heartbeat_ecg.py`:Enable a GUI with real-time ECG and heart rate. ### EMG ENVELOPE @@ -122,6 +100,32 @@ To use the script, run it from the command line with various options: - `python ffteeg.py`: Enable a GUI with real-time EEG data with its FFT. +### Keystroke + +- `python keystroke.py`: On running, a pop-up opens for connecting, and on pressing Start, blinks are detected to simulate spacebar key presses. + +## Running All Applications +To run all applications together: + + ```bash + python app.py + ``` + +This will launch a Web interface. Use the interface to control the applications: + +1. Click the **`Start LSL Stream`** button to initiate the LSL stream. +2. Then, click on any application button to run the desired module. + +### Available Applications +- `ffteeg`: Perform FFT analysis on EEG data. +- `heartbeat_ecg`: Analyze ECG data and extract heartbeat metrics. +- `eog`: Process and detect blinks in EOG signals. +- `emgenvelope`: Analyze EMG signals for muscle activity or gesture recognition. +- `keystroke`: Monitor and analyze keystroke dynamics. +- `game`: Launch an EEG game fro 2 players(Tug of War). +- `csv_plotter`: Plot data from a CSV file. +- `gui`: Launch the GUI for real time signal visualization. + ## Troubleshooting From 02b0b21e642d7ab8b1a14e0692b67e5b2d4266ba Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Tue, 28 Jan 2025 14:01:43 +0530 Subject: [PATCH 07/25] Updated Readme --- README.md | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ec17947..fe90e89 100644 --- a/README.md +++ b/README.md @@ -105,11 +105,29 @@ To use the script, run it from the command line with various options: - `python keystroke.py`: On running, a pop-up opens for connecting, and on pressing Start, blinks are detected to simulate spacebar key presses. ## Running All Applications +Here's the updated section for the README: + +--- + +### Running All Applications Together + To run all applications together: - ```bash - python app.py - ``` +```bash +python app.py +``` + +> [!NOTE] +> Before running, make sure to activate the virtual environment and install all dependencies: + +1. Activate the virtual environment: + ```bash + .\venv\Scripts\activate + ``` +2. Install dependencies: + ```bash + pip install -r chords_requirements.txt + ``` This will launch a Web interface. Use the interface to control the applications: @@ -122,11 +140,10 @@ This will launch a Web interface. Use the interface to control the applications: - `eog`: Process and detect blinks in EOG signals. - `emgenvelope`: Analyze EMG signals for muscle activity or gesture recognition. - `keystroke`: Monitor and analyze keystroke dynamics. -- `game`: Launch an EEG game fro 2 players(Tug of War). +- `game`: Launch an EEG game for 2 players(Tug of War). - `csv_plotter`: Plot data from a CSV file. - `gui`: Launch the GUI for real time signal visualization. - ## Troubleshooting - **Arduino Not Detected:** Ensure the Arduino is properly connected and powered. Check the serial port and baud rate settings. From 85137c865bd8d023e33fd9f75d86c2d1e4219b1e Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Tue, 28 Jan 2025 14:03:00 +0530 Subject: [PATCH 08/25] Updated Readme --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index fe90e89..c53dd1f 100644 --- a/README.md +++ b/README.md @@ -120,14 +120,14 @@ python app.py > [!NOTE] > Before running, make sure to activate the virtual environment and install all dependencies: -1. Activate the virtual environment: - ```bash - .\venv\Scripts\activate - ``` -2. Install dependencies: - ```bash - pip install -r chords_requirements.txt - ``` +> 1. Activate the virtual environment: +> ```bash +> .\venv\Scripts\activate +> ``` +> 2. Install dependencies: +> ```bash +> pip install -r chords_requirements.txt +> ``` This will launch a Web interface. Use the interface to control the applications: From 329c8ddf4198536ba9234dacd93085fe64ee5799 Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Tue, 28 Jan 2025 14:06:48 +0530 Subject: [PATCH 09/25] Updated Readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c53dd1f..eda8bae 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ To use the script, run it from the command line with various options: ### Options - `-p`, `--port` : Specify the serial port to use (e.g., COM5, /dev/ttyUSB0). -- `-b`, `--baudrate` : Set the baud rate for serial communication. The script will first attempt to use 230400, and if that fails, it will automatically fallback to 115200. +- `-b`, `--baudrate` : Set the baud rate for serial communication. By default the script will first attempt to use 230400, and if that fails, it will automatically fallback to 115200. - `--csv`: Enable CSV logging. Data will be saved to a timestamped file. - `--lsl`: Enable LSL streaming. Sends data to an LSL outlet. - `-v`, `--verbose`: Enable verbose output with detailed statistics and error reporting. From b1a8f2554da982ff5242b82490f5c6c5bb9c6cdf Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Tue, 28 Jan 2025 14:14:25 +0530 Subject: [PATCH 10/25] Updated Readme --- README.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/README.md b/README.md index eda8bae..1cbc64e 100644 --- a/README.md +++ b/README.md @@ -66,14 +66,6 @@ To use the script, run it from the command line with various options: - **Log Intervals**: The script logs data counts every second and provides a summary every 10 minutes, including the sampling rate and drift in seconds per hour. -### LSL Streaming - -- **Stream Name**: `BioAmpDataStream` -- **Stream Type**: `EXG` -- **Channel Count**: `6` -- **Sampling Rate**: `UNO-R3 : 250 Hz` , `UNO-R4 : 500 Hz` -- **Data Format**: `float32` - ## Applications ### GUI From 542e51bd18a669cd1e5b9ffc45ceefb8744519a5 Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Tue, 28 Jan 2025 14:15:59 +0530 Subject: [PATCH 11/25] Updated Readme --- README.md | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 1cbc64e..ee02a85 100644 --- a/README.md +++ b/README.md @@ -96,12 +96,7 @@ To use the script, run it from the command line with various options: - `python keystroke.py`: On running, a pop-up opens for connecting, and on pressing Start, blinks are detected to simulate spacebar key presses. -## Running All Applications -Here's the updated section for the README: - ---- - -### Running All Applications Together +## Running All Applications Together To run all applications together: @@ -112,14 +107,14 @@ python app.py > [!NOTE] > Before running, make sure to activate the virtual environment and install all dependencies: -> 1. Activate the virtual environment: -> ```bash -> .\venv\Scripts\activate -> ``` -> 2. Install dependencies: -> ```bash -> pip install -r chords_requirements.txt -> ``` + 1. Activate the virtual environment: + ```bash + .\venv\Scripts\activate + ``` + 2. Install dependencies: + ```bash + pip install -r chords_requirements.txt + ``` This will launch a Web interface. Use the interface to control the applications: From 12ffa17cb580cc2b7623bae78abfcf071ca02071 Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Tue, 28 Jan 2025 14:16:30 +0530 Subject: [PATCH 12/25] Updated Readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ee02a85..4260166 100644 --- a/README.md +++ b/README.md @@ -109,11 +109,11 @@ python app.py 1. Activate the virtual environment: ```bash - .\venv\Scripts\activate + .\venv\Scripts\activate ``` 2. Install dependencies: ```bash - pip install -r chords_requirements.txt + pip install -r chords_requirements.txt ``` This will launch a Web interface. Use the interface to control the applications: From 51dfdcabd6038c9e2a3d28acee7a566e43da81d4 Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Tue, 28 Jan 2025 14:17:11 +0530 Subject: [PATCH 13/25] Updated Readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ee02a85..25483c2 100644 --- a/README.md +++ b/README.md @@ -109,11 +109,11 @@ python app.py 1. Activate the virtual environment: ```bash - .\venv\Scripts\activate + .\venv\Scripts\activate ``` 2. Install dependencies: ```bash - pip install -r chords_requirements.txt + pip install -r chords_requirements.txt ``` This will launch a Web interface. Use the interface to control the applications: From 2c856660f73954f8a3d007018c01e782d3ce5e1a Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Tue, 28 Jan 2025 14:19:33 +0530 Subject: [PATCH 14/25] Updated Readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ee02a85..8aa9e06 100644 --- a/README.md +++ b/README.md @@ -107,11 +107,11 @@ python app.py > [!NOTE] > Before running, make sure to activate the virtual environment and install all dependencies: - 1. Activate the virtual environment: +1. Activate the virtual environment: ```bash .\venv\Scripts\activate ``` - 2. Install dependencies: +2. Install dependencies: ```bash pip install -r chords_requirements.txt ``` From 2ef54da087d12450ce1488cf0e5307e81870a163 Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Tue, 28 Jan 2025 17:57:04 +0530 Subject: [PATCH 15/25] Updtaed supported boards --- chords.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/chords.py b/chords.py index 8e1f074..6f6a445 100644 --- a/chords.py +++ b/chords.py @@ -54,12 +54,16 @@ board = "" # Variable for Connected Arduino Board supported_boards = { "UNO-R3": {"sampling_rate": 250, "Num_channels": 6}, - "UNO-CLONE": {"sampling_rate": 250, "Num_channels": 6}, # Baud Rate 115200 + "UNO-CLONE": {"sampling_rate": 250, "Num_channels": 6}, "GENUINO-UNO": {"sampling_rate": 250, "Num_channels": 6}, "UNO-R4": {"sampling_rate": 500, "Num_channels": 6}, "RPI-PICO-RP2040": {"sampling_rate": 500, "Num_channels": 3}, - "NANO-CLONE": {"sampling_rate": 250, "Num_channels": 8}, # Baud Rate 115200 - "BLACK-PILL": {"sampling_rate": 500, "Num_channels": 10}, + "NANO-CLONE": {"sampling_rate": 250, "Num_channels": 8}, + "STM32F4-BLACK-PILL": {"sampling_rate": 500, "Num_channels": 8}, + "STM32G4-CORE-BOARD": {"sampling_rate": 500, "Num_channels": 16}, + "MEGA-2560-R3": {"sampling_rate": 250, "Num_channels": 16}, + "MEGA-2560-CLONE": {"sampling_rate": 250, "Num_channels": 16}, + "GIGA-R1": {"sampling_rate": 500, "Num_channels": 6}, } # Initialize gloabal variables for Incoming Data From f3f99d595b838879dd329ca3efc96ecc8ab9bf87 Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Wed, 29 Jan 2025 11:39:31 +0530 Subject: [PATCH 16/25] Updated Readme --- README.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 8aa9e06..fc7e348 100644 --- a/README.md +++ b/README.md @@ -105,20 +105,14 @@ python app.py ``` > [!NOTE] -> Before running, make sure to activate the virtual environment and install all dependencies: - -1. Activate the virtual environment: - ```bash - .\venv\Scripts\activate - ``` -2. Install dependencies: +> Before running, make sure to install all dependencies: ```bash - pip install -r chords_requirements.txt + pip install -r app_requirements.txt ``` This will launch a Web interface. Use the interface to control the applications: -1. Click the **`Start LSL Stream`** button to initiate the LSL stream. +1. Click the `Start LSL Stream` button to initiate the LSL stream. 2. Then, click on any application button to run the desired module. ### Available Applications From 8fab136461dd4b662be254db34fad64563d29964 Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Wed, 29 Jan 2025 11:40:47 +0530 Subject: [PATCH 17/25] Updated Readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fc7e348..3e0bcb3 100644 --- a/README.md +++ b/README.md @@ -106,9 +106,9 @@ python app.py > [!NOTE] > Before running, make sure to install all dependencies: - ```bash - pip install -r app_requirements.txt - ``` +```bash +pip install -r app_requirements.txt +``` This will launch a Web interface. Use the interface to control the applications: From 6a57bde652821c824cd8edff5f355ebc576d1038 Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Wed, 29 Jan 2025 11:41:59 +0530 Subject: [PATCH 18/25] Updated Readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3e0bcb3..265bf01 100644 --- a/README.md +++ b/README.md @@ -105,10 +105,10 @@ python app.py ``` > [!NOTE] -> Before running, make sure to install all dependencies: -```bash -pip install -r app_requirements.txt -``` +> Before running, make sure to install all dependencies by running the command: + ```bash + pip install -r app_requirements.txt + ``` This will launch a Web interface. Use the interface to control the applications: From 75caff431237e6cccd60ce6e768c0d9f59371589 Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Wed, 29 Jan 2025 11:42:58 +0530 Subject: [PATCH 19/25] Updated Readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 265bf01..65e4271 100644 --- a/README.md +++ b/README.md @@ -106,9 +106,9 @@ python app.py > [!NOTE] > Before running, make sure to install all dependencies by running the command: - ```bash - pip install -r app_requirements.txt - ``` + ```bash + pip install -r app_requirements.txt + ``` This will launch a Web interface. Use the interface to control the applications: From 8520dc745aa9d05b17663dcfb292aa44c55bcb9c Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Wed, 29 Jan 2025 11:44:20 +0530 Subject: [PATCH 20/25] Updated Readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 65e4271..f95c82d 100644 --- a/README.md +++ b/README.md @@ -106,9 +106,9 @@ python app.py > [!NOTE] > Before running, make sure to install all dependencies by running the command: - ```bash - pip install -r app_requirements.txt - ``` +```bash +pip install -r app_requirements.txt +``` This will launch a Web interface. Use the interface to control the applications: From 06c85072e5695ab02f551f61f35c9e425d9506bd Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Wed, 29 Jan 2025 11:54:49 +0530 Subject: [PATCH 21/25] Updated Readme --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f95c82d..82a05d5 100644 --- a/README.md +++ b/README.md @@ -116,12 +116,12 @@ This will launch a Web interface. Use the interface to control the applications: 2. Then, click on any application button to run the desired module. ### Available Applications -- `ffteeg`: Perform FFT analysis on EEG data. +- `ffteeg`: Real-time EEG analysis with FFT and brainwave power calculation. - `heartbeat_ecg`: Analyze ECG data and extract heartbeat metrics. -- `eog`: Process and detect blinks in EOG signals. -- `emgenvelope`: Analyze EMG signals for muscle activity or gesture recognition. -- `keystroke`: Monitor and analyze keystroke dynamics. -- `game`: Launch an EEG game for 2 players(Tug of War). +- `eog`: Real-time EOG monitoring with blink detection. +- `emgenvelope`: Real-time EMG monitor with filtering and RMS envelope. +- `keystroke`: GUI for EOG-based blink detection triggering a keystroke. +- `game`: Launch an EEG game for 2 players (Tug of War). - `csv_plotter`: Plot data from a CSV file. - `gui`: Launch the GUI for real time signal visualization. From 49f54c6a3524a860ab2297cda6c07125f41dfdda Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Thu, 30 Jan 2025 16:14:57 +0530 Subject: [PATCH 22/25] boards updated in chords.py --- chords.py | 1 + 1 file changed, 1 insertion(+) diff --git a/chords.py b/chords.py index 6f6a445..abe9877 100644 --- a/chords.py +++ b/chords.py @@ -59,6 +59,7 @@ "UNO-R4": {"sampling_rate": 500, "Num_channels": 6}, "RPI-PICO-RP2040": {"sampling_rate": 500, "Num_channels": 3}, "NANO-CLONE": {"sampling_rate": 250, "Num_channels": 8}, + "NANO-CLASSIC": {"sampling_rate": 250, "Num_channels": 8}, "STM32F4-BLACK-PILL": {"sampling_rate": 500, "Num_channels": 8}, "STM32G4-CORE-BOARD": {"sampling_rate": 500, "Num_channels": 16}, "MEGA-2560-R3": {"sampling_rate": 250, "Num_channels": 16}, From 2d1f62961cbc73240e12fcc938a099fe74038054 Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Thu, 30 Jan 2025 17:28:03 +0530 Subject: [PATCH 23/25] rfft() is used now because it efficiently computes only the necessary positive frequency components, reducing computation time and memory usage for real-time EEG processing. --- ffteeg.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ffteeg.py b/ffteeg.py index 2b700c0..e15bcf3 100644 --- a/ffteeg.py +++ b/ffteeg.py @@ -118,9 +118,9 @@ def update_plot(self): def process_fft_and_brainpower(self): window = np.hanning(len(self.moving_window)) buffer_windowed = np.array(self.moving_window) * window - fft_result = np.abs(fft(buffer_windowed))[:len(buffer_windowed) // 2] + fft_result = np.abs(np.fft.rfft(buffer_windowed)) fft_result /= len(buffer_windowed) - freqs = np.fft.fftfreq(len(buffer_windowed), 1 / self.sampling_rate)[:len(buffer_windowed) // 2] + freqs = np.fft.rfftfreq(len(buffer_windowed), 1 / self.sampling_rate) self.fft_curve.setData(freqs, fft_result) brainwave_power = self.calculate_brainwave_power(fft_result, freqs) From a5c084c4da852cfb9156f1db105031ba9e2599be Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Fri, 31 Jan 2025 11:40:35 +0530 Subject: [PATCH 24/25] Update button --- templates/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/index.html b/templates/index.html index dbdd808..79cce2a 100644 --- a/templates/index.html +++ b/templates/index.html @@ -67,7 +67,7 @@

Chords-Python Applications