Skip to content

Commit b75edbd

Browse files
committed
USB.WIFI is working well but BLE has some issues
1 parent 7cd5745 commit b75edbd

File tree

2 files changed

+559
-827
lines changed

2 files changed

+559
-827
lines changed

app.py

Lines changed: 109 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -1,197 +1,132 @@
11
from flask import Flask, render_template, request, jsonify
2-
import asyncio
32
from connection import Connection
4-
from threading import Thread
5-
import webbrowser
6-
from pylsl import resolve_streams
7-
import time
8-
from chords_ble import Chords_BLE
9-
import subprocess
10-
import os
11-
import sys
3+
import threading
4+
import asyncio
5+
import logging
6+
from bleak import BleakScanner
7+
from flask import Response
8+
import queue
9+
import threading
1210

11+
console_queue = queue.Queue()
1312
app = Flask(__name__)
13+
logging.basicConfig(level=logging.INFO)
14+
15+
# Global variables
1416
connection_manager = None
17+
connection_thread = None
1518
ble_devices = []
16-
active_connection = None
17-
lsl_stream_active = False
18-
csv_logging_enabled = False # Global CSV logging state
19-
running_apps = {} # Dictionary to keep track of running applications
2019

21-
@app.route('/set_csv', methods=['POST'])
22-
def set_csv():
23-
global csv_logging_enabled
24-
data = request.get_json()
25-
csv_logging_enabled = data.get('enabled', False)
26-
return jsonify({'status': 'success', 'csv_logging': csv_logging_enabled})
20+
def run_async(coro):
21+
def wrapper(*args, **kwargs):
22+
loop = asyncio.new_event_loop()
23+
asyncio.set_event_loop(loop)
24+
try:
25+
return loop.run_until_complete(coro(*args, **kwargs))
26+
finally:
27+
loop.close()
28+
return wrapper
2729

2830
@app.route('/')
2931
def index():
30-
colors_list = ['#a855f7', '#93c5fd', '#a7f3d0', '#10b981', '#b91c1c', '#1d4ed8']
31-
return render_template('index.html', colors=colors_list)
32-
33-
def check_lsl_stream():
34-
global lsl_stream_active
35-
while True:
36-
streams = resolve_streams()
37-
if any(s.name() == "BioAmpDataStream" for s in streams):
38-
lsl_stream_active = True
39-
break
40-
time.sleep(0.5)
32+
return render_template('index.html')
4133

42-
@app.route('/connect', methods=['POST'])
43-
def connect():
44-
global active_connection, lsl_stream_active
45-
protocol = request.form.get('protocol')
46-
47-
if active_connection:
48-
return jsonify({'status': 'error', 'message': 'A connection is already active'})
49-
50-
lsl_stream_active = False
51-
Thread(target=check_lsl_stream).start()
52-
53-
if protocol == 'usb':
54-
thread = Thread(target=connect_usb)
55-
thread.start()
56-
return jsonify({'status': 'connecting', 'message': 'Connecting via USB...'})
57-
58-
elif protocol == 'wifi':
59-
thread = Thread(target=connect_wifi)
60-
thread.start()
61-
return jsonify({'status': 'connecting', 'message': 'Connecting via WiFi...'})
62-
63-
elif protocol == 'ble':
64-
return jsonify({'status': 'scanning', 'message': 'Scanning for BLE devices...'})
65-
66-
return jsonify({'status': 'error', 'message': 'Invalid protocol'})
67-
68-
@app.route('/check_connection', methods=['GET'])
69-
def check_connection():
70-
global lsl_stream_active
71-
if lsl_stream_active:
72-
return jsonify({'status': 'success', 'message': 'Connection established! LSL stream started.'})
73-
return jsonify({'status': 'connecting', 'message': 'Connecting...'})
74-
75-
@app.route('/scan_ble', methods=['GET'])
76-
def scan_ble():
34+
@app.route('/scan_ble')
35+
@run_async
36+
async def scan_ble_devices():
7737
global ble_devices
7838
try:
79-
ble_scanner = Chords_BLE()
80-
loop = asyncio.new_event_loop()
81-
asyncio.set_event_loop(loop)
82-
devices = loop.run_until_complete(ble_scanner.scan_devices())
83-
loop.close()
84-
85-
if not devices:
86-
return jsonify({'status': 'error', 'message': 'No BLE devices found'})
87-
88-
ble_devices = devices
89-
devices_list = [{'name': device.name, 'address': device.address} for device in devices]
90-
return jsonify({'status': 'success', 'devices': devices_list})
39+
devices = await BleakScanner.discover(timeout=5)
40+
ble_devices = [{'name': d.name or 'Unknown', 'address': d.address}
41+
for d in devices if d.name and d.name.startswith(('NPG', 'npg'))]
42+
return jsonify({'status': 'success', 'devices': ble_devices})
9143
except Exception as e:
92-
return jsonify({'status': 'error', 'message': str(e)})
93-
94-
@app.route('/connect_ble', methods=['POST'])
95-
def connect_ble_device():
96-
device_address = request.form.get('address')
97-
if not device_address:
98-
return jsonify({'status': 'error', 'message': 'No device address provided'})
99-
100-
thread = Thread(target=connect_ble, args=(device_address,))
101-
thread.start()
102-
return jsonify({'status': 'connecting', 'message': f'Connecting to BLE device {device_address}...'})
44+
logging.error(f"BLE scan error: {str(e)}")
45+
return jsonify({'status': 'error', 'message': str(e)}), 500
10346

