3.5. CncShieldGUI Example¶
This module provides a graphical interface to an Arduino running the
CNCShieldServer sketch. It is built on top of the ArduinoGUI
module.
Contents
3.5.2. CncShieldGUI.py¶
CncShieldGUI.py
GUI controller for the CNC_Shield_Server Arduino sketch.
Copyright (c) 2015, Garth Zeglin. All rights reserved. Licensed under the terms of the BSD 3-clause license.
-
class
CncShieldGUI.CncShieldGUI.
CncShieldGUIController
(client, port=None, **kwargs)[source]¶ Application-specific control object to manage the GUI for the CNC_Shield_Server. This class creates a generic ArduinoConsole GUI, adds application-specific GUI controls, and manages basic I/O using a protocol object.
Parameters: - client – a protocol object to receive commands from the GUI
- port – the name of the serial port device
- kwargs – collect any unused keyword arguments
Callback function activated to disable the motor drivers when the GUI button is pressed.
Callback function activated to enable the motor drivers when the GUI button is pressed.
Callback function activated to move to the origin when the GUI button is pressed.
-
port_data_ready
(fd)[source]¶ Callback function activated when data is received from the Arduino. This just prints a message, but would usually process status messages as part of a command protocol.
Callback function activated to start a user script.
Callback function activated to interrupt a user script.
3.5.3. CncShieldProtocol.py¶
CncShieldProtocol.py : communications logic to control an Arduino running CNC_Shield_Server
Copyright (c) 2015, Garth Zeglin. All rights reserved. Licensed under the terms of the BSD 3-clause license.
-
class
CncShieldGUI.CncShieldProtocol.
CncShieldClient
(port=None, verbose=False, debug=False, **kwargs)[source]¶ Class to manage a connection to a CNC_Shield_Server running on a serial-connected Arduino.
Parameters: - port – the name of the serial port device
- verbose – flag to increase console output
- debug – flag to print raw inputs on sconsole
- kwargs – collect any unused keyword arguments
-
close_serial_port
()[source]¶ Shut down the serial connection to the Arduino, after which this object may no longer be used.
-
distance_to_target
()[source]¶ Return the estimated total number of microsteps remaining to reach the target position, based on the most recently received position status.
-
is_startup_delay_complete
()[source]¶ Check whether the boot time interval has completed after opening the serial port.
-
issue_move
(position)[source]¶ Issue a command to move to a [x, y, z] absolute position (specified in microsteps). Returns immediately.
Parameters: position – a list or tuple with at least three elements
-
open_serial_port
()[source]¶ Open the serial connection to the Arduino and initialize communciations.
-
position_updated
()[source]¶ Method called whenever a new position update is received from the Arduino. The default implentation is null but this may be overridden in subclasses as a hook for this event.
3.5.4. run_gui.py¶
This script launches the GUI. It also includes a small custom subclass of the protocol to add scripting behaviors. This demonstrates layering in a state machine into the GUI event system.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | #!/usr/bin/env python
"""\
run_gui.py
Sample script to create and operate a graphical user interface for an Arduino
program. This interface shell communicates over a serial port to an Arduino
running a sketch which includes line-oriented text input and output.
Copyright (c) 2015, Garth Zeglin. All rights reserved. Licensed under the terms
of the BSD 3-clause license.
"""
from __future__ import print_function
import os, sys, argparse
# This requires the PyQt4 module to interface to the Qt GUI toolkit. For
# documentation on the PyQt4 API, see
# http://pyqt.sourceforge.net/Docs/PyQt4/index.html
from PyQt4 import QtGui
# Make sure that the Python libraries also contained within this course package
# are on the load path. This adds the parent folder to the load path, assuming that this
# script is still located within a subfolder of the Python library tree.
sys.path.insert(0, os.path.abspath(".."))
from CncShieldGUI.CncShieldGUI import CncShieldGUIController
from CncShieldGUI.CncShieldProtocol import CncShieldClient
################################################################
class ScriptClient(CncShieldClient):
"""A custom subclass of CncShieldClient to implement the Arduino control
protocol with additional methods to support scripted movement.
"""
def __init__(self, *args, **kwargs):
# initialize the parent class
CncShieldClient.__init__(self, *args, **kwargs)
# define a simple pose animation system which moves in a sequence from target to target
self.next_pose = None
self.pose_sequence = [ [1000, 2000, 3000],
[0, 0, 0],
[1000, 2000, 3000],
[0, 0, 0],
[3000, 2000, 1000],
[0, 0, 0],
[3000, 2000, 1000],
[0, 0, 0] ]
return
# additional methods specialized to this class
def start_script(self):
print("Restarting script state machine.")
self.next_pose = 0
# Issue a move command to stop at the most recent position in case the
# target is currently set far away.
self.issue_move( self.position )
return
def stop_script(self):
print("Stopping script state machine.")
self.next_pose = None # stop sequencer
self.issue_move( self.position ) # stop motors here
return
def position_updated(self):
"""Receive notification calls when new data is received from the Arduino."""
# wait until the most recent target was reached
if self.distance_to_target() == 0:
# if sequencer is running
if self.next_pose is not None:
# issue another pose if available
if self.next_pose < len(self.pose_sequence):
print("Issuing pose %d in sequence." % self.next_pose)
self.issue_move(self.pose_sequence[self.next_pose])
self.next_pose += 1
################################################################
# Main script follows. This sequence is executed when the script is initiated from the command line.
if __name__ == "__main__":
# process command line arguments
parser = argparse.ArgumentParser(description = """Run CncShieldGUI.""")
parser.add_argument( '-v', '--verbose', action='store_true', help='Enable more detailed output.' )
parser.add_argument( '--debug', action='store_true', help='Enable debugging output.' )
parser.add_argument( '-p', '--port', default='/dev/tty.usbmodem1411', help='Specify the name of the Arduino serial port device (default is /dev/tty.usbmodem1411).')
args = parser.parse_args()
# Convert the command line arguments from a Namespace to a set of keyword
# arguments and initialize the client protocol controller. Note that this
# will ignore extraneous values, e.g., from additional command argument
# inputs unneeded by this object.
keyword_args = vars(args)
client = ScriptClient(**keyword_args)
# initialize the Qt system itself
app = QtGui.QApplication(sys.argv)
# Create the GUI controller and connect it to the protocol controller. It
# will create a generic console window and extend the interface window
# controls to customize it for this particular application.
gui = CncShieldGUIController( client, **keyword_args )
print("""Starting up CncShieldGUI.
Toggle the 'Connected' switch to open the Arduino serial port.
Click 'Enable' while connected to enable motor drivers.
Click 'Start' to begin the script sequence.
""")
# run the event loop until the user is done
sys.exit(app.exec_())
|
3.5.5. movement_test.py¶
This is a console-only script (no GUI) to demonstrate scripted motion using the same underlying protocol class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | #!/usr/bin/env python
"""\
movement_test.py
Test the protocol for the CNC_Shield_Server without a GUI by moving through a sequence of positions.
Copyright (c) 2015, Garth Zeglin. All rights reserved. Licensed under the terms
of the BSD 3-clause license.
"""
from __future__ import print_function
import os, sys, time, argparse
# Make sure that the Python libraries also contained within this course package
# are on the load path. This adds the parent folder to the load path, assuming that this
# script is still located within a subfolder of the Python library tree.
sys.path.insert(0, os.path.abspath(".."))
from CncShieldGUI.CncShieldProtocol import CncShieldClient
################################################################
def move_to( client, position ):
"""Issue a command to move to a [x, y, z] absolute position (specified in
microsteps). Waits until completion.
:param position: a list or tuple with at least three elements
"""
client.issue_move(position)
while True:
client.wait_for_position_update()
# wait until all motors have reached their targets
if client.distance_to_target() == 0:
return
################################################################
# The following section is run when this is loaded as a script.
if __name__ == "__main__":
# Initialize the command parser.
parser = argparse.ArgumentParser( description = """Simple test client to send data to the CNC_Shield_Server on an Arduino.""")
parser.add_argument( '-v', '--verbose', action='store_true', help='Enable more detailed output.' )
parser.add_argument( '--debug', action='store_true', help='Enable debugging output.' )
parser.add_argument( '-p', '--port', default='/dev/tty.usbmodem1411', help='Specify the name of the Arduino serial port device (default is /dev/tty.usbmodem1411).')
# Parse the command line, returning a Namespace.
args = parser.parse_args()
# Convert the Namespace to a set of keyword arguments and initialize the
# client object. Note that it will ignore extraneous values, e.g., from
# additional command argument inputs unneeded by this object.
client = CncShieldClient(**vars(args))
print("Connecting to Arduino.")
client.open_serial_port()
print("Sleeping briefly while Arduino boots...")
while client.is_startup_delay_complete() is False:
time.sleep(0.5)
# throw away any extraneous input
print("Flushing Arduino input...")
client.port.flushInput()
# Begin the motion sequence. This may be safely interrupted by the user pressing Control-C.
try:
print("Beginning movement sequence.")
client.motor_enable()
move_to(client, [1000, 2000, 3000])
move_to(client, [0, 0, 0])
move_to(client, [1000, 2000, 3000])
move_to(client, [0, 0, 0])
move_to(client, [3000, 2000, 1000])
move_to(client, [0, 0, 0])
move_to(client, [3000, 2000, 1000])
move_to(client, [0, 0, 0])
except KeyboardInterrupt:
print("User interrupted motion.")
# Issue a command to turn off the drivers, then shut down the connection.
client.motor_enable(False)
client.close_serial_port()
|