NeoPixels Revealed: How to (not need to) generate precisely timed signals

There is an easier way to drive NeoPixels using code that…

  1. is simple to understand
  2. easy to change without breaking
  3. allows indefinitely long pixel strings
  4. addresses the root cause of signal reshaping glitches
  5. needs only a trivial amount of memory regardless of string length

Here is a demo of a 1,000+ pixel string being driven by a vintage Arduino DueMillinove at about 30 frames per second…

The program only uses about 5% of this Arduino’s 1K RAM. Since the amount of RAM used does not grow with the number of pixels driven, the limiting factor for this string length was the length of my apartment.

NeoPixels are not that hard

Much has been written about how picky NeoPixels are about timing. According to the excellent AdaFruit Uberguide, “the control signal has very strict timing requirements” and people have used many exotic and complex methods to meet these strict requirements- including cycle counting, PWM, SPI, and even UARTs with extra inverter hardware.

The standard way to drive NeoPixels with an Arduino is using the AdaFruit library which has some very fancy assembly code that was meticulously hand crafted to get every tick to land in precisely the right place. This time-tested library works great and if it suits your needs then you should by all means use it- but woe to he who would attempt to try to change even one line of it.

Unfortunately I could not use this library for my project anyway because it needs 3 bytes of RAM for each pixel (one byte for each R, G, and B value) in the string. It needs all that memory to get everything ready so that it can dump all the pixels as one giant, perfectly timed bit squirt. My display had 1,440 pixels, so no way this was going to work with on my humble  Arduino with 1K of RAM.

Luckily, it turns out that NeoPixels are not really that picky about timing once you get to know them.

Here is the canonical WS2812 timing diagram from the datasheet…   WS2812 Timing

Timing specs
Official WS2812 Timing specs

Let’s restate these as normalized values…

Datasheet timing constraints for driving a NeoPixel
Symbol Parameter Min Typical Max Units
T0H 0 code ,high voltage time 200 350 500 ns
T1H 1 code ,high voltage time 550 700 850 ns
T0L 0 code , low voltage time 650 800 950 ns
T1L 1 code ,low voltage time 450 600 750 ns
RES low voltage time 50,000 ns

This looks pretty constraining, but if we look closely we can see that things are not as bad as they seem. In fact, we’ll see that most of these apparent constraints are irrelevant.

If we instead think about how the different parts of the signal relate to each other, we can deduce that the important timing constraints for driving a single NeoPixel are…

  • There is a minimum width of a 0-bit pulse (T0H) to ensure it is detected
  • There is a maximum width of a 0-bit pulse (T0H) to ensure that it does not become a 1-bit
  • There is a minimum width of a 1-bit pulse (T1H) since it must be long enough to not be a 0-bit pulse.
  • There is a minimum time the level must stay low between consecutive bits (TxL), which ensures that the chip sees  separate bits rather than one long bit.
  • There is a maximum time the level can stay low (TxL) before a reset is triggered and any loaded data is latched and displayed on the LED.

That’s it! Let’s update our timing diagrams based on this new perspective…

Updated timing for a NeoPixel

Simplified timing constraints for a NeoPixel

Simplified timing constraints for driving a NeoPixel
Symbol Parameter Min Typical Max Units
T0H 0 code ,high voltage time 200 350 500 ns
T1H 1 code ,high voltage time 550 700 ns
TLD data, low voltage time 450 600 5,000 ns
TLL latch, low voltage time 6,000 ns

It is easy to comply with minimums because all we have to do is not be too fast. We can waste time in delays loops, or just do other things that take a while to make sure that we don’t arrive to the party too early.

Of the two maximums, the data low time (TLD) is very easy to comply with since 5,000ns is an eternity for a program.

So, we are left with just the single tight maximum of 500ns for a 0-bit pulse (T0H). This is 8 cycles on an 16MHz CPU. That is a pretty tight constraint, but we can do it especially since it is completely deterministic and we do not need to do any computation between the start and the end of the pulse. Heck, the AVR in an Arduino can toggle a bit from low to high and back again in 4 cycles if you are careful, so we have time.

The only tight timing parameter for NeoPixel signaling is the maximum width of a 0-bit pulse

Seriously – that is all you need to do to drive a NeoPixel!

You expect me to believe there is NO maximum width for a 1-bit (T1H)?

There is no bound for the maximum width of a 1-bit that a Neopixel will accept. I’ve tested 5-second long bits, but I am confident that you can send a full pixel with 2-week long 1-bits into a Neopixel and it will (eventually) display  it just fine. Test it yourself.

However, if you try to drive a string of Neopixels, group dynamics come into play and these jumbo sized 1-bits it will not work. To see why, check out…

NeoPixels Revealed: Going NSA on pixel-to-pixel conversations

The short answer is that the first NeoPixel will “reshape” the oversized 1-bit by shrinking it down to a normal-sized 1-bit. This leaves a low gap after the bit.  If this gap is longer than ~6,000ns, it will become a reset pulse (TLL) to the next pixel in the string. So, while there is no maximum width for T1H for a single NeoPixel, if we want to have a string of NeoPixels then we need to make sure that no chip in the chain sees an inadvertent reset pulse. This creates a dynamic maximum for T1H in a string of pixels of 5,000ns (maximum low time before causing a reset TLD)+550ns (the minimum width of the reshaped 1-bit T1H) = 5,550ns. Luckily, 5,500ns is still a very long time and so very easy to comply with.

Updated simplified timing constraints for NeoPixel strings
Symbol Parameter Min Typical Max Units
T0H 0 code ,high voltage time 200 350 500 ns
T1H 1 code ,high voltage time 550 700 5,500 ns
TLD data, low voltage time 450 600 5,000 ns
TLL latch, low voltage time 6,000 ns

But wait – what is this 6us minimum for a reset latch about? The datasheet says a reset takes 50us!

I know… and 44,000ns is a pretty big signal to loose!!

If you look at the timing diagram, you should see that the maximum width for TLD is really the same thing as the minimum for the TLL latching reset code. If the signal stays low for too long after one bit is finished and before the next bit starts, then the chip resets and latches. It turns out empirically that the maximum TLD width is about 5-6us despite what the datasheet says. Really. Leave the signal low for just 6us and the LED will latch.

