Implementation of color gradient animation effect of. Net , and raspberry pie , light band

Time:2022-4-28

In the last hydrology article, Lao Zhou demonstrated the basic use of ws28xx. At the end of the article, Lao Zhou said that this article introduces the simple implementation of color gradient animation.

Before we start, let’s make a digression.

The first thing is that the price of raspberry pie has soared recently. I believe all friends who pay attention to it know. So if you’re not in a hurry, don’t buy it first. Alternatively, you can choose raspberry PI 400. This configuration is a little higher than 4B. The current price is relatively normal. PI 400 is the raspberry pie hidden in the keyboard. In fact, the price on the official website has been adjusted back to the original price, but the Jian merchants on a treasure are still raising the price.

Second, can the application on raspberry pie be written in C? This is nonsense. The raspberry pie runs Linux, of course. Some partners will say, use Net experience? Lao Zhou can tell you: no problem at all. Lao Zhou has experimented with most APIs in this library Net IOT library performance you do not have to worry about, because in recent years Net performance has been greatly improved, not to mention Net just encapsulates the call of the underlying API. When the instruction is passed to the system driver layer, its efficiency is the same as that of C. You might as well think that even python, a programming language with poor performance and no natural enemies, can play with the Internet of things What else are you afraid of. Although there are few open source libraries, the official devices also basically cover various sensor modules.

————————————————————————————-

OK, that’s all for F. let’s start with the main film.

Let ws28xx control the light strip to produce animation, which is essentially to update the color of each light bead at regular intervals. Because the reaction speed and processing ability of human eyes are not comparable to that of cats, we will see animation. What we saw was animation, but Lao Zhou estimated that what the cats saw was ppt.

Therefore, the so-called color gradient animation, first of all, you need to determine two colors – the starting color and the final color. For example, from green to red, green is the beginning and red is the end.

Then, we need to calculate the difference between the starting color and the ending color and the values of R, G and B.

Suppose that our delay d = 40 ms (accurate to milliseconds is enough, regardless of microseconds and nanoseconds. Anyway, your eyes can’t see it), and then we have to change from red to blue.

Red: r = 255, g = 0, B = 0

Blue: r = 0, g = 0, B = 255

Calculate the gap and subtract the starting point from the ending point, regardless of positive or negative.

dif_R = 0-255 = -255

dif_G = 0-0 = 0

dif_B = 255-0 = 255

In this way, we can see that from red to blue, the value of R decreases, G remains unchanged, and the value of B increases. Let’s not think about whether the algorithm is right or not. We might as well continue to calculate:

The first cycle, r = 255-1 = 254, g = 0, B = 0 + 1 = 1, sleep 40;

The second cycle, r = 255-2 = 253, g = 0, B = 0 + 2 = 2, sleep 40;

The third cycle, r = 255-3 = 252, g = 0, B = 0 + 3 = 3, sleep 40

……

Until the target value becomes r = 0, g = 0, B = 255. Between each cycle, there will be a pause of 40 ms.

However, the algorithm can not be so simple. We have ignored a problem. Please see the following example:

Suppose you want to change from R = 120, g = 200, B = 10 to r = 255, g = 100, B = 60

Calculated difference: difr = 255-120 = 135, difg = 100-200 = – 100, difb = 60-10 = 50. The difference between rgbs is not equal. If we have + 1 or – 1 in each cycle, there will be a problem: some values may have reached the final value, while others have not reached the final value. In this case, the light gradient process will not look smooth.

Therefore, the problem we must solve is that after N cycles, the three RGB values should reach the final value at the same time. In this way, those with large difference gradient faster and those with small difference gradient slower. Those who run fast, wait a minute, those who run slowly, form a united front and reach the end at the same time.

Therefore, the number of cycles in the gradient process must be uniform, but the amount of RGB change is different in each cycle, but it will reach the final value at the same time after N cycles.

For example, from R1 = 100, G1 = 0, B1 = 230 to R2 = 20, G2 = 72, B2 = 57

Then, the difference:

  dR = 20-100=-80

  dG = 72-0=72

  dB = 57-230=-173

If the number of cycles is 80, it can be understood that it is completed in 80 steps, and set step = 80. Next, you have to calculate how much the RGB values change in each of these 80 steps (unit step).

  pR = dR / 80=-80/80 = -1

  pG = dG / 80 = 72 / 80 = 0.9

  pB = dB / 80 = -173 / 80 = -2.16

Set a cycle (a step) as I, then

for i = 0; i <= 80; i++

  R = R1 + i * -1;

  G = G1 + i * 0.9;

  B = B1 + i * -2.16;