104-
def connect_usb():
105-
global active_connection, lsl_stream_active, csv_logging_enabled
106-
active_connection = Connection(csv_logging=csv_logging_enabled)
107-
active_connection.connect_usb()
47+
@app.route('/check_stream')
48+
def check_stream():
49+
if connection_manager and connection_manager.lsl_connection:
50+
return jsonify({'connected': True})
51+
return jsonify({'connected': False})
10852

109-
def connect_wifi():
110-
global active_connection, lsl_stream_active, csv_logging_enabled
111-
active_connection = Connection(csv_logging=csv_logging_enabled)
112-
active_connection.connect_wifi()
53+
def post_console_message(message):
54+
if connection_manager:
55+
if "LSL stream started" in message:
56+
connection_manager.stream_active = True
57+
elif "Connection error" in message or "disconnected" in message:
58+
connection_manager.stream_active = False
59+
console_queue.put(message)
11360

114-
def connect_ble(address):
115-
global active_connection, lsl_stream_active, csv_logging_enabled
116-
lsl_stream_active = False
117-
Thread(target=check_lsl_stream).start() # Add this line
118-
active_connection = Connection(csv_logging=csv_logging_enabled)
119-
active_connection.connect_ble(address) # No duplicate device selection
61+
@app.route('/console_updates')
62+
def console_updates():
63+
def event_stream():
64+
while True:
65+
message = console_queue.get()
66+
yield f"data: {message}\n\n"
67+
68+
return Response(event_stream(), mimetype="text/event-stream")
12069

121-
@app.route('/run_application', methods=['POST'])
122-
def run_application():
123-
global lsl_stream_active, running_apps
124-
125-
if not lsl_stream_active:
126-
return jsonify({'status': 'error', 'message': 'LSL stream is not active. Please connect first.'})
127-
128-
app_name = request.form.get('app_name')
129-
if not app_name:
130-
return jsonify({'status': 'error', 'message': 'No application specified'})
131-
132-
if app_name in running_apps:
133-
return jsonify({'status': 'error', 'message': f'{app_name} is already running'})
134-
135-
app_mapping = {
136-
'ECG with Heart Rate': 'heartbeat_ecg.py',
137-
'EMG with Envelope': 'emgenvelope.py',
138-
'EOG with Blinks': 'eog.py',
139-
'EEG with FFT': 'ffteeg.py',
140-
'EEG Tug of War' : 'game.py',
141-
'EEG Beetle Game': 'beetle.py',
142-
'EOG Keystroke Emulator': 'keystroke.py',
143-
'GUI Visualization': 'gui.py',
144-
'CSV Plotter': 'csvplotter.py'
145-
}
146-
147-
script_name = app_mapping.get(app_name)
148-
if not script_name:
149-
return jsonify({'status': 'error', 'message': 'Invalid application name'})
150-
151-
if not os.path.exists(script_name):
152-
return jsonify({'status': 'error', 'message': f'Script {script_name} not found'})
153-
154-
try:
155-
process = subprocess.Popen([sys.executable, script_name])
156-
running_apps[app_name] = process
157-
return jsonify({'status': 'success', 'message': f'{app_name} started successfully'})
158-
except Exception as e:
159-
return jsonify({'status': 'error', 'message': str(e)})
160-
161-
@app.route('/stop_application', methods=['POST'])
162-
def stop_application():
163-
global running_apps
164-
165-
app_name = request.form.get('app_name')
166-
if not app_name:
167-
return jsonify({'status': 'error', 'message': 'No application specified'})
168-
169-
if app_name not in running_apps:
170-
return jsonify({'status': 'error', 'message': f'{app_name} is not running'})
70+
@app.route('/connect', methods=['POST'])
71+
def connect_device():
72+
global connection_manager, connection_thread, stream_active
17173