Just to be sure I wasn’t missing something, I put a photo-detector in front of a NeoPixel to see exactly when and how it was really turning on.

Here is a typical shot of a pixel getting latched…

TxL time needed to latch new color

Channel Color Connection
1 Yellow TRESET trigger (~6us wide)
2 Blue DIN Pixel Data
3 Purple Actual LED output

The yellow trace is a trigger I put in so I could catch just the right moment. It signals the end of a full frame of pixel data. The actual data bits are in the middle trace. In this case, you are seeing the trailing end of a frame that turns the pixel from off to on. The top trace is the measured light output from the LED. You’ll see there is a pause in the data of about 6us, followed about 30us later by the LED actually displaying that new data and turning on. No 50us reset anywhere. All it takes is at least 5-6us (I use 6us to be safe) of low time to latch a new color.

But what is that 30us delay between the latch and the actual display of the new color? Maybe that is where the 50us in the datasheet comes from? Maybe, but it turns out that 30us has nothing to do with the reset timeout – instead it actually comes from the chip’s PWM circuit. If you want to learn more about that, you’ll have to read…

NeoPixels Revealed: Getting physical to uncover PWM Secrets

And now for the code…

With all this in mind, we can write some very simple Arduino code to drive NeoPixels…

#define PIXELS 96*11 // Number of pixels in the string

// These values depend on which pin your string is connected to and what board you are using
// More info on how to find these at http://www.arduino.cc/en/Reference/PortManipulation

// These values are for digital pin 8 on an Arduino Yun or digital pin 12 on a DueMilinove
// Note that you could also include the DigitalWriteFast header file to not need to to this lookup.

#define PIXEL_PORT PORTB // Port of the pin the pixels are connected to
#define PIXEL_DDR DDRB // Port of the pin the pixels are connected to
#define PIXEL_BIT 4 // Bit of the pin the pixels are connected to

// These are the timing constraints taken mostly from the WS2812 datasheets
// These are chosen to be conservative and avoid problems rather than for maximum throughput 

#define T1H  900    // Width of a 1 bit in ns
#define T1L  600    // Width of a 1 bit in ns

#define T0H  400    // Width of a 0 bit in ns
#define T0L  900    // Width of a 0 bit in ns

#define RES 7000    // Width of the low gap between bits to cause a frame to latch

// Here are some convenience defines for using nanoseconds specs to generate actual CPU delays

#define NS_PER_SEC (1000000000L) // Note that this has to be SIGNED since we want to be able to check for negative values of derivatives

#define CYCLES_PER_SEC (F_CPU)

#define NS_PER_CYCLE ( NS_PER_SEC / CYCLES_PER_SEC )

#define NS_TO_CYCLES(n) ( (n) / NS_PER_CYCLE )

#define DELAY_CYCLES(n) ( ((n)>0) ? __builtin_avr_delay_cycles( n ) : __builtin_avr_delay_cycles( 0 ) ) // Make sure we never have a delay less than zero

// Actually send a bit to the string. We turn off optimizations to make sure the compile does
// not reorder things and make it so the delay happens in the wrong place.

void sendBit(bool) __attribute__ ((optimize(0)));

void sendBit( bool bitVal ) {

    if ( bitVal ) {      // 1-bit

      bitSet( PIXEL_PORT , PIXEL_BIT );

      DELAY_CYCLES( NS_TO_CYCLES( T1H ) - 2 ); // 1-bit width less overhead for the actual bit setting
                                                     // Note that this delay could be longer and everything would still work
      bitClear( PIXEL_PORT , PIXEL_BIT );

      DELAY_CYCLES( NS_TO_CYCLES( T1L ) - 10 ); // 1-bit gap less the overhead of the loop

    } else {             // 0-bit

      cli();                                       // We need to protect this bit from being made wider by an interrupt 

      bitSet( PIXEL_PORT , PIXEL_BIT );

      DELAY_CYCLES( NS_TO_CYCLES( T0H ) - 2 ); // 0-bit width less overhead
                                                    // **************************************************************************
                                                    // This line is really the only tight goldilocks timing in the whole program!
                                                    // **************************************************************************
      bitClear( PIXEL_PORT , PIXEL_BIT );

      sei();

      DELAY_CYCLES( NS_TO_CYCLES( T0L ) - 10 ); // 0-bit gap less overhead of the loop

    }

    // Note that the inter-bit gap can be as long as you want as long as it doesn't exceed the 5us reset timeout (which is A long time)
    // Here I have been generous and not tried to squeeze the gap tight but instead erred on the side of lots of extra time.
    // This has thenice side effect of avoid glitches on very long strings becuase

}

void sendByte( unsigned char byte ) {

    for( unsigned char bit = 0 ; bit < 8 ; bit++ ) {

      sendBit( bitRead( byte , 7 ) ); // Neopixel wants bit in highest-to-lowest order
                                                     // so send highest bit (bit #7 in an 8-bit byte since they start at 0)
      byte <<= 1; // and then shift left so bit 6 moves into 7, 5 moves into 6, etc

    }
}

/*

The following three functions are the public API:
  ledSetup() - set up the pin that is connected to the string. Call once at the beginning of the program.
  sendPixel( r , g , b ) - send a single pixel to the string. Call this once for each pixel in a frame.
  show() - latch the recently sent pixels on the LEDs . Call once per frame.
*/

// Set the specified pin up as digital out

void ledsetup() {

  bitSet( PIXEL_DDR , PIXEL_BIT );

}

void sendPixel( unsigned char r, unsigned char g , unsigned char b ) {

  sendByte(g); // Neopixel wants colors in green-then-red-then-blue order
  sendByte(r);
  sendByte(b);

}

// Just wait long enough without sending any bots to cause the pixels to latch and display the last sent frame

void show() {
    DELAY_CYCLES( NS_TO_CYCLES(RES) );
}

You can download the full demo program from GitHub. Open it up in your Arduino IDE and change the #define PIXEL to match your string length and update the PORT, DDR, and BIT defines to match your board and pin.

I’m sorry that you have to look up the PORT values for your Arduino (which turns out to be non-trivial!), but there does not appear to be a clean way for the code to to do this without bringing in something like the DigitalFastWrite library, which would overly complicate things.

