RGB LED Ring for Compass Project -- Teensy 3.2 + NeoPixel Ring?

Status
Not open for further replies.

bringfire

Active member
Hello,

I am designing a GPS Wayfinding/Data Logging device using the Teensy 3.2. Currently, I am using 4 LED's that denote the 4 primary cardinal directions, and blinking them at different rates to give a sense of distance...similar to the logic of an old rangefinding device:

slow blink = far away
fast blink = closer
steady light on = at location.

I am using combinations of the LED cardinal direction lights to denote the cardinal directions in between each of the primary directions, so:

North LED on + West LED on = NorthWest, etc...

I would like to indicate even more distinctions in direction and use the color information of RGB LED's for indicating other things as well...which leads me to the NeoPixel Ring as a possible solution (see the attached diagram).

NeoPixelCompass_Rose.jpg

My question is about the very specific timing needed to communicate with the NeoPixels and whether or not this is going to be problematic with running other functions "simultaneously." This thread deals with what I am referring to, and tni's code solution that uses interrupts to solve the blinking issue that I was having works really well for me. https://forum.pjrc.com/threads/36778-Teensy-3-2-Interrups-to-Blink-LED-s-at-Different-Rates

Are the NeoPixels the best way to proceed? Should I be thinking of other ways to address 16 different RGB LED's to implement this part of the project?

Any ideas would be super helpful.

Thanks!
Aaron
 
Last edited:
Maybe I need to try and be more clear with my question, now that I've researched this a bit more:

In the code that the user "tni" posted here: https://forum.pjrc.com/threads/36778-Teensy-3-2-Interrups-to-Blink-LED-s-at-Different-Rates digitalWrite() is called within the ISR to change the state of the LED.

Can the "setPixel()" and "show()" methods from the OctoWS811 LED Library be called in the ISR (interrupt service routine)? Or do they become problematic when used within an ISR, because they take too long, or do other things that shouldn't/can't be done in an ISR?

I just received the NeoPixel Ring today. I'll start testing things, and post results.

Thanks in Advance for any help and advice.
 
Just thought I would point out the leds are "static" and do not have to be continually refreshed, set and forget until something needs changed.
 
The biggest NeoPixel Ring yet! With four of these you can make a huge ring with 60 ultra bright smart LED NeoPixels are arranged in a circle with a 6.2" diameter. Each order comes with just the quarter ring. Four of this item are required to make a large ring. You will have to solder them together as well, so for the full ring of 60 LEDs, buy four and solder them together!
 
Just thought I would point out the leds are "static" and do not have to be continually refreshed, set and forget until something needs changed.

That is really helpful to know. I assumed that they needed continuous refreshing. Thanks for the info.
 
Just thought I would point out the leds are "static" and do not have to be continually refreshed, set and forget until something needs changed.

That is really helpful to know. I assumed that they needed continuous refreshing. Thanks for the info.

I guess the next question is related to the amount of time spent in an ISR.

The OctoWS811 LED Library states that in terms of timing for the "show()" method:
This function returns within 2 microseconds. The display update continues, taking 30 microseconds for for each LED, plus 50 microseconds to reset the WS2811. If called while a previous update is running, this function waits for previous update to complete and then starts a new update.

Does this mean that the full cycle to change the color or state of one NeoPixel is ~82 microseconds? Is this generally too long to be inside an ISR safely?

Thanks!
 
That is really helpful to know. I assumed that they needed continuous refreshing. Thanks for the info.

Neopixels (ws2812b) and dotstars (apa102) have a microprocessor for each LED. When you are updating them (typically using the show function), it updates the entire LED string sequentially. Internally, it has to keep an array of 3 or 4 bytes per LED to hold the color levels (R, G, B, and possibly W), and the show function iterates through the array sending each value. When you call setPixelColor, it just sets this internal array and it doesn't update the physical LEDs. Normally Teensys have plenty of memory and it isn't an issue to have this array until you get to a large number of neopixels/dotstars. With microprocessors that have much smaller amounts of memory (such as the ATtiny85 that has 512 bytes of read/write memory that everything must share), it can be an issue.

Neopixels don't have an explicit clock, instead each byte must be delivered to the neopixel within a strict time period. I believe the normal Adafruit_Neopixel library disables interrupts when it is writing the the LEDs, and re-enables interrupts afterwards. The more advanced libraries such as octows2811 and FastLed may have alternate strategies. This becomes important if you are using other things (like servos) that use interrupts, if you keep interrupts disabled for a long period of time. I suspect that a 16 LED ring won't interfere as much as if you had hundreds of LEDs. One consequence of implicit timing is when you want to pulse the lights really fast, for persistence of vision (POV) type setups, that you can't because you are limited to the neopixel clock rate. Also, it makes it harder to do neopixels on a system with an operating system like the Raspberry Pi.

