LEDs max-0032-featured-image.jpg

Controlling WS2812 Tri-color LEDs (also known as NeoPixels), part 7

Good grief! I simply can’t believe we’re already in Part VII of what was originally conceived as a trio or a trio or a trio of essays. In my previous column we wrote some simple Arduino drawing(s) to run experiments with an Adafruit 8-Element Tri-Color NeoPixel Sticks.

Experiment #1 involved lighting all the pixels on the stick simultaneously. We start by lighting them all red, wait a second, then light them all green, wait a second, light them all blue, wait a second, and do these actions over and over.

In Experiment #2, instead of lighting all the pixels at once, we decided to light them one by one without Previous pixel off and with a delay of 100 milliseconds (ms) between each pixel. Again, we started by lighting them red, then green, then blue, and then did it again as shown below.

Photo courtesy of Clive MaxfieldLEDs max-0032-fig-01.png

Lighting the NeoPixels one by one leaving the previous pixel turned on.

Just for laughs and smiles, we closed the previous column with a thought experiment. We decided that we then wanted to do is light up the pixels one by one as in Experiment #2. But this time, when a new pixel lights up, we also want to turn off the old one. Just to mix things up a bit, we decided to use the color purple for this experiment as shown below.

Photo courtesy of Clive MaxfieldLEDs max-0032-fig-02.png

NeoPixels light up one by one while the previous pixel is turned off.

Your “duty” was, as it were, to think of a task and ruminate about how we would turn off the old pixel. There are multiple ways to do this. I can think of four “over my head.” So, how often did you come up with it?

Before we get bogged down in the battle with enthusiasm and abandon (and self-confidence, of course), it might be worth noting that I personally find it easiest to think of this sort of thing by visualizing our eight pixels as arranged in a circle as shown below.

Photo courtesy of Clive Maxfieldbulbs max-0032-fig-03.png

Picture eight pixels as arranged in a circle.

So how are we going to turn off the previous pixel when we turn on the new pixel? Well, let’s start by doing it the wrong way, which will help us wrap our minds around the underlying problem. From our past experiences, we know that NUM_NEOS is a constant set with a value of 8; Also, that we have set COLOR_MAGENTA And the Black color As 24-bit hexadecimal values ​​(8 bits per red, green, and blue channels). Now consider the following code snippet:

Photo courtesy of Clive MaxfieldLEDs max-0032-fig-04.png

This is not the right way to do things.

As we can see, we are using a file for() loop to cycle through pixels from 0 to 7, where the variable iNeo Used to indicate the current pixel of interest. For each pixel position, we rotate the new indicated pixel iNeo is on, and we turn on the old referenced pixel (iNew-1) turning off.

It’s always end conditions (also known as “corner states”) that get us into trouble. In this case, the problem occurs when iNeo Refers to pixel 0, because that means that (iNew-1) equals -1, which is not a valid value because our pixels are numbered from 0 to 7. What would happen if we run this program? I’m afraid to find out (if you run it yourself, please let me know what happens).

Because the problem only occurs when iNeo points to pixel 0, the obvious solution is to add a test to handle this case as follows:

Photo courtesy of Clive Maxfieldbulbs max-0032-fig-05.png

This will work, but it’s not pretty.

As we can see, we added a test to see if it (i new == 0). If this is true, we convert the pixel to (NUM_NEOS – 1), which is 8-1 = 7, off; Otherwise, go back to using (iNew-1) for the rest of the pixels. Note that the reason we use (NUM_NEOS – 1) Rather than just turning off the pixel number 7, this is a protection for our code in the future in case we ever decide to use a stick (or loop) with more (or less) pixels.

Now, this is a perfectly serviceable solution, but it must be admitted that it lacks some elegance. Personally, I don’t like having to treat one item differently than the others. It would be nice to be able to provide at least a hint of a whiff of taste. (You might think I should have shown this test colored green because it works, but I leave it colored red because I don’t like doing it.)

Fortunately, there are a number of ways we can do this. The first requires that we understand the way computers store and manipulate numbers. Also, it only works when the number of pixels we are playing with is 2, which is the case in this case, if you see what I mean, because we currently have 8 pixels and 8 = 2^3.

Earlier, we noticed that when iNeo Refers to pixel 0, which means that (iNew-1) equals -1, which is not a valid value because our pixels are numbered from 0 to 7. The thing is that the computer is already using the value of every 1s to represent -1.

