Welcome to Our Community

Some features disabled for guests. Register Today.

Getting started with Javascript Macros in CONTROL / Library of Macros created by the community

Discussion in 'Control Software' started by flysolo206, May 21, 2020.

  1. flysolo206

    Builder

    Joined:
    Nov 4, 2017
    Messages:
    17
    Likes Received:
    2
    Just hooked up my blackbox and now diving into the Control software (very new to CNC). I have a project that requires a series of looping gcode commands. I'd like to explore using Javascript Macros for this. Can anyone point me to a resource that can help get me started with using javascript macros? I can't seem to find any documentation, videos, tutorials, or examples. Anything would be helpful at this point. Thanks.
     
    dbdb likes this.
  2. Peter Van Der Walt

    Peter Van Der Walt OpenBuilds Team
    Staff Member Moderator Builder Resident Builder

    Joined:
    Mar 1, 2017
    Messages:
    15,128
    Likes Received:
    4,329
    1) Open CONTROL, and press ctrl+Shift+I (or cmd+shift+i i think its on Mac)
    Notice the little bit of Help thats provided for you to see some of the more popular pieces of data to access and some of the popular commands

    upload_2020-5-22_13-52-28.png

    2) But you are not limited to just that of course, you can use ANY JS, you can inject html into the DOM, you can manipulate CSS, you can use all the functions in the existing code (anything from OpenBuilds/OpenBuilds-CONTROL, lots of little functions that exist in the existing code, like, stupid example, if you want your Macro to print to the serial log, use the printLog function from here OpenBuilds/OpenBuilds-CONTROL - just depends on your skill level, and looking at the sources to discover more gems inside it. And of course talk to us, we'll be happy to help

    3) So armed with some functions, and a goal

    To achieve what you want to do: You'll need

    a) the gcode to loop (I assume you loaded up our gcode into the Gcode editor?
    Code:
    editor.getValue();
    
    b) the currently running state (so you can know when the job is complete to start it again?)
    Code:
    //0 = not connected, 1 = opening, 2 = connected, 3 = playing, 4 = paused, 5 = alarm, 6 = firmware upgrade
    laststatus.comms.connectionStatus == ?
    
    c) starting the job:
    Code:
    socket.emit("runJob", {
       data: gcode-commands,
       isJob: false,
       completedMsg: "message displayed upon completion ",
    });
    

    4) We recommend you use the Devtools console to test and develop your macro code before pasting it into a macro

    Code:
    // very simple and bad version of the macro just quickly put together to demonstrate the js macro functionality
    // you asked for, NB NOTE this will run the macro FOREVER (until you quit CONTROL) - should add some way to break it (: etc
    
    setInterval(function(){
        if (laststatus.comms.connectionStatus == 2 ) {
            socket.emit("runJob", {
                data: editor.getValue(),
                isJob: false,
                completedMsg: false
            });
        } else {
            printLog("job still running, or not connected, or some error/paused state");
        }
    }, 100)
    
    
    I'll leave it up to you from here to expand it as you need it but hopefully this cover the basics of how it works

    NOTE: This thread is a showcase and introduction to Macros. Support posts here will be moved to Tech support for Javascript Macros in CONTROL - if you need help please post in Tech support for Javascript Macros in CONTROL (not in this thread)
     
    #2 Peter Van Der Walt, May 22, 2020
    Last edited: Sep 7, 2022
  3. Peter Van Der Walt

    Peter Van Der Walt OpenBuilds Team
    Staff Member Moderator Builder Resident Builder

    Joined:
    Mar 1, 2017
    Messages:
    15,128
    Likes Received:
    4,329
    More advanced version, with UI (;
    Stop and start buttons, counts the iterations, etc


    upload_2020-5-22_14-45-44.png

    Code:
    window.macro1repeat = false;
    window.macro1interval;
    window.macro1interation = 0;
    
    window.runRepeatingGcode = function() {
        macro1repeat = true; // set flag
        window.macro1interval = setInterval(function() {
            if (macro1repeat) {
                if (laststatus.comms.connectionStatus == 2) {
                    macro1interation++;
                    $("#macro1log").html("running: iteration: " + macro1interation)
                    socket.emit("runJob", {
                        data: editor.getValue(),
                        isJob: false,
                        completedMsg: false
                    });
                }
            } else {
                $("#macro1log").html("Stopping...")
                socket.emit('stop', false)
                clearInterval(window.macro1interval);
            }
        }, 100)
    }
    
    // since we have metro, use Metro.dialog https://metroui.org.ua/dialog.html to make a UI
    Metro.dialog.create({
        title: "My looping Macro",
        content: `
               <button class="button info" onclick="runRepeatingGcode()">Run</button>
               <button class="button info" onclick="macro1repeat = false;">Stop</button>
               <hr>
               <span id="macro1log">pending run...</span>
           `,
        actions: [{
            caption: "Stop and close this window",
            cls: "js-dialog-close alert",
            onclick: function() {
                macro1repeat = false;
                printLog("Repeating Macro Exited")
            }
        }]
    });
    
    
     
  4. Peter Van Der Walt

    Peter Van Der Walt OpenBuilds Team
    Staff Member Moderator Builder Resident Builder

    Joined:
    Mar 1, 2017
    Messages:
    15,128
    Likes Received:
    4,329
    Added another Javascript Macro example: See Probe Center

    upload_2020-12-8_20-25-9.png
     
    David the swarfer and sharmstr like this.
  5. Peter Van Der Walt

    Peter Van Der Walt OpenBuilds Team
    Staff Member Moderator Builder Resident Builder

    Joined:
    Mar 1, 2017
    Messages:
    15,128
    Likes Received:
    4,329
    Inject a Z Probe button onto main toolbar:

    Code:
    var probezbtntpl = `<button id="grblProbeZMenu" class="ribbon-button" onclick="openProbeZDialog();">
                     <span class="icon">
                       <span class="fa-layers fa-fw">
                         <i class="fas fa-podcast fa-rotate-180 fg-blue"></i>
                       </span>
                     </span>
                     <span class="caption grblmode">Probe Z</span>
                   </button>`
    $( "#grblProbeMenu" ).after( probezbtntpl  );
    
    [​IMG]
     
  6. sharmstr

    sharmstr OpenBuilds Team
    Staff Member Moderator Builder Resident Builder

    Joined:
    Mar 23, 2018
    Messages:
    2,059
    Likes Received:
    1,449
    #6 sharmstr, Dec 28, 2020
    Last edited by a moderator: Sep 7, 2022
    Alain JBT likes this.
  7. sharmstr

    sharmstr OpenBuilds Team
    Staff Member Moderator Builder Resident Builder

    Joined:
    Mar 23, 2018
    Messages:
    2,059
    Likes Received:
    1,449
    Updated hide Z buttons, now works with step size change via keyboard shortcuts. (though doesnt rebind if shortcuts are changed. needs a restart)

    Code:
    var macroRunResults = '';
    if (!document.getElementById('macroRan')) {
    
     
     // Remove Z jog buttons when 100mm clicked
    
      var currentStepSize = "dist10label"; // should be dist10 on startup
    
      // incase stepsize was changed before macro was run
      if ($('#dist100').hasClass('bd-openbuilds') ) {
          hideZbtns();
      } else if ($('#dist01').hasClass('bd-openbuilds')) { 
          currentStepSize = "dist01label";
      } else if ($('#dist1').hasClass('bd-openbuilds')) {
          currentStepSize = "dist1label";
      }
    
      $(document).bind('keydown', keyboardShortcuts.stepP, function(e) {
          if ( currentStepSize == 'dist10label' ) {
              hideZbtns();
          } else if (currentStepSize == 'dist01label') {
              currentStepSize = 'dist1label';
          } else if (currentStepSize == 'dist1label') {
              currentStepSize = 'dist10label';
          }
      });
    
      $(document).bind('keydown', keyboardShortcuts.stepM, function(e) {
          showZbtns();
          if ( currentStepSize == 'dist100label' ) {
              currentStepSize = 'dist10label';
          } else if (currentStepSize == 'dist10label') {
              currentStepSize = 'dist1label';
          } else if (currentStepSize == 'dist1label') {
              currentStepSize = 'dist01label';
          }
      });
    
      $('#dist100label').on('click', function() {
          hideZbtns();
      });
    
      $('#dist01label,#dist10label,#dist1label').on('click', function() {
          currentStepSize = event.srcElement.id;
          showZbtns();   
      });
    
      function hideZbtns() {
          $('#zM,#zP').hide();
          $('#dist100').css('width', '150%');
          $('#dist100').closest('td').css('padding-right', '25px');
          currentStepSize = 'dist100label'; 
      };
    
      function showZbtns() {
          $('#zM,#zP').show();
          $('#dist100').closest('td').css('padding-right', '0px');
          $('#dist100').css('width', '100%');     
      };
    
      $('#jogTypeContinuous').on('click', function() {
        if ($(this).is(':checked')) {
          showZbtns();
        } else {
          if ($('#dist100').hasClass('bd-openbuilds') ) {
            hideZbtns();
          }
        }
      });
    
      var macroHasRun = '<div id="macroRan" class="hidden"></div>'
      $('#grblProbeMenu').after(macroHasRun);
      macroRunResults = 'Macro ran.';
    } else {
      printLog('Macro has already run.')
      macroRunResults = 'Macro already ran.  Not going to run again.';
    };
    
    // since we have metro, use Metro.dialog https://metroui.org.ua/dialog.html to make a UI
    Metro.dialog.create({
        title: "Macro Run Results",
        content: macroRunResults,
        actions: [{
            caption: "OK",
            cls: "js-dialog-close alert"
        }]
    });
    
    [​IMG]
     
    #7 sharmstr, Jan 4, 2021
    Last edited: Jan 6, 2021
  8. Peter Van Der Walt

    Peter Van Der Walt OpenBuilds Team
    Staff Member Moderator Builder Resident Builder

    Joined:
    Mar 1, 2017
    Messages:
    15,128
    Likes Received:
    4,329
    Want to know how big a piece of stock to grab from the offcuts bin?

    upload_2021-6-9_16-1-32.png

    Code:
    if (typeof object !== 'undefined') {
       var string = ""
       if (object.userData.inch) {
           string += "Width (X): " + (object.userData.bbbox2.max.x - object.userData.bbbox2.min.x) + "inch" + " / " + "Height (Y): " + (object.userData.bbbox2.max.y - object.userData.bbbox2.min.y) + "inch";
       } else {
           string += "Width (X): " + (object.userData.bbbox2.max.x - object.userData.bbbox2.min.x) + "mm" + " / " + "Height (Y): " + (object.userData.bbbox2.max.y - object.userData.bbbox2.min.y) + "mm";
       }
       console.log(string)
       Metro.dialog.create({
           title: "Stock Size needed",
           clsDialog: "dark",
           content: `
              To run this job, you need a piece of stock:<hr>
              ` + string + `
          `,
           actions: [{
               caption: "Cancel",
               cls: "js-dialog-close alert",
           }]
       });
    }
    
    
     
    #8 Peter Van Der Walt, Jun 9, 2021
    Last edited: Jun 9, 2021
    BSmith and sharmstr like this.
  9. sharmstr

    sharmstr OpenBuilds Team
    Staff Member Moderator Builder Resident Builder

    Joined:
    Mar 23, 2018
    Messages:
    2,059
    Likes Received:
    1,449
    Move Stop button before Run button. Limited testing done but seems to work great.

    Code:
    $( "#stopBtn" ).remove();
    var moveStopBtn = '<button id="stopBtn" class="ribbon-button" onclick="socket.emit(\'stop\', { stop: true, jog: false, abort: false});" disabled="disabled"> \
        <span class="icon"> \
          <i class="fas fa-stop"></i> \
        </span> \
        <span class="caption">Stop<br>Job</span> \
      </button>'
     
    $( "#runBtn" ).before( moveStopBtn );
    upload_2022-9-7_9-23-18.png
    [​IMG]
     
    #9 sharmstr, Dec 8, 2021
    Last edited by a moderator: Sep 7, 2022
    Peter Van Der Walt likes this.
  10. Peter Van Der Walt

    Peter Van Der Walt OpenBuilds Team
    Staff Member Moderator Builder Resident Builder

    Joined:
    Mar 1, 2017
    Messages:
    15,128
    Likes Received:
    4,329
    Saw stop Macro: See NickEng's Sawstop Grbl interface / coding for a more in depth discussion

    [​IMG]

    In CONTROL > Macros tab > Add Macro

    Enter a name (like SawStop Mode), and click the Javascript tab
    Paste the following:

    Code:
    // Add some CSS classes
    $("<style type='text/css'> .keypadBtn {width: 80px;} .sawStopKeypadBtn {width: 80px;} .sawStopActionBtn {width: 280px; } </style>").appendTo("head");
    
    // variables placeholder
    window.sawStopValue = 0.0;
    window.sawStopFractionValue = 0;
    
    // Update the UI <span> fields to display value
    window.updateSawStopValueUI = setInterval(function() {
      $("#sawStopValue").html(sawStopValue.toFixed(0));
      if (sawStopFractionValue.length > 1) {
        $("#sawStopFraction").html("&nbsp;and&nbsp;" + sawStopFractionValue);
      } else {
        $("#sawStopFraction").html("");
      }
    }, 50);
    
    // keypad button
    window.sawStopBtn = function(val) {
      var currentValue = sawStopValue.toString();
      currentValue += val;
      sawStopValue = parseInt(currentValue);
    }
    
    // fraction button
    window.sawStopFraction = function(val) {
      sawStopFractionValue = val;
    }
    
    // backspace button
    window.sawStopBackSpace = function() {
      var sawStopcurrentValue = sawStopValue.toString();
      var sawStopNewValue = sawStopcurrentValue.slice(0, -1);
      if (sawStopNewValue.length == 0) {
        sawStopNewValue = "0";
      }
      sawStopValue = parseInt(sawStopNewValue);
    }
    
    window.sawStopMoveTo = function() {
      // Calculate Decimals from fractions
      if (sawStopFractionValue.length > 1) {
        var sawStopFractionSplit = sawStopFractionValue.split("/");
        var sawStopDecimals =
          parseInt(sawStopFractionSplit[0], 10) /
          parseInt(sawStopFractionSplit[1], 10);
        var decimalSawStopFractionValue = eval(sawStopFractionValue);
      } else {
        var decimalSawStopFractionValue = 0;
      }
      // add decimals to integers
      var sawStopFinalPosition = sawStopValue + decimalSawStopFractionValue;
      // Send move command to Grbl
      sendGcode("$J=G90 G20 X" + sawStopFinalPosition + " F10000");
    }
    
    // Create the User Interface
    Metro.dialog.create({
      title: "Saw Stop Macro",
      width: '90%',
      content: `
            <span class="display3" id="sawStopValue">0</span>
            <span class="display3" id="sawStopFraction"></span>
            <span class="display2">&nbsp;inch</span>
    
            <div id="sawStopNumbersUI">
    
            <table class="table striped compact">
            <tr>
            <td>
            <table>
             <tr>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopBtn(7);">7</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopBtn(8);">8</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopBtn(9);">9</button></td>
             </tr>
             <tr>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopBtn(4);">4</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopBtn(5);">5</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopBtn(6);">6</button></td>
             </tr>
             <tr>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopBtn(1);">1</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopBtn(2);">2</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopBtn(3);">3</button></td>
             </tr>
             <tr>
               <td></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopBtn(0);">0</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopBackSpace();"><i class="fas fa-arrow-left"></i></button></td>
             </tr>
            </table>
            </td>
            <td>
            <table>
             <tr>
               <td colspan="3">
                 <hr>
               </td>
             </tr>
             <tr>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopFraction('1/16');">1/16</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopFraction('3/16');">3/16</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopFraction('5/16');">5/16</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopFraction('7/16');">7/16</button></td>
             </tr>
             <tr>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopFraction('9/16');">9/16</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopFraction('11/16');">11/16</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopFraction('13/16');">13/16</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopFraction('15/16');">15/16</button></td>
             </tr>
             <tr>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopFraction('1/8');">1/8</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopFraction('3/8');">3/8</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopFraction('5/8');">5/8</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopFraction('7/8');">7/8</button></td>
             </tr>
             <tr>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopFraction('1/4');">1/4</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopFraction('1/2');">1/2</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopFraction('3/4');">3/4</button></td>
               <td><button class="button outline sawStopKeypadBtn" onclick="sawStopFractionValue=0;">0/0</button></td>
             </tr>
             <tr>
               <td colspan="3">
                 <hr>
               </td>
             </tr>
            </table>
    
            </td>
            </tr>
            </table>
            </div>
    
            <hr>
            <button class="button outline sawStopActionBtn alert" onclick="sawStopValue=0; sawStopFractionValue=0;">CLEAR</button>
            <button class="button outline sawStopActionBtn success" onclick="sawStopMoveTo();">MOVE TO</button>
            <hr>
            <button class="button outline sawStopActionBtn warning" onclick="sendGcode('M8');">APPLY BRAKE</button>
            <button class="button outline sawStopActionBtn primary" onclick="sendGcode('M9');">RELEASE BRAKE</button>
            <hr>
            <button class="button outline sawStopActionBtn secondary" onclick="sendGcode('$H');">HOME</button>
            <button class="button outline sawStopActionBtn secondary" onclick="sendGcode('G10 P0 L20 X0');">SETZERO</button>
           `,
      actions: [{
        caption: "Exit Macro",
        cls: "js-dialog-close alert",
        onclick: function() {
          printLog("Saw Stop Macro Exited")
        }
      }]
    });
    
    
     
    sharmstr and Mark Carew like this.
  11. Peter Van Der Walt

    Peter Van Der Walt OpenBuilds Team
    Staff Member Moderator Builder Resident Builder

    Joined:
    Mar 1, 2017
    Messages:
    15,128
    Likes Received:
    4,329
    Some of @sharmstr 's awesome Macros:

    Expand USB Port Selector in OpenBuilds Control

    [​IMG]


    ___________________________________________________________________________________________________________

    Set Work Zero Via Keyboard Shortcuts in OpenBuilds Control

    [​IMG]


    ___________________________________________________________________________________________________________

    Initiate Probing via Keyboard Shortcuts in OpenBuilds Control

    [​IMG]



    ___________________________________________________________________________________________________________


    Change OpenBuilds Control Jog Step Size

    [​IMG]


    ___________________________________________________________________________________________________________

    OpenBuilds Control Automatically Launch at Startup


    [​IMG]

    ___________________________________________________________________________________________________________



    See more of her awesome Macros here: Blog

     
    Soultie and sharmstr like this.
  12. aixpower

    aixpower New
    Builder

    Joined:
    Jun 27, 2022
    Messages:
    14
    Likes Received:
    11
    I have created a small macro I'm using for my builds and want to share it with the community, hoping it might be useful for one or the other: milling a linear cut or pocket into some stock.

    Intention
    The initial requirement was to easily perform an exact cut of some stock. As I do not have a good sawing equipment, I simply cut the stock by a milling operation. The g-code was generated by some CAD program and converted to GRBL g-code. Instead of changing the parameters all the time in the CAD program, I created this macro.

    Description
    The purpose of this macro is to cut some stock by milling. It starts at 0/0/0 (x/y/z) and mills to x/y/z.
    [​IMG]
    In fact, depending on the placement of the stock relative to 0/0 and the x/y/z length, the macro can be used to
    • cut a stock
      [​IMG]
    • mill a slot into a stock[​IMG]
    • mill a long-hole into a stock[​IMG]
    • mill a pocket into a stock[​IMG]


    The macro generates a sequence of G-Codes with linear milling movements in layers. The generated code will replace the one in the G-Code editor, and triggers the parsing and visualization of the code in the 3D viewer.
    [​IMG]
    Installation
    To install the macro, copy the code from below, or from https://raw.githubusercontent.com/poWer4aiX/ob-linear-cut-macro/master/src/ob-linear-cut-macro.js into a new macro in OpenBuilds CONTROL.

    Source
    You can find all the sources in github repository GitHub - poWer4aiX/ob-linear-cut-macro: Macro for OpenBuilds CONTROL to mill a linear horizontal cut.

    Code:
    function generateGCode(xMovement, yMovement, zMovement, stepDown, feedrate) {
      if (zMovement <= 0 || stepDown <= 0)
       return;
      var x0 = 0
      var y0 = 0
      var z0 = 0
      var zSafe = 7
    
      var x = x0
      var y = y0
      var z = z0
      var xyFeed = feedrate
      var zFeed = feedrate / 2
    
      var gCode = ''
      gCode += `; GCODE Generated by ???? on ?????\n`
      gCode += `; linear pocket movement from 0/0/0 to ${xMovement}/${yMovement}/${zMovement} in ${stepDown}mm steps\n`
      gCode += 'G21; mm-mode\n'
      gCode += 'G54; Work Coordinates\n'
      gCode += 'G21; mm-mode\n'
      gCode += 'G90; Absolute Positioning\n'
      gCode += 'M3 S1000; Spindle On\n'
      gCode += '\n'
      gCode += '; Begin of linear pocket / cut loop\n'
      gCode += '; Endmill Diameter: any\n'
      gCode += `G0 Z${z0 + zSafe}; move to z-safe height\n`
      gCode += `G0 F1000 X${x0} Y${y0}; move to x/y zeropoint\n`
      gCode += '\n'
    
      var forwardDir = 1
      while (z >= -zMovement) {
       // step down
       z -= stepDown
       if (z < -zMovement) z = -zMovement
       gCode += `G1 F${zFeed} Z${z}; step down on current position\n`
    
       // x/y movement
       if (forwardDir) {
         x = x0 + xMovement
         y = y0 + yMovement
       } else {
         x = x0
         y = y0
       }
       gCode += `G1 F${xyFeed} X${x} Y${y}; linear x/y movement\n`
    
       // check for endCondition
       if (z <= -zMovement)
         break
       forwardDir = !forwardDir
      }
    
      // move tool back to save z
      gCode += '\n'
      gCode += '; End of linear pockt / cut loop\n'
      gCode += '; retracting back to z-safe\n'
      gCode += `G0 Z${z0 + zSafe}\n`
      gCode += '\n'
      gCode += 'M5 S0; Spindle Off\n'
      gCode += '; Job completed\n'
    
      // replace code in G-Code editor
      editor.session.setValue(gCode);
    
      // refresh 3D view
      parseGcodeInWebWorker(editor.getValue())
    
      // required for testing
      return gCode;
    }
    
    function genInputHtml(label, id, value, icon, descr) {
      var html = ''
      html += '<div class="row mb-0">\n'
      html += `  <label class= "cell-sm-6" > ${label}</label >\n`
      html += '  <div class="cell-sm-6">\n'
      html += `    <input id="${id}" type="number" value="${value}" data-role="input" data-append="mm" data-prepend="<i class='fas ${icon}'></i>" data-clear-button="false">\n`
      html += '  </div>\n'
      html += '</div>\n'
      html += '<hr>\n'
      if (descr)
       html += `<small > ${descr}</small > `
      return html
    }
    
    // Dialog creation
    Metro.dialog.create({
      title: 'Linear Pocket / Cut',
      content:
       genInputHtml('X movement', 'xMovement', 100, 'fa-ruler-horizontal', '') +
       genInputHtml('Y movement', 'yMovement', 0, 'fa-ruler-vertical', '') +
       genInputHtml('Cutting deepth', 'zMovement', 10, 'fa-ruler', '') +
       genInputHtml('Step-down', 'stepDown', 1, 'fa-align-justify', '') +
       genInputHtml('Feedrate', 'feedrate', 100, 'fa-running', 'How fast to move the endmill in milling operation'),
      actions: [
       {
         caption: "Generate G-Code",
         cls: "js-dialog-close success",
         onclick: function () {
           const xMovement = parseFloat($("#xMovement").val())
           const yMovement = parseFloat($("#yMovement").val())
           const zMovement = parseFloat($("#zMovement").val())
           const stepDown = parseFloat($("#stepDown").val())
           const feedrate = parseInt($("#feedrate").val())
           generateGCode(xMovement, yMovement, zMovement, stepDown, feedrate)
         }
       }, {
         caption: "Cancel",
         cls: "js-dialog-close alert",
         onclick: function () {
         }
       }
      ]
    });
    
    // required for jest test
    if (process.env.JEST_WORKER_ID !== undefined)
      module.exports = generateGCode;
    
     
    sharmstr and Peter Van Der Walt like this.
  13. aixpower

    aixpower New
    Builder

    Joined:
    Jun 27, 2022
    Messages:
    14
    Likes Received:
    11
    I have created another small macro...

    Description
    This macro generates a sequence of G-Codes to mill a (vertical) hole into some stock at 0/0/0 (x/y/z). The generated code will replace the one in the G-Code editor, and triggers the parsing and visualization of the code in the 3D viewer.

    Usage
    When starting the macro it opens up a dialog to enter the milling parameters:
    [​IMG]
    Hole and Endmill diameter
    You need to specify the diameter of the endmill being used, as well as the hole diameter.

    Note: If the hole diameter is smaller than the endmill diameter, then the macro will not generate / update any G-Code!

    If the hole diameter is equal to the endmill diameter, then the operation is equal to a drilling operation (i.e. moving the endmill vertical in z direction only). Otherwise, the complete hole body will be milled.

    Note: Depending on your machine setup and material, the resulting hole might be smaller or even larger than the target. In this case adjust the target diameter by the messured difference.

    Receipe
    There are two base receipe implemented by which the area is being milled:
    • circle: by milling in concentric growing circles.
      [​IMG]
    • spiral: by milling in a (approximated) spiral.
      [​IMG]
    Each has two additional options which steers the milling direction:

    • cw: clock-wise milling (usually against the rotation of the endmill), aka conventional milling
    • ccw: counter-clock-wise milling (usually towards the endmill rotation), aha climb milling
    The milling direction can be defined for the rough cutting, as well for the finishing process if not disabled. As a result the receipes are following the following naming convention:

    <milling method>-<rough cut direction>-<finishing direction>
    e.g.
    spiral-ccw-cw

    Cutting depth
    Defines how deep to mill the pocket / hole into the material. Usually, this is being dome in multiple layers (see DOC).

    DOC (depth of cut)
    Milling is being done in layers. The DOC defines how deep the endmill mills in each layer (might be lower for the final layer). This depth is expressed as a percentage of the endmill diameter. U

    WOC (width of cut)
    The layers being milled will cut the material based on the receipe selected. The WOC defined how deep the endmill mills into the material on the current depth layer. Again, this value is given as a percentage to the endmill diameter.

    Finish WOC
    If you prefer to split the milling operation into a rough cutting and finishing part, you cen defined the WOC used for the very last circle being milled. A value of 0 disbles the finishing loop.

    Feedrate
    The maximum feedrate used during the milling operation.

    Installation

    The latest source can be found on gitbub: https://raw.githubusercontent.com/p...ing-macro/master/src/ob-hole-milling-macro.js

    Code:
    // mills a spiral starting at 0/0/${z}, which needs to be the current position
    function millSpiral(diam, ccw, stepWidth, g, xyFeed) {
      function f(x) { return Number(x).toFixed(6).replace(/\.?0+$/, '') }
      const d = stepWidth / 4
    
      var step = 0
      var pCurr = { x: 0, y: 0 }
      var pTarget = { x: 0, y: 0 }
      // target radius
      var rTarget = 0
      g(`; diam=${diam} stepWidth=${stepWidth}`)
      var state = 0
      var remainingClosingCount = 5
      while (rTarget < diam || remainingClosingCount) {
       step++
       rTarget = (step > 1 ? step - 0.5 : step) * d;
       if (rTarget > diam) rTarget = diam
       var xyTarget = step * d;
       if (xyTarget > diam) xyTarget = diam
       switch (state) {
         case 0: pTarget.x = xyTarget * -1; pTarget.y = 0; break;  // arc top left
         case 1: pTarget.x = 0; pTarget.y = xyTarget * -1; break;  // arc bottom left
         case 2: pTarget.x = xyTarget; pTarget.y = 0; break; // arc bottom right
         case 3: pTarget.x = 0; pTarget.y = xyTarget; break; // arc top right
       }
       // idea to determine the center point of the new circle catched from
       // https://math.stackexchange.com/questions/1781438/finding-the-center-of-a-circle-given-two-points-and-a-radius-algebraically
       //
       // distance to center of rhombus
       const xa = 1 / 2 * (pTarget.x - pCurr.x)
       const ya = 1 / 2 * (pTarget.y - pCurr.y)
       // center of rhombus
       const p0 = { x: pCurr.x + xa, y: pCurr.y + ya }
       // half lenth of diagonales of rhombus
       const a = Math.sqrt(xa * xa + ya * ya)
       const b = Math.sqrt(rTarget * rTarget - a * a)
       // center of circle
       const pCenter = { x: p0.x + (ccw ? -1 : 1) * ((b * ya) / a), y: p0.y + (ccw ? 1 : -1) * ((b * xa) / a) }
       g(`;--${pCurr}, ${pTarget}, ${a}, ${b}, ${pCenter}`)
       g(`G${ccw ? 3 : 2} F${xyFeed} X${f(pTarget.x)} Y${f(pTarget.y)} I${f(pCenter.x - pCurr.x)} J${f(pCenter.y - pCurr.y)}`)
    
       pCurr.x = pTarget.x
       pCurr.y = pTarget.y
       state += ccw ? 1 : -1
       if (state < 0) state = 3
       if (state > 3) state = 0
    
       if (rTarget >= diam)
         remainingClosingCount--
      }
    }
    
    // mills a set of circles starting at 0/0/${z}, which needs to be the current position
    function millCircles(diam, ccw, stepWidth, g, xyFeed) {
      var x = 0
      while (x < diam) {
       x += stepWidth
       if (x > diam) x = diam
       g(`G1 F${xyFeed} X${x} Y0; mill right to circle radius`)
       if (x > 0) {
         if (ccw) {
           g(`G3 F${xyFeed} X-${x} Y0 I-${x} J0; 1st half circle`)
           g(`G3 F${xyFeed} X${x} Y0 I${x} J0; 2nd half circle`)
         } else {
           g(`G2 F${xyFeed} X-${x} Y0 I-${x} J0; 1st half circle`)
           g(`G2 F${xyFeed} X${x} Y0 I${x} J0; 2nd half circle`)
         }
       }
       if (x >= diam) break
      }
    }
    
    function generateGCode(holeDiam, endmillDiam, zMovement, doc, woc, wocFinish, feedrate, receipe) {
      if (holeDiam < endmillDiam) { console.log("holeDiam < endmillDiam"); return }
      if (zMovement <= 0) { console.log("zMovement <=0"); return }
      if (doc < 10) { console.log("doc < 10"); return }
      if (doc > 200) { console.log("doc > 200"); return }
      if (woc < 5) { console.log("woc < 5"); return }
      if (woc > 30) { console.log("woc > 30"); return }
      if (wocFinish < 0) wocFinish = 0
    
      var x0 = 0
      var y0 = 0
      var z0 = 0
      var zSafe = 7
    
      var x = x0
      var y = y0
      var z = z0
      var xyFeed = feedrate
      var zFeed = feedrate / 2
      var xStep = endmillDiam * woc / 100;
      var finishOffset = endmillDiam * wocFinish / 100;
      var xMax = (holeDiam - endmillDiam - finishOffset) / 2
      if (xMax < 0) xMax = 0
      var xMax2 = (holeDiam - endmillDiam) / 2
      var stepDown = endmillDiam * doc / 100;
      const docFinish = 200
      const ccw = (() => {
       if (/^\w*-ccw/.test(receipe)) return 1
       else if (/^\w*-cw/.test(receipe)) return 0
       else return 1
      })()
      var gCode = ''
      function g(str) { gCode += str + '\n' }
    
      g(`; GCODE Generated by ob-hole-milling-macro on ${new Date().toISOString()}`)
      g(`; ${holeDiam}mm hole milling at 0/0/0 downto ${zMovement}`)
      g(`; endmill diameter=${endmillDiam}mm, DOC=${doc}%/${stepDown}mm, WOC=${woc}%/${xStep}mm`)
      g('G21; mm-mode')
      g('G54; Work Coordinates')
      g('G90; Absolute Positioning')
      g('M3 S1000; Spindle On')
      g('')
      g('; Begin of hole milling loop')
      g(`; Endmill Diameter: ${endmillDiam}`)
      g(`G0 Z${z0 + zSafe}; move to z-safe height`)
      g(`G0 F1000 X${x} Y${y}; move to x/y startpoint`)
      g('')
    
    
      // rough cut
      while (z > -zMovement) {
       z -= stepDown
       if (z < -zMovement) z = -zMovement
       g(`; layer ${z}`)
       g(`G1 F${zFeed} Z${z}; step down on current position`)
    
       if (xMax > 0) {
         if (/^circle/.test(receipe)) {
           millCircles(xMax, ccw, xStep, g, xyFeed)
         } else if (/^spiral/.test(receipe)) {
           millSpiral(xMax, ccw, xStep, g, xyFeed)
         } else {
           console.log(`unknown receipe:${receipe}`)
           return
         }
         x = 0
         g(`G1 F${xyFeed} X${x} Y0; move back to center`)
       }
      }
    
      // if we are not only drilling and not having a wocFinish of 0, then add a finishing cut
      if (xMax2 > xMax) {
       g('')
       g(`;--- finishing cut with DOC=${docFinish}%, WOC=${wocFinish}%`)
       g(`G0 Z${z0 + zSafe}; move to z-safe height`)
       g(`G0 F1000 X0 Y0 Z0; move up to zeropoint`)
       z = z0
       stepDown = endmillDiam * docFinish / 100;
       const ccw = (() => {
         if (/^\w*-\w*-ccw/.test(receipe)) return 1
         else if (/^\w*-\w*-cw/.test(receipe)) return 0
         else if (/^\w*-ccw/.test(receipe)) return 1
         else if (/^\w*-cw/.test(receipe)) return 0
         else return 1
       })()
       //g(`G1 F${zFeed} Z${z}; go back to z0`)
       while (z > -zMovement) {
         // step down
         z -= stepDown
         if (z < -zMovement) z = -zMovement
         g(`; layer ${z}`)
         g(`G1 F${zFeed} Z${z}; step down current position`)
    
         millCircles(xMax2, ccw, xMax2, g, xyFeed / 2)
         g(`G1 F${xyFeed} X${x} Y0; move to center`)
       }
      }
      // move tool back to save z
      g('')
      g('; End of hole milling loop')
      if (x != x0 || y != y0)
       g(`G1 F${xyFeed} X${x0} Y${y0}; move to center of hole`)
      g(`G0 F1000 Z${z0 + zSafe}; retracting back to z-safe`)
      g('')
      g('M5 S0; Spindle Off')
      g('; Job complete')
    
      // replace code in G-Code editor
      editor.session.setValue(gCode);
    
      // refresh 3D view
      parseGcodeInWebWorker(editor.getValue())
    
      // not required for the macro but for testing
      return gCode;
    }
    
    function genInputHtml(label, id, value, icon, descr, append = "") {
      const descrHtml = descr ? ` <small><i>(${descr})</i></small>` : ""
      var html = ''
      html += '<div class="row mb-1">\n'
      html += `  <label class= "cell-sm-8" > ${label}${descrHtml}</label >\n`
      html += '  <div class="cell-sm-4">\n'
      html += `    <input id="${id}" type="number" value="${value}" data-role="input" data-append="${append}" data-prepend="<i class='fas ${icon}'></i>" data-clear-button="false">\n`
      html += '  </div>\n'
      html += '</div>\n'
      return html
    }
    function genSelectHtml(label, id, options, selected, descr = '', opt = '') {
      const descrHtml = descr ? ` <small><i>(${descr})</i></small>` : ""
      var html = ''
      html += '<div class="row mb-1">\n'
      html += `  <label class="cell-sm-8">${label}${descrHtml}</label>\n`
      html += '  <div class="cell-sm-4">\n'
      html += `    <select id="${id}" data-role="select" ${opt}>\n`
      html += options.map(o => `      <option value="${o}"${o == selected ? ' selected="selected"' : ''}>${o}</option>\n`).join('')
      html += '    </select>\n'
      html += '  </div>\n'
      html += '</div>\n'
      return html
    }
    
    var prefs = {
      holeDiam: 8,
      endmillDiam: 4,
      holeDepth: 1,
      doc: 100,
      woc: 20,
      wocFinish: 2,
      feedrate: 500,
      receipe: "spiral-ccw-ccw"
    }
    
    function loadPrefs() { if (window.tmp_prefs_macro_hole_milling) prefs = window.tmp_prefs_macro_hole_milling }
    function savePrefs() { window.tmp_prefs_macro_hole_milling = prefs }
    loadPrefs();
    
    // Dialog creation
    Metro.dialog.create({
      title: 'Hole Milling',
      content:
       genInputHtml('Hole diameter', 'holeDiam', prefs.holeDiam, 'fa-circle', '', 'mm') +
       genInputHtml('Endmill diameter', 'endmillDiam', prefs.endmillDiam, 'fa-circle', '', 'mm') +
       genSelectHtml('Receipe', 'receipe', ['circle-cw-cw', 'circle-cw-ccw', 'circle-ccw-cw', 'circle-ccw-ccw',
         'spiral-cw-cw', 'spiral-cw-ccw', 'spiral-ccw-cw', 'spiral-ccw-ccw',], prefs.receipe, 'used to remove the material') +
       genInputHtml('Cutting depth', 'zMovement', prefs.holeDepth, 'fa-ruler', '', 'mm') +
       genInputHtml('DOC', 'doc', prefs.doc, 'fa-align-justify', 'depth of cut (10% - 200% of endmill diameter)', "%") +
       genInputHtml('WOC', 'woc', prefs.woc, 'fa-align-justify', 'width of cut (5% - 30% of endmill diameter)', "%") +
       genInputHtml('Finish WOC', 'wocFinish', prefs.wocFinish, 'fa-align-justify', 'width of cut for finish path (0 disables it)', "%") +
       genInputHtml('Feedrate', 'feedrate', prefs.feedrate, 'fa-running', 'How fast to move the endmill in milling operation', 'mm/min') +
       '',
      width: 700,
      actions: [
       {
         caption: "Generate G-Code",
         cls: "js-dialog-close success",
         onclick: function () {
           prefs.holeDiam = parseFloat($("#holeDiam").val())
           prefs.endmillDiam = parseFloat($("#endmillDiam").val())
           prefs.holeDepth = parseFloat($("#zMovement").val())
           prefs.doc = parseFloat($("#doc").val())
           prefs.woc = parseFloat($("#woc").val())
           prefs.wocFinish = parseFloat($("#wocFinish").val())
           prefs.feedrate = parseInt($("#feedrate").val())
           prefs.receipe = $('#receipe').val();
           const gCode = generateGCode(prefs.holeDiam, prefs.endmillDiam, prefs.holeDepth, prefs.doc, prefs.woc, prefs.wocFinish, prefs.feedrate, prefs.receipe)
           if (gCode)
             savePrefs();
         }
       }, {
         caption: "Cancel",
         cls: "js-dialog-close alert",
         onclick: function () {
         }
       }
      ]
    });
    
    // required for jest test
    try { module.exports = generateGCode; } catch (e) { }
    
    
    [​IMG]
     
  14. Misterg

    Misterg Veteran
    Staff Member Moderator Builder Resident Builder

    Joined:
    Aug 27, 2022
    Messages:
    372
    Likes Received:
    288
    I've been looking at this for the last few days and have come to the conclusion that tool length offset is not viable for this use in grbl. It simply isn't persistent enough: It gets cancelled without warning for all sorts of reasons (e.g. grbl alarm conditions, or aborting a program - I *think* that this is a feature of grbl rather than OBControl because it seems to do the same in UGS).

    I have come up with the following macro that, I think, achieves pretty much the same thing by adjusting the WCS Z offset in response to the results of probing tools.

    The obvious limitation that comes with this method is that it is only valid in one WCS (whereas TLO would carry through to every WCS), so if you are machining at multiple stations using multiple WCS, then this isn't for you.

    The macro adds a 'Tool Length' button to the menu that opens the dialog below:

    OBC tool length dialog.png

    This allows you to use one tool as a reference and then to set the Z offset of the WCS to maintain the same tool height through tool changes.

    It uses the GCODE preset locations G30 for the probe position and G28 for a preset tool change position (hence the machine needs to be homed) and these locations need to be set before using the macro (**IMPORTANT!**). Using these locations has the advantage that if you want to adjust (e.g.) your probe position, you can just jog the machine to where you want it to start and send 'G30.1' (or G28.1 for the tool change location) and it's done without needing to edit any code. If you already use G28 for something, then just ignore the 'Go To Tool Change' button.

    After use, it gives the option of returning the tool to the location it was in when the dialog was opened - use with caution!

    Any changes in Z zero between tool changes are carried through to the new tool.

    There are some customisable settings near the start of the file to save you from going digging for probe speeds, etc.

    I'd be interested to hear if anyone tests it - I've tested it on my little CNC router (running grbl_ESP32) and it **seems** to work well, but I would regard it as un-proven at the moment. It does fall over if the probe is in contact when it shouldn't be as GRBL throws an error, but if you close the dialog and open it again, it pulls its pants back up without any consequences, as far as I can tell. I'm not a programmer and definitely not a javascript programmer, so there may be gaffes in the code, but it seems to work OK.


    [Edit] I have updated the code below (V1.1) in response to the issue spotted by David the swarfer

    Posted in good faith, but use at your own risk! :thumbsup:

    Andy

    [Edit 2] V1.3 -11th April 23: I've updated the macro to check the 'homedRecently' status and that the spindle isn't running, etc.
    The homedRecently status shows that the machine needs homing after every error (e.g. syntax error in gcode file, attempting to jog beyond the machine limits, etc.) even though the position is reliable. Because of this, I've given the option to carry on regardless, and also the option to disable the check in the macro (set tc_ignorehomestatus to "yes").
    I've also corrected the reporting where the WCS has been changed but the tool offset hasn't, tidied up the formatting, and set the default values for the probing cycle to be 'lower and slower'.
    Also, the macro can be re-run now to update any changes to the settings without it trying to install another menu button.

    [Edit 3] V2.2 added 17th September 23: Revision to avoid causing 'error 9' under GRBLhal. Also changed the default to ignore the homed status (set 'tc_ignorehomestatus' to "no" to restore this) and added a check on Z height to avoid errors caused by trying to raise the Z axis above machine zero when attempting to return to the original location with a longer tool installed. Also added a zipped .JSON file that can be unpacked and imported directly into Control.

    [Edit 4] V2.3 added 10th August 24: Only change is that the toolbar button now works in dark mode (thanks to @sharmstr ).

    Discussion here: Tech support for Javascript Macros in CONTROL

    Known issue: If the probe isn't in the expected state before probing, or the requested probe travel is > machine travel these generate hard errors that my macro can't trap. If they happen, you will need to close the dialog and re-open it. Similarly if the requested probe travel exceeds the machine travel - take care not to set G30 too low.

    It isn't possible to pause a program to use this macro, as (I think) the comms port isn't released while the program is paused, so each tool will need its separate file.
     

    Attached Files:

    #14 Misterg, Feb 28, 2023
    Last edited: Aug 10, 2024
  15. Misterg

    Misterg Veteran
    Staff Member Moderator Builder Resident Builder

    Joined:
    Aug 27, 2022
    Messages:
    372
    Likes Received:
    288
    LASER engraving test pattern generator

    I have recently acquired a diode LASER to fit to my CNC router. I couldn't find anything like this easily available (outside Lightburn).

    Running the macro generates the GCode to produce a matrix of test patches with a range of power and feed rate settings to determine the optimum parameters for LASER marking.

    Laser macro.png

    Each row has a different power level, and each column has a different feed rate.

    IMG_2642.jpg

    The parameters can be changed by editing the first few lines of the macro:

    Resolution (number of lines per mm);
    Maximum and minimum feed rates and power levels;
    Number of rows / columns;
    The height and width of each patch, and the size of the gap between them;
    Machine acceleration.

    Power settings use splindle speed units (grbl default is 0-1000, I think). The value in $30 for the maximum speed is what you need to put into the macro to get 100% power.

    The labels are engraved at the half way point of both power and speed settings.

    The label text is fixed at 3mm high which will mean that the labels overwrite each other at patch sizes less than about 3mm.

    Acceleration is used to compute the over-travel needed to maintain a constant speed over the engraved patches. Set x_acceleration to zero to disable this, but this will make the speeds on the right hand columns inaccurate unless speeds are very slow / accelerations are very fast.

    Hopefully this will be self explanatory :)

    When the macro is run, any loaded GCode will be replaced by the output of the macro. The GCode can be run or saved as required.

    The code uses 'M4' to control the LASER which requires Laser Mode to be enabled in grbl ($32=1).

    I've tested this with my machine and my LASER, but please use with caution, and at your own risk.

    Any problems, please reply to my post on the support thread:

    Tech support for Javascript Macros in CONTROL

    (Feedback also appreciated :) ).

    Code:
    // LASER Power/Speed Raster Test Grid Macro For OpenBuilds Control
    // V 1.2
    // Change these values to customise basic operation:
    var feed_Max = 3500;        // Maximum feed rate (mm/min): Must be less than the max X speed - $110 value in GRBL
    var feed_Min = 500;            // Minimum feed rate (mm/min)
    var feed_Columns = 7;        // Number of feedrate columns to draw
    var power_Max = 1000;        // Maximum power (spindle speed units): Check value of $30 in GRBL settings for maximum power
    var power_Min = 100;        // Minimum power (spindle speed units)
    var power_Rows = 10;        // Number of power rows to draw
    var patch_Width = 5;        // Width of each test patch (mm)
    var patch_Height = 4;        // Height of each test patch (mm)
    var patch_Border = 1;         // Space between test patches (mm)
    var lines_mm = 8;            // Resolution of test patch (lines / mm)
    var x_acceleration = 320;    // Machine X acceleration value (mm/s^2): Used to calculate over-travel
                                // Check $120 value in GRBL for machine settings. Set to zero to disable over-travel
    // Generates GCode to produce a raster pattern grid with varying speeds and powers
    // Running this macro will replace any existing GCode in Control
    // The GCode uses M4 to control the LASER and so needs LASER mode enabled ($32 = 1 in GRBL settings)
    // 
    // Use at own risk!
    // DAG 19/4/23
    //
    // V 1.1 20/4/23 - corrected label power & feed calculation
    // V 1.2 23/4/23 - added over-travel to avoid speed errors due to acceleration
    //
    // Macro continues below...
    var yStep = 1/lines_mm;                                    // Calculate raster line spacing
    var patchLines = (patch_Height * lines_mm).toFixed();    // Calculate the number of lines needed for each patch
    var rowCount;                                            // Row reference
    var colCount;                                            // Column reference
    var lineCount;                                            // Raster line within each patch
    var labelPower = ((power_Max + power_Min)/2);            // Power level for labels
    var labelFeed = ((feed_Max + feed_Min)/2);                // Feed rate for labels
    var margin = ((''+power_Max).length*3)+1;                // Space at left for labels - text is ~3mm per character
    var over_right = 0;                                        // Distance required to decelerate from max feed
    var over_left = 0;                                        // Distance required to accelerate to min feed
    if (x_acceleration > 0){                                // Calculate over-travel if X axis acceleration is set
        over_right = (feed_Max / x_acceleration);
        over_left = (feed_Min / x_acceleration);
        }
    // Start building the GCode string that will be the eventual output
    var gcode = "; Laser Raster Test Pattern\n;\n";
    gcode += "; " + power_Rows + " Power levels from " + power_Min + " to " + power_Max + " (spindle speed units)\n";
    gcode += "; " + feed_Columns + " Feed rates from " + feed_Min + " to " + feed_Max + " mm/min\n";
    gcode += "; Patch size " + patch_Width + "mm x " + patch_Height + "mm\n";
    gcode += "; Generated by Openbuilds Control macro V1.2 by Misterg\n;\n";
    gcode += `G21; mm-mode
    G90; Absolute Positioning
    G0 X0 Y0; Move to origin position
    M04 S0; Laser on
    `
    // Add column labels
    for (colCount =0; colCount <feed_Columns; colCount += 1) {
               
        var pSpeed = (feed_Min +(feed_Max - feed_Min)/(feed_Columns-1) * colCount).toFixed();
        var gcXval = margin + (patch_Border + patch_Width) * colCount +1;
        var gcYval = (patch_Border + patch_Height) * (power_Rows);
        var speedString = "" + pSpeed;
        var strlen = speedString.length;
       
        gcode += "S" + labelPower.toFixed() + " F" + labelFeed.toFixed() + "\n";
       
        for (var i=0; i<(strlen); i++){
           
            gcode += "G0 X" + gcXval +" Y"+(gcYval+ i*3.5)+"\n";
            gcode += "G91\n" + getGcode( speedString.substring(strlen-i-1, strlen-i)) +"G90\n";
            }
        }
    // Generate the pattern
    for (rowCount = 0; rowCount < power_Rows; rowCount += 1) {
    //    var pPower = (power_Max - (power_Max - power_Min)/(power_Rows-1) *rowCount).toFixed();     // Power level for this row (descending)
        var pPower = ((power_Max - power_Min)/(power_Rows-1) *rowCount + power_Min).toFixed();     // Power level for this row (ascending)
        var pwrString = "" + pPower;                                                            // Convert to string for label
        gcode += "S" + labelPower.toFixed() + " F" + labelFeed.toFixed() + "\n";
        var row_Y = (patch_Border+ patch_Height)*rowCount;                                        // Y coordinate of current row
       
        // Generate row labels
       
        for (var i=0; i < (pwrString.length); i++){
           
            gcode += "G0 X" + (i*3) +" Y"+row_Y + "\n";
            gcode += "G91\n" + getGcode( pwrString.substring(i, i+1)) +"G90\n";
            }
           
        gcode += "G0 X" + (margin - over_left) +" Y" + row_Y +"\n";                                    // Move to starting point for pattern
       
        // Generate raster pattern
       
        for (lineCount =0; lineCount < patchLines; lineCount += 1){
           
            var isOdd = (lineCount & 1);                                                        //Flag to run passes in alternate directions
            for (colCount =0; colCount <feed_Columns; colCount += 1) {
               
                var wkgCol = colCount;
                if (isOdd == 1) {wkgCol = feed_Columns - colCount-1};
           
                var pSpeed = (feed_Min +(feed_Max - feed_Min)/(feed_Columns-1) *wkgCol).toFixed(); // Speed for this column
               
                var gcXval = margin + (patch_Border + patch_Width) * wkgCol;                   
                var gcYval = row_Y + (yStep * lineCount);
               
                gcode += `G0 X` + (gcXval + patch_Width * isOdd) + ` Y` + gcYval +`\n`;
                gcode += `G1 X` + (gcXval + patch_Width * (isOdd ^ 1)) + ` F` + pSpeed + ` S` + pPower + `\n`;
            }
        gcode += "G91\n G0 X"+ (isOdd * over_left * -1 )+((isOdd^1) * over_right) + "\nG90\n";    // Add over-travel
        }   
    }
           
    // Tidy up the end of the GCode and pass to OB COntrol
    gcode += `M5 S0\n`;
    gcode += `M2\n`;
    editor.session.setValue(gcode);
    parseGcodeInWebWorker(gcode)
    printLog("<span class='fg-red'>[ Laser Test Pattern ] </span><span class='fg-green'>GCODE Loaded</span>")
    // The End
    function getGcode (numeral){        // Returns GCode string representing numbers 0 - 9
                                        // No error checking!
    const gc_num = [];
    gc_num[0] = `;zero
    G0 X0.857 Y3
    G1 X0.286
    X0.428 Y-0.143
    X0.286 Y-0.428
    X0.143 Y-0.715
    Y-0.428
    X-0.143 Y-0.715
    X-0.286 Y-0.428
    X-0.428 Y-0.143
    X-0.286
    X-0.428 Y0.143
    X-0.286 Y0.428
    X-0.143 Y0.715
    Y0.428
    X0.143 Y0.715
    X0.286 Y0.428
    X0.428 Y0.143
    `;
    gc_num[1] = `;one
    G0 Y2.429
    G1 X0.286 Y0.142
    X0.428 Y0.429
    Y-3
    `;
    gc_num[2] = `;two
    G0 X0.143 Y2.286
    G1 Y0.143
    X0.143 Y0.285
    X0.143 Y0.143
    X0.285 Y0.143
    X0.572
    X0.285 Y-0.143
    X0.143 Y-0.143
    X0.143 Y-0.285
    Y-0.286
    X-0.143 Y-0.286
    X-0.285 Y-0.428
    X-1.429 Y-1.429
    X2
    `;
    gc_num[3] = `;three
    G0 X0.286 Y3
    G1 X1.571
    X-0.857 Y-1.143
    X0.429
    X0.285 Y-0.143
    X0.143 Y-0.143
    X0.143 Y-0.428
    Y-0.286
    X-0.143 Y-0.428
    X-0.286 Y-0.286
    X-0.428 Y-0.143
    X-0.429
    X-0.428 Y0.143
    X-0.143 Y0.143
    X-0.143 Y0.285
    `;
    gc_num[4] = `;four
    G0 X2.143 Y1
    G1 X-2.143
    X1.429 Y2
    Y-3
    `;
    gc_num[5] = `;five
    G0 X1.714 Y3
    G1 X-1.428
    X-0.143 Y-1.286
    X0.143 Y0.143
    X0.428 Y0.143
    X0.429
    X0.428 Y-0.143
    X0.286 Y-0.286
    X0.143 Y-0.428
    Y-0.286
    X-0.143 Y-0.428
    X-0.286 Y-0.286
    X-0.428 Y-0.143
    X-0.429
    X-0.428 Y0.143
    X-0.143 Y0.143
    X-0.143 Y0.285
    `;
    gc_num[6] = `;six
    G0 Y1
    G1 X0.143 Y0.429
    X0.286 Y0.285
    X0.428 Y0.143
    X0.143
    X0.429 Y-0.143
    X0.285 Y-0.285
    X0.143 Y-0.429
    Y-0.143
    X-0.143 Y-0.428
    X-0.285 Y-0.286
    X-0.429 Y-0.143
    X-0.143
    X-0.428 Y0.143
    X-0.286 Y0.286
    X-0.143 Y0.571
    Y0.714
    X0.143 Y0.715
    X0.286 Y0.428
    X0.428 Y0.143
    X0.286
    X0.428 Y-0.143
    X0.143 Y-0.286
    `;
    gc_num[7] = `;seven
    G0 Y3
    G1 X2
    X-1.429 Y-3
    `;
    gc_num[8] = `;eight
    G0 X0.714 Y3
    G1 X-0.428 Y-0.143
    X-0.143 Y-0.286
    Y-0.285
    X0.143 Y-0.286
    X0.285 Y-0.143
    X0.572 Y-0.143
    X0.428 Y-0.143
    X0.286 Y-0.285
    X0.143 Y-0.286
    Y-0.429
    X-0.143 Y-0.285
    X-0.143 Y-0.143
    X-0.428 Y-0.143
    X-0.572
    X-0.428 Y0.143
    X-0.143 Y0.143
    X-0.143 Y0.285
    Y0.429
    X0.143 Y0.286
    X0.286 Y0.285
    X0.428 Y0.143
    X0.572 Y0.143
    X0.285 Y0.143
    X0.143 Y0.286
    Y0.285
    X-0.143 Y0.286
    X-0.428 Y0.143
    X-0.572
    `;
    gc_num[9] = `;nine
    G0 X1.857 Y2
    G1 X-0.143 Y-0.429
    X-0.285 Y-0.285
    X-0.429 Y-0.143
    X-0.143
    X-0.428 Y0.143
    X-0.286 Y0.285
    X-0.143 Y0.429
    Y0.143
    X0.143 Y0.428
    X0.286 Y0.286
    X0.428 Y0.143
    X0.143
    X0.429 Y-0.143
    X0.285 Y-0.286
    X0.143 Y-0.571
    Y-0.714
    X-0.143 Y-0.715
    X-0.285 Y-0.428
    X-0.429 Y-0.143
    X-0.286
    X-0.428 Y0.143
    X-0.143 Y0.286
    `;
    return gc_num[numeral];
    }
    
    20/4/23 - corrected the calculation for label power & speed calculations.
    22/4/23 - added photo of the default test pattern burned on plywood with a 4W LASER
    23/4/23 - Updated the code to add over-travel so that the end patches are cut at the indicated speeds






     
    #15 Misterg, Apr 19, 2023
    Last edited: Apr 23, 2023
  16. Misterg

    Misterg Veteran
    Staff Member Moderator Builder Resident Builder

    Joined:
    Aug 27, 2022
    Messages:
    372
    Likes Received:
    288
    Focus / Depth Of Field Test Pattern Generator

    This macro generates the GCode for a test pattern that varies in Z height around the current Z0 to act as an aid in checking / setting LASER focus

    IMG_2636.jpg

    (For example, the best focus in the image above is at about Z= +0.5.)

    Parameters can be changed by altering the variables at the start of the macro:

    The range of movement from the current Z0 - note that this is +/- , so a value of 3(say) gives a range of Z-3 to Z+3;
    The power and speed for the pattern (power is a percentage of the spindle units);
    The number and spacing of the line pattern - playing with the spacing can really highlight the linewidth broadening when the beam is out of focus.

    The GCode generates travel that extends beyond the edge of the pattern to allow the machine to accelerate to the programmed speed. By default, this means that the head will move into negative X coordinates (to the left of X0) to start the pattern at X0. If this causes a problem, set 'xplus_Only' to "yes" - the pattern will be shifted to the right to allow enough travel for the machine to accelerate.

    Set 'x_acceleration' to the actual machine X acceleration ($120) for the best results, or to zero to disable the over-travel.

    As ever, use at your own risk.

    Queries / feedback:

    Tech support for Javascript Macros in CONTROL

    Code:
    // LASER Focus Pattern Generator Macro For OpenBuilds Control
    // V0.9
    // Change these values to customise basic operation:
    
    const z_Range = 3;            // Z movement each side of current position (mm)
    
    const power_Percent = 80;    // Engraving power (0-100%)
    const engrave_Speed = 2000;    // Engraving speed (mm/min)
    const num_Lines = 8;        // Number of lines in test pattern (must be >0)
    const line_Spacing = 0.3;    // Spacing between lines (mm)
    
    const power_Max = 1000;        // Maximum power in spindle speed units - Check value of $30 in GRBL settings
    const power_Min = 0;        // Minimum power in spindle speed units - Check value of $31 in GRBL settings
    const x_acceleration = 320;    // Machine X acceleration value (mm/s^2)- Check value of $120 n GRBL settings
                                // (Used to calculate over-travel - Set to zero to disable over-travel)
    
    const xplus_Only = "no";    // "no" = move to left of X0 to allow acceleration
                                // "yes" = start acceleration from X0 - pattern will be shifted to the right
    const tilt = 0.12;            //     Slope of test pattern (mm/mm)                     
    //
    // Generates GCode to produce a pattern with varying Z height to assist with LASER focussing
    // Running this macro will replace any existing GCode in Control
    // The GCode uses M4 to control the LASER and so needs LASER mode enabled ($32 = 1 in GRBL settings)
    //
    // Use at own risk!
    // DAG 23/4/23
    //
    // Macro continues below...
    
    var p_Power = ((power_Max - power_Min) * power_Percent / 100 + power_Min).toFixed();// Calculate engraving power in spindle speed units
    var over_Travel = 0;                                                                // Distance required to accelerate to engraving speed
    if (x_acceleration > 0){ over_Travel = (engrave_Speed / x_acceleration)};            // Calculate over-travel if X axis acceleration is set
    
    var line_Length = (z_Range * 2) / tilt;                                                // Calculate pattern length
    var start_X = 0;
    if (xplus_Only != "yes" ) {start_X = -over_Travel};                                    // Starting point for X travel - NOT engraving
    
    var z_Min = z_Range * (-1);
    var z_Max = z_Range;
    
    
    // Start building the GCode string that will be the eventual output
    
    var gcode = "; Laser Focus Test Pattern\n;\n";
    gcode += "; Z Range +/- " + z_Range + "mm\n";
    gcode += "; Generated by Openbuilds Control macro V0.9 by Misterg\n;\n";
    gcode += "G21; mm-mode\nG90; Absolute Positioning\n";
    gcode += "F"+ engrave_Speed +" ; Set engraving speed\n";
    gcode += "M04 S" + p_Power + " ; Laser on at set power\n";
    
    // Draw lines
    
    for (var lineCount =0; lineCount < num_Lines; lineCount ++){
        var gcYval = (line_Spacing * lineCount);                                // Current Y position
      
        gcode += "G0 X" + start_X +" Y" + gcYval +" Z"+ z_Min + "\n";            // Rapid to start of row
        gcode += "G91\nG0 X" + over_Travel + "\n";                                // Rapid to start of line (relative)
        gcode += "G1 X" + line_Length + " Z" + (z_Range * 2) + "\n";            // Cut to end of line (relative)
        gcode += "G0 X" + over_Travel +"\nG90\n";                                // Rapid to end of deceleration zone (relative)
        }
    
    // Add Tick Marks
    gcode += "G0 Z0; Add tick marks\n";
    
    var major_Ticks = z_Range * 2;                    // 1 majortick per mm
    var minor_Ticks = 10;                            // 10 minor tick marks per mm
    var y_Pos = (line_Spacing * (num_Lines -1));    // Bottom of tick marks
    var gcXval = 0;                                    // Calculated position
    var line_Start = start_X + over_Travel;            // Start of labels
    var label_Z = z_Min;
    var z_MajorIncrement = (z_Max - z_Min)/(major_Ticks );
    var z_MinorIncrement = z_MajorIncrement / minor_Ticks;
    var x_Increment = line_Length / (major_Ticks * minor_Ticks);
    
    for( var i =0 ;i<= major_Ticks;i++){
        gcXval =  line_Start + line_Length * i / major_Ticks;
        label_Z = z_Min + z_MajorIncrement * i;
      
        gcode += "G0 X" + gcXval +" Y" + y_Pos +" Z" +label_Z + "\n";                // move to start of major tick
        gcode += "G91\nG1 Y4\n";                                                    // draw tick & stay in G91
      
        // add labels
      
        if (i < major_Ticks / 2){                                                    // Add +/- sign if needed
            gcode += ";minus\nG0 X2.571 Y1.5\nG1 X-2.571\nG0 X3 Y-1.5\n";
        }
        else if (i > major_Ticks /2){
            gcode += ";plus\nG0 X1.286 Y0.214\nG1 Y2.572\nG0 X1.285 Y-1.286\nG1 X-2.571\nG0 X3 Y-1.5\n";
        }
        gcode += getGcode( ""+ Math.abs(i-major_Ticks/2)) +"G90\n";                            // Get numeral & switch back to G90
      
        gcode += "G0 X" + gcXval +" Y" + y_Pos +" Z" +label_Z + "\n";                // move back to start of major tick
    
      
        if (i < major_Ticks){                                                            // Fill in minor ticks
            for( var j = 1; j <minor_Ticks; j++){
                gcode += "G91\nG0 X" + x_Increment + " Z" + z_MinorIncrement + "\n";    // Move to start of tick
                if (j == (minor_Ticks /2)) {
                    gcode += "\nG1 Y3\nG0 Y-3\n";                                        // Draw '5s' tick
                }
                else {
                    gcode += "\nG1 Y2\nG0 Y-2\n";                                        // Draw minor tick
                }; 
            }
        gcode += "G90\n";
        }
    }
          
    // Tidy up the end of the GCode and pass to OB COntrol
    
    gcode += "G0 X0 Y0 Z0\n";
    gcode += "M5 S0\nM2\n";                // LASER off & End of program
    
    editor.session.setValue(gcode);
    parseGcodeInWebWorker(gcode)
    printLog("<span class='fg-red'>[ Laser Test Pattern ] </span><span class='fg-green'>GCODE Loaded</span>")
    
    // The End
     
    function getGcode (numeral){        // Returns GCode string representing numbers 0 - 9
                                        // No error checking!
    const gc_num = [];
    
    gc_num[0] = `;zero
    G0 X0.857 Y3
    G1 X0.286
    X0.428 Y-0.143
    X0.286 Y-0.428
    X0.143 Y-0.715
    Y-0.428
    X-0.143 Y-0.715
    X-0.286 Y-0.428
    X-0.428 Y-0.143
    X-0.286
    X-0.428 Y0.143
    X-0.286 Y0.428
    X-0.143 Y0.715
    Y0.428
    X0.143 Y0.715
    X0.286 Y0.428
    X0.428 Y0.143
    `;
    
    gc_num[1] = `;one
    G0 Y2.429
    G1 X0.286 Y0.142
    X0.428 Y0.429
    Y-3
    `;
    
    gc_num[2] = `;two
    G0 X0.143 Y2.286
    G1 Y0.143
    X0.143 Y0.285
    X0.143 Y0.143
    X0.285 Y0.143
    X0.572
    X0.285 Y-0.143
    X0.143 Y-0.143
    X0.143 Y-0.285
    Y-0.286
    X-0.143 Y-0.286
    X-0.285 Y-0.428
    X-1.429 Y-1.429
    X2
    `;
    
    gc_num[3] = `;three
    G0 X0.286 Y3
    G1 X1.571
    X-0.857 Y-1.143
    X0.429
    X0.285 Y-0.143
    X0.143 Y-0.143
    X0.143 Y-0.428
    Y-0.286
    X-0.143 Y-0.428
    X-0.286 Y-0.286
    X-0.428 Y-0.143
    X-0.429
    X-0.428 Y0.143
    X-0.143 Y0.143
    X-0.143 Y0.285
    `;
    
    gc_num[4] = `;four
    G0 X2.143 Y1
    G1 X-2.143
    X1.429 Y2
    Y-3
    `;
    
    gc_num[5] = `;five
    G0 X1.714 Y3
    G1 X-1.428
    X-0.143 Y-1.286
    X0.143 Y0.143
    X0.428 Y0.143
    X0.429
    X0.428 Y-0.143
    X0.286 Y-0.286
    X0.143 Y-0.428
    Y-0.286
    X-0.143 Y-0.428
    X-0.286 Y-0.286
    X-0.428 Y-0.143
    X-0.429
    X-0.428 Y0.143
    X-0.143 Y0.143
    X-0.143 Y0.285
    `;
    
    gc_num[6] = `;six
    G0 Y1
    G1 X0.143 Y0.429
    X0.286 Y0.285
    X0.428 Y0.143
    X0.143
    X0.429 Y-0.143
    X0.285 Y-0.285
    X0.143 Y-0.429
    Y-0.143
    X-0.143 Y-0.428
    X-0.285 Y-0.286
    X-0.429 Y-0.143
    X-0.143
    X-0.428 Y0.143
    X-0.286 Y0.286
    X-0.143 Y0.571
    Y0.714
    X0.143 Y0.715
    X0.286 Y0.428
    X0.428 Y0.143
    X0.286
    X0.428 Y-0.143
    X0.143 Y-0.286
    `;
    
    gc_num[7] = `;seven
    G0 Y3
    G1 X2
    X-1.429 Y-3
    `;
    
    gc_num[8] = `;eight
    G0 X0.714 Y3
    G1 X-0.428 Y-0.143
    X-0.143 Y-0.286
    Y-0.285
    X0.143 Y-0.286
    X0.285 Y-0.143
    X0.572 Y-0.143
    X0.428 Y-0.143
    X0.286 Y-0.285
    X0.143 Y-0.286
    Y-0.429
    X-0.143 Y-0.285
    X-0.143 Y-0.143
    X-0.428 Y-0.143
    X-0.572
    X-0.428 Y0.143
    X-0.143 Y0.143
    X-0.143 Y0.285
    Y0.429
    X0.143 Y0.286
    X0.286 Y0.285
    X0.428 Y0.143
    X0.572 Y0.143
    X0.285 Y0.143
    X0.143 Y0.286
    Y0.285
    X-0.143 Y0.286
    X-0.428 Y0.143
    X-0.572
    `;
    
    gc_num[9] = `;nine
    G0 X1.857 Y2
    G1 X-0.143 Y-0.429
    X-0.285 Y-0.285
    X-0.429 Y-0.143
    X-0.143
    X-0.428 Y0.143
    X-0.286 Y0.285
    X-0.143 Y0.429
    Y0.143
    X0.143 Y0.428
    X0.286 Y0.286
    X0.428 Y0.143
    X0.143
    X0.429 Y-0.143
    X0.285 Y-0.286
    X0.143 Y-0.571
    Y-0.714
    X-0.143 Y-0.715
    X-0.285 Y-0.428
    X-0.429 Y-0.143
    X-0.286
    X-0.428 Y0.143
    X-0.143 Y0.286
    `;
    
    return gc_num[numeral];
    }
     
    #16 Misterg, Apr 23, 2023
    Last edited: Apr 23, 2023
  17. Ivo Beltchev

    Builder

    Joined:
    Apr 17, 2023
    Messages:
    32
    Likes Received:
    15
    I published a few useful macros on my github fork of the Control software
    OpenBuilds-CONTROL/UsefulMacros at master · ivomirb/OpenBuilds-CONTROL

    DisableZ.js – improvement of sharmstr’s macro that hides the Z buttons when large jog step is selected

    OpenFile.js – removes the dropdown from the Gcode button, and adds a new button to reload the last file. Requires the use of my fork, which includes the "reload" functionality

    StoreWcs/RestoreWcs.js – stores the work offset from the last job and can restore it in case it gets lost accidentally, most commonly due to user error

    SmartHome.js – prevents accidental homing in case the machine was recently homed. Works better with my fork, which doesn’t reset the homing status as aggressively as the official software

    SmartGCode.js – restores the rapid moves from the free version of Fusion 360, detects tool changes, and other useful features. It is intended to be used with my pendant software, however the code can be incorporated in other macros.
    Caution – modifying the Gcode is dangerous. I’ve had very little time to test it before I disassembled my machine for maintenance and upgrades. Use at your own risk, and keep an eye on the job until you are comfortable with the modified Gcode.
     
  18. Ivo Beltchev

    Builder

    Joined:
    Apr 17, 2023
    Messages:
    32
    Likes Received:
    15
    Disable the Z jog buttons when 100mm is selected or continuous jog is enabled

    This macro is based on the original script by sharmstr - OpenBuildsHacks/Macros/HideZBtns.js at main · sharmstr/OpenBuildsHacks

    But with a few improvements:
    • Instead of hiding the buttons it replaces them with blank ones, which preserves the UI layout
    • Intercepts and blocks the keyboard shortcuts for Z jogging
    • Hooks more keyboard actions that might affect the step size
    • The code is streamlined and less stateful
    upload_2023-11-5_21-48-11.png

    Set it to run on startup
    Code:
    // Disable the Z jog buttons when 100mm is selected or continuous jog is enabled
    // Based on the original script by sharmstr - https://github.com/sharmstr/OpenBuildsHacks/blob/main/Macros/HideZBtns.js
    // But with a few improvements:
    //   Instead of hiding the buttons it replaces them with blank ones, which preserves the UI layout
    //   Intercepts and blocks the keyboard shortcuts for Z jogging
    //   Hooks more keyboard actions that might affect the step size
    //   The code is streamlined and less stateful
    
    $(document).ready(function()
    {
        $('#zM').before('<button class="button light square xlarge m-0 ml-2" id="zM2" />');
        $('#zP').before('<button class="button light square xlarge m-0 ml-2" id="zP2" />');
        $('#zM2,#zP2').hide();
    
        var zHidden = false;
    
        function updateZBtns()
        {
            var jog100 = $('#dist100').hasClass('bd-openbuilds');
            var contiguous = $('#jogTypeContinuous').is(":checked");
            zHidden = jog100 || contiguous;
    
            if (zHidden)
            {
                $('#zM,#zP').hide();
                $('#zM2,#zP2').show();
            }
            else
            {
                $('#zM,#zP').show();
                $('#zM2,#zP2').hide();
            }
        }
    
        // list of shortcuts that might affect the step size
        var captureShortcuts = ["stepP", "stepM", "incJogMode", "conJogMode"];
        for (var i = 0; i < captureShortcuts.length; i++)
        {
            var shortcut = keyboardShortcuts[captureShortcuts[i]];
            if (shortcut.length)
            {
                $(document).bind('keydown', shortcut, function(e) { updateZBtns(); });
            }
        }
    
        // intercept the Z+ and Z- shortcut keys if necessary
        document.addEventListener('keydown', (event) => {
            if (zHidden)
            {
                var keyName = event.key.toLowerCase();
                if (keyName == keyboardShortcuts.zP || keyName == keyboardShortcuts.zM)
                {
                    event.preventDefault();
                    event.stopImmediatePropagation();
                }
            }
        }, {capture : true});
    
        $('#dist01,#dist1,#dist10,#dist100,#jogTypeContinuous').on('click', updateZBtns);
    
        updateZBtns();
    });
    
     
    #18 Ivo Beltchev, Nov 6, 2023
    Last edited: Nov 6, 2023
    sharmstr and Peter Van Der Walt like this.
  19. Ivo Beltchev

    Builder

    Joined:
    Apr 17, 2023
    Messages:
    32
    Likes Received:
    15
    Store/Restore WCS offset

    This is a pair of macros that protect from accidental loss of the WCS offset. Imagine you run multiple programs that need to use the same offset. Then as you are changing tools you accidentally click Set Zero instead of Goto Zero. The WCS offset is lost. Can’t count how many times it has happened to me.

    The StoreWcs macro runs on startup and records the WCS offset after a job finishes.
    Code:
    // Stores the WCS offset after a job. It can later be restored by RestoreWCS.js
    
    
    $(document).ready(function()
    {
        window.LastWcs = undefined;
    
        socket.on('jobComplete', function(data)
        {
            if (data.jobStartTime)
            {
                window.LastWcs = {
                    X : laststatus.machine.position.offset.x,
                    Y : laststatus.machine.position.offset.y,
                    Z : laststatus.machine.position.offset.z,
                };
                printLog("Storing WCS: X=" + window.LastWcs.X.toFixed(3) + ", Y=" + window.LastWcs.Y.toFixed(3) + ", Z=" + window.LastWcs.Z.toFixed(3));
            }
        });
    });
    
    The RestoreWcs macro restores the saved offset.
    Code:
    // Restores the WCS offset, previously stored by StoreWcs.js
    
    if (window.LastWcs != undefined)
    {
        var gcode = "G10 G90 L2 P0 X" + window.LastWcs.X.toFixed(3) + " Y" + window.LastWcs.Y.toFixed(3) + " Z" +  + window.LastWcs.Z.toFixed(3);
        sendGcode(gcode);
        printLog("Restoring WCS: X=" + window.LastWcs.X.toFixed(3) + ", Y=" + window.LastWcs.Y.toFixed(3) + ", Z=" + window.LastWcs.Z.toFixed(3));
    }
    else
    {
        printLog("No WCS has been previously stored.");
    }
    
     
    Peter Van Der Walt likes this.
  20. Ivo Beltchev

    Builder

    Joined:
    Apr 17, 2023
    Messages:
    32
    Likes Received:
    15
    Smart Home

    If you click the Home button by accident (like, you click Home on the keyboard, unaware that OpenBuilds has the focus), the machine starts moving, potentially hitting something on the way.
    This macro replaces the behavior of the Home button to ask for confirmation if the homedRecently flag is set.
    upload_2023-11-5_21-56-49.png

    Set it to run on startup
    Code:
    // Prevents accidental homing if the machine was recently homed
    
    $(document).ready(function() {
        var homeButton = `
        <button id="homeBtn" class="ribbon-button">
            <span class="icon">
                <i class="fas fa-home"></i>
            </span>
            <span class="caption grblmode">Home<br>All</span>
        </button>`
    
        $("#homeBtn").attr('id', "oldHomeBtn");
        $("#oldHomeBtn").after(homeButton);
        $("#oldHomeBtn").remove();
    
        $("#homeBtn").on('click',function()
        {
            if (laststatus.machine.modals.homedRecently)
            {
                Metro.dialog.create({
                    title: "Home All",
                    content: "The machine was recently homed. Do you want to home again?",
                    actions: [
                        {
                            caption: "Proceed",
                            cls: "js-dialog-close success",
                            onclick: home
                        },
                        {
                            caption: "Cancel",
                            cls: "js-dialog-close",
                            onclick: function() {
                                // do nothing
                            }
                        }
                    ],
                    closeButton: true
                });
            }
            else
            {
                home();
            }
        });
    });
     
    Peter Van Der Walt likes this.
  21. Misterg

    Misterg Veteran
    Staff Member Moderator Builder Resident Builder

    Joined:
    Aug 27, 2022
    Messages:
    372
    Likes Received:
    288
    Centre Finding External Features

    This macro works with a 3D probe to locate the centre of external part features (i.e. raised bosses).

    Start with the probe roughly positioned above the centre location, run the macro, and enter the approximate size of the feature.

    It will work with rectangular / oval features provided that the length of the largest X/Y dimension is entered. Dimensions of the feature and the probe over travel entered in the dialog *must* be sufficient for the probe to clear the feature, taking into account the likely error in the initial position, or there will be a crash.

    Z travel entered must be sufficient for the probe to lower enough to detect the part when probing without unwanted contact with the part or machine bed.

    X and Y centres can be found independently by using the check boxes at the bottom of the dialog.

    After the macro runs, the probe will be positioned over the centre of the part at the original Z height and the X and/or Y coordinates of the active WCS will be set to zero.

    Z coordinates are preserved.

    By default, the routine uses a fast probe to determine the rough location, then slow probe to get a more accurate position. This can be changed to a single probing operation by clearing the checkbox at the bottom right.

    There are options to set Z travel rate and probe retract distance, but these are hidden by default. If you want to have them in the dialog box (as below) set simpleUI = "false" on line 24 of the macro.

    The macro is longer and more complicated than it needs to be as it includes embedded graphics, so I haven't included the text file, just the zipped .json file. There are signposts at the top of the macro for those that want to change the default values.

    Default UI:

    overview.png

    Alternative UI (simpleUI = "false")

    fullui.png

    Quick demo:



    Let me know how it goes :)

    [Edit] V2.1 10th August 2024: Added the option to display the apparent size of the feature as measured during probing. Screenshots above updated to include the 'Show Result' checkbox. If checked, the routine will show the pop-up below:

    done2.png

    Bear in mind that this will only reflect the actual size of the feature if the probe was already centred on it. (Run the routine twice if you want to be sure of this.)

    In all cases, the actual measurements are available in the terminal window.
     

    Attached Files:

    #21 Misterg, Nov 7, 2023
    Last edited: Aug 10, 2024
  22. sharmstr

    sharmstr OpenBuilds Team
    Staff Member Moderator Builder Resident Builder

    Joined:
    Mar 23, 2018
    Messages:
    2,059
    Likes Received:
    1,449
    Another option is to display all the comments when you load the gcode file. In your other post regarding comments in the post processor, you have the tool information and the actual material size, not just the min / max movements that the macro above gives you. Based on the gcode file you posted in the other thread, here's what you see when you load the file. Obviously your post processor needs some work to improve the quality of the information (redundant "Toolname" line, etc...)


    upload_2024-5-21_11-24-23.png



    Here's the code. Add it to a new javascript macro and set it to load at startup.


    Code:
    function alertMacro() {
      editor.session.on('change', function(e) {
        var alertText = '';
        if (editor.curOp && editor.curOp.command.name) {
          return null;
        }
    
        let lines = editor.session.doc.getAllLines()
    
        for (var i = 0, l = lines.length; i < l; i++) {
          if (lines[i].indexOf('(') != -1) {
            alertText = `${alertText} ${lines[i].replace('(', '').replace(')','')}<br>`;
          }       
        }
      
        if (alertText !== '') {
          var dialog = Metro.dialog.create({
            clsDialog: 'dark',
            title: "<i class='fas fa-exclamation-triangle'></i> JOB SETUP",
            content: alertText,
            actions: [{
              caption: "Close",
              cls: "js-dialog-close",
              onclick: function() {
                //
              }
            }]
          });
        }   
      });
    }
    $(document).ready(() => setTimeout(alertMacro, 1000));
    
     
    #22 sharmstr, May 21, 2024
    Last edited: May 21, 2024
  23. Alain JBT

    Alain JBT Well-Known
    Builder

    Joined:
    Apr 23, 2023
    Messages:
    53
    Likes Received:
    31
    Hi @Misterg
    Super useful to compare/validate the dimension with the machining part !!!
    Thank you so much for this superb work:thumbsup:
     
    Misterg likes this.

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice