A hardware based animation library,pixel driver & dynamic led mapping system for addressable LEDs
A complete solution for pixel mapping, animation and driving for addressable LEDs
The real-time programmatic animation system used in all commerical products made by Elec Dash Tron is Private IP. I want to make the entire system accessible to the public without having to distribute the source code publicly. I want to also restrict usage to specific hardware modules
The system consists of 2 physical, ESP8266 based, modules. The Driver Module (LABELED D) contains the pixel mapping, animation system and LED Driver. The Control module (LABELED C) is user programmable and runs your script of animations. The Control module communicates via the ESP-NOW protocol for realtime execution of commands.
The driver uses the ESP8266 SPI port to drive both Clocked and Unclocked Chipsets:
The Driver module will come with 2 FEMALE JST cables for you to use depending on the type of pixel you wish to use. Both connect to the same 4 Pin MALE connector
You will need a USB to Micro USB cable to conect both modules for programming via the Arduino IDE. The Wemos D1 Mini boards included in this kit use the CH341 USB to Serial chip. Drivers are located HERE for Most platforms
The Driver module comes with a USB cable with a power switch, you will need to conect this to a power sources with enough current to drive your LED count. A powerbank with a 2A output is a great start. DO NOT CONECT YOUR LEDS TO THE DRIVER MODULE UNLESS YOU ARE DELIVERING POWER VIA THIS USB CABLE or you risk blowing up the 500mA power regulator on the driver board
If you are powering your pixel array with a seperate power supply, and have LOTS of pixels, you can remove/not use the BLACK USB Cable from the driver module. Your pixel array will backfeed the driver module via the JST plug. EG...
A template spreadsheet is provided here This doc will focus on a simple matrix, but I'm adding more complex examples into the file as we go
The template spreadsheet concatenates each Column cell into a single C++ array representing that ROW. It then concatenates each ROW into a C++ array. Once done, COPY the text in the cell (marked in the above image as "Your Pixel Map array") this text is the full string you will need in the next section for the pixelMap array
The uploader, located the Uploader.ino contains all the config variables you need to fill out and is responsible for writing this to the Driver modules SPIFFS. Open the file in the Arduino IDE and fill out the following:
The Uploader code will look something like this for the example we will be working with:
//Number of Physical Pixels in your system
const short int numLeds = 255;
//bytesPerLed lets the system know if you are working with WS2812/SK6812 (3 Bytes) or APA102/SK9822 (4 Bytes) pixels
const short int bytesPerLed = 4;
//rows = is the total number of Rows(Y Axis) in your LED set up, this is usually the number of pixels TALL+2(1 NUll line at the top and bottom)
//cols = is the total number of Columns(X Axis) in your LED set up, this is usually the number of pixels WIDE+2(1 NUll line at the LEFT and RIGHT)
const short int rows = 17, cols = 19;
//SPI Frequency for clocked Pixels
const short int maxSPIFrequency = 10;
//MAC Address of Controller modules "WIFI_STA"
const short int controllerMACAddress[6] = {0x00,0x00,0x00,0x00,0x00,0x00};
//This is the array you create in the template spreadsheet above
const short int maskMap[rows][cols] = {{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},
{-1,240,239,210,209,180,179,150,149,120,119,90,89,60,59,30,29,0,-1},
{-1,241,238,211,208,181,178,151,148,121,118,91,88,61,58,31,28,1,-1},
{-1,242,237,212,207,182,177,152,147,122,117,92,87,62,57,32,27,2,-1},
{-1,243,236,213,206,183,176,153,146,123,116,93,86,63,56,33,26,3,-1},
{-1,244,235,214,205,184,175,154,145,124,115,94,85,64,55,34,25,4,-1},
{-1,245,234,215,204,185,174,155,144,125,114,95,84,65,54,35,24,5,-1},
{-1,246,233,216,203,186,173,156,143,126,113,96,83,66,53,36,23,6,-1},
{-1,247,232,217,202,187,172,157,142,127,112,97,82,67,52,37,22,7,-1},
{-1,248,231,218,201,188,171,158,141,128,111,98,81,68,51,38,21,8,-1},
{-1,249,230,219,200,189,170,159,140,129,110,99,80,69,50,39,20,9,-1},
{-1,250,229,220,199,190,169,160,139,130,109,100,79,70,49,40,19,10,-1},
{-1,251,228,221,198,191,168,161,138,131,108,101,78,71,48,41,18,11,-1},
{-1,252,227,222,197,192,167,162,137,132,107,102,77,72,47,42,17,12,-1},
{-1,253,226,223,196,193,166,163,136,133,106,103,76,73,46,43,16,13,-1},
{-1,254,225,224,195,194,165,164,135,134,105,104,75,74,45,44,15,14,-1},
{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}};
Once you have filled out all the details, check that your IDE is set up for the correct module and settings. The most important things to set are:
Connect your DRIVER module via a USB cable, Open the serial monitor & upload your code. Once complete you will see a message like this on the console...
Open the "Espressif Flash Download Tools" and select "ESP8266 Download Tool" your settings need to be as follows:
Click START to upload the Binary, when complete, disconnect the Driver Module from your PC, connect it to the panel and power it on. You will see a test patern if you have done everything correctly. The test pattern scans a singe line in RGB in alternate directions to indicate a complete map. Connecting the Driver module to your USB Port and opening the Console will help troubleshoot any issues. This is an example of what you will see on the console if everything is set up correctly:
At this point your Driver is good to go. Unless you change your pixel map or other configuration settings, you won't need to touch it again.
Clears the entire frame
Renders the contents of the current frame to the LEDs
Subtracts fadeByValue from each RGB values of each pixel in the current frame
Same as subtractiveFade, but only effects the range sent to the function. The leftBound and RightBound limit your X range and the TopBound and BottomBound limit your Y range
Draws a pixel of pixelColour at location X,Y in the current frame
Draws a horizontal line of pixelColour at location X,Y with a width of "width" pixels in the current frame
Draws a vertical line of pixelColour at location X,Y with a height of height pixels in the current frame
Draws a line of pixelColour starting at location xStart,YStart ending with location xEnd,yEnd in the current frame
Draws a circle centred at x,y with a radius of radius pixels with a coloured line of pixelColour. Any pixels out of bounds of the Frame are not drawn
Draws a polygon with N_Points points, rotated by rotationAngle, centered at x,y with a radius of radius pixels with a coloured line of pixelColour. Any pixels out of bounds of the Frame are not drawn.
Fills the areay in range with pixelColour
Shifts the selected range from the current frame by 1 pixel DOWN. If wrap=0 pixels are not wrapped around. if wrap=1 the last line is wrapped around the top
Shifts the selected range from the current frame by 1 pixel UP. If wrap=0 pixels are not wrapped around. if wrap=1 the last line is wrapped around the bottom
Shifts the selected range from the current frame by 1 pixel LEFT. If wrap=0 pixels are not wrapped around. if wrap=1 the last line is wrapped around the RIGHT
Shifts the selected range from the current frame by 1 pixel RIGHT. If wrap=0 pixels are not wrapped around. if wrap=1 the last line is wrapped around the left
There are also some helper functions that are designed to help with things like timing, generating modulation, etc...
void startTimer(unsigned long timeInMilliseconds)
byte hasTimedOut()
int getWave(float intervalTickCounter, float minimumValue, float maximumValue)
Using the EnvelopeGenerator object:
envelopeGenerator envelopeOne;
const byte numberOfPoints = 4;
unsigned short int frameCounter=0;
unsigned short int points[numberOfPoints] = {0,10,20,5};
unsigned short int ticks[numberOfPoints] = {30, 30, 30, 30};
envelopeOne.initEnvelope(points, ticks, numberOfPoints);
while(true)
{
System.printf("\r\n%d", envelopeOne.getEnvelope(frameCounter));
frameCounter = (frameCounter+1)%envelopeOne.envelopeBandwidth;
}
The above generates this curve:
The following is an example template of a simple animation loop.
You need to make sure the following 2 vars are set properly:
Once you have that set up, upload the Control Module Code to your Controller module using the same UPLOAD CONFIG details as picture:
void loop()
{
//Start a timer for 500 seconds
animationSystem.startTimer(500000);
//Start your animation
rainbowSwipe(2);
}
void rainbowSwipe(byte colourIncrement)
{
unsigned short int scanCnt = 0, cIndex=0;
//Clear the animation FRAME
animationSystem.clearBitmap();
while(true)
{
//Check if the animation has timed out
if(animationSystem.hasTimedOut()){return;}
//Place a colour into "tempColour" using index "cIndex" from the dynColObject palete
dynColObject.getColour(cIndex%dynColObject._bandWidth, tempColour);
//Draw a Vertical line of "animationSystem.rows" height
// at location ("scanCnt%animationSystem.cols", 0) in your frame,
// with colour "tempColour"
animationSystem.drawVLine(scanCnt%animationSystem.cols, 0, animationSystem.rows, tempColour);
//Render your FRAME to the LEDs
animationSystem.renderLEDs();
//Increment the colour indexer "cIndex" by "colourIncrement"
cIndex += colourIncrement;
//Indrecment the X modifier fr the next loop by 1
scanCnt++;
//Fade your entire frame by a value of 5
animationSystem.subtractiveFade(5);
//Delay for 50ms
delay(50);
}
}