FastSPI_LED2, The Introducing
The FastSPI_LED library has been growing a little long in the tooth. It is monolithic, fairly tied to the arduino platform, has a number of design decisions that reflect its scattershot growth and direction, and more. In spite of that, it has been downloaded over five thousand times (damn, what are you all doing with it? I’d love to see the projects!). However, as bug reports, feature requests (eg playing nicely with other SPI devices), new led chipsets, new host chipsets (teensy 3.0, chipkit, msp430, etc..) come in it has become increasingly clear was a cleaning house. So I stepped back and thought about the evolution of the code and the library over the past two years and where I wanted it to go, what what features were important going forward. Then I sat down and sketched out a rough structure for the library going forward, one that focused on easy extensibility, easy portability, and of course, high performance squeezed out of small platforms.
After that, and a few weekends/weeks of grabbing blocks of time here and there to work out code and ideas, I now have code! Code that appears to work on arduino, teensy 2.0 and teensy 3.0! Code that appears to be speedy! Code that makes things glow! Since I have code that means that you too, shall shortly have code. First, some details about FastSPI_LED2 as people will be able to use it out of the gate:
- Initial (tested) platform support: Arduino (uno/nano), Teensy 2.0, and Teensy 3.0 (yes, ARM!)
- Initial (tested) chipset support: ws2801, lpd8806, ucs1903, tm1809/1804, tm1803, ws2811
- Silent switching between hardware SPI (6.6+ Mbps) usage and high performance software SPI (2.7Mbps on 16Mhz arduino!) based on selected pins
- Software based SPI increases ability to play nicely with other SPI libraries/data
- Ability to run multiple sets of led strips in parallel on different pins
- Small when compiled, depending on chipsets/options, as little as a few hundred bytes (compared with over 12kb in the old library)
- Support for using ARGB data structures (up to you to do your own A based blending, however!). Not yet supported on all chipsets.
In addition, there’s some other pieces of the library and code that I want to call out, and will go into more detail about in subsequent posts:
- High performance pin access library exposed for use (write only, for now)
- High performance SPI library available for direct use (write only, for now)
- Rapid ability to add support for new LED chipsets going forward
- (Hopefully) Rapid ability to port the codebase to new platforms
Of course, now I imagine there are two questions being asked. The first being where one can obtain the code, the second being how to use the code. This being a preview release, the interface to the library is still a little bit on the raw side, and there will need to be some minor changes to your setup and show code to start using the new library. So, grab the code from the googlecode site, and settle in for some notes on porting code over to the new library. (This information, as it gets fleshed out, will find its way into in-library documentation files).
Porting Code
The old code involved calling a number of functions to set up the various parameters for the LED library, and then you would get a pointer to the block of memory that the library used for writing from. This brings us to the first major set of changes to the library. This version of the library focuses on the LED controllers, and -not- on managing your LED data itself. So, the first major change is that responsibility for setting up the array of RGB data structures moves from the FastSPI_LED library to you, and when it comes time to call show on the controller, you give the show method a pointer to where your rgb data is. The second major change is you have to declare your LED controller object – one object per set of leds you want to control. So, for example, here’s some sample code for making all of your leds blink red:
// Simple rgb data structure
#include "FastSPI_LED2.h"
#define NUM_LEDS 32
struct CRGB { byte r; byte g; byte b; };
struct CRGB ledData[NUM_LEDS];
// Setup/define the Led controller with data pin 11, clock pin 13, and latch pin 10
// this will trigger use of the hardware SPI support on the arduino uno
LPD8806Controller<11, 13, 10> LedController;
setup() {
// zero out all the leds
memset(ledData, 0, sizeof(struct CRGB) * NUM_LEDS);
// initialize the controller
LedController.init();
}
loop() {
// set all the LEDs to red
for(int i = 0; i < NUM_LEDS; i++) { ledData[i].r = 255; }
LedController.showRGB((byte*)ledData, NUM_LEDS);
delay(100);
// zero out all the leds
memset(ledData, 0, sizeof(struct CRGB) * NUM_LEDS);
LedController.showRGB((byte*)ledData, NUM_LEDS);
delay(100);
}
Pretty straight forward, no? Apologies for the need to cast to a byte* – it is something that should get fixed up fairly quickly. Also I need to add controller definitions and options for various predefined pin setups (e.g. make it easier to specify hardware SPI ports for a platform that has them). All of the LED Controller objects are instances of a base class called CLEDController, so if you want to pass your controller objects around between functions, you can just pass a reference/pointer to an object of type CLEDController, and the Right Things[tm] will happen. For now, take a peek in the FastSPI_LED2.h header file for the supported chipset classes, or look at the code sample in examples for a variety of (commented out) instantiations for various chipsets.
What’s next?
As mentioned multiple times, this is a preview release of the library. I’m about to take off on some traveling and wanted to get this out there for people to start looking at. There may be bugs. Not all the platform combinations are complete, and there are more platforms I want to fully support. The good news is that any arduino based environment not explicitly mentioned above should work out of the box, albeit a little slower, thanks to various rounds of graceful fallback in the Pin and FastSPI portions of the library. However, as with any preview type release, I already have a handful of known issues and specific things that I want to fix before a full release or immediately after the initial release, including:
- Software SPI optimizations assume no other pin writes will occur while output led data. There is a fallback, slightly slower, but definitely safer, when there are other writes occurring. I simply need a way to expose this to the user
- Cleaner high level interface to the controllers, including the ability to manage multiple controllers
- Documentation and sample/example code
- Wider arduino platform support
- Add ability to specify select pins to the non-SPI based chipsets (my test harness will make use of this to use a mux to switch between chipsets under test)
- Add the ability to specify a -set- of pins to be triggered as a select for a controller (again, for use with abovementioned mux)
- More performance tuning/code cleanup
- Getting rid of the need to explicitly cast to byte* when calling show
- Support for specifying the RGB ordering of bytes on a controller by controller basis
- Support for ‘background output’ of LED data (teensy 3.0 has a DMA subsystem!)
- Support for arduino-less AVR environments
- Support for a few more led chipsets!
- Some support library surprises
It’s been a fun 2 and a half years making and growing this library. I’m really happy to finally be dragging this library kicking and screaming into 2013, and I’m looking forward to the next few years of this library and the things you all do with it!
Credits/inspirations/motivators
Also, a shout out to blog posts that contributed to various directions my brain went into and/or information used in refining the code in this library:
- Alan’s post on WS2811 timings
- Michael Noland’s post on template abuse and avr
- AdaFruit and SparkFun for equipment and hardware
- PJRC for the teensy and support and info in spinning up the teensy 3.0 support (thanks, Paul!)

