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

    Getting actual spindle speed from "M3 R1" Mcode

    Scheduled Pinned Locked Moved
    DSF Development
    3
    15
    693
    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.
    • CthulhuLabsundefined
      CthulhuLabs
      last edited by

      I am running the 3.4.5 release of RRF right now. I have a BLDC Spindle controlled by an ODrive Pro controller. I am controlling the ODrive using a DSF Python script. It works fantastically. My Python script is intercepting M3, M4, and M5 Mcodes and sending the ODrive commands over USB. I finally got around to implementing my pause.g and resume.g files. Pause.g is working great as it just calls M5 and moves out of the way. The problem I have is the M3 R1 in my resume.g script. My DSF Python script is getting sent the R1 parameter and I am not sure how to resolve that into the spindle speed prior to the M5 Mcode in the pause.g. Looking at the Object Module I would think that I need to resolve {state.restorePoints[0].spindleSpeeds[0]} to get the value but when I run this code:

      def getRRFVar( dsf_variable, channel):
      	command_connection = CommandConnection(debug=True)
      	command_connection.connect()
      
      	try:
      		res = command_connection.perform_command(evaluate_expression( channel, dsf_variable ))
      		result = res.result
      		print(f"Evaluated expression: {result}")
      	finally:
      		command_connection.close()
      
      	return result
      
      ....
      
      # M3
      if cde.majorNumber == 3 :
      	if cde.parameter("R") :
      		rpm = getRRFVar( '{state.restorePoints[0].spindleSpeeds[0]}', cde.channel )
      		setRPS( float(int(rpm) / 60) )
      		intercept_connection.resolve_code(MessageType.Success, "ODrive RPM set to " + str(rpm) )
      	else :
      		if cde.parameter("S") is None :
      			# Let DCS know there was an ERROR
      			intercept_connection.resolve_code(MessageType.Error, "M3 must include an S parameter")
      		else :
      			rpm = cde.parameter("S").string_value
      			if cde.parameter("S").is_expression :
      				rpm = getRRFVar( rpm, cde.channel )
      			setRPS( float(int(rpm) / 60) )
      			# Resolve it so that DCS knows we took care of it
      			intercept_connection.resolve_code(MessageType.Success, "ODrive RPM set to " + str(rpm) )
      

      I get:

      Closing connection:  Internal Server Exception
      Traceback (most recent call last):
        File "odrivespindle2.py", line 115, in start_intercept
          rpm = getRRFVar( '{state.restorePoints[0].spindleSpeeds[0]}', cde.channel )
        File "odrivespindle2.py", line 74, in getRRFVar
          res = command_connection.perform_command(evaluate_expression( channel, dsf_variable ))
        File "/usr/local/lib/python3.7/dist-packages/dsf_python-3.4.5.post2-py3.7.egg/dsf/connections/base_connection.py", line 65, in perform_command
          command, response.error_type, response.error_message
      dsf.connections.exceptions.InternalServerException: Internal Server Exception
      

      I will say that Python is not my strongest programming language so it is possible I just have a syntax error or something. It is also possible I am just being dumb.

      chrishammundefined 1 Reply Last reply Reply Quote 0
      • chrishammundefined
        chrishamm administrators @CthulhuLabs
        last edited by

        @CthulhuLabs Some notes:

        1. When you intercept a code like M3, you must flush the code channel to ensure that pending codes have been executed before you actually handle it. Otherwise, you'll see out-of-order execution. See the other examples from the dsf-python library.

        2. Does echo state.restorePoints[0].spindleSpeeds[0] work from DWC?

        3. It looks like there is already some handling for S parameter expressions in your code. If 2. works, does M3 S{state.restorePoints[0].spindleSpeeds[0]} work?

        Duet software engineer

        CthulhuLabsundefined 1 Reply Last reply Reply Quote 1
        • CthulhuLabsundefined
          CthulhuLabs @chrishamm
          last edited by

          @chrishamm

          1. I am flushing. I left out a good chunk of my code. Here is the full code:
          #!/usr/bin/env python3
          """
          Script for controlling an ODrive powered BDLC spindle
          """
          
          import subprocess
          import traceback
          
          from dsf.connections import InterceptConnection, InterceptionMode, CommandConnection
          from dsf.commands.code import CodeType
          from dsf.commands.generic import evaluate_expression
          from dsf.commands.code_channel import CodeChannel
          from dsf.object_model import MessageType
          
          import serial
          from time import sleep
          
          port = '/dev/ttyACM0'   # serial port
          baud = 115200           # baudrate
          timeout = 1             # read timeout
          
          
          # function to return the serial port or none if it fails
          def getSerial(portname,baud,to):
              try:
                  return serial.Serial(port=portname,baudrate=baud,timeout=to)
              except:
                  return None
          
          
          # function to set the ODrive rps (Rotations Per Second#)
          def setRPS(rps):
              attempt = True
              while attempt:
                  ser = getSerial(port,baud,timeout)
                  if (ser is not None):
                      # get the current state of the ODrive
                      ser.write(b'r axis0.current_state\n')
                      axisState = ser.readline().decode().strip()
          
                      # get the currently set velocity in RPS
                      ser.write(b'r axis0.controller.input_vel\n')
                      reqVel = ser.readline().decode().strip()
          
                      # check if ODrive is ideal and the desired rps is not 0
                      if (( axisState == "1" ) and ( rps != 0 )):
                          # if so set the ODrive state to 8 (CLOSED LOOP CONTROL)
                          ser.write(b'w axis0.requested_state 8\n')
                          ser.flush()
          
                      # check if ODrive is not ideal and the desired rps is 0
                      if (( axisState != "1" ) and ( rps == 0)):
                          # if so set the ODrive state to 1 (IDLE)
                          ser.write(b'w axis0.requested_state 1\n')
                          ser.flush()
          
                      # check if the current set velocity is not the desired rps
                      if ( float(reqVel) != rps ):
                          # if so set the velocity to rps
                          command = str.encode('w axis0.controller.input_vel %.6f\n' %rps )
                          ser.write(command)
                          ser.flush()
          
                      ser.close()
                      attempt = False
                  else:
                      sleep(0.1)
          
          def getRRFVar( dsf_variable, channel):
              command_connection = CommandConnection(debug=True)
              command_connection.connect()
          
              try:
                  res = command_connection.perform_command(evaluate_expression( channel, dsf_variable ))
                  result = res.result
                  print(f"Evaluated expression: {result}")
              finally:
                  command_connection.close()
          
              return result
          
          
          def start_intercept():
              filters = ["M3","M4","M5"]
              intercept_connection = InterceptConnection(InterceptionMode.PRE, filters=filters, debug=True)
          
              while True:
                  intercept_connection.connect()
          
                  try:
                      while True:
                          # Wait for a code to arrive
                          cde = intercept_connection.receive_code()
          
                          # Check for the type of the code
                          if cde.type == CodeType.MCode and cde.majorNumber in [3,4,5]:
                              # --------------- BEGIN FLUSH ---------------------
                              # Flushing is only necessary if the action below needs to be in sync with the machine
                              # at this point in the GCode stream. Otherwise it can an should be skipped
          
                              # Flush the code's channel to be sure we are being in sync with the machine
                              success = intercept_connection.flush(cde.channel)
          
                              # Flushing failed so we need to cancel our code
                              if not success:
                                  print("Flush failed")
                                  intercept_connection.cancel_code()
                                  continue
                              # -------------- END FLUSH ------------------------
          
                              # M3
                              if cde.majorNumber == 3 :
          
                                  if cde.parameter("R") :
                                      rpm = getRRFVar( '{state.restorePoints[0].spindleSpeeds[0]}', cde.channel )
                                      setRPS( float(int(rpm) / 60) )
                                      intercept_connection.resolve_code(MessageType.Success, "ODrive RPM set to " + str(rpm) )
                                  else :
                                      if cde.parameter("S") is None :
                                          # Let DCS know there was an ERROR
                                          intercept_connection.resolve_code(MessageType.Error, "M3 must include an S parameter")
                                      else :
                                          rpm = cde.parameter("S").string_value
                                          if cde.parameter("S").is_expression :
                                              rpm = getRRFVar( rpm, cde.channel )
                                          setRPS( float(int(rpm) / 60) )
                                          # Resolve it so that DCS knows we took care of it
                                          intercept_connection.resolve_code(MessageType.Success, "ODrive RPM set to " + str(rpm) )
          
                              # M4
                              if cde.majorNumber == 4:
                                  if cde.parameter("S") is None :
                                      # Let DCS know there was an ERROR
                                      intercept_connection.resolve_code(MessageType.Error, "M4 must include an S parameter")
                                  else :
                                      rpm = cde.parameter("S").string_value
                                      if cde.parameter("S").is_expression :
                                          rpm = getRRFVar( rpm, cde.channel )
                                      setRPS( -( float(int(rpm) / 60) ) )
                                      # Resolve it so that DCS knows we took care of it
                                      intercept_connection.resolve_code(MessageType.Success, "Ordive RPM set to -" + str(rpm) )
          
                              # M5
                              if cde.majorNumber == 5:
                                  setRPS( 0 )
                                  # Resolve it so that DCS knows we took care of it
                                  intercept_connection.resolve_code(MessageType.Success, "ODrive RPM set to 0")
                          else:
                              # We did not handle it so we ignore it and it will be continued to be processed
                              intercept_connection.ignore_code()
                  except Exception as e:
                      print("Closing connection: ", e)
                      traceback.print_exc()
                      intercept_connection.close()
          
                  sleep(5)
          
          
          if __name__ == "__main__":
              start_intercept()
          
          1. I get this:
          echo state.restorePoints[0].spindleSpeeds[0]
          Error: Failed to evaluate "state.restorePoints[0].spindleSpeeds[0]": unknown value 'spindleSpeeds^'
          

          which I guess is the real issue here. I also tried:

          echo state.restorePoints[0].spindleSpeeds[1]
          Error: Failed to evaluate "state.restorePoints[0].spindleSpeeds[1]": unknown value 'spindleSpeeds^'
          
          echo state.restorePoints[1].spindleSpeeds[0]
          Error: Failed to evaluate "state.restorePoints[1].spindleSpeeds[0]": unknown value 'spindleSpeeds^'
          
          echo state.restorePoints[1].spindleSpeeds[1]
          Error: Failed to evaluate "state.restorePoints[1].spindleSpeeds[1]": unknown value 'spindleSpeeds^'
          

          I even tried with the the tool number:

          echo state.restorePoints[0].spindleSpeeds[251]
          Error: Failed to evaluate "state.restorePoints[0].spindleSpeeds[251]": unknown value 'spindleSpeeds^'
          
          1. I know that if I put:
          M3 S{state.restorePoints[0].spindleSpeeds[0]}
          

          in my resume.g file my ODrive control script would just get "{state.restorePoints[0].spindleSpeeds[0]}" as the string_value for cde.parameter("S") so I figured I would try doing this in my resume.g:

          var ResumeRPM = 0;
          set var.ResumeRPM = {state.restorePoints[0].spindleSpeeds[0]}
          
          ; turn the spindle back on
          M3 S{var.ResumeRPM}
          

          This way {state.restorePoints[0].spindleSpeeds[0]} would get resolved by RRF and not my code. Unfortunately {state.restorePoints[0].spindleSpeeds[0]} does not actually evaluate.

          So the question now is where is the my spindle speed getting save to? Could it have something to do with the fact that I am also intercepting the M5 Mcode so the save state for the spindle is not actually getting created?

          chrishammundefined 1 Reply Last reply Reply Quote 0
          • chrishammundefined
            chrishamm administrators @CthulhuLabs
            last edited by

            @CthulhuLabs It looks like state.restorePoints[].spindleSpeeds[] isn't available from RRF, so the evaluation (obviously) fails. I guess it was planned at some point when the spindle code was refactored but then it was discarded again without an update of the DSF/DWC OM definitions. @dc42 Any thoughts about this?

            I suggest you save the current spindle value in pause.g to a global variable and then restore it in resume.g. The spindles[] root key is definitely available.

            Duet software engineer

            CthulhuLabsundefined 1 Reply Last reply Reply Quote 1
            • CthulhuLabsundefined
              CthulhuLabs @chrishamm
              last edited by CthulhuLabs

              @chrishamm I switched:

              intercept_connection.resolve_code(MessageType.Success, "ODrive RPM set to 0")
              

              for the M5 command to:

              intercept_connection.ignore_code()
              

              and now the state.restorePoints[0].spindleSpeeds[0] is getting set. However when I try to resume my M3 code is getting executed but my spindle does not change speed. Then the entire tool path is skilled up until my next tool change. Let me get a full run's debug output for my script and ill post it.

              CthulhuLabsundefined 1 Reply Last reply Reply Quote 0
              • CthulhuLabsundefined
                CthulhuLabs @CthulhuLabs
                last edited by

                Sorry I was mistaken. My code is not getting executed. It is still throwing this error:

                Error: {state.restorePoints[0].spindleSpeeds[0]}unknown value 'spindleSpeeds^' of resume.g
                

                to the console and my code is NOT getting executed.

                dc42undefined 1 Reply Last reply Reply Quote 0
                • dc42undefined
                  dc42 administrators @CthulhuLabs
                  last edited by

                  @CthulhuLabs currently the state saved in a restore point does not include the spindle speed(s). So I suggest that in pause.g you save the spindle speed in a global variable as @chrishamm suggested.

                  Duet WiFi hardware designer and firmware engineer
                  Please do not ask me for Duet support via PM or email, use the forum
                  http://www.escher3d.com, https://miscsolutions.wordpress.com

                  CthulhuLabsundefined 1 Reply Last reply Reply Quote 1
                  • CthulhuLabsundefined
                    CthulhuLabs @dc42
                    last edited by

                    @dc42 ahhh I saw it in the object model:

                    https://github.com/Duet3D/RepRapFirmware/wiki/Object-Model-Documentation#staterestorepointsspindlespeeds

                    that is a good alternative though. So I should do something like:

                    set global.PauseRPM = spindles[0].active

                    CthulhuLabsundefined 1 Reply Last reply Reply Quote 0
                    • CthulhuLabsundefined
                      CthulhuLabs @CthulhuLabs
                      last edited by

                      Well "set global.PauseRPM = spindles[0].active" is evaluating to 0 so something is not right there. Also when I resume it is moving to the position it paused at which is good but it is not completing the tool path. Instead it is prompting me to change tools and continues on with the next tool path.

                      CthulhuLabsundefined 1 Reply Last reply Reply Quote 0
                      • CthulhuLabsundefined
                        CthulhuLabs @CthulhuLabs
                        last edited by CthulhuLabs

                        I commented out all the code in pause.g and resume.g related to the spindle and it is still skipping the rest of the tool path on resume.

                        CthulhuLabsundefined 1 Reply Last reply Reply Quote 0
                        • CthulhuLabsundefined
                          CthulhuLabs @CthulhuLabs
                          last edited by

                          I fixed the toolpath issue. My CAD software post processor was generating tool paths like this:

                          G1Z-1.016F304.8
                          X-163.747Y-122.162F1524.0
                          X-162.316Y-123.371
                          X-160.765Y-124.590
                          X-159.269Y-125.682
                          X-157.679Y-126.760
                          X-156.063Y-127.774
                          X-154.436Y-128.716
                          X-152.260Y-129.862
                          X-152.125Y-129.929
                          X-149.886Y-130.974
                          X-147.582Y-131.923
                          X-145.238Y-132.763
                          X-145.096Y-132.810
                          X-142.726Y-133.532
                          X-140.273Y-134.157
                          

                          I switched it to generate tool paths like this:

                          G1Z-1.016F304.8
                          G1X-163.747Y-122.162F1524.0
                          G1X-162.316Y-123.371
                          G1X-160.765Y-124.590
                          G1X-159.269Y-125.682
                          G1X-157.679Y-126.760
                          G1X-156.063Y-127.774
                          G1X-154.436Y-128.716
                          G1X-152.260Y-129.862
                          G1X-152.125Y-129.929
                          G1X-149.886Y-130.974
                          G1X-147.582Y-131.923
                          G1X-145.238Y-132.763
                          G1X-145.096Y-132.810
                          G1X-142.726Y-133.532
                          G1X-140.273Y-134.157
                          

                          I guess resume doesn't know how to pick up in the middle of a G1/0 command.

                          As for the M5 and M3 I think I am going to make my dsf-python script save the spindle speed to an internal LastRPM variable on M5 and restore that when it gets an M3 R1. Ill let you know if that works.

                          CthulhuLabsundefined dc42undefined 2 Replies Last reply Reply Quote 0
                          • CthulhuLabsundefined
                            CthulhuLabs @CthulhuLabs
                            last edited by

                            That did it. @chrishamm @dc42 thank you for your help.

                            CthulhuLabsundefined 1 Reply Last reply Reply Quote 1
                            • CthulhuLabsundefined
                              CthulhuLabs @CthulhuLabs
                              last edited by

                              Incase someone finds this thread and is looking to do something similar here is my dsf-python script for controlling my ODrive Spindle:

                              #!/usr/bin/env python3
                              """
                              Script for controlling an ODrive powered BDLC spindle
                              """
                              
                              import subprocess
                              import traceback
                              
                              from dsf.connections import InterceptConnection, InterceptionMode, CommandConnection
                              from dsf.commands.code import CodeType
                              from dsf.commands.generic import evaluate_expression
                              from dsf.commands.code_channel import CodeChannel
                              from dsf.object_model import MessageType
                              
                              import serial
                              from time import sleep
                              
                              port = '/dev/ttyACM0'   # serial port
                              baud = 115200           # baudrate
                              timeout = 1             # read timeout
                              LastRPS = 0             # What the spindle speed was in RPS before the last M5 was called
                              
                              
                              # function to return the serial port or none if it fails
                              def getSerial(portname,baud,to):
                                  try:
                                      return serial.Serial(port=portname,baudrate=baud,timeout=to)
                                  except:
                                      return None
                              
                              
                              # function to set the ODrive rps (Rotations Per Second#)
                              def setRPS(rps):
                                  attempt = True
                                  while attempt:
                                      ser = getSerial(port,baud,timeout)
                                      if (ser is not None):
                                          # get the current state of the ODrive
                                          ser.write(b'r axis0.current_state\n')
                                          axisState = ser.readline().decode().strip()
                              
                                          # get the currently set velocity in RPS
                                          ser.write(b'r axis0.controller.input_vel\n')
                                          reqVel = ser.readline().decode().strip()
                              
                                          # check if ODrive is ideal and the desired rps is not 0
                                          if (( axisState == "1" ) and ( rps != 0 )):
                                              # if so set the ODrive state to 8 (CLOSED LOOP CONTROL)
                                              ser.write(b'w axis0.requested_state 8\n')
                                              ser.flush()
                              
                                          # check if ODrive is not ideal and the desired rps is 0
                                          if (( axisState != "1" ) and ( rps == 0)):
                                              # if so set the ODrive state to 1 (IDLE)
                                              ser.write(b'w axis0.requested_state 1\n')
                                              ser.flush()
                              
                                          # check if the current set velocity is not the desired rps
                                          if ( float(reqVel) != rps ):
                                              # if so set the velocity to rps
                                              command = str.encode('w axis0.controller.input_vel %.6f\n' %rps )
                                              ser.write(command)
                                              ser.flush()
                              
                                          ser.close()
                                          attempt = False
                                      else:
                                          sleep(0.1)
                              
                              
                              # function to get the ODrive rps (Rotations Per Second#)
                              def getRPS():
                                  attempt = True
                                  rps = 0
                                  while attempt:
                                      ser = getSerial(port,baud,timeout)
                                      if (ser is not None):
                                          # get the currently set velocity in RPS
                                          ser.write(b'r axis0.controller.input_vel\n')
                                          rps = float(ser.readline().decode().strip())
                              
                                          ser.close()
                                          attempt = False
                                      else:
                                          sleep(0.1)
                              
                                  return rps
                              
                              
                              
                              def getRRFVar( dsf_variable, channel):
                                  command_connection = CommandConnection(debug=True)
                                  command_connection.connect()
                              
                                  try:
                                      res = command_connection.perform_command(evaluate_expression( channel, dsf_variable ))
                                      result = res.result
                                      print(f"Evaluated expression: {result}")
                                  finally:
                                      command_connection.close()
                              
                                  return result
                              
                              
                              def start_intercept():
                                  filters = ["M3","M4","M5"]
                                  intercept_connection = InterceptConnection(InterceptionMode.PRE, filters=filters, debug=True)
                              
                                  while True:
                                      intercept_connection.connect()
                              
                                      try:
                                          while True:
                                              # Wait for a code to arrive
                                              cde = intercept_connection.receive_code()
                              
                                              # Check for the type of the code
                                              if cde.type == CodeType.MCode and cde.majorNumber in [3,4,5]:
                                                  # --------------- BEGIN FLUSH ---------------------
                                                  # Flushing is only necessary if the action below needs to be in sync with the machine
                                                  # at this point in the GCode stream. Otherwise it can an should be skipped
                              
                                                  # Flush the code's channel to be sure we are being in sync with the machine
                                                  success = intercept_connection.flush(cde.channel)
                              
                                                  # Flushing failed so we need to cancel our code
                                                  if not success:
                                                      print("Flush failed")
                                                      intercept_connection.cancel_code()
                                                      continue
                                                  # -------------- END FLUSH ------------------------
                              
                                                  # M3
                                                  if cde.majorNumber == 3 :
                                                      if cde.parameter("R") :
                                                          if LastRPS < 0 :
                                                              # this is M3 LastRPS should be positve
                                                              setRPS( LastRPS * -1 )
                                                          else :
                                                              setRPS( LastRPS )
                                                          intercept_connection.resolve_code(MessageType.Success, "ODrive resuming RPM " + str( LastRPS * 60 ) )
                                                      else :
                                                          if cde.parameter("S") is None :
                                                              # Let DCS know there was an ERROR
                                                              intercept_connection.resolve_code(MessageType.Error, "M3 must include an S parameter")
                                                          else :
                                                              rpm = cde.parameter("S").string_value
                                                              if cde.parameter("S").is_expression :
                                                                  rpm = getRRFVar( rpm, cde.channel )
                                                              setRPS( float(int(rpm) / 60) )
                                                              # Resolve it so that DCS knows we took care of it
                                                              intercept_connection.resolve_code(MessageType.Success, "ODrive RPM set to " + str(rpm) )
                              
                                                  # M4
                                                  if cde.majorNumber == 4:
                                                      if cde.parameter("R") :
                                                          if LastRPS > 0 :
                                                              # this is M4 LastRPS should be negative
                                                              setRPS( LastRPS * -1 )
                                                          else :
                                                              setRPS( LastRPS )
                                                          intercept_connection.resolve_code(MessageType.Success, "ODrive resuming RPM " + str( LastRPS * 60 ) )
                                                      else :
                                                          if cde.parameter("S") is None :
                                                              # Let DCS know there was an ERROR
                                                              intercept_connection.resolve_code(MessageType.Error, "M4 must include an S parameter")
                                                          else :
                                                              rpm = cde.parameter("S").string_value
                                                              if cde.parameter("S").is_expression :
                                                                  rpm = getRRFVar( rpm, cde.channel )
                                                              setRPS( -( float(int(rpm) / 60) ) )
                                                              # Resolve it so that DCS knows we took care of it
                                                              intercept_connection.resolve_code(MessageType.Success, "Ordive RPM set to -" + str(rpm) )
                              
                                                  # M5
                                                  if cde.majorNumber == 5:
                                                      LastRPS = getRPS()
                                                      setRPS( 0 )
                                                      # Resolve it so that DCS knows we took care of it
                                                      intercept_connection.resolve_code(MessageType.Success, "ODrive RPM set to 0")
                                              else:
                                                  # We did not handle it so we ignore it and it will be continued to be processed
                                                  intercept_connection.ignore_code()
                                      except Exception as e:
                                          print("Closing connection: ", e)
                                          traceback.print_exc()
                                          intercept_connection.close()
                              
                                      sleep(5)
                              
                              
                              if __name__ == "__main__":
                                  start_intercept()
                              
                              
                              1 Reply Last reply Reply Quote 0
                              • dc42undefined
                                dc42 administrators @CthulhuLabs
                                last edited by dc42

                                @CthulhuLabs said in Getting actual spindle speed from "M3 R1" Mcode:

                                I guess resume doesn't know how to pick up in the middle of a G1/0 command.

                                Yes, that will be the reason. When you pause the print, RRF may cancel some moves that are in the movement queue. When you resume, RRF rewinds the input file back to the offset in that file of the first movement command that was cancelled. However, RRF doesn't know the context of that move, for example whether it is following on from a G0, G1, G2, G3 or some other command.

                                I have raised https://github.com/Duet3D/RepRapFirmware/issues/871.

                                dc42 created this issue in Duet3D/RepRapFirmware

                                open Problem with pausing in CNC or Laser mode #871

                                Duet WiFi hardware designer and firmware engineer
                                Please do not ask me for Duet support via PM or email, use the forum
                                http://www.escher3d.com, https://miscsolutions.wordpress.com

                                CthulhuLabsundefined 1 Reply Last reply Reply Quote 0
                                • CthulhuLabsundefined
                                  CthulhuLabs @dc42
                                  last edited by

                                  @dc42 Would you like me to send you the original GCode file I was using before I altered the post processor? Each tool path is just one giant G1 Gcode.

                                  1 Reply Last reply Reply Quote 0
                                  • First post
                                    Last post
                                  Unless otherwise noted, all forum content is licensed under CC-BY-SA