Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge in visualization #5

Open
wants to merge 33 commits into
base: visualize2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2380343
some minor library changes
ajmendez Nov 24, 2013
06713cc
added soft reset -- is broken
ajmendez Dec 1, 2013
7fd46ed
updated documentation
ajmendez Apr 7, 2014
78440fa
Update readme.md
ajmendez Apr 7, 2014
7543b97
fixed formatting
ajmendez Apr 7, 2014
778e974
working line finder
ajmendez Apr 8, 2014
b50b556
added new analysis tests
ajmendez Apr 9, 2014
85cbdf9
added circle and height measurements to orient
ajmendez Apr 11, 2014
06bd239
working on cleaning up the code and documenting it all
ajmendez Apr 12, 2014
8556788
added offset and rotate keywords to optimize
ajmendez May 13, 2014
8f577b1
some more tests for rotate
ajmendez May 13, 2014
aa2918f
should split off rotate and offset to different function
ajmendez May 13, 2014
dac1dbf
more rotation checks
ajmendez May 13, 2014
d44b991
adding some things to orient and getting stream streaming
ajmendez May 16, 2014
40ae1a8
laser scan
ajmendez May 21, 2014
39e7d8b
working on home
ajmendez Aug 1, 2014
87c5519
align: use select() properly to wait for input
Apr 12, 2015
4fed513
align: use m/M key to toggle spindle
Apr 12, 2015
48320f8
align: tweak move step with +/-
Apr 12, 2015
f19aa76
align: fix length regex
Apr 12, 2015
8716a32
Merge pull request #2 from bkurtz/master
ajmendez Jul 31, 2015
77d7182
initial work on new features
michaelerickstad Aug 10, 2015
e976519
trying to get new functions for z suface correction of pcb etching on…
michaelerickstad Aug 17, 2015
664c4d7
optimize: lift off at feed rate
bkurtz Aug 24, 2015
b05dda1
optimize: remember to add the last mill operation
Apr 12, 2015
2c9e490
add draw.py
May 1, 2015
7d5fc52
drawing: remove stupid condition
May 1, 2015
da9345d
why is there so much whitespace? well, not anymore
bkurtz Aug 24, 2015
600df1c
make optimize.py not clobber drill files
bkurtz Aug 24, 2015
1f48d58
Zcorrect related bug fixes discovered during field testing of PCB mil…
michaelerickstad Aug 30, 2015
4d586db
Merge pull request #3 from bkurtz/master
ajmendez Sep 10, 2015
63a1c6c
cleanup of merge
ajmendez Sep 10, 2015
5049476
Merge branch 'michaelerickstad-Mikes_PyGRBL_branch'
ajmendez Sep 10, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
*~
MANIFEST
build*
dist*
dist*
dist/
32 changes: 29 additions & 3 deletions align.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

