Skip to content

Commit

Permalink
Merged first commit of pull request #25 from Alexander Wenger.
Browse files Browse the repository at this point in the history
Cleanup of code and mirror the coordinate system.

This is a prerequisite to get the regmarks working.
With this commit the options return_home and no_trailer has been removed. It will be replaced in a later commt with end_position.

Comments from the author:
The biggest problem was the non-standard communication between software and cutter. There seem to be a lot of left-overs from previous work and also quite a bit of mis-interpretation.
My approach:

 * replace comma with "\x03" as command delimiter (comma is for separation of parameters within command) and properly terminate all communication
 * drop all unnecessary communication (polls where result is not used, unknown commands, anything not found in the sniffed communication
 * refactor communication in initialization, setup and job
 * do more testing to determine actual effect of commands (well, not always successful, but I think it is an improvement)
 * use cleaner coordinate transform before sending data to Cameo. Helps in offset-issue (see comment in code)

This results in much more stable and reliable operation with my cutter. I think other users could also benefit.
  • Loading branch information
EtherGraf committed Jan 16, 2017
2 parents fc90743 + 47d86e1 commit d35dfa1
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 61 deletions.
5 changes: 3 additions & 2 deletions sendto_silhouette.py
Original file line number Diff line number Diff line change
Expand Up @@ -1069,8 +1069,9 @@ def write_progress(done, total, msg):
percent_per_sec = 1000. # unreliable data

wait_sec = 1
while (percent_per_sec*wait_sec < 1.6): # max 60 dots
wait_sec *= 2
if percent_per_sec > 1: # prevent overflow if device_buffer_perc is almost 100
while (percent_per_sec*wait_sec < 1.6): # max 60 dots
wait_sec *= 2
dots = '.'
while self.options.wait_done and state == 'moving':
time.sleep(wait_sec)
Expand Down
157 changes: 98 additions & 59 deletions silhouette/Graphtec.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,9 +421,51 @@ def initialize(s):
# Initialise plotter.
s.write("\x1b\x04")

def home(s):
"""Send the home command. Untested. Called by setup()."""
s.write("TT\x03")
# Initial palaver
try:
s.write("FG\x03") # query device name
except Exception as e:
raise ValueError("Write Exception: %s, %s errno=%s\n\nFailed to write the first 3 bytes. Permissions? inf-wizard?" % (type(e), e, e.errno))

try:
resp = s.read(timeout=1000)
if len(resp) > 1:
print("FG: '%s'" % (resp[:-1]), file=s.log)
except:
pass

# Additional commands seen in init by Silhouette Studio
#s.write("FQ0\x03") # asks for something, no idea, just repeating sniffed communication
#try:
# resp = s.read(timeout=1000)
# if len(resp) > 1:
# print("FQ0: '%s'" % (resp[:-1]), file=s.log)
#except:
# pass

#s.write("FQ2\x03") # asks for something, no idea, just repeating sniffed communication
#try:
# resp = s.read(timeout=1000)
# if len(resp) > 1:
# print("FQ2: '%s'" % (resp[:-1]), file=s.log)
#except:
# pass

#s.write("TB71\x03") # asks for something, no idea, just repeating sniffed communication
#try:
# resp = s.read(timeout=1000)
# if len(resp) > 1:
# print("TB71: '%s'" % (resp[:-1]), file=s.log)
#except:
# pass

#s.write("FA\x03") # asks for something, not sure, current position?
#try:
# resp = s.read(timeout=1000)
# if len(resp) > 1:
# print("FA: '%s'" % (resp[:-1]), file=s.log)
#except:
# pass

def get_version(s):
"""Retrieve the firmware version string from the device."""
Expand All @@ -439,7 +481,7 @@ def get_version(s):
return resp[0:-2] # chop of 0x03


def setup(s, media=132, speed=None, pressure=None, pen=None, trackenhancing=False, landscape=False, leftaligned=None, return_home=True):
def setup(s, media=132, speed=None, pressure=None, pen=None, trackenhancing=False, landscape=False, leftaligned=None):
"""media range is [100..300], default 132, "Print Paper Light Weight"
speed range is [1..10], default None, from paper (132 -> 10)
pressure range is [1..33], default None, from paper (132 -> 5)
Expand All @@ -454,12 +496,9 @@ def setup(s, media=132, speed=None, pressure=None, pen=None, trackenhancing=Fals
if leftaligned is not None:
s.leftaligned = leftaligned

s.return_home = return_home

if s.dev is None: return None

s.initialize()
s.home()

if media is not None:
if media < 100 or media > 300: media = 300
Expand Down Expand Up @@ -508,20 +547,17 @@ def setup(s, media=132, speed=None, pressure=None, pen=None, trackenhancing=Fals
else:
s.write("FY0\x03")

#FNx, x = 0 seem to be some kind of reset, x = 1: plotter head moves to other
# side of media (boundary check?), but next cut run will stall
#TB50,x: x = 1 landscape mode, x = 0 portrait mode
if landscape is not None:
if landscape:
s.write("FN1\x03")
s.write("FN0\x03TB50,1\x03")
else:
s.write("FN0\x03")
s.write("FN0\x03TB50,0\x03")

# // No idea what this does.
s.write("FE0\x03")

# // Again, no idea. Maybe something to do with registration marks?
s.write("TB71\x03")
resp = s.read(timeout=10000) # // Allow 10s. Seems reasonable.
if resp != " 0, 0\x03":
raise ValueError("setup: Invalid response from plotter.")
# Don't lift plotter head between paths
s.write("FE0,0\x03")

def find_bbox(s, cut):
"""Find the bouding box of the cut, returns (xmin,ymin,xmax,ymax)"""
Expand Down Expand Up @@ -562,7 +598,25 @@ def plot_cmds(s, plist, bbox, x_off_mm, y_off_mm):
otherwise a hardcoded flipwidth is used to make the coordinate system left aligned.
x_off_mm, y_off_mm are in mm, relative to the clip urx, ury.
"""
flipwidth = int(12*25.4*20) # 6096? physical device width of the Silhouette Cameo

# Change by Alexander Senger:
# Well, there seems to be a clash of different coordinate systems here:
# Cameo uses a system with the origin in the top-left corner, x-axis
# running from top to bottom and y-axis from left to right.
# Inkscape uses a system where the origin is also in the top-left corner
# but x-axis is running from left to right and y-axis from top to
# bottom.
# The transform between these two systems used so far was to set Cameo in
# landscape-mode ("FN0.TB50,1" in Cameo-speak) and flip the x-coordinates
# around the mean x-value (rotate by 90 degrees, mirror and shift x).
# My proposed change: just swap x and y in the data (mirror about main diagonal)
# This is easier and avoids utilizing landscape-mode.
# Why should we bother? Pure technical reason: At the beginning of each cutting run,
# Cameo makes a small "tick" in the margin of the media to align the blade.
# This gives a small offset which is automatically compensated for in
# portrait mode but not (correctly) in landscape mode.
# As a result we get varying offsets which can be really annoying if doing precision
# work.

x_off = x_off_mm * 20.
y_off = y_off_mm * 20.
Expand Down Expand Up @@ -607,7 +661,7 @@ def plot_cmds(s, plist, bbox, x_off_mm, y_off_mm):
bbox['clip']['count'] = 1

if bbox['only'] is False:
plotcmds.append("M%d,%d" % (int(0.5+flipwidth-x), int(0.5+y)))
plotcmds.append("M%d,%d" % (int(0.5+y), int(0.5+x)))

for j in range(1,len(path)):
x = path[j][0]*20. + x_off
Expand Down Expand Up @@ -637,17 +691,17 @@ def plot_cmds(s, plist, bbox, x_off_mm, y_off_mm):

if bbox['only'] is False:
if inside and last_inside:
plotcmds.append("D%d,%d" % (int(0.5+flipwidth-x), int(0.5+y)))
plotcmds.append("D%d,%d" % (int(0.5+y), int(0.5+x)))
else:
# // if outside the range just move
plotcmds.append("M%d,%d" % (int(0.5+flipwidth-x), int(0.5+y)))
plotcmds.append("M%d,%d" % (int(0.5+y), int(0.5+x)))
last_inside = inside
return plotcmds


def plot(s, mediawidth=210.0, mediaheight=297.0, margintop=None,
marginleft=None, pathlist=None, offset=None, bboxonly=False,
end_paper_offset=0, no_trailer=False, regmark=False, regsearch=False,
end_paper_offset=0, regmark=False, regsearch=False,
regwidth=180, reglength=230):
"""plot sends the pathlist to the device (real or dummy) and computes the
bounding box of the pathlist, which is returned.
Expand All @@ -667,9 +721,6 @@ def plot(s, mediawidth=210.0, mediaheight=297.0, margintop=None,
If the end_paper_offset is negative, the end position is within the drawing
(reverse movmeents are clipped at the home position)
It reverse over the last home position.
no_trailer: Default false: The plot is properly terminated.
If true: The device is left hanging at the last position. You are expected to
extract the trailer from the return value, and send it ising the write() method later.
Example: The letter Y (20mm tall, 9mm wide) can be generated with
pathlist=[[(0,0),(4.5,10),(4.5,20)],[(9,0),(4.5,10)]]
"""
Expand All @@ -690,19 +741,6 @@ def plot(s, mediawidth=210.0, mediaheight=297.0, margintop=None,

print("mediabox: (%g,%g)-(%g,%g)" % (marginleft,margintop, mediawidth,mediaheight), file=s.log)

# // Begin page definition.
try:
s.write("FA\x03") # query someting?
except Exception as e:
raise ValueError("Write Exception: %s, %s errno=%s\n\nFailed to write the first 3 bytes. Permissions? inf-wizard?" % (type(e), e, e.errno))

try:
resp = s.read(timeout=10000)
if len(resp) > 1:
print("FA: '%s'" % (resp[:-1]), file=s.log)
except:
pass

width = int(0.5+20.*mediawidth)
height = int(0.5+20.*mediaheight)
top = int(0.5+20.*margintop)
Expand All @@ -718,8 +756,6 @@ def plot(s, mediawidth=210.0, mediaheight=297.0, margintop=None,
if type(offset) != type([]) and type(offset) != type(()):
offset = (offset, 0)

s.write("FU%d,%d\x03" % (height-top, width-left))
s.write("FN0\x03") # // ?
if regmark:
s.write("TB50,0\x03") #only with registration (it was TB50,1) ???
s.write("TB99\x03")
Expand Down Expand Up @@ -754,39 +790,42 @@ def plot(s, mediawidth=210.0, mediaheight=297.0, margintop=None,
# // I think this is the feed command. Sometimes it is 5588 - maybe a maximum?
s.write("FO%d\x03" % (height-top))

p = "&100,100,100,\\0,0,Z%d,%d,L0" % (width,height) # scale
#FMx, x = 0/1: 1 leads to additional horizontal offset of 5 mm, why? Has other profound
# impact (will not cut in certain configuration if x=0). Seems dangerous. Not used
# in communtication of Sil Studio with Cameo2.
#FEx,0 , x = 0 cutting of distinct paths in one go, x = 1 head is lifted at sharp angles
#\xmin, ymin Zxmax,ymax, designate cutting area

p = "\\0,0\x03Z%d,%d\x03L0\x03FE0,0\x03FF0,0,0\x03" % (height, width) #FIXME Is coordinate swap necessary here?
s.write(p)

bbox['clip'] = {'urx':width, 'ury':top, 'llx':left, 'lly':height}
bbox['only'] = bboxonly
cmd_list = s.plot_cmds(pathlist,bbox,offset[0],offset[1])
p += ',' + ','.join(cmd_list)
p = '\x03'.join(cmd_list)

if bboxonly == True:
# move the bouding box
p += ",M%d,%d" % (int(0.5+width-bbox['llx']), int(0.5+bbox['ury']))
p += ",D%d,%d" % (int(0.5+width-bbox['urx']), int(0.5+bbox['ury']))
p += ",D%d,%d" % (int(0.5+width-bbox['urx']), int(0.5+bbox['lly']))
p += ",D%d,%d" % (int(0.5+width-bbox['llx']), int(0.5+bbox['lly']))
p += ",D%d,%d" % (int(0.5+width-bbox['llx']), int(0.5+bbox['ury']))
p = "M%d,%d" % (int(0.5+bbox['ury']), int(0.5+bbox['llx']))
p += "\x03D%d,%d" % (int(0.5+bbox['ury']), int(0.5+bbox['urx']))
p += "\x03D%d,%d" % (int(0.5+bbox['lly']), int(0.5+bbox['urx']))
p += "\x03D%d,%d" % (int(0.5+bbox['lly']), int(0.5+bbox['llx']))
p += "\x03D%d,%d" % (int(0.5+bbox['ury']), int(0.5+bbox['llx']))
p += "\x03" # Properly terminate string of plot commands.

trailer = []
trailer.append("&1,1,1,TB50,0\x03") #; // TB maybe .. ah I dunno. Need to experiment. No idea what &1,1,1 does either.
trailer.append("FO0\x03") # // Feed the page out.
trailer.append("H,") # // Halt? Really move home!

# Silhouette Cameo2 does not start new job if not properly parked on left side
# Attention: This needs the media to not extend beyond the left stop
if not 'llx' in bbox: bbox['llx'] = 0 # survive empty pathlist
if not 'lly' in bbox: bbox['lly'] = 0
if not 'urx' in bbox: bbox['urx'] = 0
if not 'ury' in bbox: bbox['ury'] = 0
new_home = ",M%d,%dSO0FN0" % (int(0.5+width-bbox['llx']), int(0.5+bbox['lly']+end_paper_offset*20.))
new_home = "M%d,%d\x03SO0\x03" % (int(0.5+bbox['lly']+end_paper_offset*20.), 0)

if no_trailer:
s.write(p)
if not s.return_home: trailer.insert(0, new_home)
else:
if not s.return_home: p += new_home
s.write(p)
s.write(''.join(trailer))

s.write(p)
s.write(new_home)

return {
'bbox': bbox,
Expand All @@ -796,7 +835,7 @@ def plot(s, mediawidth=210.0, mediaheight=297.0, margintop=None,


def move_origin(s, feed_mm):
new_home = "M%d,%dSO0FN0" % (int(0.5+feed_mm*20.),0)
new_home = "M%d,%d\x03SO0\x03FN0" % (int(0.5+feed_mm*20.),0)
s.wait_for_ready(verbose=False)
s.write(new_home)
s.wait_for_ready(verbose=False)
Expand Down

0 comments on commit d35dfa1

Please sign in to comment.