Emulating a neopixel (WS2812 or SK6812) *Receiver*

Hello all, let me explain what I'd like to do.

I have a device which controls a strip of 15 SK6812 RGBW LEDs. I would like to intercept the commands going to the LED strip and use it to control separate non-addressable LEDs. Using a WS2811 chip is not an option because it does not support RGBW commands (only RGB) and therefore the incoming data will cause unwanted behavior.

What I'd like to do is set up an arduino to act like a WS2812/SK6812 neopixel (the signal characteristics are here, courtesy of adafruit) and read the commands from the driving device. However I don't know where to start creating a program which reads commands with such short and seemingly crucial timing requirements. Can anyone point me in the right direction or maybe show me an example of a piece of code which receives a similar signal? Thank you!

Well, it stands to reason that if an Arduino can generate the code for the NeoPixels, it can equally well interpret it. I fancy I have seen this task before, but have no idea how to search for it.

I will tell you two things. It will probably require actual assembler coding, and it will require interrupts to be turned off (in advance) in order to decode the data.

And the automatic question: Why? :face_with_raised_eyebrow:

1 Like

Fair point. Technically speaking the colors are not really relevant, but a ws2811 is a 3 channel receiver/forwarder and you want 4-channel.
The workaround would be to listen with 2x ws2811 on a side-branch or to use 4x ws2811 to listen to 3x RGBW channels.

The signal speed is such that it would be rather difficult, though if you could set a timer interrupt to fire every 3uS (which is possible of course) you might have resolved the issue, but preferably the resolution should be even higher, and i am pretty sure that on an AVR that is going to be difficult with CPU resources.
Other option would be to use a pin-change interrupt, but with micros() at a 4uS resolution, you would have to use you own loop-counter.
If making an attempt i would go for a faster board like an ESP.

That is optimism ! Also the reasoning is not quite correct. If there would be dedicated hardware involved, maybe, but comparing to something similar like Serial, one can reliably transmit using swSerial up to 250kbps, but reliably receive only up to 57.6 Kbps.

Probably. Timing is crucial.

Well i can think of a practical application, but i would rather go for a hardware Solution personally, thouigh a bit of a challenge is always interesting. On an ESP, assembler can be avoided i think. if you fire timer1 at 1uS, checking the pin at that resolution should do the trick and leave sufficient computational power.

Do you need the avr to chain the commmands onward to other “neopixels”, or just be the last or only receiver?

Just something that comes to mind. Are SK6812's really at such a low baud rate ? ws2811 datasheet shows much higher speed.

Thanks everyone for the input!

Fair question! I'll elaborate in case anyone has a clever approach. I have one of these , a Onewheel. You can see the light bar in the photo. The lights turn white when moving forward, and red when going backwards. I opened it up and found that it uses 13x SK6812 RGBW LEDs. What I'd like to do is replace the light bar board with a custom board using much brighter non-addressable LEDs (both white and red).

Do you mean that after receiving the data interrupts would need to be turned off to act on it? My initial (probably naive) idea was to use a pin change interrupt and a systick timer to read the signal. There are probably more sophisticated ways to do it with less horsepower but I'm not that sophisticated of a coder :slight_smile:

Yes the microcontroller would be the last and only receiver, no forwarding on.

If I check the pin every 1 uS, won't I miss most of the incoming data? For a 1, the pin supposedly only stays high for 0.6 uS.

At first I was hoping I could use a 328 or even an attiny (hah, so optimistic!), but an ESP32 sounds like a good starting point. It's about the fastest thing I have around. Do you have any guesses for some piece of dedicated hardware (or a circuit) that could do the job instead? I did a little digging and found that the protocol that IR remotes use is very similar, but orders of magnitude slower.

I'm a bit confused by this. There are no "commands": The data stream for both the SK6812 or WS2812 both consist of 24-bits of GRB data only. There's no support for a 'W' channel in that stream. Even if you could make an AVR- or ESP32-based interpreter for the stream where is the W channel info coming from?

Sorry, commands was probably the wrong word. SK6812 supports an additional 8 bits (32 bits total) of data for the W channel on their RGBW LEDs. Like these, from Adafruit. I think it was a bit of an afterthought. WS2812s don't have RGBW options at all as far as I know.

How complicated are the various display modes?

If it is only literally off, all red or all white there are some hacks I can think of.

If it is any kind of animation played out on 13 LEDs I see the point of trying to catch and interpret the data stream.

Do you have a link to these LEDs in action if is isn't just off/red/white all LEDs? I will look myself too.

If it is all 13 off/red/white you could

  1. Use a sensor to just look at one LEC and inform an idea of what is going on… you could even attach a side smart LED by tapping into the data.

Or

  1. The LED chip is available w/o the LEDs. You could again tap into the data stream and use the PWM outputs, low-pass filtered, as inputs to you Arduino.

a7

I was looking at this datasheet for the SK6812 and didn't see any indication of that.

but I see from the link you provided you're referring to the SK6812RGBW.

So if I understand correctly, you're using the SK6812RGBW and using a library such as this that gives the 32-bit RGBW stream and can't use the WS2811 because of those extra 8 bits per LED.

It may still be possible to use one or more WS2811s at the end of the chain. If it expects 24 bits but 32 bits are clocked those last 8 bits are lost out the DO pin but there should still be usable values in the 2811 that you could use.

