Klipper3d / klipper

Klipper is a 3d-printer firmware
GNU General Public License v3.0
8.98k stars 5.17k forks source link

Add mSLA capabilities #6575

Open sn4k3 opened 2 months ago

sn4k3 commented 2 months ago

Background

I'm the creator of UVtools, a software dedicated to help and make resin printing sane (mSLA/DLP) with tons of manipulations and checks.

From the past 4 years I've being deal with many mSLA formats available today, I known them in depth, I'm amazed with the fact that mSLA is far behind FDM in slicing features (In fact I could not believe 4 years ago that mSLA was not gcoded and open like FDM). We are very limited to static file formats that only follow a structure with few or none options. The existing gcode printers in this technology can be counted by hand and the ones that exists suffer from bugs or bad performance.

The bad firmwares are ruining the printers "completely", they never listen to community, never fix bugs that in a normal case takes just 1 day to fix and send the patch firmware online. They really don't care, they just flood the market with models and models of machines that many are very decent on it hardware but the firmware make them a crap machine. Companies right now are tied to one or two solutions, which are always locking formats and make the best effort to encrypt and make sure no others can be used.

Myself got asked by a board company what could be done to improve the eco-system. I tell them: Well, make the format open-source and run over gcode... A: That's not possible, our hardware is not capable, parse gcode is very hard, run png's is not possible, not economical, can't be made open-source, bla bla bla. All runaround. The fact is that now they have 4GB boards running linux, is load png still too hard? Is gcode still not possible? 🤦

Then big promises came, open-source boards and format. Guess what? They call themself open-source by just having a repo in github with just a few documentation on the format, which even with the docs we are unsure about the meaning of some fields...

Lies, promises, bad products, and more lies... This is a loop in mSLA with huge lack of vision.

This crappy mindset are ruining the technology and the overall experience. Myself I don't active do nor like resin printing, but I can't just see this and doing nothing when I have to power to change the situation! That lead me to this PR.

What is required run mSLA?

mSLA only need one Z axis, feed image to a screen and lit a UV LED over it. Looping layer by layer and forming a solid piece from cured resin from the lit pixels. FDM is far more complex to build because it motion system. So now you see another point here, why such simple tech is soo hard to implement? The answer is: Drive the displays! Most displays are held, no datasheets no information how to drive it, require custom FPC = custom board. However today most LCDs are HDMI! And you guess it, plug HDMI to PC and send image. Easy!

Why Klipper?

I have been told: Why klipper for that? Thats OP for what you want. Why not using something else? I can't disagree more!

So is Klipper OP for this!? NO! It's just perfect!

Also I think it's time for klipper to embrace other manufacturing processes, it's a win-win and will make klipper richer in capabilities and features.

Hardware

Right now I'm using:

The PR / Implementation

Note that I mark this PR as a draft because I wan't it to be review, it's my first time dealing with klipper sources. In fact I did what most said impossible in just 3 days but had my printer displaying the image using klipper in just one day. This alone means klipper perfect :) So forgive-me If something is not soo well defined.

GCode sample:

MSLA_DISPLAY_VALIDATE RESOLUTION=3840,2400 PIXEL=0.05,0.050

G21;
G90;
M17;
M1401;
G28 Z;
M106 S255;

M1451 F"1.png"; Render image into display (LED is OFF)
G0 Z7.1 F800;
G0 Z0.1 F1000;
G4 P3000;
M1400 S255 P4000 ; Turn LED ON for 4s, if image not render yet, it waits before turn ON for completion
G4 P3000;

M1451 F"2.png";
G0 Z7.2 F800;
G0 Z0.2 F1000;
G4 P3000;
M1400 S255 P4000;
G4 P3000;
...

Image display

The displays are now connected via HDMI on the Pi. Pi itself let us configure the framebuffer (config.txt) to match the display. So all we need is set correct parameters there. After I created a [framebuffer_display] module into klipper. Which allow us to configure any framebuffer and send buffers/images over there. See framebuffer_display.py I created to be generic and mux, so some may use it for other displays and send pics to a HDMI display. All code is python and it direct address the framebuffer without any external dependencies.

From that I created the [msla_display] which is the keypoint