172-
try:
173-
running_apps[app_name].terminate()
174-
del running_apps[app_name]
175-
return jsonify({'status': 'success', 'message': f'{app_name} stopped successfully'})
176-
except Exception as e:
177-
return jsonify({'status': 'error', 'message': str(e)})
74+
data = request.get_json()
75+
protocol = data.get('protocol')
76+
device_address = data.get('device_address')
77+
csv_logging = data.get('csv_logging', False)
78+
79+
# Reset stream status
80+
stream_active = False
81+
82+
# Clean up any existing connection
83+
if connection_manager:
84+
connection_manager.cleanup()
85+
if connection_thread and connection_thread.is_alive():
86+
connection_thread.join()
87+
88+
# Create new connection
89+
connection_manager = Connection(csv_logging=csv_logging)
90+
91+
def run_connection():
92+
try:
93+
if protocol == 'usb':
94+
success = connection_manager.connect_usb()
95+
elif protocol == 'wifi':
96+
success = connection_manager.connect_wifi()
97+
elif protocol == 'ble':
98+
if not device_address:
99+
logging.error("No BLE device address provided")
100+
return
101+
102+
# For BLE, we need to run in an event loop
103+
loop = asyncio.new_event_loop()
104+
asyncio.set_event_loop(loop)
105+
success = connection_manager.connect_ble(device_address)
106+
107+
if success:
108+
post_console_message("LSL stream started")
109+
else:
110+
post_console_message("Connection failed")
111+
except Exception as e:
112+
logging.error(f"Connection error: {str(e)}")
113+
post_console_message(f"Connection error: {str(e)}")
114+
115+
# Start connection in a separate thread
116+
connection_thread = threading.Thread(target=run_connection, daemon=True)
117+
connection_thread.start()
118+
119+
return jsonify({'status': 'connecting', 'protocol': protocol})
178120

179-
@app.route('/check_app_status', methods=['GET'])
180-
def check_app_status():
181-
global running_apps
182-
183-
app_name = request.args.get('app_name')
184-
if not app_name:
185-
return jsonify({'status': 'error', 'message': 'No application specified'})
186-
187-
if app_name in running_apps:
188-
if running_apps[app_name].poll() is None:
189-
return jsonify({'status': 'running', 'message': f'{app_name} is running'})
190-
else:
191-
del running_apps[app_name]
192-
return jsonify({'status': 'stopped', 'message': f'{app_name} has stopped'})
193-
else:
194-
return jsonify({'status': 'stopped', 'message': f'{app_name} is not running'})
121+
@app.route('/disconnect', methods=['POST'])
122+
def disconnect_device():
123+
global connection_manager
124+
if connection_manager:
125+
connection_manager.cleanup()
126+
connection_manager.stream_active = False
127+
post_console_message("disconnected")
128+
return jsonify({'status': 'disconnected'})
129+
return jsonify({'status': 'no active connection'})
195130

196131
if __name__ == "__main__":
197132
app.run(debug=True)

0 commit comments

Comments
 (0)