Duet3D Logo Duet3D
    • Tags
    • Documentation
    • Order
    • Register
    • Login
    1. Home
    2. Schmart
    • Profile
    • Following 0
    • Followers 1
    • Topics 6
    • Posts 75
    • Best 7
    • Controversial 0
    • Groups 0

    Schmart

    @Schmart

    9
    Reputation
    5
    Profile views
    75
    Posts
    1
    Followers
    0
    Following
    Joined Last Online

    Schmart Unfollow Follow

    Best posts made by Schmart

    • Pressure advance tuning with conditional G-code

      I've created a conditional G-code macro that prints a model to help tune pressure advance. It's based on the model used in this post, which I happen to like very much. It's a fast and native solution, starts from your printer directly and requires no slicer or other software.

      The macro is a work-in-progress, but offers a lot of configurability already. I will probably make a number of improvements as I use it myself, but I wanted to share this with you at this stage for feedback and educational purposes (i.e. my education).

      I can't guarantee that this code will work properly on your machine. I have a simple cartesian model with XYZE axes. Also, I've tested this on my printer with a self-compiled post-beta 5 firmware 3.4.0, since I ran into some rounding issues in the output of earlier RRF firmwares, though this has been fixed by @dc42 and I've seen mentioned that this fix is part of the beta 6 release.

      I've kept the variable names as descriptive as possible. However, let me know if you need explanation on how to use this:

      ; This macro generates a test print to determine the best pressure advance factor (M572 S-parameter)
      ; It uses the test print described in this forum post:
      ; https://forum.duet3d.com/topic/6698/pressure-advance-calibration
      
      ; Author: Schmart
      
      ; WARNING: all dimensions, size units and speeds are in mm and mm/s
      
      ; Reference for conditional G-code:
      ; - https://duet3d.dozuki.com/Wiki/GCode_Meta_Commands
      
      ; Wishes for conditional G-code:
      ; - Canceling or stopping running conditional G-code
      ; - Simulation; logging what commands would be executed/generated without actually executing them
      ; - Dumping the variables (names and values) in a macro, perhaps also treating variable in a meta-way, e.g. {#var} for count, {var[0].name} and {var['layer_height'].value}
      ; - Assigning an object or array to a variable. E.g. var axis = move.axes[0]; echo {var.axis.letter}
      ; - Custom subroutine/function definition, e.g. def print_line(var a, var b)
      ; - ceiling(), pow() and sq() functions
      
      ; TODO:
      ; - Many optimizations still remaining
      ; - Perhaps the most important variables (bed and print temperature, filament diameter, extrusion multiplier, PA stepping)
      ;   can be made into macro parameters, e.g. M98 P"0:/macros/4 - Print Tuning/Pressure Advance" B40 P204 D1.75 X0.95 S0.02 P0.002
      
      ; Starting value for pressure advance
      var pa_start = 0.0
      ; Pressure advance increment for each (whole) millimeter print height
      ; e.g. if var.height = 20, var.pa_start = 0.1 and var.pa_stepping = 0.004, then
      ; the PA test range is from 0.1 to (0.1 + 20 * 0.004) => 0.1 to 0.18.
      var pa_stepping = 0.01
      ; The extruder to apply the pressure advance factor to
      var pa_extruder = 0
      ; These values specify the center of the print bed
      ; The default bed center is at (0,0)
      var x_center = 0
      var y_center = 0
      ; Print temperatures
      var print_temperature = 205
      var standby_temperature = 120
      var bed_temperature = 40
      ; Height of the test print
      var height = 20
      var layer_height = 0.20
      ; Number of fast segments
      var fast_segments = 3
      ; Slow segments are at the beginning and the end, and in between fast segments, e.g. SLOW FAST SLOW FAST SLOW
      var slow_segment_length = 10
      var fast_segment_length = 20
      ; Definition of speeds
      var first_layer_speed = 20
      var travel_speed = 80
      var fast_segment_speed = 60
      var slow_segment_speed = 10
      ; Number of perimeters around the object as a stable base
      var skirt_loops = 6
      ; Height of the skirt in layers
      var skirt_layers = 2
      ; The tool number to print with
      var tool_number = 0
      ; Extrusion width is calculated here, but can also be set with a literal value
      ; Note that 1.05 and 1.125 are common factors that result in 0.42mm or 0.45mm width respectively
      var nozzle_bore_diameter = 0.40
      var extrusion_width = {var.nozzle_bore_diameter * 1.125}
      var filament_diameter = 1.78
      var extrusion_multiplier = 0.93
      ; Firmware retraction settings
      var retract_length = 0.5
      var retract_restart_length = 0
      var retract_speed = 40
      var deretract_speed = 40
      var retract_z_lift = 0
      
      ; Flow math
      var filament_flow = {pi * var.filament_diameter * var.filament_diameter / 4}
      var regular_flow = {(var.extrusion_width - var.layer_height) * var.layer_height + pi * var.layer_height * var.layer_height / 4}
      var bridge_flow = {pi * var.nozzle_bore_diameter * var.nozzle_bore_diameter / 4}
      var line_spacing = {var.extrusion_width - var.layer_height * (1 - pi / 4)}
      var regular_flow_ratio = { var.extrusion_multiplier * var.regular_flow / var.filament_flow}
      var purge_line_flow_ratio = { 2.0 * var.regular_flow_ratio }
      
      echo "extrusion_width: " ^ var.extrusion_width
      echo "layer_height: " ^ var.layer_height
      echo "filament_flow: " ^ var.filament_flow
      echo "bridge_flow: " ^ var.bridge_flow
      echo "regular_flow: " ^ var.regular_flow
      echo "line_spacing: " ^ var.line_spacing
      echo "regular_flow_ratio: " ^ var.regular_flow_ratio
      
      ;M37 S1 ; Enter simulation mode
      
      ; Set firmware retraction
      M207 S{var.retract_length} R{var.retract_restart_length} F{60 * var.retract_speed} T{60 * var.deretract_speed} Z{var.retract_z_lift}
      
      T{var.tool_number} ; Select tool
      M106 S0 ; Turn off part cooling fan
      M568 P{var.tool_number} S{var.print_temperature} R{var.standby_temperature} A1 ; Set tool to standby temperature
      M190 S{var.bed_temperature} ; Wait for bed temperature to reach setpoint
      M116 P{var.tool_number} ; Wait for temperatures associated with the selected tool to be reached
      
      ; Make an inventory of axes that have not yet been homed
      var axes = ""
      echo "Total number of axes: " ^ {#move.axes}
      while {iterations < #move.axes}
        if {!move.axes[iterations].homed}
          set var.axes = {var.axes ^ move.axes[iterations].letter}
      
      ; Home applicable axes
      echo "Axes to be homed: " ^ var.axes
      G28 {var.axes}
      
      ;G28 XYZ ; Home the X, Y and Z axes
      ;G28 XY ; Home the X and Y axes
      ;G28 Z ; Home the Z axis
      
      G21 ; Set units to millimeters
      M83 ; Use relative distances for extrusion
      
      ; Calculate object width
      var width = {var.fast_segments * var.fast_segment_length + (1 + var.fast_segments) * var.slow_segment_length}
      ; Calculate starting coordinates and other constant(s)
      var x_start = {var.x_center - 0.5 * var.width + var.skirt_loops * var.line_spacing}
      var y_start = {var.y_center - 0.5 * var.line_spacing + var.skirt_loops * var.line_spacing}
      var travel_feedrate = {60 * var.travel_speed}
      var first_layer_feedrate = {60 * var.first_layer_speed}
      
      ; Absolute position for purge line in X and Y space, 50 mm behind model
      G90 ; Use absolute coordinates
      G1 X{var.x_start} Y{var.y_start + 50} F{var.travel_feedrate}
      
      ; Set heater to final temperature and wait
      M568 A2
      M116 P{var.tool_number}
      
      ; Absolute position of nozzle at first layer height
      G1 Z{var.layer_height} F{var.travel_feedrate}
      
      ; Relatively print two fat purge lines
      G91 ; Switch to relative coordinates
      G1 X{var.width} E{var.width * var.purge_line_flow_ratio} F{var.first_layer_feedrate}
      G1 Y{-(2 * var.line_spacing)} F{var.travel_feedrate}
      G1 X{-var.width} E{var.width * var.purge_line_flow_ratio} F{var.first_layer_feedrate}
      
      G10 ; Retract to prevent oozing
      
      ; Move to the start of the model in X, Y and Z space
      ; The skirt code also moves to the start, but the skirt can be disabled.
      ; Also, the skirt code does not set Z, and there may be no purge line for which Z is set. Safety first.
      G90 ; Use absolute coordinates
      G1 X{var.x_start} Y{var.y_start} Z{var.layer_height} F{var.travel_feedrate}
      G91 ; Switch to relative coordinates
      
      G11 ; Advance/unretract/deretract in preparation to print
      
      ; Routine for printing the test object
      var layers = {floor(var.height / var.layer_height)}
      echo "Total number of layers: " ^ var.layers
      
      while {iterations < var.layers}
        ; Track current layer
        var layer = {iterations + 1}
        ; Current height in mm
        var z = {var.layer * var.layer_height}
        ; Calculate pressure advance factor
        var pa = {var.pa_start + floor(var.z) * var.pa_stepping}
        ; Set pressure advance
        M572 D{var.pa_extruder} S{var.pa}
      
        ; Output some statistics while printing
        echo "Layer " ^ iterations ^ " (" ^ {iterations + 1} ^ " of " ^ {var.layers} ^ " at " ^ {var.z} ^ "mm)"
        echo "Pressure advance: " ^ {var.pa}
      
        ; Pre-calculate feedrates for first layer and other layers
        var slow_segment_feedrate = {60 * (var.layer == 1 ? var.first_layer_speed : var.slow_segment_speed)}
        var fast_segment_feedrate = {60 * (var.layer == 1 ? var.first_layer_speed : var.fast_segment_speed)}
      
        ; Print skirt
        if {iterations < var.skirt_layers}
          G90 ; Use absolute coordinates
          ; Move to absolute XY start coordinates
          G1 X{var.x_start} Y{var.y_start} F{var.travel_feedrate}
          G91 ; Switch to relative coordinates
      
          ; Print all loops of the skirt
          while {iterations < var.skirt_loops}
            var skirt_loop = {var.skirt_loops - iterations}
            var x = {var.width + 2 * var.skirt_loop * var.line_spacing}
            var y = {var.line_spacing + 2 * var.skirt_loop * var.line_spacing}
            ; Print one full skirt loop
            while iterations < 2
              var direction = {iterations == 0 ? 1 : -1}
              G1 X{var.direction * var.x} E{var.x * var.regular_flow_ratio} F{var.fast_segment_feedrate}
              G1 Y{var.direction * var.y} E{var.y * var.regular_flow_ratio} F{var.fast_segment_feedrate}
            ; Travel to the start of the next skirt loop
            G1 X{var.line_spacing} Y{var.line_spacing} F{var.travel_feedrate}
      
        ; Print two perimeters back and forth of alternating slow and fast segments
        while iterations < 2
          var direction = {iterations == 0 ? 1 : -1}
          ; Slow starting segment (X)
          G1 X{var.direction * var.slow_segment_length} E{var.slow_segment_length * var.regular_flow_ratio} F{var.slow_segment_feedrate}
          ; Remaining fast and slow segments (X)
          while iterations < var.fast_segments
            G1 X{var.direction * var.fast_segment_length} E{var.fast_segment_length * var.regular_flow_ratio} F{var.fast_segment_feedrate}
            G1 X{var.direction * var.slow_segment_length} E{var.slow_segment_length * var.regular_flow_ratio} F{var.slow_segment_feedrate}
          ; Print the side perimeter (Y)
          G1 Y{var.direction * var.line_spacing} E{var.line_spacing * var.regular_flow_ratio} F{var.slow_segment_feedrate}
      
        ; Move one layer up
        G1 Z{var.layer_height} F{var.travel_feedrate}
      
      G10 ; Retract
      
      G91 ; Relative positioning
      G1 F3000 Z20 ; Move gantry up 20mm
      G90 ; Absolute positioning
      G28 X ; Home X axis
      ;M104 S0 ; Turn off nozzle heat block
      M568 P{var.tool_number} S0 R0 A2 ; Set required heater temperature off
      M140 S0 ; Turn off bed
      M106 S0 ; Turn off part cooling fan
      M18 ; Disable stepper motors
      
      ;M37 S0 ; Leave simulation mode
      

      Photo of one print in progress and one done:

      IMG_9105.jpg

      Lastly, I'm so happy with the concept and speed, that I'm now toying with the idea to make macros for:

      • Tuning bridge parameters (with flow, temperature and part cooling as variables)
      • Layer adhesion (with temperature and part cooling are variable)
      • Tuning the extrusion multiplier

      EDIT: The code didn't look right when it contains words like "doesn't" and "haven't" in comments. This results in random "{0}" inserted and illegal G-code.

      posted in Gcode meta commands
      Schmartundefined
      Schmart
    • RE: RepRapFirmware 3.01-RC4 and DSF 1.2.5 now available

      Hi @arhi, documentation is not yet fully updated with the added behavior, but M220 essentially scales the print speed, as if you set higher or lower print speeds in your slicer. M220 now does not apply to retract/unretract and move commands in macros anymore.

      M221 scales the extrusion, which is independent of M220. So if you want 50% higher speed and 5% more extrusion, that would be M220 S150 and M221 S105.

      posted in Beta Firmware
      Schmartundefined
      Schmart
    • RE: Duet 3 Mini 5+ : Initial announcement

      That is the intention, however it's one of the few hardware features of the board that I haven't yet written the firmware support for.

      @dc42 do you mean support for the Neopixel backlight specifically or the MiniPanel itself? Because you're welcome to use the code in my fork of RRF3 for the MiniPanel support. I've been using this build successfully for many weeks now. I suppose that even if the Mini 5+ hardware side changed a bit, my work could still save you some time.

      BTW, I'm also interested in beta testing the new board. I have this 'gift' of being detail oriented and noticing issues. Also curious to find out if the TMC2209 drivers will better cooperate with my steppers compared to the TMC2224 drivers on my Maestro.

      posted in General Discussion
      Schmartundefined
      Schmart
    • RE: Pressure advance tuning with conditional G-code

      @t3p3tony Apologies, I was indeed referring to the markdown representation of G-code in the forum. Fortunately, the firmware itself handles comments with single quotes without any issue. @o_lampe I wasn't aware of the 'perl' option, I will try this when I'm back home!

      posted in Gcode meta commands
      Schmartundefined
      Schmart
    • RE: Genuine BLTouch v3.0 weirdness with RFF >= 3.3

      @dc42 I hooked up my logic analyser to the zprobe.mod and VCC pins of the BLTouch, and tested a couple of scenarios with both RRF 3.2.2 and RRF 3.4.0 beta 4, including the startup sequence, reset and deploy.

      I'm not seeing differences in the PWM generated by the M280 commands between the two firmwares.

      However, I do see a significant difference between the firmwares in the time it takes from VCC fully powering the BLTouch and the zprobe.mod pin going LOW:

      • RRF 3.2.2 takes about 20.6ms
      • RRF 3.4.0 takes about 40.6ms

      Here are my measurements. I performed these multiple times to verify the timings were consistent. Channel 0 is zprobe.mod and Channel 1 is VCC to the BLTouch. T0 is when I power on the printer (small power spike) and after about 1.2-1.3s it seems that the board starts initialisation.

      RRF 3.2.2:

      RRF 3.2.2 Startup (zprobe.mod and VCC).png

      RRF 3.4.0 beta 4:

      RRF 3.4.0 beta 4 Startup (zprobe.mod and VCC).png

      My theory is that on startup of the Duet 2 Maestro with RRF 3.4.0, the BLTouch interrupts its self-test because zprobe.mod happens to turn LOW in the middle of that process. Once it has entered that error state, it remains there until it can successfully complete its self-test.

      This effect is also described on the Antclabs site:

      BLTouch Flashing 80% Duty Cycle.png

      I suspect that the reason that this doesn't happen on RRF 3.2.2, is that it turns the zprobe.mod pin LOW before the BLTouch starts its self-test.

      I looked at the Platform.cpp code, is there a safe way to turn zprobe.mod low earlier (UrgentInit() perhaps?) or delay the InitZProbe() by the 100ms 'non-PWM Time' that Antclabs recommends?

      Signal timing

      posted in Beta Firmware
      Schmartundefined
      Schmart
    • RE: Pressure advance tuning with conditional G-code

      @t3p3tony said in Pressure advance tuning with conditional G-code:

      @cncmodeller, @Schmart you would need to use a global variable, set in the sub macro, to pass information back to the main calling macro.

      Hi Tony, I had already done some testing with moving subroutine code in separate macros. On the surface, it seems that, between the Neanderthal approach of copying and pasting code, and re-using logic through parameterized macros, the balance tips towards the latter. However, I came across some conceptual and technical downsides that made me decide otherwise:

      • Abstraction. I consider the macro as a vehicle for an end-user, not a developer; a cohesive unit of code that an end user would want to run. The macro is the 'public interface' to the functionality provided. A 'submacro' should not be able to have a public interface since it doesn't provide a useful experience by itself.
      • Coupling and reliability. Subroutines exist to improve the maintainability of code and are not intended for an end-user to touch or run. If a unit of code is spread across multiple macros, it's easy for the end user to break functionality (e.g. remove a macro) or never get the functionality to run (e.g. not naming macros correctly). The absolute locations and names of macros are suddenly important and tightly coupled to the whole solution.
      • Deployment and versioning. Depending on the complexity of the test print I want to generate, there could be many submacros involved, for things like circles, infill, fonts, etc. Splitting a solution in (potentially) many different macros complicates the setup, handling and publishing of code and updates. This becomes even more challenging when the same submacro is used by multiple main macros and the submacro needs an update that maintains backward compatibility.
      • Testability. When using submacros, I have even less troubleshooting options and I can't isolate problems in my code properly. The only surefire way to test a multi-macro solution is to actually execute it with M98 and send it to the printer. The developer experience is therefore not optimal:
        1. If I call the main macro with M37 P"parent", the M98 P"child" within the main macro (with the purpose of calling the submacro) is not executed. Submacros are only called when I execute the main macro with M98.
        2. If I send an M37 S1 command in the DWC console, this causes any echo statement to be suppressed, leaving me fully in the dark. Using M37 S1 in the main macro is an option, but then submacros are still not called.
        3. Isolated testing of a 'submacro' is not possible since M37 doesn't accept parameters. Wrapping that submacro with another macro is of no use because of point #1.
      • Scope. Traditionally, variables in a parent's scope are available to a subroutine, function, a C++ preprocessor directive or a code fragment that's simply #included from another file. However, variables in a parent macro cannot be shared with a child macro, and that makes sense in terms of separation of concerns. Ways to solve this:
        • Making all or only the necessary variables global is an option, though, to avoid naming clashes, these should be namespaced properly. However, I consider this a workaround. I believe the concept of global variables is more suited to defining a small number of shared and relatively static or read-only printer settings. Global variables don't really seem intended for inter-scope communication where there are a lot of global variables in use and where they continually change.
        • Using parameterized macros is also an option. However, because of parameter naming requirements, the number of variables that can be passed to a macro is limited. You can use single letters only, and three or four letters are already reserved (G, M, N and T). Additionally, this requires me to convert back and forth between highly recognizable local variable names and obscure macro parameter names, and keep the mapping between these logical.
      • Performance. I haven't yet determined if the speed with which macros can be called would be an issue, so whether e.g. caching of called macros is implemented. For sections of a print that need to be executed quickly and frequently, this would be a requirement to prevent stuttering in the movement and blobs in the print. Accuracy and timing is pretty important in test prints, so that's something I need to avoid.

      All in all, I've settled on putting the code as one big chunk in a single macro for now, and my hopes for the future are that some of the remarks above can be addressed!


      I've included the test code. I had to slightly clean up the code to include it in this post.

      Hopefully that provides some additional clarification to the above epistle (TLDR anyone? 😉 ).

      _Parent code

      Run this with M37 P"0:/macros/_Parent". Note how messages from the child macro are not included. Running this with M98 P"0:/macros/_Parent" does call the child macro, but ⚠ also actually tries to print this. Wrapping the parent code between M37 S1 and M37 S0 commands and running it with M98 also ignores the call to the child macro. At least on my RepRapFirmware 3.4.0 beta 6.

      echo "Parent started"
      
      while {iterations < 10}
        echo "Calling child macro, iteration #" ^ {iterations}
        M98 P"0:/macros/_Child" X50 Y50 F2400 R6000
      
      echo "Parent ended"
      

      _Child code

      Note that a standalone simulation with M37 P"0:/macros/_Child" X100 Y100 F2400 R6000 does not work because M37 does not pass parameters, and causes unknown parameter errors.

      var size_x = {param.X}
      var size_y = {param.Y}
      var feedrate = {param.F}
      var travel_feedrate = {param.R}
      
      echo "Child started"
      
      ; Print single-perimeter rectangle
      G90 ; Use absolute coordinates
      G1 X{-var.size_x / 2} Y{-var.size_y / 2} F{var.travel_feedrate}
      G91 ; Use relative coordinates
      G1 X{var.size_x} F{var.feedrate}
      G1 Y{var.size_y} F{var.feedrate}
      G1 X{-var.size_x} F{var.feedrate}
      G1 Y{-var.size_y} F{var.feedrate}
      
      echo "Child ended"
      
      posted in Gcode meta commands
      Schmartundefined
      Schmart
    • RE: Genuine BLTouch v3.0 weirdness with RFF >= 3.3

      @dc42 Apologies, I neglected to mention that I had tried this, both the delay/dwell and moving around commands in the config.g. I'm confident that zprobe.mod pin going LOW happens before my config.g even gets loaded. The G4 S10 I used at the start of config.g really made this clear.

      That's why I mentioned there's maybe something that can be done relatively quickly after board startup, e.g. in UrgentInit() or Platform::Init(), or perhaps even prevent the pin going HIGH in the first place (I haven't yet checked if the SAM4S8C initializes pins HIGH by default, or that this is done by a specific piece of RRF code).

      As a proof of concept, I've built the latest GitHub branch 3.4-dev and modified Init() in Platform.cpp slightly with just one statement added at the beginning:

      // Initialise the Platform. Note: this is the first module to be initialised, so don't call other modules from here!
      void Platform::Init() noexcept
      {
      	// Prevent BLTouch from receiving a delayed transition to LOW on its zprobe.mod pin.
      	// This confuses the self-test process and throws the BLTouch in a 80% duty cycle blinking mode.
      	// DiagPin is identical to Z_PROBE_MOD_PIN on Duet 2 Maestro.
      	// This statement was also present in earlier firmwares, e.g. <= 3.2.2
      	pinMode(DiagPin, OUTPUT_LOW);
      

      This has drastically reduced the time that it takes for zprobe.mod to become LOW. It now only takes 8.6ms (!) measured from the point that VCC is stable:

      RRF 3.4.0 beta 4 Startup (Modified Init()).png

      I can happily report that this quick fix has proven that I found the culprit and that this has solved the issue. The BLTouch is now performing its self-test completely and correctly: the pin deploys and retracts twice when powering on the printer and the red LED is on continuously now.

      P.S. I had mentioned UrgentInit() and InitZProbe() before, but I was looking at an older dev branch, and things have changed considerably since then with the whole state machine in Platform::Tick(), so please forget I mentioned that.

      posted in Beta Firmware
      Schmartundefined
      Schmart

    Latest posts made by Schmart

    • Tabs and spaces in the G-code editor, encore

      Re: [DWC 3.2](gcode editor issue)

      Hi, I found the older topic referenced above mentioning that tabs should be replaced by spaces in the editor. However, while working intensively with conditional G-code/meta commands in the last couple of weeks (DWC 3.4.0 beta 5/6), I noticed this does not happen?

      I have to manually check the code with Visual Studio Code (search for \t) to prevent RRF complaining that spaces and tabs are mixed.

      Take the following code as a start:

      while {false}
      echo "Tab before line indent"
      echo "Selected line level 1 indent"
      echo "Selected line level 2 indent"
      
      • When I put the cursor in front of line 2 and press [Tab] → a tab character is inserted.
      • When I select line 3 with [Shift]+[Down] and press [Tab] → two spaces are inserted.
      • When I select line 4 with [Shift]+[Down] and press [Tab] twice → a tab character is inserted.

      So it looks like this now:

      1d21d81e-4ad0-43aa-b1b8-e1a093360332-image.png

      I would like that the built-in DWC editor converts indents into either spaces or tabs, but that it prevents a mix of both.

      P.S. I think two spaces are a good alternative to a space, but I know there's a camp that would prefer four spaces or tab characters only. So ultimately, a user setting for this would be nice.

      posted in Duet Web Control
      Schmartundefined
      Schmart
    • RE: Pressure advance tuning with conditional G-code

      @cncmodeller said in Pressure advance tuning with conditional G-code:

      @schmart feel free to use anything I've contributed.

      Thanks! I took the liberty of integrating your suggestions.

      Personally I was thinking the same way as you, two decent diameter thin wall towers and judge stringing and wall gaps on un-retract when starting a layer on each tower.

      I came across this site which seems to offer an interesting approach to retraction tuning as well. For now, I hacked together a more classical attempt to tune retraction settings . Please see attached file Retraction.g.

      ⚠ I tested this on my printer, but still, please consider this a beta release.

      posted in Gcode meta commands
      Schmartundefined
      Schmart
    • RE: Pressure advance tuning with conditional G-code

      @t3p3tony said in Pressure advance tuning with conditional G-code:

      @cncmodeller, @Schmart you would need to use a global variable, set in the sub macro, to pass information back to the main calling macro.

      Hi Tony, I had already done some testing with moving subroutine code in separate macros. On the surface, it seems that, between the Neanderthal approach of copying and pasting code, and re-using logic through parameterized macros, the balance tips towards the latter. However, I came across some conceptual and technical downsides that made me decide otherwise:

      • Abstraction. I consider the macro as a vehicle for an end-user, not a developer; a cohesive unit of code that an end user would want to run. The macro is the 'public interface' to the functionality provided. A 'submacro' should not be able to have a public interface since it doesn't provide a useful experience by itself.
      • Coupling and reliability. Subroutines exist to improve the maintainability of code and are not intended for an end-user to touch or run. If a unit of code is spread across multiple macros, it's easy for the end user to break functionality (e.g. remove a macro) or never get the functionality to run (e.g. not naming macros correctly). The absolute locations and names of macros are suddenly important and tightly coupled to the whole solution.
      • Deployment and versioning. Depending on the complexity of the test print I want to generate, there could be many submacros involved, for things like circles, infill, fonts, etc. Splitting a solution in (potentially) many different macros complicates the setup, handling and publishing of code and updates. This becomes even more challenging when the same submacro is used by multiple main macros and the submacro needs an update that maintains backward compatibility.
      • Testability. When using submacros, I have even less troubleshooting options and I can't isolate problems in my code properly. The only surefire way to test a multi-macro solution is to actually execute it with M98 and send it to the printer. The developer experience is therefore not optimal:
        1. If I call the main macro with M37 P"parent", the M98 P"child" within the main macro (with the purpose of calling the submacro) is not executed. Submacros are only called when I execute the main macro with M98.
        2. If I send an M37 S1 command in the DWC console, this causes any echo statement to be suppressed, leaving me fully in the dark. Using M37 S1 in the main macro is an option, but then submacros are still not called.
        3. Isolated testing of a 'submacro' is not possible since M37 doesn't accept parameters. Wrapping that submacro with another macro is of no use because of point #1.
      • Scope. Traditionally, variables in a parent's scope are available to a subroutine, function, a C++ preprocessor directive or a code fragment that's simply #included from another file. However, variables in a parent macro cannot be shared with a child macro, and that makes sense in terms of separation of concerns. Ways to solve this:
        • Making all or only the necessary variables global is an option, though, to avoid naming clashes, these should be namespaced properly. However, I consider this a workaround. I believe the concept of global variables is more suited to defining a small number of shared and relatively static or read-only printer settings. Global variables don't really seem intended for inter-scope communication where there are a lot of global variables in use and where they continually change.
        • Using parameterized macros is also an option. However, because of parameter naming requirements, the number of variables that can be passed to a macro is limited. You can use single letters only, and three or four letters are already reserved (G, M, N and T). Additionally, this requires me to convert back and forth between highly recognizable local variable names and obscure macro parameter names, and keep the mapping between these logical.
      • Performance. I haven't yet determined if the speed with which macros can be called would be an issue, so whether e.g. caching of called macros is implemented. For sections of a print that need to be executed quickly and frequently, this would be a requirement to prevent stuttering in the movement and blobs in the print. Accuracy and timing is pretty important in test prints, so that's something I need to avoid.

      All in all, I've settled on putting the code as one big chunk in a single macro for now, and my hopes for the future are that some of the remarks above can be addressed!


      I've included the test code. I had to slightly clean up the code to include it in this post.

      Hopefully that provides some additional clarification to the above epistle (TLDR anyone? 😉 ).

      _Parent code

      Run this with M37 P"0:/macros/_Parent". Note how messages from the child macro are not included. Running this with M98 P"0:/macros/_Parent" does call the child macro, but ⚠ also actually tries to print this. Wrapping the parent code between M37 S1 and M37 S0 commands and running it with M98 also ignores the call to the child macro. At least on my RepRapFirmware 3.4.0 beta 6.

      echo "Parent started"
      
      while {iterations < 10}
        echo "Calling child macro, iteration #" ^ {iterations}
        M98 P"0:/macros/_Child" X50 Y50 F2400 R6000
      
      echo "Parent ended"
      

      _Child code

      Note that a standalone simulation with M37 P"0:/macros/_Child" X100 Y100 F2400 R6000 does not work because M37 does not pass parameters, and causes unknown parameter errors.

      var size_x = {param.X}
      var size_y = {param.Y}
      var feedrate = {param.F}
      var travel_feedrate = {param.R}
      
      echo "Child started"
      
      ; Print single-perimeter rectangle
      G90 ; Use absolute coordinates
      G1 X{-var.size_x / 2} Y{-var.size_y / 2} F{var.travel_feedrate}
      G91 ; Use relative coordinates
      G1 X{var.size_x} F{var.feedrate}
      G1 Y{var.size_y} F{var.feedrate}
      G1 X{-var.size_x} F{var.feedrate}
      G1 Y{-var.size_y} F{var.feedrate}
      
      echo "Child ended"
      
      posted in Gcode meta commands
      Schmartundefined
      Schmart
    • RE: Pressure advance tuning with conditional G-code

      @cncmodeller said in Pressure advance tuning with conditional G-code:

      This seems to be working a treat on my machine. Thanks very much for the effort!

      You're welcome, I'm happy that you got it working 👍

      • Add a var to control if the file was simulated or not in a single location. I then put an if before the relevant GCode statements w.r.t. setting and un setting simulation mode.

      I've since learned that M37 also supports simulating a macro based on meta commands, and that doesn't require changing the code itself, so that might even be a more robust option for a less confident user?

      On the other hand, your approach 1) puts the code itself in "debugging mode" which is persistent and developer-friendly and 2) doesn't add the "time taken" at the bottom of the macro file.

      Disable Bed heating if the temperature is set to 0 as I don't have a working heated bed yet.

      That's an excellent point, I hadn't considered printers without a heated bed. If you don't mind, I would like to incorporate your optimizations.

      Just thinking a similar approach to retraction / de-retraction distances might be a nice project too, albeit I'm going to have to learn a lot about conditional GCode to figure that out!!

      The reason why I found the pressure advance tuning such a good first use case, is because I couldn't convince my slicer to print a contiguous perimeter/wall with different speeds. At least in PrusaSlicer, the modifiers insisted on splitting the model in separate parts.

      However, I was also a bit proud and hand-crafted all G-code and calculations myself, with secondary features like the brim taking a disproportional amount of time. So I wouldn't necessarily recommend doing that.

      I'm currently tackling retraction tuning with PrusaSlicer entirely. I simply add two cylinder shapes some distance apart, and enter a height-based formula in the "Before layer change G-code" text box in PrusaSlicer that determines the retraction value.

      Theoretically, such a test model would not be difficult to convert to a macro. However, I'm dearly missing the concept of a 'function' in the meta command instruction set, which would make it easier to create and maintain such code. Therefore, I think the quickest road to success is to further resist the urge to DIY things like infill and circular perimeters and start by parameterizing the G-code of a pre-sliced model.

      Do you have any favorite models to tune retraction?

      Anyway thanks again, this is great 🙂

      😁

      posted in Gcode meta commands
      Schmartundefined
      Schmart
    • RE: Pressure advance tuning with conditional G-code

      @medicusdkfz said in Pressure advance tuning with conditional G-code:

      Hi,

      thank you for your incredible job... Could you tell me, what the latest version is?

      TY, Pierre

      Thanks Pierre! And yes, the macro in the first post is still the latest version. I'm planning to provide an update with minor changes this week, including the ones from @CNCModeller, but probably nothing drastic. If you have any ideas for improvement, let me know.

      posted in Gcode meta commands
      Schmartundefined
      Schmart
    • RE: Pressure advance tuning with conditional G-code

      @heartleander81 that little one-liner you ran (error message pun intended) confirmed that variables in the meta command language are not yet (fully) supported on SBC setups. Unfortunately, my macro heavily relies on variables and I don't see a simple alternative to avoid using them. I think your best chance is to ask one of the RRF developers for an outlook on support for SBC-based setups.

      posted in Gcode meta commands
      Schmartundefined
      Schmart
    • RE: Pressure advance tuning with conditional G-code

      @izeman said in Pressure advance tuning with conditional G-code:

      I wanted to try the code as well, and it doesn't seem to work as expected. The nozzle and bed heat up, and then the heater is turned off again as it starts printing it seems.

      I came across this fix in the release notes for RepRapFirmware 3.4.0beta3:

      M568 did not allow the P parameter to be omitted

      So that explains that! 🤗

      posted in Gcode meta commands
      Schmartundefined
      Schmart
    • RE: Pressure advance tuning with conditional G-code

      @heartleander81 About variables and this script running on a SBC, the documentation does still state that "These are supported in RRF 3.3 running in standalone mode". I'm not sure if this still applies, but it might explain your problems.

      posted in Gcode meta commands
      Schmartundefined
      Schmart
    • RE: Pressure advance tuning with conditional G-code

      @heartleander81 said in Pressure advance tuning with conditional G-code:

      Am I correct that the SBC scans the file and since it does not find a clear layer height, the error is there? Is only a guess, since without sbc the error is apparently not there

      Well, it does indeed seem the handling is different compared to running the G-code on a standalone board. Please run the following macro, first with M37 and then with M98. Make sure you've homed the Z-axis yourself, and/or adjust the macro to suit your printer.

      var z = 25.0
      G1 Z{var.z} F600
      

      I think that if this small snippet fails, we should follow up with one of the developers.

      I've also run the code posted by @PCR on my printer as well, with M37, M32 and M98. The only thing that was noticeably different, were some printer-specific adjustments he made, and lines ending with \x0A\x0A (LFLF) characters, while mine uses \x0D\x0A (CRLF). So this code should run fine.

      The test run now at my printer with the 2 error line 139 and 141. Nice work.

      So you're still seeing the weird 'purge_139_flow_ratio' and 'purge_141_flow_ratio' errors? These kind of errors are triggered when a variable is not defined. I can't explain the cause of that. But it seems that the word 'line' in the variable name purge_line_flow_ratio gets substituted with the named constant called 'line' in the firmware part that prepares macro error messages. That also shouldn't happen.

      I can't reproduce this with my firmware build, and I haven't yet tested with an older firmware. Can you check what the following macro yields?

      G1 X{var.a_line_liner} F600
      

      On my printer this results in the following expected error message. It doesn't say unknown variable 'a_1_liner' or unknown variable 'a_1_1r' or something like that:

      1b67efa6-9389-4a6c-b1d3-112343e05db0-image.png

      Furthermore, can you run the macro(s) without the SBC (don't know if that's possible with your setup) and/or share what M122 returns?

      posted in Gcode meta commands
      Schmartundefined
      Schmart
    • RE: Pressure advance tuning with conditional G-code

      @izeman I'm missing text like 'Axes to be homed' in the console, so I'm assuming you had to modify the code for the topology of your printer.

      Can you verify that the code still contains the T{var.tool_number} and M568 P{var.tool_number} S{var.print_temperature} R{var.standby_temperature} A1 statements? This selects the current tool, respectively sets the active and standby temperatures and requests to go to the standby temperature.

      If that's still in there, according to the documentation, the M568 A2 on line 131 should use the current tool (which should be selected with T{var.tool_number}) and therefore M568 should not require the P parameter, but you can try if changing the code M568 A2 (originally on line 131) to M568 P{var.tool_number} A2 helps mitigate the error.

      posted in Gcode meta commands
      Schmartundefined
      Schmart