We don’t want to get into signed binary numbers here, but another way to think about this is that int (Integer) Like iNeo The variable is represented using a fixed number of bits (16 in the case of the Arduino Uno). Let’s say that variable already contains all ones, in which case adding an extra 1 will override it, and end up containing all 0s. Conversely, if the variable already contains all zeros, then subtracting 1 will lower the flow, It ends up containing all Ones.

What we are going to do is use the bitwise AND operator (“&”) to logically and the resulting value of (iNew-1) Operation with 0x7 (hexadecimal 7), which is 111 in binary (the compiler will automatically insert leading 0s as required). (If you are new to programming, you may want to check out Masking and C/C++ Bitwise Transactions column, which provides a good, playful introduction to this topic.) The easiest way to visualize what we’re doing here is by using a table as shown below:

Photo courtesy of Clive MaxfieldLEDs max-0032-fig-06.png

Use the unary (“&”) operator as a mask.

In the case of pixels from 1 to 7 (001 to 111 in binary), subtracting 1 leaves 0 to 6 (000 to 110 in binary). Since all of these values ​​fit into the three least significant bits, putting them together with 0x7 (111 in binary) has no effect and leaves it “as is”. However, in the case of pixel 0, subtracting 1 leaves us 11111111111111111 (remember we are assuming Arduino Uno with 16-bit integers). Having this value with 0000000000000111 (remember the compiler adds the leading zeros) leaves us with 111, which is 7 in decimal, which is what we want (I feel like “Tra-la!” in order). This allows us to rewrite our code as shown below:

Photo courtesy of Clive MaxfieldLEDs max-0032-fig-07.png

Use the unary (“&”) operator as a mask.

As we can see, we no longer need to do a test. Alternatively, we can use a single statement to turn off any of the old pixels.

The main problem with this approach – as we noted earlier – is that it only works when the number of pixels is a power of 2. If we decide to use one Adafruit’s 12-NeoPixel EpisodesFor example, this masking technique will not work (sad face).

Fortunately, there is another method we can use (happy face). Go back to look at the image that shows the pixels arranged in a ring and visualize this being drawn with chalk on the floor. Now let’s say you’re standing next to a pixel – let’s say pixel 4. There are two ways for you to get to pixel 3. One is to walk one pixel counterclockwise (we can think of this as -1); The other is to walk around the 7px ring clockwise (we can think of this as (NUM_NEOS – 1)).

This is where we switch to another C/C++ operator called modulo (‘%’). In C and C++, if x And the y are integers, and remainder of integer division (x/y) are disposed of. By comparison, modulo factor (x% y) Returns the remainder of the division. This leads us to the following table:

Photo courtesy of Clive Maxfieldbulbs max-0032-fig-08.png

Using the modulo operator (‘%’).

In this case, let’s start with the “what we have” column shown in the middle in bold. If we want to specify the value of the previous pixel, then what we want is (pixel – 1), in which case the appropriate calculation is shown in the left column. Alternatively, if we want to specify the value of the next pixel, then what we want is (pixel + 1), in which case the appropriate calculation is displayed in the right column.

Let’s put this into practice as shown in the code snippet shown below:

Photo courtesy of Clive Maxfieldbulbs max-0032-fig-09.png

Using the modulo operator (‘%’).

Again, we no longer need to take a test. And again, we can use a single statement to turn off any of the old pixels (in this case we use the “Pixel – 1” computation from our table to turn off the old pixel). Last but not least, let’s think of another way to do things that get rid of for() loop as follows:

Photo courtesy of Clive MaxfieldLEDs max-0032-fig-10.png

Another way is to use the modulo operator (‘%’).

Personally, I find this approach makes things a little easier to wrap one’s brain around. What we do is declare the pointer to our pixels, iNeoas a global variable we initialize to contain 0.

Once again, we use the “Pixel-1” account from our table to turn off the old pixel. Also, at the end of the loop, we use the “Pixel + 1” computation of our table to point iNeo at the next pixel in the chain.

Phew! I know there’s a lot to think about in this column if you’re new to programming, but it’s not really bad. The important thing to note is that the techniques we’ve discussed here (such as the use of masks and a modular trigger) are applicable to a wide range of tasks beyond blinking LEDs.

I think we’re going to stop here for now to let all of this sink in. And as always, I welcome your engaging comments, insightful questions, and smart suggestions on anything we’ve discussed in this column.

#Controlling #WS2812 #Tricolor #LEDs #NeoPixels #part

Leave a Comment

Your email address will not be published. Required fields are marked *