If you had a chain of two devices, a SK6812RGBW at position one and a WS2811 at position 2 then you'd send RGBW : RGBW and 8 bits would fall out the endo f the WS2811:

        ---- SK6812 ----                 ---- WS2811 ----
76543210765432107654321076543210 76543210765432107654321076543210
GGGGGGGGRRRRRRRRBBBBBBBBWWWWWWWW GGGGGGGGRRRRRRRRBBBBBBBBWWWWWWWW
                                                         ^^^^^^^^ - lost

Maybe you can still use the GRB (or at least the GR) outputs to do something with external LEDs.

Actually, that would work perfectly! Problem solved! :sunglasses:


There are two obvious problems with using interrupts, the first and most serious being the delay involved.

Using a timer interrupt supposes that the data stream synchronises to the timer, or vice versa. Clearly it will not.

The opposite. You cannot have interrupts while you are running a tight loop to sense timings. And in fact, you have to be ready waiting for the next data stream to start, wchih makes it particularly difficult to be doing anything else while you are waiting. You would have to assume that after receiving one data stream, you will have a "break" in which to do whatever you need to do with that data, perhaps with interrupts enabled, and then revert to listening for the next stream with interrupts disabled. You clearly cannot afford to miss the start of the data stream.

Exactly.

I wish there was a SK6812RGBW chip available without the LEDs. The WS2811 is so close but won't work here. Maybe eventually it will come to market.

This would work great if I could reprogram the transmitting board, but it utilizes only the R and W channels. If I use a WS2811, there's no way to capture whether the W LED should be on. I could default to leaving it on at all times unless there is R data, but it would be nice to keep the original functionality of turning it off completely.

I think I understand. luckily I do get an 80 uS break to set a couple pins high or low to drive LEDs, so that shouldn't be a problem. That's the only job I need to do. How would I sense the beginning of the next data stream? Just one pin change interrupt at the beginning to sync and then check the level of the pin every 0.3 or 0.6 uS? Luckily I only need to know if there are any 1s in the R or W byte, so some errors are tolerable. This might make things a bit easier on me.

I don't follow. Will the WS2811 read the SK6812 data stream or not? Does it use a different format of data? How many bytes - three or four - is entirely irrelevant.

Yes, the two are compatible for RGB data. The WS2811 I believe would throw away the last 8 bits of RGBW data. As far as I can gather from the datasheet (pleas correct me if I'm wrong) a second ws2811 in the chain wouldn't catch that data either, because of the reset inbetween. I have some WS2811s that should be arriving in the mail today so I haven't been able to experiment yet.

Update:

Thank you all for guiding me (foolishly) into the light. I have hooked up two WS2811s in series and fed it GRBW data from an Uno. Turns out they are indeed capable of "rolling over" the last 8 bits to the next chip, so no scary low-level AVR or ESP32 code will be necessary :smile:
Good trick, too, for anyone else that may want to "roll their own" RGBW neopixel. Thank you again all for your input!

1 Like

Nope.

You would know that if you had examined the protocol. :sunglasses:

I did (try to) examine the protocol, but apparently misunderstood how it works. I'm not an expert by any means so that's no surprise, which is why I came asking for help from you all :slight_smile:

I'll have another look at the datasheets and see if I can understand where I went wrong.

Yeah i was wondering about what i read in the datasheet. I must have been reading that wrong because it stated LOW 3uS - 9uS and HIGH 6uS-uS, which i already found rather slow.

Reading the application, yes it does. You can drive mosfets from the PWM output, Keep in mind they are open collector outputs, so you have to add pullup resistors and logic level inverters. But i have already build a board like that for RGB, and for RGBW all you need is one extra channel.

Agree to disagree.

That is where i think you are wrong. The datastream is quiet simply a continuous stream of bits, with a break to start the strem

Ah great ! i was sure without trying it (and not the only one)

If only we could set the UART to receive bytes without a startbyte !

It is indeed, quite simply a continuous stream of bits, which happens to be organised into bytes but there is no delimiter, either of individual bytes or byte groups.

Each chip simply strips off as many bytes as it is designed to do, either three or four, and passes the others on verbatim. If it takes three bytes out of a stream which actually represents four byte data, the byte it does not "consume" simply becomes the first byte into the next chip.

You could actually mix RGB and RGBW chips in a string. You would merely have to adapt the byte array used to construct the data to match.

I found some idea on that in Makuna Neopixelbus

class NeoEsp8266UartSpeed800KbpsBase
{
public:
    static const uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element at 800khz speed
    static const uint32_t UartBaud = 3200000; // 800mhz, 4 serial bytes per NeoByte
};

Particularly, the '4 Serial bytes per NeoByte.
The UART is set to 6N1. Sending 1 '6-bit byte' results in 2 bits at 800KKHz (800mhz ?? ah c'mon, you wish ! )
bits 3, 4 & 5 define the logic level of the 1st bit and 1 & 2 of the second bit., and bit 0 should probably be kept 'LOW'
Great trick !!
It should not be so hard to do the same thing for reception. On an AVR i seem to remember that the maximum UART speed is 1MHz, so you would need an ESP to do this.
For sure on an ESP i think it can be done