Note, when you buy the neopixels, they come in two varieties, one that just has 3 LEDs internally (RGB or red, green, blue) and the other that has 4 LEDs (RGB plus white). The two are not interchangeable. You must use a different constructor for each, and in any strand of neopixels, you must have the same type. I believe some ws2812b's that came from other manufacturers than Adafruit, may have the colors in a different order (RBG, etc.). You may need to test things out to make sure you get the colors you want (the constructor specifies which order to send out the RGB or RGBW colors).

You can tell by looking at a neopixel whether it is RGB or RGBW, since it has a yellow half circle for the white light. Adafruit sells RGBW neopixels with three different white color temp. settings and there is no indication on the neopixel which white color setting was used (because they use a common PCB for all three variants). So if you are buying neopixels over time, you may want to mark on the neopixel which type it is.

Finally, if you are going to mix ring sizes, Adafruit made their 16 LED initial ring run in a clockwise fashion, but all other rings with a different number of LEDs run in a counter-clockwise fashion.

Unfortunately, the Adafruit neopixel ring with 12 LEDs is slightly too large to allow you to put the 16 LED ring on the outside, and the 12 LED ring on the inside (for example if you wanted to do a compass with 16 LEDs and inside a clock with 12 LEDs). If you search around ebay/amazon for third party ws2812b LED ring vendors, there are vendors that sell 16/12 LED rings that nest.

Dotstars are different in that there are 2 data wires instead of 1, and the second data wire is an explicit clock pulse. This means the timing is not as critical for dotstars. You can do POV and it works on OSes. However, I'm not aware of any dotsar rings.

Another neopixel/dotstar issue that comes up is these LEDs really want 5v, and the Teensy runs at 3.3v. Due to the timing constraints, a lot of the voltage level shifters don't work so well for neopixels. If you are powering your Teensy with a 3.7-4.2v LiPoly battery, you won't need a level shifter when you are using the battery, because the neopixels will get 3.7-4.2v from the battery, and 3.3v data signal from the Teensy. However, if you are powering the Teensy via USB, it may be a problem, since the difference between 5v and 3.3v is much higher, and some neopixels might not recognize that the Teensy sent a signal. I find that the neopixels I bought several years ago were more forgiving than some of the ones I bought recently. Many of the level shifters that are meant for handling i2c/spi will not work for neopixels due to the timing requirements. The 74AHCT125 level shifter is the one that Paul recommends for shifting neopixels.

If you don't use a level shifter and you normally run off of the battery, you have several options:
  • You can just use the USB to reprogram the Teensy and then disconnect the USB and connect the battery to see the result;
  • You can cut the solder trace under the Teensy that disconnects the USB power to the Teensy (and then you have to use the battery all of the time);
  • The two prop shields have level shifters that work with neopixels (you have to set pin 7 high to enable the level shifter);
  • If you don't need all of the Teensy 3.2 features, you could use the Teensy LC, which has a built-in level shifter for pin 17 built-in (that's what I use for a simple neopixel led ring flash on a wooden bow tie);
  • The octows2811 adapter will also work, but it is overkill for just a few LEDs.
 
Last edited:
MichaelMeissner So much great information here:

I believe the normal Adafruit_Neopixel library disables interrupts when it is writing the the LEDs, and re-enables interrupts afterwards.

I stopped looking at the Adafruit_Neopixel library after finding this out from their post here: https://learn.adafruit.com/neopixels-and-servos/overview and realizing that it probably wasn't the right approach. I'll have to dig a little deeper into the OctoWS2811 library and FastLED libraries to see how they operate with the NeoPixels that I just purchased.


Note, when you buy the neopixels, they come in two varieties, one that just has 3 LEDs internally (RGB or red, green, blue) and the other that has 4 LEDs (RGB plus white). The two are not interchangeable. You must use a different constructor for each, and in any strand of neopixels, you must have the same type. I believe some ws2812b's that came from other manufacturers than Adafruit, may have the colors in a different order (RBG, etc.). You may need to test things out to make sure you get the colors you want (the constructor specifies which order to send out the RGB or RGBW colors).

I purchased the "standard" RGB NeoPixel ring that doesn't have the White channel. I'll have to look more closely at how the OctoWS2811 library deals with the different constructors, but it looks like the default constructor covers this most basic RGB case (where they also conform to that order of red, blue and green). Testing required.

