Complete programming noob here. I have a strip of 600 LEDs arranged into roughly a circle shape. I have a pulse of variable brightness LEDs that travel around this Loop. I’m trying to figure out how to get them to transition across the joint from the end of the loop to the beginning of the loop in a seamless fashion. I have been at it for a while but now I’m hoping the real smart people can step in!
Somehow I think I need to test if each pixel is at the end of the string and if so then do some math to translate any pixels beyond the end of the string to the correct HSV pixel at the beginning of the string.
I have a more complicated ultimate goal in mind but this is the first step to get working so that I can start building on it. Any advice would be greatly appreciated!
My current code looks like this:
export function beforeRender(delta) {
t1 = time(4 / 65.536) // From 0..1 every 4 seconds
}
export function render(index) {
halfWidth = 200 // pixels
loopedIndex = index >= pixelCount - halfWidth ? index - pixelCount : index
// If the index is greater than pixel count-halfWidth, we assume it has looped around
// and adjust it to a negative value so that the calculation below works correctly.
// Otherwise, we use the index as is.
pulsePosition = t1 * pixelCount - loopedIndex // In units of pixels
// By subtracting loopedIndex from the pulse position, we make the pulse transition smoothly
// from the last LED to the first LED.
distanceFromPulse = abs(pulsePosition) // Still in pixels
proximityToPulse = halfWidth - distanceFromPulse
pctCloseToPulse = proximityToPulse / halfWidth // Now from 1 to 0
v = clamp(pctCloseToPulse, 0, 1)
hsv(0.666, 1, v)
}
This is an interesting problem that almost everybody eventually runs into. You’ve got the right idea – at the wrap point, the distance between head and tail of your pulse drastically changes, and you have to do a little arithmetic to compensate.
Here’s one way to think about it, using all 0 to 1 ratios instead of pixel indices.
export function beforeRender(delta) {
t1 = time(.2)
}
pulseLength = 0.25; // percentage of ring to light
export function render(index) {
dist = t1 - index/pixelCount;
// when we wrap, the distance suddenly gets large. We check for this
// and adjust the distance to account for the wrap
dist = (abs(dist) > 1 - pulseLength) ? dist + 1 : dist;
// control the head to tail brightness scaling
v = (dist >= 0 && dist < pulseLength) ? 1 - dist / pulseLength : 0;
// cube brightness to make the curve steeper
hsv(0.6667,1, v * v * v)
}
Thanks! That works great for the transition. What I’m seeing now is the pattern stutters as it goes around the loop. It is not really traversing the loop smoothly. Is it possible there are too many math operations in the render function? It’s running about 37 frames per second so I’m not sure why that would make it stutter. Maybe it’s getting caught between pixel counts and running the same frame twice every so often?
Is it jumpy on actual LEDs, or just in the preview window?
My preview window is jumpy, but I’m running it on 800 SK9822s at 2Mhz, and the LEDs are very smooth, doing about 70fps (which is about the same frame generation rate as yours considering the communication speed difference between the SKs and WS2815s).
This is a pretty minimal pattern. There’s very little math, just a few add/subtract/multiply/divides. It’s well within what a v3 can handle.
@wizard, is this something we can do anything about – an internal buffer being overwritten, or small differences in the time the PB takes between frames causing t1 to jump? Or did I just do something silly in the code that I’m not seeing?
It’s running around the actual LED strip jumpy by what looks like one extra LED at a time or so. It sort of seems like a steady cycle of a jump rather than a random. I’m wondering if I run it using exact LED count rather than scaling 0 to 1 if that might fix it but I haven’t worked on that yet.
I played around with it some, but it hasn’t helped with the jumpy progress. I can’t figure it out. Still the same stuttering of the leading edge as it goes around the loop. Here’s the current code:
export function beforeRender(delta) {
t1 = time(.1)
}
pulseLength = 200; // length of ring to light
export function render(index) {
dist = t1*600 - index;
// when we wrap, the distance suddenly gets large. We check for this
// and adjust the distance to account for the wrap
dist = (abs(dist) > 600 - pulseLength) ? dist + 600 : dist;
// control the head to tail brightness scaling
v = (dist > 0 && dist < pulseLength) ? (pulseLength - dist)/pulseLength : 0;
// cube brightness to make the curve steeper
hsv(0.6667,1, v * v * v)
}
I haven’t tried zranger1’s code (and his code is ALWAYS great!), but wanted to throw in something I use a lot. Instead of a pulse length, it returns a dark-to-light value you can use for brightness.
var halfwidthDefault = 0.125. // Results in 25% of the circle being on and fading in.
/*
Assuming a & b are given as angles, where a=0 corresponds to 0 radians,
a=0.25 corresponds to Pi/2 radians, and a=1 wraps back to 0 radians.
Returns 1 when a & b are proximate as angles on a circle, 0 when they
are more than `halfwidth/2` apart
*/
function wrappedNear(a, b, halfwidth) {
if (halfwidth == 0) halfwidth = halfwidthDefault
return clamp(1 - triangle(a - b) / halfwidth / 2, 0, 1)
}
// Example use: Rotate a circle, lighting 25% of the strip
export function render(index) {
pulseCenter = time(4 / 65.535) // 4 seconds per revolution
frac = index / pixelCount // fractional percentage of this pixel in the strip
v = wrappedNear(pulseCenter, frac)
hsv(0,0,v)
@jeff, I love this approach – hadn’t even occurred to me to treat it as angles. And this’d make a nice, smooth color gradient too!
@SeaLaVie, sorry for the slow response. I’m just swamped, going full blast on two big projects. Things should be better in a day or two. I’ll set up a bunch of ws2812s and see if I can figure out why this would be jumpy.
Just tested setups with 600 and 1200 sk6812 RGBW LEDs, on the theory that maybe communication speed had something to do with this. It doesn’t seem to. With 600, both my code and Jeff’s are glassy smooth at a bit over 30fps.
With 1200 LEDs, it’s running at 15fps, so movement is rougher, but progress around the strips stays steady. Nothing you could really call stuttering.
So I don’t think it’s anything to do with pattern code. Here are are a couple more things to check:
A lot of websocket traffic might slow a Pixelblaze down, though that’s way harder to do on a V3. Are you using firestorm, or running the Web UI on multiple browsers or anything else that would increase websocket load?
I’m sure you’ve already tried this, but check your data and ground setup. I don’t have any WS2815s around, but my understanding of the wiring is this:
the data line from the Pixelblaze goes to DI on the LEDs
the backup data line (BI) on the LEDs should be grounded (only) at the first LED. (If you’re connecting multiple strips, their BI lines must be connected for the backup data thing to work.)
all grounds should be connected. The LED’s ground line should be connected to both the power supply ground and to the Pixelblaze’s ground.