R1, G1 and B1 refer to the values of the initial color. In one cycle, add the product of I and the unit step (PR, PG and Pb).

In this way, we can ensure that after N cycles, the three values can reach the final value at the same time.

——————————————————————————————

OK, with the above deduction process, we can translate it into code. I encapsulate it directly as a class.

public class GradLeds
    {
        Ws28xx _leds;
        public GradLeds(Ws28xx ws) => _leds = ws;

        public void Run(Color start, Color end, int steps = 120, int delay_ms = 30)
        {
            if (steps <= 0)
                Throw new exception ("steps cannot be less than / equal to 0");
            if (delay_ms <= 0)
                Throw new exception ("delay must be greater than 0");

            //Calculate the difference of RGB, whether positive or negative
            float dR = (float)end.R - start.R;
            float dG = (float)end.G - start.G;
            float dB = (float)end.B - start.B;
            //Calculate the value to be increased for each step
            float ir = dR / steps;
            float ig = dG / steps;
            float ib = dB / steps;

            //Get the number of light beads by width
            int ledNum = _leds.Image.Width;
            for (var a = 0; a <= steps; a++)
            {
                //If the loop state is false, it will exit
                if(AppContext.TryGetSwitch("running",out bool b) && !b)
                {
                    break;
                }
                Color tc = Color.FromArgb(
                        (int)(start.R + a * ir),
                        (int)(start.G + a * ig),
                        (int)(start.B + a * ib)
                );
                //Fill all lamp beads
                for (var n = 0; n < ledNum; n++)
                {
                    _leds.Image.SetPixel(n, 0, tc);
                }
                _leds.Update();
                //Delay
                Thread.Sleep(delay_ms);
            }
        }
    }

In this class, I use appcontext. If you read the blog written by Lao Zhou thousands of years ago, you should remember this appcontext class, which can be used to set some global switches. The switch name is a string and the value is a Boolean value. Using this class directly, we don’t need to deliberately write a class and get a static field to be a global variable. Moreover, static members can’t share values across AppDomains. If multiple threads are used, synchronization must be considered.

In appcontext, Lao Zhou will set a switch called running. If it is true, it indicates that the program is running; If it is false, it means that the program is about to exit, and there will be no gradient.

Because this gradient process will last for a few seconds or even longer, if the program wants to exit, don’t recycle, but quickly terminate the operation.

Start and end represent the start color and end color, steps represent the number of steps (cycles), and delay_ The MS parameter indicates the delay between each cycle.

Return to the main program and call the test.

using System.Device.Spi;
using Iot.Device.Ws28xx;
using Grdtest;
using System.Drawing;

//Initialize SPI bus
SpiConnectionSettings settings = new(0)
{
    Mode = SpiMode.Mode0,
    DataBitLength = 8,
    ClockFrequency = 2400_000
};
using SpiDevice device = SpiDevice.Create(settings);

//Ws28xx, 30 light beads
Ws28xx ws = new Ws2812b(device, 30);
GradLeds grdled = new(ws);

int steps = 90; // 90 Cycles
int delay = 25; // Delay (MS)
//Set running status
AppContext.SetSwitch("running", true);

//Press Ctrl + C to exit the program. Deal with it
Console.CancelKeyPress += async (_, e) =>
{
    e.Cancel = true;    // Block the program and exit immediately
    //Turn off the switch to indicate that the program is no longer running
    AppContext.SetSwitch("running", false);
    await Task. Delay(150);  // Be safe. Wait a minute
    e.Cancel = false;   // Tell the system that you can exit
};

//Main cycle
while (AppContext.TryGetSwitch("running", out bool b) && b)
{
    //From red to blue
    grdled.Run(Color.Red, Color.Blue, steps, delay);
    //From blue to yellow
    grdled.Run(Color.Blue, Color.Yellow, steps, delay);
    //From yellow to dark pink
    grdled.Run(Color.Yellow, Color.DeepPink, steps, delay);
    //From dark pink to white
    grdled.Run(Color.DeepPink, Color.White, steps, delay);
    //From white to red
    grdled.Run(Color.White, Color.Red, steps, delay);
}

//Black light finish
ws.Image.Clear(Color.Black);
ws.Update();

The last two sentences are to turn off all the light beads after exiting the while cycle (black means the light is off).


ws.Image.Clear(Color.Black);
ws.Update();

Well, let’s see the effect. The effect should be acceptable.

For other animation algorithms, we might as well try them ourselves. The algorithm does not have to be copied from the Internet, but can be designed according to your own understanding. You can make your own ideas and play as you like.