Python Stepper Scripting

The stepper_motion_script.py Python script demonstrates sending motion commands from a laptop to the CNCShieldServer Arduino Sketch running on an Arduino.

The script file can be found in the cncclient folder, and are also available in a single zip file.

CncShieldClient Class Documentation

class cncclient.stepper_motion_script.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()[source]

Shut down the serial connection to the Arduino, after which this object may no longer be used.

motor_enable(value=True)[source]

Issue a command to enable or disable the stepper motor drivers.

move_to(position)[source]

Issue a command to move to a [x, y, z] absolute position (specified in microsteps) and wait until completion.

Parameters:position – a list or tuple with at least three elements
wait_for_wakeup()[source]

Issue a status query and wait until an ‘awake’ status has been received.

Full Script Code

  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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#!/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

#================================================================
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
    
    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

    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
            
    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()
            
    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()