The top part of the demo program is the same code shown above and the bottom part is a rework of the AdaFruit strandtest program just to give you something familiar to start with. Keep in mind that I picked values that would look good on a huge string, so if you connect it to a 60 pixel strip that you might not be visually impressed.

This code should work unchanged on any AVR CPU that can toggle the 0-bit fast enough to meet the T0H maximum (that includes all Arduinos). This code is optimized for readability, simplicity, and changeability rather than speed. That said, it is still plenty fast and probably within 25% of being as fast as possible. I’ve also found that this technique of generating pixels on the fly encourages a more functional-ish programming style than working with buffers. This style often leads to code that ends up having faster refresh rates overall even though the per-bit times might not be as fast as the hand optimized bit-banging out of a buffer.  It also turns out that having some slop time after each bit is actually helpful for making very long strings stable…

NeoPixels Revealed: Why you should give your bits room to breathe

FAQ

Q: In the video, when the entire strip is lit full white, it looks like the far end has a orange tint to it. Is this an artifact of the camera or something?
A: Wow, you are very perceptive! Yes, the string did start to get orange at the end at full power. I actually didn’t just run out of apartment when making this long string – I also ran out of power supplies. I had 3 supplies, each 10amps. This is almost enough, but not when every single LED is full brightness white. When that happens, the voltage at the very end of the strip starts to get too low and the LEDs start looking pallid. Why orange? Because blue LEDs need the highest voltage drop to light so when the voltage sags they are the first to go followed by the greens – leaving yellows to oranges and ultimately red.

Q: Are you really, really sure that the reset pulse doesn’t have to be 50us long? ‘Cause that could make my display silky smooth!
A: I’ve tried lots and lots of NeoPixels and WS2812’s and I can not find one that needs more than 6us of low signal to latch. If you find one that needs longer, please let me know!

Q: I have some interrupt timing sensitive code that I have always wanted to run while also driving NeoPixels. Are you saying this might be possible?
A: No problem! As long as your interrupt service time is shorter than ~5us, you can leave interrupts on while driving your NeoPixels with the above code! The only place where we turn them off is during the 350ns + 4 cycles when we are actually generating a 0-bit pulse to make sure that it stays below the maximum T0H length.

Q: Can I call SendPixel() from inside a blocking interrupt routine?
A: Since sendBit() blindly disables and re-enables interrupts, it will blindly turn interrupts back on every time it sends a 0-bit. This is easy to fix. If you are going to always be sending bits from inside a blocking interrupt routine, you can just totally delete the cli() and sei() since interrupts will already be off. If you want to send both normally and from a blocking interrupt, you could store the interrupt state and then restore it rather than blindly sei()’ing.

Q: What NeoPixels are you testing with?
A: I’ve tried every WS2812 I could get my hands on, including lots of NeoPixels from AdaFruit, eBay, and Alibaba. If you find some picky NeoPixels that I missed, please let me know!

Q: Where are your favorite places to get your NeoPixels?
A: Of course I love AdaFruit because they are awesome, have great prices, and ship from NYC in 1-2 days. For huge bulk orders or when I need something special that AdaFruit doesn’t sell, I also have been super-happy with Mr-Right-LED because Mr. Zhang can make me 96 LED-per-meter strings just the way I like them with no silicone, heat shrink,  or adhesive – and they also have great prices and ship from Hong Kong in 2-3 days.

Q: Could I save time by using the gaps in between individual bits for image computation rather than waiting until the end of a pixel?
A: If your algorithm is fine grained enough to be efficiently broken into 24 very short work units per pixel, then this could be tremendously efficient -and impressive! Please send what you come up with!

Q: Silly question, but what if I want to show video!?!
A: No problem, just use Flash! Arduinos have way, way more Flash than they do RAM so there is plenty of room for video data especially if compressed. Read the video data out of flash, decompress it, and send it to the NeoPixels on the fly. No frame buffer needed as long as you can beat the generous 5us timeout. You will probably want to write a program in Java or Python to generate the PROGMEM C code with your compressed video data in it. Ognite uses this read-video-data-on-the-fly-from-flash technique, so is a good place to look for example code. If someone comes up with some super awesome video source material maybe I’ll build an example project to show exactly how it is done.

UPDATES:

3/13/2015 – I’ve updated the code on GitHub so that it now  compiles correctly on all version of the Arduino compiler. I had to get rid of the calls to _deley_cycles() because this function seems to be missing on some version of the OSX compiler. It is instead replaced with some (very simple) inline assembly. You can see the code changes here.

7/8/2014- Thanks to Hack-a-day, I found Tim’s similar work on WS2812Bs. He was motivated to optimize for speed and efficiency, but ends up with code that is also simple and elegant. If you are looking for code that is all C and easy to play with and understand, then my code is good – but if you want a fast library to use in production then you should check out his excellent library.

6/12/2014- Updated the code snipit on this page to match the code in the demo project on GitHub. The timing constants have been relaxed even further (things that have minimums are longer and things that have maximums are shorter) based on feedback I’ve gotten from people running the code on lots of different WS2812’s from lots of places. The code overall now takes slightly longer, but should work with any chip no matter how how sloppy they are.