QUIT = ['q','Q']
UPDATE =['u','U']
TWEAKL =['+','-']
MOTOR =['m','M']
UP = ['\x1b[B']
DOWN = ['\x1b[A']
RIGHT = ['\x1b[D']
Expand All @@ -24,18 +26,21 @@
G91 (Incremental)
G0 X0.000 Y0.000 Z0.000
'''

HELP = '''\
Board Alignment Keys:
q/Q : Quit
u/U : Update moveLength -- Amount to nudge
+/- : Increase/Decrease moveLength by a factor of 2 (round to nearest mil)
m/M : Toggle spindle
Arrow Keys : Move in X [Forward/Back]
Y [Left/Right]
a/A / z/Z : Move in Z [Raise/Lower]
'''

moveLength = 0.020 # Inches [0.020] : amount to move use update(), to change
location = dict(X=0.0, Y=0.0, Z=0.0) # store the current location inches

spindleState = 0;


# Some helper functions, scroll down for tasty bits
Expand All @@ -58,6 +63,14 @@ def move(direction=''):
puts(colored.blue('(%s)'%', '.join(['%.3f'%location[k] for k in location])))
# isAt = ', '.join(['%s=%.3f'%(a,location[a]) for a in location])
# puts(colored.blue(' Currently at: %s'%isAt))

def toggleSpindle(state):
'''Send on/off command for spindle'''
if state:
serial.run('M05')
else:
serial.run('M03')
return not state

def update():
'''Update the moveLength for each command'''
Expand All @@ -78,7 +91,7 @@ def update():
value = re.sub(r'\s','',userValue.strip()) # Remove Whitespace

# match to units and values
c = re.match(r'(?P<num>\d+\.?\d+)(?P<unit>'+'|'.join(units)+')', value, re.IGNORECASE)
c = re.match(r'(?P<num>(?:\d*\.)?\d+)(?P<unit>'+'|'.join(units)+')', value, re.IGNORECASE)

# if the user was bad just go back
if not c or not c.group('unit') in units:
Expand All @@ -90,6 +103,17 @@ def update():
puts(colored.blue(' > moveLength is now: %.3f inch\n'%newLength))
return newLength

def tweakLength(origLength, key):
newLength = origLength
if key == '+':
newLength *= 2
else:
newLength /= 2
# round to the nearest mil because we're outputting that resolution with relative addressing
# if we try to use fractional mils, we'll end up with the position we tell the user not matching where the machine actually is
newLength = round(newLength*1000)/1000
puts(colored.blue(' > moveLength is now: %.3f inch\n'%newLength))
return newLength



Expand All @@ -112,11 +136,13 @@ def update():
print '<waiting for key>'
with Terminal() as terminal:
while True:
if terminal.isData():
if terminal.waitForData():
c = terminal.getch()
terminal.wait()
if c in QUIT: sys.exit() # Quit the program
elif c in UPDATE: moveLength = update()
elif c in TWEAKL: moveLength = tweakLength(moveLength, c)
elif c in MOTOR: spindleState = toggleSpindle(spindleState)
elif c in UP: move('X-')
elif c in DOWN: move('X+')
elif c in RIGHT: move('Y-')
Expand Down
6 changes: 3 additions & 3 deletions command.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
with Communicate(args.device, args.speed, timeout=args.timeout,
debug=args.debug,
quiet=args.quiet) as serial:

# now we send commands to the grbl, and wait waitTime for some response.
while True:
# Get some command
Expand All @@ -26,7 +26,7 @@
if x in ['~']: serial.run('~\n?')
# run it if is not a quit switch
serial.run(x)

except KeyboardInterrupt:
puts(colored.red('Emergency Feed Hold. Enter "~" to continue'))
serial.run('!\n?')
serial.run('!\n?')
61 changes: 61 additions & 0 deletions draw.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env python
# draw.py : simulate mill/drill/etch to an EPS file
# [2015-04-17] - bkurtz
import os, re, sys
from math import ceil
from datetime import datetime
from lib.gcode import GCode
from lib.tool import Tool
from lib.drawing import Drawing
from lib import argv
from lib.util import deltaTime
from clint.textui import puts, colored


def main(etch_file, args=None):
start = datetime.now()
name = etch_file if isinstance(etch_file,str) else etch_file.name
puts(colored.blue('Visualizing the file: %s\n Started: %s'%(name,datetime.now())))

# Read in the gcode
gcode = GCode(etch_file, limit=None)
gcode.parse()

# parse the code into an array of tool moves
tool = Tool(gcode)
box = tool.boundBox()

# proces and save image
outfile = os.path.splitext(etch_file.name)[0] + '.eps'
print box
print box[0:2]
image = Drawing(outfile)#, bbox=box)
image.process(tool)
image.save()

# how long did this take?
puts(colored.green('Time to completion: %s'%(deltaTime(start))))
print


if __name__ == '__main__':
## I should wrap this in a __main__ section
# Initialize the args
start = datetime.now()
args = argv.arg(description='PyGRBL gcode imaging tool',
getFile=True, # get gcode to process
getMultiFiles=True, # accept any number of files
getDevice=False) # We dont need a device


# optimize each file in the list
for gfile in args.gcode:
# only process things not processed before.
# c = re.match(r'(?P<drill>\.drill\.tap)|(?P<etch>\.etch\.tap)', gfile.name)
c = re.match(r'(.+)(\.tap)', gfile.name)
# c = True # HAX and accept everything
if c: # either a drill.tap or etch.tap file
main(gfile, args=args)

print '%s finished in %s'%(args.name,deltaTime(start))

14 changes: 9 additions & 5 deletions home.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,26 @@
__DOC__ = 'Setup homing for the machine'

SETUP = '''(Setting up homing)
$16=0 (homing on -- requires reset)
$17=1 (homing on -- requires reset)
$H
$N0 = (setup Staring block)
G28.1 (Soft abs home position)
G30.1 (Sott adjust position)
G10 L2 P1 X0.5 Y0.5 Z0.5 (setup G55)

G10 L2 P1 X0.5 Y0.5 Z-0.5 (setup G54)

G10 L2 P2 X2.5 Y0.5 Z-0.5 (setup G55)

G28 Z0.3 (rapid to 0.3 and then go home)
'''

RESET = '''(Turning off Homing)
$16=0 (Homing off -- requires reset)
$16=0 (Hard Limits off )
$17=0 (Homing off -- requires reset)
$N1= (clear out)
G28
G10 L2 P1 X0 Y0 Z0 (Reset G55)
G10 L2 P1 X0 Y0 Z0 (Reset G54)
G10 L2 P2 X0 Y0 Z0 (Reset G54)

