#!/usr/bin/env python
"""\
test_client.py : sample code in Python to communicate with an Arduino running CNC_Shield_Server
Copyright (c) 2015, Garth Zeglin. All rights reserved. Licensed under the terms
of the BSD 3-clause license.
"""
#================================================================
from __future__ import print_function
import argparse
import time
# This requires a pySerial installation.
# Package details: https://pypi.python.org/pypi/pyserial,
# Documentation: http://pythonhosted.org/pyserial/
import serial
#================================================================
[docs]class CncShieldClient(object):
"""Class to manage a connection to a CNC_Shield_Server running on a
serial-connected Arduino.
:param port: the name of the serial port device
:param verbose: flag to increase console output
:param debug: flag to print raw inputs on sconsole
:param kwargs: collect any unused keyword arguments
"""
def __init__(self, port=None, verbose=False, debug=False, **kwargs ):
# initialize the client state
self.arduino_time = 0
self.position = [0, 0, 0]
self.target = [0, 0, 0]
self.verbose = verbose
self.debug = debug
self.awake = False
# open the serial port, which should also reset the Arduino
self.port = serial.Serial( port, 115200, timeout=5 )
if self.verbose:
print("Opened serial port named", self.port.name)
print("Sleeping briefly while Arduino boots...")
# wait briefly for the Arduino to finish booting
time.sleep(2) # units are seconds
# throw away any extraneous input
self.port.flushInput()
return
[docs] def close(self):
"""Shut down the serial connection to the Arduino, after which this object may no longer be used."""
self.port.close()
self.port = None
return
def _wait_for_input(self):
line = self.port.readline().rstrip()
if line:
elements = line.split()
if self.debug:
print("Received: '%s'" % line)
if elements[0] == 'txyz':
self.arduino_time = int(elements[1])
self.position = [int(s) for s in elements[2:]]
elif elements[0] == 'awake':
self.awake = True
elif elements[0] == 'dbg':
print("Received debugging message:", line)
else:
print("Unknown status message: ", line)
return
def _send_command(self, string):
if self.verbose:
print("Sending: ", string)
self.port.write( string+'\n')
return
[docs] def motor_enable( self, value=True):
"""Issue a command to enable or disable the stepper motor drivers."""
self._send_command( "enable 1" if value is True else "enable 0" )
return
[docs] def wait_for_wakeup(self):
"""Issue a status query and wait until an 'awake' status has been received."""
while self.awake is False:
self._send_command( "ping" )
self._wait_for_input()
[docs] def move_to(self, position):
"""Issue a command to move to a [x, y, z] absolute position (specified in microsteps) and wait until completion.
:param position: a list or tuple with at least three elements
"""
self._send_command( "goto %d %d %d" % tuple(position))
self.target = position
while self.position[0] != position[0] or self.position[1] != position[1] or self.position[2] != position[2]:
self._wait_for_input()
if self.verbose:
print ("Position:", self.position)
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( '--extra', action='store_true', help='Unused.')
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))
# Begin the motion sequence. This may be safely interrupted by the user pressing Control-C.
try:
print("Waiting for wakeup.")
client.wait_for_wakeup()
print("Beginning movement sequence.")
client.motor_enable()
client.move_to([1000, 2000, 3000])
client.move_to([0, 0, 0])
client.move_to([1000, 2000, 3000])
client.move_to([0, 0, 0])
client.move_to([3000, 2000, 1000])
client.move_to([0, 0, 0])
client.move_to([3000, 2000, 1000])
client.move_to([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()