diff --git a/Software/Zprobe/Zprobe.py b/Software/Zprobe/Zprobe.py index bea8e1f..ab125a5 100644 --- a/Software/Zprobe/Zprobe.py +++ b/Software/Zprobe/Zprobe.py @@ -1,6 +1,6 @@ #!/usr/bin/python -###### Cyclone PCB console v0.1 ###### +###### Cyclone PCB console v0.2 ###### # # DESCRIPTION: # Controller for the Cyclone PCB Factory: @@ -14,7 +14,7 @@ # Attribution - Share Alike - Creative Commons (http://creativecommons.org/licenses/by-sa/3.0/) # # DISCLAIMER: -# This software is provided “as is," and you use the software at your own risk. Under no +# This software is provided "as is", and you use the software at your own risk. Under no # circumstances shall Carlosgs be liable for direct, indirect, special, incidental, or # consequential damages resulting from the use, misuse, or inability to use this software, # even if Carlosgs has been advised of the possibility of such damages. @@ -32,7 +32,7 @@ # Begin configuration BAUDRATE = 115200 -DEVICE = "/dev/ttyUSB0" +DEVICE = "/dev/ttyUSB1" # End configuration import sys @@ -43,20 +43,22 @@ from datetime import datetime #fileToFeed = sys.argv[1] # Will use this later on to load #gcode = open(fileToFeed, "r") -millis_wait = 0.1 # Delay used when re-trying to send/receive from the serial port [seconds] -serial_timeout = 0.1 # Timeout for the serial port [seconds] +millis_wait = 0.5 # Delay used when re-trying to send/receive from the serial port [seconds] +serial_timeout = 5 # Timeout for the serial port [seconds] + +OK_response = "ok" # First two characters of an OK response (case insensitive) def getCurrentTime(): timeNow = datetime.now() - print "Time:",str(timeNow) + print "Time:", str(timeNow) return timeNow -def emptyMachineRecvBuffer(): - response = CNC_Machine.readline() - while response != '': - print "IGNO: ", response - time.sleep(millis_wait) # Wait some milliseconds between attempts +def emptyMachineRecvBuffer(): # We could also use flushInput(), but showing the data that is being discarded is useful for debugging + while CNC_Machine.inWaiting() > 0: response = CNC_Machine.readline() + if response != '': + print "IGNO: ", response + time.sleep(millis_wait) # Wait some milliseconds between attempts def sendToMachine(line): emptyMachineRecvBuffer() @@ -67,21 +69,28 @@ def recvFromMachine(): response = CNC_Machine.readline() if response != '': print "RECV: ", response + else: + print "RECV: Receive timed out!" return response def machineSaysOK(): response = recvFromMachine() - if response[:2] == "ok": + if response[:2].lower() == OK_response.lower(): return 1 return 0 -def waitForOK(): +def waitForOK(): # This is a blocking function print "Waiting for confirmation" while machineSaysOK() != 1: - #print "." + print " Checking again..." time.sleep(millis_wait) # Wait some milliseconds between attempts +def sendCommandToMachine(command): # Send command and wait for OK + sendToMachine(command) + waitForOK() + def checkConnection(): + print "Checking the connection..." sendToMachine("G21\n") # We check the connection setting millimiters as the unit and waiting for the OK response time.sleep(0.5) while machineSaysOK() != 1: @@ -94,73 +103,61 @@ CNC_Machine = serial.Serial(DEVICE, BAUDRATE, timeout = serial_timeout) print "Serial port opened, checking connection..." -time.sleep(0.5) +time.sleep(1) checkConnection(); print "CONNECTED" -time.sleep(1) - -sendToMachine("G90\n") # Set absolute positioning -waitForOK() +sendCommandToMachine("G90\n") # Set absolute positioning def machineHomeZXY(): print "Homing all axis..." - sendToMachine("G28 Z0\n") # move Z to min endstop - waitForOK() - sendToMachine("G28 X0\n") # move X to min endstop - waitForOK() - sendToMachine("G28 Y0\n") # move Y to min endstop - waitForOK() + sendCommandToMachine("G28 Z0\n") # move Z to min endstop + sendCommandToMachine("G28 X0\n") # move X to min endstop + sendCommandToMachine("G28 Y0\n") # move Y to min endstop machineHomeZXY() # Home all the axis F_slowMove = 200 # Move speed [mm/min?] -F_fastMove = 600 +F_fastMove = 700 def floats(val): # This is used to convert a float value to a string (avoiding exponent notation) - return '{:f}'.format(float(val)) # It would be interesting to truncate the decimals that aren't used + return '{:.3f}'.format(float(val)) # It truncates the decimals that aren't used -def machineToCoords(X,Y,Z,F): +def machineToCoords(X, Y, Z, F): print "Moving to:" - sendToMachine("G1 X"+floats(X)+" Y"+floats(Y)+" Z"+floats(Z)+" F"+floats(F)+"\n") - waitForOK() + sendCommandToMachine("G1 X"+floats(X)+" Y"+floats(Y)+" Z"+floats(Z)+" F"+floats(F)+"\n") -def machineToCoordsXY(X,Y,F): +def machineToCoordsXY(X, Y, F): print "Moving to:" - sendToMachine("G1 X"+floats(X)+" Y"+floats(Y)+" F"+floats(F)+"\n") - waitForOK() + sendCommandToMachine("G1 X"+floats(X)+" Y"+floats(Y)+" F"+floats(F)+"\n") -def machineToCoordsZ(Z,F): +def machineToCoordsZ(Z, F): print "Moving Z absolute:" - sendToMachine("G1 Z"+floats(Z)+" F"+floats(F)+"\n") - waitForOK() + sendCommandToMachine("G1 Z"+floats(Z)+" F"+floats(F)+"\n") -def machineToCoordsZrelative(Z,F): +def machineToCoordsZrelative(Z, F): print "Moving Z relative:" - sendToMachine("G91\n") # Set relative positioning - waitForOK() - sendToMachine("G1 Z"+floats(Z)+" F"+floats(F)+"\n") - waitForOK() - sendToMachine("G90\n") # Set absolute positioning - waitForOK() + sendCommandToMachine("G91\n") # Set relative positioning + sendCommandToMachine("G1 Z"+floats(Z)+" F"+floats(F)+"\n") + sendCommandToMachine("G90\n") # Set absolute positioning -grid_origin_X = float(0) # Initial point of the grid +grid_origin_X = float(0) # Initial point of the grid [mm] grid_origin_Y = float(0) -grid_len_X = float(135) # Distance to probe [mm] -grid_len_Y = float(84) +grid_len_X = float(135) #135 # Distance to probe [mm] +grid_len_Y = float(84) #84 -grid_N_X = int(12) # Number of points -grid_N_Y = int(6) +grid_N_X = int(12) #12 # Number of points +grid_N_Y = int(6) #6 -grid_inc_X = grid_len_X/float(grid_N_X-1) # mm +grid_inc_X = grid_len_X/float(grid_N_X-1) # [mm] grid_inc_Y = grid_len_Y/float(grid_N_Y-1) probe_grid = [ [ 0 for i in range(grid_N_X) ] for j in range(grid_N_Y) ] -# Show our grid +# Show our grid (initialised as zeros) for row in probe_grid: print row @@ -168,23 +165,29 @@ print "Probing begins!" print "WARNING: Keep an eye on the machine, unplug if something goes wrong!" beginTime = getCurrentTime() # Store current time in a variable, will be used to measure duration of the probing -# Warning: Do not lower too much or you will cause damage! -# machineToCoordsZrelative(-10,F_slowMove/2) # Move Z towards the PCB (saves some probing time for the first coord) + # Move to grid's origin +machineToCoordsXY(grid_origin_X, grid_origin_Y, F_fastMove) + +# Warning: Do not lower too much or you will potentially cause damage! +initial_Z_lowering_distance = -15 +sendCommandToMachine("M121\n") # Enable endstops (for protection! it should tap the copper SLOWLY) +machineToCoordsZrelative(initial_Z_lowering_distance,F_slowMove) # Move Z towards the PCB (saves some probing time for the first coord) +sendCommandToMachine("M120\n") # Disable endstops (we only use them for homing) def machineProbeZ(): print "Probing Z" sendToMachine("G30\n") # Launch probe command - response = recvFromMachine() # Read the response, it is a variable time so we make multiple attempts + response = recvFromMachine() # Read the response, it is a variable run time so we may need to make multiple attempts while response == '': #print "." time.sleep(millis_wait) # Wait some milliseconds between attempts response = recvFromMachine() response_vals = response.split() # Split the response (i.e. "ok Z:1.23") - if response_vals[0][:2] == "ok": - Zres = response_vals[1][2:] # Ignore the "Z:" and read the coordinate + if response_vals[0][:2].lower() == OK_response.lower(): + Zres = response_vals[1][2:] # Ignore the "Z:" and read the coordinate value print "Result is Z=",Zres return float(Zres) - return 400 # Error case, it has never happened :) + return 400 # Error case, don't worry: it has never happened :) def isOdd(number): if number % 2 == 0: @@ -192,25 +195,29 @@ def isOdd(number): else: return 1 # Odd number -for x_i in range(grid_N_X): - x_val = float(x_i)*grid_inc_X+grid_origin_X; +Z_probing_lift = 0.5 # lift between Z probings [mm] +# Note: The lift is relative to the PCB board, you can usually set a low value to speedup the process. +# But PLEASE keep an eye for possible collisions! + +for x_i in range(grid_N_X): # For each point on the grid... + x_val = float(x_i)*grid_inc_X + grid_origin_X; # Calculate X coordinate optimal_range = range(grid_N_Y) - if isOdd(x_i): # This creates a more optimal path for the probing + if isOdd(x_i): # This optimises a bit the probing path optimal_range = reversed(optimal_range) for y_i in optimal_range: - y_val = float(y_i)*grid_inc_Y+grid_origin_Y; - machineToCoordsXY(x_val,y_val,F_fastMove) - val = machineProbeZ() - machineToCoordsZrelative(0.5,F_fastMove/2) - probe_grid[y_i][x_i]= val + y_val = float(y_i)*grid_inc_Y + grid_origin_Y; # Calculate Y coordinate + machineToCoordsXY(x_val, y_val, F_fastMove) # Move to position + probe_grid[y_i][x_i] = machineProbeZ() # Do the Z probing + machineToCoordsZrelative(Z_probing_lift, F_fastMove/2) # Lift the probe # Once we have all the points, we set the origin as (0,0) and offset the rest of values ZoffsetOrigin = probe_grid[0][0] +print "The origin Z height is", ZoffsetOrigin probe_grid = [[elem - ZoffsetOrigin for elem in row] for row in probe_grid] # Return to the grid's origin -machineToCoordsZrelative(10,F_slowMove) -machineToCoordsXY(grid_origin_X,grid_origin_Y,F_fastMove) +machineToCoordsZrelative(10, F_slowMove) # Lift Z +machineToCoordsXY(grid_origin_X, grid_origin_Y, F_fastMove) # Move to grid's origin # Show our grid @@ -223,7 +230,7 @@ print probe_grid # Right now I am copying this to an Octave script for the visua print "Finished probing!" getCurrentTime() -print "Probing duration:",str(datetime.now()-beginTime) +print "Probing duration:", str(datetime.now() - beginTime) @@ -250,7 +257,11 @@ print "Probing duration:",str(datetime.now()-beginTime) #gcode.close() -CNC_Machine.close() # Close the serial port + +# IMPORTANT: Before closing the serial port we must make a blocking move in order to wait for all the buffered commands to end +sendCommandToMachine("G28 Z0\n") # move Z to min endstop + +CNC_Machine.close() # Close the serial port connection # Bilinear interpolation code by Raymond Hettinger from http://stackoverflow.com/a/8662355 @@ -282,3 +293,4 @@ def bilinear_interpolation(x, y, points): q12 * (x2 - x) * (y - y1) + q22 * (x - x1) * (y - y1) ) / ((x2 - x1) * (y2 - y1) + 0.0) +