'''


Expand Down
40 changes: 22 additions & 18 deletions lib/argv.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
from util import error


def arg(description=None, getDevice=True,
defaultSpeed=9600, defaultTimeout=0.70,
def arg(description=None, getDevice=True,
defaultSpeed=115200, defaultTimeout=0.70,
getFile=False, getMultiFiles=False,
otherOptions=None):
'''This is a simple arugment parsing function for all of the command line tools'''
if not description:
description='python grbl arguments'

parser = argparse.ArgumentParser(description=description)
parser.add_argument('-q','--quiet',
action='store_true',
Expand All @@ -25,16 +25,16 @@ def arg(description=None, getDevice=True,
action='store_true',
default=False,
help='[DEBUG] use a fake Serial port that prints to screen')

# by default just get the device
# HOWEVER if we want a file to be run, get it FIRST
if getFile:
nargs = '+' if getMultiFiles else 1
parser.add_argument('gcode',
parser.add_argument('gcode',
nargs=nargs,
type=argparse.FileType('r'),
type=argparse.FileType('r'),
help='gCode file to be read and processed.')

# if we want a device get an optional speed / and a device
if getDevice:
parser.add_argument('-s','--speed',
Expand All @@ -47,27 +47,31 @@ def arg(description=None, getDevice=True,
default=defaultTimeout,
type=float,
help='Serial Port Timeout: Amount of time to wait before gathering data for display [%.2f]'%(defaultTimeout))

parser.add_argument('device',
nargs='?',
# action='store_true',
default=False,
help='GRBL serial dev. Generally this should be automatically found for you. You should specify this if it fails, or your have multiple boards attached.')
help='GRBL serial dev. Generally this should be automatically found for you. You should specify this if it fails, or your have multiple boards attached.')

# For any specalized options lets have a general import method
if otherOptions:
for item in otherOptions:
args = otherOptions[item].pop('args')
parser.add_argument(*args, **otherOptions[item])

if isinstance(otherOptions,(dict)):
for item in otherOptions:
args = otherOptions[item].pop('args')
parser.add_argument(*args, **otherOptions[item])
else:
for option in otherOptions:
args = option.pop('args')
parser.add_argument(*args, **option)

args = parser.parse_args()

# lets see if we can find a default device to connect too.
if args.debug: args.device='fakeSerial'
if (getFile) and (not getMultiFiles): args.gcode = args.gcode[0]
if getDevice and not args.device:
# Where they generally are:
# Where they generally are:
devs = ['/dev/tty.usb*','/dev/ttyACM*','/dev/tty.PL*','/dev/ttyUSB*']
founddevs = []
for d in devs:
Expand All @@ -79,8 +83,8 @@ def arg(description=None, getDevice=True,
else:
parser.print_help()
error('Found %d device(s) -- You need to connect a device, update %s, or specify wich device you want to use.'%(len(founddevs),sys.argv[0]))


args.name = sys.argv[0]
args.argv = sys.argv
return args
34 changes: 19 additions & 15 deletions lib/communicate.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def __init__(self, device, speed, debug=False, quiet=False, timeout=None):
# select the right serial device
if debug: s = FakeSerial()
else: s = serial.Serial(device, speed, timeout=timeout)

if not quiet: print '''Initializing grbl at device: %s
Please wait 1 second for device...'''%(device)
s.write("\r\n\r\n")
Expand All @@ -26,16 +26,17 @@ def __init__(self, device, speed, debug=False, quiet=False, timeout=None):
# self.run('$H')
# self.run('G20 (Inches)')
# self.run('G90 (Absolute)')


def run(self, cmd, singleLine=False):
'''Extends either serial device with a nice run command that prints out the
command and also gets what the device responds with.'''
puts(colored.blue(' Sending: [%s]'%cmd ), newline=(not singleLine))
out = '' # i think it is better to initialize out before sending the command
self.write(cmd+'\n')
out = ''
time.sleep(self.timeout)
# while s.inWaiting() > 0: out += s.read(10)
#Why is it not self.s.inWaiting() ????
while self.inWaiting() > 0: out += self.readline()
if out != '':
if singleLine:
Expand All @@ -44,24 +45,27 @@ def run(self, cmd, singleLine=False):
else:
puts(colored.green(''.join([' | '+o+'\n' for o in out.splitlines()])))
return out

def sendreset(self):
'''Sends crtl-x which is the reset key ::
\030 24 CAN \x18 ^X (Cancel)
'''
self.s.write(24)


def reset(self):
'''sends crtl-x to grbl to reset it -- clean up in the future'''
self.s.write(24)

def __enter__(self):
return self

def __exit__(self, type, value, traceback):
self.s.setDTR(False)
time.sleep(0.022)
self.s.setDTR(True)
self.s.close()
#self.s.setDTR(False)
#time.sleep(0.022)
#self.s.setDTR(True)
#self.s.close()
return isinstance(value, TypeError)

def __getattr__(self, name):
'''if no command here, see if it is in serial.'''
try:
Expand All @@ -73,14 +77,14 @@ def __getattr__(self, name):


class FakeSerial():
'''This is a fake serial device that mimics a true serial devie and
'''This is a fake serial device that mimics a true serial devie and
does fake read/write.'''
def __init__(self):
'''init the fake serial and print out ok'''
self.waiting=1 # If we are waiting
self.ichar=0 # index of the character that we are on.
self.msg='ok' # the message that we print when we get any command

def __getattr__(self, name):
print 'DEBUG SERIAL: %s'%(name)
return self.p
Expand Down Expand Up @@ -112,4 +116,4 @@ def inWaiting(self):
out = self.waiting
if self.waiting == 0:
self.waiting = 1
return out
return out