klipper
klipper copied to clipboard
SET_PIN not usable for laser engraving
Using SET_PIN for laser raster engraving is very slow. Same code runs much faster on marlin using m42 instead of SET_PIN. Attached gcode to test. Removing m400 will make execution asynchronous.
On Sun, Jan 21, 2018 at 04:03:58AM +0000, GitHub1712 wrote:
Using SET_PIN for laser raster engraving is very slow. Same code runs much faster on marlin using m42 instead of SET_PIN. Attached gcode to test. Removing m400 will make execution asynchronous.
It will be very slow if there are a lot of M400 commands in the file. The M400 command instructs Klipper to completely wait for all activity on the printer to stop.
If you're seeing an issue without the M400 commands, can you post the log of a session without the M400 commands (but with the M112)?
-Kevin
It's worth noting that the SET_PIN command was not intended as a mechanism for high speed updates. A SET_PIN command has a minimum time of 100ms - if multiple commands are sent within a smaller window, then the subsequent commands will be delayed so that each pin setting is active for at least 100ms.
This behavior is due to how commands are buffered between host and micro-controller. Things like step times are buffered on the micro-controller, and hundreds of thousands of them can be queued. However, pin updates have a very small queue on the micro-controller (no more than 1 update) and instead most of the queuing is done on the host. So, there would need to be development work necessary to make high speed arbitrary pin updates with precise timing work.
-Kevin
thank you for checking, so thats a point better working with marlin. Many are attaching laser on 3d printer. I think set_pin is something that should work fast. I will try to get it working with extruder commands to controll the laser.
2018-01-21 6:57 GMT+01:00 KevinOConnor [email protected]:
It's worth noting that the SET_PIN command was not intended as a mechanism for high speed updates. A SET_PIN command has a minimum time of 100ms - if multiple commands are sent within a smaller window, then the subsequent commands will be delayed so that each pin setting is active for at least 100ms.
This behavior is due to how commands are buffered between host and micro-controller. Things like step times are buffered on the micro-controller, and hundreds of thousands of them can be queued. However, pin updates have a very small queue on the micro-controller (no more than 1 update) and instead most of the queuing is done on the host. So, there would need to be development work necessary to make high speed arbitrary pin updates with precise timing work.
-Kevin
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/KevinOConnor/klipper/issues/133#issuecomment-359226158, or mute the thread https://github.com/notifications/unsubscribe-auth/ACA3huaDUQKYO-cPyH3PPFidduWuUWuqks5tMtHUgaJpZM4RlsDc .
In issue #152 Lucas wrote:
As the title says, the printer stops with a 'timer too close' error.
Now, before anything else, I should mention 2 things:
- It's not 3d printing. It's laser engraving. I've attached a laser driver to the fan output and control the laser with M106 and M107 (only on and off, no power control)
- I modified fan.py to make FAN_MIN_TIME = 0. [...] So, the thing is, this may not be a klipper bug as I'm using the fan output as something else, but is there a way to achieve what I'm trying to achieve here with klipper? (rapid fan on/off changes)
If not, is there something like a safe min time between pwm events? (less than the default 0.1 s)
Unfortunately, it doesn't seem that there is a simple python-only solution to make Klipper compatible with laser engravers.
There are two major components in the Klipper system - the host code and the micro-controller code. The host is responsible for building the schedule of events and the micro-controller executes that schedule at the specified times. Right now, the micro-controller is coded so that it can store a schedule of 100K+ steps in its internal memory. However, the micro-controller is not written to store more than a handful of scheduled pin events.
This was done simply because on a traditional 3d-printer there's no particular need to store more than a few pin changes in memory at a time (as, stepper motors aside, there are no pins that rapidly and repeatedly change).
If the laser needs to be able to reliably alter its settings with high frequency and high predictability, then I suspect the best solution would be to extend the micro-controller code to create a queue of scheduled pin events - just as the micro-controller stepper code does. Then, the host code could fill that queue of scheduled events instead of its current mechanism (which is to buffer the events on the host side). This type of change is a bit involved, however. It would require python code changes, host C code changes, and micro-controller code changes.
-Kevin
I've been reading some code but I don't think I'm understanding this correctly. Let's see. For each pin a oid object containing the pin info is created in the MCU. This oid object is the one being queued/scheduled (at least it holds a timer) and thus it cannot be scheduled more than once?
Is that it?
So when my log shows things like the thing below and tries to schedule the same pin 4 times in a row it messes something up?
Sent 95 13602.031433 13602.030873 14: seq: 14, schedule_soft_pwm_out oid=4 clock=2274294805 value=256
Sent 96 13602.045151 13602.044631 13: seq: 15, schedule_soft_pwm_out oid=4 clock=2274534805 value=0
Sent 97 13602.061034 13602.060474 14: seq: 16, schedule_soft_pwm_out oid=4 clock=2274774805 value=256
Sent 98 13602.075648 13602.075128 13: seq: 17, schedule_soft_pwm_out oid=4 clock=2275570795 value=0
I think I'm getting it wrong :P
On Tue, Jan 30, 2018 at 11:30:54PM +0000, Lucas wrote:
I've been reading some code but I don't think I'm understanding this correctly. Let's see. For each pin a oid object containing the pin info is created in the MCU. This oid object is the one being queued/scheduled (at least it holds a timer) and thus it cannot be scheduled more than once?
Is that it?
Yes.
So when my log shows things like the thing below and tries to schedule the same pin 4 times in a row it messes something up?
Sent 95 13602.031433 13602.030873 14: seq: 14, schedule_soft_pwm_out oid=4 clock=2274294805 value=256 Sent 96 13602.045151 13602.044631 13: seq: 15, schedule_soft_pwm_out oid=4 clock=2274534805 value=0 Sent 97 13602.061034 13602.060474 14: seq: 16, schedule_soft_pwm_out oid=4 clock=2274774805 value=256 Sent 98 13602.075648 13602.075128 13: seq: 17, schedule_soft_pwm_out oid=4 clock=2275570795 value=0
I think I'm getting it wrong :P
Very close. First, run your log through ./scripts/logextract.py (as described in docs/Debugging.md). That will help cross index some of the information in the log.
Because the mcu schedule_soft_pwm_out command can only schedule one event at a time, the host will only send one event at a time to the mcu. The host waits until the last event is finished before sending the next. Eventually, this slow "drip feed" can't handle the number of events and the mcu receives a request for a time that is in the past. That results in a "timer too close" error.
As earlier, the way I would address it would be to allow more than one scheduled event to be stored in the mcu, thus avoiding the slow "drip feed" issue.
-Kevin
Sorry to bother you again.
As earlier, the way I would address it would be to allow more than one scheduled event to be stored in the mcu, thus avoiding the slow "drip feed" issue.
I've been fiddling with the code, and have modified the schedule_digital_out
command, so now it's possible to schedule several events of this type. It works, but...
Because the mcu schedule_soft_pwm_out command can only schedule one event at a time, the host will only send one event at a time to the mcu. The host waits until the last event is finished before sending the next.
This is what has been bothering me for the past few days. I can now have several commands of this type scheduled, but it won't do anything different because of this limit. Where in the code is that 'send only one event at a time and wait for the response'?
I think the problem is that I'm not understanding how the serialqueue works.
Some questions that may (or may not) help me understand.
- What does the
stalled_bytes
ofserialqueue
means? - I see there are 4
alloc_command_queue
(endstop, digital_out, pwm and adc). Each one of this queues have their own bg thread, and each one takes turns writing to theserial_fd
in turns. Does it really work that way? I fell I'm misundestanting/missing something. - In the micro controller,
command_find_block
reads the serial port messages and then sends theack
inmediatly back to the host. So the host code could never know when said command finished executing. Is this correct?
On Sun, Feb 18, 2018 at 01:00:03PM -0800, Lucas wrote:
Sorry to bother you again.
As earlier, the way I would address it would be to allow more than one scheduled event to be stored in the mcu, thus avoiding the slow "drip feed" issue.
I've been fiddling with the code, and have modified the
schedule_digital_out
command, so now it's possible to schedule several events of this type. It works, but...Because the mcu schedule_soft_pwm_out command can only schedule one event at a time, the host will only send one event at a time to the mcu. The host waits until the last event is finished before sending the next.
This is what has been bothering me for the past few days. I can now have several commands of this type scheduled, but it won't do anything different because of this limit. Where in the code is that 'send only one event at a time and wait for the response'?
It's in mcu.py:MCU_digital_out:set_digital() - it sets the next requests minclock to the time of the last request. So, serialqueue.c will not transmit the message until it believes the MCU clock is past minclock.
I think the problem is that I'm not understanding how the serialqueue works.
Some questions that may (or may not) help me understand.
- What does the
stalled_bytes
ofserialqueue
means?
The stalled_bytes are the number of bytes that are queued in serialqueue.c that can not yet be transmitted because their minclock has yet to be satisfied. These are messages being delayed.
In contrast, the ready_bytes counter is the number of bytes stored in serialqueue.c that have met the minclock. (These are messages that are likely to go out in the next few milliseconds.)
- I see there are 4
alloc_command_queue
(endstop, digital_out, pwm and adc). Each one of this queues have their own bg thread, and each one takes turns writing to theserial_fd
in turns. Does it really work that way? I fell I'm misundestanting/missing something.
No - there is only one thread in serialqueue.c. The command queues are just linked lists of commands to be sent to the MCU. It's done this way to prioritize messages should there be a situation where there are more messages to be sent than available bandwidth. So, for example, if a PWM message needs to be received by clock 101 (a send() with reqclock=101), a digital_out message needs to be received by clock 999, and there is limited bandwidth, then we'll send the PWM message first. Messages can be reordered wrt other command queues, but messages can not be reordered within a given command queue.
- In the micro controller,
command_find_block
reads the serial port messages and then sends theack
inmediatly back to the host. So the host code could never know when said command finished executing. Is this correct?
We don't release messages by acks. Instead, the code in clocksync.py calculates a relationship between the MCU clock and the host clock. The serialqueue.c code uses a conservative version of this host-to-mcu clock prediction to determine when to release messages so that they are never received at the MCU prior to their minclock.
-Kevin
Has there been a solution at all for this? I've been using klipper as is for cutting for abit now, and find its works ok under 300mm/min but would love to run at 10 times that speed.
+1 for laser support please
https://github.com/gnea/grbl/wiki/Grbl-v1.1-Laser-Mode https://github.com/Smoothieware/Smoothieware/tree/edge/src/modules/tools/laser
Some important features that's must have
Proportional laser (M4 mode in grbl, on by default in smoothie) scales PWM power according to acceleration to prevent darker burns during (ac/de)celleration G0=laseroff move, G1=laseronmove (Makes gcode generator more efficient, no need for m3/m5 on every segment / change etc PWM freq configurable (we found that between various diode drivers and CO2 driver, it helps to tweak pwm freq for a better linear response. CO2 PSUs seem to like a 1khz, whereas some of the chinesier diode drivers need 30khz, etc)
Just checking in, if we are any closer yet?
I'm not aware of any progress on this topic. I don't have any short term plans to work on this myself.
-Kevin
How about changing the interface between host CPU and MCU/s with 2 x SPI for fast communication ? Another way might be porting the MCU code on the CPU as process or on a co-processor. Allwinner H3 (Orange Pi, NanoPi NEO , ...) has AR100 co-processor and this seems to be done in LinuxCNC + Orange Pi project (https://github.com/orangecnc), ARISC firmware for making a real-time GPIO manipulations (https://github.com/orangecnc/h3_arisc_firmware).
Does this mean that if only one pin change is issued, this pin change will occur immediately after the previous move has finished, but any subsequent pin change will occur at least 100 ms later?
I need to send out one pulse at a very precise position within the g-code, without interrupting the rest of the g-code. Is that possible?
The current code supports scheduling of pin updates at very precise times. However, it does not support queuing of multiple updates on the micro-controller - thus one can write python code to arrange for a pin to be set at an arbitrary time, but in practice the next update has to be ~100ms or so in the future.
-Kevin
Is there anyone who has found some solution for laser control? I want to make SLS printer. I have an idea that may be it would be possible to connect laser ON/OFF pin to extruders Enable PIN? How fast can it change PIN state? Does it still has this 100 ms limitation? Then it would be necessary to add code that extruder is disabled after each move or each time when G0 command is received and activated each time when extruder is moved (E values).
A faster PIN-Operation on any digital out pin is possible on this experimental branch, but be aware that it will not be continued (more on that at #3338 and #3515).
Has there been a solution at all for this? I've been using klipper as is for cutting for abit now, and find its works ok under 300mm/min but would love to run at 10 times that speed.
How did you enable laser control for your use of Klipper in a laser cutter? I'm trying to do the same thing. Fan?
My setup is a switchable printer/Laser head. The laser is connected to the part-fan PWM output. This is currently possible on this experimental branch. But keep in mind that this is all work-in-progress and proof-of-concept. Currently, the "fast" PWM updates only work on hardware-pwm there, but soft-pwm is not much of a hurdle.
My printer starts to shake at 100mm/s short-movements, so I did not test it that fast. On my GT2560, 75mm/s with 0.1mm resolution works with 40% MCU load.
The roadmap to fast PWM support is to finish https://github.com/KevinOConnor/klipper/pull/3995 first before the "big" stepcompress/movequeue may be started. This will probably be a longer process, but testing then will be easier when I start a PR with detailed instructions on how to test that.
I solved this problem electronically. I use extruder step pulse as signal for laser ON/OFF and FAN PWM for laser power adjustments. Additional benefit of using extruder step pulse is that it adjusts laser power together with acceleration/deacceleration- less step pulses compared to max speed automatically reduces laser power from maximum. I have tested it till 500 mm/s, at the moment it works ok.
How do you generate your GCode for that? But this is a very interesting and creative solution!
Edit: The "proportional" mode is something my solution still lacks. But how would you laser-raster with that, if you still have the PWM limitation of 0.1s/update?
You can use any standard slicer. It will generate necessary E values. Only trick in the beginning is to find and set - extruder and filament diameter relation, so you can get desired step pulses for extruder.
PWM (fan) is used only to set max power. And it doesn't change so often, so you are not limited to 0.1s/update. Proportional power is adjusted based on extruders step pulses. It is easier to configure Klipper to use the same amount of steps per rotation for extruder and XY movement, then it is easier to calculate how many pulses you will get for linear movement. For example you move G1 X100 ,then you have to adjust slicer that it generates E value 100 ( extruder and filament diameter, flow multiplier ), then it will generate the same amount of pulses as X&Y stepper. During acceleration and deacceleration extruder will generate less step pulses so you can proportionally reduce max power which you have set using PWM fan output.
That's a creative solution, but stepper pulses are frequency modulated and most lasers expect pulse width modulation for power control. How do you convert the signal? And with a raster resolution of around 0.1mm, you'd use a command for every raster movement? Something like G1 X0.1 E0.1 G1 X0.2 E0.1 for a X-move at full power (relative extruder, absolute positions)? I have pins available on my board that could be configured as a stepper.
Completely different idea: How fast can the pins on the host be controlled? I could wire the laser power input directly to the Pi and output the PWM signal there, but if the same restrictions apply to the host driver, that won't work.
The best solution imo would be to implement something like Marlin has, where laser power can be specified per move, so the output power is automatically adjusted to acceleration. They call it "inline laser power" and I had spectacular results with it: https://github.com/MarlinFirmware/Marlin/blob/e3a12c3c28c2b6c4882eaa6549c6053b4d38953e/Marlin/Configuration_adv.h#L2954
I am converting stepper pulses to analog voltage and then I am using arduino to calculate necessary power to PWM range.
Arduino sketch reads FAN pwm output from Klipper and using map function based of extruders step pulses adjusts power. Additionally if there is any signal in extruder it activates digital output which controls lasers ON/OFF signal.
Completely different idea: How fast can the pins on the host be controlled? I could wire the laser power input directly to the Pi and output the PWM signal there, but if the same restrictions apply to the host driver, that won't work.
I suppose that the PI will be able to drive this pretty fast, but never as accurate as the MCU can do it. This is why realtime-tasks are not done by the klipper host software, but scheduled.
The best solution imo would be to implement something like Marlin has, where laser power can be specified per move, so the output power is automatically adjusted to acceleration. They call it "inline laser power" and I had spectacular results with it:
This is on my roadmap. This will not only add a proportionality, but also reduce the GCODE-bottleneck.
Currently, I am finalizing https://github.com/KevinOConnor/klipper/pull/3995, after that comes the integration of fast pwm scheduling. I don't think that there is a need for overcomplicating things when our Klipper/MCU combination is well capable of everything we need. Judging by the benchmarks, I am confident that laser updates can reach the range of 100k-500k updates per second, surpassing every other printer/laser-firmware I know of.
Cool, I'll wait for the implementation of PWM scheduling then! I already wrote my own little "slicer" for images, so syntax adjustments are not an issue (still working on minimizing GCode output).
Might build a little ATtiny-based step-to-pwm-converter if I have time, but configuring an additional extruder also takes up lots of pins, I'll have to check if I have enough.
Completely different idea: How fast can the pins on the host be controlled? I could wire the laser power input directly to the Pi and output the PWM signal there, but if the same restrictions apply to the host driver, that won't work.
I suppose that the PI will be able to drive this pretty fast, but never as accurate as the MCU can do it. This is why realtime-tasks are not done by the klipper host software, but scheduled.
The best solution imo would be to implement something like Marlin has, where laser power can be specified per move, so the output power is automatically adjusted to acceleration. They call it "inline laser power" and I had spectacular results with it:
This is on my roadmap. This will not only add a proportionality, but also reduce the GCODE-bottleneck.
Currently, I am finalizing #3995, after that comes the integration of fast pwm scheduling. I don't think that there is a need for overcomplicating things when our Klipper/MCU combination is well capable of everything we need. Judging by the benchmarks, I am confident that laser updates can reach the range of 100k-500k updates per second, surpassing every other printer/laser-firmware I know of.
IMO, if this is able to implemented, there will be no better option for laser engravers / cutters. Almost every engraving system today is limited by processor speed. I was in the middle of building an arudino based Z-axis height to PWM converter, but will also now wait for this to arrive. I'm not (yet) able to develop on Klipper, but will be a happy tester when needed. I've got both a Co2 laser and a diode laser.
I'm not (yet) able to develop on Klipper, but will be a happy tester when needed. I've got both a Co2 laser and a diode laser.
I would be happy if you could test #4128. Questions regarding the Hardware-Setup and in general are welcome, so that we can adapt the how-to file to being more understandable.
I tested https://github.com/KevinOConnor/klipper/pull/4128 today, works quite well with my setup. Hardware:
- Raspberry Pi 3B, default clock
- BTT SKR 1.4 100MHz
- Some generic 1.3W diode laser with PWM input
Fastest feed rate I tested so far was 50mm/s, resolution 0.1mm. Above that I sometimes get "Timer too close", but I'm not sure when exactly, going to test that the next days.
So far the results are very promising:
You can also try my image to GCode converter here if you want. 100% JS, no server-side code, feel free to download and modify the script!
I would prefer if we could continue discussing the quirks of #4128 inside the merge request. I will update some fixes that may reduce the problems on other systems.
Btw: I love this goofy Hamster in Tuxedo :)