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 @@ + + + + + +