diff --git a/144.ngc b/144.ngc
deleted file mode 100644
index f71a21b..0000000
--- a/144.ngc
+++ /dev/null
@@ -1,30 +0,0 @@
-O144 sub
- (raster scanning)
- (O123 call [pic] [x] [y] [w] [h] [x-dpi] [overscan] [y-gap])
- ( 1 2 3 4 5 6 7 8 )
- #11=[25.4 / #6] (X per col)
- #13=[#5 / #8] (rows)
- (gross hack to pass several params to M144)
- M144 P[#1]
- M145 P[#2] Q[#3]
- M145 P[#4] Q[#5]
- M145 P[#11] Q[#8]
- (loop to produce raster scans)
- #20=0
- G0 X[#2-#7] Y[#3]
- O101 while [#20 lt #13]
- (move to next line)
- G0 Y[#3-#20*#8]
- (forward scan)
- G1 X[#2+#4+#7]
- #20=[#20+1]
- O102 if [#20 lt #13]
- (move to next line)
- G0 Y[#3-#20*#8]
- (backward scan)
- G1 X[#2-#7]
- #20=[#20+1]
- O102 endif
- O101 endwhile
-O144 endsub
-M2
diff --git a/145.ngc b/145.ngc
deleted file mode 100644
index 4073ffa..0000000
--- a/145.ngc
+++ /dev/null
@@ -1,29 +0,0 @@
-O145 sub
- (raster scanning)
- (O145 call [pic] [x] [y] [w] [h] [x-gap] [y-gap] [overscan])
- ( 1 2 3 4 5 6 7 8 )
- #13=[#5 / #7] (rows)
- (gross hack to pass several params to M144)
- M144 P[#1]
- M145 P[#2] Q[#3]
- M145 P[#4] Q[#5]
- M145 P[#6] Q[#7]
- (loop to produce raster scans)
- #20=0
- G0 X[#2-#8] Y[#3]
- O101 while [#20 lt #13]
- (move to next line)
- G0 Y[#3-#20*#7]
- (forward scan)
- G1 X[#2+#4+#8]
- #20=[#20+1]
- O102 if [#20 lt #13]
- (move to next line)
- G0 Y[#3-#20*#7]
- (backward scan)
- G1 X[#2-#8]
- #20=[#20+1]
- O102 endif
- O101 endwhile
-O145 endsub
-M2
diff --git a/2x_Laser.hal b/2x_Laser.hal
index cba3be2..88e6ec4 100755
--- a/2x_Laser.hal
+++ b/2x_Laser.hal
@@ -2,109 +2,123 @@
# If you make changes to this file, they will be
# overwritten when you run stepconf again
-loadrt threads name1=laser-thread period1=[EMCMOT]BASE_PERIOD
+loadrt threads name1=base-thread period1=[EMCMOT]BASE_PERIOD fp1=0 name2=charge-pump-thread period2=100000 fp2=0
loadrt trivkins
-loadrt [EMCMOT]EMCMOT base_period_nsec=[EMCMOT]BASE_PERIOD servo_period_nsec=[EMCMOT]SERVO_PERIOD num_joints=[TRAJ]AXES
+loadrt [EMCMOT]EMCMOT servo_period_nsec=[EMCMOT]SERVO_PERIOD num_joints=[TRAJ]AXES
loadrt probe_parport
loadrt hal_parport cfg="0x378 out"
-setp parport.0.reset-time 1000
+# setp parport.0.reset-time 1200
loadrt stepgen step_type=0,0,0
loadrt pwmgen output_type=0
+loadrt charge_pump
+loadrt laserraster linear_units=[TRAJ]LINEAR_UNITS axis_scale=[AXIS_0]SCALE axis_min_limit=[AXIS_0]MIN_LIMIT axis_max_limit=[AXIS_0]MAX_LIMIT
+loadrt laserfreq
addf parport.0.read base-thread
addf stepgen.make-pulses base-thread
-# need capture-position in laser-thread for raster engraving:
-addf stepgen.capture-position laser-thread
+addf laserraster.0.make-pulses base-thread
+addf laserfreq.0.make-pulses base-thread
addf pwmgen.make-pulses base-thread
addf parport.0.write base-thread
-addf parport.0.reset base-thread
+# addf parport.0.reset base-thread
+addf stepgen.capture-position servo-thread
addf motion-command-handler servo-thread
addf motion-controller servo-thread
addf stepgen.update-freq servo-thread
+addf laserraster.0.update servo-thread
+addf laserfreq.0.update servo-thread
addf pwmgen.update servo-thread
+addf charge-pump charge-pump-thread
+
net laser-power-cmd <= motion.analog-out-00 => pwmgen.0.value
net laser-pwm <= pwmgen.0.pwm
# or tie pwmgen.0.enable to dout-00??
setp pwmgen.0.enable 1
# pwm-freq 0 means PDM (pulse density modulation)
setp pwmgen.0.pwm-freq 0
-setp pwmgen.0.scale 1
-setp pwmgen.0.offset 0
+setp pwmgen.0.max-dc 0.6
+setp pwmgen.0.scale 200
+setp pwmgen.0.offset 0.1
setp pwmgen.0.dither-pwm true
-net dout-02 <= motion.digital-out-02
-net din-02 => motion.digital-in-02
+net coolant-mist <= iocontrol.0.coolant-mist
+net estop-out <= iocontrol.0.user-enable-out
+net estop-out => iocontrol.0.emc-enable-in
-net laser-chiller => parport.0.pin-01-out
+net estop-out => parport.0.pin-01-out
net xstep => parport.0.pin-02-out
-setp parport.0.pin-02-out-reset 1
+# setp parport.0.pin-02-out-reset 1
net xdir => parport.0.pin-03-out
-setp parport.0.pin-03-out-invert 1
+setp parport.0.pin-03-out-invert 0
net ystep => parport.0.pin-04-out
-setp parport.0.pin-04-out-reset 1
+# setp parport.0.pin-04-out-reset 1
net ydir => parport.0.pin-05-out
setp parport.0.pin-05-out-invert 1
-net ustep => parport.0.pin-06-out
-setp parport.0.pin-06-out-reset 1
-net udir => parport.0.pin-07-out
+net wstep => parport.0.pin-06-out
+# setp parport.0.pin-06-out-reset 1
+net wdir => parport.0.pin-07-out
setp parport.0.pin-07-out-invert 1
-net dout-02 => parport.0.pin-08-out
+net xenable => parport.0.pin-08-out
+setp parport.0.pin-08-out-invert 1
net laser-pwm => parport.0.pin-14-out
-net xenable => parport.0.pin-16-out
+net coolant-mist => parport.0.pin-16-out
net laser-final => parport.0.pin-17-out
-net home-x <= parport.0.pin-10-in-not
-net home-y <= parport.0.pin-11-in-not
+net both-w <= parport.0.pin-12-in-not
+net max-home-y <= parport.0.pin-13-in-not
+net min-home-x <= parport.0.pin-15-in-not
-net din-02 <= parport.0.pin-13-in
+# charge pump output on pin 9
+net charge-pump-out-09 charge-pump.out => parport.0.pin-09-out
-# based on Pololu drivers with Allegro drivers (1us ea hi/lo step, 200ns dir setup/hold)
+# Keling 4030 driver 1.2us step, 5us dir setup
setp stepgen.0.position-scale [AXIS_0]SCALE
-setp stepgen.0.steplen 1000
-setp stepgen.0.stepspace 0
-setp stepgen.0.dirhold 200
-setp stepgen.0.dirsetup 200
+setp stepgen.0.steplen 1200
+setp stepgen.0.stepspace 1200
+setp stepgen.0.dirhold 1000
+setp stepgen.0.dirsetup 5000
setp stepgen.0.maxaccel [AXIS_0]STEPGEN_MAXACCEL
net xpos-cmd axis.0.motor-pos-cmd => stepgen.0.position-cmd
net xpos-fb stepgen.0.position-fb => axis.0.motor-pos-fb
net xstep <= stepgen.0.step
net xdir <= stepgen.0.dir
net xenable axis.0.amp-enable-out => stepgen.0.enable
-net home-x => axis.0.home-sw-in
+net min-home-x => axis.0.home-sw-in
+net min-home-x => axis.0.neg-lim-sw-in
setp stepgen.1.position-scale [AXIS_1]SCALE
-setp stepgen.1.steplen 1000
-setp stepgen.1.stepspace 0
-setp stepgen.1.dirhold 200
-setp stepgen.1.dirsetup 200
+setp stepgen.1.steplen 1200
+setp stepgen.1.stepspace 1200
+setp stepgen.1.dirhold 1000
+setp stepgen.1.dirsetup 5000
setp stepgen.1.maxaccel [AXIS_1]STEPGEN_MAXACCEL
net ypos-cmd axis.1.motor-pos-cmd => stepgen.1.position-cmd
net ypos-fb stepgen.1.position-fb => axis.1.motor-pos-fb
net ystep <= stepgen.1.step
net ydir <= stepgen.1.dir
net yenable axis.1.amp-enable-out => stepgen.1.enable
-net home-y => axis.1.home-sw-in
-
-setp stepgen.2.position-scale [AXIS_6]SCALE
-setp stepgen.2.steplen 1000
-setp stepgen.2.stepspace 0
-setp stepgen.2.dirhold 200
-setp stepgen.2.dirsetup 200
-setp stepgen.2.maxaccel [AXIS_6]STEPGEN_MAXACCEL
-net upos-cmd axis.6.motor-pos-cmd => stepgen.2.position-cmd
-net upos-fb stepgen.2.position-fb => axis.6.motor-pos-fb
-net ustep <= stepgen.2.step
-net udir <= stepgen.2.dir
-net uenable axis.6.amp-enable-out => stepgen.2.enable
+net max-home-y => axis.1.home-sw-in
+net max-home-y => axis.1.pos-lim-sw-in
+
+setp stepgen.2.position-scale [AXIS_8]SCALE
+setp stepgen.2.steplen 1200
+setp stepgen.2.stepspace 1200
+setp stepgen.2.dirhold 1000
+setp stepgen.2.dirsetup 5000
+setp stepgen.2.maxaccel [AXIS_8]STEPGEN_MAXACCEL
+net wpos-cmd axis.8.motor-pos-cmd => stepgen.2.position-cmd
+net wpos-fb stepgen.2.position-fb => axis.8.motor-pos-fb
+net wstep <= stepgen.2.step
+net wdir <= stepgen.2.dir
+net wenable axis.8.amp-enable-out => stepgen.2.enable
+net both-w => axis.8.neg-lim-sw-in
+net both-w => axis.8.pos-lim-sw-in
net zpos-cmd-fb <= axis.2.motor-pos-cmd => axis.2.motor-pos-fb
-net estop-out <= iocontrol.0.user-enable-out
-net estop-out => iocontrol.0.emc-enable-in
-
net tool-change-loop iocontrol.0.tool-change => iocontrol.0.tool-changed
net tool-prepare-loop iocontrol.0.tool-prepare => iocontrol.0.tool-prepared
@@ -118,89 +132,30 @@ net laser-dout <= motion.digital-out-00
########################
-loadrt laserfreq
-addf laserfreq.0.make-pulses laser-thread
-addf laserfreq.0.update servo-thread
+setp laserraster.0.enable 1
+setp laserraster.0.laser-on-delay [LASER]TRIGGER_DELAY
+net raster-data-index <= motion.analog-out-01 => laserraster.0.data-index
+net raster-data-1 <= motion.analog-out-02 => laserraster.0.data-1
+net xstep => laserraster.0.stepgen-step
+net xdir => laserraster.0.stepgen-dir
+net laser-raster <= laserraster.0.laser-on
+
+########################
+
setp laserfreq.0.duration [LASER]PULSED_CUT_DURATION
net current-vel motion.current-vel => laserfreq.0.velocity
-net laser-freq motion.spindle-speed-out => laserfreq.0.pulse-per-unit
+net laser-freq-speed motion.spindle-speed-out => laserfreq.0.pulse-per-unit
+net laser-freq-trick-axis-rev motion.spindle-reverse
#net laser-dout => laserfreq.0.enable
setp laserfreq.0.enable 1
net laser-pulsed <= laserfreq.0.pulse
########################
-# XXX configurable axis 0
-
-# Compute motion-offset by observing how it affects the cmd so we can
-# apply it manually to the fb. That allows us to use axis.0.position-fb
-# directly (at base-thread rate) rather than waiting for servo-thread
-# do do the same math and give us axis.N.joint-pos-fb
-loadrt sum2 names=motion-offset-sub,motion-offset-apply
-addf motion-offset-sub servo-thread
-setp motion-offset-sub.gain1 -1
-net xpos-jcmd axis.0.joint-pos-cmd => motion-offset-sub.in0
-net xpos-cmd axis.0.motor-pos-cmd => motion-offset-sub.in1
-net motion-offset <= motion-offset-sub.out
-
-addf motion-offset-apply laser-thread
-net xpos-fb => motion-offset-apply.in0
-net motion-offset => motion-offset-apply.in1
-net raster-pos-fb <= motion-offset-apply.out
-
-
-loadrt comp names=raster-pos-gt-comp,laser-magic-z-comp
-loadrt lut5 names=raster-read-lut,raster-fire-lut,laser-final-lut
-
-loadrt streamer depth=256 cfg=bbf
-
-# The order of addf determines the order of execution.
-# Order these so that the inputs cascade correctly to the outputs:
-addf raster-pos-gt-comp laser-thread
-addf raster-read-lut laser-thread
-addf streamer.0 laser-thread
-addf raster-fire-lut laser-thread
-addf laser-final-lut laser-thread
-
-net raster-fire <= streamer.0.pin.0
-net raster-rev <= streamer.0.pin.1
-net raster-pos <= streamer.0.pin.2
-net raster-empty <= streamer.0.empty
-net raster-read => streamer.0.enable
-
-# has the raster reached the target?
-# raster-pos-gt := raster-pos-fb > raster-pos
-net raster-pos => raster-pos-gt-comp.in0
-net raster-pos-fb => raster-pos-gt-comp.in1
-net raster-pos-gt <= raster-pos-gt-comp.out
-
-# equation for reading the stream:
-# 1) XXX you can't avoid reading when empty or the state never changes
-# 2) flush (read all) when not running (M5 / laser-master off)
-# 3) when the laser position passes the target position in the right direction
-# raster-read := (~laser-master | (raster-pos-gt ^ raster-rev))
-#setp raster-read-lut.function 0x15511551 (with raster-empty)
-setp raster-read-lut.function 0x3ff33ff3
-net raster-empty => raster-read-lut.in-0
-net laser-master => raster-read-lut.in-1
-net raster-pos-gt => raster-read-lut.in-2
-net raster-rev => raster-read-lut.in-3
-net raster-read <= raster-read-lut.out
-
-# equation for firing the laser:
-# 1) never fire when empty (underflow or job finished)
-# 2) fire when raster-fire enabled
-# laser-raster := ~raster-empty & raster-fire
-setp raster-fire-lut.function 0x44444444
-net raster-empty => raster-fire-lut.in-0
-net raster-fire => raster-fire-lut.in-1
-net laser-raster <= raster-fire-lut.out
-
-########################
-
+loadrt comp names=laser-magic-z-comp
addf laser-magic-z-comp servo-thread
net zpos-fb <= axis.2.joint-pos-fb => laser-magic-z-comp.in0
-setp laser-magic-z-comp.in1 0.0
+setp laser-magic-z-comp.in1 -0.0001
net laser-magic-z <= laser-magic-z-comp.out
########################
@@ -212,6 +167,8 @@ net laser-magic-z <= laser-magic-z-comp.out
# (laser-raster |
# ((laser-magic-z | laser-dout) & laser-pulsed))
+loadrt lut5 names=laser-final-lut
+addf laser-final-lut base-thread
setp laser-final-lut.function 0xfeaa0000
net laser-raster => laser-final-lut.in-0
net laser-dout => laser-final-lut.in-1
diff --git a/2x_Laser.ini b/2x_Laser.ini
index ce72b3e..85bcd33 100755
--- a/2x_Laser.ini
+++ b/2x_Laser.ini
@@ -7,32 +7,36 @@ DEBUG = 0
[LASER]
EXTRA_CHILLER_TIME = 20
-# duration of pulse "dashes" in seconds
-PULSED_CUT_DURATION = 0.003
+# duration of pulse "dashes" in ns
+PULSED_CUT_DURATION = 3000000
+# delay between triggering laser to fire and actual pulse in ns
+TRIGGER_DELAY = 200
[RASTER]
#IMAGE_PATH = /home/bjj/Desktop:/home/bjj
[DISPLAY]
DISPLAY = axis
-USER_COMMAND_FILE = /home/bjj/Desktop/2x_Laser/axisrc
+USER_COMMAND_FILE = /home/jvangrin/linuxcnc/configs/2x_laser/axisrc
EDITOR = gedit
POSITION_OFFSET = RELATIVE
POSITION_FEEDBACK = ACTUAL
MAX_FEED_OVERRIDE = 5
-INTRO_GRAPHIC = emc2.gif
+INTRO_GRAPHIC = linuxcnc.gif
INTRO_TIME = 5
-#PROGRAM_PREFIX = /home/ubuntu/emc2/nc_files
-PROGRAM_PREFIX = /home/bjj/Desktop/2x_Laser
-INCREMENTS = 5mm 1mm .5mm .1mm .05mm .01mm .005mm
-
-#[FILTER]
-#PROGRAM_EXTENSION = .png,.gif,.jpg Greyscale Depth Image
-#PROGRAM_EXTENSION = .py Python Script
-#png = image-to-gcode
-#gif = image-to-gcode
-#jpg = image-to-gcode
-#py = python
+PROGRAM_PREFIX = /home/jvangrin/linuxcnc/configs/2x_laser
+INCREMENTS = .1in .05in .01in .005in .001in .0001in
+GEOMETRY = XYZ
+PYVCP = custompanel.xml
+
+[FILTER]
+PROGRAM_EXTENSION = .[nN][gG][cC] rs273ngc Gcode File
+PROGRAM_EXTENSION = .png,.gif,.jpg Greyscale Depth Image
+PROGRAM_EXTENSION = .py Python Script
+png = image-to-gcode
+gif = image-to-gcode
+jpg = image-to-gcode
+py = python
[TASK]
TASK = milltask
@@ -40,29 +44,37 @@ CYCLE_TIME = 0.010
[RS274NGC]
PARAMETER_FILE = emc.var
+RS274NGC_STARTUP_CODE = G20 G90 G64 P0.001
[EMCMOT]
EMCMOT = motmod
COMM_TIMEOUT = 1.0
COMM_WAIT = 0.010
-BASE_PERIOD = 27000
+BASE_PERIOD = 25000
SERVO_PERIOD = 1000000
[HAL]
HALFILE = 2x_Laser.hal
HALFILE = custom.hal
POSTGUI_HALFILE = custom_postgui.hal
-# for joystick:
HALUI = halui
+[HALUI]
+MDI_COMMAND = G0 G53 Z0
+MDI_COMMAND = G10 L20 P1 X0 Y0
+MDI_COMMAND = G38.2 W20 F4
+MDI_COMMAND = G92 W0
+MDI_COMMAND = G0 W0 F4
+
[TRAJ]
-AXES = 7
-COORDINATES = X Y Z U
-LINEAR_UNITS = mm
+AXES = 9
+COORDINATES = X Y Z W
+LINEAR_UNITS = inch
ANGULAR_UNITS = degree
CYCLE_TIME = 0.010
-DEFAULT_VELOCITY = 50.00
-MAX_LINEAR_VELOCITY = 470
+DEFAULT_VELOCITY = 8.0
+MAX_LINEAR_VELOCITY = 18.0
+#NO_FORCE_HOMING = 1
[EMCIO]
EMCIO = io
@@ -71,46 +83,47 @@ TOOL_TABLE = tool.tbl
[AXIS_0]
TYPE = LINEAR
-HOME = 0.0
-MAX_VELOCITY = 470
-#MAX_ACCELERATION = 2500.0
-MAX_ACCELERATION = 7500.0
-STEPGEN_MAXACCEL = 9375.0
-SCALE = 78.7401574803
-FERROR = 1
-MIN_FERROR = .25
+MAX_VELOCITY = 18.0
+MAX_ACCELERATION = 270.0
+STEPGEN_MAXACCEL = 337.0
+SCALE = 1000.0
+FERROR = 0.05
+MIN_FERROR = 0.01
MIN_LIMIT = -0.001
-MAX_LIMIT = 535
-HOME_OFFSET = 0.000000
-HOME_SEARCH_VEL = 120
-HOME_LATCH_VEL = 1
+MAX_LIMIT = 31.501
+HOME = 0
+HOME_OFFSET = -0.25
+HOME_SEARCH_VEL = -2.5
+HOME_LATCH_VEL = 0.83
HOME_SEQUENCE = 0
+HOME_IGNORE_LIMITS = YES
[AXIS_1]
TYPE = LINEAR
-#HOME = 285.0
-MAX_VELOCITY = 200.0
-MAX_ACCELERATION = 5000.0
-STEPGEN_MAXACCEL = 6250.0
-SCALE = 78.7401574803
+MAX_VELOCITY = 18.0
+MAX_ACCELERATION = 200.0
+STEPGEN_MAXACCEL = 250.0
+SCALE = 1000.0
FERROR = 1
-MIN_FERROR = .25
+MIN_FERROR = 0.05
MIN_LIMIT = -0.001
-MAX_LIMIT = 285
-HOME_OFFSET = 0.000000
-HOME_SEARCH_VEL = 100
-HOME_LATCH_VEL = 1
+MAX_LIMIT = 15.751
+HOME = 15.75
+HOME_OFFSET = 16.0
+HOME_SEARCH_VEL = 2.5
+HOME_LATCH_VEL = -0.83
HOME_SEQUENCE = 0
+HOME_IGNORE_LIMITS = YES
-[AXIS_6]
+[AXIS_8]
TYPE = LINEAR
HOME = 0.0
-MAX_VELOCITY = 6
-MAX_ACCELERATION = 50.0
-STEPGEN_MAXACCEL = 62.5
-SCALE = 6047.24409
-FERROR = 1
-MIN_FERROR = .25
+MAX_VELOCITY = 0.25
+MAX_ACCELERATION = 20.0
+STEPGEN_MAXACCEL = 25.0
+SCALE = 19200.0
+FERROR = 0.05
+MIN_FERROR = 0.01
HOME_SEQUENCE = 0
[AXIS_2]
@@ -122,6 +135,7 @@ STEPGEN_MAXACCEL = 60000
SCALE = 1
FERROR = 1
MIN_FERROR = .25
-MIN_LIMIT = -100
-MAX_LIMIT = 100
+MIN_LIMIT = -6
+MAX_LIMIT = 6
HOME_SEQUENCE = 0
+
diff --git a/M144 b/M144
deleted file mode 100755
index df9c462..0000000
--- a/M144
+++ /dev/null
@@ -1,153 +0,0 @@
-#!/usr/bin/env python
-
-import os, sys, time, glob
-from subprocess import *
-from itertools import *
-from PIL import Image
-from raster_utils import *
-from raster_gui import *
-import emc
-
-P, Q = map(lambda x: float(x), sys.argv[1:])
-
-try:
- INI_FILE_NAME = os.environ['INI_FILE_NAME']
-except KeyError:
- INI_FILE_NAME = '2x_Laser.ini'
-
-try:
- ini = emc.ini(INI_FILE_NAME)
-except emc.error:
- fatal('Expected EMC2 configuration in %s' % INI_FILE_NAME)
-
-emc.nmlfile = ini.find('EMC', 'NML_FILE')
-if emc.nmlfile is None:
- fatal('Expected [EMC]NML_FILE in INI')
-
-axis = ini.find('RASTER', 'AXIS')
-if axis is None:
- axis = 0
-
-stat = emc.stat()
-
-# Origin contains the G92 and G5x work offsets so we can translate
-stat.poll()
-origin = stat.origin[axis]
-
-if stat.current_line < 0:
- fatal('cannot execute M144 from MDI')
-
-if False:
- # Unfortunately when M144 is invoked from inside O145 the stat.file
- # is the 145.ngc file rather than the original source. Previously
- # there was clever code to extract the filename from the comment on
- # the M144 line.
- image_name = get_comment(stat.file, stat.current_line)
- dirname = os.path.dirname(stat.file)
- image_name = os.path.normpath(os.path.join(dirname, image_name))
-else:
- IMAGE_PATH = ini.find('RASTER', 'IMAGE_PATH')
- if IMAGE_PATH is None:
- IMAGE_PATH = os.environ['HOME']
- IMAGE_PATH = IMAGE_PATH.split(os.path.pathsep)
- image_name = None
- for dir in IMAGE_PATH:
- images = glob.glob(os.path.join(dir, '*-%u.*' % int(P)))
- if images:
- image_name = images[0]
- if image_name is None:
- image_name = image_not_found(int(P))
- print 'image = %s' % image_name
-
-if os.fork():
- # parent must wait until child is ready, then exit
- #...
- time.sleep(1)
- sys.exit(0)
-else:
- os.setsid()
-
- streamer = Popen(['halstreamer'], stdin=PIPE)
- stream = streamer.stdin
-
- image = Image.open(image_name)
-
- # wait for M145 to send us the image info
- X, Y = recv_params()
- W, H = recv_params()
- XSCANGAP, YSCANGAP = recv_params()
-
- # program units are 1:inch, 2:mm, while linear_units are 0:inch, 1:mm
- if stat.program_units == 1 and stat.linear_units == 1:
- scale = 25.4
- elif stat.program_units == 2 and stat.linear_units == 0:
- scale = 1/25.4
- else:
- scale = 1
- X *= scale
- Y *= scale
- W *= scale
- H *= scale
- XSCANGAP *= scale
- YSCANGAP *= scale
-
- x_mmpd = XSCANGAP
- y_mmpd = YSCANGAP
- origin += X
-
- pix_w = int(W / x_mmpd)
- pix_h = int(H / y_mmpd)
- W = pix_w * x_mmpd
- H = pix_h * y_mmpd
-
- reverse_fudge = 0.0
- #reverse_fudge = 0.339
-
- print 'rescaling to %u,%u' % (pix_w, pix_h)
- #XXX
- image = image.resize((pix_w, pix_h), Image.BICUBIC).convert('1')
- #image = image.resize((pix_w, pix_h), Image.NEAREST).convert('1')
- image.save('actual.png')
- # XXX possibly rotate based on axis
-
- pix = list(image.getdata())
-
- for y in xrange(0,pix_h):
- forward = (y & 1) == 0
-
- # laser is off until cued for this line:
- if forward:
- stream.write('0 1 %0.3f\n' % (origin))
- else:
- stream.write('0 0 %0.3f\n' % (origin + W + x_mmpd))
-
- if False:
- if forward == (y > pix_h / 2):
- continue
-
- row = pix[y * pix_w:(y + 1) * pix_w]
- grouped = groupby(row)
- groups = map(lambda (v,run): (v, len(list(run))), groupby(row))
- if forward:
- x = 0
- else:
- groups.reverse()
- x = pix_w
- for v, run in groups:
- if (v <= 127):
- # off until we hit start / on until end
- if forward:
- stream.write('0 0 %0.3f\n' % (origin + x * x_mmpd))
- stream.write('1 0 %0.3f\n' % (origin + (x + run) * x_mmpd))
- else:
- stream.write('0 1 %0.3f\n' % (origin + x * x_mmpd + reverse_fudge))
- stream.write('1 1 %0.3f\n' % (origin + (x - run) * x_mmpd + reverse_fudge))
- if forward:
- x += run
- else:
- x -= run
-
- stream.close()
- streamer.wait()
-
- print 'DONE!'
diff --git a/M145 b/M145
deleted file mode 100755
index bae8503..0000000
--- a/M145
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/usr/bin/env python
-
-import sys
-import emc
-from raster_utils import *
-
-P, Q = map(lambda x: float(x), sys.argv[1:])
-print 'M145 P=%g Q=%g' % (P, Q)
-send_params(P, Q)
diff --git a/README.md b/README.md
index 3522b98..c00bb28 100755
--- a/README.md
+++ b/README.md
@@ -1,28 +1,26 @@
Overview
========
-This is an EMC2 configuration for the Buildlog.net 2.x laser cutter.
+This is an LinuxCNC 2.5+ configuration for the Buildlog.net 2.x laser cutter.
It has the following features:
* X/Y axis for the laser gantry and carriage. Configured for MXL belts, 400 step motors and 8x microstepping.
-* U axis for the table. Configured for 1/4" 20 TPI threaded rod driven by a 400 step motor, 8x microstepping via a 48:20 belt reduction.
+* W axis for the table. Configured for 1/4" 20 TPI threaded rod driven by a 400 step motor, 8x microstepping via a 48:20 belt reduction.
* Z axis which does not move the table at all but instead activates the laser when Z<0. This provides some "instant compatibility" with mill/router CAM.
-There is some minor customization to Axis, the primary EMC2 GUI. Some
+There is some minor customization to Axis, the primary LinuxCNC GUI. Some
of the viewing angle buttons have been eliminated and page up/down have
been shifted to the U (table) axis.
-The laser has a master enable provided by M3/M5 (spindle control). When the
+The laser has a master enable provided by M3/M4/M5 (spindle control). When the
"spindle" is off the laser cannot fire. This means the laser turns off when
you expect it to, such as when aborting a job.
-When the laser is enabled via M3 it can be fired either by digital IO or
+When the laser is enabled via M3/M4 it can be fired either by digital IO or
by moving the imaginary Z axis to any negative position. Using a high "plunge"
speed in the CAM job and a very small depth of cut (such as 0.01mm) avoids
having the laser pause when it starts and stops cuts.
-A custom "M" script is provided, M144, which can raster engrave images.
-
Special Commands
================
@@ -31,14 +29,14 @@ Laser control
Laser firing control is on parallel port pin 17.
-Set laser power with M68 E0 Qxxx where xxx is a number from 0.0 to 1.0.
-It is likely that your printer port's PWM output at 1.0 will be *more
-than 100% power* for your laser. In my case my laser PSU current is
-20mA when there is 3V at the IN terminal. My parallel port's "on"
-voltage is about 4.3V, so M68 E0 Q0.7 produces full power for my setup.
-Below about 0.5V (or Q0.012) my laser will not fire. This power setting
-can generally go in the preamble of your CAM setup since you will vary
-PPI and speed rather than power for most cutting jobs.
+Set laser power with M68 E0 Qxxx where xxx is a number from 0 to 100.
+It is likely that your printer port's PWM output at 100 may be *more
+or less than 100% power* for your laser. The scaling values in pwmgen
+component can be adjusted to bring the pwm power values inline with the
+actual milli-amp power being output from the laser power supply. This
+power setting can generally go in the preamble of your CAM setup since
+you will vary PPI/DutyCycle and speed rather than power for most cutting
+jobs.
Enable the laser with M3 Sxx where the spindle speed xx is in "pulses
per mm" (or about 1/25th a PPI or "pulses per inch" setting). M3 S0 is
@@ -48,6 +46,12 @@ continuous wave output by simply picking a high enough S value for your
feed rate that pulses happen more frequently than 3ms (e.g. S10000 is
continuous for anything faster than F2).
+Enable the laser with M4 Sxx where the spindle speed xx is in
+"percent duty-cycle". M4 S0 is equivalent to "off" (or M5) and the laser
+will not fire. The laser on pulse length is 3ms (in 2x_Laser.ini) and
+duty-cycle percent adjusts now long the laser if off. Continuous wave
+output can be selected by S100.
+
If you choose direct digital control of the laser, use M65 P0 ("immediate
off") in your preamble and use M62 P0/M63 P0 to turn the laser on and off
within a sequence of G1 movements. The M62/63 are queued with movement
@@ -75,101 +79,37 @@ that removes smoke from the laser.
Raster Engraving
----------------
-Raster operation is done by calling a subroutine O145 from within gcode
-with several parameters. It invokes M144 and M145 which are external
-python scripts. Those stream data back into EMC2's realtime engine while
-the subroutine in O145 sweeps out the raster pattern.
-
-Due to limitations in EMC2 there is no way to pass a filename for the
-rastering process. Instead you must put a number in the filename, such
-as "flower-123.png". The text name is for your convenience, but only the
-"123" will select the image from within gcode. The program will search for
-the wildcard `*-123.*` in [RASTER]IMAGE_PATH (can be a colon separated list)
-and use the first one it finds. If that is not configured in your INI it
-will default to your home directory. If it does not find the file it will
-prompt you with a file selector.
-
-The image can be of any size or shape and will be rescaled and dithered to
-black and white to match the parameters of the engraving (see below). You
-can provide a black and white image with the correct DPI and size and it will
-be used unmodified.
-
-To do a raster engraving, the spindle must be enabled with M3 (as always
-for any laser firing operation). However, the pulse setting does not
-matter and all pulsing is controlled by the engraving process.
-
-The O145 script will operate in inches or mm (G20 or G21) and all sizes
-just need to be in the appropriate units:
-
- M3 S1 (enable spindle)
- M68 E0 Q0.2 (choose an engraving power)
- F28200 (choose an engraving feed rate)
- O145 call [pic] [x] [y] [w] [h] [x-gap] [y-gap] [overscan]
-
-Where the parameters are:
-
-* pic - number used to select the image file (with the wildcard `*-pic.*`)
-* x, y - the upper lefthand corner of the engraving (the spot where the image's (0,0) will appear)
-* w, h - the width and height of the engraving
-* x-gap - units per pixel column (in mm, 25.4/DPI, in inches 1/DPI)
-* y-gap - units per pixel row (see x-gap)
-* overscan - overshoot of the laser carriage to either side of the image
-
-The x-gap and y-gap are independent. Choosing a y-gap of 0.085mm (about
-300 DPI) means that the laser carriage will sweep back and forth 300 times
-for every inch of image height. The finer the y-gap the longer the engraving
-will take. The x-gap only modifies how frequently the laser is modulated
-and thus has no effect on rastering time as long as your system has enough
-memory. Obviously any x-gap smaller than 1/SCALE is meaningless, but with
-the stock setup that would be close to 2000 DPI.
-
-The overscan is to give the laser carriage time to accelerate to full speed.
-If the laser is still accelerating within the field of the image then the
-left/right edges will be engraved slightly darker. You can compute the
-exact distance needed as 0.5 * F^2 / A where F is feed speed (in mm/s, not
-mm/min) and A is [AXIS_0]MAX_ACCELERATION. Assuming you always engrave
-at top speed ([AXIS_0]MAX_VELOCITY = 470mm/s) and modulate power to suit,
-the value of overscan should be about 15mm. If you tune the accel faster
-or engrave at a lower speed you can use smaller overscan. If you are willing
-to tolerate some uneven engraving power at the edges you can turn overscan
-way down to get near the edge of your work area. I have not tested with
-overscan of 0, that might break the algorithm.
-
-Example:
-
- G20 ( set inches mode )
- M3 S20
- M68 E0 Q0.5
- F1110
- O145 call [1587] [0.2133] [10.2867] [10.0733] [10.0733] [0.0033] [0.0033] [0.5]
-
-(Note that the example is in inches due to G20!) That engraves an image
-"dominos-1587.png" at X0.2133 Y10.2867 which is 10.0733 inches square.
-The x- and y-gaps are both 0.0033" making 303 DPI. The feedrate is 1110 IPM
-and the overscan is 0.5".
+Raster operation is done by calling raster_engrave.py to generate a g-code
+file that will engrave an image. Currently all parameters are hard coded
+inside this python script. It is really a proof of concept and is in
+desperate need of a front end gui to setup raster jobs.
+
+The generated g-code file can be touched off and executed in LinuxCNC just
+like any other job.
Installation
============
-This is based on an installed copy of the EMC2 Ubuntu 10.04 LTS Live CD.
+This is based on an installed copy of the LinuxCNC 2.5 Ubuntu 10.04 LTS Live CD.
Install the custom laser pulse HAL component. The first command installs
the necessary tools in case you don't have them. For more information see
http://wiki.linuxcnc.org/emcinfo.pl?ContributedComponents
- sudo apt-get install emc2-dev build-essential
+ sudo apt-get install linuxcnc-dev build-essential
sudo comp --install laserfreq.comp
+ sudo comp --install laserraster.comp
The configuration will not work without that component installed.
-Find all occurances of "/home/bjj/Desktop/2x_Laser" in the INI and replace
+Find all occurances of "/home/jvangrin/Desktop/2x_Laser" in the INI and replace
with the path to your own configuration.
Configuration
=============
-You must first get EMC2's realtime configuration sorted out on your hardware.
-There is extensive documentation for this online based around the EMC2
+You must first get LinuxCNC's realtime configuration sorted out on your hardware.
+There is extensive documentation for this online based around the LinuxCNC
latency-test program: http://wiki.linuxcnc.org/emcinfo.pl?Latency-Test
My system was able to use a [EMCMOT]BASE_PERIOD of 27000 (27us) which
@@ -184,7 +124,7 @@ in 2x_Laser.ini. Ignore AXIS_2, it is the imaginary Z axis.
My build resulted in a maximum travel of 285x535mm. These are the
[AXIS_0]MAX_LIMIT and [AXIS_1]MAX_LIMIT. Setting these correctly will keep
you from banging into the physical endstops. The 2.x build homes in the
-lower left, but you can cause EMC2 to automatically reposition anywhere
+lower left, but you can cause LinuxCNC to automatically reposition anywhere
after homing with the [AXIS_0]HOME and [AXIS_1]HOME.
If any axis moves backwards from what you expect, modify the parport
@@ -213,3 +153,6 @@ Dirk Van Essendelft has done numerous experiments in DIY lasercutting
which he has documented on the buildlog.net forums. His research into the
behavior of PPI with our CO2 lasers lead to improved the performance of the
PPI implementation in this configuration.
+
+Ben Jackson for all the original work getting a working LinuxCNC 2.x laser
+config and laser frequency PPI custom HAL component.
diff --git a/axisrc b/axisrc
index ddc352c..50dc506 100644
--- a/axisrc
+++ b/axisrc
@@ -16,8 +16,8 @@ laser_hide(widgets.view_p)
commands.set_view_z()
# Attach table U to traditional Pg Up/Dn
-bind_axis("Next", "Prior", 6)
-bind_axis("KP_Next", "KP_Prior", 6)
+bind_axis("Next", "Prior", 8)
+bind_axis("KP_Next", "KP_Prior", 8)
# Make Z accessible with [] just in case
bind_axis("bracketleft", "bracketright", 2)
diff --git a/custom_postgui.hal b/custom_postgui.hal
index 786382c..8241cb6 100755
--- a/custom_postgui.hal
+++ b/custom_postgui.hal
@@ -1,3 +1,25 @@
# Include your customized HAL commands here
# The commands in this file are run after the AXIS GUI (including PyVCP panel) starts
+# **** Setup of spindle speed display using pyvcp -START ****
+# **** Use COMMANDED spindle velocity from EMC because no spindle encoder was specified
+# **** COMANDED velocity is signed so we use absolute component (abs.0) to remove sign
+
+loadrt abs
+addf abs.0 servo-thread
+net laser-freq-speed => abs.0.in
+
+net laser-freq-duty-cycle <= abs.0.is-negative => pyvcp.duty-cycle-mode
+net laser-freq-ppi <= abs.0.is-positive => pyvcp.ppi-mode
+net laser-freq-abs <= abs.0.out => pyvcp.spindle
+net laser-power-cmd => pyvcp.power
+
+net laser-master => pyvcp.laser-master
+net laser-raster => pyvcp.laser-raster
+net laser-dout => pyvcp.laser-dout
+net laser-continuous <= laserfreq.0.continuous => pyvcp.laser-continuous
+net laser-magic-z => pyvcp.laser-magic-z
+
+net z-to-zero pyvcp.z-to-zero => halui.mdi-command-00
+net xy-touchoff pyvcp.xy-touchoff => halui.mdi-command-01
+
diff --git a/custompanel.xml b/custompanel.xml
new file mode 100644
index 0000000..f55de5c
--- /dev/null
+++ b/custompanel.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ "spindle"
+ 500
+
+
+
+
+
+
+
+
+
+ "power"
+ 100
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/joystick.hal b/joystick.hal
deleted file mode 100755
index 2fb0f8d..0000000
--- a/joystick.hal
+++ /dev/null
@@ -1,32 +0,0 @@
-# Include your customized HAL commands here
-# The commands in this file are run after the AXIS GUI (including PyVCP panel) starts
-
-
-##########################
-# Joystick
-
-loadusr -W hal_input -KRAL Gravis
-
-loadrt mux2 names=joystick-speeds
-addf joystick-speeds servo-thread
-setp joystick-speeds.in0 1000
-setp joystick-speeds.in1 28000
-net joystick-speed-sel <= input.0.btn-tl2 => joystick-speeds.sel # "select"
-net joystick-speed <= joystick-speeds.out => halui.jog-speed
-
-net joy-x-jog <= input.0.abs-x-position => halui.jog.0.analog
-net joy-y-jog <= input.0.abs-y-position => halui.jog.1.analog
-net joy-u-jog-up <= input.0.btn-y => halui.jog.6.plus # trigger left up
-net joy-u-jog-dn <= input.0.btn-tl => halui.jog.6.minus # trigger left down
-
-# reverse Y
-setp input.0.abs-y-scale -127.5
-
-net joy-prog-run <= input.0.btn-c => halui.program.run # green
-net joy-prog-stop <= input.0.btn-a => halui.program.stop # red
-net joy-prog-pause <= input.0.btn-b => halui.program.pause # yellow
-#input.0.btn-x # blue
-#input.0.btn-z # trigger right up
-#input.0.btn-tr # trigger right down
-#input.0.btn-tr2 # "start"
-
diff --git a/laserfreq.comp b/laserfreq.comp
index bc016fa..0be2ce3 100644
--- a/laserfreq.comp
+++ b/laserfreq.comp
@@ -1,53 +1,61 @@
component laserfreq "Pulse driver for CO2 Laser";
author "Ben Jackson ";
-pin in bit enable "Enable pulse generation";
-pin in float velocity "Velocity of the laser head (e.g. motion.current-vel)";
-pin in float pulse-per-unit "How many pulses per unit distance";
+pin in bit enable = 0 "Enable pulse generation";
+pin in float velocity = 0 "Velocity of the laser head (e.g. motion.current-vel)";
+pin in float pulse-per-unit = 0 "How many pulses per unit distance";
pin out bit pulse "Output firing pulse";
pin out bit continuous "True if current inputs are causing continuous (not pulsed) output";
-param rw float duration "Duration of each pulse in seconds";
-function make_pulses fp "Add to fast thread to make laser pulses";
+param rw s32 duration "Duration of each pulse in ns";
+
+function make_pulses nofp "Add to fast thread to make laser pulses";
function update fp "Add to update thread to compute pulse parameters";
+
variable int updated;
-variable float interval;
-variable float accum;
-variable float pulse_remain;
+param r s32 interval;
+param r s32 accum;
+param r s32 pulse_remain;
license "GPL";
;;
+
FUNCTION(update) {
+
if (velocity == 0.0 || pulse_per_unit == 0) {
- updated = 0;
+ updated = 0;
} else {
- // velocity * pulse-per-unit gives pulses per second
- // pulse interval is then 1/pps
- interval = 1.0 / (velocity * pulse_per_unit);
-
- updated = 1;
+ if ( pulse_per_unit < 0.0 ) {
+ // duty cycle is just a function of % and duration
+ interval = duration * -100.0 / pulse_per_unit;
+ } else {
+ // velocity * pulse-per-unit gives pulses per second
+ // pulse interval is then 1/pps
+ interval = 1000000000.0 / (velocity * pulse_per_unit);
+ }
+ updated = 1;
}
}
FUNCTION(make_pulses) {
if (!enable || !updated) {
- accum = 0;
- pulse_remain = 0;
- pulse = 0;
- continuous = 0;
- return;
+ accum = 0;
+ pulse_remain = 0;
+ pulse = 0;
+ continuous = 0;
+ return;
}
- accum += fperiod;
+ accum += period;
if (accum > interval) {
- accum -= interval;
- if (accum > interval)
- accum = 0.0;
- continuous = (pulse_remain > 0);
- pulse_remain = duration;
+ accum -= interval;
+ if (accum > interval)
+ accum = 0;
+ continuous = (pulse_remain > 0);
+ pulse_remain = duration;
}
if (pulse_remain > 0) {
- pulse = 1;
- pulse_remain -= fperiod;
+ pulse = 1;
+ pulse_remain -= period;
} else {
- pulse = 0;
+ pulse = 0;
}
}
diff --git a/laserraster.comp b/laserraster.comp
new file mode 100644
index 0000000..6d28aad
--- /dev/null
+++ b/laserraster.comp
@@ -0,0 +1,252 @@
+component laserraster "Laser raster engrave engine";
+author "Jeremy Van Grinsven ";
+option extra_setup yes;
+
+pin in bit enable = 0 "Enable component";
+pin in float data_index = 0 "Input raster data index";
+pin in float data_1 = 0 "Input raster data 1";
+pin in bit stepgen-dir = 0 "Direction pin from raster axis stepgen";
+pin in bit stepgen-step = 0 "Step pin from raster axis stepgen";
+pin out bit raster-active = 0 "Raster data loaded and laser can fire";
+pin out bit laser-on = 0 "Laser on signal";
+
+function update fp "Read the raster data";
+function make_pulses nofp "Generate laser pulses";
+
+param rw s32 laser_on_delay = 0 "Time in ns between triggering and the laser will fire";
+
+param r float raster-speed = 0 "Speed of raster, machine units/min";
+param r s32 raster-direction = 1 "Sweep direction, 1=neg-to-pos, -1=pos-to-neg";
+param r float dots-per-unit = 0 "Dots per machine unit";
+param r s32 bits-per-float = 0 "Bits of bitmap data per data-XX";
+param r s32 laser-on-time = 0 "Laser pulse time per dot, ns";
+param r float raster-lead-in = 0 "Lead in distance, machine units";
+param r s32 rawcounts = 0 "Calculated step count from stepgen step/dir";
+
+license "GPL";
+;;
+
+#include
+#include
+#include "strtod.h"
+
+
+static char* linear_units;
+RTAPI_MP_STRING(linear_units,"Specify the machine units of the raster axis");
+
+static char* axis_scale;
+RTAPI_MP_STRING(axis_scale,"Raster axis scale");
+
+static char* axis_min_limit;
+RTAPI_MP_STRING(axis_min_limit,"Raster axis min limit");
+
+static char* axis_max_limit;
+RTAPI_MP_STRING(axis_max_limit,"Raster axis max limit");
+
+
+// calculated
+static hal_s32_t m_delaySteps = 0;
+static hal_s32_t m_startStep = 0;
+static hal_s32_t m_endStep = 0;
+static int m_gcodeIsMetric = 0;
+static int m_machineIsMetric = 0;
+static hal_float_t m_convertScale = 1;
+static hal_float_t m_axisScale = 0;
+static hal_float_t m_axisLength = 0;
+
+// step bitmask storage
+static int m_stepMaskLen = 0;
+typedef hal_u32_t step_mask_type;
+const int m_stepMaskBits = (sizeof(step_mask_type)*8);
+static step_mask_type* m_stepMask = 0;
+
+FUNCTION(update) {
+ int i;
+ static hal_s32_t s_lastIndex = 0;
+
+ hal_s32_t data_index_s32 = data_index;
+ long long int data_1_s64 = data_1;
+
+ if ( !enable ) return;
+
+ if ( data_index_s32 < 0 ) {
+
+ if ( data_index_s32 != s_lastIndex ) {
+ if ( data_index_s32 == -1 ) {
+ // clear the step data array
+ for (i=0;i 0 ? 1 : -1);
+ break;
+ case -5:
+ dots_per_unit = data_1;
+ break;
+ case -6:
+ bits_per_float = data_1_s64;
+ break;
+ case -7:
+ laser_on_time = data_1_s64;
+ break;
+ case -8:
+ raster_lead_in = data_1;
+ do_init = 1;
+ break;
+ }
+ if ( do_init ) {
+ // params are done, lets init
+ if ( !m_machineIsMetric && m_gcodeIsMetric ) {
+ m_convertScale = 1/25.4;
+ } else if ( m_machineIsMetric && !m_gcodeIsMetric ) {
+ m_convertScale = 25.4;
+ } else {
+ m_convertScale = 1;
+ }
+
+ // convert passed in values to machine units
+ raster_speed *= m_convertScale;
+ dots_per_unit *= m_convertScale;
+ raster_lead_in *= m_convertScale;
+
+ m_startStep = rawcounts + raster_lead_in * m_axisScale * raster_direction;
+ m_endStep = m_startStep;
+ m_delaySteps = laser_on_delay * raster_speed / 60 / 1000 / 1000 * m_axisScale;
+ raster_active = 1;
+
+ rtapi_print("speed=%f %s/min, dots/%s=%f, raster_lead_in=%f %s, axisScale=%f step/%s, axisLength=%f %s\n",
+ raster_speed,linear_units, linear_units,dots_per_unit, raster_lead_in,linear_units, m_axisScale,linear_units, m_axisLength,linear_units);
+ rtapi_print("rawcounts=%i, startStep=%i, delaySteps=%i, machMetric=%i, gcodeMetric=%i, convertScale=%f bpf=%d\n",
+ rawcounts, m_startStep, m_delaySteps, m_machineIsMetric, m_gcodeIsMetric, m_convertScale, bits_per_float);
+ }
+ }
+ s_lastIndex = data_index_s32;
+ }
+ } else if ( data_index_s32 > 0 ) {
+ if ( data_index_s32 != s_lastIndex ) {
+ int i;
+ hal_u32_t src_bit_index_start;
+
+ rtapi_print("index=%d data=%f data_1_s64=0x%x%08x\n",
+ data_index_s32, data_1, (unsigned int)(data_1_s64>>32), (unsigned int)data_1_s64);
+
+ src_bit_index_start = (data_index_s32-1)*bits_per_float;
+
+ for (i=0;i=0 && index_i < m_stepMaskLen ) {
+ m_stepMask[index_i] |= (1< 0 ) {
+ pulse_remain -= period;
+ } else {
+ laser_on = 0;
+ }
+ if ( raster_active ) {
+ hal_s32_t rawcounts_with_delay = rawcounts - m_delaySteps * raster_direction;
+
+ if ( (raster_direction > 0 && rawcounts < last_rawcounts) ||
+ (raster_direction < 0 && rawcounts > last_rawcounts) )
+ {
+ // we moved in the wrong direction
+ rtapi_print("raster off, moved in wrong direction\n");
+ raster_active = 0;
+ }
+ else if ( (raster_direction > 0 && rawcounts_with_delay > m_endStep) ||
+ (raster_direction < 0 && rawcounts_with_delay < m_endStep) )
+ {
+ // after end of raster data
+ rtapi_print("raster off, past raster data\n");
+ raster_active = 0;
+ }
+ else if ( rawcounts != last_rawcounts &&
+ ( (raster_direction > 0 && rawcounts_with_delay >= m_startStep) ||
+ (raster_direction < 0 && rawcounts_with_delay <= m_startStep) ) ) {
+ hal_s32_t bit_index = (rawcounts_with_delay - m_startStep)*raster_direction;
+ int index_i = bit_index / m_stepMaskBits;
+ int index_b = bit_index % m_stepMaskBits;
+
+ //rtapi_print("raster rawcounts_with_delay=%d index=%d i=%d b=%d\n",rawcounts_with_delay,bit_index,index_i,index_b);
+
+ if ( index_i >=0 && index_i < m_stepMaskLen ) {
+ if ( m_stepMask[index_i] & (1< 1 and os.path.exists(sys.argv[1]) ):
+ image_name = sys.argv[1]
+else:
+ image_name = image_not_found()
+
+print('%')
+print '(image = %s)' % image_name
+
+image = Image.open(image_name)
+
+(img_w,img_h) = image.size
+print('(original size w=%u,h=%u)' % (img_w,img_h))
+
+# user defined parameters
+SPEED = 600
+ACCEL = 270
+laser_power = 0.15
+laser_on_time = 0.5
+is_metric = 0
+origin_x = 0
+origin_y = 0
+# center,
+origin_loc = 'bottomleft'
+# for mirroring
+orientation_y = -1
+orientation_x = 1
+raster_w = 5
+raster_h = raster_w*float(img_h)/img_w
+XDPI = 200
+YDPI = 200
+
+# calc lead in + 100% fudge
+leadIn = (1.0*SPEED*SPEED/3600)/ACCEL
+#leadIn = 1
+
+# calc image raster size
+pix_w = int(raster_w * XDPI)
+pix_h = int(raster_h * YDPI)
+W = float(pix_w) / XDPI
+H = float(pix_h) / YDPI
+MAX_BPF = 53
+
+# handle origin offsetting
+if ( origin_loc == 'center' ):
+ X = origin_x - W/2.0
+ Y = origin_y + H/2.0
+else:
+ if ( 'top' in origin_loc ):
+ Y = origin_y
+ elif ( 'bottom' in origin_loc ):
+ Y = origin_y + H
+ elif ( 'middle' in origin_loc ):
+ Y = origin_y + H/2.0
+ else:
+ print('unknown origin_loc='+origin_loc)
+ sys.exit()
+
+ if ( 'left' in origin_loc ):
+ X = origin_x
+ elif ( 'center' in origin_loc ):
+ X = origin_x - W/2.0
+ elif ( 'right' in origin_loc ):
+ X = origin_x - W
+ else:
+ print('unknown origin_loc='+origin_loc)
+ sys.exit()
+
+print '(rescaling to %u,%u w=%u,h=%u)' % (pix_w, pix_h, W, H)
+image = image.resize((pix_w, pix_h), Image.BICUBIC).convert('1')
+image.save('actual.png')
+
+pix = list(image.getdata())
+
+# gcode header
+if ( is_metric ):
+ print('G21')
+else:
+ print('G20')
+print('M63 P0 (turn off laser dout)')
+print('G0 Z0 (turn off magic z)')
+print('G64 P0.0001 Q0.0001 (minimal path blending)')
+print('M68 E0 Q%0.3f (set laser power level)' % laser_power)
+print('M3 S1 (master laser power on)')
+print('# = %0.3f' % SPEED)
+
+# gcode skip lines that show raster image run box
+print('/ F[#]')
+print('/ G0 X%0.3f Y%0.3f' % (X,Y))
+print('/ G1 X%0.3f Y%0.3f' % (X+W*orientation_x,Y))
+print('/ G1 X%0.3f Y%0.3f' % (X+W*orientation_x,Y+H*orientation_y))
+print('/ G1 X%0.3f Y%0.3f' % (X,Y+H*orientation_y))
+print('/ G1 X%0.3f Y%0.3f' % (X,Y))
+print('/ M2')
+
+forward = 1
+
+for y in xrange(0,pix_h):
+ offset_y = Y + float(y)/YDPI*orientation_y
+
+ print('(setup raster line %d)' % y)
+
+ row = pix[y * pix_w:(y + 1) * pix_w]
+
+ if not forward:
+ row.reverse()
+
+ first_non_zero = -1
+ last_non_zero = -1
+ for index, pixel in enumerate(row):
+ if (pixel <= 127):
+ if (first_non_zero == -1):
+ first_non_zero = index
+ last_non_zero = index
+
+ # debug raster
+ #first_non_zero, last_non_zero = (0,len(row)-1)
+
+ # some data to output
+ if (first_non_zero >= 0):
+ # figure out how many max bpf floats to hold the data and
+ # then evenly distribute the bits
+ total_bits = last_non_zero - first_non_zero + 1;
+ BPF = ceil(total_bits / (ceil(float(total_bits) / MAX_BPF)))
+
+ bits = []
+ i=0
+ bitval=0
+ for v in row[first_non_zero:last_non_zero+1]:
+ if (v <= 127):
+ bitval += (1<= BPF):
+ bits.append(bitval);
+ bitval = 0
+ i = 0
+ if (i > 0):
+ bits.append(bitval);
+
+ if forward:
+ offset_start = X + (float(first_non_zero)/XDPI - leadIn)*orientation_x
+ offset_end = X + (float(last_non_zero+1)/XDPI + leadIn)*orientation_x
+ else:
+ offset_start = X + (W - float(first_non_zero)/XDPI + leadIn)*orientation_x
+ offset_end = X + (W - float(last_non_zero+1)/XDPI - leadIn)*orientation_x
+
+ print('G0 X%0.3f Y%0.3f' % (offset_start,offset_y))
+ print('F[#]')
+ print('M68 E1 Q-1 (start new line)')
+ print('M68 E2 Q%d (gcode is metric 0=no,1=yes)' % is_metric)
+ print('M68 E1 Q-2')
+ print('M68 E2 Q[#] (speed, in/min or mm/min)')
+ print('M68 E1 Q-3')
+ print('M68 E2 Q%d (direction)' % (1 if forward else -1))
+ print('M68 E1 Q-4')
+ print('M68 E2 Q%0.3f (dpi)' % XDPI)
+ print('M68 E1 Q-5')
+ print('M68 E2 Q%u (bits per float)' % BPF)
+ print('M68 E1 Q-6')
+ print('M68 E2 Q%d (laser on time, ns)' % (laser_on_time*1000000))
+ print('M68 E1 Q-7')
+ print('M68 E2 Q%0.3f (lead in)' % leadIn)
+ print('M68 E1 Q-8')
+ print('(raster data start)')
+
+ for index, bitval in enumerate(bits):
+ if forward:
+ offset_i = X + float(first_non_zero + index*BPF)/XDPI*orientation_x
+ else:
+ offset_i = X + (W - float(first_non_zero + index*BPF)/XDPI)*orientation_x
+ print('M68 E2 Q%u' % (bitval))
+ print('M68 E1 Q%u' % (index+1))
+ #print('G1 X%0.3f' % offset_i)
+
+ print('G1 X%0.3f' % offset_end)
+ print('M1')
+
+ # next line is reverse direction
+ forward = not forward
+
+
+print('%')
diff --git a/raster_gui.py b/raster_gui.py
index cae24b1..b66f40b 100644
--- a/raster_gui.py
+++ b/raster_gui.py
@@ -2,17 +2,15 @@
def app():
import Tkinter
- app = Tkinter.Tk(className='emcM144')
+ app = Tkinter.Tk(className='emcRasterEngrave')
app.withdraw()
return app
-def image_not_found(P):
+def image_not_found():
import tkFileDialog, tkMessageBox
app()
- tkMessageBox.showwarning(title='M144 Raster Image Not Found',
- message='Could not find image *-%u.*\nafter searching [RASTER]IMAGE_PATH (default $HOME)' % P)
- name = tkFileDialog.askopenfilename(title='M144 Raster Image',
- initialfile='%u' % P,
+ name = tkFileDialog.askopenfilename(title='Raster Image',
+ initialfile='',
filetypes=[('Images',('*.png', '*.gif', '*.jpg', '*.tif', '*.bmp')),
('Any File', '*.*')])
if not name:
@@ -23,6 +21,6 @@ def image_not_found(P):
def fatal(msg):
import tkMessageBox
app()
- tkMessageBox.showerror(title='M144 Raster Error', message=msg)
+ tkMessageBox.showerror(title='Raster Engrave Error', message=msg)
sys.exit(2)
diff --git a/raster_utils.py b/raster_utils.py
deleted file mode 100644
index e453532..0000000
--- a/raster_utils.py
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/env python
-
-import os, re
-import emc
-
-emc_ini = None
-
-def open_raster_fifo(mode):
- global emc_ini
- if emc_ini is None:
- emc_ini = emc.ini('2x_Laser.ini')
- pipefile = emc_ini.find('RASTER', 'PIPE_FILE')
-
- if pipefile is None:
- pipefile = '/tmp/emc2_raster_fifo';
-
- try:
- return open(pipefile, mode)
- except IOError:
- os.mkfifo(pipefile)
- return open(pipefile, mode)
-
-def send_params(p, q):
- fp = open_raster_fifo('w')
- fp.write('%g %g' % (p, q))
- fp.close()
-
-def recv_params():
- fp = open_raster_fifo('r')
- params = fp.readline().strip()
- fp.close()
- return map(lambda x: float(x), params.split())
-
-
-def get_comment(file, lineno):
- fp = open(file, 'r')
- lineno = lineno - 1
- for i, line in enumerate(fp):
- if lineno == i:
- break
- m = re.search('\(([^)]+)\)', line)
- if m is not None:
- return m.group(1).strip()
- return ''
diff --git a/strtod.h b/strtod.h
new file mode 100644
index 0000000..c530998
--- /dev/null
+++ b/strtod.h
@@ -0,0 +1,150 @@
+
+#define HUGE_VAL (__builtin_huge_val())
+
+//
+// strtod.c
+//
+// Convert string to double
+//
+// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// 3. Neither the name of the project nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+//
+
+static int isspace(char c){ return c==' '; }
+static int isdigit(char c){ return (c>='0'&&c<='9'); }
+
+static double strtod(const char *str, char **endptr)
+{
+ double number;
+ int exponent;
+ int negative;
+ char *p = (char *) str;
+ double p10;
+ int n;
+ int num_digits;
+ int num_decimals;
+
+ // Skip leading whitespace
+ while (isspace(*p)) p++;
+
+ // Handle optional sign
+ negative = 0;
+ switch (*p)
+ {
+ case '-': negative = 1; // Fall through to increment position
+ case '+': p++;
+ }
+
+ number = 0.;
+ exponent = 0;
+ num_digits = 0;
+ num_decimals = 0;
+
+ // Process string of digits
+ while (isdigit(*p))
+ {
+ number = number * 10. + (*p - '0');
+ p++;
+ num_digits++;
+ }
+
+ // Process decimal part
+ if (*p == '.')
+ {
+ p++;
+
+ while (isdigit(*p))
+ {
+ number = number * 10. + (*p - '0');
+ p++;
+ num_digits++;
+ num_decimals++;
+ }
+
+ exponent -= num_decimals;
+ }
+
+ if (num_digits == 0)
+ {
+ return 0.0;
+ }
+
+ // Correct for sign
+ if (negative) number = -number;
+
+ // Process an exponent string
+ if (*p == 'e' || *p == 'E')
+ {
+ // Handle optional sign
+ negative = 0;
+ switch (*++p)
+ {
+ case '-': negative = 1; // Fall through to increment pos
+ case '+': p++;
+ }
+
+ // Process string of digits
+ n = 0;
+ while (isdigit(*p))
+ {
+ n = n * 10 + (*p - '0');
+ p++;
+ }
+
+ if (negative)
+ exponent -= n;
+ else
+ exponent += n;
+ }
+
+ if (exponent < __DBL_MIN_EXP__ || exponent > __DBL_MAX_EXP__)
+ {
+ return HUGE_VAL;
+ }
+
+ // Scale the result
+ p10 = 10.;
+ n = exponent;
+ if (n < 0) n = -n;
+ while (n)
+ {
+ if (n & 1)
+ {
+ if (exponent < 0)
+ number /= p10;
+ else
+ number *= p10;
+ }
+ n >>= 1;
+ p10 *= p10;
+ }
+
+ if (endptr) *endptr = p;
+
+ return number;
+}
+