Laser start
Hi,
trying to get my Laser running i had that finding:
when starting Laser it needs a little time to get the material burnt , this should be done with the first G1 move to get no gaps
in the lines we burn.
When we switch off Laser during G0 move we have the same situation.
So where is the best place to insert a short delay which is executed with the 1st G1 move?
i tried several positions , but i didn´t succeed with it.
when i add it in command loop for G0/ G1
it gets executed for a single manual move , but running from a file with several moves it doesn´t work...
void Commands::processGCode(GCode *com) {
uint32_t codenum; //throw away variable
volatile float g1_feedrate_backup; //backup variable for G1 Feedrate
bool Laseroff;
switch(com->G) {
case 0: // G0 -> G1
case 1: // G1
#if defined(SUPPORT_LASER) && SUPPORT_LASER
{
// disable laser for G0 moves
bool laserOn = LaserDriver::laserOn;
if(com->G == 0 && Printer::mode == PRINTER_MODE_LASER) {
Laseroff=true; // set flag here
LaserDriver::laserOn = false;
}
#endif // defined
if(com->hasS()) Printer::setNoDestinationCheck(com->S != 0);
if(Printer::setDestinationStepsFromGCode(com)) // For X Y Z E F
#if NONLINEAR_SYSTEM
if (!PrintLine::queueNonlinearMove(ALWAYS_CHECK_ENDSTOPS, true, true)) {
Com::printWarningFLN(PSTR("executeGCode / queueDeltaMove returns error"));
}
#else
if ( com->G == 0 )
{
g1_feedrate_backup = Printer::feedrate;
Printer::feedrate = Printer::maxFeedrate[X_AXIS];
PrintLine::queueCartesianMove(ALWAYS_CHECK_ENDSTOPS, true);
Printer::feedrate = g1_feedrate_backup;
}
else
{
PrintLine::queueCartesianMove(ALWAYS_CHECK_ENDSTOPS, true);
}
#endif
#if UI_HAS_KEYS
// ui can only execute motion commands if we are not waiting inside a move for an
// old move to finish. For normal response times, we always leave one free after
// sending a line. Drawback: 1 buffer line less for limited time. Since input cache
// gets filled while waiting, the lost is neglectible.
PrintLine::waitForXFreeLines(1, true);
#endif // UI_HAS_KEYS
#ifdef DEBUG_QUEUE_MOVE
{
InterruptProtectedBlock noInts;
int lc = (int)PrintLine::linesCount;
int lp = (int)PrintLine::linesPos;
int wp = (int)PrintLine::linesWritePos;
int n = (wp - lp);
if(n < 0) n += PRINTLINE_CACHE_SIZE;
noInts.unprotect();
if(n != lc)
Com::printFLN(PSTR("Buffer corrupted"));
}
#endif
#if defined(SUPPORT_LASER) && SUPPORT_LASER
if(Laseroff)
{
Laseroff=false;// reset flag to catch the next G0
HAL::delayMilliseconds(LASER_WAIT_TIME);//time to start Material burning
}
LaserDriver::laserOn = laserOn;
}
#endif // defined
break;
trying to get my Laser running i had that finding:
when starting Laser it needs a little time to get the material burnt , this should be done with the first G1 move to get no gaps
in the lines we burn.
When we switch off Laser during G0 move we have the same situation.
So where is the best place to insert a short delay which is executed with the 1st G1 move?
i tried several positions , but i didn´t succeed with it.
when i add it in command loop for G0/ G1
it gets executed for a single manual move , but running from a file with several moves it doesn´t work...
void Commands::processGCode(GCode *com) {
uint32_t codenum; //throw away variable
volatile float g1_feedrate_backup; //backup variable for G1 Feedrate
bool Laseroff;
switch(com->G) {
case 0: // G0 -> G1
case 1: // G1
#if defined(SUPPORT_LASER) && SUPPORT_LASER
{
// disable laser for G0 moves
bool laserOn = LaserDriver::laserOn;
if(com->G == 0 && Printer::mode == PRINTER_MODE_LASER) {
Laseroff=true; // set flag here
LaserDriver::laserOn = false;
}
#endif // defined
if(com->hasS()) Printer::setNoDestinationCheck(com->S != 0);
if(Printer::setDestinationStepsFromGCode(com)) // For X Y Z E F
#if NONLINEAR_SYSTEM
if (!PrintLine::queueNonlinearMove(ALWAYS_CHECK_ENDSTOPS, true, true)) {
Com::printWarningFLN(PSTR("executeGCode / queueDeltaMove returns error"));
}
#else
if ( com->G == 0 )
{
g1_feedrate_backup = Printer::feedrate;
Printer::feedrate = Printer::maxFeedrate[X_AXIS];
PrintLine::queueCartesianMove(ALWAYS_CHECK_ENDSTOPS, true);
Printer::feedrate = g1_feedrate_backup;
}
else
{
PrintLine::queueCartesianMove(ALWAYS_CHECK_ENDSTOPS, true);
}
#endif
#if UI_HAS_KEYS
// ui can only execute motion commands if we are not waiting inside a move for an
// old move to finish. For normal response times, we always leave one free after
// sending a line. Drawback: 1 buffer line less for limited time. Since input cache
// gets filled while waiting, the lost is neglectible.
PrintLine::waitForXFreeLines(1, true);
#endif // UI_HAS_KEYS
#ifdef DEBUG_QUEUE_MOVE
{
InterruptProtectedBlock noInts;
int lc = (int)PrintLine::linesCount;
int lp = (int)PrintLine::linesPos;
int wp = (int)PrintLine::linesWritePos;
int n = (wp - lp);
if(n < 0) n += PRINTLINE_CACHE_SIZE;
noInts.unprotect();
if(n != lc)
Com::printFLN(PSTR("Buffer corrupted"));
}
#endif
#if defined(SUPPORT_LASER) && SUPPORT_LASER
if(Laseroff)
{
Laseroff=false;// reset flag to catch the next G0
HAL::delayMilliseconds(LASER_WAIT_TIME);//time to start Material burning
}
LaserDriver::laserOn = laserOn;
}
#endif // defined
break;
Comments
rewrote the G0/G1 in commands.cpp( separated G0 and G1) also use max feedrate of machine now for G0.
for me it´s easier to follow up even if more lines.
//modified part Commands
void Commands::processGCode(GCode *com) {
uint32_t codenum; //throw away variable
volatile float BackupFeedrate; //backup variable for G1 Feedrate
uint8_t BackupIntensity ;
bool LaserOn = true;
switch(com->G) {
case 0: // G0
if(com->hasS()) Printer::setNoDestinationCheck(com->S != 0);
if(Printer::setDestinationStepsFromGCode(com)) // For X Y Z E F
#if defined(SUPPORT_LASER) && SUPPORT_LASER
LaserOn = LaserDriver::laserOn;
if( Printer::mode == PRINTER_MODE_LASER)
{
LaserDriver::laserOn = false;
LaserDriver::firstMove=true;//set G1 flag for Laser
//BackupIntensity=LaserDriver::intensity;
}
#endif // defined
#if NONLINEAR_SYSTEM
if (!PrintLine::queueDeltaMove(ALWAYS_CHECK_ENDSTOPS, true, true))
{
Com::printWarningFLN(PSTR("executeGCode / queueDeltaMove returns error"));
}
#else
// G0 with machine max feedrate
// found in repetier forum posted by galmiklos may 2015
BackupFeedrate = Printer::feedrate; //backup feedrate
Printer::feedrate = Printer::maxFeedrate[X_AXIS];//use max Feedrate for G0
PrintLine::queueCartesianMove(ALWAYS_CHECK_ENDSTOPS, true);
Printer::feedrate = BackupFeedrate;//restore Feedrate for G1
LaserDriver::laserOn = LaserOn;//laser on again
#endif
#if UI_HAS_KEYS
// ui can only execute motion commands if we are not waiting inside a move for an
// old move to finish. For normal response times, we always leave one free after
// sending a line. Drawback: 1 buffer line less for limited time. Since input cache
// gets filled while waiting, the lost is neglectible.
PrintLine::waitForXFreeLines(1, true);
#endif // UI_HAS_KEYS
#ifdef DEBUG_QUEUE_MOVE
{
InterruptProtectedBlock noInts;
int lc = (int)PrintLine::linesCount;
int lp = (int)PrintLine::linesPos;
int wp = (int)PrintLine::linesWritePos;
int n = (wp-lp);
if(n < 0) n += PRINTLINE_CACHE_SIZE;
noInts.unprotect();
if(n != lc)
Com::printFLN(PSTR("Buffer corrupted"));
}
#endif
break;
case 1: // G1
if(com->hasS()) Printer::setNoDestinationCheck(com->S != 0);
if(Printer::setDestinationStepsFromGCode(com)) // For X Y Z E F
/*
#if defined(SUPPORT_LASER) && SUPPORT_LASER
if( Printer::mode == PRINTER_MODE_LASER)
{
LaserDriver::laserOn = true;
}
#endif // defined
*/
#if NONLINEAR_SYSTEM
if (!PrintLine::queueDeltaMove(ALWAYS_CHECK_ENDSTOPS, true, true))
{
Com::printWarningFLN(PSTR("executeGCode / queueDeltaMove returns error"));
}
#else
#if defined(SUPPORT_LASER) && SUPPORT_LASER
if( Printer::mode == PRINTER_MODE_LASER)
{
if(LaserDriver::firstMove==true)
{
Commands::waitUntilEndOfAllMoves();
Com::printWarningFLN(PSTR("firstmove_ON"));//just for debug
LaserDriver::changeIntensity(LaserDriver::intensity);//BackupIntensity);//restore intensity
codenum = LASER_WAIT_TIME;//code from G4 DWELL
codenum += HAL::timeInMilliseconds(); // keep track of when we started waiting
while((uint32_t)(codenum-HAL::timeInMilliseconds()) < 2000000000 )
{
GCode::readFromSerial();
Commands::checkForPeriodicalActions(true);
Com::printWarningFLN(PSTR("wait loop"));//just for debug
}
LaserDriver::firstMove=false;//reset G1 flag for Laser
Com::printWarningFLN(PSTR("firstmove_OFF")); //just for debug
}
}
#endif
PrintLine::queueCartesianMove(ALWAYS_CHECK_ENDSTOPS, true);
#endif
#if UI_HAS_KEYS
// ui can only execute motion commands if we are not waiting inside a move for an
// old move to finish. For normal response times, we always leave one free after
// sending a line. Drawback: 1 buffer line less for limited time. Since input cache
// gets filled while waiting, the lost is neglectible.
PrintLine::waitForXFreeLines(1, true);
#endif // UI_HAS_KEYS
#ifdef DEBUG_QUEUE_MOVE
{
InterruptProtectedBlock noInts;
int lc = (int)PrintLine::linesCount;
int lp = (int)PrintLine::linesPos;
int wp = (int)PrintLine::linesWritePos;
int n = (wp-lp);
if(n < 0) n += PRINTLINE_CACHE_SIZE;
noInts.unprotect();
if(n != lc)
Com::printFLN(PSTR("Buffer corrupted"));
}
#endif
break;
added a flag in LaserDriver (LaserDriver::firstMove), use some code from G4 for the startup delay and modified the following parts in motion.cpp to check the flag
#if defined(SUPPORT_LASER) && SUPPORT_LASER
else if((Printer::mode == PRINTER_MODE_LASER)&!(LaserDriver::firstMove)) // Last move disables laser for safety!
{
LaserDriver::changeIntensity(0);
Startup delay for 1st move works , no gaps visible :-) at the moment running a testprint (testburn) what do you think about that solution?
Think the LASER_WAIT_TIME should be storable in EEprom as it´s strongly dependent on material and lasertype itself.
something unknown.
did some test engrave and viewed so the stop and go as you mentioned is not the problem as anyway after G0 it has to stop
for the LASER_WAIT_TIME to heat up the material gefore G1 move and buffer is filled up quickly.
(did some engraving for PCB-Layout for a TSSOP28 chip and it looks absolutely great.)
anyway, it´s just a temporary solution.
I´m working on modding the "wait move" now , got delay running without buffer running empty but i have
no idea how to generate a "move without motion" to switch on laser before/while running the delay.
So I´m not shure if i get it done this way.
i think "easiest" would be a move without motion that takes a defined time selectable by placeholder and to
insert this move to the buffer as wait move does.
Maybe you find the time to help me respectively point me a way to do this.
Just for my information ,
why did you couple the laser to moves?
wouldn´t it be easier to do the on/off for safety reasons in the process gcode?
(for example : switching off at beginning of G0 Switching on again at beginning of G1)
so how do we actual have the possibility to Switch it on to adjust Focus for machine setup?
Ok, thanks a lot!
I´ll try that this evening, sounds good...
So i didn´t know about dir function and left it 0 wondering why laser doesn´t Switch on...
I´ll inform you the result as soon as i verified
added motion.cpp
uint8_t PrintLine::insertWait( uint32_t wait)
{
PrintLine *p = getNextWriteLine();
p->flags = FLAG_WARMUP;
p->joinFlags = FLAG_JOIN_STEPPARAMS_COMPUTED | FLAG_JOIN_END_FIXED | FLAG_JOIN_START_FIXED;
p->dir = 1;
p->setWaitForXLinesFilled(1);
p->setWaitTicks(long(wait/1000*(F_CPU)));//in ms
pushLine();
Com::printFLN(PSTR("InsertWait"));//for debug
return 1;
}
to use it in commands
case 1: // G1
if(com->hasS()) Printer::setNoDestinationCheck(com->S != 0);
if(Printer::setDestinationStepsFromGCode(com)) // For X Y Z E F
#if NONLINEAR_SYSTEM
if (!PrintLine::queueDeltaMove(ALWAYS_CHECK_ENDSTOPS, true, true))
{
Com::printWarningFLN(PSTR("executeGCode / queueDeltaMove returns error"));
}
#else
#if defined(SUPPORT_LASER) && SUPPORT_LASER // check for first move to start burn
if( Printer::mode == PRINTER_MODE_LASER)
{
if(LaserDriver::firstMove==true)
{
LaserDriver::changeIntensity(255); // think it should work without this
PrintLine::insertWait(2500);
LaserDriver::firstMove=false;//reset G1 flag for Laser
}
}
#endif
PrintLine::queueCartesianMove(ALWAYS_CHECK_ENDSTOPS, true);
#endif
#if UI_HAS_KEYS
// ...........
when i do manual commands from host it works as it should,as long as i keep the
LaserDriver::changeIntensity(255); in the loop.
but as laser is switched on again at end of G0 move it shoul work without that.
when i start a file , just delay works but laser does not switch on even with
LaserDriver::changeIntensity(255); in the loop.
so delay is working, but laser doesn´t switch on.
i know the problem is sitting in front of my computer but i cannot find the bug
waitForXFreeLines(1,true);
I miss the mod for bresenham where the waits are handled. Without it, you only have a delay.
Change intensity is not needed. The enable from G1 handling should be good as bresenham has to handle it and not your code while parsing!
mod for bresenham means i have to add a flag for example:
#define FLAG_WAIT_FOR_LASER 256 and handle that in bresenhamStep() ??
similar to it´s done for (cur->isWarmUp()) without returning?
i spent several nights "standing between the trees while looking for the forest".
my thoughts were too complicated, didn´t see the need for
if(cur->dir)
.........
thanks again!!!
you saved my weekend:-)
so now i want to add the delaytime to eeprom to get it easily modified during operation , as it strongly depends on material you want to engrave .
is there something special to take care of?
my thoughts are adding number to eeprom.h and handling in eeprom.cpp
making it changeable via uiaction similar to bed coating
what´s your meaning?
upper : without wait time
lower : 200ms wait before firstmove
had to add it to G2/G3 also ,now it´s great engraving result
3W blue Laser , feedrate 400mm/min linewidth 0.1mm material :Formica / Resopal
difference in size is from taking macro picture , so in reality it´s exactly the same.
for me it was worth the effort and @REPETIER thank you so much for your patience and pointing me the way!!
I´ll do some further test before cleaning up the code and hopefully i get the eeprom stuff done..
afterwards I´ll send a pull request against delvelopment branch
including this , the safety door stuff and M6
kind regards,
RAyWB
so i think it will stay a very individual setup depending on laser Type, wavelenght,emitted power and material.
for a cw laser diode (which i think are most popular in our field of use ) problem will be mostly the one we can catch by starting
time. so we get the material,diode startup and focus related stuff in one setup.(so once the material starts burning it will
continue and even feedrate change by 10% will not really be visible , thats what happens on wood , bamboo, paper and that piece of resopal i used for the test above. so that´s my findings)
i think feedrate is not in direct relation to startup time but also variable on the fly by printspeed, which we already have.
and should be nearly constant.another part is the time material needs to heat up and start burning.
ordered a photodiode now (photoresistor is too slow)to do some measurements which part is major and of what percentage.
simple test procedure, just measure time between switching on and light coming.
think i´ll do it just adding another arduino , trigger two interrupts and count micros... should be precise enough.
By the way , i did some test cutting today, generated g-code by cambam using engrave function and had to do
z-move for several passes, found that g1 z-move has to be filtered out also.
so delay is just to be added for the first G1 in x and y direction. will give you an update regarding result
causes major part of delay.
I got a modification instruction from dtrlpf today (i use the x-drive driver from them)to have a pwm pin.
will try that and compare the result.
hope the photo diode arrives at the weekend to get measured result.
Result shows 14 to 16 milliseconds to start Laser by Power ON/OFF
My warmup time i found experimental is 200 milliseconds so Laser Power UP is minor part of needed time.
will try to do mesurement using PWM input tomorrow , first have to disassemble the unit
Switch off time should go ->0 it can just depend on capacitors in Laser Driver.
Laser current is 2.4 A and Cap is quite small so there is no chance for Switch off delay.
I think the 14ms base on a kind of softstart to prevent from current overshoot but as i don´t know about the IC used in
the Driver and it´s kept secret by vendor i just can guess about that.
I used a Pin Photodiode BPX65 for my measurement (specified Rise time 12 ns) followed by BC847 so we can see delay coming from Setup as 0
i mean G-Code style:
i just found by M3 S... or is it even possible by G1 S...?(some CAM software does this style)
other style i found is switching on by M106 S... /off by M107