Plasma 2040
Bought another Plasma 2040 and a RGB LED Star Wire.
I already have some code to turn the LEDs on and off (and control their colours). But it was a quick hack to make some Christmas lights, and I want to have a bit more control.
To do that, I’m going to use cubic Bézier curves. The idea is to calculate the curve and then take the y values as the R, G and B values.
But I have 66 LEDs, each has 3 colours, and I want to update them several times a second. 20 times a second means a lot of calculations (I’m too lazy to calculate how many, but roughly 30,000 multiplications a second), so I may need to be a bit clever about it.
But premature optimisation is the root of all evil, so start simple.
The equations:
Bx(t) = (1-t)^3*p1x + 3*t*(1-t)^2*p2x + 3*t^2*(1-t)*p3x + t^3*p4x
By(t) = (1-t)^3*p1y + 3*t*(1-t)^2*p2y + 3*t^2*(1-t)*p3y + t^3*p4y
There’s a lot of repetition here, and some potential for using integers instead of floating points.
RGB or HSV
RGB is how the LEDs work. Each LED is actually three small LEDs, a red one, a green one and a blue one. So that’s the only way we can control the colours. But HSV is an interesting colour space, because it allows us to do cool things, for example to go through all the colours in a very easy way (all H values for a fixed S and V). H is Hue, S is Saturation (how much of that colour, from grey to vivid) and V and Value (lightness, how dark the colour is). It’s more intuitive.
I have some code to translate from HSV to RGB. Also, LEDs are not linear - value 60 is not half as bright as value 120, which is not half as bright as value 240. So there is some Gamma correction for which I also have some code.
Bézier Curves
First I want to choose some Bézier curves. But even before that, I want to come up with some ideas for effects, and then see if I can model them using Bézier curves. I may get tired of the acute accent and just write Bezier.
### All Colours
To go through all the colours, I just play with H. The function is basically linear. I want to go from 0.0 to 1.0. I don’t want a sine wave because there is no reason to alter the speed of change between colours. So just linear.
It’s possible to model a straight line with a cubic Bezier curve, so that becomes quite simple. Since H=0.0 is the same as H=1.0, we can just use a single curve to cycle through all the colours.
We also want some “phase” so that not all the LEDs show the same colour at the same time. The idea is to use a function that takes an LED number and returns an array of Bezier curves.
Phase and “Wave Length”
I’m implementing the Bezier curve using t not between 0.0 and 1.0, but between 0 and 255. However, forcing it to change once every 10ms for example is very inflexible. I may want the change from 0 to 255 to take 2 seconds or 200 seconds. So there needs to be a way to map a “tick” to a t value.
The phase can just be a 0-255 offset.
### Calculation
There’s point calculating Bx(t) if all I’m ever using is By(t). In other words, I just care about:
b = (255-t)*(255-t)*(255-t)*p[0] + 3*(255-t)*(255-t)*t*p[1] + 3*(255-t)*t*t*[2] + t*t*t*p[3]
There’s a lot of redundancy here, and two dimensions.
In the t dimension, it’s possible to use a lookup table for things like 3*(255-t)*(255-t)*t
instead of calculating it each time.
In the p dimension, it’s possible to see how to move from b to b’ (that is, from t to t+1).
For p[0]:
(255-(t+1))*(255-(t+1))*(255-(t+1)) - (255-t)*(255-t)*(255-t) =
(255-t - 1)*(255-t - 1)*(255-t - 1) - (255-t)*(255-t)*(255-t) =
now (a-b)^3 = a^3-3a^2b+3=ab^3-b^3
, so:
(255-t)^3-3(255-t)^2+3(255-t)-1 - (255-t)^3 = -3(255-t)^2+3(255-t)-1
That can save some calculations, but it not straight-forward to exploit. If for every t the amount we need to add to the previous result depends on t, then we may as well use a lookup table.
Dimensions
We want to have 3 Bezier curves - one for hue, one for saturation and one for value. Granted, the saturation is not going to change much (between grey and “colourful” where half-way is “pastel”).
### Stretch
Updating t every 10ms is OK - the cycle then takes 2.5 seconds. But Christmas lights shouldn’t be annoying. To make it stretch, we need a function from tick to t. It’s pretty linear, given slope:
t(tick) = tick * slope
We can use a float to start off with. If it’s way too slow, it’s possible to use Besenham’s line algorithm, but starting simple is better.
Conclusion
Stopping abruptly here. I’ve programmed here, used a lookup table for the calculation, and tweaked the parameters until it looked OK. The lights are on a Christmas tree now.