diff --git a/README.md b/README.md
index fe98810..c81b838 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,17 @@
# mm-python-api
MachineMotion Python API
+
+## Version: 1.6.5
+
+Date: June 4 2019
+
+### Bug Fixes:
+- Fix Line Number mismatch with the help of the 'resend' message
+- Fix application hang on termination
+
+### Improvements:
+- Added the indexer and conveyot gain in the constants definitions.
+- Added more examples for each sensor port for the different control devices functions.
+- Auto reconnect on connection loss
+- Instead of starting a new thread each 0.1 seconds, we now start one thread at the beginning and keep it alive forever to receive messages from the server
+
diff --git a/_MachineMotion.py b/_MachineMotion.py
new file mode 100644
index 0000000..4a78707
--- /dev/null
+++ b/_MachineMotion.py
@@ -0,0 +1,773 @@
+# File name: _MachineMotion_1_6_5.py #
+# Version: 1.6.5 #
+# Author: Francois Giguere #
+# Note: Information about all the g-Code #
+# commands supported are available at #
+# the following location of the SDK: #
+# ./documentation #
+
+# Import standard libraries
+import json, time, threading, sys
+
+# Import package dependent libraries
+from pathlib import Path
+from socketIO_client import SocketIO, BaseNamespace
+import paho.mqtt.client as mqtt
+
+# Misc. Variables
+motion_completed = "false"
+waiting_motion_status = "false"
+
+machineMotionRef = None
+gCodeCallbackRef = None
+lastSendTimeStamp = None
+
+class CONTROL_DEVICE_SIGNALS:
+ SIGNAL0 = "SIGNAL0"
+ SIGNAL1 = "SIGNAL1"
+ SIGNAL2 = "SIGNAL2"
+ SIGNAL3 = "SIGNAL3"
+ SIGNAL4 = "SIGNAL4"
+ SIGNAL5 = "SIGNAL5"
+ SIGNAL6 = "SIGNAL6"
+
+class CONTROL_DEVICE_TYPE:
+ IO_EXPANDER_GENERIC = "IO_EXPANDER_GENERIC"
+ ENCODER = "ENCODER"
+
+class CONTROL_DEVICE_PORTS:
+ SENSOR4 = "SENSOR4"
+ SENSOR5 = "SENSOR5"
+ SENSOR6 = "SENSOR6"
+
+class DIRECTION:
+ positive = "positive"
+ negative = "negative"
+
+class AXIS_NUMBER:
+ DRIVE1 = 1
+ DRIVE2 = 2
+ DRIVE3 = 3
+
+class DEFAULT_IP_ADDRESS:
+ usb_windows = "192.168.7.2"
+ usb_mac_linux = "192.168.6.2"
+ ethernet = "192.168.0.2"
+
+class NETWORK_MODE:
+ static = "static"
+ dhcp = "dhcp"
+
+class MICRO_STEPS:
+ ustep_full = 1
+ ustep_2 = 2
+ ustep_4 = 4
+ ustep_8 = 8
+ ustep_16 = 16
+
+class MECH_GAIN:
+ timing_belt_150mm_turn = 150
+ legacy_timing_belt_200_mm_turn = 200
+ ballscrew_10mm_turn = 10
+ legacy_ballscrew_5_mm_turn = 5
+ indexer_deg_turn = 85
+ conveyor_mm_turn = 157
+
+def fastMotionStatusCallback(data, mm):
+ global motion_completed
+ global waiting_motion_status
+
+ # Was a motion status request received
+ if data.find("Motion Status") != -1:
+ # print "A motion status was requested"
+ if data.find("Motion Status = COMPLETED") != -1 and waiting_motion_status == "true":
+ # print "Move was completed"
+ motion_completed = "true"
+ waiting_motion_status = "false"
+ else:
+ # print "move is in progress"
+ motion_completed = "false"
+ waiting_motion_status = "true"
+ mm.__emit__("V0")
+
+#
+# Class that handles all gCode related communications
+# @status
+#
+class GCode:
+ mySocket = "unassigned"
+ ackReceived = "false"
+ waiting_motion_status = "false"
+ motion_completed = "false"
+ lineNumber = 1
+ lastPacket = {"data": "null", "lineNumber": "null"}
+ gCodeErrors = {"checksum": "Error:checksum mismatch, Last Line: ", "lineNumber": "Error:Line Number is not Last Line Number+1, Last Line: "}
+ userCallback = None
+
+ #
+ # Class constructor
+ # PRIVATE
+ # @param socket --- Description: The GCode class requires a socket object to communicate with the controller. The socket object is passed at contruction time.
+ # @status
+ #
+ def __init__(self, socket):
+ # Passing in the socket instance at construction
+ self.mySocket = socket
+
+ #
+ # Function that indicates if the GCode communication port is ready to send another command.
+ # @status
+ #
+ def __isReady__(self):
+ return self.ackReceived
+
+ #
+ # Function that indicates if the the last move has completed
+ # @status
+ #
+ def __isMotionCompleted__(self):
+ global motion_completed
+ return motion_completed
+
+ #
+ # Function to add the transport layer over a raw gCode packet.
+ # PRIVATE
+ # @param string --- Description: The GCode command to send.
+ # @status
+ #
+ def __addTransLayer__(self, string):
+ # Save the last packet sent
+ self.lastPacket = {"data": string, "lineNumber": self.lineNumber}
+
+ # Add the line number to the packet
+ string = "N" + str(self.lineNumber) + " " + string
+
+ # Increment the line number
+ self.lineNumber = self.lineNumber + 1
+
+ cs = 0
+
+ # Calculate the checksum
+ for i in range (0, len(string)):
+ cs = cs ^ ord(string[i])
+
+ # Returns the completed packet with checksum and line number
+ return string + "*" + str(cs);
+
+ #
+ # Upon reception of an error message, this function extract the line number in the communication at which the error occured.
+ # PRIVATE
+ # @param string --- Description: The error message from the controller.
+ # @status
+ #
+ def __extractLineNumberInError__(self, message):
+ if message.find(self.gCodeErrors['checksum']) != -1:
+ lineNumberBaseIndex = len(self.gCodeErrors['checksum']) - 1
+ return int(message[lineNumberBaseIndex:])
+ elif message.find(self.gCodeErrors['lineNumber']) != -1:
+ lineNumberBaseIndex = len(self.gCodeErrors['lineNumber']) - 1
+ return int(message[lineNumberBaseIndex:])
+
+ #
+ # Upon reception of a resend message, this function extract the line number in the message.
+ # PRIVATE
+ # @param string --- Description: The error message from the controller.
+ # @status
+ #
+ def __extractLineNumberInResend__(self, message):
+ lineNumberBaseIndex = len('Resend: ') - 1
+ return int(message[lineNumberBaseIndex:])
+
+ #
+ # Function to reset the current line number in the communication.
+ # PRIVATE
+ # @param line --- Description: The new line number.
+ # @status
+ #
+ def __setLineNumber__(self, line):
+ self.lineNumber = line
+ self.__emit__("M110 N" + str(line))
+
+ #
+ # Function to map API axis labels to motion controller axis labels
+ # PRIVATE
+ # @param axis --- Description: The API axis label.
+ # @status
+ #
+ def __getTrueAxis__(self, axis):
+ if axis == 1: return "X"
+ elif axis == 2: return "Y"
+ elif axis == 3: return "Z"
+ else: return "Axis Error"
+
+ #
+ # Function that packages the data in a JSON object and sends to the MachineMotion server over a socket connection.
+ # PRIVATE
+ # @param axis --- Description: The API axis label.
+ # @status
+ #
+ def __send__(self, cmd, data):
+ global lastSendTimeStamp
+
+ # Add the transport layer data
+ data['value'] = self.__addTransLayer__(data['value'])
+
+ # Serialize the dictionary in json format
+ packet = json.dumps(data)
+
+ # Reset the GCode status
+ self.ackReceived = "false"
+
+ lastSendTimeStamp = time.time()
+
+ # Sending
+ self.mySocket.emit(cmd, packet)
+
+ #
+ # Function to send a raw G-Code ASCII command
+ # @param gCode --- Description: gCode is string representing the G-Code command to send to the controller. Type: string.
+ # @status
+ #
+ def __emit__(self, gCode):
+
+ # # When a G-Code command is sent, it is assumed that it is a motion command and the move_completed attriute is set to "false". The user has to used
+ # # the isMotionCompleted() function to verify if the motion is completed.
+ # if(gCode != "V0"):
+ # self.motion_completed = "false"
+
+ # Object to transmit data
+ gCodeCmd = {"command": "gCode", "value": gCode}
+
+ self.__send__('gCodeCmd', gCodeCmd)
+
+ time.sleep(0.05)
+
+ @staticmethod
+ def __userCallback__(data): return
+
+ #
+ # Function that executes upon reception of messages from the motion controller. The user configured callback in ran after this function.
+ # PUBLIC
+ # @param data --- Description: The data sent by the motion controller. Type: string.
+ # @status
+ #
+ def __rxCallback__(self, data):
+
+ global waiting_motion_status
+ global lastSendTimeStamp
+
+ # print "DEBUG---Last command sent: " + str(self.lastPacket)
+ # print "DEBUG---Last received data: " + data
+
+ # Look if the echo of the last command was found in the incoming data
+ if (data.find(self.lastPacket['data'])) != -1:
+ # The last command was acknowledged
+ self.ackReceived = "true"
+ # print "DEBUG---Received ack for packet " + str(self.lastPacket['data']) + ", line = " + str(self.lastPacket['lineNumber']) + "\n"
+ #self.lastPacket = {"data": "null", "lineNumber": "null"}
+ # Special ack for homing
+ elif data.find('X:0.00') != -1 and self.lastPacket['data'].find('G28') != -1:
+ # The last command was acknowledged
+ self.ackReceived = "true"
+ # print "DEBUG---Received ack for homing"
+ #self.lastPacket = {"data": "null", "lineNumber": "null"}
+ # Special ack for homing
+ elif data.find('DEBUG') != -1 and self.lastPacket['data'].find('M111') != -1:
+ # The last command was acknowledged
+ self.ackReceived = "true"
+ # print "DEBUG---Received ack for debug setup" + "\n"
+ #self.lastPacket = {"data": "null", "lineNumber": "null"}
+ # Look if errors were received
+ elif (data.find('Error:') != -1):
+ # print "DEBUG--Error received from controller. Last line correct line was " + str(self.__extractLineNumberInError__(data))
+ # print "DEBUG--Error received from controller. Last line number sent " + str(self.lastPacket['lineNumber'])
+
+ if (self.__extractLineNumberInError__(data) == (int(self.lastPacket['lineNumber']) - 1)):
+ # print "DEBUG--Error received on line " + str(self.__extractLineNumberInError__(data))
+ self.lastPacket = {"data": self.lastPacket['data'], "lineNumber": int(self.__extractLineNumberInError__(data))+1}
+ self.ackReceived = "false"
+ self.__emit__(self.lastPacket['data'])
+ elif (data.find('Resend:') != -1):
+ self.lineNumber = self.__extractLineNumberInResend__(data)
+
+ fastMotionStatusCallback(data, self)
+
+ self.__userCallback__(data)
+
+
+
+ # Private function
+ class ListenToSocket(threading.Thread):
+ def __init__(self, gCode):
+ self.gcode = gCode
+ threading.Thread.__init__(self)
+ def run(self):
+ global lastSendTimeStamp
+
+ self.gcode.mySocket.on('machineMotionAck', self.gcode.__rxCallback__)
+ while True:
+ if (self.gcode.mySocket.connected and self.gcode.ackReceived == 'false' and (lastSendTimeStamp is not None) and (time.time() - lastSendTimeStamp > 5)):
+ # Trigger a reconnection
+ self.gcode.mySocket.disconnect()
+ self.gcode.mySocket.connect('', True)
+ self.gcode.mySocket.wait(1)
+
+ def __keepSocketAlive__(self):
+ thread = GCode.ListenToSocket(self)
+ thread.daemon = True # Stops this thread if main one exits
+ thread.start()
+
+ # Private function
+ def __setUserCallback__(self, userCallback):
+
+ # Save the user function to call on incoming messages locally
+ self.__userCallback__ = userCallback
+
+ # Start the periodic process that fetches the sockets that were received by the OS
+ self.__keepSocketAlive__()
+
+#
+# Class that encapsulates code that waits for a certain socket topic to be received
+# @status
+#
+class WaitForSocketTopic:
+
+ response_received = False
+ mySocket = None
+ myTopic = None
+
+ # Function redefined by the user
+ @staticmethod
+ def _user_callback_(data): return
+
+ # Wrapper to invoke the user defined function and manage the completion flag
+ def _callback_(self, data):
+ self.response_received = True
+ self._user_callback_(data)
+
+ def set_user_callback(self, callback):
+ self._user_callback_ = callback
+
+ def wait_for_response(self, socket, topic, callback):
+ self.response_received = False
+
+ self.mySocket = socket
+ self.myTopic = topic
+ self._user_callback_ = callback
+
+ while self.response_received == False:
+ self.mySocket.on(self.myTopic, self._callback_)
+ self.mySocket.wait(seconds = 0.1)
+
+#
+# Class used to encapsulate the MachineMotion controller
+# @status
+#
+class MachineMotion:
+ # Class variables
+ mySocket = "notInitialized"
+ myConfiguration = {"machineIp": "notInitialized", "machineGateway": "notInitialized", "machineNetmask": "notInitialized"}
+ myGCode = "notInitialized"
+ myGCode = "notInitialized"
+
+ myMqttClient = None
+
+ myAxis1_steps_mm = "notInitialized"
+ myAxis2_steps_mm = "notInitialized"
+ myAxis3_steps_mm = "notInitialized"
+
+ validPorts = ["SENSOR4", "SENSOR5", "SENSOR6"]
+ validDevices = ["IO_EXPANDER_GENERIC", "ENCODER"]
+ validSignals = ["SIGNAL4", "SIGNAL5", "SIGNAL6", "SIGNAL7", "SIGNAL0", "SIGNAL1", "SIGNAL2", "SIGNAL3"]
+
+ portInputs = dict.fromkeys(validPorts, 0)
+ signalMasks = dict.fromkeys(validSignals, 0)
+ for i, v in enumerate(validSignals) :
+ signalMasks[v] = 1 << i
+
+ valid_u_step = [1, 2, 4, 8, 16]
+
+ attach_control_device_socket_response = WaitForSocketTopic()
+ read_control_device_socket_response = WaitForSocketTopic()
+ write_control_device_socket_response = WaitForSocketTopic()
+ detach_control_device_socket_response = WaitForSocketTopic()
+ attachedDevices = {}
+
+ def __isPortValid(self, portName):
+ if portName in self.validPorts:
+ return True
+
+ print "ERROR: Port name " + portName + " is invalid. Try 'SENSOR4', 'SENSOR5' or 'SENSOR6'."
+
+ sys.exit()
+
+ def __isDeviceValid(self, deviceName):
+ if deviceName in self.validDevices:
+ return True
+
+ print "ERROR: Device name " + deviceName + " is invalid. Try 'ENCODER' or 'IO_EXPANDER_GENERIC'."
+
+ sys.exit()
+
+ def __isSignalValid(self, signalName):
+ if signalName in self.validSignals:
+ return True
+
+ print "ERROR: Signal name " + signalName + " is invalid. Try 'SIGNAL0', 'SIGNAL1', 'SIGNAL2', 'SIGNAL3', 'SIGNAL4', 'SIGNAL5' or 'SIGNAL6'."
+
+ sys.exit()
+
+ #
+ # Function that will immediately stop all motion of all the axes
+ # @status
+ #
+ def emitStop(self):
+ global motion_completed
+
+ motion_completed = "false"
+
+ self.myGCode.__emit__("M410")
+
+ # Wait and send a dummy packet to insure that other commands after the emit stop are not flushed.
+ time.sleep(0.500)
+ self.myGCode.__emit__("G0 X0")
+ while self.isReady() != "true": pass
+
+ #
+ # Function that will initiate the homing sequence of all axes. The sequence will home all axes using the endstop signals
+ # @status
+ #
+ def emitHomeAll(self):
+ global motion_completed
+
+ motion_completed = "false"
+
+ self.myGCode.__emit__("G28")
+
+ #
+ # Function that will initiate the homing sequence for the axis specified. The sequence will home the axis using the endstops signals.
+ # @param axis --- Description: "axis" is the axis number that will be set to home location. --- Type: number.
+ # @status
+ #
+ def emitHome(self, axis):
+ global motion_completed
+
+ motion_completed = "false"
+
+ self.myGCode.__emit__("G28 " + self.myGCode.__getTrueAxis__(axis))
+
+ #
+ # Function to send a displacement speed configuration command
+ # @param mm_per_min --- Description: mm_per_mim is the displacement speed in mm/min --- Type: number.
+ # @status
+ #
+ def emitSpeed(self, mm_per_min):
+ self.myGCode.__emit__("G0 F" +str(mm_per_min))
+ while self.isReady() != "true": pass
+
+ #
+ # Function to send a displacement acceleration configuration command
+ # @param mm_per_sec_sqr --- Description: mm_per_sec_sqr is the displacement acceleration in mm/sec^2 --- Type: number.
+ # @status
+ #
+ def emitAcceleration(self, mm_per_sec_sqr):
+ self.myGCode.__emit__("M204 T" + str(mm_per_sec_sqr))
+ while self.isReady() != "true": pass
+
+ #
+ # Function to send an absolute move command to the MachineMotion controller
+ # @param axis --- Description: axis is the axis on which the command will be applied. --- Type: string or number.
+ # @param position --- Description: position is the position from its home location where the axis will go. --- Type: string or number.
+ # @status
+ #
+ def emitAbsoluteMove(self, axis, position):
+ global motion_completed
+
+ motion_completed = "false"
+
+ # Set to absolute motion mode
+ self.myGCode.__emit__("G90")
+ while self.isReady() != "true": pass
+
+ # Transmit move command
+ self.myGCode.__emit__("G0 " + self.myGCode.__getTrueAxis__(axis) + str(position))
+ while self.isReady() != "true": pass
+
+ #
+ # Function to send a relative move command to the MachineMotion controller
+ # @param axis --- Description: axis is the axis on which the command will be applied. --- Type: int or string.
+ # @param direction --- Description: direction is the direction in which the relative move will be conducted. --- Type: string of value equal to "positive" or "negative"
+ # @param distance is the distance of the relative move.
+ # @status
+ #
+ def emitRelativeMove(self, axis, direction, distance):
+ global motion_completed
+
+ motion_completed = "false"
+
+ # Set to relative motion mode
+ self.myGCode.__emit__("G91")
+ while self.isReady() != "true": pass
+
+ if direction == "positive":distance = "" + str(distance)
+ elif direction == "negative": distance = "-" + str(distance)
+
+ # Transmit move command
+ self.myGCode.__emit__("G0 " + self.myGCode.__getTrueAxis__(axis) + str(distance))
+ while self.isReady() != "true": pass
+
+ #
+ # Function to send a raw G-Code ASCII command
+ # @param gCode --- Description: gCode is string representing the G-Code command to send to the controller. Type: string.
+ # @status
+ #
+ def emitgCode(self, gCode):
+ global motion_completed
+
+ motion_completed = "false"
+
+ self.myGCode.__emit__(gCode)
+
+ #
+ # Function that indicates if the GCode communication port is ready to send another command.
+ # @status
+ #
+ def isReady(self):
+ return self.myGCode.__isReady__()
+
+ #
+ # Function that indicates if the the last move has completed
+ # @status
+ #
+ def isMotionCompleted(self):
+ global motion_completed
+ return motion_completed
+
+ def waitForMotionCompletion(self):
+ self.emitgCode("V0")
+ while self.isMotionCompleted() != "true": pass
+
+ #
+ # Function to setup the static IP and the router gateway of the MachineMotion controller
+ # @param machineIp --- Description: desired static ip address to assign to the MachineMotion controller. --- Type: string (xxx.xxx.xxx.xxxx) where x are numbers.
+ # @param gatewayIP --- Description: ip address of the LAN router. Setting a proper gateway ip addreess enables MachineMotion to access the internet for downloads --- Type: string (xxx.xxx.xxx.xxxx) where x are numbers.
+ # @note: --- For the MachineMotion to access the Internet after an configIp() call, the MachineMotion device must be rebooted.
+ # @status
+ #
+ def configMachineMotionIp(self, mode, machineIp, machineNetmask, machineGateway):
+
+ # Create a new object and augment it with the key value.
+ self.myConfiguration["mode"] = mode
+ self.myConfiguration["machineIp"] = machineIp
+ self.myConfiguration["machineNetmask"] = machineNetmask
+ self.myConfiguration["machineGateway"] = machineGateway
+
+
+ self.mySocket.emit('configIp', json.dumps(self.myConfiguration))
+
+ time.sleep(1)
+
+ #
+ # Function to configure the axis motion.
+ # @param axis --- Description: The axis number. --- Type: number [1, 2, 3]
+ # @param u_step --- Description: uStep setting. --- Type: number either [1, 2, 4, 8, 16]
+ # @param mech_gain --- Description: Mechanical gain of the axis in mm / turn. --- Type: number
+ # @status
+ #
+ def configAxis(self, axis, u_step, mech_gain):
+
+
+ # validate that the uStep setting is valid
+ if (self.valid_u_step.index(u_step) != -1):
+ if(axis == 1):
+ self.myAxis1_steps_mm = 200 * u_step / mech_gain
+ self.myGCode.__emit__("M92 " + self.myGCode.__getTrueAxis__(axis) + str(self.myAxis1_steps_mm))
+ elif(axis == 2):
+ self.myAxis1_steps_mm = 200 * u_step / mech_gain
+ self.myGCode.__emit__("M92 " + self.myGCode.__getTrueAxis__(axis) + str(self.myAxis1_steps_mm))
+ elif(axis == 3):
+ self.myAxis1_steps_mm = 200 * u_step / mech_gain
+ self.myGCode.__emit__("M92 " + self.myGCode.__getTrueAxis__(axis) + str(self.myAxis1_steps_mm))
+ else:
+ pass
+ # print "Argument error, {configAxis(self, axis, u_step, mech_gain)}, {axis} argument is invalid"
+
+ else:
+ pass
+ # print "Argument error, {configAxis(self, axis, u_step, mech_gain)}, {u_step} argument is invalid"
+ #
+ # Function to save/persist data in the MachineMotion Controller (key - data pair)
+ # @param key --- Description: key is a string that identifies the data to save for future retrieval. --- Type: string or number.
+ # @param data --- Description: data is a dictionary containing the data to save. --- Type: dictionary.
+ # @status
+ #
+ def saveData(self, key, data):
+ # Create a new object and augment it with the key value.
+ dataPack = {}
+ dataPack["fileName"] = key;
+ dataPack["data"] = data;
+
+ # Send the request to MachineMotion
+ self.mySocket.emit('saveData', json.dumps(dataPack))
+ time.sleep(0.05)
+
+ #
+ # Function to retrieve saved/persisted data in the MachineMotion Controller (key - data pair)
+ # @param key --- Description: key is a string that identifies the data to retrieve. --- Type: string.
+ # @param callback --- Description: callback is the function to invoke when the asynchronous data is received. --- Type: function with on argument that will contain the data in json serialized format.
+ # @status
+ #
+ def getData(self, key, callback):
+ #Send the request to MachineMotion
+
+ self.mySocket.emit('getData', key)
+
+ # On reception of the data invoke the callback function.
+ self.mySocket.on('getDataResponse', callback)
+
+ def detachControlDevice(self, port, callback):
+
+ if (self.__isPortValid(port)):
+ if port in self.attachedDevices.keys():
+ del self.attachedDevices[port]
+
+ # Assign the user callback to the socket response object
+ self.detach_control_device_socket_response.set_user_callback(callback)
+
+ # Send a command to reada control device that is connected to the MachineMotion controller
+ detachCmd = {"port": port}
+ packet = json.dumps(detachCmd)
+ self.mySocket.emit('detachControlDevice', packet)
+
+ self.detach_control_device_socket_response.wait_for_response(self.mySocket, 'detachControlDeviceResponse', callback)
+
+ #time.sleep(0.25)
+
+ def attachControlDevice(self, port, device, callback):
+
+ if (self.__isPortValid(port) and self.__isDeviceValid(device)):
+ self.attachedDevices[port] = device
+
+ # Assign the user callback to the socket response object
+ self.attach_control_device_socket_response.set_user_callback(callback)
+
+ # Send a command to reada control device that is connected to the MachineMotion controller
+ attachCmd = {"port": port, "device": device}
+ packet = json.dumps(attachCmd)
+ self.mySocket.emit('attachControlDevice', packet)
+
+ self.attach_control_device_socket_response.wait_for_response(self.mySocket, 'attachControlDeviceResponse', callback)
+
+ #time.sleep(0.25)
+
+ def readControlDevice(self, port, signal, callback):
+ if (self.__isPortValid(port) and self.__isSignalValid(signal)):
+ if port not in self.attachedDevices.keys() or self.attachedDevices[port] == "IO_EXPANDER_GENERIC":
+ # Unattached or IO device
+ callback("true" if (self.portInputs[port] & self.signalMasks[signal] > 0) else "false")
+ # Legacy devices
+ else:
+ # Send a command to read a control device that is connected to the MachineMotion controller
+ readCmd = {"port": port, "signal": signal}
+ packet = json.dumps(readCmd)
+ self.mySocket.emit('readControlDevice', packet)
+
+ self.read_control_device_socket_response.wait_for_response(self.mySocket, 'readControlDeviceResponse', callback)
+
+
+ def writeControlDevice(self, port, signal, value, callback):
+ if (self.__isPortValid(port) and self.__isSignalValid(signal)):
+ # Unattached or IO device
+ if port not in self.attachedDevices.keys() or self.attachedDevices[port] == "IO_EXPANDER_GENERIC":
+ portNumber = self.validPorts.index(port) + 1
+ signalNumber = self.validSignals.index(signal) - 4
+ self.myMqttClient.publish('digitalOutput/' + str(portNumber) + '/' + str(signalNumber), '1' if value else '0')
+ callback("true" if value else "false")
+ # Legacy devices
+ else :
+ # Assign the user callback to the socket response object
+ self.write_control_device_socket_response.set_user_callback(callback)
+
+ # Send a command to read a control device that is connected to the MachineMotion controller
+ writeCmd = {"port": port, "signal": signal, "value": value}
+ packet = json.dumps(writeCmd)
+ self.mySocket.emit('writeControlDevice', packet)
+
+ self.write_control_device_socket_response.wait_for_response(self.mySocket, 'writeControlDeviceResponse', callback)
+
+
+
+ def __onConnect(self, client, userData, flags, rc):
+ if rc == 0:
+ self.myMqttClient.subscribe('digitalInput/#')
+
+ def __onMessage(self, client, userData, msg):
+ port = int(msg.topic.replace('digitalInput/', '')) - 1
+ values = int(msg.payload, 16)
+ if(port >= 0 and port < len(self.validPorts)) :
+ self.portInputs[self.validPorts[port]] = values
+
+ def __onDisconnect(self, client, userData, rc):
+ print("Disconnected with rtn code [%d]"% (rc) )
+
+ def __establishConnection(self, isReconnection):
+ global gCodeCallbackRef
+
+ # Create the web socket
+ self.mySocket = SocketIO(self.myConfiguration['machineIp'], 8888, MySocketCallbacks)
+ self.myGCode = GCode(self.mySocket)
+
+ # Send a command to initialize the MachineMotion system
+ configCmd = {"parameter": "init", "value": "sysInit"}
+ packet = json.dumps(configCmd)
+ self.mySocket.emit('sysInit', packet)
+
+ # Give 5 seconds to the MachineMotion system to initialize the hardware
+ time.sleep(5)
+
+ # Set the callback to the user specified function. This callback is used to process incoming messages from the machineMotion controller
+ self.myGCode.__setUserCallback__(gCodeCallbackRef)
+
+ # Set the line number to initialize the communication
+ self.myGCode.__setLineNumber__(0)
+ while self.isReady() != "true": pass
+
+ #Set the debug level of the motionController to "247" to enable echo on all commands. Refer to http://marlinfw.org/docs/gcode/M111.html for more details.
+ self.emitgCode("M111 S247")
+ while self.isReady() != "true": pass
+
+ # Class constructor
+ def __init__(self, gCodeCallback, machineIp):
+ global machineMotionRef
+ global gCodeCallbackRef
+
+ self.myConfiguration['machineIp'] = machineIp
+
+ # MQTT
+ self.myMqttClient = mqtt.Client()
+ self.myMqttClient.on_connect = self.__onConnect
+ self.myMqttClient.on_message = self.__onMessage
+ self.myMqttClient.on_disconnect = self.__onDisconnect
+ self.myMqttClient.connect_async(machineIp)
+ self.myMqttClient.loop_start()
+
+ machineMotionRef = self
+ gCodeCallbackRef = gCodeCallback
+
+ self.__establishConnection(False)
+
+class MySocketCallbacks(BaseNamespace):
+
+ def on_connect(self):
+ print('[SocketIO Connected]')
+
+ def on_reconnect(self):
+ print('[SocketIO Reconnected]')
+ global lastSendTimeStamp
+ global machineMotionRef
+
+ lastSendTimeStamp = time.time()
+ machineMotionRef.myGCode.__setLineNumber__(1)
+
+ def on_disconnect(self):
+ print('[SocketIO Disconnected]')
diff --git a/_MachineMotion.pyc b/_MachineMotion.pyc
new file mode 100644
index 0000000..b54ce4f
Binary files /dev/null and b/_MachineMotion.pyc differ
diff --git a/documentation/auto_HTG0006_Using the MachineMotion Python SDK.html b/documentation/auto_HTG0006_Using the MachineMotion Python SDK.html
new file mode 100644
index 0000000..e37fce7
--- /dev/null
+++ b/documentation/auto_HTG0006_Using the MachineMotion Python SDK.html
@@ -0,0 +1,1001 @@
+
+
+
Introduction
+
This guide will cover the setup and use of Vention’s MachineMotion™ Python Software Development Kit (SDK). After reading this guide, the user will be ready to deploy custom motion & control applications using Vention’s MachineMotion controller. It is recommended to read the MachineMotion Quick Start Guide to get familiar with the technology prior to reading this document.
+
Typical System Overview
+
Numerous systems are built by centralizing application level software on a host computer. The software role is to interact with various devices (i.e. robotic devices, sensors, proprietary products, data acquisition equipment) and deliver the required application behavior (see Exhibit 2).
+
+
+
Figure 1: Typical System Configuration
+
+
The use of MachineMotion and Python is perfectly suited for this type of application, especially where an easy to deploy motion control system is necessary.
+
Installation
+
Windows
+
Follow the steps below to set up the MachineMotion Python
+library:
Note for Windows Users: make sure to add Python.exe to the PATH environment carriable as shown in Figure 2.
+
+
Figure 2: Make sure to "Add python.exe to path" if intsalling on Windows.
+
+
+
Open the command promt (Window users) or the terminal (Mac or Linux Users) and run the following installations
+
pip install -U socketIO-client
pip install -U pathlib
+
+
The MachineMotion Python library is now ready to use. Programs can be created and ran from the workspace folder.
+
+
+
Connectivity Setup
+
MachineMotion has two communication ports, one labeled USB and one labelled ETHERNET. Both use IP connectivity and have their own
+distinct IP address, see Figure 3.
+
+
+
Figure 3: MachineMotion Front Panel.
+
The IP address associated with the USB port is static (192.168.7.2 for Windows and 192.168.6.2 for Mac and Linux). It cannot be configured or modified. The USB port should be used for direct computer to MachineMotion connectivity.
+
The IP address associated with the ETHERNET port can be modified to suit the user’s network requirements. Refer to the How-to Guide: MachineMotion Network Setup for more details.
+
Configuration Functions
+
When creating a Python program to control the MachineMotion controller, the first step is always to create a MachineMotion instance. This will establish the communication with the actual controller and also expose different functions that can be used to send commands.
+
Following the creation of the MachineMotion instance, the network configuration and axis configuration need to be done. The functions to perform these steps are covered in this section.
+
+
MachineMotion(callback, ip)
+
Creates a MachineMotion instance and establishes the TCP/IP communication. This function is a constructor.
+
callback {def callbackName(data):}
+
+
Handle in which incoming messages from the controller can be processed. The data will be passed as a string argument to the callback.
+
+
ip {string}
+
+
Ip address of the MachineMotion controller to connect to.
+
+
return value {MachineMotion}
+
+
Instance created by the constructor.
+
+
Example
+
from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+deftemplateCallback(data):
+ print"Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+print"Controller connected"
Configure the axis mechanical gain and micro-stepping settings to ensure that motion is accurate.
+
axis {Number}
+
+
Axis to configure
+
+
u_step {MICRO_STEPS}
+
+
Micro-step setting
+
+
mech_gain {MECH_GAIN}
+
+
Mechanical gain of the axis in mm / turn
+
+
return value
+
+
none
+
+
Example
+
from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+deftemplateCallback(data):
+ print"Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Configure the axis number one, 8 uSteps and 150 mm / turn for a timing belt
+machine_motion_example = machine_motion_example.configAxis(1, MICRO_STEPS.ustep_8, MECH_GAIN.timing_belt_150mm_turn)
+
+print"--> Controller axis 1 configured"
from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+deftemplateCallback(data):
+ print"Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Saving a string on the controller
+machine_motion_example = machine_motion_example.saveData("data_1", "save_this_string_on_the_controller")
+
+print"--> Data sent on controller"
+
getData(key, callback)
+
Retrieve a key-value pair that was saved on the controller.
+
key {String}
+
+
Key is a string that identifies the data to retrieve
+
+
callback {def callbackName(data):}
+
+
Function to invoke once the data is available. The data will be passed as an argument to the callback as a serialized JSON string.
+
+
Example
+
from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+deftemplateCallback(data):
+ print"Controller gCode responses " + data
+
+# Define a callback to print the data retrieved using the getData function
+defprintGetDataResult(data):
+ print"--> Retrieved data = " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Saving a string on the controller
+machine_motion_example.saveData("data_1", "save_this_string_on_the_controller")
+
+
+machine_motion_example.getData("data_1", printGetDataResult)
Blocking function that waits for the MachineMotion controller to ackowledge the command before continuing code execution. This is a code flow control function.
+
return value
+
+
none
+
+
Example
+
from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+deftemplateCallback(data):
+ print"Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Homing axis one
+machine_motion_example.emitHome(1)
+# Wait for the message to be acknowledged by the motion controller
+while machine_motion_example.isReady() != "true": pass
+
+print"--> This line executes after the motion controller has acknowledged the reception of the command."
Output
+
Controller gCode responses MachineMotion Session Start
+Controller gCode responses echo:N0 M110 N0*125
+Controller gCode responses ok
+Controller gCode responses echo:N1 M111 S247*97
+Controller gCode responses echo:DEBUG:ECHO,INFO,ERRORS,COMMUNICATION
+Controller gCode responses ok
+Controller gCode responses echo:N2 G28 X*105
+Controller gCode responses X:0.00 Y:0.00 Z:0.00 E:0.00 Count X: 0 Y:0 Z:0
+Controller gCode responses ok
+--> This line executes after the motion controller has acknowledged the reception of the command.
+
waitForMotionCompletion()
+
Blocking function that holds the program execution until the last motion command sent completes. As long as the machine has not finised its last movement, code execution will be blocked.
+
return value
+
+
none
+
+
Example
+
from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+deftemplateCallback(data):
+ print"Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Homing axis one
+machine_motion_example.emitHome(1)
+# Wait for the message to be acknowledged by the motion controller
+while machine_motion_example.isReady() != "true": pass
+machine_motion_example.waitForMotionCompletion()
+
+print"--> This line executes after the motion controller has acknowledged the reception of the command."
Output
+
Controller gCode responses MachineMotion Session Start
+Controller gCode responses echo:N0 M110 N0*125
+Controller gCode responses ok
+Controller gCode responses echo:N1 M111 S247*97
+Controller gCode responses echo:DEBUG:ECHO,INFO,ERRORS,COMMUNICATION
+Controller gCode responses ok
+Controller gCode responses echo:N2 G28 X*105
+Controller gCode responses X:0.00 Y:0.00 Z:0.00 E:0.00 Count X: 0 Y:0 Z:0
+Controller gCode responses ok
+Controller gCode responses echo:N3 V0*59
+A motion status was requested
+move is in progress
+Controller gCode responses Motion Status = COMPLETED
+Controller gCode responses ok
+Controller gCode responses echo:N4 V0*60
+A motion status was requested
+Move was completed
+Controller gCode responses Motion Status = COMPLETED
+--> This line executes after the motion controller has acknowledged the reception of the command.
+Controller gCode responses ok
+
emitStop()
+
Immediately stops motion on all axes.
+
return value
+
+
none
+
+
Example
+
from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+deftemplateCallback(data):
+ print"Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Send a stop command to the Machine (even if it is not moving yet !)
+machine_motion_example.emitStop()
+
+print"--> Machine Stopped"
Moves all carriages to their home location sequentially, axis one to axis three.
+
return value
+
+
none
+
+
Example
+
from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+deftemplateCallback(data):
+ print"Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Homing all the axes of the controller sequentially
+machine_motion_example.emitHomeAll()
+machine_motion_example.waitForMotionCompletion()
+
+print"--> All axes are now at home position."
+
emitHome(axis)
+
Moves the carriage of the corresponding axis to its home location.
+
axis {AXIS_NUMBER}
+
+
Axis to move to home location
+
+
return value
+
+
none
+
+
Example
+
from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+deftemplateCallback(data):
+ print"Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Homing axis one
+machine_motion_example.emitHome(1)
+machine_motion_example.waitForMotionCompletion()
+
+print"--> Axis 1 is now at home position."
Output
+
Controller gCode responses MachineMotion Session Start
+Controller gCode responses echo:N0 M110 N0*125
+Controller gCode responses ok
+Controller gCode responses echo:N1 M111 S247*97
+Controller gCode responses echo:DEBUG:ECHO,INFO,ERRORS,COMMUNICATION
+Controller gCode responses ok
+Controller gCode responses echo:N2 G28 X*105
+Controller gCode responses X:0.00 Y:0.00 Z:0.00 E:0.00 Count X: 0 Y:0 Z:0
+Controller gCode responses ok
+Controller gCode responses echo:N3 V0*59
+A motion status was requested
+move is in progress
+Controller gCode responses Motion Status = COMPLETED
+Controller gCode responses ok
+Controller gCode responses echo:N4 V0*60
+A motion status was requested
+Move was completed
+Controller gCode responses Motion Status = COMPLETED
+--> Axis 1 is now at home position.
+Controller gCode responses ok
+
emitSpeed(mmPerMin)
+
Configures the travel speed. Travel speed applies to combined axis moves and single axis moves.
+
mmPerMin {Number}
+
+
Motion speed in millimeters per minute
+
+
return value
+
+
none
+
+
Example
+
from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+deftemplateCallback(data):
+ print"Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Configuring the travel speed to 10 000 mm / min
+machine_motion_example.emitSpeed(10000)
+
+print"--> Machine moves are not set to 10 000 mm / min"
+
emitAcceleration(mmPerSecSqr)
+
Configures the travel acceleration. Travel acceleration applies to combined axis moves and single axis moves.
+
mmPerSecSqr {Number}
+
+
Motion speed in millimeters per minute
+
+
return value
+
+
none
+
+
Example
+
from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+deftemplateCallback(data):
+ print"Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Configuring the travel speed to 1000 mm / second^2
+machine_motion_example.emitAcceleration(1000)
+
+print"--> Machine moves are not set to accelerate @ 1000 mm / second^2"
Output
+
Controller gCode responses MachineMotion Session Start
+Controller gCode responses echo:N0 M110 N0*125
+Controller gCode responses ok
+Controller gCode responses echo:N1 M111 S247*97
+Controller gCode responses echo:DEBUG:ECHO,INFO,ERRORS,COMMUNICATION
+Controller gCode responses ok
+Controller gCode responses echo:N2 M204 T1000*82
+Controller gCode responses Setting Travel Acceleration: 1000.00
+Controller gCode responses ok
+--> Machine moves are not set to accelerate @ 1000 mm / second^2
+
emitAbsoluteMove(axis, position)
+
Configures the travel acceleration. Travel acceleration applies to combined axis moves and single axis moves.
+
axis {AXIS_NUMBER}
+
+
Axis to move to home location
+
+
position {String}
+
+
Position of the carriage
+
+
return value
+
+
none
+
+
Example
+
from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+deftemplateCallback(data):
+ print"Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Configuring the travel speed to 10 000 mm / min
+machine_motion_example.emitSpeed(10000)
+
+# Configuring the travel speed to 1000 mm / second^2
+machine_motion_example.emitAcceleration(1000)
+
+# Homing axis one
+machine_motion_example.emitHome(1)
+machine_motion_example.waitForMotionCompletion()
+
+# Move the axis one to position 100 mm
+machine_motion_example.emitAbsoluteMove(1, 100)
+machine_motion_example.waitForMotionCompletion()
+
+print"--> Example completed."
Moves the gantry to a relative position based on the current gantry location.
+
axis {AXIS_NUMBER}
+
+
Axis to move to home location
+
+
direction {String}
+
+
Motion direction
+
+
distance {Number}
+
+
Distance to move in mm
+
+
return value
+
+
none
+
+
Example
+
from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+deftemplateCallback(data):
+ print"Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Configuring the travel speed to 10 000 mm / min
+machine_motion_example.emitSpeed(10000)
+
+# Configuring the travel speed to 1000 mm / second^2
+machine_motion_example.emitAcceleration(1000)
+
+# Homing axis one
+machine_motion_example.emitHome(1)
+
+# Move the axis one to position 100 mm
+machine_motion_example.emitAbsoluteMove(1, 100)
+machine_motion_example.waitForMotionCompletion()
+
+# Move the axis one by a negative increment of 100 mm
+machine_motion_example.emitRelativeMove(1, "negative", 100)
+machine_motion_example.waitForMotionCompletion()
+
+print"--> Example completed."
Sends a direct G-Code string command. See the G-Code Commands section for more details.
+
gCode {String}
+
+
G-Code command to send to the controller
+
+
return value
+
+
none
+
+
Example
+
from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+deftemplateCallback(data):
+ print"Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Configuring the travel speed to 10 000 mm / min
+machine_motion_example.emitSpeed(10000)
+
+# Configuring the travel speed to 1000 mm / second^2
+machine_motion_example.emitAcceleration(1000)
+
+# Homing axis one
+machine_motion_example.emitHome(1)
+machine_motion_example.waitForMotionCompletion()
+
+# Use the G0 command to move both axis one and two by 500mm at a travel speed of 10 000 mm / minute
+machine_motion_example.emitgCode("G0 X500 Y500 F10000")
+machine_motion_example.waitForMotionCompletion()
+
+print"--> Example completed."
Output
+
Controller gCode responses MachineMotion Session Start
+Controller gCode responses echo:N0 M110 N0*125
+Controller gCode responses ok
+Controller gCode responses echo:N1 M111 S247*97
+Controller gCode responses echo:DEBUG:ECHO,INFO,ERRORS,COMMUNICATION
+Controller gCode responses ok
+Controller gCode responses echo:N2 G0 F10000*124
+Controller gCode responses ok
+Controller gCode responses echo:N3 M204 T1000*83
+Controller gCode responses Setting Travel Acceleration: 1000.00
+Controller gCode responses ok
+Controller gCode responses echo:N4 G28 X*111
+Controller gCode responses echo:busy: processing
+Controller gCode responses X:0.00 Y:3000.00 Z:0.00 E:0.00 Count X: 0 Y:24000 Z:0
+Controller gCode responses ok
+Controller gCode responses echo:N5 V0*61
+Controller gCode responses Motion Status = COMPLETED
+Controller gCode responses ok
+Controller gCode responses echo:N6 V0*62
+Controller gCode responses Motion Status = COMPLETED
+Controller gCode responses ok
+Controller gCode responses echo:N7 G0 X50 Y50 F10000*120
+Controller gCode responses ok
+Controller gCode responses echo:N8 V0*48
+Controller gCode responses Motion Status = IN_PROGRESS
+Controller gCode responses ok
+Controller gCode responses echo:N9 V0*49
+Controller gCode responses Motion Status = IN_PROGRESS
+Controller gCode responses ok
+Controller gCode responses echo:N10 V0*9
+Controller gCode responses Motion Status = IN_PROGRESS
+Controller gCode responses ok
+Controller gCode responses echo:N11 V0*8
+Controller gCode responses Motion Status = IN_PROGRESS
+Controller gCode responses ok
+Controller gCode responses echo:N12 V0*11
+Controller gCode responses Motion Status = IN_PROGRESS
+Controller gCode responses ok
+Controller gCode responses echo:N13 V0*10
+Controller gCode responses Motion Status = COMPLETED
+--> Example completed.
+Controller gCode responses ok
Control Device Functions
+
Functions that exchange data with the MachineMotion controller (no movement involved).
+
+
attachControlDevice(port, device, callback)
+
Configures a control device on a specific MachineMotion port.
+
port {CONTROL_DEVICE_PORTS}
+
+
Port on which the device is connected
+
+
device {CONTROL_DEVICE_TYPE}
+
+
Type of device connected
+
+
callback {def callbackName(data):}
+
+
Function to invoke once the data is available. The data will be passed as an argument to the callback as a serialized JSON string.
+
+
return value
+
+
none
+
+
callback argument {String}
+
+
Status message containing
+
+
Example
+
from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+deftemplateCallback(data):
+ print"Controller gCode responses " + data
+
+# Define a callback to invoked when a control device is attached to the controller
+defattachControlDeviceCallback(data):
+ print"Attach control device callback: " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Attach (configure) a control device (an encoder in this case) to the MachineMotion controller.
+machine_motion_controller.attachControlDevice("SENSOR4","ENCODER", attachControlDeviceCallback)
+
detachControlDevice(port, callback)
+
deConfigures a control device on a specific MachineMotion port.
+
port {CONTROL_DEVICE_PORTS}
+
+
Port on which the device is connected
+
+
callback {def callbackName(data):}
+
+
Function to invoke once the data is available. The data will be passed as an argument to the callback as a serialized JSON string.
+
+
return value
+
+
none
+
+
callback argument {String}
+
+
Status message containing
+
+
Example
+
from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+deftemplateCallback(data):
+ print"Controller gCode responses " + data
+
+# Define a callback to invoke when a control device is attached to the controller
+defattachControlDeviceCallback(data):
+ print"Attach control device callback: " + data
+
+# Define a callback to invoke when a control device is detached from the controller
+defdetachControlDeviceCallback(data):
+ print"Detach control device callback: " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Attach (configure) a control device (an encoder in this case) to the MachineMotion controller.
+machine_motion_controller.attachControlDevice("SENSOR4","ENCODER", attachControlDeviceCallback)
+
+# Some other code here ...
+
+# Detach (deconfigure) a control device that was previously attached to the MachineMotion controller.
+machine_motion_controller.detachControlDevice("SENSOR4", detachControlDeviceCallback)
+
readControlDevice(port, signal, callback)
+
Read a given signal on a control device on a specific MachineMotion port.
+
port {CONTROL_DEVICE_PORTS}
+
+
Port on which the device is connected
+
+
signal {CONTROL_DEVICE_SIGNALS}
+
+
Signal to read on the device
+
+
callback {def callbackName(data):}
+
+
Function to invoke once the data is available. The data will be passed as an argument to the callback as a serialized JSON string.
from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+deftemplateCallback(data):
+ print"Controller gCode responses " + data
+
+# Define a callback to invoke when a control device is attached to the controller
+defattachControlDeviceCallback(data):
+ print"Attach control device callback: " + data
+
+# Define a callback to invoke when a control device is detached from the controller
+defdetachControlDeviceCallback(data):
+ print"Detach control device callback: " + data
+
+# Define a callback to invoke when a control device is read
+defreadControlDeviceCallback(data):
+ print"Read control device callback: " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Attach (configure) a control device (an encoder in this case) to the MachineMotion controller.
+machine_motion_controller.attachControlDevice("SENSOR4","ENCODER", attachControlDeviceCallback)
+
+count = 0
+
+for count in range (0, 100):
+
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_controller.readControlDevice("SENSOR4", "SIGNAL0", readControlDeviceCallback)
+
+ time.sleep(1)
+
+# Detach (deconfigure) a control device that was previously attached to the MachineMotion controller.
+machine_motion_controller.detachControlDevice("SENSOR4", detachControlDeviceCallback)
+
writeControlDevice(port, signal, callback)
+
DeConfigures a control device on a specific MachineMotion port.
from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+deftemplateCallback(data):
+ print"Controller gCode responses " + data
+
+# Define a callback to invoke when a control device is attached to the controller
+defattachControlDeviceCallback(data):
+ print"Attach control device callback: " + data
+
+# Define a callback to invoke when a control device is detached from the controller
+defdetachControlDeviceCallback(data):
+ print"Detach control device callback: " + data
+
+# Define a callback to invoke when a control device is read
+defreadControlDeviceCallback(data):
+ print"Read control device callback: " + data
+
+# Define a callback to invoke when a control device is written
+defwriteControlDeviceCallback(data):
+ print"Write control device callback: " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Attach (configure) a control device (an encoder in this case) to the MachineMotion controller.
+machine_motion_controller.attachControlDevice("SENSOR4","IO_EXPANDER_GENERIC", attachControlDeviceCallback)
+
+count = 0
+
+for count in range (0, 100):
+
+ # Read the signal of a control device. SIGNAL0 of the IO expander. Returns True or Flase depending on the state of the IO.
+ machine_motion_controller.readControlDevice("SENSOR4", "SIGNAL0", readControlDeviceCallback)
+
+ time.sleep(1)
+
+ # Write the signal of a control device. SIGNAL0 of the IO expander.
+ machine_motion_controller.writeControlDevice("SENSOR4", "SIGNAL0", writeControlDeviceCallback)
+
+ time.sleep(1)
+
+# Detach (deconfigure) a control device that was previously attached to the MachineMotion controller.
+machine_motion_controller.detachControlDevice("SENSOR4", detachControlDeviceCallback)
Executing Python Programs
+
+
+
Open the command prompt (Windows users) or terminal (for Mac & Linux users).
+
+
+
Browse to the directory where you program is saved.
+
+
+
Execute your program.
+
python yourProgram.py
+
+
Various status messages will be displayed on the console when the program starts running.
+
+
+
To stop the execution of the program, press CRTL + c
+
+
+
G-Code
+
Sending G-Code
+
G-Code is a text based protocol broadly utilized to control the motion of multi axis machines. The API presented in the previous section employs it to communicate motion-related commands to the MachineMotion controller
+
More advanced users might wish to utilize specialized commands that are available via the G-Code protocol. The emitgCode (gCode) function presented in Table 6 can be used for this purpose.
+
G-Code Reference
+
The MachineMotion Python Library Package contains a list of G-Code commands available for advanced users. You can download it at the following link G-Code Reference Functions.
+
Some useful G-Code commands that can be utilized via the emitgCode function are listed below for convenience.
+
Configuration Functions
+
+
+
+
+
G-Code Function ID
+
Description
+
+
+
+
+
M201
+
Set Max Acceleration
+
+
+
M203
+
Set Max Feedrate
+
+
+
M204
+
Set Acceleration
+
+
+
G0 & G1
+
Set Feedrate (Travel Speed)
+
+
+
+
+
Table 1: G-Code Configuration Functions
+
Control Functions
+
+
+
+
+
G-Code Function ID
+
Description
+
+
+
+
+
G90
+
Set Absolute Motion
+
+
+
G91
+
Set Relative Motion
+
+
+
G0
+
Linear Move
+
+
+
G28
+
Home
+
+
+
+
+
Table 2: G-Code Control Functions
+
Communication Functions
+
+
+
+
+
G-Code Function ID
+
Description
+
+
+
+
+
M114
+
Get Current Position
+
+
+
M119
+
GEt EndStop States
+
+
+
+
+
Table 3: G-Code Communication Functions
+
Note on Axis Mapping
+
Note that when using direct G-Code commands, the axis name mapping of Table 4 applies.
+
+
+
+
+
G-Code Axis Name
+
MachineMotion Axis Name
+
+
+
+
+
X
+
1
+
+
+
Y
+
2
+
+
+
z
+
3
+
+
+
+
\ No newline at end of file
diff --git a/documentation/gCode/__G000-G001--Linear Move.md b/documentation/gCode/__G000-G001--Linear Move.md
new file mode 100644
index 0000000..33843ec
--- /dev/null
+++ b/documentation/gCode/__G000-G001--Linear Move.md
@@ -0,0 +1,95 @@
+---
+tag: g00
+title: Linear Move
+brief: Add a straight line movement to the planner
+
+experimental: false
+since: 1.0.0-beta
+group: planner
+
+codes:
+ - G0
+ - G1
+
+long: |
+ The `G0` and `G1` commands add a linear move to the queue to be performed after all previous moves are completed. These commands yield control back to the command parser as soon as the move is queued, but they may delay the command parser while awaiting a slot in the queue.
+
+ A linear move traces a straight line from one point to another, ensuring that the specified axes will arrive simultaneously at the given coordinates (by linear interpolation). The speed may change over time following an acceleration curve, according to the acceleration and jerk settings of the given axes.
+
+ A command like `G1 F1000` sets the feedrate for all subsequent moves.
+
+ Marlin treats `G0` (rapid linear movement) as an alias to `G1` (rapid movement).
+
+ By convention, most G-code generators use `G0` for non-extrusion movements (those without the E axis) and `G1` for moves that include extrusion. This is meant to allow a kinematic system to, optionally, do a more rapid uninterpolated movement requiring much less calculation.
+
+notes: |
+ - Coordinates are given in millimeters by default. Units may be set to inches by `G20`.
+ - In Relative Mode (`G91`) all coordinates are interpreted as relative, adding onto the previous position.
+ - A single linear move may generate several smaller moves due to kinematics and bed leveling compensation.
+ - Developers, keep using `G0` for non-print moves. It makes G-code more adaptable to lasers, engravers, etc.
+ - In a future version of Marlin, `G0` will do rapid movement, optionally, on SCARA machines.
+
+parameters:
+ -
+ tag: X
+ optional: true
+ description: A coordinate on the X axis
+ values:
+ -
+ tag: pos
+ type: float
+ -
+ tag: Y
+ optional: true
+ description: A coordinate on the Y axis
+ values:
+ -
+ tag: pos
+ type: float
+ -
+ tag: Z
+ optional: true
+ description: A coordinate on the Z axis
+ values:
+ -
+ tag: pos
+ type: float
+ -
+ tag: E
+ optional: true
+ description: The length of filament to feed into the extruder between the start and end point
+ values:
+ -
+ tag: pos
+ type: float
+ -
+ tag: F
+ optional: true
+ description: The maximum movement rate of the move between the start and end point. The feedrate set here applies to subsequent moves that omit this parameter.
+ values:
+ -
+ tag: rate
+ type: float
+
+examples:
+ -
+ pre: The most basic move sets a feedrate and moves the tool to the given position.
+ code:
+ - G0 X12 ; move to 12mm on the X axis
+ - G0 F1500 ; set the feedrate to 1500mm/minute
+ - G1 X90.6 Y13.8 ; move to 90.6mm on the X axis and 13.8mm on the Y axis
+ -
+ pre: There are some caveats related with feedrates. Consider the following:
+ code:
+ - G1 F1500 ; set the feedrate to 1500mm/minute
+ - G92 E0
+ - G1 X50 Y25.3 E22.4 ; move while extruding
+ post: In the above example the feedrate is set to 1500mm/minute, then the tool is moved 50mm on the X axis and 25.3mm on the Y axis while extruding 22.4mm of filament between the two points.
+ -
+ code:
+ - G1 F1500
+ - G92 E0
+ - G1 X50 Y25.3 E22.4 F3000
+ post: However, in the above example, we set a feedrate of 1500 mm/minute on line 1 then do the move described above, accelerating to a feedrate of 3000 mm/minute (if possible). The extrusion will accelerate along with the X and Y movement, so everything stays synchronized.
+
+---
diff --git a/documentation/gCode/__G002-G003--Controlled Arc Move.md b/documentation/gCode/__G002-G003--Controlled Arc Move.md
new file mode 100644
index 0000000..43cfade
--- /dev/null
+++ b/documentation/gCode/__G002-G003--Controlled Arc Move.md
@@ -0,0 +1,116 @@
+---
+tag: g02
+title: Controlled Arc Move
+brief: Add an arc movement to the planner
+
+experimental: false
+since: 1.0.0-beta
+group: planner
+
+codes:
+ - G2
+ - G3
+
+long: |
+ `G2` adds a clockwise arc move to the planner; `G3` adds a counter-clockwise arc. An arc move starts at the current position and ends at the given XYZ, pivoting around a center-point offset given by `I` and `J` or `R`.
+
+ This command has two forms:
+ #### I J Form
+
+ - `I` specifies an X offset. `J` specifies a Y offset.
+ - At least one of the `I` `J` parameters is required.
+ - `X` and `Y` can be omitted to do a complete circle.
+ - The given `X` `Y` is not error-checked.
+ The arc ends based on the angle of the destination.
+ - Mixing `I` or `J` with `R` will throw an error.
+
+ #### R Form
+ - `R` specifies the radius. `X` or `Y` is required.
+ - Omitting both `X` and `Y` will throw an error.
+ - `X` or `Y` must differ from the current XY position.
+ - Mixing `R` with `I` or `J` will throw an error.
+
+ Arc moves actually generate several short straight-line moves, the length of which are determined by the configuration option `MM_PER_ARC_SEGMENT` (default 1mm). Any change in the Z position is linearly interpolated over the whole arc.
+
+parameters:
+ -
+ tag: X
+ optional: true
+ description: A coordinate on the X axis
+ values:
+ -
+ tag: pos
+ type: float
+ -
+ tag: Y
+ optional: true
+ description: A coordinate on the Y axis
+ values:
+ -
+ tag: pos
+ type: float
+ -
+ tag: Z
+ optional: true
+ description: A coordinate on the Z axis
+ values:
+ -
+ tag: pos
+ type: float
+ -
+ tag: I
+ type: float
+ optional: false
+ description: An offset from the current X position to use as the arc center
+ values:
+ -
+ tag: offset
+ type: float
+ -
+ tag: J
+ type: float
+ optional: false
+ description: An offset from the current Y position to use as the arc center
+ values:
+ -
+ tag: offset
+ type: float
+ -
+ tag: R
+ type: float
+ optional: false
+ description: A radius from the current XY position to use as the arc center
+ values:
+ -
+ tag: radius
+ type: float
+ -
+ tag: E
+ type: float
+ optional: true
+ description: The amount to extrude between the start point and end point
+ values:
+ -
+ tag: pos
+ type: float
+ -
+ tag: F
+ optional: true
+ description: The maximum rate of the move between the start and end point
+ values:
+ -
+ tag: rate
+ type: float
+ optional: false
+
+examples:
+ -
+ pre: Move in a clockwise arc from the current position to (125, 32) with the center offset from the current position by (10.5, 10.5).
+ code: G2 X125 Y32 I10.5 J10.5
+ -
+ pre: Move in a counter-clockwise arc from the current position to (125, 32) with the center offset from the current position by (10.5, 10.5).
+ code: G3 X125 Y32 I10.5 J10.5
+ -
+ pre: Move in a complete clockwise circle with the center offset from the current position by 20, 20.
+ code: G2 I20 J20
+---
diff --git a/documentation/gCode/__G004--Dwell.md b/documentation/gCode/__G004--Dwell.md
new file mode 100644
index 0000000..b649312
--- /dev/null
+++ b/documentation/gCode/__G004--Dwell.md
@@ -0,0 +1,45 @@
+---
+tag: g04
+title: Dwell
+brief: Pause the planner
+
+experimental: false
+since: 1.0.0-beta
+group: planner
+
+codes:
+ - G4
+
+long:
+ - Dwell pauses the command queue and waits for a period of time.
+
+notes:
+ - If both `S` and `P` are included, `S` takes precedence.
+ - '`M0`/`M1` provides an interruptible "dwell" (Marlin 1.1.0 and up).'
+ - '`G4` with no arguments is effectively the same as `M400`.'
+
+parameters:
+ -
+ tag: S
+ optional: true
+ description: Amount of time to dwell
+ values:
+ -
+ tag: time
+ type: int
+ unit: sec
+ -
+ tag: P
+ optional: true
+ description: Amount of time to dwell
+ values:
+ -
+ tag: time
+ type: int
+ unit: ms
+
+example:
+ -
+ code:
+ - G4 P500 ; Dwell for 1/2 second
+---
diff --git "a/documentation/gCode/__G005--B\303\251zier cubic spline.md" "b/documentation/gCode/__G005--B\303\251zier cubic spline.md"
new file mode 100644
index 0000000..6164456
--- /dev/null
+++ "b/documentation/gCode/__G005--B\303\251zier cubic spline.md"
@@ -0,0 +1,84 @@
+---
+tag: g05
+title: Bézier cubic spline
+brief: Cubic B-spline with XYZE destination and IJPQ offsets
+
+experimental: true
+since: 1.1.0
+group: planner
+
+codes:
+ - G5
+
+long: |
+ `G5` creates a cubic B-spline in the XY plane with the X and Y axes only. `P` and `Q` parameters are required. `I` and `J` are required for the first `G5` command in a series. For subsequent `G5` commands, either both `I` and `J` must be specified, or neither. If `I` and `J` are unspecified, the starting direction of the cubic will automatically match the ending direction of the previous cubic (as if `I` and `J` are the negation of the previous `P` and `Q`).
+
+ See [This interactive demo](https://www.geogebra.org/m/WPHQ9rUt) to understand how Bézier control points work.
+
+notes:
+ - It is an error if an axis other than `X` or `Y` is specified.
+
+parameters:
+ -
+ tag: X
+ optional: false
+ description: A coordinate on the X axis
+ values:
+ -
+ tag: pos
+ type: float
+ -
+ tag: Y
+ optional: false
+ description: A coordinate on the Y axis
+ values:
+ -
+ tag: pos
+ type: float
+ -
+ tag: I
+ optional: false
+ description: X incremental offset from start point to first control point
+ values:
+ -
+ tag: pos
+ type: float
+ -
+ tag: J
+ optional: false
+ description: Y incremental offset from start point to first control point
+ values:
+ -
+ tag: pos
+ type: float
+ -
+ tag: P
+ optional: false
+ description: X incremental offset from end point to second control point
+ values:
+ -
+ tag: pos
+ type: float
+ -
+ tag: Q
+ optional: false
+ description: Y incremental offset from end point to second control point
+ values:
+ -
+ tag: pos
+ type: float
+
+examples:
+ -
+ pre:
+ - 'For example, to program a curvy "N" shape:'
+ code:
+ - G0 X0 Y0
+ - G5 I0 J3 P0 Q-3 X1 Y1
+
+ -
+ pre:
+ - 'A second curvy "N" that attaches smoothly to this one can now be made without specifying `I` and `J`:'
+ code:
+ - G5 P0 Q-3 X2 Y2
+---
diff --git a/documentation/gCode/__G020--Inch Units.md b/documentation/gCode/__G020--Inch Units.md
new file mode 100644
index 0000000..1bc3935
--- /dev/null
+++ b/documentation/gCode/__G020--Inch Units.md
@@ -0,0 +1,21 @@
+---
+tag: g20
+title: Inch Units
+brief: Set Units to Inches.
+
+experimental: false
+since: 1.1.0
+requires: INCH_MODE_SUPPORT
+group: units
+
+codes:
+ - G20
+
+long:
+ - Set units to inches. In this mode, all positions, offsets, rates, accelerations, etc., specified in G-code parameters are interpreted as inches.
+
+example:
+ -
+ code:
+ - G20 ; set units to inches
+---
diff --git a/documentation/gCode/__G021--Millimeter Units.md b/documentation/gCode/__G021--Millimeter Units.md
new file mode 100644
index 0000000..0ea1a47
--- /dev/null
+++ b/documentation/gCode/__G021--Millimeter Units.md
@@ -0,0 +1,21 @@
+---
+tag: g21
+title: Millimeter Units
+brief: Set Units to Millimeters.
+
+
+experimental: false
+since: 1.1.0
+requires: INCH_MODE_SUPPORT
+group: units
+
+codes:
+ - G21
+
+long: Set units to millimeters. In this mode, all positions, offsets, rates, accelerations, etc., specified in GCode parameters are interpreted as millimeters.
+
+example:
+ -
+ code:
+ - G21 ; set units to millimeters
+---
diff --git a/documentation/gCode/__G028--Auto Home.md b/documentation/gCode/__G028--Auto Home.md
new file mode 100644
index 0000000..4ff911c
--- /dev/null
+++ b/documentation/gCode/__G028--Auto Home.md
@@ -0,0 +1,49 @@
+---
+tag: g28
+title: Auto Home
+brief: Auto home one or more axes.
+
+experimental: false
+since: 1.0.0-beta
+group: calibration
+
+codes:
+ - G28
+
+long:
+ - Auto-home one or more axes, moving them towards their endstops until triggered. Each axis is backed off and re-bumped according to the `[XYZ]_HOME_BUMP_MM` and `HOMING_BUMP_DIVISOR` settings.
+
+notes:
+ - Homing is required before `G29`, `M48`, and some other procedures.
+ - If homing is needed the LCD will blink the X Y Z indicators.
+
+parameters:
+ -
+ tag: X
+ type: boolean
+ optional: true
+ description: Flag to go back to the X axis origin
+ -
+ tag: Y
+ type: boolean
+ optional: true
+ description: Flag to go back to the Y axis origin
+ -
+ tag: Z
+ type: boolean
+ optional: true
+ description: A coordinate on the Z axis
+
+examples:
+ -
+ pre:
+ - 'The most-used form of this command is to home all axes:'
+ code:
+ - G28 ; Go to origin on all axes
+ post:
+ - With no arguments to `G28`, Marlin homes according to the `Z_SAFE_HOMING`, `QUICK_HOME` and `HOME_Y_BEFORE_X` settings.
+
+ -
+ code:
+ - G28 X Z ; Home the X and Z axes
+---
diff --git a/documentation/gCode/__G033--Delta Auto Calibration.md b/documentation/gCode/__G033--Delta Auto Calibration.md
new file mode 100644
index 0000000..43f5ac6
--- /dev/null
+++ b/documentation/gCode/__G033--Delta Auto Calibration.md
@@ -0,0 +1,150 @@
+---
+tag: g33
+title: Delta Auto Calibration
+brief: Calibrate various Delta parameters
+
+author: LVD-AC
+contrib: thinkyhead
+experimental: false
+since: 1.1.0
+requires: DELTA_AUTO_CALIBRATION
+group: calibration
+
+codes:
+ - G33
+
+long: |
+ With the `G33` command you can:
+ - Probe a circular grid of points,
+ - calibrate Delta Height,
+ - calibrate endstops,
+ - calibrate Delta Radius, and
+ - calibrate Tower Angles.
+
+parameters:
+ -
+ tag: P
+ optional: true
+ values:
+ -
+ tag: 1
+ description: Probe center and set height only.
+ -
+ tag: 2
+ description: Probe center and towers. Set height, endstops, and delta radius.
+ -
+ tag: 3
+ description: Probe all positions - center, towers and opposite towers. Set all.
+ -
+ tag: 4-7
+ description: Probe all positions at different locations and average them.
+ -
+ tag: T
+ optional: true
+ description: Enable or disable tower angle corrections calibration (`P3`-`P7`)
+ values:
+ -
+ type: bool
+ -
+ tag: V
+ optional: true
+ description: Set the verbose level
+ values:
+ -
+ tag: 0
+ description: Dry run, no calibration
+ -
+ tag: 1
+ description: Report settings
+ -
+ tag: 2
+ description: Report settings and probe results
+
+notes:
+
+examples:
+ -
+ pre: Default (Verbose 1)
+ code: |
+ G33
+
+ > G33 Auto Calibrate
+ > Checking... AC
+ > .Height:295.00 Ex:+0.00 Ey:+0.00 Ez:+0.00 Radius:100.00
+ > .Tower angle : Tx:+0.00 Ty:+0.00 Tz:+0.00
+ > Iteration : 01 std dev:2.665
+ > .Height:297.85 Ex:-0.18 Ey:-0.13 Ez:+0.00 Radius:100.68
+ > .Tower angle : Tx:-0.05 Ty:+0.08 Tz:+0.00
+ > Iteration : 02 std dev:0.128
+ > .Height:297.77 Ex:-0.19 Ey:-0.09 Ez:+0.00 Radius:100.80
+ > .Tower angle : Tx:-0.07 Ty:+0.15 Tz:+0.00
+ > Iteration : 03 std dev:0.025
+ > .Height:297.78 Ex:-0.17 Ey:-0.09 Ez:+0.00 Radius:100.78
+ > .Tower angle : Tx:-0.09 Ty:+0.20 Tz:+0.00
+ > Iteration : 04 std dev:0.022
+ > .Height:297.80 Ex:-0.14 Ey:-0.07 Ez:+0.00 Radius:100.79
+ > .Tower angle : Tx:-0.10 Ty:+0.22 Tz:+0.00
+ > Iteration : 05 std dev:0.019
+ > .Height:297.81 Ex:-0.13 Ey:-0.06 Ez:+0.00 Radius:100.80
+ > .Tower angle : Tx:-0.10 Ty:+0.25 Tz:+0.00
+ > Calibration OK rolling back.
+ > .Height:297.80 Ex:-0.14 Ey:-0.07 Ez:+0.00 Radius:100.79
+ > .Tower angle : Tx:-0.10 Ty:+0.22 Tz:+0.00
+ > Save with M500 and/or copy to Configuration.h
+ -
+ pre: Verbose 2
+ code: |
+ G33 V2
+
+ > G33 Auto Calibrate
+ > Checking... AC
+ > .Height:297.80 Ex:-0.14 Ey:-0.07 Ez:+0.00 Radius:100.79
+ > .Tower angle : Tx:-0.10 Ty:+0.22 Tz:+0.00
+ > . c:+0.01 x:+0.06 y:+0.04 z:+0.01
+ > . yz:-0.02 zx:-0.01 xy:+0.01
+ > Iteration : 01 std dev:0.028
+ > .Height:297.81 Ex:-0.10 Ey:-0.04 Ez:+0.00 Radius:100.81
+ > .Tower angle : Tx:-0.10 Ty:+0.24 Tz:+0.00
+ > . c:-0.03 x:-0.01 y:-0.02 z:-0.03
+ > . yz:-0.05 zx:-0.05 xy:-0.06
+ > Calibration OK rolling back.
+ > .Height:297.80 Ex:-0.14 Ey:-0.07 Ez:+0.00 Radius:100.79
+ > .Tower angle : Tx:-0.10 Ty:+0.22 Tz:+0.00
+ > Save with M500 and/or copy to Configuration.h
+ -
+ pre: Using `V0` for Dry Run with no calibration.
+ code: |
+ G33 V0
+
+ > G33 Auto Calibrate
+ > Checking... AC (DRY-RUN)
+ > .Height:295.00 Ex:+0.00 Ey:+0.00 Ez:+0.00 Radius:100.00
+ > .Tower angle : Tx:+0.00 Ty:+0.00 Tz:+0.00
+ > . c:-2.86 x:-2.68 y:-2.62 z:-2.56
+ > . yz:-2.55 zx:-2.61 xy:-2.78
+ > End DRY-RUN std dev:2.668
+ -
+ pre: Using the `T` flag for no tower angles.
+ code: |
+ G33 T
+
+ > G33 Auto Calibrate
+ > Checking... AC
+ > .Height:297.79 Ex:-0.13 Ey:-0.06 Ez:+0.00 Radius:100.83
+ > Iteration : 01 std dev:0.024
+ > .Height:297.82 Ex:-0.09 Ey:-0.05 Ez:+0.00 Radius:100.82
+ > Calibration OK rolling back.
+ > .Height:297.79 Ex:-0.13 Ey:-0.06 Ez:+0.00 Radius:100.83
+ > Save with M500 and/or copy to Configuration.h
+ -
+ pre: Use a single point (`P1`) to check the height.
+ code: |
+ G33 P1
+
+ > G33 Auto Calibrate
+ > Checking... AC
+ > .Height:297.79
+ > Calibration OK
+ > .Height:297.80
+ > Save with M500 and/or copy to Configuration.h
+---
diff --git a/documentation/gCode/__G090--Absolute Positioning.md b/documentation/gCode/__G090--Absolute Positioning.md
new file mode 100644
index 0000000..0fdc80a
--- /dev/null
+++ b/documentation/gCode/__G090--Absolute Positioning.md
@@ -0,0 +1,19 @@
+---
+tag: g90
+title: Absolute Positioning
+brief: Use absolute positions.
+
+experimental: false
+since: 1.0.0-beta
+group: units
+
+codes:
+ - G90
+
+long:
+ - In absolute mode all coordinates given in G-code are interpreted as positions in the logical coordinate space. This includes the extruder position unless overridden by [`M83`](/docs/gcode/M083.html).
+
+notes:
+ - Absolute positioning is the default.
+
+---
diff --git a/documentation/gCode/__G091--Relative Positioning.md b/documentation/gCode/__G091--Relative Positioning.md
new file mode 100644
index 0000000..3ba1996
--- /dev/null
+++ b/documentation/gCode/__G091--Relative Positioning.md
@@ -0,0 +1,16 @@
+---
+tag: g91
+title: Relative Positioning
+brief: Use relative positions.
+
+experimental: false
+since: 1.0.0-beta
+group: units
+
+codes:
+ - G91
+
+long:
+ - Set relative position mode. In this mode all coordinates are interpreted as relative to the last position. This includes the extruder position unless overridden by [`M82`](/docs/gcode/M082.html).
+
+---
diff --git a/documentation/gCode/__G092--Set Position.md b/documentation/gCode/__G092--Set Position.md
new file mode 100644
index 0000000..5fb9dc0
--- /dev/null
+++ b/documentation/gCode/__G092--Set Position.md
@@ -0,0 +1,64 @@
+---
+tag: g92
+title: Set Position
+brief: Set the current position of one or more axes.
+
+experimental: false
+since: 1.0.0-beta
+group: planner
+
+codes:
+ - G92
+
+long:
+ - Set the current position to the values specified. In Marlin 1.1.0 and up, the software endstops are adjusted to preserve the physical movement limits. Thus you could use `G92` to set the middle of the bed to 0,0 and then run .gcode that was sliced for a Deltabot.
+
+notes:
+ - In earlier versions of Marlin `G92` doesn't update the software endstops, so it was unsupported to set coordinates outside these boundaries. In Marlin 1.1.0 and up, the physical boundaries are maintained. This means you can no longer use `G92` to move below the bed, for example.
+
+parameters:
+ -
+ tag: X
+ optional: true
+ description: New X axis position
+ values:
+ -
+ tag: pos
+ type: float
+ -
+ tag: Y
+ optional: true
+ description: New Y axis position
+ values:
+ -
+ tag: pos
+ type: float
+ -
+ tag: Z
+ optional: true
+ description: New Z axis position
+ values:
+ -
+ tag: pos
+ type: float
+ -
+ tag: E
+ optional: true
+ description: New extruder position
+ values:
+ -
+ tag: pos
+ type: float
+
+examples:
+ -
+ pre:
+ - Specify that the nozzle's current X position is 10 and the current extruder position is 90.
+ code:
+ - G92 X10 E90
+ -
+ pre:
+ - Specify that the nozzle's current XYZ position is 0, 0, 0.
+ code:
+ - G92 X0 Y0 Z0
+---
diff --git a/documentation/gCode/__M000-M001--Unconditional stop.md b/documentation/gCode/__M000-M001--Unconditional stop.md
new file mode 100644
index 0000000..cc5eaa7
--- /dev/null
+++ b/documentation/gCode/__M000-M001--Unconditional stop.md
@@ -0,0 +1,57 @@
+---
+tag: m000
+title: Unconditional stop
+brief: Stop and wait for user.
+
+experimental: false
+since: 1.0.0-beta
+group: planner
+
+codes:
+ - M0
+ - M1
+
+long:
+ - The `M0` and `M1` commands pause after the last movement and wait for the user to continue.
+
+notes:
+ - If both `S` and `P` are included, `S` takes precedence.
+ - Without an LCD controller or `EMERGENCY_PARSER` this command is ignored.
+ - With `EMERGENCY_PARSER` enabled the `M108` command can be used to continue.
+
+parameters:
+ -
+ tag: S
+ optional: true
+ description: Expire time, in seconds
+ values:
+ -
+ tag: sec
+ type: int
+ -
+ tag: P
+ optional: true
+ description: Expire time, in milliseconds
+ values:
+ -
+ tag: ms
+ type: int
+ -
+ tag: string
+ optional: true
+ description: An optional message to display on the LCD
+
+examples:
+ -
+ pre:
+ Stop and wait
+ code:
+ - M0
+
+ -
+ pre:
+ Display a message, stop, and wait
+ code:
+ - M0 Click to continue
+
+---
diff --git a/documentation/gCode/__M017--Enable Steppers.md b/documentation/gCode/__M017--Enable Steppers.md
new file mode 100644
index 0000000..08b01d6
--- /dev/null
+++ b/documentation/gCode/__M017--Enable Steppers.md
@@ -0,0 +1,23 @@
+---
+tag: m017
+title: Enable Steppers
+brief: Power on all steppers
+
+experimental: false
+group: control
+
+codes:
+ - M17
+
+long: Enable power on all stepper motors.
+
+notes:
+
+parameters:
+
+examples:
+ -
+ pre: Enable power on all stepper motors
+ code: M17
+
+---
diff --git a/documentation/gCode/__M018--Disable steppers.md b/documentation/gCode/__M018--Disable steppers.md
new file mode 100644
index 0000000..dabfc70
--- /dev/null
+++ b/documentation/gCode/__M018--Disable steppers.md
@@ -0,0 +1,78 @@
+---
+tag: m018
+title: Disable steppers
+brief: Disable steppers (same as M84).
+
+experimental: false
+group: control
+
+codes:
+ - M18
+ - M84
+
+long: |
+ This command can be used to set the stepper inactivity timeout (`S`) or to disable one or more steppers (`X`,`Y`,`Z`,`E`).
+
+ If a timeout is given with `S`, this command just sets the stepper inactivity timeout.
+
+ If no steppers are specified, this command disables all steppers immediately.
+
+ If one or more axes are specified, this command disables the specified steppers immediately.
+
+notes:
+
+parameters:
+ -
+ tag: S
+ optional: true
+ description: Inactivity Timeout. If none specified, disable now.
+ values:
+ -
+ tag: seconds
+ type: int
+ -
+ tag: X
+ optional: true
+ description: X Disable
+ values:
+ -
+ tag: flag
+ type: bool
+ -
+ tag: Y
+ optional: true
+ description: Y Disable
+ values:
+ -
+ tag: flag
+ type: bool
+ -
+ tag: Z
+ optional: true
+ description: Z Disable
+ values:
+ -
+ tag: flag
+ type: bool
+ -
+ tag: E
+ optional: true
+ description: E Disable
+ values:
+ -
+ tag: flag
+ type: bool
+
+examples:
+ -
+ pre: Set the stepper inactivity timeout to 1 minute
+ code: M18 S60
+ -
+ pre: Disable all steppers immediately
+ code: M18
+ -
+ pre: Disable Z and E steppers immediately
+ code: M18 Z E
+
+---
+
diff --git a/documentation/gCode/__M085--Inactivity Shutdown.md b/documentation/gCode/__M085--Inactivity Shutdown.md
new file mode 100644
index 0000000..8b7e706
--- /dev/null
+++ b/documentation/gCode/__M085--Inactivity Shutdown.md
@@ -0,0 +1,29 @@
+---
+tag: m085
+title: Inactivity Shutdown
+brief: Set the inactivity timeout.
+
+experimental: false
+group: control
+
+codes:
+ - M85
+
+long:
+ - Use this command to set a maximum period of time for the machine to be inactive (with no moves). If the machine is idle for longer than the set period, the firmware will shut everything down and halt the machine.
+
+notes:
+
+parameters:
+ -
+ tag: S
+ optional: false
+ description: Max inactive seconds
+ values:
+ -
+ tag: seconds
+ type: int
+
+examples:
+
+---
diff --git a/documentation/gCode/__M092--Set Axis Steps-per-unit.md b/documentation/gCode/__M092--Set Axis Steps-per-unit.md
new file mode 100644
index 0000000..c4dc86d
--- /dev/null
+++ b/documentation/gCode/__M092--Set Axis Steps-per-unit.md
@@ -0,0 +1,71 @@
+---
+tag: m092
+title: Set Axis Steps-per-unit
+brief: Set the number of steps-per-mm or steps-per-inch.
+
+experimental: false
+group: planner
+
+codes:
+ - M92
+
+long:
+ - Use `M92` to set the steps-per-unit for one or more axes. This setting affects how many steps will be done for each unit of movement. Units will be in steps/mm unless *inch* mode is set with [`G20`](/docs/gcode/G020.html) (which requires `INCH_MODE_SUPPORT`).
+
+notes:
+ - |
+ Get the current steps-per-unit settings with `M503`.
+
+ With `EEPROM_SETTINGS` enabled:
+
+ - This setting for all axes is saved with `M500` and loaded with `M501`.
+ - `M502` resets steps-per-unit for all axes to the values from `DEFAULT_AXIS_STEPS_PER_UNIT`.
+
+parameters:
+ -
+ tag: X
+ optional: true
+ description: X steps per unit
+ values:
+ -
+ tag: steps
+ type: float
+ -
+ tag: Y
+ optional: true
+ description: Y steps per unit
+ values:
+ -
+ tag: steps
+ type: float
+ -
+ tag: Z
+ optional: true
+ description: Z steps per unit
+ values:
+ -
+ tag: steps
+ type: float
+ -
+ tag: E
+ optional: true
+ description: E steps per unit
+ values:
+ -
+ tag: steps
+ type: float
+ -
+ tag: T
+ optional: true
+ description: Target extruder (Requires `DISTINCT_E_FACTORS`)
+ values:
+ -
+ tag: index
+ type: int
+
+example:
+ -
+ pre: Set E steps for a new extruder
+ code: M92 E688.4
+
+---
diff --git a/documentation/gCode/__M110--Set Line Number.md b/documentation/gCode/__M110--Set Line Number.md
new file mode 100644
index 0000000..2d463c6
--- /dev/null
+++ b/documentation/gCode/__M110--Set Line Number.md
@@ -0,0 +1,30 @@
+---
+tag: m110
+title: Set Line Number
+brief: Set the current line number.
+
+experimental: false
+group: hosts
+
+codes:
+ - M110
+
+long: Hosts can use `M110` to set the current line number in a print job. Each line number sent by a host must be one higher than the previous line number, or the firmware will ignore the line and send an error requesting a resend of the missing line. This is one technique Marlin uses to keep in sync with hosts.
+
+notes:
+ - 'All these are valid: `N100 M110`, `M110 N100`, `N101 M110 N100`.'
+
+parameters:
+ -
+ tag: N
+ optional: false
+ description: Line number
+ values:
+ -
+ tag: line
+ type: int
+
+examples:
+
+---
+
diff --git a/documentation/gCode/__M111--Debug Level.md b/documentation/gCode/__M111--Debug Level.md
new file mode 100644
index 0000000..6c802f9
--- /dev/null
+++ b/documentation/gCode/__M111--Debug Level.md
@@ -0,0 +1,49 @@
+---
+tag: m111
+title: Debug Level
+brief: Report and optionally set the debug flags.
+
+experimental: false
+group: hosts
+
+codes:
+ - M111
+
+long: |
+ Marlin has several debug bits that can be set, in combination, to help configure, troubleshoot, and debug the firmware. Add up the debug bits you need:
+
+ Mask|Name|Description
+ 1|ECHO|Echo all commands sent to the parser.
+ 2|INFO|Print extra informational messages.
+ 4|ERRORS|Print extra error messages.
+ 8|DRYRUN|Don't extrude, don't save leveling data, etc.
+ 16|COMMUNICATION|Not currently used.
+ 32|LEVELING|Detailed messages for homing, probing, and leveling. (Requires `DEBUG_LEVELING_FEATURE`.)
+ 64|Reserved|Reserved for future usage
+ 128|Reserved|Reserved for future usage
+
+notes:
+
+parameters:
+ -
+ tag: S
+ optional: true
+ description: Debug flag bits
+ values:
+ -
+ tag: flags
+ type: byte
+
+examples:
+ -
+ pre: Enable extra messages
+ code: M111 S38 ; LEVELING, ERRORS, INFO
+ -
+ pre: Enable dry-run mode
+ code: M111 S8
+ -
+ pre: Enable everything except dry-run mode
+ code: M111 S247 ; 255 - 8
+
+---
+
diff --git a/documentation/gCode/__M112--Emergency Stop.md b/documentation/gCode/__M112--Emergency Stop.md
new file mode 100644
index 0000000..6ec8f7b
--- /dev/null
+++ b/documentation/gCode/__M112--Emergency Stop.md
@@ -0,0 +1,24 @@
+---
+tag: m112
+title: Emergency Stop
+brief: Shut everything down and halt the machine.
+
+experimental: false
+group: safety
+
+codes:
+ - M112
+
+long: Used for emergency stopping, `M112` shuts down the machine, turns off all the steppers and heaters, and if possible, turns off the power supply. A reset is required to return to operational mode.
+
+notes: '`M112` is the fastest way to shut down the machine using a host, but it may need to wait for a space to open up in the command queue. Enable `EMERGENCY_PARSER` for an instantaneous `M112` command.'
+
+parameters:
+
+examples:
+ -
+ pre: Shut down now!
+ code: M112
+
+---
+
diff --git a/documentation/gCode/__M113--Host Keepalive.md b/documentation/gCode/__M113--Host Keepalive.md
new file mode 100644
index 0000000..fbe4e10
--- /dev/null
+++ b/documentation/gCode/__M113--Host Keepalive.md
@@ -0,0 +1,30 @@
+---
+tag: m113
+title: Host Keepalive
+brief: Get or set the host keepalive interval.
+
+experimental: false
+requires: HOST_KEEPALIVE_FEATURE
+group: hosts
+
+codes:
+ - M113
+
+long: During some lengthy processes, such as `G29`, Marlin may appear to the host to have "gone away." The "host keepalive" feature will send messages to the host when Marlin is busy or waiting for user response so the host won't try to reconnect.
+
+notes: Requires `HOST_KEEPALIVE_FEATURE`.
+
+parameters:
+ -
+ tag: S
+ optional: true
+ description: Keepalive interval (0-60)
+ values:
+ -
+ tag: seconds
+ type: int
+
+examples:
+
+---
+
diff --git a/documentation/gCode/__M114--Get Current Position.md b/documentation/gCode/__M114--Get Current Position.md
new file mode 100644
index 0000000..a22d9e0
--- /dev/null
+++ b/documentation/gCode/__M114--Get Current Position.md
@@ -0,0 +1,24 @@
+---
+tag: m114
+title: Get Current Position
+brief: Report the current tool position to the host.
+
+experimental: false
+group: hosts
+
+codes:
+ - M114
+
+long: Get the current position of the active nozzle. Includes stepper values.
+
+notes: Hosts should respond to the output of `M114` by updating their current position.
+
+parameters:
+
+examples:
+ -
+ pre: Get the current position
+ code: M114
+
+---
+
diff --git a/documentation/gCode/__M119--Endstop States.md b/documentation/gCode/__M119--Endstop States.md
new file mode 100644
index 0000000..8f7d92f
--- /dev/null
+++ b/documentation/gCode/__M119--Endstop States.md
@@ -0,0 +1,26 @@
+---
+tag: m119
+title: Endstop States
+brief: Report endstop and probe states to the host.
+
+experimental: false
+group: debug
+
+codes:
+ - M119
+
+long:
+ - Use this command to get the current state of all endstops, useful for setup and troubleshooting. Endstops are reported as either "`open`" or "`TRIGGERED`".
+ - The state of the Z probe is also reported.
+
+notes:
+
+parameters:
+
+examples:
+ -
+ pre: Get all endstop states
+ code: M119
+
+---
+
diff --git a/documentation/gCode/__M120--Enable Endstops.md b/documentation/gCode/__M120--Enable Endstops.md
new file mode 100644
index 0000000..3f3af58
--- /dev/null
+++ b/documentation/gCode/__M120--Enable Endstops.md
@@ -0,0 +1,24 @@
+---
+tag: m120
+title: Enable Endstops
+brief: Enable endstops and keep them enabled when not homing.
+
+experimental: false
+group: control
+
+codes:
+ - M120
+
+long: Enable endstops.
+
+notes: After this command endstops will be kept enabled when not homing. This may have side-effects if using `ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED`.
+
+parameters:
+
+examples:
+ -
+ pre: Enable endstops
+ code: M120
+
+---
+
diff --git a/documentation/gCode/__M121--Disable Endstops.md b/documentation/gCode/__M121--Disable Endstops.md
new file mode 100644
index 0000000..df59eb2
--- /dev/null
+++ b/documentation/gCode/__M121--Disable Endstops.md
@@ -0,0 +1,23 @@
+---
+tag: m121
+title: Disable Endstops
+brief: Disable endstops and keep them enabled when not homing.
+
+experimental: false
+group: control
+
+codes:
+ - M121
+
+long: Disable endstops.
+
+notes: After this command endstops will be kept disabled when not homing. This may have side-effects if using `ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED`.
+
+parameters:
+
+examples:
+ -
+ pre: Disable endstops
+ code: M121
+
+---
diff --git a/documentation/gCode/__M201--Set Max Acceleration.md b/documentation/gCode/__M201--Set Max Acceleration.md
new file mode 100644
index 0000000..5b38e9b
--- /dev/null
+++ b/documentation/gCode/__M201--Set Max Acceleration.md
@@ -0,0 +1,66 @@
+---
+tag: m201
+title: Set Print Max Acceleration
+brief: Set maximum acceleration for print moves one or more axes.
+
+experimental: false
+group: planner
+
+codes:
+ - M201
+
+long: Set the max acceleration for one or more axes (in current units-per-second squared).
+
+notes:
+ - View the current setting with `M503`.
+ - If `EEPROM_SETTINGS` is enabled, these are saved with `M500`, loaded with `M501`, and reset with `M502`.
+
+parameters:
+ -
+ tag: X
+ optional: true
+ description: X axis max acceleration
+ values:
+ -
+ tag: accel
+ type: float
+ -
+ tag: Y
+ optional: true
+ description: Y axis max acceleration
+ values:
+ -
+ tag: accel
+ type: float
+ -
+ tag: Z
+ optional: true
+ description: Z axis max acceleration
+ values:
+ -
+ tag: accel
+ type: float
+ -
+ tag: E
+ optional: true
+ description: E axis max acceleration
+ values:
+ -
+ tag: accel
+ type: float
+ -
+ tag: T
+ optional: true
+ description: Target extruder (Requires `DISTINCT_E_FACTORS`)
+ values:
+ -
+ tag: index
+ type: int
+
+examples:
+ -
+ pre: 'Set max acceleration lower so it sounds like a robot:'
+ code: M201 X50 Y50
+
+---
+
diff --git a/documentation/gCode/__M203--Set Max Feedrate.md b/documentation/gCode/__M203--Set Max Feedrate.md
new file mode 100644
index 0000000..3941cb8
--- /dev/null
+++ b/documentation/gCode/__M203--Set Max Feedrate.md
@@ -0,0 +1,66 @@
+---
+tag: m203
+title: Set Max Feedrate
+brief: Set maximum feedrate for one or more axes.
+
+experimental: false
+group: planner
+
+codes:
+ - M203
+
+long: Set the max feedrate for one or more axes (in current units-per-second).
+
+notes:
+ - View the current setting with `M503`.
+ - If `EEPROM_SETTINGS` is enabled, these are saved with `M500`, loaded with `M501`, and reset with `M502`.
+
+parameters:
+ -
+ tag: X
+ optional: true
+ description: X axis max feedrate
+ values:
+ -
+ tag: accel
+ type: float
+ -
+ tag: Y
+ optional: true
+ description: Y axis max feedrate
+ values:
+ -
+ tag: accel
+ type: float
+ -
+ tag: Z
+ optional: true
+ description: Z axis max feedrate
+ values:
+ -
+ tag: accel
+ type: float
+ -
+ tag: E
+ optional: true
+ description: E axis max feedrate
+ values:
+ -
+ tag: accel
+ type: float
+ -
+ tag: T
+ optional: true
+ description: Target extruder (Requires `DISTINCT_E_FACTORS`)
+ values:
+ -
+ tag: index
+ type: int
+
+examples:
+ -
+ pre: 'Set max feedrate for XY to 100mm/s:'
+ code: M203 X6000 Y6000
+
+---
+
diff --git a/documentation/gCode/__M204--Set Starting Acceleration.md b/documentation/gCode/__M204--Set Starting Acceleration.md
new file mode 100644
index 0000000..ded5a55
--- /dev/null
+++ b/documentation/gCode/__M204--Set Starting Acceleration.md
@@ -0,0 +1,47 @@
+---
+tag: m204
+title: Set Starting Acceleration
+brief: Set the starting acceleration for moves by type.
+
+experimental: false
+group: planner
+
+codes:
+ - M204
+
+long: Set the preferred starting acceleration for moves of different types.
+
+notes:
+ - View the current setting with `M503`.
+ - If `EEPROM_SETTINGS` is enabled, these are saved with `M500`, loaded with `M501`, and reset with `M502`.
+
+parameters:
+ -
+ tag: P
+ optional: true
+ description: Printing acceleration
+ values:
+ -
+ tag: accel
+ type: float
+ -
+ tag: R
+ optional: true
+ description: Retract acceleration
+ values:
+ -
+ tag: accel
+ type: float
+ -
+ tag: T
+ optional: true
+ description: Travel acceleration
+ values:
+ -
+ tag: accel
+ type: float
+
+examples:
+
+---
+
diff --git a/documentation/gCode/__M205--Set Advanced Settings.md b/documentation/gCode/__M205--Set Advanced Settings.md
new file mode 100644
index 0000000..08d4909
--- /dev/null
+++ b/documentation/gCode/__M205--Set Advanced Settings.md
@@ -0,0 +1,82 @@
+---
+tag: m205
+title: Set Advanced Settings
+brief: Set some advanced settings related to movement.
+
+experimental: false
+group: planner
+
+codes:
+ - M205
+
+long: Set various motion settings. See parameters for details.
+
+notes:
+ - View the current setting with `M503`.
+ - If `EEPROM_SETTINGS` is enabled, these are saved with `M500`, loaded with `M501`, and reset with `M502`.
+
+parameters:
+ -
+ tag: X
+ optional: true
+ description: X max jerk (units/s)
+ values:
+ -
+ tag: jerk
+ type: float
+ -
+ tag: Y
+ optional: true
+ description: Y max jerk (units/s)
+ values:
+ -
+ tag: jerk
+ type: float
+ -
+ tag: Z
+ optional: true
+ description: Z max jerk (units/s)
+ values:
+ -
+ tag: jerk
+ type: float
+ -
+ tag: E
+ optional: true
+ description: E max jerk (units/s)
+ values:
+ -
+ tag: jerk
+ type: float
+ -
+ tag: B
+ optional: true
+ description: Minimum segment time (µs)
+ values:
+ -
+ tag: µs
+ type: int
+ -
+ tag: S
+ optional: true
+ description: Minimum feedrate for print moves (units/min)
+ values:
+ -
+ tag: feedrate
+ type: float
+ -
+ tag: T
+ optional: true
+ description: Minimum feedrate for travel moves (units/min)
+ values:
+ -
+ tag: feedrate
+ type: float
+
+examples:
+ -
+ pre: Set some advanced settings.
+ code: M205 T2400
+
+---
+
diff --git a/documentation/gCode/__M206--Set Home Offsets.md b/documentation/gCode/__M206--Set Home Offsets.md
new file mode 100644
index 0000000..6b9efb8
--- /dev/null
+++ b/documentation/gCode/__M206--Set Home Offsets.md
@@ -0,0 +1,78 @@
+---
+tag: m206
+title: Set Home Offsets
+brief: Description Here
+
+experimental: false
+requires: HAS_HOME_OFFSET
+group: planner
+
+codes:
+ - M206
+
+long: |
+ Use `M206` to apply a persistent offset to the native home position and coordinate space. This effectively shifts the coordinate space in the negative direction. See examples below.
+
+ - The current position is adjusted to align to the new home offset values.
+ - The home offset is persistent — added to the current position until changed.
+ - Some uses include fine adjustment of Z position (without moving endstops) and shifting the coordinate space to print on a different part of the bed.
+
+notes: |
+ - This command isn't available on `DELTA`. (For delta use `M665 H`.)
+ - This GCode can be disabled with `NO_WORKSPACE_OFFSETS` to optimize movement.
+ - Changing the home offsets will not invalidate bed leveling or other saved data.
+ - View the current offsets with `M503`.
+ - If `EEPROM_SETTINGS` is enabled, the home offsets are saved with `M500`, loaded with `M501`, and reset with `M502`.
+ - [`M428`](/docs/gcode/M428.html) sets home offsets so the current position aligns to the native home position.
+
+parameters:
+ -
+ tag: P
+ optional: true
+ description: SCARA Psi offset (Requires `MORGAN_SCARA`)
+ values:
+ -
+ tag: offset
+ type: float
+ -
+ tag: T
+ optional: true
+ description: SCARA Theta offset (Requires `MORGAN_SCARA`)
+ values:
+ -
+ tag: offset
+ type: float
+ -
+ tag: X
+ optional: true
+ description: X home offset
+ values:
+ -
+ tag: offset
+ type: float
+ -
+ tag: Y
+ optional: true
+ description: Y home offset
+ values:
+ -
+ tag: offset
+ type: float
+ -
+ tag: Z
+ optional: true
+ description: Z home offset
+ values:
+ -
+ tag: offset
+ type: float
+
+examples:
+ -
+ pre: 'Raise Z up a little bit on the first layer:'
+ code: M206 Z-0.2
+ -
+ pre: 'Shift the print area 10mm to the left:'
+ code: M206 X10
+
+---
diff --git a/documentation/gCode/__M211--Software Endstops.md b/documentation/gCode/__M211--Software Endstops.md
new file mode 100644
index 0000000..8d6f6f5
--- /dev/null
+++ b/documentation/gCode/__M211--Software Endstops.md
@@ -0,0 +1,33 @@
+---
+tag: m211
+title: Software Endstops
+brief: Set and/or get the software endstops state
+
+experimental: false
+requires: (MIN|MAX)_SOFTWARE_ENDSTOPS
+group: planner
+
+codes:
+ - M211
+
+long:
+ - Optionally enable/disable software endstops, then report the current state.
+ - With software endstops enabled, moves will be clipped to the physical boundaries from `[XYZ]_MIN_POS` to `[XYZ]_MAX_POS`.
+
+notes:
+ - Requires either `MIN_SOFTWARE_ENDSTOPS` or `MAX_SOFTWARE_ENDSTOPS` for the enable option.
+
+parameters:
+ -
+ tag: S
+ optional: true
+ description: Software endstops state
+ values:
+ -
+ tag: flag
+ type: bool
+
+examples:
+
+---
+
diff --git a/documentation/gCode/__M220--Set Feedrate Percentage.md b/documentation/gCode/__M220--Set Feedrate Percentage.md
new file mode 100644
index 0000000..a9203e9
--- /dev/null
+++ b/documentation/gCode/__M220--Set Feedrate Percentage.md
@@ -0,0 +1,29 @@
+---
+tag: m220
+title: Set Feedrate Percentage
+brief: Set the global feedrate percentage.
+
+experimental: false
+group: planner
+
+codes:
+ - M220
+
+long: Set the feedrate percentage, which applies to all G-code-based moves.
+
+notes:
+
+parameters:
+ -
+ tag: S
+ optional: false
+ description: Feedrate percentage
+ values:
+ -
+ tag: percent
+ type: int
+
+examples:
+
+---
+
diff --git a/documentation/gCode/__M400--Finish Moves.md b/documentation/gCode/__M400--Finish Moves.md
new file mode 100644
index 0000000..b8175ad
--- /dev/null
+++ b/documentation/gCode/__M400--Finish Moves.md
@@ -0,0 +1,26 @@
+---
+tag: m400
+title: Finish Moves
+brief: Wait for all moves to finish
+
+experimental: false
+group: planner
+
+codes:
+ - M400
+
+long: This command causes all GCode processing to pause and wait in a loop until all moves in the planner are completed.
+
+notes:
+
+parameters:
+
+example:
+ -
+ pre: Wait for moves to finish before turning off the spindle
+ code:
+ - M400
+ - M5 ; Without M400 this happens too soon
+
+---
+
diff --git a/documentation/gCode/__M410--Quickstop.md b/documentation/gCode/__M410--Quickstop.md
new file mode 100644
index 0000000..b8c3220
--- /dev/null
+++ b/documentation/gCode/__M410--Quickstop.md
@@ -0,0 +1,26 @@
+---
+tag: m410
+title: Quickstop
+brief: Stop all steppers instantly
+
+experimental: false
+group: planner
+
+codes:
+ - M410
+
+long: Stop all steppers instantly. Since there will be no deceleration, steppers are expected to be out of position after this command.
+
+notes:
+ - This command is intended only for emergency situations.
+ - If `EMERGENCY_PARSER` is not enabled, this will be delayed.
+
+parameters:
+
+example:
+ -
+ pre: Stop all steppers now.
+ code: M410
+
+---
+
diff --git a/documentation/gCode/__M428--Home Offsets Here.md b/documentation/gCode/__M428--Home Offsets Here.md
new file mode 100644
index 0000000..2e14765
--- /dev/null
+++ b/documentation/gCode/__M428--Home Offsets Here.md
@@ -0,0 +1,37 @@
+---
+tag: m428
+title: Home Offsets Here
+brief: Set home offsets based on current position
+
+experimental: false
+requires: HAS_HOME_OFFSET
+group: planner
+
+codes:
+ - M428
+
+long: |
+ Use `M428` to set a persistent offset to the native home position and coordinate space by assigning the current position as the native home position. See the example below.
+
+ - The current position is set to the native home position.
+ - Any previous position shift from `G92` is cleared.
+ - The home offset is persistent — added to the current position until changed.
+ - Some uses include fine adjustment of Z position (without moving endstops) and shifting the coordinate space to print on a different part of the bed.
+
+notes: |
+ - Only the Z offset can be altered on `DELTA`.
+ - This GCode can be disabled with `NO_WORKSPACE_OFFSETS` to optimize movement.
+ - Changing the home offsets will not invalidate bed leveling or other saved data.
+ - If `EEPROM_SETTINGS` is enabled, the home offsets are saved with `M500`, loaded with `M501`, and reset with `M502`.
+ - Use [`M206`](/docs/gcode/M206.html) to set the home offsets directly.
+
+parameters:
+
+examples:
+ -
+ pre: What was X=10 becomes X=0. So the X home offset becomes -10.
+ code:
+ - G1 X10
+ - M428
+
+---
diff --git a/documentation/gCode/__M500--Save Settings.md b/documentation/gCode/__M500--Save Settings.md
new file mode 100644
index 0000000..e3fc38a
--- /dev/null
+++ b/documentation/gCode/__M500--Save Settings.md
@@ -0,0 +1,26 @@
+---
+tag: m500
+title: Save Settings
+brief: Save settings to EEPROM.
+
+experimental: false
+requires: EEPROM_SETTINGS
+group: eeprom
+
+codes:
+ - M500
+
+long: Save all configurable settings to EEPROM.
+
+notes:
+ - Requires `EEPROM_SETTINGS`.
+ - Since Marlin 1.1.0 only changed bytes are written to prolong EEPROM life.
+
+parameters:
+
+examples:
+ -
+ pre: Save settings
+ code: M500
+
+---
diff --git a/documentation/gCode/__M501--Restore Settings.md b/documentation/gCode/__M501--Restore Settings.md
new file mode 100644
index 0000000..6801010
--- /dev/null
+++ b/documentation/gCode/__M501--Restore Settings.md
@@ -0,0 +1,24 @@
+---
+tag: m501
+title: Restore Settings
+brief: Restore settings from EEPROM.
+
+experimental: false
+requires: EEPROM_SETTINGS
+group: eeprom
+
+codes:
+ - M501
+
+long: Load all saved settings from EEPROM.
+
+notes: Requires `EEPROM_SETTINGS`.
+
+parameters:
+
+examples:
+ -
+ pre: Restore all settings.
+ code: M501
+
+---
diff --git a/documentation/gCode/__M503--Report Settings.md b/documentation/gCode/__M503--Report Settings.md
new file mode 100644
index 0000000..a3a97ad
--- /dev/null
+++ b/documentation/gCode/__M503--Report Settings.md
@@ -0,0 +1,29 @@
+---
+tag: m503
+title: Report Settings
+brief: Report all settings that may be saved to EEPROM.
+
+
+experimental: false
+group: eeprom
+
+codes:
+ - M503
+
+long: Print a concise report of all current settings (in SRAM) to the host console.
+
+notes: Does not require `EEPROM_SETTINGS`.
+
+parameters:
+ -
+ tag: S
+ optional: true
+ description: Detailed output flag. (`true` if omitted.)
+ values:
+ -
+ tag: flag
+ type: bool
+
+examples:
+
+---
diff --git a/documentation/gCode/__M999--STOP Restart.md b/documentation/gCode/__M999--STOP Restart.md
new file mode 100644
index 0000000..e9e67c2
--- /dev/null
+++ b/documentation/gCode/__M999--STOP Restart.md
@@ -0,0 +1,33 @@
+---
+tag: m999
+title: STOP Restart
+brief: Return the machine to Running state
+
+
+experimental: false
+group: control
+
+codes:
+ - M999
+
+long: |
+ If a **STOP** occurs you can use `M999` to restart the "stopped" machine after resolving the issue.
+
+ Marlin will call **STOP** if any error occurs that would make continuing the current process problematic. For example, if the probe fails to deploy, it will abort probing and STOP. Note that this disables all heaters.
+
+notes:
+
+parameters:
+ -
+ tag: S
+ description: Resume without flushing the command buffer. The default behaviour is to flush the serial buffer and request a resend to the host starting on the last `N` line received.
+ values:
+ -
+ type: bool
+
+examples:
+ -
+ pre: Restart the machine
+ code: M999
+
+---
diff --git a/examples/example--MachineMotion.py b/examples/example--MachineMotion.py
new file mode 100644
index 0000000..c73e6e5
--- /dev/null
+++ b/examples/example--MachineMotion.py
@@ -0,0 +1,9 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, "192.168.7.2")
+
+print "Controller connected"
\ No newline at end of file
diff --git a/examples/example--configAxis.py b/examples/example--configAxis.py
new file mode 100644
index 0000000..6808578
--- /dev/null
+++ b/examples/example--configAxis.py
@@ -0,0 +1,12 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Configure the axis number one, 8 uSteps and 150 mm / turn for a timing belt
+machine_motion_example = machine_motion_example.configAxis(1, MICRO_STEPS.ustep_8, MECH_GAIN.timing_belt_150mm_turn)
+
+print "--> Controller axis 1 configured"
\ No newline at end of file
diff --git a/examples/example--configMachineMotionIp.py b/examples/example--configMachineMotionIp.py
new file mode 100644
index 0000000..0ed4d06
--- /dev/null
+++ b/examples/example--configMachineMotionIp.py
@@ -0,0 +1,11 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, "192.168.7.2",)
+
+machine_motion_example = machine_motion_example.configMachineMotionIp(NETWORK_MODE.static, "192.168.0.2", "255.255.255.0", "192.168.0.1")
+
+print "--> Controller connected & ethernet interface configured (static)"
\ No newline at end of file
diff --git a/examples/example--configMachineMotionIp_2.py b/examples/example--configMachineMotionIp_2.py
new file mode 100644
index 0000000..c49336f
--- /dev/null
+++ b/examples/example--configMachineMotionIp_2.py
@@ -0,0 +1,11 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, "192.168.7.2")
+
+machine_motion_example = machine_motion_example.configMachineMotionIp(NETWORK_MODE.dhcp, "", "", "")
+
+print "--> Controller connected & ethernet interface configured (dhcp)"
\ No newline at end of file
diff --git a/examples/example--emitAbsoluteMove.py b/examples/example--emitAbsoluteMove.py
new file mode 100644
index 0000000..58726a6
--- /dev/null
+++ b/examples/example--emitAbsoluteMove.py
@@ -0,0 +1,21 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Configuring the travel speed to 10 000 mm / min
+machine_motion_example.emitSpeed(10000)
+
+# Configuring the travel speed to 1000 mm / second^2
+machine_motion_example.emitAcceleration(1000)
+
+# Homing axis one
+machine_motion_example.emitHome(1)
+
+# Move the axis one to position 100 mm
+machine_motion_example.emitAbsoluteMove(1, 100)
+
+print "--> Example completed."
\ No newline at end of file
diff --git a/examples/example--emitAcceleration.py b/examples/example--emitAcceleration.py
new file mode 100644
index 0000000..6b842eb
--- /dev/null
+++ b/examples/example--emitAcceleration.py
@@ -0,0 +1,12 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Configuring the travel speed to 1000 mm / second^2
+machine_motion_example.emitAcceleration(1000)
+
+print "--> Machine moves are now set to accelerate @ 1000 mm / second^2"
\ No newline at end of file
diff --git a/examples/example--emitHome.py b/examples/example--emitHome.py
new file mode 100644
index 0000000..d0fb283
--- /dev/null
+++ b/examples/example--emitHome.py
@@ -0,0 +1,13 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Homing axis one
+machine_motion_example.emitHome(1)
+machine_motion_example.waitForMotionCompletion()
+
+print "--> Axis 1 is now at home position."
\ No newline at end of file
diff --git a/examples/example--emitHomeAll.py b/examples/example--emitHomeAll.py
new file mode 100644
index 0000000..f34d99a
--- /dev/null
+++ b/examples/example--emitHomeAll.py
@@ -0,0 +1,12 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Homing all the axes of the controller sequentially
+machine_motion_example.emitHomeAll()
+
+print "--> All Axes are now at home position."
\ No newline at end of file
diff --git a/examples/example--emitRelativeMove.py b/examples/example--emitRelativeMove.py
new file mode 100644
index 0000000..73c1778
--- /dev/null
+++ b/examples/example--emitRelativeMove.py
@@ -0,0 +1,24 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Configuring the travel speed to 10 000 mm / min
+machine_motion_example.emitSpeed(10000)
+
+# Configuring the travel speed to 1000 mm / second^2
+machine_motion_example.emitAcceleration(1000)
+
+# Homing axis one
+machine_motion_example.emitHome(1)
+
+# Move the axis one to position 100 mm
+machine_motion_example.emitAbsoluteMove(1, 100)
+
+# Move the axis one by a negative increment of 100 mm
+machine_motion_example.emitRelativeMove(1, "negative", 100)
+
+print "--> Example completed."
\ No newline at end of file
diff --git a/examples/example--emitSpeed.py b/examples/example--emitSpeed.py
new file mode 100644
index 0000000..cd54b80
--- /dev/null
+++ b/examples/example--emitSpeed.py
@@ -0,0 +1,12 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Configuring the travel speed to 10 000 mm / min
+machine_motion_example.emitSpeed(10000)
+
+print "--> Machine moves are now set to 10 000 mm / min"
\ No newline at end of file
diff --git a/examples/example--emitStop.py b/examples/example--emitStop.py
new file mode 100644
index 0000000..b1996cb
--- /dev/null
+++ b/examples/example--emitStop.py
@@ -0,0 +1,15 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Send a stop command to the Machine (even if it is not moving yet !)
+
+for i in range(0, 50):
+ machine_motion_example.emitStop()
+ machine_motion_example.emitRelativeMove(1, "positive", 10)
+
+print "--> Machine Stopped"
diff --git a/examples/example--emitgCode.py b/examples/example--emitgCode.py
new file mode 100644
index 0000000..b879a15
--- /dev/null
+++ b/examples/example--emitgCode.py
@@ -0,0 +1,23 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Configuring the travel speed to 10 000 mm / min
+machine_motion_example.emitSpeed(10000)
+
+# Configuring the travel speed to 1000 mm / second^2
+machine_motion_example.emitAcceleration(1000)
+
+# Homing axis one
+machine_motion_example.emitHome(1)
+machine_motion_example.waitForMotionCompletion()
+
+# Use the G0 command to move both axis one and two by 500mm at a travel speed of 10 000 mm / minute
+machine_motion_example.emitgCode("G0 X50 Y50 F10000")
+machine_motion_example.waitForMotionCompletion()
+
+print "--> Example completed."
\ No newline at end of file
diff --git a/examples/example--getData.py b/examples/example--getData.py
new file mode 100644
index 0000000..8d30823
--- /dev/null
+++ b/examples/example--getData.py
@@ -0,0 +1,16 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+# Define a callback to print the data retrieved using the getData function
+def printGetDataResult(data):
+ print "--> Retrieved data = " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Saving a string on the controller
+machine_motion_example.saveData("data_1", "save_this_string_on_the_controller")
+
+machine_motion_example.getData("data_1", printGetDataResult)
\ No newline at end of file
diff --git a/examples/example--isReady.py b/examples/example--isReady.py
new file mode 100644
index 0000000..1fd2065
--- /dev/null
+++ b/examples/example--isReady.py
@@ -0,0 +1,14 @@
+from _MachineMotion import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Homing axis one
+machine_motion_example.emitHome(1)
+# Wait for the message to be acknowledged by the motion controller
+while machine_motion_example.isReady() != "true": pass
+
+print "--> This line executes after the motion controller has acknowledged the reception of the command."
diff --git a/examples/example--readControlDevice--sensor4.py b/examples/example--readControlDevice--sensor4.py
new file mode 100644
index 0000000..0bbe397
--- /dev/null
+++ b/examples/example--readControlDevice--sensor4.py
@@ -0,0 +1,38 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+# Define a callback to invoke when a control device is attached to the controller
+def attachControlDeviceCallback(data):
+ print "Attach control device callback: " + data
+
+# Define a callback to invoke when a control device is detached from the controller
+def detachControlDeviceCallback(data):
+ print "Detach control device callback: " + data
+
+# Define a callback to invoke when a control device is read
+def readControlDeviceCallback(data):
+ print "Read control device callback: " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Attach a control device (in this case, and encoder) to the MachineMotion controller.
+machine_motion_example.attachControlDevice("SENSOR4",CONTROL_DEVICE_TYPE.IO_EXPANDER_GENERIC, attachControlDeviceCallback)
+
+count = 0
+
+for count in range (0, 100):
+
+ machine_motion_example.readControlDevice("SENSOR4", "SIGNAL4", readControlDeviceCallback)
+ time.sleep(1)
+
+ machine_motion_example.readControlDevice("SENSOR4", "SIGNAL5", readControlDeviceCallback)
+ time.sleep(1)
+
+ machine_motion_example.readControlDevice("SENSOR4", "SIGNAL6", readControlDeviceCallback)
+ time.sleep(1)
+
+# Detach (deconfigure) a control device that was previously attached to the MachineMotion controller.
+machine_motion_example.detachControlDevice("SENSOR4", detachControlDeviceCallback)
diff --git a/examples/example--readControlDevice--sensor5.py b/examples/example--readControlDevice--sensor5.py
new file mode 100644
index 0000000..12a36b7
--- /dev/null
+++ b/examples/example--readControlDevice--sensor5.py
@@ -0,0 +1,38 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+# Define a callback to invoke when a control device is attached to the controller
+def attachControlDeviceCallback(data):
+ print "Attach control device callback: " + data
+
+# Define a callback to invoke when a control device is detached from the controller
+def detachControlDeviceCallback(data):
+ print "Detach control device callback: " + data
+
+# Define a callback to invoke when a control device is read
+def readControlDeviceCallback(data):
+ print "Read control device callback: " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Attach a control device (in this case, and encoder) to the MachineMotion controller.
+machine_motion_example.attachControlDevice("SENSOR5",CONTROL_DEVICE_TYPE.IO_EXPANDER_GENERIC, attachControlDeviceCallback)
+
+count = 0
+
+for count in range (0, 100):
+
+ machine_motion_example.readControlDevice("SENSOR5", "SIGNAL4", readControlDeviceCallback)
+ time.sleep(1)
+
+ machine_motion_example.readControlDevice("SENSOR5", "SIGNAL5", readControlDeviceCallback)
+ time.sleep(1)
+
+ machine_motion_example.readControlDevice("SENSOR5", "SIGNAL6", readControlDeviceCallback)
+ time.sleep(1)
+
+# Detach (deconfigure) a control device that was previously attached to the MachineMotion controller.
+machine_motion_example.detachControlDevice("SENSOR5", detachControlDeviceCallback)
diff --git a/examples/example--readControlDevice--sensor6.py b/examples/example--readControlDevice--sensor6.py
new file mode 100644
index 0000000..a8dbc68
--- /dev/null
+++ b/examples/example--readControlDevice--sensor6.py
@@ -0,0 +1,38 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+# Define a callback to invoke when a control device is attached to the controller
+def attachControlDeviceCallback(data):
+ print "Attach control device callback: " + data
+
+# Define a callback to invoke when a control device is detached from the controller
+def detachControlDeviceCallback(data):
+ print "Detach control device callback: " + data
+
+# Define a callback to invoke when a control device is read
+def readControlDeviceCallback(data):
+ print "Read control device callback: " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Attach a control device (in this case, and encoder) to the MachineMotion controller.
+machine_motion_example.attachControlDevice("SENSOR6",CONTROL_DEVICE_TYPE.IO_EXPANDER_GENERIC, attachControlDeviceCallback)
+
+count = 0
+
+for count in range (0, 100):
+
+ machine_motion_example.readControlDevice("SENSOR6", "SIGNAL4", readControlDeviceCallback)
+ time.sleep(1)
+
+ machine_motion_example.readControlDevice("SENSOR6", "SIGNAL5", readControlDeviceCallback)
+ time.sleep(1)
+
+ machine_motion_example.readControlDevice("SENSOR6", "SIGNAL6", readControlDeviceCallback)
+ time.sleep(1)
+
+# Detach (deconfigure) a control device that was previously attached to the MachineMotion controller.
+machine_motion_example.detachControlDevice("SENSOR6", detachControlDeviceCallback)
diff --git a/examples/example--readEncoder--sensor4.py b/examples/example--readEncoder--sensor4.py
new file mode 100644
index 0000000..1b12362
--- /dev/null
+++ b/examples/example--readEncoder--sensor4.py
@@ -0,0 +1,35 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+# Define a callback to invoke when a control device is attached to the controller
+def attachControlDeviceCallback(data):
+ print "Attach control device callback: " + data
+
+# Define a callback to invoke when a control device is detached from the controller
+def detachControlDeviceCallback(data):
+ print "Detach control device callback: " + data
+
+# Define a callback to invoke when a control device is read
+def readControlDeviceCallback(data):
+ print "Read control device callback: " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+
+# Attach a control device (in this case, and encoder) to the MachineMotion controller.
+machine_motion_example.attachControlDevice("SENSOR4",CONTROL_DEVICE_TYPE.ENCODER, attachControlDeviceCallback)
+
+count = 0
+
+for count in range (0, 100):
+
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.readControlDevice("SENSOR4", "SIGNAL0", readControlDeviceCallback)
+
+ time.sleep(1)
+
+# Detach (deconfigure) a control device that was previously attached to the MachineMotion controller.
+machine_motion_example.detachControlDevice("SENSOR4", detachControlDeviceCallback)
diff --git a/examples/example--readEncoder--sensor5.py b/examples/example--readEncoder--sensor5.py
new file mode 100644
index 0000000..51a0a4d
--- /dev/null
+++ b/examples/example--readEncoder--sensor5.py
@@ -0,0 +1,35 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+# Define a callback to invoke when a control device is attached to the controller
+def attachControlDeviceCallback(data):
+ print "Attach control device callback: " + data
+
+# Define a callback to invoke when a control device is detached from the controller
+def detachControlDeviceCallback(data):
+ print "Detach control device callback: " + data
+
+# Define a callback to invoke when a control device is read
+def readControlDeviceCallback(data):
+ print "Read control device callback: " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+
+# Attach a control device (in this case, and encoder) to the MachineMotion controller.
+machine_motion_example.attachControlDevice("SENSOR5",CONTROL_DEVICE_TYPE.ENCODER, attachControlDeviceCallback)
+
+count = 0
+
+for count in range (0, 100):
+
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.readControlDevice("SENSOR5", "SIGNAL0", readControlDeviceCallback)
+
+ time.sleep(1)
+
+# Detach (deconfigure) a control device that was previously attached to the MachineMotion controller.
+machine_motion_example.detachControlDevice("SENSOR5", detachControlDeviceCallback)
diff --git a/examples/example--readEncoder--sensor6.py b/examples/example--readEncoder--sensor6.py
new file mode 100644
index 0000000..996f3eb
--- /dev/null
+++ b/examples/example--readEncoder--sensor6.py
@@ -0,0 +1,35 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+# Define a callback to invoke when a control device is attached to the controller
+def attachControlDeviceCallback(data):
+ print "Attach control device callback: " + data
+
+# Define a callback to invoke when a control device is detached from the controller
+def detachControlDeviceCallback(data):
+ print "Detach control device callback: " + data
+
+# Define a callback to invoke when a control device is read
+def readControlDeviceCallback(data):
+ print "Read control device callback: " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+
+# Attach a control device (in this case, and encoder) to the MachineMotion controller.
+machine_motion_example.attachControlDevice("SENSOR6",CONTROL_DEVICE_TYPE.ENCODER, attachControlDeviceCallback)
+
+count = 0
+
+for count in range (0, 100):
+
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.readControlDevice("SENSOR6", "SIGNAL0", readControlDeviceCallback)
+
+ time.sleep(1)
+
+# Detach (deconfigure) a control device that was previously attached to the MachineMotion controller.
+machine_motion_example.detachControlDevice("SENSOR6", detachControlDeviceCallback)
diff --git a/examples/example--saveData.py b/examples/example--saveData.py
new file mode 100644
index 0000000..9cab678
--- /dev/null
+++ b/examples/example--saveData.py
@@ -0,0 +1,12 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Saving a string on the controller
+machine_motion_example = machine_motion_example.saveData("data_1", "save_this_string_on_the_controller")
+
+print "--> Data sent on controller"
\ No newline at end of file
diff --git a/examples/example--waitForMotionCompletion.py b/examples/example--waitForMotionCompletion.py
new file mode 100644
index 0000000..3d7fa10
--- /dev/null
+++ b/examples/example--waitForMotionCompletion.py
@@ -0,0 +1,15 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def templateCallback(data):
+ print "Controller gCode responses " + data
+
+machine_motion_example = MachineMotion(templateCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Homing axis one
+machine_motion_example.emitHome(1)
+# Wait for the message to be acknowledged by the motion controller
+while machine_motion_example.isReady() != "true": pass
+machine_motion_example.waitForMotionCompletion()
+
+print "--> This line executes after the motion controller has acknowledged the reception of the command."
diff --git a/examples/example--writeControlDevice--sensor4.py b/examples/example--writeControlDevice--sensor4.py
new file mode 100644
index 0000000..b15e5e5
--- /dev/null
+++ b/examples/example--writeControlDevice--sensor4.py
@@ -0,0 +1,43 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def motionControllerMessagesCallback(data):
+ print "Controller gCode responses " + data
+
+def unusedCallback(data):
+ pass
+
+# Define a callback to invoke when a control device is read
+def writeControlCallback(data):
+ print "Read control device callback: " + data
+
+machine_motion_example = MachineMotion(motionControllerMessagesCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Attach a control device (in this case, and encoder) to the MachineMotion controller.
+machine_motion_example.attachControlDevice("SENSOR4",CONTROL_DEVICE_TYPE.IO_EXPANDER_GENERIC, unusedCallback)
+
+count = 0
+
+for count in range (0, 100):
+
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.writeControlDevice("SENSOR4", "SIGNAL0", False, writeControlCallback)
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.writeControlDevice("SENSOR4", "SIGNAL1", False, writeControlCallback)
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.writeControlDevice("SENSOR4", "SIGNAL2", False, writeControlCallback)
+
+ time.sleep(1)
+
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.writeControlDevice("SENSOR4", "SIGNAL0", True, writeControlCallback)
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.writeControlDevice("SENSOR4", "SIGNAL1", True, writeControlCallback)
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.writeControlDevice("SENSOR4", "SIGNAL2", True, writeControlCallback)
+
+ time.sleep(1)
+
+# Detach (deconfigure) a control device that was previously attached to the MachineMotion controller.
+machine_motion_example.detachControlDevice("SENSOR4", unusedCallback)
+
diff --git a/examples/example--writeControlDevice--sensor5.py b/examples/example--writeControlDevice--sensor5.py
new file mode 100644
index 0000000..eb2625d
--- /dev/null
+++ b/examples/example--writeControlDevice--sensor5.py
@@ -0,0 +1,43 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def motionControllerMessagesCallback(data):
+ print "Controller gCode responses " + data
+
+def unusedCallback(data):
+ pass
+
+# Define a callback to invoke when a control device is read
+def writeControlCallback(data):
+ print "Read control device callback: " + data
+
+machine_motion_example = MachineMotion(motionControllerMessagesCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Attach a control device (in this case, and encoder) to the MachineMotion controller.
+machine_motion_example.attachControlDevice("SENSOR5",CONTROL_DEVICE_TYPE.IO_EXPANDER_GENERIC, unusedCallback)
+
+count = 0
+
+for count in range (0, 100):
+
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.writeControlDevice("SENSOR5", "SIGNAL0", False, writeControlCallback)
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.writeControlDevice("SENSOR5", "SIGNAL1", False, writeControlCallback)
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.writeControlDevice("SENSOR5", "SIGNAL2", False, writeControlCallback)
+
+ time.sleep(1)
+
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.writeControlDevice("SENSOR5", "SIGNAL0", True, writeControlCallback)
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.writeControlDevice("SENSOR5", "SIGNAL1", True, writeControlCallback)
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.writeControlDevice("SENSOR5", "SIGNAL2", True, writeControlCallback)
+
+ time.sleep(1)
+
+# Detach (deconfigure) a control device that was previously attached to the MachineMotion controller.
+machine_motion_example.detachControlDevice("SENSOR5", unusedCallback)
+
diff --git a/examples/example--writeControlDevice--sensor6.py b/examples/example--writeControlDevice--sensor6.py
new file mode 100644
index 0000000..94c1d55
--- /dev/null
+++ b/examples/example--writeControlDevice--sensor6.py
@@ -0,0 +1,43 @@
+from _MachineMotion_v1_6_5 import *
+
+# Define a callback to process controller gCode responses (if desired)
+def motionControllerMessagesCallback(data):
+ print "Controller gCode responses " + data
+
+def unusedCallback(data):
+ pass
+
+# Define a callback to invoke when a control device is read
+def writeControlCallback(data):
+ print "Read control device callback: " + data
+
+machine_motion_example = MachineMotion(motionControllerMessagesCallback, DEFAULT_IP_ADDRESS.usb_windows)
+
+# Attach a control device (in this case, and encoder) to the MachineMotion controller.
+machine_motion_example.attachControlDevice("SENSOR6",CONTROL_DEVICE_TYPE.IO_EXPANDER_GENERIC, unusedCallback)
+
+count = 0
+
+for count in range (0, 100):
+
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.writeControlDevice("SENSOR6", "SIGNAL0", False, writeControlCallback)
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.writeControlDevice("SENSOR6", "SIGNAL1", False, writeControlCallback)
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.writeControlDevice("SENSOR6", "SIGNAL2", False, writeControlCallback)
+
+ time.sleep(1)
+
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.writeControlDevice("SENSOR6", "SIGNAL0", True, writeControlCallback)
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.writeControlDevice("SENSOR6", "SIGNAL1", True, writeControlCallback)
+ # Read the signal of a control device. SIGNAL0 of the encoder returns the relative position in turns. The encoder resets its position to 0 at power-on. Position is given in turns.
+ machine_motion_example.writeControlDevice("SENSOR6", "SIGNAL2", True, writeControlCallback)
+
+ time.sleep(1)
+
+# Detach (deconfigure) a control device that was previously attached to the MachineMotion controller.
+machine_motion_example.detachControlDevice("SENSOR6", unusedCallback)
+
diff --git a/release-notes.md b/release-notes.md
new file mode 100644
index 0000000..e55adef
--- /dev/null
+++ b/release-notes.md
@@ -0,0 +1,16 @@
+# Version: 1.6.5
+
+Date: June 4 2019
+
+## Bug Fixes:
+- Fix Line Number mismatch with the help of the 'resend' message
+- Fix application hang on termination
+
+## New Features:
+- Support for the rotatory indexer with constants for mechanical gain.
+
+## Improvements:
+- Added more examples for each sensor port for the different control devices functions.
+- Auto reconnect on connection loss
+- Instead of starting a new thread each 0.1 seconds, we now start one thread at the beginning and keep it alive forever to receive messages from the server
+