Skip to content

Commit 3fe7440

Browse files
Demo without GUI, only CLI
1 parent 7ad80e8 commit 3fe7440

File tree

3 files changed

+600
-0
lines changed

3 files changed

+600
-0
lines changed

foo.py

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
"""Demonstration of multithreaded live Arduino data
4+
- CLI output only
5+
- Mode: SINGLE_SHOT_WAKE_UP
6+
"""
7+
__author__ = "Dennis van Gils"
8+
__authoremail__ = "vangils.dennis@gmail.com"
9+
__url__ = "https://github.com/Dennis-van-Gils/DvG_Arduino_PyQt_multithread_demo"
10+
__date__ = "26-06-2020"
11+
__version__ = "2.1"
12+
13+
import os
14+
import sys
15+
from pathlib import Path
16+
17+
import numpy as np
18+
import psutil
19+
import time
20+
21+
from PyQt5 import QtCore
22+
from DvG_debug_functions import dprint, print_fancy_traceback as pft
23+
24+
import DvG_dev_Arduino__fun_serial as Arduino_functions
25+
import DvG_QDeviceIO
26+
27+
# Constants
28+
DAQ_INTERVAL_ARDUINO = 10 # 10 [ms]
29+
30+
# Show debug info in terminal? Warning: Slow! Do not leave on unintentionally.
31+
DEBUG = False
32+
33+
# ------------------------------------------------------------------------------
34+
# Arduino state
35+
# ------------------------------------------------------------------------------
36+
37+
38+
class State(object):
39+
"""Reflects the actual readings, parsed into separate variables, of the
40+
Arduino(s). There should only be one instance of the State class.
41+
"""
42+
43+
def __init__(self):
44+
self.time = np.nan # [ms]
45+
self.reading_1 = np.nan
46+
47+
48+
state = State()
49+
50+
51+
# ------------------------------------------------------------------------------
52+
# Program termination routines
53+
# ------------------------------------------------------------------------------
54+
55+
56+
@QtCore.pyqtSlot()
57+
def notify_connection_lost():
58+
print("\nCRITICAL ERROR: Connection lost")
59+
exit_program()
60+
61+
62+
@QtCore.pyqtSlot()
63+
def exit_program():
64+
print("\nAbout to quit")
65+
66+
app.processEvents()
67+
68+
timer.stop()
69+
qdev.quit()
70+
ard.close()
71+
72+
app.quit()
73+
74+
75+
# ------------------------------------------------------------------------------
76+
# update_CLI
77+
# ------------------------------------------------------------------------------
78+
79+
80+
@QtCore.pyqtSlot()
81+
def update_CLI():
82+
print(
83+
"%i\t%.3f\t%.4f"
84+
% (qdev.update_counter_DAQ, state.time, state.reading_1)
85+
)
86+
87+
88+
# ------------------------------------------------------------------------------
89+
# Your Arduino update function
90+
# ------------------------------------------------------------------------------
91+
92+
93+
def DAQ_function():
94+
# Query the Arduino for its state
95+
[success, tmp_state] = ard.query_ascii_values("?", separator="\t")
96+
if not (success):
97+
dprint("'%s' reports IOError" % ard.name)
98+
return False
99+
100+
# Parse readings into separate state variables
101+
try:
102+
[state.time, state.reading_1] = tmp_state
103+
except Exception as err:
104+
pft(err, 3)
105+
dprint("'%s' reports IOError" % ard.name)
106+
return False
107+
108+
# Use Arduino time or PC time?
109+
# Arduino time is more accurate, but rolls over ~49 days for a 32 bit timer.
110+
use_PC_time = True
111+
if use_PC_time:
112+
state.time = time.perf_counter()
113+
114+
return True
115+
116+
117+
# ------------------------------------------------------------------------------
118+
# Main
119+
# ------------------------------------------------------------------------------
120+
121+
if __name__ == "__main__":
122+
# Set priority of this process to maximum in the operating system
123+
print("PID: %s\n" % os.getpid())
124+
try:
125+
proc = psutil.Process(os.getpid())
126+
if os.name == "nt":
127+
proc.nice(psutil.REALTIME_PRIORITY_CLASS) # Windows
128+
else:
129+
proc.nice(-20) # Other
130+
except:
131+
print("Warning: Could not set process to maximum priority.\n")
132+
133+
# --------------------------------------------------------------------------
134+
# Connect to Arduino
135+
# --------------------------------------------------------------------------
136+
137+
ard = Arduino_functions.Arduino(name="Ard", baudrate=115200)
138+
ard.auto_connect(
139+
Path("last_used_port.txt"), match_identity="Wave generator"
140+
)
141+
142+
if not (ard.is_alive):
143+
print("\nCheck connection and try resetting the Arduino.")
144+
print("Exiting...\n")
145+
sys.exit(0)
146+
147+
# --------------------------------------------------------------------------
148+
# Create application
149+
# --------------------------------------------------------------------------
150+
QtCore.QThread.currentThread().setObjectName("MAIN") # For DEBUG info
151+
152+
app = 0 # Work-around for kernel crash when using Spyder IDE
153+
app = QtCore.QCoreApplication(sys.argv)
154+
app.aboutToQuit.connect(exit_program)
155+
156+
# --------------------------------------------------------------------------
157+
# Set up multithreaded communication with the Arduino
158+
# --------------------------------------------------------------------------
159+
160+
# Create QDeviceIO
161+
qdev = DvG_QDeviceIO.QDeviceIO(ard)
162+
163+
# Create workers
164+
# fmt: off
165+
qdev.create_worker_DAQ(
166+
DAQ_trigger = DvG_QDeviceIO.DAQ_trigger.SINGLE_SHOT_WAKE_UP,
167+
DAQ_function = DAQ_function,
168+
debug = DEBUG,)
169+
# fmt: on
170+
171+
qdev.create_worker_jobs(debug=DEBUG)
172+
173+
# Connect signals to slots
174+
qdev.signal_DAQ_updated.connect(update_CLI)
175+
qdev.signal_connection_lost.connect(notify_connection_lost)
176+
177+
# Start workers
178+
qdev.start(DAQ_priority=QtCore.QThread.TimeCriticalPriority)
179+
180+
# --------------------------------------------------------------------------
181+
# Create wake-up timer
182+
# --------------------------------------------------------------------------
183+
184+
timer = QtCore.QTimer()
185+
timer.setInterval(DAQ_INTERVAL_ARDUINO)
186+
timer.setTimerType(QtCore.Qt.PreciseTimer)
187+
timer.timeout.connect(qdev.wake_up_DAQ)
188+
timer.start()
189+
190+
# --------------------------------------------------------------------------
191+
# Start the main loop
192+
# --------------------------------------------------------------------------
193+
194+
while True:
195+
app.processEvents()
196+
197+
if qdev.update_counter_DAQ >= 20:
198+
exit_program()
199+
break