If you are powering your Teensy with a 3.7-4.2v LiPoly battery, you won't need a level shifter when you are using the battery, because the neopixels will get 3.7-4.2v from the battery, and 3.3v data signal from the Teensy. However, if you are powering the Teensy via USB, it may be a problem, since the difference between 5v and 3.3v is much higher, and some neopixels might not recognize that the Teensy sent a signal.

This piece of information is super helpful! I was wondering if powering the NeoPixels AND sending control signals to the NeoPixels from my 3.7v 1200mAh LiPo battery would eliminate the need for level shifting. I'm glad the answer appears to be yes. One less component/issue to incorporate/overcome.

You can cut the solder trace under the Teensy that disconnects the USB power to the Teensy (and then you have to use the battery all of the time);

I am using the LiPo charging board from Pesky Products found here: https://www.tindie.com/products/onehorse/stbc08-high-current-lipo-battery-charger/ and it is working out great for testing my project while it is mobile in the field, while also allowing me to program the board and charge from the USB port on my laptop. The battery is always connected, but that is fine for me.

Thanks again for all of the valuable information. I'm sure I'll have more questions for the forum as I proceed, but it sounds like I'm on the right track with the NeoPixels.

Onward to more testing!
 
Note, I've seen posts that some versions of FastLed don't work on Teensy. Since I only use the Adafruit libraries, I haven't paid much attention to it.

The octows2811 library/board is much more heavyweight, in that it is designed for video walls where you are updating hundred to thousands of LEDs and need to do things in parallel, using special purpose SPI code to optimize things. It will work for a small set of LEDs, but it may be overkill.

<edit>
I suspect for human reaction times it probably doesn't matter. Use intervalTimer (https://www.pjrc.com/teensy/td_timing_IntervalTimer.html) to set a volatile state variable in the interrupt handler, and on the next run of loop check the variable, and update the lights via setPixelColor and show.

Or use elapsedMillis (https://www.pjrc.com/teensy/td_timing_elaspedMillis.html) to keep track of the blinking state without using interrupts.

For things like servos and stepper motors you may need finer grain, but for just blinking lights to clue in a human, you don't need the precision.
 
Last edited:
I was curious, and with a google search I found: https://learn.adafruit.com/adafruit-neopixel-uberguide/advanced-coding

According to that, it takes roughly 30 microseconds per LED + 50 microseconds to each the transmission. For 16 LEDs, that is 530 microseconds where interrupts must be disabled. And you could refresh a strand of 16 LEDs 1,886 times a second assuming you aren't doing significant calculations. I doubt most humans can see a blink rate of 1,000 times a second (if they can, it must be painful to watch movies or TV, which is around 24 to 120 frames per second).

And note that if you are rapidly blinking a light on/off to set the power level and dim the lights, the neopixels do the PWM action themselves. You just have to give the power level for each of the 3 colors for each pixel. So you don't have to rapidly turn them on/off.

So assuming you want to change the LEDs 100 times a second, there is plenty of time when you are not blinking the lights. As I said, there are times when it becomes critical, such as keeping track of encoder pulses to track a wheel revolution, reading serial data at really high speeds, etc. And if you are doing hundreds of LEDs it becomes more critical than if you are writing just 16 LEDs.
 
Last edited:
Ok, that's helpful. Right now I am using extra code in my systick() ISR to toggle the LED's because I was getting erratic blinking while reading, parsing, and logging the GPS data, and I didn't want to try to chase the problem by adding millis() or ellapsedMillis() polling after every portion of code. I'm using the solution posted here by the user tni: https://forum.pjrc.com/threads/36778-Teensy-3-2-Interrups-to-Blink-LED-s-at-Different-Rates which works great and I was hoping to adapt this approach with the NeoPixels.

So, because I am currently using an interrupt based approach, the disabling of interrupts (ala the Adafruit Library approach) seemed like a logical dead end, but I'm very much still learning about interrupts and other ways of coordinating the timing of events.

Maybe the Adafruit Library will still work with a different technique than what I was imagining.

My admittedly naive first question to myself regarding this interrupt based approach was whether or not setting the NeoPixel values in the ISR, using the OctoWS2811 show() method would take too much time from the perspective of "good practice" with regards to time spent inside the interrupt service routine. Right now there are just a few comparison operations and digitalWrite() in the ISR to implement the blink. I'm so new to interrupts that I just can't gauge whether or not this might be a bad idea from the beginning.
 
Status
Not open for further replies.
Back
Top