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

    Running a Background Macro

    Scheduled Pinned Locked Moved
    General Discussion
    4
    16
    463
    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.
    • Aitorundefined
      Aitor @Aitor
      last edited by

      Good morning @dc42,

      I think I'm doing something wrong, or there is something I haven't understood well. I am trying to use Multiple Motion Systems to run a macro that turns my pellet feeder on and off, to prevent lines from taking too long during printing and to ensure it reacts in the expected time, but I can't get it to work properly and I don't know what I'm doing wrong.

      Today, I tried configuring a T5 tool, for example:

      M563 P5 F3 X4 Y5 S"Feeder"; assigning it a "part cooling fan" which will be my feeder and assigning it fictitious axes, so that when it is selected in M596 P1, there are no waiting conflicts.

      Despite this, the result was not what I expected, as now it behaves strangely. It seems to execute everything I specify in the macro, but once the long movements of the main gcode finish, the secondary ones execute all at once, causing my feeder to turn on and off almost instantly.

      This is the modified code I used this time. I thought I might need to treat it as a tool to make it work:

      ;daemon.g
      M596 P1
      T5
      if state.status = "processing"
      	if global.DaemonActive = true
      		set global.DaemonActive = false
      		if global.ContPelletsT0 <= 10
      			if {sensors.gpIn[1].value = 1}
      				var TiempoCarga = 6
      				var Bucle = true
      				echo >>"0:/sys/Alimentador Registro" {global.ContPelletsT0}
      				while var.Bucle = true
      					M106 S1
      					G4 S1 M400
      					if sensors.gpIn[1].value = 0 && var.Bucle = true
      						M107
      						M300 S5000 P500
      						G4 S1 M400
      						if sensors.gpIn[1].value = 0
      							set global.ContPelletsT0 = 1
      							M300 S5000 P500
      							set var.Bucle = false
      					if var.TiempoCarga <= 0 && var.Bucle = true
      						set global.ContPelletsT0 = global.ContPelletsT0+1
      						M300 S5000 P3000
      						set var.Bucle = false
      					set var.TiempoCarga = var.TiempoCarga-1
      				M107
      				G4 S20 M400
      			else
      				set global.ContPelletsT0 = 1
      		else
      			set global.ContPelletsT0 = 1
      			M596 P0
      			M25
      			M400
      			M291 R"Sensor Right" P"No Pellets T0 detected" S2 T0
      		set global.DaemonActive = true
      

      Best regards,

      dc42undefined 1 Reply Last reply Reply Quote 0
      • OwenDundefined
        OwenD
        last edited by OwenD

        I haven't delved into your code, but might I suggest an alternate method?

        I presume you have some sort of hopper that has an inductive sensor at the bottom?

        If so then I suggest you also fit an inductive sensor near the top.

        Then simply set up triggers on the two sensors. (they can both point to the same trigger file)
        The lower one simply turns on a motor (a fan or heater output would work)
        The upper sensor trigger turns the motor off again when the hopper is full.

        so your config.g might look like this.
        I don't know what board you have but this should work on a mini5

        EDIT
        Added code to pause the print if the hopper hasn't filled in X seconds

        M950 F3 C"!out9"  ; Fan 3 uses inverted !out9 pin
        M106 P3 C"Pellet_Feeder" S0 H-1 
        M950 J1 C"IO2.in"            ; Input 1 uses IO2.in pin
        M950 J2 C"IO3.in"            ; Input 2 uses IO2.in pin
        M581 T5 P1 S0 R1 ; ; invoke trigger 5 when an active to inactive edge is detected on input 1 and a file is being printed from SD card
        M581 T5 P2 S1 R1 ; invoke trigger 5 when an inactive-to-active edge is detected on input 2 and a file is being printed from SD card
        

        Then in trigger5.g something like

        var maxFillTime = 60 ; maximum time (seconds)  to fill hopper.  Print paused if not filled in this time
        if !exist global.pauseNoPelletsTime
          global pauseNoPelletsTime = state.upTime + var.maxFillTime
        if sensors.gpIn[1].value = 0  ; if the lower sensor 
          M106 P3 S1
          set global.pauseNoPelletsTime = state.upTime + var.maxFillTime
        if sensors.gpIn[2].value = 1
          M106 P3 S0  
        

        If you want the beep then I'd put this in daemon.g
        The while true loop will keep it running so it must have a delay in it to hand back to the main process.

        while true
          if fans[3].actualValue > 0
            M300 S5000 P500
            G4 P500
          ; at this point you could check either sensor depending how you want it to work
          if (global.pauseNoPelletsTime < state.upTime) && (sensors.gpIn[2].value = 0) && (state.status = "processing")
            M106 P3 S0 ; turn off feeder
            M25 ; pause print as hopper hasn't refilled
          G4 S3 ; delay between loops
        
        Aitorundefined 1 Reply Last reply Reply Quote 0
        • dc42undefined
          dc42 administrators @Aitor
          last edited by

          @Aitor I am out of the office today but I have a few comments on your posts:

          1. I don't understand the reason for the M400 commands. Please explain what you mean by "On the other hand, without using it, when my single-line print codes exceed my G4 codes, they take longer to respond."

          2. You should not need to use a tool in the daemon.g code.

          3. What's the purpose of setting global.DaemonActive false and then true in the daemon.g file? RRF will never re-enter the daemon.g file, so you don't need this to prevent the daemon.g code being executed twice in parallel.

          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

          Aitorundefined 1 Reply Last reply Reply Quote 0
          • Aitorundefined
            Aitor @dc42
            last edited by

            Good morning @dc42,

            Thanks for taking a look. First of all, to also respond to @OWEND, initially I had configured this code as a trigger and used the file called trigger2.g, as I normally do with filament equipment. However, I encountered certain problems that a constant check would solve. Therefore, I decided to use daemon.g, although it is likely that if I solve the daemon.g problem, I can apply it in trigger#. (response to point 2)

            The case is that I am using a Pulsar head, which integrates a capacitive sensor to detect if there are pellets in the head. I have connected it to the Duet and configured it as a trigger as follows:

            M950 J1 C"1.io2.in"			; Create GPIO/servo pin
            M581 P1 T2 R-1				; Configure external trigger#.g
            

            I am using a custom feeder that absorbs pellets through the Venturi effect, so my fan output simply activates or deactivates an electropneumatic relay.

            The main problem is that I must activate this output for 6 seconds. Less time does not perform a load, and more time excessively empties my compressor, losing the necessary pressure for a second load. Therefore, I also keep the valve closed for at least 20 seconds. (I am trying to buy a larger capacity compressor, but I doubt it will be ready in time for the fair). Additionally, I count failed load attempts, and if this number is 10 or more, I pause the print, assuming that either my feed load has failed or it has run out of material. (response to point 1)

            The reason for blocking daemon.g was that I realized it sometimes executed before a previous execution had finished, causing strange behaviors. I decided to block it for safety, although I am not sure this was the cause of the strange behavior. To be sure, I blocked it. If there is an active daemon.g and it has not finished executing in 10 seconds, is it possible for it to execute again? (response to point 3)

            Delving into point 1, whether I use daemon.g or trigger#.g in the same stack as the g-code, daemon or trigger mix with the print code. That is, they execute at the same time as printing. When I print pieces where the extrusion lines are short and execute quickly, such as printing a circle composed of hundreds of small straight lines, the macros execute quickly and even behave as expected. The problem arises when the extrusion lines are long, such as in a solid fill. Also, keep in mind that my print area is 1000mm x 1000mm, so these lines can take quite a while to go from one point to another. For example, I notice that when my solenoid valve is activated, and then a long print line follows, such as a diagonal from side to side, from point 0,0 to 1000,1000, which can take approximately 15-30 seconds, my solenoid valve remains active all this time, emptying my compressor and preventing a successful second load. Therefore, I thought of using M596 P1, either for daemon.g or trigger#.g, so that my macro executes at the times set in the macro, regardless of what lines of code the main stack is printing and the time it takes to execute them. The problem is that M596 P1 does not work as I expect, or I am not using it correctly.

            Sorry for the long message, and if something is unclear, please feel free to ask.

            Best regards,

            droftartsundefined dc42undefined 2 Replies Last reply Reply Quote 0
            • droftartsundefined
              droftarts administrators @Aitor
              last edited by

              @Aitor said in Running a Background Macro:

              The problem arises when the extrusion lines are long, such as in a solid fill.

              If it works on short line segments, but not on long ones, you could try using segmentation, to segment long lines into smaller moves. See M996 S and T parameters, here: https://docs.duet3d.com/en/User_manual/Reference/Gcodes#m669-set-kinematics-type-and-kinematics-parameters

              e.g.:

              M669
              Kinematics is Cartesian, no segmentation, matrix:
              1.00 0 0
              0 1.00 0
              0 0 1.00
              
              M669 S1
              
              M669
              Kinematics is Cartesian, 1 segments/sec, min. segment length 0.20mm, matrix:
              1.00 0 0
              0 1.00 0
              0 0 1.00
              

              Ian

              Bed-slinger - Mini5+ WiFi/1LC | RRP Fisher v1 - D2 WiFi | Polargraph - D2 WiFi | TronXY X5S - 6HC/Roto | CNC router - 6HC | Tractus3D T1250 - D2 Eth

              Aitorundefined 1 Reply Last reply Reply Quote 0
              • Aitorundefined
                Aitor @OwenD
                last edited by

                Good morning @OwenD,

                I will carefully review your suggestion, as there are certain points that I am not fully understanding and need to analyze in depth. I am still getting used to programming and there are certain object models in your code that I do not know by heart.

                Anyway, at first glance, I think my main problem with the compressor issue will still exist.

                Thank you for your contribution. I would be happy to hear any further suggestions you have.

                Best regards,

                OwenDundefined 1 Reply Last reply Reply Quote 0
                • Aitorundefined
                  Aitor @droftarts
                  last edited by

                  Good morning @droftarts,

                  I will try this immediately. Just to clarify, if I have code running, for example, a print line from point 0,0 to point 1000,1000, does this mean that it will be divided into parts so that other commands can execute before this line finishes?

                  Best regards,

                  droftartsundefined 1 Reply Last reply Reply Quote 0
                  • OwenDundefined
                    OwenD @Aitor
                    last edited by OwenD

                    @Aitor said in Running a Background Macro:

                    Good morning @OwenD,

                    I will carefully review your suggestion, as there are certain points that I am not fully understanding and need to analyze in depth. I am still getting used to programming and there are certain object models in your code that I do not know by heart.

                    Anyway, at first glance, I think my main problem with the compressor issue will still exist.

                    Thank you for your contribution. I would be happy to hear any further suggestions you have.

                    Best regards,

                    OK
                    If I understand your setup correctly then I believe this code will work.
                    By using the while true in daemon.g, it will only open once (and continue endlessly), not run every 10 seconds,
                    So you MUST have a delay inside the while loop

                    if !exists global.runUntilTime
                    	global runUntilTime = state.upTime + 6
                    if !exists global.noRunBeforeTime
                    	global noRunBeforeTime = state.upTime
                    if !exists global.failedLoadCount 
                    	global failedLoadCount = 0	
                    
                    while true
                    	if (state.status = "processing") && (sensors.gpIn[2].value=0) && (global.noRunBeforeTime < state.upTime)
                    		M106 P3 S1
                    		set global.runUntilTime = state.upTime + 6
                    		while state.upTime < global.runUntilTime ; count off the fill time
                    			M300 S5000 P500
                    			G4 S1
                    		M106 P3 S0
                    		set global.noRunBeforeTime = state.upTime + 20 ; set the new time to allow compressor refill
                    		if sensors.gpIn[2].value=0 ; is hopper still empty?
                    			set global.failedLoadCount = global.failedLoadCount + 1
                    		else
                    			global.failedLoadCount = 0
                    		if global.failedLoadCount > 10
                    			M25
                    	G4 S1
                    
                    1 Reply Last reply Reply Quote 1
                    • droftartsundefined
                      droftarts administrators @Aitor
                      last edited by

                      @Aitor said in Running a Background Macro:

                      Just to clarify, if I have code running, for example, a print line from point 0,0 to point 1000,1000, does this mean that it will be divided into parts so that other commands can execute before this line finishes?

                      Yes, it should do. Use the M669 S and T parameters to get the granularity that you need. My example chose M669 S1, which means minimum 1 segment per second, so it can have more than 1 segment per second (ie multiple short moves) and will stop/pause within a second (or do other tasks like checking triggers).

                      M669 T parameter is similar, it just means that any move longer than the minimum segment size is broken up into equal length moves that are, at a minimum, the minimum segment size (yes, slightly confusing that one!).

                      e.g. with M669 T0.1 set:

                      • a 0.199mm move will be 1 segment of 0.199mm, as it cannot be divided into equal length moves of less than 0.1mm.
                      • a 0.201mm move would be split into two moves of 0.1005mm
                      • a 0.299mm move would be split into two moves of 0.1495mm

                      So any move that is more than double the size of M669 T#, will be split into equal length segments that are between # and 1.5x # long.

                      Ian

                      Bed-slinger - Mini5+ WiFi/1LC | RRP Fisher v1 - D2 WiFi | Polargraph - D2 WiFi | TronXY X5S - 6HC/Roto | CNC router - 6HC | Tractus3D T1250 - D2 Eth

                      Aitorundefined 1 Reply Last reply Reply Quote 1
                      • Aitorundefined
                        Aitor @droftarts
                        last edited by

                        Good morning everyone,

                        Thank you very much @droftarts, your contribution was key to solving the main problem of the excessively long lines. Configuring M669 T0.1 S1 worked perfectly, starting and stopping my feeder during long runs. However, it only worked when using M596 P1; I don't completely understand why, but I really appreciate your help.

                        @OwenD Thank you too for your code. Although it doesn't behave as it should, probably due to the "while true", it will help me a lot to optimize my code. After seeing yours, I realize that sometimes I'm a bit clumsy. 😁

                        Best regards,

                        Aitorundefined 1 Reply Last reply Reply Quote 0
                        • Aitorundefined
                          Aitor @Aitor
                          last edited by

                          Good morning everyone again,

                          I have a question: Can M669 T0.1 S1 potentially slow down other processes or make the Duet work excessively? I will adjust this value to something less extreme, as I understand that M669 T15 S1 might be sufficient.

                          I also increased the value of the second stack with the command M595 Q1 P60. My question here is similar: What could be the implications of increasing the stack so much? I will continue testing, but I understand that setting the same number of lines as my macro, or even fewer, should be enough, right? Or would this slow down the process?

                          Best regards,

                          droftartsundefined 1 Reply Last reply Reply Quote 0
                          • droftartsundefined
                            droftarts administrators @Aitor
                            last edited by

                            @Aitor I'm not sure if segmentation is applied before or after the command is added to the queue, but I think after, so a command added to the queue is still just a single command (I'll check with @dc42). I don't think segmentation would slow down other processes particularly. Some kinematics (Polar, SCARA) usually have segmentation configured, with S100 T0.2, but as far as I'm aware there's no appreciable slow down.

                            Having a long command queue can cause problems. Yes, you don't want a short queue, and then not be able to supply enough commands to keep the axes moving continuously, but then you also have the problem of sending commands that go into the queue, and how long it takes for them to be enacted. For a summary of this, see https://docs.duet3d.com/User_manual/Reference/Gcodes#command-queueing
                            You don't need to have the whole of your macro in the queue, just enough of it that it doesn't stutter. You also don't have any moves that need to be 'planned' ie G0/1/2/3 moves, you're just turning on the hopper motor with M106 commands, and there's a lot of G4 pauses.

                            Running the macro supplied by @OwenD should work, though looking at it it doesn't look like it's running in the second motion system? With all the pauses, and only using M106, perhaps it doesn't need to. I have a feeling that running the M300 command to play a beep sound pauses the whole queue while sound is played, so that might be causing pauses if it's not run in the second motion system.

                            Ian

                            Bed-slinger - Mini5+ WiFi/1LC | RRP Fisher v1 - D2 WiFi | Polargraph - D2 WiFi | TronXY X5S - 6HC/Roto | CNC router - 6HC | Tractus3D T1250 - D2 Eth

                            Aitorundefined 1 Reply Last reply Reply Quote 0
                            • Aitorundefined
                              Aitor @droftarts
                              last edited by

                              Good morning, @droftarts,

                              I understand the topic of segmentation and also the queue. I believe that leaving the stack of 3 as the default might be sufficient; however, I will conduct tests regarding this.

                              I have modified @OwenD's macro a bit to run it in the background. Additionally, I have added some features to save compressed air and to keep a record of my attempts, in order to analyze the number of attempts I usually need, and thus be able to better adjust the value of 10 repetitions. The result so far is this:

                              M596 P1
                              if (state.status = "processing") && (sensors.gpIn[1].value = 1) && (global.DaemonActive < state.upTime)
                              	M106 P3 S1
                              	set global.TimerPelletsT0 = state.upTime + 6
                              	echo >>"0:/sys/Alimentador Registro" {global.ContPelletsT0} ;Log of number of unsuccessful attempts
                              	while state.upTime < global.TimerPelletsT0	; count off the fill time
                              		if sensors.gpIn[1].value = 0 ;Check for pellets
                              			M106 P3 S0
                              			G4 P500 M400 ;Perform a short wait before checking
                              				if sensors.gpIn[1].value = 0 ;Check that it was not a false detection
                              					set global.TimerPelletsT0 = state.upTime - 6
                              					M300 S5000 P500
                              				else ;A false detection was detected, continue.
                              					M106 P3 S1
                              		G4 S1 M400
                              	M106 P3 S0
                              	set global.DaemonActive = state.upTime + 20 ; set the new time to allow compressor refill
                              	if sensors.gpIn[1].value = 1 ; is hopper still empty?
                              		set global.ContPelletsT0 = global.ContPelletsT0 + 1
                              	else
                              		set global.ContPelletsT0 = 1
                              	if global.ContPelletsT0 > 10
                              		M596 P0
                              		M25
                              		M291 R"Material Sensor" P"No Pellets detected" S2 T0
                              

                              I can also tell you that making beeps does not cause any pauses, they only generate an interruption if you place a G4 and M400 right after, but in my case, running in the background, the M400s do not cause pauses. What I have noticed is that when I run this same macro without M400 in the foreground, the delays execute before reaching those lines, meaning they don't wait to reach that line and then everything executes at once. However, it's true that segmenting the line shouldn't cause this, so I will review it again. I am sure that if I run this macro with M400s in the foreground, I will have pauses during printing, hence the reason for running them in the background.

                              Regards,

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

                                @Aitor said in Running a Background Macro:

                                The reason for blocking daemon.g was that I realized it sometimes executed before a previous execution had finished, causing strange behaviors.

                                RRF will never re-enter daemon.g, unless you use M98 to execute daemon.g explicitly from somewhere else.

                                Likewise when a trigger is executing, another trigger (whether the same one of a different one) won't be executed until the original one has completed or aborted; except that trigger 0 can always be executed.

                                @Aitor said in Running a Background Macro:

                                Delving into point 1, whether I use daemon.g or trigger#.g in the same stack as the g-code, daemon or trigger mix with the print code. That is, they execute at the same time as printing.

                                What do you mean by "in the same stack"?

                                @Aitor said in Running a Background Macro:

                                For example, I notice that when my solenoid valve is activated, and then a long print line follows, such as a diagonal from side to side, from point 0,0 to 1000,1000, which can take approximately 15-30 seconds, my solenoid valve remains active all this time, emptying my compressor and preventing a successful second load.

                                That is not expected, if the solenoid valve is activated only by daemon.g or by the trigger. Both daemon.g and a trigger file (if triggered) should execute concurrently (several times if needed) with long moves in the job file; provided that you do not either command motion in the same motion system or use M400 in the trigger file or daemon.g.

                                If your daemon.g or trigger files does not command any motion, you should not need to use M596 P1.

                                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

                                Aitorundefined 1 Reply Last reply Reply Quote 0
                                • Aitorundefined
                                  Aitor @dc42
                                  last edited by

                                  Good morning @dc42,

                                  Thank you for your response. Regarding blocking daemon.g, it is clear to me that it is not necessary. It was likely a feeling I had due to the behavior I observed, and I decided to block it to be sure, but I have now removed it from my code.

                                  Regarding "stack," it is probably a mistranslation; I mean the movement queue (M596 P0 or P1).

                                  As for your last comment, I just tried it and still have to use M569 P1 because if I don't use it, it does not execute in the desired times. Even using M669 T0.1 S1, the execution response of daemon.g is slow. Here are the details of how I conducted the tests.

                                  Gcode being printed:

                                  ; G-Code generated by INDART3D
                                  
                                  G21                         ; Metric values
                                  T0 P0                       ; Select Tool
                                  G28                         ; Home all
                                  G0 Z900
                                  M400                        ; Wait for current moves to finish
                                  
                                  ; Macro
                                  G1 X0 Y0 F3000
                                  G1 X1000 Y1000
                                  G1 X0 Y1000
                                  G1 X1000 Y0
                                  G1 X0 Y0 F3000
                                  G1 X1000 Y1000
                                  G1 X0 Y1000
                                  G1 X1000 Y0
                                  G1 X0 Y0 F3000
                                  G1 X1000 Y1000
                                  G1 X0 Y1000
                                  G1 X1000 Y0
                                  G1 X1000 Y0
                                  G1 X0 Y0 F3000
                                  ; The gcode keeps repeating these commands hundreds more times
                                  

                                  daemon.g modified with your instructions:

                                  ; M596 P1
                                  if (state.status == "processing") && (sensors.gpIn[1].value == 1) && (global.DaemonActive < state.upTime)
                                      M106 P3 S1
                                      set global.TimerPelletsT0 = state.upTime + 6
                                      echo >>"0:/sys/Alimentador Registro" {global.ContPelletsT0} ; Log of number of unsuccessful attempts
                                      while state.upTime < global.TimerPelletsT0 ; count off the fill time
                                          if sensors.gpIn[1].value == 0 ; Check for pellets
                                              M106 P3 S0
                                              G4 P500 ; M400 ; Perform a short wait before checking
                                              if sensors.gpIn[1].value == 0 ; Check that it was not a false detection
                                                  set global.TimerPelletsT0 = state.upTime - 6
                                                  M300 S5000 P500
                                              else ; A false detection was detected, continue.
                                                  M106 P3 S1
                                          G4 S1 ; M400
                                      M106 P3 S0
                                      set global.DaemonActive = state.upTime + 20 ; set the new time to allow compressor refill
                                      if sensors.gpIn[1].value == 1 ; is hopper still empty?
                                          set global.ContPelletsT0 = global.ContPelletsT0 + 1
                                      else
                                          set global.ContPelletsT0 = 1
                                      if global.ContPelletsT0 > 10
                                          set global.ContPelletsT0 = 1
                                          ; M596 P0
                                          M25
                                          M291 R"Material Sensor" P"No Pellets detected" S2 T0
                                  

                                  The result I get is the same. Right now, I am testing with an empty system, so sensors.gpIn[1].value is always equal to 1. The issue is that without using the secondary motion queue, it does not respect the 6 seconds, it always takes longer than desired, and it always turns off or on at the end of the complete Gcode lines, despite being fragmented with M669 T0.1 S1.

                                  Best regards,

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