Since I can't do any printing at the moment (printer is broken), I have put together a bit of a tutorial on "best practice" when creating conditional G code.
There will be many far more qualified people than I on the forum to do this, and I'm sure that there is better and more concise documentation coming, but some may find it useful.
It is far from a complete or even comprehensive treatise on the subject, nor is it meant to be.
Feel free to correct, disagree or ignore as you see fit.
Especially since the code therein is untested (printer broken remember)
Conditional G Code best practice.pdf
![](/assets/uploads/system/avatar-default.png?v=1521803371351)
Best posts made by OwenD
-
Conditional G Code introduction "tutorial" (PDF)
-
RE: Baby Stepping.. can it, or can it not be permanent?
just run a macro. (assuming you're running RRF3)
You could either do it manually or put it in the stop gcode of your slicer;save_babystep.g ; Add babystep to Z offset and make "persistant" if move.axes[2].babystep !=0 echo {"Z trigger height altered by " ^ move.axes[2].babystep ^ "mm"} echo {"OLD: " ^ sensors.probes[0].triggerHeight ^ " new: " ^ sensors.probes[0].triggerHeight - move.axes[2].babystep} G31 Z{sensors.probes[0].triggerHeight - move.axes[2].babystep} M500 P10:31 ; save settings to config-overide.g - Must have M501 in config.g else echo "No babystepping set. Nothing to save"
EDIT: Original post incorrectly added baby steps to trigger height. It should subtract.
-
Pressure Advance Tuning file generator
Hi All,
In my quest to tune my various settings I decided I needed to be able to automatically generate a test file that would change the pressure advance at set intervals.
The only programming language I know is Delphi/Lazarus, so I created a windows application to carry out the task.Essentially that application creates a square box with two wall perimeters.
The inner wall prints at a constant speed and acts merely for support.
The outer wall alternates between a high and low speed with the transition point being mid way along the wall.
At the end of each layer a retraction is added.This allows you to see how the PA adjustments are affecting corners at the two speeds and how it is affecting the retraction settings.
A start and end G code section takes care of temps etc.
Here are some pics of tests starting a PA of zero and increasing by 0.05 every 5mm in height.
My printer runs about a 350mm bowden tube.Pic 1: PA range 0-0.35
Note the distinct thickness variations at the speed change point and the blobbing at the retraction/layer change point.
Pic2: PA Range 0.4-0.75
Transition line still faintly visible and some issues around retraction area
Pic3: PA range 0.8-1.15
Looks pretty good at about mid way
Pic4: PA range 1.2-1.55
Here we can see that pressure advance is starting to cause too much retraction.
I'd either have to reduce PA or try again with less retraction.
If anyone would like to try the application and provide feedback , I have put a link to my dropbox.
Download link:
https://www.dropbox.com/s/nolo5aca26e7fni/PaAdvanceTestInstall_v1-0.zip?dl=0BE WARNED!!
This should be considered very much beta software.
Use at your own risk.
I've only tested on Windows 10.I'm not sure my extrusion volume calculations are 100% accurate.
They seem close, but not identical to Cura's values.
At any rate it seems good enough for the test purposes. -
Macro for "automatic" calibration of BL Touch
This macro uses variables so needs RRF3.3b2 or later.
EDIT: Object model values for M558 are now stored in mm/min so no conversion necessary. - please used amended macro below which includes improvements and corrects some errors pointed out in this thread by other users.
It prompts you to jog the nozzle to the bed and then runs 10 probe offset tests (this number is configurable as a variable)
At the end, it discards the highest and lowest reading and averages the rest.
You can then choose to save the result in config-overide.g;Calibrate BL Touch ; Reprap firmware version 3.3b2 or later required! ; if two speed probing is configured in M558,we probably want to reduce the speed for this test var ProbeSpeedHigh = sensors.probes[0].speeds[0]*60 ; Speeds are saved in mm/sec in the object model but M558 uses mm/min var ProbeSpeedLow = sensors.probes[0].speeds[1]*60 M558 F60 ; reduce probe speed to 60mm/min for accuracy - adjust F parameter as required ;define some variables to store readings var NumTests=10 ; modify this value to define number of tests ; Do not change below this line var RunningTotal=0 var Average=0 var Lowest=0 var Highest=0 ; If the printer hasn't been homed, home it if !move.axes[0].homed || !move.axes[1].homed || !move.axes[2].homed G28 else G1 Z{sensors.probes[0].diveHeight} F360 ; if axes homed move to dive height M561 ; clear any bed transform M290 R0 S0 ; clear babystepping ; move nozzle to centre of bed G1 X{(move.axes[0].min + move.axes[0].max)/2} Y{(move.axes[1].min + move.axes[1].max)/2} M564 S0 H0 ; Allow movement beyond limits ;ensure you have room for the probe if move.axes[2].machinePosition < sensors.probes[0].diveHeight G1 Z{sensors.probes[0].diveHeight} M280 P0 S160 I1 ; reset BL Touch G4 S0.5 M98 P"0:/sys/retractprobe.g" ; Ensure probe is retracted & reset G4 S0.5 M561 ; clear any bed transform ; Jog head to position M291 P"Jog nozzle to touch bed" R"Set nozzle to zero" S3 Z1 G92 Z0 ; set Z position to zero M291 P"Press OK to begin" R"Ready?" S3; ; Move probe over top of same point that nozzle was when zero was set G1 Z{sensors.probes[0].diveHeight}; lift head G1 X{move.axes[0].machinePosition - sensors.probes[0].offsets[0]} Y{move.axes[1].machinePosition - sensors.probes[0].offsets[1]} F1800 echo "Current probe offset = " ^ sensors.probes[0].triggerHeight ^ "mm" ; carry out 10 probes (or what is set in NumTests variable) while iterations < var.NumTests G1 Z{sensors.probes[0].diveHeight} ; move to dive height if sensors.probes[0].value[0]=1000 ; if probe is in error state echo "Probe in error state- resetting" M280 P0 S160 I1 ; reset BL Touch G4 S0.5 M98 P"0:/sys/retractprobe.g" ; Ensure probe is retracted & reset G4 S0.5 G30 S-1 M118 P2 S{"Test # " ^ (iterations+1) ^ " Triggered @ " ^ move.axes[2].machinePosition ^ "mm"} ; send trigger height to Paneldue console M118 P3 S{"Test # " ^ (iterations+1) ^ " Triggered @ " ^ move.axes[2].machinePosition ^ "mm"} ; send trigger height to DWC console if iterations == 0 set var.Lowest={move.axes[2].machinePosition} ; set the new lowest reading to first probe height set var.Highest={move.axes[2].machinePosition} ; set the new highest reading to first probe height if move.axes[2].machinePosition < var.Lowest set var.Lowest={move.axes[2].machinePosition} ; set the new lowest reading ;M118 P3 S{"new low reading = " ^ move.axes[2].machinePosition} ; send trigger height to DWC console G4 S0.3 if move.axes[2].machinePosition > var.Highest set var.Highest={move.axes[2].machinePosition} ; set the new highest reading ;M118 P3 S{"new high reading = " ^ move.axes[2].machinePosition} ; send trigger height to DWC console G4 S0.3 set var.RunningTotal={var.RunningTotal + move.axes[2].machinePosition} ; set new running total ;M118 P3 S{"running total = " ^ var.RunningTotal} ; send running total to DWC console G4 S0.5 set var.Average = {(var.RunningTotal - var.Highest - var.Lowest) / (var.NumTests - 2)} ; calculate the average after discarding th ehigh & low reading ;M118 P3 S{"running total = " ^ var.RunningTotal} ; send running total to DWC console ;M118 P3 S{"low reading = " ^ var.Lowest} ; send low reading to DWC console ;M118 P3 S{"high reading = " ^ var.Highest} ; send high reading to DWC console M118 P2 S{"Average excluding high and low reading = " ^ var.Average} ; send average to PanelDue console M118 P3 S{"Average excluding high and low reading = " ^ var.Average} ; send average to DWC console G31 P500 Z{var.Average} ; set Z probe offset to the average reading M564 S0 H1 ; Reset limits M558 F{var.ProbeSpeedHigh}:{var.ProbeSpeedLow} ; reset probe speed to original G1 Z{sensors.probes[0].diveHeight} F360 ; move head back to dive height M291 P{"Trigger height set to : " ^ sensors.probes[0].triggerHeight ^ " OK to save to config-overide.g, cancel to use until next restart"} R"Finished" S3 M500 P31 ; optionally save result to config-overide.g
-
RE: oibject model "status" dcos.
@Tinchus
Political correctness has spread even to RRF.
In the spirit of inclusiveness , you must now use "processing" in deference to those machines that do not identify as "printers" -
Setting up video streaming using raspberry pi
Recently I had to set up a Raspberry Pi is camera to use as a monitoring device for my printer.
I found that pretty much all the online information was outdated and had no end trouble getting it to work on the current version of Pi OS (BullseyeBookworm).
The entire camera and video handling system has changed over the years.
So I decided to document what worked for me.
In the end it was relatively easy (once you knew the correct steps0I must first point out I know bugger all about Linux.
All this work is taken from bits and pieces I found on various sites and cobbled together, so my apologies for not crediting the original author(s)NOTE: These instructions were done using a Raspberry Pi 4 running
BullseyeBookworm 64 bit and an official Pi camera
Also if you're running RRF in SBC mode, you don't need to do this as I believe it has a built in camera setup.The first step is to install the OS using the Pi Imager
https://www.raspberrypi.com/software/When prompted, choose your user name, password and Wifi details.
NOTE: The current version no longer uses the default user pi and password raspberrywhen you get to this point, click on "edit settings"
Enable SSH so that you can connect to the Pi via PUTTY rather than always needing a monitor and keyboard.
If you installed a Pi OS that has a desktop you can use the inbuilt command line terminal for all the steps listed below.Then click on YES to apply the settings.
Once the image has been loaded onto the SD card, insert the card in your Pi and start it up.
Start Putty (or some other terminal) and SSH into the Pi
If you used the default settings, you should be able to go to
raspberrypi.local
You should see something like this
You may get any updates/upgrade by using these commands
sudo apt update
sudo apt upgrade
sudo apt install
Change directory
cd /usr/local/bin/
Open a text edior
sudo nano streamVideo.py
Paste in the following code
#!/usr/bin/python3 # This is the same as mjpeg_server.py, but uses the h/w MJPEG encoder. import io import logging import socketserver from http import server from threading import Condition from picamera2 import Picamera2 from picamera2.encoders import MJPEGEncoder from picamera2.outputs import FileOutput PAGE = """\ <html> <head> <title>3D Printer Camera</title> </head> <body> <img src="stream.mjpg" width="800" height="600" /> </body> </html> """ class StreamingOutput(io.BufferedIOBase): def __init__(self): self.frame = None self.condition = Condition() def write(self, buf): with self.condition: self.frame = buf self.condition.notify_all() class StreamingHandler(server.BaseHTTPRequestHandler): def do_GET(self): if self.path == '/': self.send_response(301) self.send_header('Location', '/index.html') self.end_headers() elif self.path == '/index.html': content = PAGE.encode('utf-8') self.send_response(200) self.send_header('Content-Type', 'text/html') self.send_header('Content-Length', len(content)) self.end_headers() self.wfile.write(content) elif self.path == '/stream.mjpg': self.send_response(200) self.send_header('Age', 0) self.send_header('Cache-Control', 'no-cache, private') self.send_header('Pragma', 'no-cache') self.send_header('Content-Type', 'multipart/x-mixed-replace; boundary=FRAME') self.end_headers() try: while True: with output.condition: output.condition.wait() frame = output.frame self.wfile.write(b'--FRAME\r\n') self.send_header('Content-Type', 'image/jpeg') self.send_header('Content-Length', len(frame)) self.end_headers() self.wfile.write(frame) self.wfile.write(b'\r\n') except Exception as e: logging.warning( 'Removed streaming client %s: %s', self.client_address, str(e)) else: self.send_error(404) self.end_headers() class StreamingServer(socketserver.ThreadingMixIn, server.HTTPServer): allow_reuse_address = True daemon_threads = True picam2 = Picamera2() picam2.configure(picam2.create_video_configuration(main={"size": (640, 480)})) output = StreamingOutput() picam2.start_recording(MJPEGEncoder(), FileOutput(output)) try: address = ('', 8888) server = StreamingServer(address, StreamingHandler) server.serve_forever() finally: picam2.stop_recording()
Press CTRL + X to exit and choose Y to save file
change directory
cd /etc/systemd/system
Open the text editor to create a system service file
sudo nano streamVideo.service
Paste in the following code
[Unit] Description=A script for straming video to http After=syslog.target network.target [Service] WorkingDirectory=/usr/local/bin/ ExecStart=/usr/bin/python3 /usr/local/bin/streamVideo.py Restart=always RestartSec=120 [Install] WantedBy=multi-user.target
PRess CTRL + X
press Y and enter to saveEnter the following to reload the daemon
sudo systemctl daemon-reload
Enable the service
sudo systemctl enable streamVideo.service
You should see something like
Created symlink /etc/systemd/system/multi-user.target.wants/streamVideo.service ā /etc/systemd/system/streamVideo.service.
Restart the Pi
sudo shutdown -r now
After the Pi has rebooted, you should be able to access the stream by going to the following URL in your browser
http://raspberrypi.local:8888/index.html
In DWC you need to enter this URL on order to get the stream
http://raspberrypi.local:8888/stream.mjpg
Your video stream should now be visible in the Webcam tab
I hope this saves someone some frustration.
-
RE: Reprap G-Code syntax now part of RJ TextEd text editor
I have updated the syntax file for RJ TextEd to include more object model items.
I have also included a suggested colour format file.
You will need to update RJ TextEd to the latest version in order to take advantage of hints etc.Replace the existing files (reprap.syx, reprap.ini) in C:\Users"yourname"\AppData\Roaming\RJ TextEd\Syntax
Copy the file "Reprap Dark.xml" to C:\Users"yourname"\AppData\Roaming\RJ TextEd\Syntax color themes\To install.
1: Go to ENVIRONMENT > THEMES > CUSTOMISE
2: Select "Reprap G Code" from the drop down list of highlighters
3: Click the button, and select import colors from file
4: Select the "Reprap Dark.xml" file previously copied.
You can customize the colours to suit yourself.
Different G Code parameters such as X, Y, Z , E, F, G etc can be coloured to make individually.
Likewise, comments and other items can be individually coloured.
-
Forum categories for conditional Gcode & macros
Now that 3.1 is released, I think it would be beneficial to have a forum category dedicated to conditional gcode & macros.
This should probably be broken down into various kinematic types.
Furthermore it may be good to have some sort of āduet approvedā marker like the āsolvedā marker, once the macros have been peer reviewed and improved where necessary.
I usually try to make macros āportableā by not hard coding anything, but the very fact that certain parts of the object model are not visible if you donāt run that type of configuration, probably makes putting them in the categories for which they were designed beneficial and will reduce angst and support time.
(Yes, I know we should be checking for null objects and kinematic types as a matter of course in every macro)
Which leads to my final point, which is once we have some documentation on the object model, perhaps someone could do a small tutorial on best practice when writing macros.
My programming background is not in C or its variants so I admit Iām constantly searching for proper syntax etc.
To a non-programmer it is just gobbledygook.
If thereās going to be dozens of people learning to do it, they may as well learn to do it in a way that ensure portability and reduces the chance of unexpected issues.
Iāve always found it usually takes 10 lines of code to do the job, the another 50 lines of code to stop people trying to do it out of order or in a way you never imagined -
RE: Setting up video streaming using raspberry pi
I found another application that works quite easily and has the benefit of being able to adjust all the settings from the web interface.
It's called picamera2-WebUI-Lite
There's a slight error on the instructions to install and it doesn't tell you how to set it up as a service, so I've listed thesteps.
To install.
SSH into your PI (or open a terminal if using a desktop)
Change directory
cd /usr/local/bin/
Clone the repositry
sudo git clone https://github.com/monkeymademe/picamera2-WebUI-Lite.git
Change directory
cd /usr/local/bin/picamera2-WebUI-Lite
Test that it's running
python3 app.py
Open your browser and go to
http://raspberrypi.local:8080/To see just the video feed go to
http://raspberrypi.local:8080/video_feedGo back to the terminal and hot CTRL + C to shut the app down
To set it up as a service
Change directorycd /etc/systemd/system
Open a text editor to create a system service file
sudo nano webui-lite.service
Paste in the following
[Unit] Description=Start script for WebUi-Lite as a service After=syslog.target network.target [Service] WorkingDirectory=/usr/local/bin/picamera2-WebUI-Lite/ ExecStart=/usr/bin/python3 /usr/local/bin//picamera2-WebUI-Lite/app.py Restart=always RestartSec=120 [Install] WantedBy=multi-user.target
Press CTRL + X to exit and Y to save
Reload the daemon
sudo systemctl daemon-reload
Enable the service
sudo systemctl enable webui-lite.service
NOTE that if you already have the other streamVideo service I listed above using Picamera you will have to disable it as you can't run both.
sudo systemctl disable streamVideo.service
Reboot the system to make sure the service starts
sudo shutdown -r now
Adjust your DWC settings as follows
This will allow you to easily get to the settings page by clicking on the webcam image in DWC
From there adjust your resolution and you can also zoom in a particular area using the scaler crop settings.Full view
Cropped view
-
RE: Conditional G Code - RJ TextEd Syntax file.
For those using RJ Text Editor, I have posted an updated syntax file that includes the new object model items and G Codes contained in RRF3.5.0 b2
It can be downloaded here
https://www.rj-texted.se/Forum/viewtopic.php?p=18659#p18659It includes mouse over hints for G Code commands and auto-complete
The program comes with the RRF syntax file as standard but you may need to update it as new firmware versions come out. -
Heater fault checking routine to be run in daemon.g
This is a routine I run in daemon.g to check for heater faults.
It is mainly aimed at covering the time when the printer is idle as RRF already checks when a print is running.
I have excluded heaters that are currently being PID auto tuned
It does not take action if the heater temp is < 45 degrees but will shut down if temp is < zero as that would indicate a thermistor fault.
If the temperature is over 45 degrees it will only fault if the current temp is > 15 degrees from the set active temp AND the temperature is rising by > 0.5 degrees over 3 seconds.
This is to avoid activating when the temp is deliberately lowered.
NOTE: I have used a dummy tool to store variables which allow me to have this routine run only at intervals greater than 10 seconds whilst allowing the rest of daemon.g to run at normal intervals. Without this (and until we get variables) you will need to modify the code and use G4 to set the timing
For the timing I have used state.upTime. which will probably roll over at some point if the printer is left on long enough (I'm guessing it's a 16 bit signed integer?), so I have a check for that.
I have tested it in both idle and printing states and it seems to work as expected but have not confirmed the upTime rollover works as expected.; 0:/sys/daemon.g ; runs continually in background at approximately 1Hz if not delayed internally ;HEATER CHECKS ; this section of daemon.g checks for heater faults ; RRF doesn't currently check for faults when idle but by default will shutdown during printing if temperature excursion is > 15 degrees. ; Note: temp excursion value and time may be manually set using M570 ; checking if temp is rising requires a variable. I have used a dummy tool offset (Tool2) for this and to do elapsed time since last check ; G4 could be used but would also delay anything else in daemon.g ; this way allows other checks to run more frequently if needed however the G4 delays inside the loop will affect the frequency of daemon.g ; will be updated when variables are available in RRF. while iterations < #heat.heaters ; loop through all configured heaters if ((tools[2].offsets[1]+10) > state.upTime) ; if checked in last 10 seconds escape loop and go to rest of daemon.g if present. offset will be zero at startup via config.g ;echo "skipping loop " ^ " " ^ state.upTime ^ " " ^ tools[2].offsets[1]+10 if tools[2].offsets[1] - state.upTime > 60 ; uptime must have rolled over so reset off set to zero G10 P2 Y0 echo "upTime has rolled over. Heater checking reset" break ;echo "checking heater " ^ iterations ^ " " ^ state.upTime ^ " " ^ tools[2].offsets[1]+10 if heat.heaters[iterations].state="tuning" ;echo "heater " ^ iterations ^ " is tuning - no check carried out" continue ; don't check this heater as it is PID auto tuning if (heat.heaters[iterations].current) > (heat.heaters[iterations].max) ; temp is over max so emergency shutdown required ;M41 P5 S1 ; activate output connected to externally powered latching relay here to sound alarm echo "heater over max temp fault detected in daemon.g. - shutting down" M112; emergency shutdown M81 S1 ; turn off power when fans have turned off if (heat.heaters[iterations].current > 45) ; no real danger at below this temp as ambient may be close to this ;echo "heater " ^ iterations ^ " is above 45 degrees" if (heat.heaters[iterations].state!="off") && (heat.heaters[iterations].current > heat.heaters[iterations].active + 15) ; temp is > 15 degrees above target. ;echo "heater " ^ iterations ^ " is on or in standby - checking if temp is rising" G10 P2 X{heat.heaters[iterations].current} ; set dummy tool X offset to current temp of heater G4 S3 ; wait 3 seconds if (heat.heaters[iterations].current > tools[2].offsets[0] + 0.5) ; heat is rising by more than 0.5 degrees in 3 seconds echo "heater runaway fault detected in daemon.g. - shutting down" if (state.status=="processing") M25 ; pause print so you might be able to save it using M119 ;M41 P5 S1 ; activate output connected to externally powered latching relay here to sound alarm M0 ; unconditional stop. If axes are homed and a print is being canceled will run cancel.g otherwise will run stop.g M81 S1 ; turn off power when fans have turned off else ;echo "heater is on or standby but temp is falling on heater " ^ iterations ^ " - no action needed" elif (heat.heaters[iterations].state="off") && (heat.heaters[iterations].current >= 45) ; if heater is off and temp is greater than 45 there could be an issue ;echo "heater " ^ iterations ^ " is off but checking if temp is rising" G10 P2 X{heat.heaters[iterations].current} ; set dummy tool X offset to current temp of heater G4 S3 ; wait 3 seconds if (heat.heaters[iterations].current > tools[2].offsets[0] + 0.5) ; heat is rising by more than 0.5 degrees in 3 seconds ;echo "heater is off but temp is rising on heater " ^ iterations ^ "emergency shutdown" ;M41 P5 S1 ; activate output connected to externally powered latching relay here to sound alarm echo "heater runaway fault detected in daemon.g. - shutting down" M112; emergency shutdown M81 S1 ; turn off power when fans have turned off else ;echo "heater is off & temp is falling or stable on heater " ^ iterations ^ " - no action needed" else ;heater is below 45 degrees so only other fault may be an open circuit thermistor which should show -275 degrees if heat.heaters[iterations].current < -0 ; we probably have a thermistor fault if heater is less than 0 degrees M112 ; emergency shutdown M81 S1 ; turn off power when fans have turned off if iterations == #heat.heaters-1 ; all heaters have been checked G10 P2 Y{state.upTime} ; set the new time to check again ; END HEATER CHECKS ; run other checks ;G4 S10 ; pause daemon.g for 10 seconds before next run - required if not using variables
-
RE: Triggering 'Load Filament' prompt with a macro
@Surgikill said in Triggering 'Load Filament' prompt with a macro:
@dc42 Yes, I am doing that now. However, I want the prompt to contain options that are dynamically created based on the content of "0:/filaments". I don't want to update the prompt every time I add a filament, or modify the name of a filament. Just like how DWC creates the prompt when pressing the 'Load Filament' button.
At present RRF doesn't have any string handling routines and no way of extracting file information or file lists.
The best you can do is use the object model item
move.extruders[0].filament
which will show the currently loaded filament (or null)
I ran into a similar issue trying to create a filament change macro to use from PanelDue as my printer is in the shed and my computer inside.
I had to manually list all the filament types I use in an array.This is what I came up with.
It may (or may not) help in your case.; ChangeFilament.g ; requires RRF 3.5 or later! ; list of filaments must follow rules for array ; https://docs.duet3d.com/User_manual/Reference/Gcode_meta_commands#array-expressions var filaments = {"ABS","ABS-CF","ABS-GF","ASA","EDGE","eFlex","eLastic","FLEX","HIPS","NGEN","NYLON","PA-CF","PC","PCABS","PDVF","PEEK","PEI","PEKK","PET","PETG","PLA","POM","PP","PSU","PVA","SCAFF","TPE","TPU",} ; list your filaments here var maxBtns = 10; Max number of buttons per page on PanelDue. Adjust as required. 5 works OK on 7"paneldue - 9 is max! ; don't change below here var thisTool = state.currentTool var allTools = vector(#tools,null) if var.thisTool = -1 if #tools > 1 while iterations < #tools set var.allTools[iterations] = tools[iterations].name ^ "" M291 S4 P"Select tool" K{var.allTools} F0 T{input} G4 S1 if state.currentTool = -1 abort "No tool selected" else set var.thisTool = state.currentTool else T1 var thisExtruder = 0 var allExtruders = vector(#tools[var.thisTool].extruders,null) if #tools[var.thisTool].extruders > 1 while iterations < #tools[var.thisTool].extruders set var.allExtruders[iterations] = iterations ^ "" M291 P"Select extruder" S4 K{var.allExtruders} F0 set var.thisExtruder = input var thisFilament = move.extruders[tools[var.thisTool].extruders[var.thisExtruder]].filament var newFilament = null if var.maxBtns > 10 set var.maxBtns = 10 echo "Paneldue can only display 10 buttons in total" echo "Max buttons has been reset" var thisPage = vector(var.maxBtns,"") var numPages = floor(#var.filaments / (var.maxBtns - 1)) if mod(#var.filaments , var.maxBtns - 1) > 0 set var.numPages = var.numPages + 1 var pagesDone = 0; var btnsDone = 0 var nextFilament = "" var nextItem = 0 while var.pagesDone < var.numPages set var.thisPage = vector(var.maxBtns,"") set var.btnsDone = 0 while var.btnsDone < var.maxBtns-1 set var.nextItem = iterations + (var.pagesDone * (var.maxBtns-1)) if var.nextItem = #var.filaments break set var.thisPage[var.btnsDone] = var.filaments[var.nextItem] set var.nextFilament = var.filaments[var.nextItem] set var.btnsDone = var.btnsDone + 1 if var.pagesDone = var.numPages - 1 set var.thisPage[{var.maxBtns-1}] = "Cancel" else set var.thisPage[{var.maxBtns-1}] = "Next" set var.pagesDone = var.pagesDone + 1 M291 P"Select filament" S4 K{var.thisPage} if input = var.maxBtns-1 continue else set var.newFilament = var.thisPage[input] break if (var.newFilament = null) || (var.newFilament = "") abort "No filaments chosen" else echo "Filament chosen : ", var.newFilament, " : commence change" ; M701 only works if the tool only has one extruder ; If more than one extruder is present on the tool, we'll directly call the macros for load, unload and config ; It's unclear if the filament will be marked as loaded in DWC after this approach. if var.newFilament = "noFilament" if #tools[var.thisTool].extruders > 1 M98 P{directories.filaments ^ var.newFilament ^ "/load.g"} S{var.thisExtruder} M98 P{directories.filaments ^ var.newFilament ^ "/config.g"} S{var.thisExtruder} else M701 S{var.newFilament} if result != 0 abort "Error during loading" M703 else if (var.thisFilament != "noFilament") && (var.thisFilament != null) if #tools[var.thisTool].extruders > 1 ; M701 only works if the too only has one extruder M98 P{directories.filaments ^ var.newFilament ^ "/unload.g"} S{var.thisExtruder} M98 P{directories.filaments ^ var.newFilament ^ "/load.g"} S{var.thisExtruder} M98 P{directories.filaments ^ var.newFilament ^ "/config.g"} S{var.thisExtruder} else M702 M701 S{var.newFilament} if result != 0 abort "Error during loading" M703
-
RE: How can I create a toggle macro?
@paulhew said in How can I create a toggle macro?:
@dc42 said in How can I create a toggle macro?:
state.gpout
Appreciate the input you have given, maybe I need to start at the beginning.
This is my config for my Lights;Lights M950 F10 C"out1" M106 P10 S0 H-1 C"Front Lights" M106 P10 S1
It looks correct, it works. Personally it would be better if it was an on/off and not PWM'd, Do not think I require mood lighting on my chamber lights
So after enabling Object Model Browser, I found F10 under Fans and it shows the correct info as in my config.
I change the lights on and off and it changes 'actualValue'Not being as clever as you guys but willing to learn, how do I query 'Fan.10' actualValue'?
I sort of understand the 'if' part
if 'actualValue' = 1
then set to 0
Elif 'actualValue' = 0
set to 1Personally, it would be handy if there were some basic 'Meta Guides' to help us understand how to write queries and some simple ones we could implement to advance our learning.
try here
https://duet3d.dozuki.com/Wiki/GCode_Meta_CommandsLike if the printer is not homed, then home it and how to turn on /off lights with a one button macro!
If you look through the meta commands topic section of the forum there are many examples which should help you understand.
https://forum.duet3d.com/category/34/gcode-meta-commandsMuch like you have done with the RRF configurator, it explains the function which has helped me learn a lot.
I did search for the "state.gpout[n].pwm" and variations in the dozuki and could not find any reference to it.
Again, Thanks in advance.
You have to use the object model to reference the proper name & syntax for the GPIO output, fan or whatever.
In our case, you're using a fan output, not a GPIO pin.
So you need to look in the fans section of the object browser.
Fan outputs are PWM capable, so that you can set a fan to any speed.
In your case, you can just set it to 0 or 1 using M106.
A GPIO output would be set by M42in terms of code you could have something like
if fans[10].actualValue = 1 M106 P10 S0 else M106 P10 S1
This example is only meant for an LED connected to a fan ouput that is only ever set full on, or full off.
-
RE: What useful things have you printed on a 3D printer?
Apart from sundry brackets, housings etc around the house, most of my efforts are devoted to custome skirted lure designs for marlin fishing.
"Traditional" lures have a head cast from resin. Shaping the face creates a swimming action and having holes through it helps create a bubble trail to help attract the fish.
It's a big ocean out there, so anything out of the ordinary for a predator gets attention and hopefully an "I wonder if I can eat that" moment.I don't try to replicate traditional lures. We all know they work and resin being heavier has different properties.
I try to design things that have never been tried before.
Flexibility of design and fast prototyping allow me to create weird shapes to see what they do to the action as different areas grab the water.
Easy creation of complex air pathways allow differing bubble trails.
I can combine tings like bibs (normally only used on non game fishing lures) and add pivoting sections.Some designs work, others are abject failures , but it's all fun.
Unfortunately I don't own a game boat, so usually only do one trip a year chasing marlin.A few designs
Checking out theaction and bubble trail
A marlin checking out the spread.
Underwater camera towed in the lure spread shows fish come for a look but often don't strike. -
RE: A couple of questions regarding global variables
As has been pointed out, you can declare the globals wherever you like, but of course you must ensure that you do so before you try to use them anywhere.
I always check if the global has already been created as I don't like seeing the errors if I run M98 P"0:/sys/config.g" for example.
It also ensures the global is reset in case I changed it in code somewhere.
In some cases I check for a null value, but in most cases that's probably not necessary.if !exists(global.InputStart) || global.InputStart=null global InputStart=0 else set global.InputStart=0 if !exists(global.InputEnd) || global.InputEnd=null global InputEnd=1 else set global.InputEnd=1
With regards to sending parameters using M98, it's pretty straight forward.
For example you might use it when there is a value you want to use from your slicer.
Here I pass an "S" and a "D" parameter taken from the slicerM98 P"0:/macros/print/doPrimeLine" S[extrusion_multiplier] D[nozzle_diameter] F[filament_diameter] ; does prime line with calculated extrusion amount
In my macro I use the values passed thus
This macro also uses local variables which are freed once the macro has run.
It's a fair bit of overkill to do a prime line, but it was mainly a test bed for various methods; 0:/macros/print/doPrimeLine if state.currentTool = -1 abort "No tool selected" var ThisHeater = tools[state.currentTool].heaters[0] ; create local variable if {heat.heaters[var.ThisHeater].state != "active"} abort "Heater " ^ var.ThisHeater ^ " on " ^ tools[state.currentTool].name ^ " not active" if {heat.heaters[var.ThisHeater].active < heat.coldExtrudeTemperature} abort "Heater " ^ var.ThisHeater ^ " on " ^ tools[state.currentTool].name ^ " set below min extrude temp" if !exists(param.S) abort "no ""S"" (extrusion factor) parameter sent to macro in M98" if !exists(param.D) abort "no ""D"" (nozzle diameter) parameter sent to macro in M98" if !exists(param.F) abort "no ""F"" (filament diameter) parameter sent to macro in M98" M116 ; wait for heaters to settle var height = 0.2 ; create a variable then set it depending on slicer commanded position prior to calling this macro if move.axes[2].machinePosition <= 0.4 set var.height = move.axes[2].machinePosition else set var.height = 0.3 var SF = 100 if param.S <=1 ; check if slicer sends percentage of 1 or 100 set var.SF = param.S ; extrusion multiplier as a percentage of 1 else set var.SF = param.S / 100 var FD = 1.75 ; set default filament diameter set var.FD = param.F ; but over write with slicer setting ; set start and end of move in Y direction - X position will be set manually var start = {move.axes[1].max -10 } ; extrude line start position var end = {move.axes[1].min +10} ; extrude line end position var l = var.start - var.end ; calculated line length var d = param.D ; nozzle diameter that is passed to macro as paramater var amount = ((var.d*var.height*var.l) + (pi*((var.height/2)*(var.height/2))*var.l)) / (pi*((var.FD/2)*(var.FD/2))) * var.SF ; calculate how many mm of filament to extrude - Owen's calculation echo "flow rate value " ^ var.SF ^ " with " ^ var.d ^ "mm nozzle, " ^ var.FD ^ "mm filament dia over " ^ var.l ^ "mm length = E" ^ var.amount ^ "mm" M83 ; set relative extrusion G1 X0 Y{var.start} Z{var.height} F1200 ; move to edge to wipe any oozed filament G1 X0 Y{var.end} F600 E{var.amount}; move and extrude G1 X{var.d} ; move by one nozle width G1 Y{var.start} F600 E{var.amount}; move and extrude G10 ; retract
-
Macro to start job after a delay or at specific time
Sometimes I'm reticent to leave certain jobs printing over night.
I did this macro to allow me to work on models at night and have them start the print process before I get up. (download link at bottom)
This allows all the heating, bed leveling etc to have taken place by the time I'm up.
It should be commented well enough to allow you to figure out how to use it.
Naturally you have to be confident nothing will go wrong before you get to the printer (we all get perfect first layers every time right?).
I highly recommend that it is called BEFORE any heaters are activated.
If your intention is to preheat beds, chambers etc but wait until you command a print, then I suggest starting the heating AFTER the macro exits then putting a pause command in your start G-code after the heating if you don't want printing to start before you get there.The countdown displays continually and is updated at various intervals depending on how long is left till blast off.
There is no option to cancel once started.EDIT: I've modified the code to create a global variable which can be set to cancel the operation. Read the comments.
To test from DWC without running a print, the X parameter must be used.
This will escape the macro after X loops. The loops vary in time depending on the time left, so for example to get the last minute then X must be greater than 22.
Naturally it should not be used when printing.There will be beeps at 5 second intervals in the last minute and 1 second intervals in the last 10 seconds.
; start_after_delay.g ; should be called from slicer start gcode or start.g BEFORE any heating actions ; Accepts four parameters ; X = a testing parameter to allow early exit after X loops ; S = number of minutes to delay ; H = number of hours to delay ; H & S times are added to achieve total delay ; R = start time. Must have the format R"yyyy-mm-ddThh:mm:ss" e.g R"2022-01-04T13:30:00" ; NOTE: the use of curly braces surrounding time works in DWC but fails in SuperSlicer ; R{"2022-01-04T16:47:00"} will fail if used in SuperSlicer ; if R parameter (start time) is passed, H & S are ignored ; at least one time parameter must be passed ; e.g ; M98 P"start_after_delay.g" H4 S20 ; (delay the start of the print for 4 hours 20 minutes) from now ; M98 P"start_after_delay.g" H1 ; (delay the start of the print for 1 hour) from now ; M98 P"start_after_delay.g" S10 ; (delay the start of the print for 10 minutes) from now ; M98 P"start_after_delay.g" R"2022-04-01T06:00:00" ; start the print on 1st April 2022 at 06:00AM var LongDelay = 60 ; Delay between message updates if time left is greater than 1 hour var MediumDelay = 20 ; Delay between message updates if time left is less than 1 hour but greater than 10 minutes var ShortDelay = 10 ; Delay between message updates if time left less than 10 minutes ; at less than one minute updates will happen every 5 seconds ; at less than 10 seconds, updates will happen every second var BeepFrequencyLow = 1000 ; frequency (low pitch) of beep play every var.ShortDelay when one minute left var BeepFrequencyHigh = 3000 ; frequency (high pitch) of beep play every var.ShortDelay when ten seconds left var BeepDuration = 200 ; duration of beep in milliseconds (must be less than one second) - note 1 second will cause constant beep for last 10 seconds ; ************** Don't change below this line ***************** ; Create a global variable to allow the process to be cancelled. if !exists(global.Cancelled) global Cancelled = false ; NOTE: There will be a delay between setting this global to true and the macro/print cancelling ; as it may be called during a G4 wait commmand and can't be activated on until that finishes. ; To use, either send "set global.Cancelled = true" from the command line or a macro, or set up a button with an associated trigger. ; A separate macro may be hard to execute when the loop timer gets down to 5 or 1 seconds. var FileName = "No file selected" if !exists(param.X) if (job.file.fileName = "") || (job.file.fileName=null) abort "No print in progress" else set var.FileName=job.file.fileName else if (job.file.fileName!=null) set var.FileName=job.file.fileName if var.BeepDuration > 1000 echo "Invalid beep duration - reset to 1/2 second) set var.BeepDuration = 1000 if !exists(param.R) if !exists(param.H) if !exists(param.S) echo "No parameters passed - exiting macro" M99 var Hours = 0 ; variable for number of whole hours from start time until run time var Minutes = 0 ; variable for number of whole minutes from start time until run time var Delay = 10 ; variable for delay between displaying messages var HoursLeft= 0 ; variable for number of whole hours from current time until run time var MinutesLeft = 0 ; variable for number of whole minutes from current time until run time var SecondsLeft = 0 ; variable for number of whole seconds from current time until run time var StartTime = datetime(state.time) ; variable to hold time when macro first called var RunTime = datetime(state.time) ; variable to hold time when macro will end and print will run if exists(param.R) set var.StartTime = datetime(param.R) set var.StartTime = var.StartTime - state.time set var.RunTime = state.time + var.StartTime set var.Hours = floor(var.StartTime / 3600) ; calculate number of whole hours till start time set var.Minutes = floor(var.StartTime/60)-(var.Hours*60) ; calculate number of whole minutes till start time else if exists(param.H) set var.Hours = param.H if exists(param.S) set var.Minutes = param.S set var.StartTime = state.time + var.Hours*60*60 + var.Minutes*60 set var.RunTime = var.StartTime var Loops = 0 ; used if a testing parameter X is passed in order to exit after number of loops has expired echo "Print start time is " ^ var.RunTime while state.time < var.RunTime if exists(global.Cancelled) if global.Cancelled = true M291 P"Operation has been cancelled" S0 T3 G4 S3 abort "Deferred print cancelled." if exists(param.X) set var.Loops = var.Loops + 1 set var.HoursLeft = floor((var.RunTime - state.time )/60/60) set var.MinutesLeft = floor((var.RunTime - state.time)/60)-(var.HoursLeft*60) set var.SecondsLeft = mod((var.RunTime - state.time),3600)-(var.MinutesLeft*60) if var.RunTime - state.time > 3600 set var.Delay = var.LongDelay else set var.Delay = var.MediumDelay if (var.RunTime - state.time) > 600 M291 R{var.FileName} T{var.Delay} S1 P{"Print start deferred for " ^ var.HoursLeft ^ " hrs : " ^ var.MinutesLeft ^ " mins - " ^ var.RunTime} G4 S{var.Delay} elif (var.RunTime - state.time) > 60 set var.Delay = var.ShortDelay M291 R{var.FileName} T{var.Delay} S1 P{"Print start deferred for " ^ var.HoursLeft ^ " hrs : " ^ var.MinutesLeft ^ " mins : " ^ var.SecondsLeft ^ " secs - " ^ var.RunTime} G4 S{var.Delay} elif (var.RunTime - state.time) > 10 set var.Delay = 5 M291 R{var.FileName} T{var.Delay} S1 P{"Print starting in " ^ floor((var.RunTime - state.time)) ^ " seconds"} M300 S{var.BeepFrequencyLow} P{var.BeepDuration} G4 S{var.Delay} else set var.Delay = 1 M291 R{var.FileName} T{var.Delay} S0 P{"Print starting in " ^ floor((var.RunTime - state.time)) ^ " seconds"} M300 S{var.BeepFrequencyHigh} P{var.BeepDuration} G4 S{var.Delay} if exists(param.X) && (var.Loops = param.X) break M118 S"Starting Deferred Print" M300 S{floor(var.BeepFrequencyHigh * 1.2)} P1000 G4 S2
-
RE: Paneldue access to Filament Config load/unload options
@nirin
There's probably not going to be a similar functionality to DWC because PanelDue doesn't use a browser.
If you run RRF 3.5 there is a better option to creating a macro button for each filament.
This macro takes advantage of M291 and will allow you to put your filaments into a list and cycle through the pages of them until you find the one you want.
You could simply use M291 S7 to prompt for the name of the filament you want to change to, which would be a simpler macro, but would require you to type it in exactly as it is in DWC
This way you can cut and past your filament names from DWC into the macro.
The best way to get the list is sendM20 S0 P{directories.filaments}
and copy the result from the consoleYou can adjust the number of buttons displayed on each page to suit yourself.
I found five buttons per page fitted on one line (7i)
If you use six, the NEXT button is on another line.
On the last page not all buttons will have a name and a cancel button appears.
EDIT: 7i PD can only display a max of 10 buttons, so I've limited it too that.Download link at bottom of post.
; ChangeFilament.g ; requires RRF 3.5 or later! ; list of filaments must follow rules for array ; https://docs.duet3d.com/User_manual/Reference/Gcode_meta_commands#array-expressions var filaments = {"ABS","ASA","EDGE","eFlex","eLastic","FLEX","HIPS","NGEN","NYLON","PA-CF","PC","PCABS","PDVF","PEEK","PEI","PEKK","PET","PETG","PLA","POM","PP","PSU","PVA","SCAFF","TPE","TPU",} ; list your filaments here var maxBtns = 10; Max number of buttons per page on PanelDue. Adjust as required. 5 works OK on 7"paneldue - 9 is max! ; don't change below here var thisTool = state.currentTool if var.thisTool = -1 abort "No tool selected" var thisFilament = move.extruders[tools[var.thisTool].extruders[0]].filament var newFilament = null if var.maxBtns > 10 set var.maxBtns = 10 echo "Paneldue can only display 10 buttons in total" echo "Max buttons has been reset" var thisPage = vector(var.maxBtns,"") var numPages = floor(#var.filaments / (var.maxBtns - 1)) if mod(#var.filaments , var.maxBtns - 1) > 0 set var.numPages = var.numPages + 1 var pagesDone = 0; var btnsDone = 0 var nextFilament = "" var nextItem = 0 while var.pagesDone < var.numPages set var.thisPage = vector(var.maxBtns,"") set var.btnsDone = 0 while var.btnsDone < var.maxBtns-1 set var.nextItem = iterations + (var.pagesDone * (var.maxBtns-1)) if var.nextItem = #var.filaments break set var.thisPage[var.btnsDone] = var.filaments[var.nextItem] set var.nextFilament = var.filaments[var.nextItem] set var.btnsDone = var.btnsDone + 1 if var.pagesDone = var.numPages - 1 set var.thisPage[{var.maxBtns-1}] = "Cancel" else set var.thisPage[{var.maxBtns-1}] = "Next" set var.pagesDone = var.pagesDone + 1 M291 P"Select filament" S4 K{var.thisPage} if input = var.maxBtns-1 continue else set var.newFilament = var.thisPage[input] break if (var.newFilament = null) || (var.newFilament = "") abort "No filaments chosen" else echo "Filament chosen : ", var.newFilament, " : commence change" if var.newFilament = "noFilament" M701 S{var.newFilament} if result != 0 abort "Error during loading" M703 else if (var.thisFilament != "noFilament") && (var.thisFilament != null) M702 M701 S{var.newFilament} if result != 0 abort "Error during loading" M703
-
RE: Chose random spot/area to home Z?
Yes it's possible, but why?
Assuming you're on V3.2b you can use the random(nn) function
Check release notes for which version it was introduced in.E.g
G1 X{move.axes[0].max/2 - 25 + random(50)}
Would put you +/- 25mm from bed centre assuming X max defines the edge of the bed and X min = 0
You could always hard code it. -
RE: Slicer-adjustment, script or something else?
You can just select laser mode using M452
https://duet3d.dozuki.com/Wiki/Gcode#Section_M452_Select_Laser_DeviceModeUse the pin defined in the P parameter to control a relay which in turn can be wired to your torch trigger.
Your image appears to depict tig or plasma cold wire.
this is a poor choice for WAAM using a printer base as you have no way of keeping the wire in the correct orientation with regards to the arc and direction of travel.The best process is CMT (Cold Metal Transfer)
This is being used by several commercial WAAM systems.