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 ("\n CRITICAL ERROR: Connection lost" )
59+ exit_program ()
60+
61+
62+ @QtCore .pyqtSlot ()
63+ def exit_program ():
64+ print ("\n About 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 ("\n Check 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
0 commit comments