Permalink
Comparing changes
Open a pull request
2
contributors
Commits on Apr 18, 2016
Commits on May 01, 2016
Commits on May 05, 2016
Commits on May 07, 2016
Commits on Jun 26, 2016
Commits on Jul 13, 2016
Unified
Split
Showing
with
484 additions
and 1 deletion.
- +10 −1 js/main.js
- +2 −0 modules/g1.gcode
- +71 −0 modules/printengine.cfg
- +354 −0 modules/printengine.pl
- +47 −0 modules/send.pl
@@ -454,7 +454,16 @@ function startSlicing() { | ||
if (settings.get('slicer.zip')) { | ||
zipFile = new JSZip(); | ||
zipFolder = zipFile.folder('slices'); | ||
zipFile.file("README.txt", 'Generated by SLAcer.js\r\nhttp://lautr3k.github.io/SLAcer.js/\r\n'); | ||
ontime = parseInt(times.on); | ||
offtime = parseInt(times.off); | ||
layerheight=settings.get('slicer.layers.height'); | ||
zipdelimiter='\"' | ||
ziptext1= '#Generated by SLAcer.js\r\n#http://lautr3k.github.io/SLAcer.js/\r\nlayer_height='+zipdelimiter+layerheight; | ||
ziptext2='\r\nexposure_time='+zipdelimiter+ontime; | ||
ziptext3= '\r\nresin_settling_time='+zipdelimiter+offtime; | ||
ziptext= ziptext1+zipdelimiter+ziptext2+zipdelimiter+ziptext3+zipdelimiter; | ||
|
||
zipFile.file("README.txt",ziptext); | ||
} | ||
|
||
slicesNumber && slice(); | ||
@@ -0,0 +1,2 @@ | ||
G28 Z | ||
G1 Z30 F30 |
@@ -0,0 +1,71 @@ | ||
#Configuration file for printengine.pl to be called by SLAcer.js and other printer services | ||
#Copyright 2016 Robert Koeppl, Fablab Leoben robert.koeppl@fablab-leoben.at | ||
#http://www.fablab-leoben.at | ||
#http://www.github.com/fablab-leoben | ||
#released under the GPL v2 | ||
#this piece of software is provided with absolutely no warranty | ||
#use at your own risk | ||
log_file = "../log/printengine.log" | ||
temporary_folder="./temp" | ||
logging_enabled="TRUE" | ||
#controllerboard="BBB" | ||
controllerboard="raspiarduinoramps" | ||
arduinotty="/dev/ttyACM0" | ||
arduinottybaudrate="115200" | ||
steps_per_mm="100" | ||
#steps needed to move the Z-Axis 1mm | ||
projector_type="Lightcrafter4500" | ||
#default Light engine | ||
projector_usb_device="/dev/ttyUSB0" | ||
endstop_Z_max="TRUE" | ||
endstop_Z_max_type="NC" | ||
#NO="Normally Open", NC="Normally Closed". NC is preferred | ||
endstop_Z_min="TRUE" | ||
endstop_Z_min_type="NC" | ||
#NO="Normally Open", NC="Normally Closed". NC is preferred | ||
wiper="FALSE" | ||
door_contact="TRUE" | ||
#Defines if there is a door contact to determine if the enclosure is closed | ||
X_pixels="912" | ||
Y_pixels="1140" | ||
Z_speed="5" | ||
#mm/s | ||
Z_max_speed="10" | ||
#mm/s | ||
Z_Autocal="True" | ||
#Automatically determine Z-travel by triggering both endstops and dividing the Distance | ||
overshoot="2" | ||
#overshoot in mm to make sure the whole area is covered in resin correctly | ||
testrun_capable="true" | ||
#Capability to run testruns with wavelengthe that do not trigger polymerisation | ||
testrun_color="RED" | ||
#Color channel used for test run | ||
prodrun_color="BLUE" | ||
#Color Channel used for production run | ||
vat_heatable="false" | ||
vat_target_temperature="" | ||
#Vat temperature in Celsius | ||
check_vat_presence="false" | ||
#board specific configuration for Beaglebone Black | ||
virtual_terminal="1" | ||
display_software="builtin" | ||
#default is the builtin functionality support for other methods not implemented yet. fbi as other option planned next | ||
display_device="/dev/fb0" | ||
#PIN asignment | ||
pin_zmin="testpin" | ||
pin_zmax="testpin" | ||
pin_wiper_min="testpin" | ||
pin_wiper_max="testpin" | ||
pin_door="testpin" | ||
pin_step_Z="testpin" | ||
pin_enable_Z="testpin" | ||
pin_direction_Z="testpin" | ||
pin_trigger_pre="testpin" | ||
pin_trigger_post="testpin" | ||
pin_enable_wiper="testpin" | ||
pin_dir_wiper="testpin" | ||
pin_step_wiper="testpin" | ||
pin_vat_heater="testpin" | ||
pin_vat_temperature="testpin" | ||
pin_vat_presence="testpin" | ||
#end of BBB specifics |
@@ -0,0 +1,354 @@ | ||
#!/usr/bin/perl | ||
#print engine to be called by SLAcer.js and other printer services orstandalone from command line | ||
#Copyright 2016 Robert Koeppl, Fablab Leoben robert.koeppl@fablab-leoben.at | ||
#http://www.fablab-leoben.at | ||
#http://www.github.com/fablab-leoben | ||
#released under the GPL v2 | ||
#this piece of software is provided with absolutely no warranty | ||
#use at your own risk | ||
#configuration is stored in printengine.cfg, do not use hardcoded configuration in ths perl script, that is bad practice. | ||
#definition of libraries and modules to include | ||
use warnings; | ||
use strict; | ||
use Getopt::Std; | ||
use Getopt::Long; | ||
use feature qw(say); | ||
use Config::Simple; | ||
use File::Which; | ||
use Sys::Mmap; | ||
use Graphics::Framebuffer; | ||
use Time::HiRes; | ||
use Archive::Zip; | ||
use IO::File ; | ||
use File::Spec::Functions qw(splitpath); | ||
use File::Path qw(mkpath); | ||
use File::Path 'rmtree'; | ||
use Device::SerialPort; | ||
use Slurp; | ||
#import configuration from configuration file | ||
our $cfg = new Config::Simple(); | ||
$cfg->read("printengine.cfg"); | ||
|
||
#asign config values from config file to values in script | ||
my $log_file = $cfg->param("log_file"); | ||
my $temporary_folder=$cfg->param("temporary_folder"); | ||
my $logging_enabled=$cfg->param("logging_enabled"); | ||
my $controllerboard=$cfg->param("controllerboard"); | ||
my $steps_per_mm=$cfg->param("steps_per_mm"); | ||
my $projector_type=$cfg->param("projector_type"); | ||
my $projector_usb_device=$cfg->param("projector_usb_device"); | ||
my $endstop_Z_max=$cfg->param("endstop_Z_max"); | ||
my $endstop_Z_max_type=$cfg->param("endstop_Z_max_type"); | ||
my $endstop_Z_min=$cfg->param("endstop_Z_min"); | ||
my $endstop_Z_min_type=$cfg->param("endstop_Z_min_type"); | ||
my $wiper=$cfg->param("wiper"); | ||
my $door_contact=$cfg->param("door_contact"); | ||
my $X_pixels=$cfg->param("X_pixels"); | ||
my $Y_pixels=$cfg->param("Y_pixels"); | ||
my $Z_speed=$cfg->param("Z_speed"); | ||
my $Z_max_speed=$cfg->param("Z_max_speed"); | ||
my $Z_Autocal=$cfg->param("Z_Autocal"); | ||
my $overshoot=$cfg->param("overshoot"); | ||
my $testrun_capable=$cfg->param("testrun_capable"); | ||
my $testrun_color=$cfg->param("testrun_color"); | ||
my $prodrun_color=$cfg->param("prodrun_color"); | ||
my $vat_heatable=$cfg->param("vat_heatable"); | ||
my $vat_target_temperature=$cfg->param("vat_target_temperature"); | ||
my $check_vat_presence=$cfg->param("check_vat_presence"); | ||
my $virtual_terminal=$cfg->param("virtual_terminal"); | ||
my $display_software=$cfg->param("display_software"); | ||
my $display_device=$cfg->param("display_device"); | ||
my $pin_zmin=$cfg->param("pin_zmin"); | ||
my $pin_zmax=$cfg->param("pin_zmax"); | ||
my $pin_door=$cfg->param("pin_door"); | ||
my $pin_step_Z=$cfg->param("pin_step_Z"); | ||
my $pin_enable_Z=$cfg->param("pin_enable_Z"); | ||
my $pin_direction_Z=$cfg->param("pin_direction_Z"); | ||
my $pin_trigger_pre=$cfg->param("pin_trigger_pre"); | ||
my $pin_trigger_post=$cfg->param("pin_trigger_post"); | ||
my $pin_enable_wiper=$cfg->param("pin_enable_wiper"); | ||
my $pin_dir_wiper=$cfg->param("pin_dir_wiper"); | ||
my $pin_step_wiper=$cfg->param("pin_step_wiper"); | ||
my $pin_wiper_max=$cfg->param("pin_wiper_max"); | ||
my $pin_wiper_min=$cfg->param("pin_wiper_min"); | ||
my $pin_vat_heater=$cfg->param("pin_vat_heater"); | ||
my $pin_vat_temperature=$cfg->param("pin_vat_temperature"); | ||
my $pin_vat_presence=$cfg->param("pin_vat_presence"); | ||
my $arduinotty=$cfg->param("arduinotty"); | ||
my $arduinottybaudrate=$cfg->param("arduinottybaudrate"); | ||
#asign additional variables | ||
my $picturesarchive; | ||
my $archivesource; | ||
GetOptions("picturesarchive=s"=>\$picturesarchive, "archivesource=s"=>\$archivesource);#read pictures archive from command line option | ||
my $layer_height; | ||
my $exposure_time; | ||
my $resin_settling_time; | ||
my $cfg2; | ||
# | ||
# | ||
#activate logging to logfile | ||
if ($logging_enabled eq "TRUE") | ||
{ | ||
unless (defined $log_file and length $log_file){ | ||
die "logging enabled but no logfile defined\n";} | ||
open my $log_fh, ">", $log_file; | ||
} | ||
#basic sanity checks to determine whether or not the configuration is complete and makes at least some sense | ||
#check if the parameters and pins needed for the features enabled are configured | ||
unless (defined $steps_per_mm and length $steps_per_mm){ | ||
die "No steps per mm for Z Axis defined\n";} | ||
unless (defined $overshoot and length $overshoot){ | ||
die "No overshoot for Z Axis defined\n";} | ||
unless (defined $temporary_folder and length $temporary_folder){ | ||
die "No temporary folder defined\n";} | ||
unless (defined $controllerboard and length $controllerboard){ | ||
die "No controllerboard defined\n";} | ||
unless (defined $projector_type and length $projector_type){ | ||
die "No projector type defined\n";} | ||
unless (defined $display_device and length $display_device){ | ||
die "No display device defined\n";} | ||
unless (defined $X_pixels and length $X_pixels){ | ||
die "No pixels in X direction defined\n";} | ||
unless (defined $Y_pixels and length $Y_pixels){ | ||
die "No pixels in Y direction defined\n";} | ||
unless (defined $Z_speed and length $Z_speed){ | ||
die "No Z speed defined\n";} | ||
unless (defined $Z_max_speed and length $Z_max_speed){ | ||
die "No Z max speed defined\n";} | ||
unless (defined $prodrun_color and length $prodrun_color){ | ||
die "No production run color defined\n";} | ||
if ($check_vat_presence eq "TRUE") | ||
{ | ||
unless (defined $pin_vat_presence and length $pin_vat_presence ) | ||
{ | ||
die "Vat presence pin definition missing\n";} | ||
} | ||
if ($door_contact eq "TRUE") | ||
{ | ||
unless (defined $pin_door and length $pin_door ) | ||
{ | ||
die "door pin not defined\n";} | ||
} | ||
if ($testrun_capable eq "TRUE") | ||
{ | ||
unless (defined $testrun_color and length $testrun_color ) | ||
{ | ||
die "Z max endstops definition incomplete\n";} | ||
} | ||
if ($endstop_Z_max eq "TRUE") | ||
{ | ||
unless (defined $endstop_Z_max_type and length $endstop_Z_max_type and | ||
defined $pin_zmax and length $pin_zmax ) | ||
{ | ||
die "Z max endstops definition incomplete\n";} | ||
} | ||
if ($endstop_Z_min eq "TRUE") | ||
{ | ||
unless (defined $endstop_Z_min_type and length $endstop_Z_min_type and | ||
defined $pin_zmin and length $pin_zmin ) | ||
{ | ||
die "Z min endstops definition incomplete\n";} | ||
} | ||
if ($Z_Autocal eq "TRUE") | ||
{ | ||
unless ($endstop_Z_max eq "TRUE" and $endstop_Z_min eq "TRUE" and | ||
defined $endstop_Z_max_type and length $endstop_Z_max_type and | ||
defined $pin_zmax and length $pin_zmax and | ||
defined $endstop_Z_min_type and length $endstop_Z_min_type and | ||
defined $pin_zmin and length $pin_zmin) | ||
{ | ||
die "Z endstops definition incomplete for autotune of Z axis Length\n";} | ||
} | ||
if ($wiper eq "TRUE") | ||
{ | ||
unless (defined $pin_enable_wiper and length $pin_enable_wiper and | ||
defined $pin_step_wiper and length $pin_step_wiper and | ||
defined $pin_dir_wiper and length $pin_dir_wiper and | ||
defined $pin_zmin and length $pin_zmin and | ||
defined $pin_wiper_max and length $pin_wiper_max and | ||
defined $pin_wiper_min and length $pin_wiper_min | ||
) | ||
{ | ||
die "pin definition incomplete for wiper usage\n";} | ||
} | ||
if ($vat_heatable eq "TRUE") | ||
{ | ||
unless (defined $vat_target_temperature and length $vat_target_temperature and | ||
defined $pin_vat_heater and length $pin_vat_heater and | ||
defined $pin_vat_temperature and length $pin_vat_temperature) | ||
{ | ||
die "Vat temperature or heater parameters are configured incompletely\n";} | ||
} | ||
|
||
if ($controllerboard eq "BBB") #if the Board is set to be a Beagle Bone Black, we need to check if it actually is one | ||
{ | ||
say "Beagle Bone Black selected, checking board type! - check command still missing"; | ||
unless (defined $pin_trigger_post and length $pin_trigger_post and defined $pin_trigger_pre and length $pin_trigger_pre){ | ||
die "trigger pin definition incomplete\n";} | ||
} | ||
elsif ($controllerboard eq "raspiarduinoramps"){ | ||
say "Raspberry PI with Arduino selected, checking board type! - check command still missing"; | ||
unless (defined $arduinotty and length $arduinotty and defined $arduinottybaudrate and length $arduinottybaudrate){ | ||
die "trigger pin definition incomplete\n";} | ||
} | ||
else { | ||
say "unknown controller type $controllerboard , please review your configuration, get in touch with developers or fork the code on Github and contribute the code to use the new printer" | ||
; | ||
die "unknown board in configuration!\n"; | ||
} | ||
if ($projector_type eq "Lightcrafter4500") | ||
{ | ||
unless (defined $projector_usb_device and length $projector_usb_device){ | ||
die "No projector USB device defined\n";} | ||
} | ||
else { | ||
say "unknown projector type $projector_type , please review your configuration, get in touch with developers or fork the code on Github and contribute the code to use the new printer" | ||
; | ||
die "unknown projector in configuration!\n"; | ||
} | ||
|
||
if ($display_software eq "fbi") #ckeck for configured Software to send Pictures to the projector | ||
{ | ||
my $display_software_path= which "fbi"; | ||
unless (defined $display_software_path and length $display_software_path){ | ||
die "configured display software not found\n";} | ||
unless (defined $display_device and length $display_device){ | ||
die "display devicde not configured\n";} | ||
unless (defined $virtual_terminal and length $virtual_terminal){ | ||
die "virtual terminal not configured\n";} | ||
} | ||
elsif ($display_software eq "builtin") | ||
{ | ||
unless (defined $display_device and length $display_device){ | ||
die "display device not configured\n";} | ||
} | ||
|
||
else { #if the configured Display software matches none of the supported packages, die | ||
say "unknown display software $display_software , please review your configuration, get in touch with developers or fork the code on Github and contribute the code to use the new printer" | ||
; | ||
die "unknown display software in configuration!\n"; | ||
} | ||
#uncompresse archive to temporary folder | ||
unless (defined $temporary_folder and length $temporary_folder){ | ||
die "No temporary folder defined\n";} | ||
rmtree([ "$temporary_folder"]) or die "$!: for directory $temporary_folder\n"; | ||
unless (-d $temporary_folder) { | ||
mkpath($temporary_folder) or die "Couldn't mkdir $temporary_folder: $!";} | ||
my $port = Device::SerialPort->new($arduinotty); | ||
|
||
# 19200, 81N on the USB ftdi driver | ||
$port->baudrate($arduinottybaudrate); | ||
$port->databits(8); | ||
$port->parity("none"); | ||
$port->stopbits(1); | ||
|
||
my $zip = Archive::Zip->new($picturesarchive); | ||
$zip->extractTree('',$temporary_folder); | ||
|
||
|
||
|
||
#read filelist from testfolder | ||
my $dir = $temporary_folder; | ||
if ($archivesource eq "slacer") { | ||
$dir = "$temporary_folder/slices"; | ||
$cfg2 = new Config::Simple(); | ||
$cfg2->read("$temporary_folder/README.txt"); | ||
$layer_height=$cfg2->param("layer_height"); | ||
$exposure_time=$cfg2->param("exposure_time"); | ||
$resin_settling_time=$cfg2->param("resin_settling_time");} | ||
else { | ||
$dir = $temporary_folder; | ||
} | ||
opendir(DIR, $dir) or die $!; | ||
my @pics | ||
= grep { | ||
m/\.png$/ # png files only | ||
&& -f "$dir/$_" # and is a file | ||
} readdir(DIR); | ||
closedir(DIR); | ||
#sort array | ||
my @pics_sorted=sort { length $a <=> length $b||$a cmp $b } @pics; | ||
say "layer_height=$layer_height µm, exposure_time=$exposure_time ms,resin_settling_time=$resin_settling_time ms\n"; | ||
my $exposure_time_us=1000*$exposure_time;#conversion to microseconds | ||
my $resin_settling_time_us=1000*$resin_settling_time;#conversion to microseconds | ||
say "layer_height=$layer_height µm, exposure_time=$exposure_time_us µs,resin_settling_time=$resin_settling_time_us µs\n"; | ||
sleep 10; | ||
# Home Z-Axis | ||
my @command_list=('G21','G28 Z'); | ||
send_commands(@command_list); | ||
sleep 30; | ||
|
||
my $z=0; | ||
my $zdelta=$layer_height/1000; | ||
# | ||
#builtin framebuffer access | ||
|
||
my $fb = Graphics::Framebuffer->new( FB_DEVICE=>$display_device, SPLASH=>0 ); | ||
$fb->clear_screen('OFF'); | ||
foreach(@pics_sorted){ | ||
#turn on LED -LED is mapped to the Fan, Fan Pin in Firmware has been set to a PWM pin. | ||
my @command_list=('M106 S255'); | ||
send_commands(@command_list); | ||
$fb->blit_write( | ||
$fb->load_image( | ||
{ | ||
|
||
'width' => $X_pixels, # Optional. Resizes to this maximum | ||
# width. It fits the image to this | ||
# size. | ||
|
||
'height' => $Y_pixels, # Optional. Resizes to this maximum | ||
# height. It fits the image to this | ||
# size | ||
'scale_type' => 'max', | ||
'center' => $fb->{'CENTER_XY'}, | ||
'file' => "$dir/$_" # Usually needs full path | ||
} | ||
) | ||
); | ||
Time::HiRes::usleep("$exposure_time_us"); | ||
my @command_list=('M107'); | ||
send_commands(@command_list); | ||
$fb->clear_screen('OFF'); | ||
$z=$z+$zdelta; | ||
my $ztemp=$z+$overshoot; | ||
my @command_list=("G1 Z $ztemp F $Z_speed","G1 Z $z F $Z_speed"); | ||
send_commands(@command_list); | ||
my $zsleep=60*($zdelta+2*$overshoot)/$Z_speed*1000000; #microseconds, conversion from mm/min to mm/s | ||
Time::HiRes::usleep("$zsleep"); | ||
Time::HiRes::usleep("$resin_settling_time_us"); | ||
} | ||
#home Z axis to retrieve printed parts | ||
my @command_list=('G28 Z'); | ||
send_commands(@command_list); | ||
|
||
$fb->clear_screen('ON'); | ||
##sendcode- adapted and partially rewritten, inspiration taken from http://www.contraptor.org/about | ||
|
||
|
||
sub send_commands{ | ||
my @command_list = @_; | ||
|
||
#Open port | ||
|
||
|
||
while (1) { | ||
# Poll to see if any data is coming in | ||
if ( my $char = $port->lookfor() ) { | ||
$char =~ s/\r//; | ||
print "$char\n"; | ||
if( $char =~ m/^(ok|start)$/){ | ||
#Send next command | ||
my $next_command = shift @command_list; | ||
print "$next_command\n"; | ||
$port->write("$next_command\n"); | ||
}else{ | ||
print "unknown: $char\n"; | ||
} | ||
} | ||
sleep 0.01; | ||
unless(@command_list){last; } | ||
} | ||
} | ||
###end sendcode | ||
exit 0; |
@@ -0,0 +1,47 @@ | ||
#!/usr/bin/perl | ||
use Device::SerialPort; | ||
use Time::HiRes qw/sleep/; | ||
use Slurp; | ||
|
||
#Slup each file into a command list | ||
my @command_list; | ||
for my $file ( @ARGV ){ | ||
push @command_list, split('\n',slurp $file); | ||
} | ||
|
||
#If your board autoresets when talked to ( like a Sanguino ), you can uncomment the line bellow to get the machine to home position before sending the actual gcode | ||
#send_commands('G21','G91','G1 X-150 Y-150','G1 X-150 Y-150','G1 X-150 Y-150','G1 X-150 Y-150'); | ||
|
||
send_commands(@command_list); | ||
|
||
sub send_commands{ | ||
my @command_list = @_; | ||
|
||
#Open port | ||
my $port = Device::SerialPort->new("/dev/ttyACM0"); | ||
|
||
# 19200, 81N on the USB ftdi driver | ||
$port->baudrate(115200); | ||
$port->databits(8); | ||
$port->parity("none"); | ||
$port->stopbits(1); | ||
|
||
while (1) { | ||
# Poll to see if any data is coming in | ||
if ( my $char = $port->lookfor() ) { | ||
$char =~ s/\r//; | ||
print "$char\n"; | ||
if( $char =~ m/^(ok|start)$/){ | ||
#Send next command | ||
my $next_command = shift @command_list; | ||
print "$next_command\n"; | ||
$port->write("$next_command\n"); | ||
}else{ | ||
print "unknown: $char\n"; | ||
} | ||
} | ||
sleep 0.01; | ||
unless(@command_list){last; } | ||
} | ||
} | ||
|