Wrong colors from WS2812 LED strips driven by RoboRIO

After a repair, WS2812 “smart” LED strips driven with the same data gave different colors.

TL;DR Summary:

  • The RoboRIO’s PWM outputs can just barely drive the fast data stream needed by these “smart” LEDs.
  • Signal Integrity is important on the LED data stream from RoboRIO to LEDs.
  • Not all “compatible” RGB LED strips behave exactly the same when given a distorted data stream.
  • There are workarounds and fixes. Simplest: keep the LED data wires SHORT.

What We Did

Given good success using “smart” RGB LED strips driven directly by the RoboRIO last year, my team, 4795, decided to use them again for Hydra, our robot for Charged UP. We followed the usual software guide: Addressable LEDs — FIRST Robotics Competition documentation

This season, the LEDs mainly signal the humanplayer which game piece is desired. Some additional colors and patterns also show some software-debugging status.

We used three strips, two up vertical structural members, and one inside the rotating arm . Just like last year, all strips are fed the same data stream from the RoboRIO, using PWM “Y” cables to split the
data. Each strip is wired with heavier 5V power and ground wires and a scrap of PWM cable with a connector for data and data-ground. Power wires from each strip run directly to 5v outputs from a VRM.

Splitting the data stream with a “Y” cable makes the wiring neater: we don’t have to daisy chain from the “output” end of one strip across or back down the robot structure and over to the next strip of LEDs. We
also don’t have to duplicate the data in software to guarantee that we show the same pattern on every strip so it can be seen from all angles.

Our original LED strips we believe came from Rev, although we’re not 100% sure since we’ve had them since last season

The Problem

All was good at the UNC-Ashville competition until one of the strips was broken in a collision with another robot. The pit crew replaced the damaged strip with another from a different source. That one was from Amazon:

https://www.amazon.com/dp/B08GSCJPMD

The replacement strip shows bright white when the others are magenta, and a bright, pale green when the others are yellow. (See picture at the top of this post.) I guessed at this point that there must be some difference in the chips inside these two different “WS2812B compatible” LED strips - but what?

Debugging and Speculation

When we fed the robot’s three LED strips with properly formatted data from an Arduino running a WS2812 LED demo program (GitHub - adafruit/Adafruit_NeoPixel: Arduino library for controlling single-wire LED pixels (NeoPixel, WS2812, etc.)), all three Y-connected strips show the same, correct colors.

Thinking about the data stream for these LED strips, I got to wondering: what could turn some of the “0” bits into “1” bits, resulting in a brighter, washed-out appearance?