The LPD 8806 RGB LED strip has only clock and data lines. No provision for a latch pin. How are you connecting this?
This is one of those things that I need to clear up in both code and documentation. With SPI, the latch pin is how you indicate which SPI device should listen to what’s coming down the SPI line, this way you can have multiple devices share the lines. With the chipsets that use SPI as their data protocol, they don’t ever, directly, care about such things, so it is effectively ignored. However, it is possible to have some extra hardware in place that basically gates the clock/data line to the LED strip based on the state of the latch pin, and since there are some users of the library who are interested in doing things like that, I’ve added support here for that to be done.
A near future (and before final release, hopefully) rev of the library will extend the concept of supporting latch pins to -all- supported chipsets, as well as provide a way to specify “don’t use a latch pin”, as well as a way to specify a set of pins to use for latching (when working with a 1×16 MUX, which my own testing rig will eventually be using so I can easily test multiple strips off of one controller).
In other words, you don’t have to worry/care about it
Your library is amazing. I am able to update 240 LEDs in 2.38 miliseconds. I used micros() to calculate this. Is it safe to assume that I can update 512 LEDs in 6 milliseconds? It worked like a charm
. Thanks so much. I am aiming 60 Hz refresh rate and I am planning on using serial port. I think this is achievable within a 16ms time frame (to achieve 60 fps frame rate). What is the license for this library? Can I use it to port it to my AVR studio based project?
There’s still some work that I need to do to the library to make it usable in AVR studio without the rest of the arduino packaging. Luckily, I think it’s a fairly small amount of work at the moment, I just need to get an AVR studio setup going.
I just checked your site. It is MIT license. Thanks
I think you’re confusing/conflating the latch and chip select pins. Chip Select is the pin that controls whether or not a chip pays attention to the data it’s receiving. Latch is the pin that tells a chip “display the data you’ve been sent”.
Many cheaper chips don’t have latches, they are latched by either pausing the data stream or by sending specific data.
Ack – you’re right – i should be referring to those as select, not latch pins. I’ll change up the code before I do the next release.
first, thank you for releasing this preview version!
ive ported my code to this version but i still have some flickering with ws2811.
maybe its not a problem of your library but with boblight
my setup has 198leds with a total of 594 channels. maybe this is to much to handle for boblight.
I have a similar question. I tested the code with 240 LEDs and I tried to simulate 512 LEDs. It worked fine(The 240 LEDs lit up and it takes 5ms to push a data of 512 bytes). But when I increase it to 600, the 240 LEDs don’t light up. I am trying to understand what’s going on….
I think you are seeing a slightly different problem from Marco – what platform are you trying this on? If it is on an arduino with only 2k of ram, 600 LEDs is 1800 bytes of memory – combine that with other memory your application may be using and you’re running out of memory, which pretty much just results in nothing happening.
I would be willing to port it to AVR studio for you if you could give me a general idea of doing it (I am about to start a project. I totally forgot about the 2K RAM on the arduino. I was able to modify the code a bit to test if it’s possible to update 1024 LEDs and it takes only 4ms! I did some math and I arrived at a calculation that the LEDs are being updated at 250kHz. Is this correct?
I’m going to insist on doing the port locally, mostly because I need to make sure that it is done right and fits with the other platform ports I will be supporting – but I suspect compiler errors will show you fairly quickly where the problem is. Also at 1024 leds in 4ms, that’s 256 leds in 1ms, each led is 24 bits so that’s 6.144k/ms, or 6.144Mbps, which sounds about right for running hardware SPI at the fastest speed
Thanks again for sharing the library
I will also try porting the library.
You may be having a power problem. How much power are you applying to the lines?
power source is the 400w power supply of my htpc. its connected to both ends of the stripe.
i dont think thats a power problem. i do think its my arduino 2560.
getting a teensy 3.0 by the end of february.
i can post you shematics and scripts if you want to look over it.
Randomly – just because the power supply is 400 watts, those supplies will limit the amount of current on an individual 5v or 12v rail.
Are your lights 5v or 12v? What happens if you scale back the number of LEDs, is there a count of LEDs where you stop seeing the flickering?
You may also need to be injecting power at multiple points in the chain of leds, and I suspect that need for power injection is higher with 5v than 12v, but I haven’t played with it too much yet on that front.
Hit post too soon – anyway, I also have a 2560 here that I can rig up, possibly later tonight, and see what’s happening with timing and such coming off of there.
its late in germany.
ill post a video tomorrow so that you can see whats happening.
first ill try to use less leds
and then ill use more 5v connections of my power supply.
hello again. im glad to tell you all that my ambilight setup is now working perfectly with teensy 3.0. no changes to the code has been made.
so it never was my power connecions. it really was the arduino mega which seems to be to slow for my setup.
ill post a video today to contribute to your great library!
Huh – ok, I’ll double check the timings that are coming out of the mega. I wonder if I slipped them up somehow before getting the latest release out? Thank you for the updates, and love the video!
One suggestion – the color changes in the video are fairly sudden/harsh – I wonder if you could implement some sort of fading to go between color sets, to make the color changes a little less jarring?
yes i can change it to a more fading methode but the color change would be much slower.
im at work right now. ill post a new video with the slower Methode for you tomorrow.
here is a video.
bad quality due to use of my smartphone
Hi Macro,
I also have a ws2811 that i’m trying to get working with boblightd, I’m pretty new to this, but can follow directions in attempt to learn.
I have alittle over 5m’s worth of flexible strip ws2811′s (around 300LED’s) .
I have a Arduino Mega 2560, but it seems that it doesn’t work for you. Anychance you can share your code or hex file for the teensy 3.0?
Thanks
hi.
ill post my code later.
im going to bed now.
Thanks Marco,
Much Appreciated, I have a Teensy 3.0 coming my way now. Will be awesome to get my ws2811 strips working with XBMC/boblight.
Hi Marco,
I just received my Teensy 3.0 last night. I was curious when you have some time, if you could post the code. Or if someone can guide me in getting the ws2811 working with boblightd, it would be greatly appreciated.
I’m willing to offer up a case of beer towards anyone that can help me get it working with boblightd
Thanks!
ive tried to post my files here but the posts wont be displayed.
contact me at marco[dot]goezelt[at]live[dot]de
Just wanted to thank Marco for helping me get setup.
Here’s my setup:
Oh, that is fantastic! Also – for some reason your link doesn’t appear to be showing up – here’s the
Just wanted to thank Marco for getting me setup. I’m using the preview library with a teensy 3.0.
[youtube http://www.youtube.com/watch?v=kNKtZOVmPnA?rel=0&w=1280&h=720%5D
Awesome work!
I’ll be testing/playing with this as soon as I can! I’m extremely psyched about finally getting my high framerate pov sword working!
Thank you again, and keep up the awesome work – also – enjoy your vacation!
-Frollard
Double Post –
Got it up and running with my WS2801 32 leds 5V on a Teensy 3.0.
With 1ms delay (double what is required for the strip) I can get the test pattern running at 800fps 32 leds 24bpp = 691kbps. The strip simply won’t accept spi datarate below ’9′. I was shocked. Does the library do the 500us delay after outputting a frame to latch the ws2801?
The old code I could run spi at full speed (speed = 1, old library) on the teensy3. Still, with the datarate at 9, I get no missed data and beautiful updates. I get wicked flicker/partial updates if I use anything faster than 9.
The Chip Select/SS doesn’t work as I implemented it with the old library. I think it’s inverted since I use an NPN transistor to cut off the clock line to the strip, when the SS goes HIGH it enables the clock output. I suspect this implementation is active LOW as proper SPI implementation should be. Tomorrow I attempt to code for this AND the SD card at the same time.
So the new data rates and old data rates are different. The new data rates are,roughly speaking, the number of clocks of delay involved in sending out a single bit.
I am not intentionally adding any extra delay at the end of a frame because I figured in most use cases, preparing the next frame of output would take long enough, why burn the CPU time?
Finally – it looks like I have the latch/release states backwards. Unfortunately I am out of the country right now and won’t be able to touch the code for another ten days – if you want to fix the ss stuff look in fastspi.h in the library and swap the hi/lo calls in the latch/release functions (which are poorly named and in the future will be changed to select/release).
Also – I found that the 5v ws2801s can’t take data as fast as I can sling it out. You want fast data rates over spi, check out the lpd8806′s – which I can push data to at about 6.6Mbps
GREAT SUCCESS!
I’ve abandoned attempting to share hardware spi between the sd card and the strip. Why? Because on the teensy, the soft-spi is DAMN fast – almost as fast as the hardware. A true testament to the skill of the coder combined with a truly remarkable platform.
at 11 ms frame delay, it takes 1272ms to output a 107 line bitmap.
that’s 1177ms of intentional delay, leaving 95ms remaining to actually output 107 lines of spi…
0.89ms per 32 leds @ 24bit
functionally…it’s incredible. LED Lightsaber (with bitmap POV animations) on instructables soon! 1100fps on ws2801 boo yeah! ALL the time in the world to calculate animations.
That’s fantastic! I can’t wait to see what you end up getting out of it!
I’m also hopefully going to be getting the hardware and the software output faster on the teensy 3.0 over the next few weeks. I also need to get support added in for the teensy 3.0′s dma system!
Does not seem to work with my setup: WS2811 LEDs on a Leonardo. LEDs are connected to pin 3. With FastSPI_LED this was working. Any ideas?
OK, works, D3 on my board seems to be PIN 5.
Ah – ok, the Leonardo might have different pin-port mappings. I’ll dig into that and look it up when I get back into the country. Thanks for the info!
Is it possible to stop the output, when serial data arrives? When outputting data cli() is called and then sometimes serial data is lost. I tried to describe the problem here: https://code.google.com/p/fastspi/issues/detail?id=5#c4 but never found a solution. So maybe a solution would be something like a stop() function, which immediately stops showRGB (exit the while-loops) and calls sei();?
Not with the WS2811/TM1809 style chipsets, it isn’t. The timing for those is fairly exact, which is why I disable interrupts (it takes 5 clocks just to -start- handling an interrupt, before any interrupt handler code gets called) and I don’t have a whole lot of spare cycles (20 total per bit) to even add a regular check for incoming data.
A better solution would be for your code to somehow indicate to whatever is sending serial data whether or not it is ready for incoming data (e.g. raising a line high, or sending a READY message of some kind up the serial channel) and/or switch to one of the clock/data line based chipsets (ws2801/lpd8806) which don’t need to disable interrupts to write data out the line.
Thanky ou for your answer. I’m not really experienced in programming with Arduino or/and AVRs. What happens with USB connection on a FT232 chip, when interrupts on the Atmega are disabled? Will the FT232 buffer in this time? Or are the packages lost?
iirc the serial data comes in zero buffer – the arduino doesn’t signal ready to receive nor does it acknowledge – if it misses it, it misses it.
With the ws2801/lpd8806, how much time can an ISR safely take (ie: injecting extra delay between bits)? If it’s too long, the chips might latch the data, right? I know that 500us is supposed to be long enough to guarantee that the ws2801 WILL latch the data, but what the longest a bit clock can be delayed to guarantee that it WILL NOT latch?
I think it depends on the specific chipsets. I think the ws2801 latches after 50µs, give or take. The lpd8806 has a specific sequence that gets sent out to reset the chips into listening, so I think the timing will be less of an issue there.
Generally speaking, though, you really want ISRs to be as fast as possible. Ideally, set a flag, and then handle the interrupt on your next pass through the loop function.
How to use the new library with WS2801 chipsets?
I found out:
WS2801Controller LedController;
But my other stripes just have CLOCK and DATA, so what is the select pin and spi speed?
Sorry, comment form kicket out some text,
WS2801Controller<DATAPIN, CLOCKPIN, SELECTPIN, SPISPEED> LedController;
OK, i got it working with the example above. Just changed:
#define NUM_LEDS 21
struct CRGB { byte b; byte g; byte r; };
WS2801Controller<11, 13, 0, 9> LedController;
After some seconds the output stops. Then all LEDs keeps off or on. Any ideas?
Old library seems to work better with the WS2801 stripes.
There were some bugs that i’ve found with the WS2801 strips that I have fixed but haven’t pushed new updates for yet. As for the select pin and speed values – the way those are set are going to change in the next preview release for the library (this is why I’m publishing preview releases for now – to shake out some API decisions and bugs) – and I will make sure there is documentation for them when they come out.
You need to make sure that the select pin is 10, for now, though if you’re going to keep using the ws2801 strips and hardware SPI -it’s a quirk of how the arduino does its SPI and pin setting, I suspect what’s happening is noise on pin 10 is causing the arduino to believe that data is coming -in- to the SPI system and then it blocks waiting for said data, which never comes.
Thank you, with selectpin 10 and speed 9 it seems to work. But i’m looking forward to the new version.