Duet3D Logo Duet3D
    • Tags
    • Documentation
    • Order
    • Register
    • Login

    Cura Script to Automatically Probe Only Printed Area

    Scheduled Pinned Locked Moved
    General Discussion
    15
    60
    7.5k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • OwenDundefined
      OwenD @Red Sand Robot
      last edited by

      @Red-Sand-Robot
      Double check the content of the script.
      I just tried yesterday and was getting the same error.
      The first time I downloaded, I right clicked the link and selected "download link" or some such.
      Problem was it downloaded the whole HTML page, not just the python script. (DOH!)
      After copying and pasting just the script content it worked fine.

      1 Reply Last reply Reply Quote 0
      • zaptaundefined
        zapta @Red Sand Robot
        last edited by

        @Red-Sand-Robot, I would suggest to first run it manually from the command line. It will give you more information on what's going on. (after verifying that the script download is ok per OwenD's suggestion)

        Red Sand Robotundefined 1 Reply Last reply Reply Quote 0
        • Red Sand Robotundefined
          Red Sand Robot @zapta
          last edited by

          @zapta Command line? As in my computer or PrusaSlicer? Sorry, the programming side of things isn't my forte.

          almost an engineer

          zaptaundefined 1 Reply Last reply Reply Quote 0
          • zaptaundefined
            zapta @Red Sand Robot
            last edited by

            @Red-Sand-Robot, it seems that you work on windows. I meant commands types in the DOS window. I don't have here a Windows computer but others here may be able to explain it better.

            https://www.lifewire.com/how-to-open-command-prompt-2618089

            Red Sand Robotundefined 1 Reply Last reply Reply Quote 0
            • Red Sand Robotundefined
              Red Sand Robot @zapta
              last edited by

              @zapta Yes, I work on windows. Haven't made the jump to attempt to learn Linux or associated stuff yet though I suppose I should if I ever want to use a RPI for anything useful.

              When I ran the script from the command prompt window, I received this output:

              C:\Users\Jack>AppData\Local\Programs\Python\Python38-32\python.exe "C:\Users\Jack\Documents\3D Printing\PrusaSlicerScript\duet3d_automesh.py"
              usage: duet3d_automesh.py [-h] [--meshable MESHABLE] [--margin MARGIN] [--spacing SPACING]
                                        [--min_points {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49}]
                                        [--max_points {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49}]
                                        [--first_layer_start FIRST_LAYER_START] [--first_layer_end FIRST_LAYER_END]
                                        file_path
              duet3d_automesh.py: error: the following arguments are required: file_path
              

              Does that look like it is functioning correctly? I'm guessing that the file_path argument is talking about the .gcode file, but if not that would indicate an error with how I'm calling the script from what I understand.

              almost an engineer

              1 Reply Last reply Reply Quote 0
              • Red Sand Robotundefined
                Red Sand Robot
                last edited by

                @zapta Figured out that my main issue was that I never changed the meshable area parameters to match my machine. I guess I just assumed when it says "default" that the script would also parse that stuff from the slicer, but in hindsight the script doesn't function that way, it only looks at the .gcode file after it has been output by the slicer.

                Leaving output up as an example of what it looks like the user does not properly define the meshable area in regards to their machine. My machine has -100:100,-100:100 for a meshable area.

                C:\Users\Jack>C:\Users\Jack\AppData\Local\Programs\Python\Python38-32\python.exe C:\Users\Jack\Documents\3D-Printing\PrusaSlicerScript\duet3d_automesh.py C:\Users\Jack\Documents\3D-Printing\Stuff\flex_tube_support2.gcode
                MESHABLE area: 30.0:280.0,30.0:280.0
                Opening gcode file: C:\Users\Jack\Documents\3D-Printing\Stuff\flex_tube_support2.gcode
                Read 33095 lines
                Parsing state = ParsingState.WAITING_FOR_LAYER1
                ; Automesh: begin layer 0
                Parsing state = ParsingState.IN_LAYER1
                ; Automesh: begin layer 1
                Parsing state = ParsingState.LAYER1_DONE
                First layer print areas: -13:13,-48:48
                printArea: -13:13,-48:48
                Traceback (most recent call last):
                  File "C:\Users\Jack\Documents\3D-Printing\PrusaSlicerScript\duet3d_automesh.py", line 371, in <module>
                    main()
                  File "C:\Users\Jack\Documents\3D-Printing\PrusaSlicerScript\duet3d_automesh.py", line 218, in main
                    mesh_area.clip_to(MESHABLE_AREA)
                  File "C:\Users\Jack\Documents\3D-Printing\PrusaSlicerScript\duet3d_automesh.py", line 165, in clip_to
                    self.x_span.clip_to(other_rect.x_span)
                  File "C:\Users\Jack\Documents\3D-Printing\PrusaSlicerScript\duet3d_automesh.py", line 126, in clip_to
                    self.__check()
                  File "C:\Users\Jack\Documents\3D-Printing\PrusaSlicerScript\duet3d_automesh.py", line 105, in __check
                    fatal_error(f'Invalid range value: {self}')
                  File "C:\Users\Jack\Documents\3D-Printing\PrusaSlicerScript\duet3d_automesh.py", line 83, in fatal_error
                    raise Exception('Fatal error: ' + message)
                Exception: Fatal error: Invalid range value: 30.0:23
                

                almost an engineer

                zaptaundefined 1 Reply Last reply Reply Quote 0
                • zaptaundefined
                  zapta @Red Sand Robot
                  last edited by zapta

                  @Red-Sand-Robot, I think we are almost there 😉

                  Can you post here your file flex_tube_support2.gcode ? I will try to run it myself and check that error.

                  Edit: to set your own meshable area, add to the command line the following flag:

                  --meshable=-100:100,-100:100

                  Red Sand Robotundefined 1 Reply Last reply Reply Quote 0
                  • Red Sand Robotundefined
                    Red Sand Robot @zapta
                    last edited by

                    @zapta Would --meshable=-100:100,-100:100 go after the output path specification in PrusaSlicer? I just manually changed the default in the script.

                    parser.add_argument('--meshable',
                                        default="-100:100,-100:100",
                                        help='Bed meshable area x1:x2,y1:y2')
                    

                    Here is the .gcode file. I have my 0,0 point in the center of the build plate. I know that you previously added support for negative positional values so I don't think that would be the issue
                    flex_tube_support2.gcode

                    almost an engineer

                    zaptaundefined 1 Reply Last reply Reply Quote 0
                    • tcjundefined
                      tcj @Baenwort
                      last edited by

                      @Baenwort said in Cura Script to Automatically Probe Only Printed Area:

                      Does this bring it closer to working for Delta's? Or at least probing a square area of the circular bed?

                      Yes, this makes it usable for delta's, because
                      "For Cartesian printers, specify minimum and maximum X and Y values to probe and the probing interval. For Delta printers, specify the probing radius. If you define both, the probing area will be the intersection of the rectangular area and the circle. "
                      https://duet3d.dozuki.com/Wiki/Gcode#Section_M557_Set_Z_probe_point_or_define_probing_grid

                      Baenwortundefined 2 Replies Last reply Reply Quote 1
                      • zaptaundefined
                        zapta @Red Sand Robot
                        last edited by zapta

                        @Red-Sand-Robot, the change that you made in the program, changing the default meshable area to -100:100 is correct. Just remember to apply it again if you will download a newer version of this script.

                        I run your gcode file and got this which looks ok to me

                        MESHABLE area: -100.0:100.0,-100.0:100.0
                        Opening gcode file: flex.gcode
                        Read 33099 lines
                        Parsing state = ParsingState.WAITING_FOR_LAYER1
                        ; Automesh: begin layer 0
                        Parsing state = ParsingState.IN_LAYER1
                        ; Automesh: begin layer 1
                        Parsing state = ParsingState.LAYER1_DONE
                        First layer print areas: -13:13,-48:48
                        printArea: -13:13,-48:48
                        Will use 3 x 4 mesh points
                        Marker: M557 X-23:23 Y-58:58 P3:4
                        Inserted: M557 X-23:23 Y-58:58 P3:4
                        

                        Does it work for you now or are there still problems?

                        Edit: for your question the -- flags should be specified immediately after the .py script name.

                        1 Reply Last reply Reply Quote 0
                        • Baenwortundefined
                          Baenwort @tcj
                          last edited by Baenwort

                          @tcj Great! I'll give it a try on my next print.

                          Do you think it will use the Delta probe interval for the new area or one specified by the gcode?

                          1 Reply Last reply Reply Quote 0
                          • Baenwortundefined
                            Baenwort @tcj
                            last edited by

                            @tcj So I'm still learning Cura after switching from using MatterControl since I got my Rostock v3. I tried adding the duet3d_automesh.py from zapta's github to the scripts area of the postprocessing folder of Cura's plugin folder.

                            I do have Python 3 installed (3.8.3 to be exact) but this seems to make no difference to it showing up in Cura. 😕

                            zaptaundefined 1 Reply Last reply Reply Quote 0
                            • zaptaundefined
                              zapta @Baenwort
                              last edited by

                              @Baenwort, can you provide a little bit more info. E.g. what computer you are using (windows? mac? linux?) and how you set up the script in cura.

                              Baenwortundefined 1 Reply Last reply Reply Quote 0
                              • Baenwortundefined
                                Baenwort @zapta
                                last edited by

                                @zapta

                                Window's but I also tried it with a Ubuntu 20.04 Cura install. Both are Cura 4.6 and the OP and most post processing scripts direct placing the .py file in the plugin post processing directory.

                                I can get you the exact tree when I get.back to my home tonight.

                                1 Reply Last reply Reply Quote 0
                                • sandozundefined
                                  sandoz
                                  last edited by

                                  I have installed this into Cura 4.62 and using on my Railcore II. When it goes to run it always defines my X end point as being 1800.

                                  M557 X103.850:1800.000 Y115.304:164.705 S10.000 ; Leveling mesh defined by LevelingMeshOptimizer

                                  Railcore of course doesn't like this and then just skips the mesh:

                                  Error: M557: bad grid definition: Too many grid points; suggest increase spacing to 44.2mm
                                  Error: G29: No valid grid defined for bed probing

                                  Is there something I am doing wrong?

                                  zaptaundefined 1 Reply Last reply Reply Quote 0
                                  • zaptaundefined
                                    zapta @sandoz
                                    last edited by

                                    @sandoz said in Cura Script to Automatically Probe Only Printed Area:

                                    Is there something I am doing wrong?

                                    My guess is that there is a X1800 somewhere in the gcode that is picked by the script. Can you post the gcode file?

                                    sandozundefined 1 Reply Last reply Reply Quote 1
                                    • sandozundefined
                                      sandoz @zapta
                                      last edited by sandoz

                                      @zapta Alright.. using your comments as a place to start I found that having my Jerk control turned on in Cura led to some M566 x1800 commands that it was picking up. I turned off Curas control and just set it in the machine settings and now it slices properly.

                                      zaptaundefined 1 Reply Last reply Reply Quote 1
                                      • zaptaundefined
                                        zapta @sandoz
                                        last edited by

                                        @sandoz, the code is dump, looking for X<number> in the text regardless of the command, may even look in comments, I am not sure. If needed, it can be restricted it to a set of commands such as G0, G1, etc.

                                        1 Reply Last reply Reply Quote 0
                                        • MartinNYHCundefined
                                          MartinNYHC
                                          last edited by

                                          I wanted to set the probe spacing according to the size of the model. If it's <= 100 spacing should be 20, if it's >100 spacing should be 50.

                                          Need to find the proper size/spacing values, but if anyone is interested, here's the diff:

                                          128,135d127
                                          <     sizeX = int(bounds['X']['max'] - bounds['X']['min'])
                                          <     sizeY = int(bounds['Y']['max'] - bounds['Y']['min'])
                                          <
                                          <     if sizeX >= 100 or sizeY >= 100:
                                          <         bounds['S'] = 50
                                          <     else:
                                          <         bounds['S'] = 20
                                          <
                                          137c129
                                          <     gridNew = 'M557 X%d:%d Y%d:%d S%d' % (
                                          ---
                                          >     gridNew = 'M557 X%d:%d Y%d:%d' % (
                                          140d131
                                          <         bounds['S'],
                                          147c138
                                          <             linesNew.append(re.sub(r'^M557 X\d+:\d+ Y\d+:\d+ S\d+', gridNew, line, flags=re.MULTILINE))
                                          ---
                                          >             linesNew.append(re.sub(r'^M557 X\d+:\d+ Y\d+:\d+', gridNew, line, flags=re.MULTILINE))
                                          
                                          
                                          1 Reply Last reply Reply Quote 0
                                          • nick9one1undefined
                                            nick9one1
                                            last edited by nick9one1

                                            I'm trying to get this working in superslicer but get an error. Can anyone help?

                                            Capture.PNG

                                            in post-processing script

                                            C:\Users\mumby\.windows-build-tools\python27\python.exe "C:\Program Files\SuperSlicer\meshgrid.py"
                                            

                                            my start gcode (added M557)

                                            M557 X20:292 Y15:300 P5                      	; define mesh grid
                                            M104 S[first_layer_temperature]                   ; set extruder temp
                                            M140 S[first_layer_bed_temperature]           ; set bed temp
                                            G21			                   ; metric values
                                            M190 S[first_layer_bed_temperature]           ; wait for bed temp
                                            G28			                   ; home all
                                            G32			                   ; Run mesh grid compenstion
                                            G92 E0 F100			; reset extruder
                                            G1 Z10 F3000 			; move z up little to prevent scratching of surface
                                            M109 S[first_layer_temperature]                    ; wait for extruder temp
                                            M98 P"0:/macros/Filament/Clean Nozzle"	; Clean nozzle macro
                                            M98 P/macros/Filament/Purge		; Run nozzle purge macro
                                            G1 E-3 F2000		                  ; Retract 3mm filament
                                            G92 E0 		                                     ; reset extruder
                                            
                                            

                                            this script itself (coped from an earlier post here)

                                            #!/usr/bin/env python3
                                            """Simplify3D post-processing script for RepRap firmware printers which dynamically defines the mesh grid dimensions (M557) based on the print dimensions. 
                                            {1}
                                            Usage:
                                            {1}
                                                Within Simplify3D > Process Settings > Scripts > Post Processing > add the following command:
                                                    python3 <script_location>/meshgrid.py "[output_filepath]"
                                                
                                                Starting script must contain M557 Command (ie M557 X30:300 Y30:300 P20).
                                            {1}
                                            Args:
                                            {1}
                                                Path: Complete path to the gcode file created by Simplify 3d.
                                            {1}
                                            Requirements:
                                            {1}
                                                Tested using Python 3.8.1.
                                            {1}
                                            Credit:
                                            {1}
                                                Adapted from code originally posted by CCS86 on https://forum.duet3d.com/topic/15302/cura-script-to-automatically-probe-only-printed-area?_=1587348242875.
                                            {1}
                                            """
                                            import sys
                                            import re
                                            import math
                                             
                                            def main(filename):
                                                
                                                try:
                                                    _s3dFile = open(filename, encoding='utf-8')
                                             
                                                except TypeError:
                                                    try:
                                                        _s3dFile = open(filename)
                                                        
                                                    except:
                                                        print("Open file exception. Exiting meshgrid.py.")
                                                        sys.exit()
                                                
                                                except FileNotFoundError:
                                                    print('File not found. Exiting meshgrid.py.')
                                                    sys.exit()
                                                
                                                lines = _s3dFile.readlines()
                                                _s3dFile.close()
                                             
                                                linesNew = calcBed(lines)
                                             
                                                _s3dFileNew = open(filename, "r+")
                                                _s3dFileNew.seek(0)                       
                                                _s3dFileNew.truncate()
                                                for element in linesNew:
                                                    _s3dFileNew.write(element)
                                                _s3dFileNew.close()
                                             
                                                return
                                             
                                             
                                            def calcBed(lines):
                                             
                                                bounds = findBounds(lines)
                                                bed = findBed(lines)
                                             
                                                for axis in bounds:
                                                    if bounds[axis]['max'] - bounds[axis]['min'] < bed[axis]:
                                                        print(f'Success: {axis} mesh is smaller than bed')
                                                        
                                                    else:
                                                        print('Error: Mesh is larger than bed. Exiting meshgrid.py.')
                                                        sys.exit()
                                             
                                                    for limit in bounds[axis]:
                                                        if limit == 'min':
                                                            if (bed[axis] / 2) - bounds[axis][limit] > 0: 
                                                                print (f'Success: {axis} {limit} coordinate is on the bed.')
                                                            else:
                                                                print (f'Error: {axis} {limit} coordinate is off the bed. Exiting meshgrid.py.')
                                                                sys.exit()
                                             
                                                        if limit == 'max':
                                                            if (bed[axis]) - bounds[axis][limit] > 0: 
                                                                print (f'Success: {axis} {limit} coordinate is on the bed.')
                                                            else:
                                                                print (f'Error: {axis} {limit} coordinate is off the bed. Exiting meshgrid.py.')
                                                                sys.exit()
                                             
                                                return fillGrid(bounds, lines)
                                             
                                                
                                            def findBed(lines):
                                                bed = {
                                                    'X': 0,
                                                    'Y': 0,
                                                    }
                                             
                                                for line in lines:
                                                    if line.startswith(';   strokeXoverride,'):
                                                        bed['X'] = int(re.search(r'\d.+\S', line).group())
                                                    elif line.startswith(';   strokeYoverride,'):
                                                        bed['Y'] = int(re.search(r'\d.+', line).group())
                                                        break
                                                        
                                                return bed
                                             
                                             
                                            def findBounds(lines):
                                                bounds = {
                                                    'X': {'min': 9999, 'max': 0},
                                                    'Y': {'min': 9999, 'max': 0},
                                                    }
                                                parsing = False
                                                for line in lines:
                                                    if line.startswith('; layer 1,'):
                                                        parsing = True
                                                        continue
                                                    elif line.startswith('; layer 2,'):
                                                        break
                                             
                                                    if parsing:
                                                        # Get coordinates on this line
                                                        for match in re.findall(r'([YX])([\d.]+)\s', line):
                                                            # Get axis letter
                                                            axis = match[0]
                                             
                                                            # Skip axes we don't care about
                                                            if axis not in bounds:
                                                                continue
                                             
                                                            # Parse parameter value
                                                            value = float(match[1])
                                             
                                                            # Update bounds
                                                            bounds[axis]['min'] = math.floor(min(bounds[axis]['min'], value))
                                                            bounds[axis]['max'] = math.ceil(max(bounds[axis]['max'], value))
                                             
                                                return bounds
                                             
                                             
                                            def fillGrid(bounds, lines):
                                                # Fill in the level command template
                                                gridNew = 'M557 X%d:%d Y%d:%d' % (
                                                    bounds['X']['min'], bounds['X']['max'],
                                                    bounds['Y']['min'], bounds['Y']['max'],
                                                )
                                             
                                                # Replace M557 command in GCODE
                                                linesNew = []
                                                for line in lines:
                                                    if line.startswith('M557'):
                                                        linesNew.append(re.sub(r'^M557 X\d+:\d+ Y\d+:\d+', gridNew, line, flags=re.MULTILINE))
                                                    else:
                                                        linesNew.append(line)
                                                return linesNew
                                             
                                             
                                            if __name__ == '__main__':
                                                if sys.argv[1]:
                                                    main(filename = sys.argv[1])
                                                else:
                                                    print('Error: Proper s3d post processing command is python3 <script_location>/meshgrid.py "[output_filepath]". Exiting meshgrid.py.')
                                                    sys.exit()
                                            
                                            PCRundefined 1 Reply Last reply Reply Quote 0
                                            • Phaedruxundefined Phaedrux referenced this topic
                                            • First post
                                              Last post
                                            Unless otherwise noted, all forum content is licensed under CC-BY-SA