eg:

# Also compatible with [pwm_tool]
[output_pin msla_uvled]
pin: PA5

[msla_display]
type: Mono
model: TM089CFSP01
framebuffer_index: 0
resolution_x: 3840
resolution_y: 2400
pixel_width: 0.05
pixel_height: 0.05
cache: 1
uvled_output_pin_name: msla_uvled

type, model are just information, not actually used. All other parameters are used to have sane checks but also provide information about the display which is required. the cache parameter is a smart-feature to cache next layers so buffer is much more fast to send when time comes.

The most smart approach to exposure an layer is:

  1. Starts new layer
  2. Display image
  3. Do lift/rectract and/or wait times
  4. Turn on UV LED
  5. Wait the exposure time
  6. Turn of UV LED
  7. Loop to 1

In my implementation before UV LED is turn on, it waits for image buffer send to be completed. A smart feature that most of gcode printers does not have, they never know when buffer is actually completed, in consequence UV LED can be lit before image is present. I really can't understand why, why so hard to make it right? With cache system and direct buffers I manage fast speeds on transfers, not that we really need them, because they way mSLA works with lifts and waits make time to image to complete (In most cases).

Here the results of an 4K buffer, sampled and avg 10 times:

Clear time: 7.285666ms (10 samples)
Send cached buffer: 3.448391ms (10 samples)
Read image from disk + send time: 94.163990ms (10 samples)
Buffer size: 9216000 bytes (8.79 MB)
MSLA_DISPLAY_RESPONSE_TIME AVG=10

So in normal conditions if image is load from disk it took about 94ms, but if cached it took 3.5ms to be rendered to the display. Pretty good.

Some gcode commands where created to:

M1400: Because UV wavelength goes from 100nm-400nm M1451: Command used by other gcode printers to display the image is M6054, but opt for M1451 to be closer from M1400

Commands are debatable, we should set an standard now.

[virtual_sdcard]

The mSLA requires an image per layer. Some users prints can go up to 4000 layers. Upload folders is a bit messy, the format I think is best it's the Zip file (pngs + gcode) I had to alter the virtual_sdcard.py with some features:

[toolhead.py]

I admit that I had a hard time discovering the [printer] 😅 I have added the property manufacturing_process this is required for others interfaces to adapt. Up now all is related to FDM, so interfaces like mainsail would need to know proper process to adapt interface, eg: For mSLA we want to see current layer image that is printing.

[printer]
manufacturing_process: mSLA
kinematics: cartesian
max_velocity: 300
max_accel: 3000
max_z_velocity: 25
max_z_accel: 150

[printer_stats.py]

Klipper was build around FDM, stats is filament based. Current system is very limited so I rewrote the stats to be universal and more useful. UI's can now render information easier without parse.

Syntax:

        Syntax: SET_PRINT_STATS_INFO TOTAL_LAYER=[count]
                                     CURRENT_LAYER=[number]
                                     MATERIAL_NAME=["name"]
                                     MATERIAL_UNIT=[unit]
                                     MATERIAL_TOTAL=[total]
                                     CONSUME_MATERIAL=[amount]

        TOTAL_LAYER: Total layer count
        CURRENT_LAYER: Current printing layer number
        MATERIAL_NAME: Name of the material being used
        MATERIAL_UNIT: Material unit
        MATERIAL_TOTAL: Total material this print will consume
        CONSUME_MATERIAL: Consume material and increment the used material

FDM sample:

Print start: SET_PRINT_STATS_INFO TOTAL_LAYER=1000 MATERIAL_NAME="Generic Orange PLA" MATERIAL_TOTAL=100000 Before layer: SET_PRINT_STATS_INFO LAYER=1 After layer: SET_PRINT_STATS_INFO CONSUME_MATERIAL=100

echo: 100 / 100000 mm -> This is the used material in mm

Advanced:

SET_PRINT_STATS_INFO MATERIAL_NAME="Brand Red PLA" 
M600

Then our M600 macro can be expanded to show the material to insert.

image

Resin sample:

Print start: SET_PRINT_STATS_INFO TOTAL_LAYER=1000 MATERIAL_NAME="Generic ABS-Like" MATERIAL_TOTAL=1000 Before layer: SET_PRINT_STATS_INFO LAYER=1 After layer: SET_PRINT_STATS_INFO CONSUME_MATERIAL=1

echo: 1 / 1000 ml -> This is the used material in ml

image

Laser sample:

Print start: SET_PRINT_STATS_INFO TOTAL_LAYER=1 MATERIAL_NAME="MDF" MATERIAL_TOTAL=10000 On etch/period: SET_PRINT_STATS_INFO CONSUME_MATERIAL=10

echo: 10 / 10000 mm^2 --> This is the etched / cut area

CNC sample:

Print start: SET_PRINT_STATS_INFO TOTAL_LAYER=1 MATERIAL_NAME="MDF" MATERIAL_TOTAL=10000 On cut/period: SET_PRINT_STATS_INFO CONSUME_MATERIAL=10

echo: 10 / 10000 mm^3 --> This is the removed material volume

This are just samples, and uses are not limited to them. UI's can now render better stats without the need of parse nor if's to display stats to other manufacturing processes.

[display]

2024-05-02-21-08-44-129

What's missing

Breaking changes:

None.

Video

(Sorry for the bad quality.)

Sample print file

https://we.tl/t-l9a9fMftQN

WIP

~This is a WIP,~ looking for ideias, improvements but the implementation is working stable.

sn4k3 commented 2 months ago

Some questions:

  1. Is there events I can listen for print start/end/pause/cancel? Because I want to clear cache on print end event, and right now I only do at print start. But also it's safer to issue a UV LED OFF command in case macro or slicer miss that on print end.
  2. I'm thinking in offer a option to limit the UV LED ON time, why? Because UV LED are powerful, they saturate the heatsink very fast, if on for long periods can damage itself, also wear LCD. Giving the option in seconds to limit the amount of time the LED is ON would be a safe feature, in a normal print we never need more than 30s. So user can configure eg. 60s maximum. If he forget to turn LED OFF, system will do it as a safe-guard. My question: You think this is something I can implement on [output_pin] and benefit other uses, or do it at [msla_display] level?
  3. In mSLA TSMC was introduced to cut time in print, it does two lifts (Slow + Fast) and then retract with (Fast + slow). It need to detach from FEP slow and when done raise a bit more fast. TSMC is a stupid fix, we don't really need it. What we need is Acceleration! So we want to have a slow accel on lift start, but fast accel on stop. Then fast accel on retract start and slow accel on stop. Currently I think it's only possible to set accel in one way. Would be possible to have a second parameter to define the stop accel?

See this code:

M6054 "2.png";
G0 Z7.2 F8; -> Safe lift, detach from fep + refresh resin
G0 Z0.2 F10; -> Retract to cure the layer at 0.2mm pos
G4 P3000; -> Wait to platform settle
M1400 S255; -> led on
G4 P20000; -> 20s
M1400 S0; -> led off
G4 P3000; -> wait for layer to cooldown

What would be optimal:

M6054 "2.png";
SET_VELOCITY_LIMIT ACCEL=5,500 -> Slow detach but fast stop
G0 Z7.2 F8; -> Safe lift, detach from fep + refresh resin
SET_VELOCITY_LIMIT ACCEL=500,5 -> Fast retract but slow stop
G0 Z0.2 F10; -> Retract to cure the layer at 0.2mm pos
G4 P3000; -> Wait to platform settle
M1400 S255; -> led on
G4 P20000; -> 20s
M1400 S0; -> led off
G4 P3000; -> wait for layer to cooldown

You think the SET_VELOCITY_LIMIT can be improved in that way?

github-actions[bot] commented 1 month ago

Thank you for your contribution to Klipper. Unfortunately, a reviewer has not assigned themselves to this GitHub Pull Request. All Pull Requests are reviewed before merging, and a reviewer will need to volunteer. Further information is available at: https://www.klipper3d.org/CONTRIBUTING.html

