I am the bearer of some amazing news. We have a simulator for my 12 x 12 ping pong ball array that will allow anyone to create and test programs to run on the little beauty!
On the off chance you have just arrived at this party, let’s start by reminding ourselves that I’ve created a 12 x 12 array of ping pong balls, each having a WS2812 tricolor LED inside. I use NeoPixels from Adafruit, so I tend to think of WS2812s and NeoPixels as being synonymous, although this really isn’t the case because lots of other people make WS2812-based products. Be this as it may, as you can see from the videos on my Cool Beans YouTube Channel, the result is pretty spectacular.
I currently have plans to use this bodacious beauty to display all sorts of things. For example, I’m planning on implementing Conway’s Game of Life and — in addition to the full-speed evolution mode — I was thinking about including the ability to single-step through half-generations. That is, you would start with Generation (n), then half-step to see a visualization of the intermediate state where the system is calculating which cells will live and which will die, using different colors for different cases, then half-step again to see Generation (n+1).
I’m using a Seeeduino XIAO microcontroller to power my array, and I program this little scamp via the Arduino’s integrated development environment (IDE).
Now, as you may recall from my previous column on this topic, my chum Ted Fried suggested that I hold a competition. Ted’s idea in a nutshell was that people could write Arduino sketches (programs) to run on the array and email these sketches to me. I then run these little rascals on the array and take videos of them in action. I post all of these videos to YouTube and everyone votes to pick the winners — that is, the coolest and/or most interesting and/or most spectacular effects.
There was just one little niggling problem with Ted’s idea, which is that it’s well-nigh impossible to get a program like this to do what you want it to do without having access to a 12 x 12 ping pong array to run it on, which sort of defeats the purpose of the exercise.
This is where Ted came up with a cunning plan. You know when you were a kid and you would draw little stick figures in the corners of the pages of a book, where each figure was slightly different to the one before and the one after. When you subsequently flipped through the pages, it looked like the stick figures were animated (see this Flipbook video as a reminder). Well, Ted’s idea was to create a pseudo simulator by coercing the Arduino IDE’s Serial Monitor window to act like a sort of flipbook.
The easiest way to wrap your brain around all of this is to watch this video to see it in action, after which we’ll consider how it’s achieved in more detail.
As a starting point, I created a simple worm program. The worm has four segments — the head, which is colored magenta, and three body segments, which are colored blue. The program cycles around randomly picking a direction (North, South, East, or West) and a distance to travel in that direction.
With regard to picking a direction, the only rules are that the worm cannot turn back on itself (e.g., if it was going North, it can’t decide to go South because that would be like it was eating itself) and it cannot pass outside the edges of the array. In the case of picking a distance, I decided to base this on the worm’s location and direction — whichever direction it’s pointing in, we pick a random distance between one pixel (ping pong ball) and one pixel past the boundary in that direction. If the worm runs headfirst into one of the edges, its head turns white and it pauses for a short time, after which it continues on its travels.
When it came to implementing what we will call the NeoPixel Simulator, our original plan was to create a bunch of special functions that users would have to employ in their programs, but the more we played with this, the messier and more complex it became. It was at this point that I came up with a cunning plan of my own.
I remembered what the clever folks at Alorium Technology have done with their FPGA-based microcontroller emulator boards. They now have a bunch of these boards, including the Evo M51, the Hinj, the Snō, and the SnōMākr, but the one that started things off — and the one I think of most fondly — is the XLR8 (“accelerate,” get it?).
The XLR8 has the same physical footprint as an Arduino Uno, and its FPGA is programmed to act like a cycle-accurate version of the Uno’s 16 MHz 8-bit AVR processor with its 32 KB of Flash and 2 KB of SRAM. The thing is that, because the XLR8 is based on an FPGA, you can also use the FPGA’s programmable fabric to implement functions like pulse-width modulation (PWM) and suchlike.
Suppose you wanted to use a regular Arduino Uno to drive one or more servos. In this case, you would start by including the Arduino’s Servo Library at the beginning of your program as follows:
After this, you could use any of the servo-related commands provided by this library in your program. The problem is that Arduino Uno-based servo control solutions can suffer from servo jitter and twitching due to the 8-bit micro not being able to maintain precise timing required on the PWM signal that controls the servo.
This is the clever bit. If you are using an XLR8, you can continue to use the regular Servo library if you wish. Alternatively, you can use Alorium’s Servo Control Library by changing the include statement as follows:
#include < XLR8Servo.h>
All of the other commands in your program stay as-is. You can see the difference between these two libraries in action in this video.
Well, you can tickle my toes with a mallet if this isn’t just what we need for our simulator, so I called my chum Jason Pecor at Alorium and explained what I was trying to do. In turn, Jason explained the position to Bryan Craker, who is a master of the mystic software arts, and — before you could say “Max, could I possibly tempt you with a hot bacon sandwich and a cold beer?” — Bryan and Jason had created and posted my NeoPixel Simulator on Github.
Here’s the way it works. If you look in the Arduino folder on your computer, among other things you will see two sub-folders: Arduino/libraries and Arduino/sketches.
What you do is click the “Code” button on the Github page to download the “NeoPixel_Simulator-master.zip” file to some temporary folder on your system. When you unzip this compressed file, you will see a folder called “NeoPixel_Simulator-master”. The “-master” portion of this moniker is part of the Github system and is used to designate a master branch of the code. All you have to do is rename this folder from “NeoPixel_Simulator-master” to “NeoPixel_Simulator” and then copy this folder and its contents under your Arduino libraries folder; that is, you should end up with Arduino/libraries/NeoPixel_Simulator
Note that if you were already running the Arduino IDE while you were doing all of this, you will have to exit the IDE and then restart it before it will see your new library.
OK, suppose I were to create a program to run on my real 12 x 12 ping pong ball array. In this case, my program would contain the following statements:
#include <Adafruit_NeoPixel.h> #define NUM_NEOS 145 const int PinNeos = 10; Adafruit_NeoPixel Neos(NUM_NEOS, PinNeos, NEO_GRB + NEO_KHZ800);
Pay particular attention to the last statement. This instantiates my string of 145 NeoPixels, which I call “Neos”, and assigns (connects) them to pin 10, which is the pin I’m using to drive my array.
Remember that, as discussed in this column, the reason I have 145 NeoPixels, which are numbered from 0 to 144, is that pixel 0 is a “sacrificial” pixel that is used as a voltage level converter. The pixels forming the actual array are numbered from 1 to 144.
Now, the pixels in my array are connected in a serpentine fashion, but you don’t have to worry about this. All you have to do is visualize the array as being an X-Y matrix that is X columns wide (numbered from 0 to 11) and Y rows tall (numbered from 0 to 11).
In the framework program I will tell you about in a moment, there’s a function called GetNeoNum(). When you call this function passing in X and Y values, it will return the number of the corresponding NeoPixel in the array. So, for example, if we have declared an integer called iNeo and we want to know the number of the pixel at X = 4, Y = 7, we could use the following statement:
iNeo = GetNeoNum(4,7);
I think that this is probably a good time for you to take a look at the program I created to run on the real array — the one we saw in the video above. I called this Worm Demo Real Array. If you look inside this program, you will observe that it uses a number of NeoPixel-related commands as follows:
Neos.begin(); // Initializes the array Neos.show(); // Uploads new values to the physical array Neos.getPixelColor(i); // Returns the 32-bit color value 'c' from pixel 'i' Neos.setPixelColor(i, c); // Sets pixel 'i' to a 32-bit color value color 'c'
Remember that, the way this works, when you “get” and “set” pixel colors, you are performing these actions on a copy of the array stored in your microcontroller’s memory. It’s only when you use the show() function that the contents of this array are uploaded to the real array in the outside world.
Now, suppose that we want to use the simulator. Theoretically, all we have to do is to swap the Adafruit_NeoPixel library for our NeoPixel_Simulator library. In practice, we do this in a slightly cleverer fashion as follows:
// Decide which library to use #define SIMULATE_NEOS #ifdef SIMULATE_NEOS #include <NeoPixel_Simulator.h> #define Adafruit_NeoPixel NeoPixel_Simulator #else #include <Adafruit_NeoPixel.h> #endif
The reason we do things this way is that it gives me the ability to quickly and easily swap back and forth between the simulator and the real array. From my point of view, when you send me a sketch, I can immediately run it using the simulator. When I’m ready to run it on the real array, all I have to do is comment out the definition of SIMULATE_NEOS as follows:
// #define SIMULATE_NEOS
Pretty clever, huh? To see this in action, you can download the simulator version of our worm program, which I called Worm Demo Simulator. Once you’ve compared the two versions of our program, you can compile and run the simulator version on whatever processor is connected to your Arduino IDE. When you open the Serial Monitor window (remember to set its baud rate to 115200), you will see the simulated worm in action. You will find that playing with the vertical height of the window will allow you to obtain the best results.
We are almost finished. One thing about the simulator is that, in addition to white and black, it can only represent the 12 colors on our color wheel as described in The Color of Virtual Drips. That is, when working with the simulator, your programs can’t use any clever fading effects from one color to another. The more you think about this, the more it makes sense.
As a starting point, we’ll all use the same Framework Program. As you will see, in addition to the various definitions and the instantiation of the simulator, this program includes the GetNeoNum() function we talked about earlier.
So, off you go. Run wild and free. When you have a sketch working to your satisfaction on the simulator, email it to me at email@example.com and I’ll run it on the real array (it might be useful if you were to include a note in your email explaining what the program is and what it does). I can’t wait to see what wonders you come up with. And, as always, I appreciate any comments, questions, and suggestions.