If we google the datasheet for these parts
(https://www.digikey.com/en/datasheets/parallaxinc/parallax-inc-28085-ws2812b-rgb-led-datasheet),
we see that “0” bits are represented by narrow pulses (400ns), and “1” bits by wider pulses (800ns). A complete date bit is about 1200ns, for a data rate of about 800 Kbps. This is a form of pulse-width
modulation, although quite different (and much faster) than the PWM used by servos and motor controllers.

The RoboRIO documentation isn’t very good on electrical charateristics of its outputs, and there’s no schematic for the RoboRIO. Brief rant: NI doesn’t need to provide a complete schematic, but enough shematic of each input and output to fully document (and support teaching about) the interfaces should be required.

The RoboRIO V2 documentation does say that the PWM outputs are rated for “Maximum Frequency 150KHz”. There’s the first problem: the LED data stream is much faster than that. But it works for many teams, at least sometimes.

The RoboRIO V2 documentation also says “the lines on the PWM and Relay ports all have 40KΩ pulldown resistors to ground”, along with a schematic fragment.

Which leads me to wonder, do the PWM outputs not really drive “low” (pull the pin to ground) very well? Is that pulldown resistor the only thing sinking current?

What exactly do motor controllers do with the PWM signal, anyway?
What kind of circuit is the RoboRIO probably designed to drive?

The only FIRST-legal motor controller for which I’ve seen a schematic was the original Jaguar, also known as the “Texas Instruments Stellaris microcontroller reference design kit.” It uses an opto-isolator on the
PWM input, with the PWM data directly driving the LED in the isolator:

This is quite nice, as the pwm is completely isolated from the potentially-noisy motor power. This only requires the RoboRIO (or cRIO!) PWM output to drive “high” (+5 volts), and not to sink much
current.

But if the RoboRIO’s PWM outputs actually are designed especially for this, they’ll actually perform quite poorly for faster data (with narrower pulses).

And there we have my best guess as to the problem: The RoboRIO’s PWM outputs are being asked to drive faster data than they’re designed for, and they do a poor job of pulling the signal back “low”. If
there’s much stray capacitance in the wiring - say from wires that are rather long, or multiple LED strips Y-cabled to the same data - the falling edges of the data bits will be slowed down, making some or all
of the “0” bits stretch into “1” bits. (The rising edges won’t be affected much).

I was able to turn the arduino’s “good” LED data stream into a “bad” stream using a diode and a resistor.

This reliably turns our oddball LED strip to pale, bright, nearly-white colors, while our original LED strip doesn’t seem to mind as much. Here’s a snapshot of two different strips driven through that circuit on
my messy workbench.

Changing to a much lower valued resitor restores a good edge rate, and works around the problem. 2.2K or 2.7K works nicely at home on my bench.

The workaround we’re using on the robot right now is to only drive two strips (one of each type) and disconnect the second Y cable and the long cable (about feet) running up to the rotary arm. That restored both strips to the correct color for our second event, FNC Wake County, and also for the NC DCMP.

But for reliably driving a few feet of cable, or multiple LED strips from any vendor, an active buffer circuit would likely be more reliable. I’ve prototyped this at home with a 74HC04 chip wired as a buffer:

The prototype breadboard is visible in the photo above.

Testing all of this out on the actual robot and competition-veteran LED strips will probably wait until the end of the season. Likewise, dragging my oscilloscope into school to verify and demonstrate all of this in detail to the electrical subteam students will also get to wait until after our last event - which we hope is in Houston!
Update: yes, our first order of business is getting the team and robot to the championship!
Look for me if you want to talk robot electronics!

  • Steve
8 Likes

The RoboRIO specifications say that each PWM output has a 330 ohm resistor in series on the output and a maximum of 15 mA of output current, and also give the following test conditions:
Output high voltage, sourcing 0.1 mA: 4.75 V minimum; 5.25 V maximum
Output low voltage, sinking 0.1 mA: 0.0 V minimum; 0.25 V maximum

0.1 mA is definitely not a lot of current in either direction, but should be able to handle a typical high-impedance load (which should be in the 10s of uA range). I wonder if the new strip has a very poor input characteristic that is not particularly high impedance?

I’ll also note that Y-splitting signal lines can result in very poor signal integrity due to reflections.

You can also adjust the bit timings the FPGA outputs.

2 Likes

Doesn’t sound like the case here, but it might be useful for other teams - some strips are GRB rather than RGB, and the green and red need to be switched in code.

1 Like

We had a similar problem on our robot this year. We had 4 parallel LED strips that were relatively short - 15 LEDs apiece - and wired in a Y configuration. One of the strips started lighting with random colors. When we started troubleshooting, the problem moved to a different strand. I suspected a signal integrity issue but we didn’t have time to track it down at comps.

Now that we’re back in the shop, I might use it as an excuse to teach the students how to use an oscilloscope.

Thank you for the detailed write-up!

There are SPI-controlled LED strips that should sidestep many of these issues. A really nice product would be a little board with power converters for two or three of these strips, and the SPI multiplexing – properly isolated from the roboRIO, so as to avoid introducing ground loops.

So what’s recommended to use instead of a Y-split? I know its not just powering it from the rio (over drawing current and 6V be very fun).

An easy option is to use multiple PWM ports, if a single port works out for interfacing a single LED strip. But this would be wired in a similar way as in the original post, so it’s still more involved than just connecting to the PWM port.

A more involved option is to use something like this part. You would probably need to make a small custom board though. This part only uses 10μA from the PMW signal, so you can use several of these with the same PWM signal. Basically, you connect the input side of this part to the three pins from a PWM port on the roboRIO. A custom board allows keeping any “Y” very short. Also, it makes it easy to include bypass capacitors, which can be important in this type of circuit.

The output side is also three pins, and would be powered by the same power supply providing power to the LED strip. This ensures there is no possibility of a ground loop, and (again) the output signal does not have to be used with more than a single strip. The output stage is designed for this type of application (with no series resistor).

So a Y split of the power is fine (e.g. power to regulator, signal to Rio). What’s not fine is Y splits of the signal line. A better approach there is simply to daisy chain the strips and if you want the same output on multiple strips, duplicate the values in code.

Currently the Rio only supports output of one strand onto a single PWM. Hopefully there will be a future upgrade to allow for multiple PWM outputs (e.g. each with different start/end addresses in the buffer).

2 Likes

I’ll also point out that there are a lot of digital RGB LEDs out there (WS2812, WS2812b, SK6812, no-name clones…) that, due to being Chinese, often get sold as each other. The required bit timings are similar enough that they work the same… kinda… usually.

image

4 Likes

There’s one other magic trick you can do with addressable leds like this. Put one as close to the roborio as possible, and then run a longer wire and/or your splitter after the first led. Pretty much all buffer their output signal, so you’ll get a clean signal out of that first light. So in essence it becomes a buffer. This also helps because we’ve ran into cases where bad power causes the first light to die. It’s much easier to replace it when it’s easily accessible.

3 Likes

This is brilliant!!! (Bonus pun not intended!)

Thanks for the interesting discussion!
The oscilloscope will tell for sure, but I’ll bet that reflections aren’t an issue: with that 330 ohm series resistor in the RoboRIO output and the capacitance from a few feet of wiring, the risetime will be long compared to the propagation delay.

Using one of the LEDs as a buffer - with bonus as a test indicator - is indeed brilliant! One of the datasheets I have (SK2812) lists the output current as 50 mA, which is much beefier than the 'HC04.
For even better signal integrity, one could use source-series termination, putting a 100 or 120 ohm resistor in series with the driver, right near the driver. Feeding several strips in parallel would best be done with seperate buildout resistors from the same driver, or parallel drivers. Longish runs out to the LED strips would be better done with twisted pair cable - just like CAN bus.

Be sure to power that first buffer chip or LED from 5 volts (say from a handy RoboRIO DIO pin), not from the 6v on the PWM port.
I should have thought of using one LED as a buffer - our programmers have a little two-LED assembly (with seperate connections for PWM data and DIO-power) they use to test code with only a RoboRIO when a full strip and seperate VRM isn’t handy. Looks like this, and just needs a data-output connector.

led-test-board

Also, some of the tutorials on these LED strips recommend a series resistor in the data line near the LED’s data input pin. Apparently some of them can be damaged by providing data without power, a comon issue with CMOS devices.
I also worry about the ground wire running along with the data wire trying to put all of the LED return current through the PWM connector if the ground wire running from the LEDs to the VRM becomes disconnected.

  • Steve
2 Likes

There’s a scope reading of the signal. Definitely a large amount of rise time.

Also the default timing values for the output are as follows.

Low Time, 0 bit: 900ns
High Time, 0 bit: 400ns
Low Time, 1 bit: 600ns
High Time, 1 bit: 900ns

(I updated these values to actually match. Turns out the HAL set timing function had these backwards. So default things would work, but if you actually attempted to set timing values, things wouldn’t work)

That’s pretty close to what the scope is reading for each of these.

2 Likes

Its trivial enough to just have a second LED strip declared in the code, and replicate the pattern on another strip. I find that the more you mess with the wires, like adding a Y to split the signal, you introduce more points of failure. If you really do want to split the signal in hardware, you can look into something like the goBilda Servo PDB. It has a signal sharing feature that allows you to split a PWM signal to multiple strips, as well as makes it neater to distribute power to all your strips.

The roboRIO only supports the use of one AddressableLED output at once.

This may not support the frequency needed for driving addressable LEDs, as it’s designed for servos. It doesn’t look like it passes the signal through passively.

Wait really? That’s a major bummer… I didn’t see anything to that effect in the docs.

It’s documented in the api documentation:

We also have it documented on the beta frc-docs site; that was a recent change.

From what this image says, it seems like it is passive. Seems like its just joining the signal pin.

1 Like

Out of curiosity, what is the reason why the roboRIO only supports one AddressableLED output?

Only a single strip exists in the FPGA code. It actually takes up a decent amount of FPGA resources due to the amount of memory required.

2 Likes