There are some steps that you can take now:

  1. Perform a self-review of your Pull Request by following the steps at: https://www.klipper3d.org/CONTRIBUTING.html#what-to-expect-in-a-review If you have completed a self-review, be sure to state the results of that self-review explicitly in the Pull Request comments. A reviewer is more likely to participate if the bulk of a review has already been completed.
  2. Consider opening a topic on the Klipper Discourse server to discuss this work. The Discourse server is a good place to discuss development ideas and to engage users interested in testing. Reviewers are more likely to prioritize Pull Requests with an active community of users.
  3. Consider helping out reviewers by reviewing other Klipper Pull Requests. Taking the time to perform a careful and detailed review of others work is appreciated. Regular contributors are more likely to prioritize the contributions of other regular contributors.

Unfortunately, if a reviewer does not assign themselves to this GitHub Pull Request then it will be automatically closed. If this happens, then it is a good idea to move further discussion to the Klipper Discourse server. Reviewers can reach out on that forum to let you know if they are interested and when they are available.

Best regards, ~ Your friendly GitIssueBot

PS: I'm just an automated script, not a human being.

KevinOConnor commented 2 weeks ago

Interesting, thanks.

I'm curious what your long-term plans are for this work. Are you still working on it? Are there users utilizing this system today?

I didn't see your questions earlier because of the many earlier updates to the PR. I'll try to answer them:

Is there events I can listen for print start/end/pause/cancel?

Unfortunately that is difficult to do with g-code because there isn't an explicit "start of print" nor "end of print" command in g-code. The closest equivalent would be the events generated from the idle_timeout module (see the send_event() calls in klippy/extras/idle_timeout.py ).

You think this is something I can implement on [output_pin] and benefit other uses, or do it at [msla_display] level?

That sounds like you'll want to implement in a module. You could also look at klippy/extras/pwm_tool.py - as that has emergency cutoff code in it. (Should the host disconnect from the mcu while the gpio is enabled, the mcu will know to transition to an error state and turn off the gpio.)

Would be possible to have a second parameter to define the stop accel?

