Neopixels as progress bar
-
Hi there,
I am curious if it would be possible to show the progress of a running print with Neopixel LEDs. For instance: you have 100 LEDs and the print is at 30% which would make 30 LEDs light up... .
Something like this: Klipper NeoPixel Templates
Would it be possible to implement this in the slicer of choice, simply by some conditional Gcode which would update the LEDs every 10 layers or similar?
Or is it possible to read the current status of the print and update the LEDs every so often with a macro?If I look at the object model documentation, maybe job.layer (Number of the current layer or null not available) and job.file.numLayers (Number of total layers or 0 if unknown) could be a simple and reliable way to calculate the print progress?
Thanks in advance for possible input.
-
It's certainly possible, and fairly straight forward to do with macros and daemon.g. This code is straight from my own printer. It incrementally lights each LED in green based on file position.
in config.g:
if !exists(global.runDaemon) global runDaemon = true ; enable/disable on power-on-reset.
in daemon.g:
while global.runDaemon if state.status == "processing" M98 P"statusled.g" G4 S1
in statusled.g:
var strip_number = 0 var num_leds = 12 var brightness = 255 var chunk_size = job.file.size / var.num_leds var led = 0 var chunk = var.chunk_size while var.led < var.num_leds var f = var.led < (var.num_leds - 1) ? 1 : 0 if job.filePosition >= var.chunk M150 E{var.strip_number} P{var.brightness} F{var.f} R U255 B0 W0 S1 else M150 E{var.strip_number} P{var.brightness} F{var.f} R0 U0 B0 W0 S1 set var.chunk = var.chunk + var.chunk_size set var.led = var.led + 1
It's not perfect; for example for a small model, the first 20% of the file might be comments from the slicer noting the slicer settings, in which case, the LED's will show the print as 20% complete on starting; but perhaps that can be fixed through g-code post-processing.
-
@semi55 said in Neopixels as progress bar:
Hi there,
I am curious if it would be possible to show the progress of a running print with Neopixel LEDs. For instance: you have 100 LEDs and the print is at 30% which would make 30 LEDs light up... .
My solution uses dsf-python to extract the object model and manipulate LED strings in response to what is in the model. I actually used one row of 40x SK9822 for a progress bar and a row of 12x APA102 for status lights (which are both equivalent to 'dotstar' rather 'neopixel' - but they are just Adafruit labels). If you specifically want to use neopixels I wouldn't do it from a Pi - it can be done, but the timing can be tricky.
My delta has two progress bars growing to meet in the middle while it's heating (showing bed and hotend temperature), but once it is at temperature one progress bar from left to right shows file progress:
-
@robotsneversleep nice, that seems promising. I just have to understand the code tbh. Programming is still quite abstract for me sometimes but that is super helpful to start with.
I will try to implement that once my printer is build (probably end of June this year). -
@achrn that looks really cool! Great implementation.
I am aiming to get this running without a SBC, so that sounds promising.
For the LEDs, I am using standard ws2812B ones.
Would be so kind to share your code for controlling your LEDs? -
@semi55 said in Neopixels as progress bar:
@achrn that looks really cool! Great implementation.
I am aiming to get this running without a SBC, so that sounds promising.
For the LEDs, I am using standard ws2812B ones.
Would be so kind to share your code for controlling your LEDs?Mine does require a SBC, and WS2812 (and similar) are what Adafruit call neopixels and you don't want to try driving them from a SBC - you want something with a rock solid time base on the interfacing.
Even if my solution was appropriate, this is not the code you are looking for, though I'm working on a less embarrassing implementation that I probably will share in due course.
-
@achrn thank you nonetheless for you input
-
@semi55 I'm in the process right now. This was a 3 day project I started over 6 months ago, so beware.
The first issue is what board you have. Some of them have support natively for NeoPixels and similar displays, but some don't have robust enough hardware to handle it, so the CPU has to do the timing work. But given a choice of the CPU working on bling vs. moving and extruding, it makes sense the latter takes precedence. So those boards (Duet 2, I think) have sending NeoPixel using M150 deferred while moves take place. Good for your print. Bad for your bling. But if you have one with actual NeoPixel-type support, that can work. But you may need a CMOS logic buffer to pull the voltage up to the 5V the NeoPixels need. And unless you have only a half dozen LEDs, you'll need a sufficiently capable 5V power supply to light them. I'd consider that almost a definite need.
I have the Duet 2 WiFi, which means I'm not one of the lucky ones. So I got an AdaFruit NeoDriver I2C NeoPixel "Driver". It's a card that your Duet communicates with via I2C protocol (use the M260 GCode), and it handles the heavy processing to manage the NeoPixel signal timing. Amazingly, AdaFruit sells it direct for $7.50. I've found it a bit clunky in that it doesn't like things like too little time between setting the color values and executing the "Show" command, so I've had to put G4 commands in between. Also I think the data is ins something like RBG order instead of RGB, or something like that.
The I2C communication is slow, so I've taken to tracking the colors of the LEDs locally, to avoid sending the data redundantly. There also can be rounding issues with converting the data you're displaying into how many LEDs to display, but it can be worked out.
My conclusion is that the web interface uses filament usage to figure percentage done; so something like this:
100 * (job.rawExtrusion)/(∑job.file.filament[extruder]〗)
These values are available from the object model. You'd replace "extruder" with a number. I'd be happy to do what I can to help. Most of what I know about this was from extensive experimentation. -
@semi55 I've been working on bed leveling display this evening. Cyan on the upper right track probe points. Lower left is a complicated overlay of a target error maximum of 0.005 and an actual error of 0.041. Tens digit on top, ones on bottom. Green for target, blue for actual. Unfortunately my phone doesn't pick up the difference between green and blue-green very well, so the bottom 5 LEDs are actually one blue-green and 4 green. Not immediately clear but you get used to reading it and it provides lots of information.
-
@DonStauffer I will be using a Duet 6XD main board with NeoPixels natively supported, and they will get 5V from an external power supply.
It sounds like a lot to consider... . Hopefully this will go somewhat smoothly with my hardware when everything else is ready as this will only be a nice to have feature on the wishlist (I am building the printer at work, so depending on other projects, there will be limited time to experiment with this).@robotsneversleep I've taken a closer look at your code and have some questions I hope you could answer.
If I understand correctly, the daemon.g runs in the background. Do I have to set the state.status in the slicer Gcode or will it automatically run every now and then when I start a print because the firmware automatically sets the state.status?
How often will the macro be executed? Could that interrupt the print?Thanks in advance!
-
@robotsneversleep Regarding your problem with small prints and not showing the correct percentage:
Maybe using the actual layer number instead of the file progress would fix this. I can't test this right now but maybe something like this (swapping job.file.size with job.file.numLayers and job.filePosition with job.layer) :var strip_number = 0 var num_leds = 12 var brightness = 255 var chunk_size = job.file.numLayers / var.num_leds var led = 0 var chunk = var.chunk_size while var.led < var.num_leds var f = var.led < (var.num_leds - 1) ? 1 : 0 if job.layer >= var.chunk M150 E{var.strip_number} P{var.brightness} F{var.f} R U255 B0 W0 S1 else M150 E{var.strip_number} P{var.brightness} F{var.f} R0 U0 B0 W0 S1 set var.chunk = var.chunk + var.chunk_size set var.led = var.led + 1
-
@semi55 IIRC using job.file.numLayers doesn't work because the layer height is determined based on the first layer only, which is problematic if the first layer is different from the rest of your model, or you're using adaptive layer height.
To answer your other questions... daemon.g is typically checked for every 10s by the firmware for existence and if it exists, it's executed.
I decided an update of the LED's every 10s is not fast enough, so put a while loop in it which is executed every 1s. The dwell (G4 S1) is there to hand control back to the firmware. You do have to be careful not to let the macro take too long to execute or it will affect printing.state.status is updated automatically by the firmware.
-
@robotsneversleep thanks for clarification, that makes sense.
Sounds quite impressive that updating the LEDs every second does not interfere with the print (from a noobs perspective).What about the following object models for calculating the true print progress:
job.file.height
job.layers[].heightThat sounds like it should use the actual Z-coordinate when printing and compare it to the objects height.
-
@semi55 I had things working reasonably well with the M150 command on my Duet 2 WiFi. It just didn't work while the motors were running, so I got the NeoDriver and used M260. But you still might need a CMOS buffer to pull up the voltage.
My macros would not be too hard to retrofit for M150 commands. All the M260 commands are run through a small group of macros. The biggest difference is M150 (native support) starts at the beginning of the strip, where M260 (NeoDriver I2C), and hence my macros, can start at an arbitrary LED number. That could require resending data just to get to the starting LED number. You also might not need my system of deferred writes, since you're not using I2C, but it would still work.
This was the culmination of my work with M150:
https://forum.duet3d.com/topic/35190/m150-with-neopixels?_=1716145163555
-
@semi55 Using height might not be an intuitive way to generate a percentage, because sometimes higher layers take much less time. That's why I intend to use filament usage.
-
@DonStauffer that also makes sense.
Thanks for your input, I will keep that in mind when starting to implement this for my future machine.