Laser start

edited April 2016 in Questions & Answers
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;



Comments

  • Sound like a difficult problem. If I understand you correctly you want a longer wait on each first G1 move even after G0.
    So I think you need to modify the complete move queue handling. First you need a flag what the last move was, so you know if we are handling the first G1 in a sequence. Then we need a way to add a dummy move with laser on, no distance but with time. That would break the path planing a bit, so we need some tricks like adding it with start/end speed set to jerk/2 and start/end speed fixed so it is not mangled through path planner. The move must have also a special indicator like the wait moves we insert for the first move. Maybe we could even misuse them with the laser flag on so they turn laser on for the time of the wait time stored in them. Last thing is when you add a G1 and laser is on you need to add this move before it but definitely after the waits. Only this way you get the right timings.

    I think modding the "wait move" to do it seems the easiest way.
  • edited April 2016
    i defined LASER_WAIT_TIME 500 in configuration.h

    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.




              
  • Commands are not in sync with the buffered moves. You did a wait until end of moves as you knew this, but it still adds some wait on first move from path planner which adds 3 wait moves before the real move. So your delay is always > LASER_WAIT_TIME .

    So it works somehow but is surely not the best solution as it emties the buffer all the time and makes travel a stop and go. For cutting not the worst but engraving could have visible indications of the problem.
  • edited April 2016
    i agree with you , so it´s a preliminary solution for me and you are right i used it because i knew it and didn´t want to touch
    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?

  • Actually my laser has a focus light even when off, so that problem never occured to me. And letting it full power seemed to dangerous.

    I think for focussing we could make a special command and in temperature interrupt which is called 3906 times per second we could then enable it for 10/3906 calls per second. Or maybe adjustable for low intensity. That would give less power which is better for focussing.

    The coupling to G1 is for safety as it enables full power and between G0/G1 there is no pause (or was regarding your med) so we need it there anyway.

    For adding the move see
    insertWaitMovesIfNeeded 

    in motion.cpp. That is what you need. We could say dir=0 is wait, dir=1 means with laser on.
    It is then evaluated in bresenham where it normally just returns with given delay to come back. 
    There you test for dir to enable laser before returning.
    It pushes a move with fixed end speeds so just insert that where you new have your wait in G1 logic.
  • 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

  • OK, did it as follows:


    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
  • You should insert it before #if NONLINEAR_SYSTEM. Deltas could also have lasers.

    Your insert must ensure there is a free line available






    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!

  • edited April 2016
    Ok,
    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?


  • You have the flag already. You have set dir=1 which is otherwise 0 in warmup. That was so you could mod the wait in bresenham and have some means to detect it.
  • edited April 2016
    Thank you so much for your patience.
    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:-)
  • Ok, it´s working now without buffer running empty , detect first move and laser switched on with added delay before first move :-)

    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?



  • edited May 2016
    Here you can see the difference:

    upper : without wait time

    lower : 200ms wait before firstmove

    image


    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
  • Ok difference looks good enough to say we want this:-)

    Actually I'm not sure we want a time as parameter also we need the time in the end. But as you already noted the time differs. On the other side the continuous move works fine, so there the time is ok. I think time could be a function depending on focus diameter and print speed. So it must be somewhere 50-100% of focus width/print speed. That way you would not need to adjust it every time.

    I think we should add a variable in Printer:: class for this. This should be adjustable with a command on the fly without disturbing eeprom. EEPROM will wear out as some point if you mod it too much. A default could be stored in eeprom that defines start value. Hope you catch all the eeprom issues with restore/write/... Easiest is just so search for a variable and do it accordingly. And do not forget to increase eeprom protocol version to init it for upgraders:-)
  • to get setup against focus i think it´s square function based on power/area.
    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.
  • My logic was quite simple. Lets say we laser with 10mm/s and 0.1mm focus- So each point on the line gets 0.1/10 = 0.01 seconds power from laser. When we start the 50% we are moving away from did not have the power so with 0.01s pause it gets the same power as the line. The other side gets 50% more, which is not that good visible so it is no problem.

    Well that was what I thought, but I have not had enough time to test all this and your code, so you know more about that. Soif you think that idea is wrong implement it as time. After all it always goes down to time so it can not be wrong.
  • edited May 2016
    may be i´m wrong regarding the reason for the delay, part of it seems to come from laser driver during power up
    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
  • Ok, if the laser needs a warmup time a delay makes even more sense. Always thought it would be immediate on since drivers use PWM to change intensity and always thought it would really go on/off. So I'm very interested in your measurement.
  • edited May 2016
    so in may case i dont have a pwm input, i just can switch power on and off thats why i think that
    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.
  • edited May 2016
    As Photodiode arrived now i did the measurement with Laser Power up.
    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
  • 14 milliseconds is still more then I thought. Will be interesting if pwm shows the same or if it is the delay of the photodiode.

    Do you measure also the time laster of->diode off? Here I think we can assume laser goes instantly off I guess.
  • 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

  • edited May 2016
    What´s the implemented version of changing intensity at the moment?
    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
  • You can only set intensity in M3 but that is currently ignored. Only if you overwrite the driver you can use that value.
Sign In or Register to comment.