Skip to content

Commit c18ad73

Browse files
authored
Merge pull request #43 from PayalLakra/new
FFT Visualizer-Multiple EEG & FFT Visualization
2 parents 94709e9 + 8a0fbd6 commit c18ad73

File tree

11 files changed

+1077
-404
lines changed

11 files changed

+1077
-404
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Recorded data
22
*.csv
33

4+
/logs/
5+
logging.txt
6+
47
# Byte-compiled / optimized / DLL files
58
__pycache__/
69
*.py[cod]

README.md

Lines changed: 92 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -1,175 +1,109 @@
11
# Chords - Python
22

3-
Chords Python script is designed to interface with an Arduino-based bio-potential amplifier, read data from it, optionally log this data to CSV or stream it via the Lab Streaming Layer (LSL), and visualize it through a graphical user interface (GUI) with live plotting.
4-
5-
> [!NOTE]
6-
> Flash Arduino code to your hardware from [Chords Arduino Firmware](https://github.com/upsidedownlabs/Chords-Arduino-Firmware) to use this python tool.
7-
8-
## Features
9-
10-
- **Automatic Arduino Detection:** Automatically detects connected Arduino devices via serial ports.
11-
- **Data Reading:** Read data packets from the Arduino's serial port.
12-
- **CSV Logging:** Optionally logs data to a CSV file.
13-
- **LSL Streaming:** Optionally streams data to an LSL outlet for integration with other software.
14-
- **Verbose Output:** Provides detailed statistics and error reporting, including sampling rate and drift.
15-
- **GUI:** Live plotting of six channels using a PyQt-based GUI.
16-
- **Invert:** Optionally Invert the signal before streaming LSL and logging
17-
- **Timer:** Record data for a set time period in seconds.
18-
19-
## Requirements
20-
21-
- Python
22-
- `pyserial` library (for serial communication)
23-
- `pylsl` library (for LSL streaming)
24-
- `argparse`, `time`, `csv`, `datetime` (standard libraries)
25-
- `pyqtgraph` library (for GUI)
26-
- `PyQt5` library
27-
- `numpy` library
28-
29-
## Installation
30-
31-
1. Ensure you have latest version of Python installed.
32-
2. Create Virtual Environment
3+
Chords- Python is a bag of tools designed to interface with Micro-controller development boards running [Chords Arduino Firmware](https://github.com/upsidedownlabs/Chords-Arduino-Firmware).Use Upside Down Labs bio-potential amplifiers to read data, visualize it, record data in CSV Files, and stream it via Lab Streaming Layer.
4+
5+
> [!NOTE]
6+
> **Firmware Required:**
7+
> - For Arduino: [Chords Arduino Firmware](https://github.com/upsidedownlabs/Chords-Arduino-Firmware)
8+
9+
## Features
10+
- **Multiple Protocols**: Supports `Wi-Fi`, `Bluetooth`, and `Serial` communication.
11+
- **LSL Data Streaming**:Once the LSL stream starts, any PC on the same Wi-Fi network can access the data using tools like BrainVision LSL Viewer.
12+
- **CSV Logging**: Save raw data with Counter
13+
- **GUI**: Live plotting for all channels.
14+
- **Applications**: EEG/ECG/EMG/EOG-based games and utilities (e.g., Tug of War, Keystroke Emulator).
15+
16+
## Installation
17+
1. **Python**: Ensure Latest version of Python is installed.
18+
2. **Virtual Environment**:
3319
```bash
34-
python -m venv venv
35-
```
36-
20+
python -m venv venv
21+
source venv/bin/activate # Linux/macOS
22+
.\venv\Scripts\activate # Windows
23+
```
24+
3. **Dependencies**:
3725
```bash
38-
.\venv\Scripts\activate
39-
```
40-
41-
> [!IMPORTANT]
42-
> You may get an execution policy error if scripts are restricted. To fix it, run:
43-
44-
> ```bash
45-
> Set-ExecutionPolicy Unrestricted -Scope Process
46-
> ```
47-
48-
3. Install the required Python libraries needed to run the python script:
49-
```bash
50-
pip install -r chords_requirements.txt
51-
```
52-
53-
4. Install the required Python libraries needed to run the applications:
54-
```bash
55-
pip install -r app_requirements.txt
56-
```
57-
58-
## Usage
59-
60-
To use the script, run it from the command line with various options:
61-
```bash
62-
python chords.py [options]
63-
```
64-
### Options
65-
66-
- `-p`, `--port` <port>: Specify the serial port to use (e.g., COM5, /dev/ttyUSB0).
67-
- `-b`, `--baudrate` <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.
68-
- `--csv`: Enable CSV logging. Data will be saved to a timestamped file.
69-
- `--lsl`: Enable LSL streaming. Sends data to an LSL outlet.
70-
- `-v`, `--verbose`: Enable verbose output with detailed statistics and error reporting.
71-
- `-t` : Enable the timer to run program for a set time in seconds.
72-
73-
### Example:
74-
```bash
75-
python chords.py --lsl -v --csv -t 60
76-
```
77-
- This command executes the Python script `chords.py`, initiates the LSL stream, enables verbose output, activates CSV logging, and sets a timer for 60 seconds:
78-
79-
### Data Logging
80-
81-
- **CSV Output**: The script saves the processed data in a CSV file with a timestamped name.
82-
- The CSV file contains the following columns:
83-
- `Counter`: The sample counter from the Arduino.
84-
- `Channel1` to `Channel6`: The data values from each channel.
85-
86-
- **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.
87-
88-
## Applications
89-
Open another terminal and run an application. Ensure the LSL Stream is running first.
90-
91-
### Installation
92-
Before running any application, install all dependencies with the following command:
93-
94-
```bash
95-
pip install -r app_requirements.txt
96-
```
97-
98-
### Available Applications
26+
pip install -r requirements.txt
27+
```
9928

100-
#### ECG with Heart Rate
29+
> [!IMPORTANT]
30+
> On Windows, if scripts are blocked, run:
31+
> ```powershell
32+
> Set-ExecutionPolicy Unrestricted -Scope Process
33+
> ```
10134
102-
- `python heartbeat_ecg.py`:Enable a GUI with real-time ECG and heart rate.
103-
104-
#### EMG with Envelope
105-
106-
- `python emgenvelope.py`: Enable a GUI with real-time EMG & its Envelope.
107-
108-
#### EOG with Blinks
109-
110-
- `python eog.py`: Enable a GUI with real-time EOG that detects blinks and mark them with red dots.
111-
112-
#### EEG with FFT
113-
114-
- `python ffteeg.py`: Enable a GUI with real-time EEG data with its FFT and band powers.
115-
116-
#### EEG Tug of War Game
117-
118-
- `python game.py`: Enable a GUI to play tug of war game using EEG Signal.
119-
120-
#### EEG Beetle Game
121-
122-
- `python beetle.py`: Enable a GUI for Beetle Game using EEG signal.
123-
124-
#### GUI
125-
126-
- `python gui.py`: Enable the real-time data plotting GUI.
127-
128-
#### EOG Keystroke Emulator
129-
130-
- `python keystroke.py`: On running, a pop-up opens for connecting, and on pressing Start, blinks are detected to simulate spacebar key presses.
131-
132-
#### CSV Plotter
133-
134-
- `python csv_plotter.py`: On running, a pop-up window opens with option to load a file, select a channel to plot, and then plot the data.
135-
136-
## Running All Applications Together in a Web-Interface
137-
138-
To run all applications simultaneously, execute:
139-
140-
```bash
141-
python app.py
142-
```
143-
144-
> [!NOTE]
145-
> Before running, make sure to install all dependencies by running the command:
35+
## Usage
36+
Run the script and access the web interface:
14637
```bash
147-
pip install -r app_requirements.txt
148-
```
149-
150-
This will launch a Web interface. Use the interface to control the applications:
151-
152-
1. Click the `Start LSL Stream` button to initiate the LSL stream or `Start NPG Stream` button to initiate the NPG stream.
153-
2. Then, click on any application button to run the desired module.
154-
Important: Keep the `python app.py` script running in the background while using any application.
155-
156-
### Available Applications
157-
- `ECG with Heart Rate`: Analyze ECG data and extract heartbeat metrics.
158-
- `EMG with Envelope`: Real-time EMG monitor with filtering and RMS envelope.
159-
- `EOG with Blinks`: Real-time EOG monitoring with blink detection.
160-
- `EEG with FFT`: Real-time EEG analysis with FFT and brainwave power calculation.
161-
- `EEG Tug of War`: A 2-player game where brain activity determines the winner in a battle of focus.
162-
- `EEG Beetle Game`: Use your concentration to control a beetle's movement in this brain-powered challenge.
163-
- `GUI of Channels`: Launch the GUI for real time signal visualization.
164-
- `EOG Keystroke Emulator`: GUI for EOG-based blink detection triggering a keystroke.
165-
- `CSV Plotter`: Plot data from a CSV file.
38+
python app.py
39+
```
40+
**Web Interface Preview**:
41+
![Web Interface Screenshot](./media/Interface.png)
42+
43+
![Web Interface Screenshot](./media/Webinterface.png)
44+
45+
### Key Options:
46+
47+
- **LSL Streaming**: Choose a protocol (`Wi-Fi`, `Bluetooth`, `Serial`).
48+
- **CSV Logging**: Data saved as `ChordsPy_{timestamp}.csv`.
49+
- **Applications**: Multiple Applications can be Launch from the Interface simultaneously(e.g., `EEG Tug of War`).
50+
51+
## Connection Guide
52+
53+
#### WIFI Connection
54+
1. Upload the NPG-Lite WIFI Code to your device.
55+
2. Connect to the device's WIFI network.
56+
3. Click the **WIFI** button in the interface, then select **CONNECT**.
57+
4. Once connected, the button will change to **Disconnect**, and a pop-up will confirm: *"Connected via Wifi!"*
58+
59+
#### Bluetooth Connection
60+
1. Ensure Bluetooth is turned ON on your system.
61+
2. Upload the Bluetooth code to your device.
62+
3. Click the **Bluetooth** button to scan for available devices.
63+
4. Select your device from the list and click **Connect**.
64+
5. Once connected, the button will change to **Disconnect**, and a pop-up will confirm: *"Connected via Bluetooth!"*
65+
66+
#### Serial Connection
67+
1. Ensure Bluetooth is OFF and the device is connected via USB.
68+
2. Upload the required code to your hardware.
69+
3. Click the **Serial** button, then select **Connect**.
70+
4. Once connected, the button will change to **Disconnect**, and a pop-up will confirm: *"Connected via Serial!"*
71+
72+
## CSV Logging
73+
To save sensor data for future analysis, follow these steps:
74+
1. **Start Data Streaming** – Begin streaming data via **WiFi, Bluetooth, or Serial**.
75+
2. **Start Recording** – Click the **Start Recording** button (it will change to **Stop Recording**).
76+
3. **File Saved Automatically** – The data is saved as `ChordsPy_{timestamp}.csv` in your default folder.
77+
78+
Visualizing CSV Data - You can plot the recorded data using the **CSV Plotter** tool.
79+
80+
## Applications
81+
| Application | Description |
82+
|----------------------------|------------------------------------------------------------------|
83+
| **ECG with Heart Rate** | Real-time ECG with BPM calculation. |
84+
| **EMG with Envelope** | Real-time EMG Visualization with Envelope. |
85+
| **EOG with Blinks** | Real-time EOG Signal visualization with Blinks marked as Red Dot.|
86+
| **EEG with FFT** | Real-time EEG Signal visualization with FFT and Brainpower bands.|
87+
| **EEG Tug of War Game** | 2 Player EEG Based Game |
88+
| **EEG Beetle game** | Real-time EEG focus based game. |
89+
| **EOG Keystroke Emulator** | Blink detection triggers spacebar. |
90+
| **GUI** | Visualize raw data in real-time |
91+
| **CSV Plotter** | Tool to plot the recorded CSV Files |
16692

16793
## Troubleshooting
16894

16995
- **Arduino Not Detected:** Ensure the Arduino is properly connected and powered. Check the serial port and baud rate settings.
17096
- **CSV File Not Created:** Ensure you have write permissions in the directory where the script is run.
17197
- **LSL Stream Issues:** Ensure that the `pylsl` library is properly installed and configured. Additionally, confirm that Bluetooth is turned off.
17298

99+
## How to Contribute
100+
101+
You can add your project to this repo:
102+
103+
- Add a button in apps.yaml to link your application.
104+
- Include your script as a .py file with LSL Data Reception code.
105+
(Pull requests welcome!)
106+
173107
## Contributors
174108

175109
We are thankful to our awesome contributors, the list below is alphabetically sorted.

app.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import queue
99
import yaml
1010
from pathlib import Path
11+
import os
1112

1213
console_queue = queue.Queue()
1314
app = Flask(__name__)
@@ -20,6 +21,22 @@
2021
stream_active = False
2122
running_apps = {} # Dictionary to track running apps
2223

24+
@app.route('/log_error', methods=['POST'])
25+
def log_error():
26+
try:
27+
error_data = request.get_json()
28+
if not error_data or 'error' not in error_data or 'log_error' in str(error_data):
29+
return jsonify({'status': 'error', 'message': 'Invalid data'}), 400
30+
31+
os.makedirs('logs', exist_ok=True)
32+
33+
with open('logs/logging.txt', 'a') as f:
34+
f.write(error_data['error'])
35+
36+
return jsonify({'status': 'success'})
37+
except Exception as e:
38+
return jsonify({'status': 'error', 'message': 'Logging failed'}), 500
39+
2340
def run_async(coro):
2441
def wrapper(*args, **kwargs):
2542
loop = asyncio.new_event_loop()
@@ -63,9 +80,8 @@ async def scan_ble_devices():
6380

6481
@app.route('/check_stream')
6582
def check_stream():
66-
if connection_manager and connection_manager.stream_active:
67-
return jsonify({'connected': True})
68-
return jsonify({'connected': False})
83+
is_connected = connection_manager.stream_active if hasattr(connection_manager, 'stream_active') else False
84+
return jsonify({'connected': is_connected})
6985

7086
@app.route('/check_connection')
7187
def check_connection():

chords_ble.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ class Chords_BLE:
1313
CONTROL_CHAR_UUID = "0000ff01-0000-1000-8000-00805f9b34fb"
1414

1515
# Packet parameters
16-
SINGLE_SAMPLE_LEN = 7 # (1 Counter + 3 Channels * 2 bytes)
16+
NUM_CHANNELS = 3
17+
SINGLE_SAMPLE_LEN = (NUM_CHANNELS * 2) + 1 # (1 Counter + Num_Channels * 2 bytes)
1718
BLOCK_COUNT = 10
1819
NEW_PACKET_LEN = SINGLE_SAMPLE_LEN * BLOCK_COUNT
1920

@@ -72,10 +73,8 @@ def process_sample(self, sample_data: bytearray):
7273
if self.start_time is None:
7374
self.start_time = time.time()
7475

75-
channels = [
76-
int.from_bytes(sample_data[1:3], byteorder='big', signed=True),
77-
int.from_bytes(sample_data[3:5], byteorder='big', signed=True),
78-
int.from_bytes(sample_data[5:7], byteorder='big', signed=True)]
76+
channels = [int.from_bytes(sample_data[i:i+2], byteorder='big', signed=True)
77+
for i in range(1, len(sample_data), 2)]
7978

8079
self.samples_received += 1
8180

chords_wifi.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from scipy.signal import butter, filtfilt
66

77
class Chords_WIFI:
8-
def __init__(self, stream_name='NPG', channels=3, sampling_rate=250, block_size=13, timeout_sec=1):
8+
def __init__(self, stream_name='NPG', channels=3, sampling_rate=500, block_size=13, timeout_sec=1):
99
self.stream_name = stream_name
1010
self.channels = channels
1111
self.sampling_rate = sampling_rate

0 commit comments

Comments
 (0)