foo_bar.py

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
"""Demonstration of multithreaded live Arduino data
4+
- CLI output only
5+
- Mode: INTERNAL_TIMER
6+
"""
7+
__author__ = "Dennis van Gils"
8+
__authoremail__ = "vangils.dennis@gmail.com"
9+
__url__ = "https://github.com/Dennis-van-Gils/DvG_Arduino_PyQt_multithread_demo"
10+
__date__ = "26-06-2020"
11+
__version__ = "2.1"
12+
13+
import os
14+
import sys
15+
from pathlib import Path
16+
17+
import numpy as np
18+
import psutil
19+
import time
20+
21+
from PyQt5 import QtCore
22+
from DvG_debug_functions import dprint, print_fancy_traceback as pft
23+
24+
import DvG_dev_Arduino__fun_serial as Arduino_functions
25+
import DvG_QDeviceIO
26+
27+
# Constants
28+
DAQ_INTERVAL_ARDUINO = 10 # 10 [ms]
29+
30+
# Show debug info in terminal? Warning: Slow! Do not leave on unintentionally.
31+
DEBUG = False
32+
33+
# ------------------------------------------------------------------------------
34+
# Arduino state
35+
# ------------------------------------------------------------------------------
36+
37+
38+
class State(object):
39+
"""Reflects the actual readings, parsed into separate variables, of the
40+
Arduino(s). There should only be one instance of the State class.
41+
"""
42+
43+
def __init__(self):
44+
self.time = np.nan # [ms]
45+
self.reading_1 = np.nan
46+
47+
48+
state = State()
49+
50+
51+
# ------------------------------------------------------------------------------
52+
# Program termination routines
53+
# ------------------------------------------------------------------------------
54+
55+
56+
@QtCore.pyqtSlot()
57+
def notify_connection_lost():
58+
print("\nCRITICAL ERROR: Connection lost")
59+
exit_program()
60+
61+
62+
@QtCore.pyqtSlot()
63+
def exit_program():
64+
print("\nAbout to quit")
65+
66+
app.processEvents()
67+
68+
qdev.quit()
69+
ard.close()
70+
71+
app.quit()
72+
73+
74+
# ------------------------------------------------------------------------------
75+
# update_CLI
76+
# ------------------------------------------------------------------------------
77+
78+
79+
@QtCore.pyqtSlot()
80+
def update_CLI():
81+
print(
82+
"%i\t%.3f\t%.4f"
83+
% (qdev.update_counter_DAQ, state.time, state.reading_1)
84+
)
85+
86+
87+
# ------------------------------------------------------------------------------
88+
# Your Arduino update function
89+
# ------------------------------------------------------------------------------
90+
91+
92+
def DAQ_function():
93+
# Query the Arduino for its state
94+
[success, tmp_state] = ard.query_ascii_values("?", separator="\t")
95+
if not (success):
96+
dprint("'%s' reports IOError" % ard.name)
97+
return False
98+
99+
# Parse readings into separate state variables
100+
try:
101+
[state.time, state.reading_1] = tmp_state
102+
except Exception as err:
103+
pft(err, 3)
104+
dprint("'%s' reports IOError" % ard.name)
105+
return False
106+
107+
# Use Arduino time or PC time?
108+
# Arduino time is more accurate, but rolls over ~49 days for a 32 bit timer.
109+
use_PC_time = True
110+
if use_PC_time:
111+
state.time = time.perf_counter()
112+
113+
return True
114+
115+
116+
# ------------------------------------------------------------------------------
117+
# Main
118+
# ------------------------------------------------------------------------------
119+
120+
if __name__ == "__main__":
121+
# Set priority of this process to maximum in the operating system
122+
print("PID: %s\n" % os.getpid())
123+
try:
124+
proc = psutil.Process(os.getpid())
125+
if os.name == "nt":
126+
proc.nice(psutil.REALTIME_PRIORITY_CLASS) # Windows
127+
else:
128+
proc.nice(-20) # Other
129+
except:
130+
print("Warning: Could not set process to maximum priority.\n")
131+
132+
# --------------------------------------------------------------------------
133+
# Connect to Arduino
134+
# --------------------------------------------------------------------------
135+
136+
ard = Arduino_functions.Arduino(name="Ard", baudrate=115200)
137+
ard.auto_connect(
138+
Path("last_used_port.txt"), match_identity="Wave generator"
139+
)
140+
141+
if not (ard.is_alive):
142+
print("\nCheck connection and try resetting the Arduino.")
143+
print("Exiting...\n")
144+
sys.exit(0)
145+
146+
# --------------------------------------------------------------------------
147+
# Create application
148+
# --------------------------------------------------------------------------
149+
QtCore.QThread.currentThread().setObjectName("MAIN") # For DEBUG info
150+
151+
app = 0 # Work-around for kernel crash when using Spyder IDE
152+
app = QtCore.QCoreApplication(sys.argv)
153+
app.aboutToQuit.connect(exit_program)
154+
155+
# --------------------------------------------------------------------------
156+
# Set up multithreaded communication with the Arduino
157+
# --------------------------------------------------------------------------
158+
159+
# Create QDeviceIO
160+
qdev = DvG_QDeviceIO.QDeviceIO(ard)
161+
162+
# Create workers
163+
# fmt: off
164+
qdev.create_worker_DAQ(
165+
DAQ_trigger = DvG_QDeviceIO.DAQ_trigger.INTERNAL_TIMER,
166+
DAQ_function = DAQ_function,
167+
DAQ_interval_ms = DAQ_INTERVAL_ARDUINO,
168+
debug = DEBUG,)
169+
# fmt: on
170+
171+
qdev.create_worker_jobs(debug=DEBUG)
172+
173+
# Connect signals to slots
174+
qdev.signal_DAQ_updated.connect(update_CLI)
175+
qdev.signal_connection_lost.connect(notify_connection_lost)
176+
177+
# Test to connect the internal clock to another QDeviceIO instance in
178+
# mode SINGLE_SHOT)_WAKE_UP. Is a viable option :)
179+
qdev.worker_DAQ._timer.timeout.connect(lambda: print("\t%.3f" % time.perf_counter()))
180+
181+
# Start workers
182+
qdev.start(DAQ_priority=QtCore.QThread.TimeCriticalPriority)
183+
184+
# --------------------------------------------------------------------------
185+
# Start the main loop
186+
# --------------------------------------------------------------------------
187+
188+
while True:
189+
app.processEvents()
190+
191+
if qdev.update_counter_DAQ >= 20:
192+
exit_program()
193+
break

0 commit comments

Comments
 (0)