92 comments

  1. Kevin Wilson

    Thanks for all the great info! I am looking into using a lot of neopixels to indirect light a cofferred ceiling in a room addition we are planning. I do software and know too little about the hardware side, so I am a little concerned about doing the right thing for powering this project… that’s a lot of current! I may do 5V power supplies around the loop or was thinking about running 12 or 24VDC and using buck converters for the 5V. so I am thinking I want a continuous run for the ground and data line, but break the 5V line where each powersupply is inserted.. is this the right way to power a 30+ foot string? Can you point me somewhere to understand how this is done?
    Thanks for the help and all the great content!

    • bigjosh2

      I prefer to keep the distribution voltage as high as possible and then drop it down when it gets to where I need it, so I’d use 5 volt power supplies near the pixels they are powering. This lets you use much smaller distribution wires (12 volts needs 10x more current than 120 volts to transfer the same power), and also skips the extra equipment and losses of the buck converters.

      You do want to connect ground and data lines between adjacent strips (data line is not really “continuous” because it is generated at the output each pixel). You could split the power segments, but this could lead to a problem if two adjacent strips end up at significantly different voltage levels because, say, one happens to have all LEDs on bright while the other happens to have all LEDs off.

      Long story short, you can end up with a case where the voltage of a data in pin on the pixel next to the break is much higher than the power supply voltage that pixel is getting. This is not good.

      If you always connect the power supply pins together, then adjacent pixels will always have power voltages (and thus data voltage levels) that are very close to each other.

      Luckily, NeoPixel strips have pretty high resistance so as long as there are a couple of strips between power supplies, then the resistance of the strips will prevent the supplies from fighting each other too much even if they have slightly different voltages.

      Let me know if this doesn’t make sense and I’ll do a diagram that will hopefully explain better.

  2. Chris

    Thanks again for this information. Exactly what I wanted to know. Now I can run NeoPixels on a JeeNode, too, with interrupts enabled for RFM12b reception. Sweet.

    • Mike

      Chris,

      I’m working on a similar problem: WS2812 and RFM12B on a JeeNode. Would it be possible to send me a link to your solution–that’s surely close to what I’m after.

      Warm Regards,
      Mike

      • Chris Hodges

        Hi Mike. I’m now using FastLED ( http://fastled.io/ ). The core update loop does turn off the interrupts for a while, but I didn’t have problems with that so far. You could modify the code though that it enables the interrupts after each LED for a short time (in pin low state though), so that the RF12 interrupt has time to read out a byte during reception.

        Cheers, Chris

      • 1magister

        Chris,

        Thanks. My concern would be that if a packet comes into the RF board during the cli()/sei()-protected WS2812 updates in FastLED, the RF board will throw an interrupt… which is ignored/dropped. While we can work to minimize the interval of time, I think we’re always left with some interval where a radio interrupt is dropped?

        The only path forward I can see is to recode the RF library to move away from interrupts and use polling approaches instead.

        That said, if you’ve got a working amalgam of RFM+WS2812 library code that “plays well together”, I’d love (love love) to see how you fit those pieces together. Do you have a github repository that I could reference, please?

        Regards,
        Mike

      • bigjosh2

        Ok, so it is actually possible to drive NeoPixels without turning off interrupts at all by using the Timers to drive the data bits. Since this seems to be coming up a lot (especially people wanting to receive RF and serial while fiddling NeoPixels), I’ll go ahead and do a quick article showing how it is done!

      • 1magister

        bigjosh2,

        That’d be huge, thank you. ISR’s are more straight-forward than internal peripherals (and possibly easier to port around hardware), but running multiple devices requiring ISRs is quite difficult.

        Another option using internal peripherals would be most welcome. Please let us know where that post goes up!

        Regards,
        Mike

        • bigjosh2

          So, I’ve written some code that effectively eliminates any interrupt latency due to the neopixels, but there are two problems remaining. (1) The Arduino’s normal timer that keeps track of ticks for the millis() function and other things makes more than 5us of latency on its own, (2) the RTM library also causes more than 5us of latency. So, you could comeplete disable (or rewrite) the Arduino clock, but you’d still have to live with corrupted pixels anytime the RF code was receiving. i did look at the RF code and I think it might be possible to rewrite it for acceptable interrupt latency, but it would be a lot of work.

    • bigjosh2

      After some research, I now think it is impossible to write any practical code that is dependent on exact timing and sequence in AVR-GCC (the compiler used in the Arduino IDE) because there is no way to prevent the compiler from rearranging the order of operations when it optimizes. You might be able to craft a piece of code that happens to result in the correct order of operations for a specific compiler version with specific settings, but it is just luck and there is no guarantee that the same code will continue to work correctly with other versions/settings.

      My new strategy is to generate the critical timing sequences using the chip’s Timer2 hardware. The output of this unit is precise to a single clock cycle (62.5ns with a 16Mhz clock) and completely deterministic. Also, once started it will run completely independently of any code that is executing so you can even do simultaneous foreground processing and completely turn off interrupts. As long as the system clock is running, the correct pulse length will go out.

      BTW, I know that many people use this code so they can respond to latency sensitive interrupts (i.e. RF reception) while also driving NeoPixels. While the current code is succeeds in keeping interrupts enabled while driving the NeopIxels, it is still possible that the NeoPixels output can be corrupted because the RF receive routine turns of interrupts long enough to cause the NeoPixels to reset. There is not much you can do about this without re-writing the receive code, but at least with the new Timer2-based approach you can detect when this happens and limit the damage to a single pixel, and then immediately try to refresh the NeoPixel string again with the correct data.

      I hope to have an article with code up by next week!

  3. Bill Morton

    Thank you for the amazing in-depth tutorial, I stumbled across it as I am starting to have trouble with quick animations on my 900 neopixel christmas light strip. I downloaded your code off GitHub, however when I compile it I get an error in DelayCycles that __builtin_avr_delay_cycles is not defined in this scope. Any help would be appreciated.

      • Bill Morton

        Ok, after beating my head against a wall I still can’t get this to work :( I have tried it on a Yun, Mega, and Uno, and I can’t get much of anything to happen. At best a few pixels will light up pure white and that is it. I have tried multiple strings as well (https://www.sparkfun.com/products/12028).

        Any help would be appreciated. I have no issues with the Adafruit library, and light_ws2812 works, but only for strings < 256 pixels.

      • bigjosh2

        It looks like you are using a mac? The mac version of the Arduino IDE has a broken compiler (see FAQ above). Either you can email me the code and I’ll try it on my PC- or I can convert the timing critical part of the code to ASM- which will work but loose some of the aesthetics.

          • Chris Hodges

            The code here is good to start fiddling with NeoPixels. But I then found a library called FastLED, which also supports the NeoPixel protocol but additionally has all sorts of support routines, such as fast 8 bit maths and HSV colour space. Works like a charm both on my NeoRings and NeoMatrix…

            You could give it a try — fixing the mac compiler is recommended nevertheless…

  4. Tanner

    What is the SimpleNeoPixelDemo code licensed under? Would you mind licensing it under an open source license that is compatible with GPLv3 to me? Thanks.

  5. jaypaulb

    Hi I’m trying to get this to work with the Trinket ATtiny85 and I can’t identify the correct port mapping. I have the data output connected to pin0 on the Trinket but I’m lost as to what entry I would need to make under the port settings in the code. Any help greatly appreciated.

    • bigjosh2

      Looking at the Trinket pinout here…

      https://learn.adafruit.com/assets/13866

      It looks like the Trinket Pin #1 is on PB1. This means “Port B, bit 1” on the chip. So, the corresponding NoePixel code would look like this…


      // These values depend on which pin your string is connected to and what board you are using
      // More info on how to find these at http://www.arduino.cc/en/Reference/PortManipulation

      // These values are for digital pin 1 on an AdaFruit Trinket
      // Note that you could also include the DigitalWriteFast header file to not need to to this lookup.

      #define PIXEL_PORT PORTB // Port of the pin the pixels are connected to
      #define PIXEL_DDR DDRB // Port of the pin the pixels are connected to
      #define PIXEL_BIT 1 // Bit of the pin the pixels are connected to

      Note that the Trinket only runs at 8Mhz, which is just barely fast enough to make the required signals. It is possible to speed the Trinket up to 16Mhz with some code if there are any problems.

      Let me know if it works!

  6. tom jennings

    bummer about the interrupt issue, i can’t imagine writing real programs leaving interrupts off. i’m a very big proponent of “cooperative multitasking”, “tasks” coded as state machines that never (never) block — but as you say, radio and inter-processor serial needs interrupts for character reception and transmission and cli() kills the deal.

    nice work though!

    • bigjosh2

      It is possible to only turn of interrupts long enough to send a full pixel. This way, if you do get interrupted then the worst that can happen is an inadvertent RESET, which at worst will not update the full string but each pixel will still be showing a valid (albeit possibly stale) color. You can then attempt to refresh the string again and visually everything will likely look fine as long as you don’t keep getting interrupted again and again.

      • js

        I’ve never tried this but you should be able to “freeze” transmission while the data line is high and enable interrupts. You could then do one or two nops to allow the interrupt to be detected if one is pending. then re-enable interrupts and continue with your transmit. While the WS2812 data line in high, no data is lost, and you can pick up where you left off. I will not effect any timing on the WS2812. All you have to do is figure how often this procedure is required to not miss any interrupts and how to stick it in the library.

          • bigjosh2

            On strategy that does work is to check for a pending interrupt after each pixel. If there is one, then stop sending, go into reset, enable interrupts. When the pending interrupt is complete, you restart sending again from scratch. This works fine as long as the interrupts don’t happen so often that it visually messes up the string because, say, you almost never get to send a full set of pixels. This obviously becomes more of a problem with longer strings since it takes long to send all the data.

  7. Tom Roden

    Hi Josh,

    Thanks for the great NeoPixels posts. They really helped me out with my PIC Microcontroller WS2812 driver. I did run into some real-world limitations that made me tighten up my timing (500ns T0H didn’t work on all my strips).

    I wrote this up and linked to a couple of your posts. I hope I gave sufficient credit. Have a look if you get a chance. I would love your feedback and will update my post accordingly.

    Here it is: https://www.linkedin.com/pulse/ws2812-dummies-tom-roden

  8. Aj fasching

    I’m not an engineer, nor understand coding….I’m looking for noepixel strip weatherproof lights to go on my houseboat at lake mead. I am in need of 150′ roll or can split that in half and do 2. I have tried rope light, the sun beat them up and only lasted a year. I tried purple led rope in tracks, and only lasted a year. I’m now on heavy duty RGB with controller and I have about 4 sections that are burnt out.
    So I’m in need of really awesome lights that will last.

  9. dntruong

    Following the impulse of your blog I finally got in the mood to finish implementing my WS2812B library: It supports color palette, and the methods can be called repeatedly to light up an infinite LED strip with a 16MHz UNO. Added bonus, bit banging is done without inline assembly, just using careful C++ coding:

    https://dntruong.wordpress.com/2016/02/29/my-ws2812b-library-with-palettes

    I can define 1,2 4, 8 bit, 2 bytes, 3 bytes or 4 bytes per pixel.

    The demo i put on my blog uses a 12 bytes palette and 4 bytes for the 16 pixel pattern.

    It’s a bit tight at 16MHz to run, so next I’d want to learn how to program PWM to shape the pulses without bit-banging. Maybe this could run on 8MHz?

    Woohoo! :)

  10. dntruong

    You’ve been an inspiration for me to write a new LED library that handles any number of pixels, and supports palettes. The code is on GitHUB as FAB_LED:

    https://dntruong.wordpress.com/2016/03/23/my-ws2812b-library-with-palettes/
    https://github.com/sonyhome/FAB_LED/archive/master.zip

    I’d love to see you take a peek at it and get feedback on the implementation,
    feature set and performance. Note I put very aggressive LED timings tested
    only on strips I have on about a 160 LEDs.

    I believe this library has a small edge over AdaFruit and FastLED libraries.

    I’d like to learn and grok PWM programming to replace bitbanging and maybe
    generate cleaner signals on even 4MHz CPUs. I’ve only tested on 16MHz
    Uno and AtTiny85 so far.

    The code took forever because coding after work where I code requires me to still be in the mood.

  11. lukony

    Hello there.
    Did you tested RGB-W LED named SK6812?
    I’m just curious since I want to run this Red-Green-Blue-White color LEDs on single line in series with WS2812B.

    Those LEDs are supported by Adafruit Neopixel library but coding is different so I have to choose whether line will be feeded by WS2812B signal OR SK6812 signal.

    I think that ATtiny85 with piece of smart code can do the trick and act as converter between those two standards, so I can start with WS2812B strip, then convert its output from last pixel by ATtiny and continue the line with SK6812.

    This can be done also opposite way if needed.

    I know that I can simply run those strips separately using parallel wiring, but in my case it requires car interior dismantle, so that’s the reason why I’m thinking about WS2812B>>>SK6812 signal converter.

    Hey, another point to think about:
    If I want to leave strictly serial conducting of WS2812Bs since it is very inconvenient to direct data wire to some “leaf” and then wire it back.
    Its much better to use tree topology for leafs, so there is no need for return of signal wire.
    BUT: If you make leaf of 3pixels, connecting them in parallel to main LED strip, you have to sacrifice next 3pixels on the main strip and mute them by black foil to simulate illusion that leaf AND main strip are addressable individually..
    This is cheap and easy while making 3pixels leafs. But become expansive and also power consuming while there is need for 30pixel leafs.

    In this case it should be very handy to power tree topology by ATTiny in each node, which will “eat” the data mentioned for the leaf LEDs and forward rest of signal to next LED in the main chain.

    • bigjosh2

      >I think that ATtiny85 with piece of smart code can do the trick
      >and act as converter between those two standards, so I can start
      >with WS2812B strip, then convert its output from last pixel by
      >ATtiny and continue the line with SK6812.

      I do not think it is possible to convert WS2812B data stream to SK6812 format on the fly because the SK6812 need 4 bytes per pixel rather than 3. While you are sending the 4th byte for the 1st pixel, the 1st byte for the 2nd pixel will already be coming in. With each pixel you get further behind. I guess you could try to buffer the incoming data into memory, but if the WS2812B is refreshing quickly then you will eventually fall too far behind.

      >In this case it should be very handy to power tree topology by ATTiny in each node,
      >which will “eat” the data mentioned for the leaf LEDs and forward rest of signal to next LED in the main chain.

      I do not think this is necessary – it turns out that you can directly connect the Data Out pin from NeoPixel into the Data In pins of more than one downstream pixel. It really works – I do it all the time! Of course the two stream that are in parallel will display the same patterns, but often this is a benefit.

      • JS

        Very odd. I have an SK6812 datasheet which describes essentially a WS2812B.
        There are only 3 leds: RGB. I’m using the SK2812MINI on a project currently. It’s 3.5mm square and is in a DFN package.They are driven exactly like a WS2812. Chinese documentation, part numbering, and nomenclature is just about worthless!
        Could someone point to a datasheet for this part you are discussing?

          • lukony

            Hello.
            Thanks for really fast reply :-)

            Indeed. SK6812 could be either 24bit OR 32bit.
            https://forum.arduino.cc/index.php?topic=364208.0

            I’m impressed that White chip is quite powerful and its color is very natural.
            But its price is doubled comparing to WS2812B at the moment.

            I’m aware that its not possible to carry 4Byte information in 3Bytes.
            But I was thinking that I can at least extrapolate White value from RGB.

            For example (250,50,30) for 24bit strip can be considered as (250,50,30, 30) for 32bit string. Don’t you think?

            Also when converting from 32bit to 24bit information, White value should be simply dropped while forwarding just RBG only.

            However, my RGB-W strip is now used as addressable foot-well light in the car. More or less I use animated patterns with white light only depending on CAN-BUS information on the input.

            I use also RGB LEDs for door lock handle back-light, depending on conditions. Red/Green blink while locking/unlocking car, White back-light while car is not moving, blackout while reverse-gear is shifted.
            I don’t want to dismantle four doors and change RGB leds to RGB-W at the moment.

            There are also WS2812 Neopixel sticks in both mirrors as animated turn signal.
            Those are sealed and I use amber light only so RGB is perfect here and change to RGB-W is not desired.

            I can still keep all LEDs as RGB only OR add extra wiring for 32bit RGB-W strips.
            But I was just curious to discuss this option of simple 24>>32bit and 32>>24 bit convertor to run “mixed environment” in series :-)

    • dntruong

      I could extend my own library to support SK6812 including RGBW, assuming there’s interest for it and someone to test and validate it works. I do have a 32bit pixel mode already where one byte is just dropped.

      • Lukas Konarski

        Hello.
        Im definitly in :-)
        Please note that Adafruit Neopixel library already supports 32bit LEDs since Adafruit is selling those LEDs.

        But I assume that your library should be faster and more efficient.

        My primary interest is library which can read signal from last 24bit LED of my string and convert it to 32bit stream for continuing 32bit strip.

        The same functionality would be benefical also opposite way. From 32bit strip output to 24bit strip input.

        B.R. Lukas

        • bigjosh2

          I do not think it is necessary to read the signal from the end of one strip and actively convert to the next strip. You should be able to just directly connect the the two strings together and send the right data and everything will work. This is because once a pixel has read its own bits, it will blindly relay any additional bit down the line. It does not try to decode or count these bits (except for re-timing the single bits)- it just sends them on down.

          • dntruong

            I believe Lukas is talking of combining WS2812B RGB and SK6812 RGBW, in which case the signals are not compatible and the 2 LED strips need to be put on separate ports. Otherwise, if both are RGB the timings compatibility would be the only thing to consider.

            With FAB_LED if you need to support RGBW, it’s easy to convert on the fly an RGB value to RGBW, assuming the W channel is just off, or requires simple math. With my library, if I add support, you would just pass the offset of the array where you’re drawing to each port.

            With Adafruit if they have support, you’d have to draw 2 separate arrays.

          • lukony

            Indeed.
            In my car setup I run “main” Atmega328P which is reading signals from CAN-BUS modules and controls 4 arrays of WS2812B LED strings.

            My dream is to have additional ATtiny which will be used in any point of the string as interface, when changing from WS2812B TO SK6812 signal OR opposite way :-)

            I’m aware that this strange setup requires new ATtiny each time when combining 24bit LED string with 32bit one, but for me its small price comparing to add extra wiring for each place in the car where I decide to use 32bit LEDs. :-)

          • dntruong

            You would have to implement the attiny input to receive the signal from the LED strip. Doesn’t sound too difficult to do.

            You would have to buffer the input and when you see a reset signal, send the bits you received.
            The question is will you have time to send it as soon as you receive it.
            I think it is possible.
            Otherwise you would have to wait for the refresh signal which incurred a delay in the forwarding of the bits but it might not matter either for you.

          • lukony

            Hello there :-)
            As you assumed.
            Time doesn’t matter, since human eye is not able to measure that differences.
            I’m just dimming foot-well lights, door lock levers and animating turn signals.

            Can you help me with some kick off, of ATtiny code?

            For example left back seat:
            I’m wiring cable under driver seat. There is node.
            Two WS2812Bs for back-light of left door lock handle @left back seat.
            Two hidden WS2812Bs just to eat that signal, continuing to 15x SK6812 for left back seat foot-well light.
            At the moment there is 15xWS2812B, but SKxxxx is more powerful and more natural color.

            Front seats works opposite way:
            Foot-well light on the beginning of the strip. (SK6812)
            Continue to door lock lever back-light.. (WS2812B)
            Continue to turn signal in back-mirror. (WS2812)
            At the moment all LEDs are WSxxxx, but it should be nice to add those with RGB-W capability :-)

          • dntruong

            Well you’ll need to do some coding for reading theLED strip output yourself, in a loop that runs about 800kHz (?) and detect zeros and ones. Zero is a short high level, and a one is a long high level.
            If you have a long duration without a high level, it’s a led strip reset signal. so clear any bits you had stored.
            I suggest doing this test with a arduino uno, and printing the data seen to the console with Serial, and verify it matches what you are feeding in.

            Now the question is: Is adding an attiny85 in the middle of a led strip simpler than wiring the data line from the main arduino to all the led strips? Is that really a cleaner design to drop in into the car? you still need to find somewhere to hide an atiny, so why not find a way to run a hidden line?

          • Lukas Konarski

            Yes, you are right. Simplest and most reliable thing that I can do is implementing extra wiring for 32bit strips, if I need to use them. Since library is able to drive both kind of strips, this way is probably best.

          • dntruong

            However this idea has merit where the lighting patterns have to be inlined, and matched, I see some applications:
            * inline Apa106 (RGB) with apa104/ws2812b (GRB) – different form factors require mix n match for some projects.
            * inline SK6812 (RGB or RGBW) with apa104 – less usefull unless the RGBW is first, and W dropped, or W rebuilt from RGB value. Seems more of a special use case.
            * mix Apa102 and Apa104 strips – why? maybe just because that’s what you have in stock, and willing to sacrifice a controller for that?

          • bigjosh2

            I think the bit timings of the SK6812RGBW and the WS2812B are compatible. The only difference in the data-stream is number number of bytes per pixel (4 four SK6812RGBW and three for WS2812B. So say you connected one long string of 8 pixels- the first 4 pixels SK6812RGBW and then last 4 pixels WS2812B. I mean literally connect them directly together, end-to-end. No extra wires.

            Now you could just have the software driving the full string from the head on the ATMEGA send a total of (4*4)+(4*3)=28 data bytes and everything should work. The bytes would look like “R1 G1B1 W1 R2 G2 B2 W2 R3 G3 B3 W3 R4 G4 B4 W4 G5 R5 B5 G6 R6 B6 G7 R7 B7 G8 R8 B8”.

            Make sense?

          • dntruong

            Dang!

            Josh might be right, if the bits are just pushed blindly there’s no reason this would not work.

            With my library or adafruit’s, that becomes relatively simple:
            * manage the 2 strips separately
            * send the 2 entries back to back.

            Now that means that the software has to generate RGB and RGBW. If I implement support in my library for RGBW, then you could just manage one uint32_t array and draw on it, just know the offsets of the strips and write each half with the proper object (one GRB, and RGBW).

            If you’re willing to share the result as an example for my library, I’m OK helping you out. Especially if Adafruit’s library timings don’t work.

          • Lukas Konarski

            Its GREAT idea to feed single line either 32bit AND 24bit information. So neat and easy without additional wiring or hardware. :-)

          • dntruong

            Well this got me thinking and I’ve coded up something in this tree:
            https://github.com/sonyhome/FAB_LED/tree/sk6812
            It’s *completely untested* yet so it’s not on my main tree…

            Basically I added support for RGBW, with the class sk6812. You can pass it an array of 32 bit pixels or use an array of type rgbw. You can also define a class ws2812b and pass it that array of type rgbw, and it should display it ignoring the white component.
            This means you could draw into that rgbw array, and send a piece to the ws2812b led strip, and another to the sk6812. If both are on the same port, and you send one after the other fast enough and in the proper order (aka disable interupts) then you would send the proper pattern out…

            A basic test code showing RGB would be… assuming my library has no bug, and no timing issue:

            #include

            // Both strips are soldered together attached on port D6.
            ws2812b myWs2812;
            sk6812 mySk6812;

            loop() {
            rgbw pixels[6] ={};
            pixels[0].r=32;
            pixels[1].g=32;
            pixels[2].b=32;
            pixels[3].r=32;
            pixels[4].g=32;
            pixels[5].b=32;

            // Draw the pixels, sk strip is after ws strip hence pushed first.
            const uint8_t oldSREG = SREG;
            __builtin_avr_cli();
            mySk6812.sendPixels(3,&pixels[0]);
            myWs2812.sendPixels(3,&pixels[3]);
            SREG = oldSREG;

            delay(1000);

            // Erase up to 1000 pixels
            myWs2812.clear(1000);
            }

          • Lukas Konarski

            Hello there :-)
            You are superfast.
            Thrilled to test your code.
            Today I spent babysitting day so there was no chance to play by my own.
            Will be back to you soon :-)

          • Lukas Konarski

            Hello.
            Trying to test your RGBW example.
            I’m not successful at the moment. Didn’t even passed compilation.
            Can you please provide me by example for absolute dummies? :-)

          • dntruong

            Did you get the library, and tried to compile one of the examples coming with it?
            That’s for starters, if there’s compile bugs, please cut and paste them to me, but this might not be the best place to debug this by polluting Josh’s blog :) I’ve built it on a mac only as I’m traveling till tuesday. You can pollute my blog’s comments for that library instead :P or file a bug/comment on the GIT repository.
            I’ll try to take the code I put on this blog into an example that i’ll compile for you asap. But I can’t test it.

          • dntruong

            OK I found the issue, fixed it and updated the examples.

            In A_testPixelStruct example, there is a method that does exactly what you want. All you need to change is the array dimensions… I even added some white so you can see the white LEDs light up a bit when you test.

            If it works for you please post on my github, if not blog or arduino.cc entry for this project to help me get folks interested in this library so it gets some traction to motivate me to improve its feature set.

            Thanks.

          • dntruong

            Cool indeed. I merged back the code in the main tree, I think it’s solid enough.

            For the video and your own project, you’re gonna have to work the hues, your SK strip color is very different from the WS one. Maybe your white component was not set to zero? It won’t show up in the WS strip.

            I myself will move forward with the library, I want to support 2D displays, and make sure my palettes support works with the pixel types I created with this.

            Note:

            Adafruit’s compiled code is 2848 bytes, versus 736 bytes for FAB_LED to do a simple loop to light up a few LEDs (same functionality). I need to check FastLED’s…

          • Lukas Konarski

            Hello, in my Video example, I sent moderate orange (80,50,0) to RGB and dimmed white (0,0,0,10) to RGBW.
            Thats why it looks how it looks.
            However, for demonstration that your code address each pixel in correct way its sufficient.

            BTW, I’m in the middle of changing my CAN-BUS controlled lighting from Adafruit lib to your FAB_LED lib and it works awesome. :-)

            I really appreciate your help with mixed type LED strips.

          • dntruong

            You’re most welcome, and thank you for being a willing guinea pig.

            I am hoping FAB_LED will catch on as an alternative to the other libraries. It’s kind of my goal for polishing it’s API, instead of hacking my own stuff up on top of Josh’s proof of concepts.

  12. bigjosh2

    Can you change the code that is running in the Atmega328? If so, then you should not need to add any extra ATINYs, you can just splice in the new strings and send the correct data.

  13. Jack

    Hi Josh,
    Saw your article on neopixel timing. I’m looking to run neopixels off an arm7 chip using the pwm pins but having issues getting the timing down when working at C (not assembler) level. Running around 812 kHz after using a clock divider on 260 MHZ chip. It looks like it’s generating extra pwm duty cycles of 1’s and 0’s between switches – i.e. if I want to generate a 1-0-0-0 1-0-0-0, it will instead generate 1-1-1-0 1-1-1-0 due to lag between high level command and the pwm generator.

    Looking into assembler manipulation but having issues getting port specifications. Would be awesome if you could follow up with an email. Thanks.

      • Jack

        Hi Josh, thanks for reply. I found some of the issues after diving in. The PWM voltage to be around 2.8-2.9 volts on this particular board. LEDs need 3.3V+ from what I understand. I am getting partial leds lighting (I can get nth led to light up) with some error with no ability to control color whatsoever – all bright or tinted leds.

        I will try the DMA approach as the voltage seems to be higher. DmaChannel.h is not part of the platform’s architecture so might have to dig bit deeper into SDK. Are you familiar with the Linkit One board?

  14. dntruong

    I’m adding support to APA-102 LEDs and ARM to my FAB_LED library. Maybe you have advice on it:

    * I’m waiting on apa-102’s to see if I coded the SPI bitbanging right (it was too easy so IDK if I missed something). I’m told the hardware spi is not worth it.
    * I’m stalled on ARM because I need to create a reliable, precise busy-loop with nops, and I dunno how. yet.
    * I put stubs for future support of other ways to push the pixels to the LED strips. For ARM there must be a better way, dmas, interrupts…

    FYI I sized my library and it uses at least 4X less Flash RAM than Adafruit and FastLED.

    • bigjosh2

      RE SPI on AVR: The SPI hardware is handy, but does force you to use the SPI pins. If you are going for pure speed, I think Blind Send SPI is the fastest possible way to squirt bits to an APA102.

      RE ARM: If you plan to work on top of normal Linux, it is very hard to get the correct timing with a NOP loop so you’ll want hardware support. Two options are using a serial port or using PWM driven by DMA.

      • dntruong

        Thanks!
        I’ve joined LEDs ARE AWESOME on FB, recommended by Phil Burgese, to get the library some attention. There I was asked if I could do parallel strip updates, and duh, it’s 8x faster so I’m adding that too now :P

    • bigjosh2

      I think it is possible using the same trick that PJRC uses in the ARM PWM code.- divide each bit into 3 segments. Then preload a section of memory (ram or rom) with the correct sequence of bytes to drive all 8 strings at the same time. Then a simple tight loop that (1) sets the initial phase (all 1’s), (2) pulls a byte from mem and writes it to the PORT, (3) sets the final phase (all 0’s). Timing for each step so they are 1/3 of a fullbit time. Makes sense?

      This kills the whole idea of generating the bit stream on demand without a memory buffer, but is simple and should work. Next step would be to try and generate the data on the fly. I think you could do it for very simple patterns, but not much else at 16mhz. LMK what you find!

      • dntruong

        Daniel Garcia (FastLED) told me in on paper he could do 4 pins in theory in ASM, but has implemented it only 8 or more pins for faster CPUs/ARM. I’m not clear what you said, but I think you mean building the bytemask of bits set to 1, to push in a buffer, send 0xFF, &ones, 0x00. I’ve tried it and failed w/o a buffer. Building the buffer defeats the purpose: be faster than sending to each pin separately, and is not memory efficient.

  15. gogol

    Hello,
    I came via https://github.com/bigjosh/NoCliNeoPixelDemo to this blog page.
    You write there:
    A demonstration showing how to drive WS2812 NeoPixels without ever turning off interrupts.
    However all the examples in github still contain cli() and sei(). Do I have missed something?
    I wish to build a LED clock, which reads the time from DCF77 (https://en.wikipedia.org/wiki/DCF77). Reading and syncing the time, I need reliable interrupts, as it takes at least one full minute to read the current time.

    • bigjosh2

      Those SEI/CLI’s are only in there so the code will work on a stock Arduino running the default libraries. Unfortunately the timer ISR in the library is a dog and takes long enough that any connected NeoPixels will RESET. If you disable or replace that ISR (which it sounds like you will need to do since interrupts are off when that ISR is active), then you will not need the CLI/SEI’s. Do make sure that your own ISR will return before the NeoPixels time out – which is between 5us and 50us depending on the manufacturer of the chips.

      • dntruong

        I think Gogol should either consider using APA102’s which don’t require cli/sei have a low enough refresh rate of the clock’s LEDs, aka every second, so that in the down time he has plenty of time to read the radio clock. If the protocol takes 950ms, that leave plenty of time to update a few ws2812b LEDs.

  16. Mark Balliet

    I’m impressed with your work figuring out the in’s and outs of the protocol for the WS2812. I’m building an I2C to WS2812 gateway board for my home automation system, and writing my code for an ATTiny85. It works on the ATTiny85 as well, I didn’t think it would, but I just used your code to control 900 LEDs (I need a MUCH bigger PSU). Thank you for your hard work on this project!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s