This was discussed on Discourse. It's possible, but I'd guess it would be simpler to break up the moves and change acceleration between move segments (as you've indicated on Discourse).

I also have a few high-level questions:

  1. I'm curious why you plan to use G1 and M6054 style g-code commands? I understand the desire for an open text-based format, but I'd have thought more verbose commands something like MOVE_AND_DISPLAY_PICTURE HEIGHT=2.300 FILE="2.png" would be possible.
  2. Is there readily available hardware that this code can run on today? That is, can many users buy off-the-shelf hardware and use this support on it, or would users be required to build a printer from scratch.
  3. What software will be used to produce the gcode/png/zip files that are proposed here? Is that software readily available today.
  4. I understand from your description that mSLA firmware is often proprietary. Are there any other popular open source mSLA firmwares at all? Or, is this goal for this to be the basis for the first popular open source mSLA firmware. (Excuse my ignorance here - I have no experience at all with resin based printers.)

Thanks again, -Kevin

sn4k3 commented 2 weeks ago

I'm curious what your long-term plans are for this work.

I plan to maintain it and work with community to attend and lead mSLA forward. Klipper has everything to drive mSLA, only the display image part is missing. I also made it to support other techs and not be "filament" specific. I think Klipper core should go in this direction, abstract rather than specific.

Are you still working on it?

No, I find this PR very stable and everything is ready to be used. I'm printing with confidence and without any failure so far. But will be optimal to have another set of eyes on the code.

Are there users utilizing this system today?

Just me. I converted my printer and throw it a Manta M5P + CM4 to make this proof of concept. Currently printing with it using mainsail.

2024-05-11-18-38-19-895

The Athenna printers (Runs nanoDLP with klipper) also had they sync problem with image and exposure time, they looked my PR and adopted the concept and rewrite my M1400

This was discussed on Discourse. It's possible, but I'd guess it would be simpler to break up the moves and change acceleration between move segments (as you've indicated on Discourse).

Yes, topic was open by me. Is true that 4xG1+SET_VELOCITY does optimize (1s less in my tests) however in future would be a nice to have this and benefit other systems. However not critical for PR.

I'm curious why you plan to use G1 and M6054 style g-code commands? I understand the desire for an open text-based format, but I'd have thought more verbose commands something like MOVE_AND_DISPLAY_PICTURE HEIGHT=2.300 FILE="2.png" would be possible.

mSLA requires lifts before exposure next layer. Here a normal sequence for print layer 1:

;LAYER_START:0
;PositionZ:0.05mm
SET_PRINT_STATS_INFO CURRENT_LAYER=1
BEFORE_LAYER_CHANGE LAYER=1 HEIGHT=0.05 PIXELS=189274 BOUNDS=81.75,46.15,25.2,28.85 AREA=473.185 VOLUME=23.659 MATERIALML=0.0237
M73 P0 R69.88;Set print progress
M1451 F"1.png";Show image
M204 S5;Set acceleration
G1 Z4.05 F600;Z Lift (1)
M204 S100;Set acceleration
G1 Z6.05 F600;Z Lift (2)
G1 Z1.55 F600;Retract (1)
M204 S5;Set acceleration
G1 Z0.05 F600;Retract (2)
G4 P3000;Wait before cure
M1400 S255 P30000;Exposure for 30s
G4 P3000;Wait after cure
SET_PRINT_STATS_INFO CONSUME_MATERIAL=0.0237
AFTER_LAYER_CHANGE
;LAYER_END

You can notice the 4xG1 to perform the lift and retract to detach model from FEP and refresh the resin under it. However this is all setup by user preference, they can opt for just 1 simple lift rather than my optimization with two lifts. There are also situations where we don't want to lift, eg: Cure supports at one exposure time, cure model at other exposure time. Between this two images changes we don't want to lift because they are same layer. Another point is multiple exposure tests and models. There are also users using mSLA printers to fabricate PCBs with high precision.

Regarding that I think MOVE_AND_DISPLAY_PICTURE is very limited and an extra command that slicers will skip. For that be usable it would need to accept optional HEIGHT1/2 and SPEED1/2, ACCEL1/2, WAIT, and whatever comes next to implement.

I think we should stay with standalone commands for best compatibility and customizable.

Here the parameters users can adjust per layer:

image

Is there readily available hardware that this code can run on today? That is, can many users buy off-the-shelf hardware and use this support on it, or would users be required to build a printer from scratch.

Any FDM board and a Pi will do it. I have written step by step guide for who want to convert their printer to this firmware: https://github.com/sn4k3/klipper-msla

In my case I have used a Manta M5P + CM4 to make my UNIZ IBEE klipper based.

What software will be used to produce the gcode/png/zip files that are proposed here? Is that software readily available today.

No slicer so far is able to output this zip format with klipper flavor. The current slicers can output zip but with CWS gcode norm. However my software (UVtools) can use any slicer and convert to klipper format. It's a workflow some users already use, They use PrusaSlicer to slice .sl1 and UVtools to convert to their target printer/format.

image

Here a sample sliced file, PrusaSlicer (SL1) -> UVtools -> Klipper zip

I understand from your description that mSLA firmware is often proprietary. Are there any other popular open source mSLA firmwares at all? Or, is this goal for this to be the basis for the first popular open source mSLA firmware. (Excuse my ignorance here - I have no experience at all with resin based printers.)

There are no popular open-source mSLA firmwares. This is the problem :) Well we have the Prusa SL1 but is not popular along mSLA, also it's a downgrade comparing to FDM none control over printing, it does all internally. So you have no chance to make your custom moves. There are "open-spec" formats, gcode formats, but none is truly open-source. Most consumer printers uses CTB ecosystem. Other share is held by Anycubic then Creality and now Elegoo. None listen to community nor have good firmware, most still not able to control layers individual, lots of problems which I'm tired to patch with my software. There's also the nanoDLP which use klipper but it's a close-source propertary product and have it problems. You can read more about this bad experiences on my repo (One just came today, one didn't even follow own spec) and also the "war" with formats: https://github.com/sn4k3/UVtools/discussions/319 and https://github.com/sn4k3/UVtools/discussions/442

Working with all these formats over years it's an experience I don't wish for anyone. I wonder why is so hard to write a good firmware?

This PR aims to free users from all crappy firmwares monopoly and revindicate the machines under their control. And most important, having the first all-in and good open-source mSLA firmware that allow you to tune and add features to your printer like we do with FDM.