Category Archives: ws2811

The Orbitron

The main part of the Orb is complete. I have gently sealed in all the glass prisms and stretched Cotton/Lycra over the top part of the sphere:

wpid-20150826_215818-1.jpg wpid-20150826_215828-1.jpg

I was able to crunch the code down to the Nano module. I’m left with just over 400 Bytes free. Here are 2 short demo patterns:

 

I still need to cut out a section of the sphere to store the Arduino Nano and then cut out a mounting hole for a staff.

Production Update – LED Orb

I Still don’t have a good name for this piece, however it is coming along nicely. I was able to get all the inside LEDs into the sphere and wire everything up:

wpid-20150826_001018-1.jpgI will shortly begin adding the outer LEDs. I ended up scrapping the center strip as it made the entire bitmap too large for the Arduino Nano to drive.

wpid-20150826_001007.jpgHere is a nice preview of the orb with the glass jewels placed on top, but not yet secured

Full LED Mask

20150616_211145Im using a new type of render process, via software instead of Hardware. It means that the LED Strips can be stuck any which way on any surface an then mapped using 2 arrays. I can then render using the addresses int he array. I use a spreadsheet to mock up the LEDs REAL wold addresses and then map them in any way I want. Here is an example of the rotation pattern map:

rotatemapping

Here are some examples of this mask in action:

20150616_22113720150616_22111220150616_221103

 

LOL

 

Driving an LED Strip with SPI

Driving Chart for the WS28XX series ICs

WS2811 ws2811 0
0.5μs H
2.0μs L1
1.2μs H
1.3μs LReset Time
Above 50μs
 WS2812B  ws2811 0
0.40μs H
0.85μs L1
0.85μs H
0.40μs LReset Time
Above 50μs

Because I would like to use the Galileo, I’m limited in not being able to use the FastLED library, as its intended for AVR.  One approach is to use SPI at a very high frequency, and send whole Byte sequences to imitate a tight timing sequence for the above:

For the WS2811 1 bit is transmitted in 2.5μs. So if We set up an SPI driver to push out the 8 bit number 255 , 11111111 in binary, at 400khz

ws2911spiex1

Now as a whole pulse that’s useless to us. Because we need to pulse a HIGH and LOW in a specific pattern. To do this, we can mimic the signal by increasing the SPI frequency and sending in BYTES with specific patterns. For example If we double the frequency to 800Khz we can now send in 2 bytes in the same time frame. So if we send in a 255 and a 0:

ws2911spiex2

We get the above. Which is almost to a point where we can start making up masks. In the above example each BYTE, that’s 8 bits, takes 1.25μs to transmit, and looks like a continuous HIGH signal for that amount of time. This means that each BIT takes 1.25μs/8 = 0.15625μs to transmit…

ws2911spiex3

To get a correctly timed pulse for the WS2811 we will need to adjust a few things. 1st we need to find out a nice round number for each Bit to sent out at.  We need to be able to add multiple bits transmition time together to get exactly 1.2μs and 1.3μs. As well as 0.5μs and 2μs. For example…

Time Required for 1 Bit to Transmit = 0.1μs

Time required for 1Byte = 0.8μs

At 1,250,000Hz a single Byte takes 0.8μs to transmit and a Single bit takes 0.1μs to transmit. So…

ws2911spiex4

Above we transmitted 4Bytes over SPI [255,240,0,0] to get a Logic 1

ws2911spiex5And here we transmitted 4Bytes over SPI [248,0,0,0] to get Logic 0

What this means is that we have to transmit 4 Bytes for each BIT in a standard Byte of representation Data. If I have to control 1 LED i need 3 bytes to control Red, Green and Blue. That means I need:

3bytes x 8bits x 4spi-block-bytes = 96Bytes per LED!!!! With my current suit layout that has 288 RGB LEDs, I need 864 Bytes of control data. With the Above protocol, ill need 27,648 Bytes per FRAME….Now what I’m getting at here is that Using SPI to control these drivers is probably not a great idea. It will take 22.1184 milliseconds per frame which is at best around 5 frames per second.

My other concern is that the Galileo probably wont allow such tight control over the SPI frequency. I will be able to set it at 1mhz and 2mhz but not anywhere in between with any kind of certainty. I spent some time on my oscilloscope last night to see if the SPI frequency actually changes in between MHz but saw no difference unless the change was significant .

Here is the same diagrams but using the WS2812B timing, which is the same as the WS2811 but using the double data rate:

Set SPI to 2,500,000Hz Use 4SPI Bytes per LED BYTE:

To send  Logic 1 you send [255,255,128,0]

ws2812sendhighTo send Logic 0 you send [255,0,0,0]

ws2812sendlow

I can confirm that the 144LED strips uses fast mode… This was sent ot me directly from WorldSemi

“The 144LED Strip uses the WS2812B chipset(built-in IC), and HIGH speed mode”

Their Web page states they use the WS2811 chip but they also say that they use High Speed  mode in prepackaged systems… Using High Speed mode I can get maybe 10 frames per second with the same number of LEDs.

Here is some NodeJS code I wrote that will grab an array of Bytes and create a Buffer() of bytes with the appropriate bit pattern to be pushed out to the SPI bus.

var ledBytes = 1*3;
var ledBar = new Array(ledBytes);
var aBuffer = new Buffer(0);
var counter = 0;

init();
runMain();

function init()
{
 //clear the led array
 clearSuit();
}

function runMain()
{
 var testBuffer = WS2811b_SPI();
 
 for(counter=0; counter<testBuffer.length; counter++)
 {
 console.log("Index "+counter+"->"+testBuffer[counter]);
 }
 
}

function WS2811b_SPI()
{
 var spiBuffer = new Buffer(ledBytes*8*4);
 var currentByteSPIBlock = new Buffer(8*4);
 var spiBufferIndex = 0;
 
 for(counter=0; counter<ledBytes; counter++)
 {
 currentByteSPIBlock = createSPIBlockFromByte(ledBar[counter]);
 currentByteSPIBlock.copy(spiBuffer,spiBufferIndex,0,currentByteSPIBlock.length);
 spiBufferIndex+=currentByteSPIBlock.length;
 }
 return spiBuffer;
}

function createSPIBlockFromByte(dataToPush)
{
 var mask=1, bitResult=0, cnt=0, bufferIndex=0; 
 var tempBuffer = new Buffer(4*8);
 var spiBlock = new Buffer(4);
 
 mask = mask << 7; 
 for(cnt=0; cnt<8; cnt++) 
 { 
 bitResult = dataToPush & mask; 
 bitResult = bitResult >> (7-cnt); 
 spiBlock = createSPIBit(bitResult);
 spiBlock.copy(tempBuffer,bufferIndex,0,spiBlock.length);
 bufferIndex+=spiBlock.length;
 mask = mask >> 1; 
 } 
 return tempBuffer;
 
}

function createSPIBit(value)
{
 var spiBitBlock = new Buffer(4);
 
 if(value==1)
 {
 spiBitBlock[0] = 255;
 spiBitBlock[1] = 255;
 spiBitBlock[2] = 128;
 spiBitBlock[3] = 0;
 }
 else
 {
 spiBitBlock[0] = 255;
 spiBitBlock[1] = 0;
 spiBitBlock[2] = 0;
 spiBitBlock[3] = 0;
 }
 return spiBitBlock;
}

function clearSuit()
{
 for(counter=0; counter<ledBytes; counter++)
 {
 ledBar[counter] = 0;
 }
}