WS2812 IC To "Drop" LED Nodes

What would be the best IC/component (in red) that I could use in my configuration below. I have a WS2812 data-stream (controlling a large number of LEDs), and I would like to be able to "trim/drop" (not sure the best terminology) the input data-signal to remove a defined number of "nodes/LEDs" for the output signal.

I know this can be done with an Arduino Uno, as I inherited a sketch that accomplishes this task (and even worked with an ATMEGA328P and a few supporting components).

Essentially in the example below, each IC is dropping the data for the first 5 LEDs, then passing the remaining data-stream to its output.

The basic requirements:

  • It needs to be as small and cost-effective as possible--as I need to replicate "trim/drop" many dozen times (hence an Arduino dev board is too large and cost-prohibitive). I even think the ATMEGA328P was overkill.

Is there a better/optimal IC that I could use instead of an Arduino/ATMEGA that could accomplish this trivial(?) task?

Are you always stripping 5, or is it variable? If variable, how does each node know how many to remove?

It will always be the same number. 5 in the example here….but eventually around 20 in the “final” project. But it will be a fixed number for all

Right. So in that case, the same code will be used for each, and you don't need pins to configure the device, for example - just an in pin, an out pin, and some code that hustles, using inline assembly or other low-overhead coding to allow direct pin-to-pin signal interpretation and replication.
Sounds like something an ATTiny-85, or even simpler, could do with the right coding.
I've written the output code to write to a WS2812D string, but I've not tried to read/interpret/write. My expectation is, you'd likely have to use an interrupt driven structure, although if the device is doing NOTHING else but look at it's input pin, that might not be necessary.
Good luck!

Exactly--the same basic code would be used in each node.

Good to hear that something like an ATTiny-85 would work. The code I inherited was all port-manipulation (meaning I have zero clue how to interpret/modify)--but I do recall something about it being "interrupt-driven." Any ideas on what areas I should start looking at to make this work (or adapt my current code for an ATTiny)?

I think thats the to go here.

This surprises me. I would like to see the code.

Ok so let me get this right, You want the datastream to be chopping off a number of pixel data pulses from the start. Yes i think even an AtTiny13 can do it. (much cheaper than an AtTiny85.)

It is a bit of a tricky concept to get working properly though.

Basically the stream is sent, first pixel first, and the counter is reset when a break time is detected. (for ws2812b's that is 300us)

Well if you show us that sketch that could be helpful !

The nature of the WS2812 signal is that every pixel contains 24-bits and every bit has a LOW to HIGH and HIGH to LOW cycle and the duration of this cycle defines whether the bit sent is defined as 0 or 1.

what is required is to detect the 300us break, and then to count the transitions from LOW to HIGH and once the correct number of transitions has been recorded, the counter can stop and the pin-state can be forwarded.

Now this forwarding needs to be done with consistent timing. I am not sure is the AtTiny will manage this accurately enough, but another option could be to simply use a Nor-Gate to do the forwarding & switching.

Some experiments will be required to do this. I am curious though if i can make it work, and so therefore motivated to do some of that.

I would say so too. Just for break detection already, but for pulse counting for sure that should be the way to go.

Yes, although i think just using an ISR to forward the pin Change, probably already should suffice.

a small state machine is required though i think with the break detection icw the forwarding. The pulse counting will work even at 800KHz, it is the forwarding i am more concerned about, hence the thought of using 2x a NOR-Gate

Yes that would be great, i have never done this though, how does that work ?

Actually the simplest and cheapest solution would be a thin data cable back from the last LED back to the main chain, and if required a TTL-chip to amplify / square-off the signal.

I have done this for a client this summer who needed 8 sticks from the center to light an octagonal object.
The sticks were over 1.5 meters in length but their base was close together at the center of the object.
The final pixel was simply forwarded to the first of the next stick and that is what seems to me the best solution. A well protected strand of thin wire. Can you elaborate as to why this is not what would work for you ?
Not that i am not interested in looking for another solution using more complex methods, but i can't quite imagine a scenario in which what you need is actually required.

2 Likes

Since every bit contains the same high- low transition, it's "just" a matter of counting those, and resending all the data after the first n, where n = 8 x 3 x 5, in the example. On that basis, an interrupt may not be required.

No i don't think it will be required but for me personally i think it would be easier to program it that way. The pulses are rather quick, so polling is probably out of the question. And there is the matter of break detection, which probably should be done with polling.
And then there is the matter of forwarding... I am mostly curious about methods for doing this without any external hardware.

Polling will be better than interrupts, I think, because this is a single task. Interrupts require overhead to respond, and there's nothing to interrupt anyway.
A tight loop monitoring the one input and executing deterministic paths every time should do it. I'm tight for time today, but we'll see what transpires. Coffee breaks may be at risk.

You're probably right, if i think about it. Flute practice and work can hardly be interrupted for it over here, i am just thinking that the constant testing of a condition may slow things down, but maybe that is just not correct as a thought. AtTiny13 should be the go though for the money, although that won't beat a thin strand of wire !

1 Like

Agreed that this is a viable option (and I've gone this route too). A few issues is the distances involved (thus requiring the chip to amplify as you mentioned). However, the biggest drawback is that all the "downstream" LEDs are reliant on all the upstream LEDs working properly....and all being of the uniform length...and not being unplugged (thus breaking the loop).

That's been enough of a hassle...prompting me to explore this other config.

For a project like this, what specs would I be interested in when comparing the ATTiny13 vs 85 (or other similar IC)?

Here's the original code I inherited. Given that's is all port manipulation, it's very difficult for me to decipher exactly what is happening (luckily, there are comments that help).

This code does work for me on an Arduino UNO, and I'm able to set/adjust the number of skipped nodes. Also, this code "trims" two separate outputs (each can be adjusted independently)--nice to have, but not needed in what I'm trying here (I just need to be able to trim a single output.


void setup() 
{
 
/*OCR1B = (number of pixels attached to output 1* 24) -1
*For the example of 50 pixels
*OCR1B = 50 * 24 = 1200 -1 = 1199.
*OCR1A = ((number of pixels attached to output 1 + the number of pixels attached to output 2) * 24) -1
*For the example of 50 & 75 pixels
*OCR1A = 50 + 75 = 125 * 24 = 3000 -1 = 2999
 */
   //********* PORTS *********
  DDRD &= ~B00101000; // set PD3 & PD5 as inputs.
  DDRB |= B00000110; // set PB1 & PB2 as outputs.
  PORTB |= B00000110; // set PB1 & PB2 high.
  //********* INTERRUPT 1 *********
  EICRA = B00000000; // clear EICRA register.
  EICRA |= (1 << ISC10) | (1 << ISC11); // set interrupt on rising.
  EIMSK |= (1 << INT1); // enable interrupt.
  OCR2A = 250; // set OCR2A (count to) value about 250uS inc int.
  //********* CLEAR TIMER INTERRUPTS *********
  TIMSK0 = B00000000; // clear any T0 interrupts.
  TIMSK1 = B00000000; // clear any T1 interrupts.
  TIMSK2 = B00000000; // clear any T2 interrupts.
  //********* TIMER 2 *********
  TIMSK2 = (1 << OCIE2A); // enable interrupt.
  TCCR2A = B00000000; // clear TCCR1A register.
  //********* TIMER 1 *********
  TCCR1A = B00000000; // clear TCCR1A register.
  TCCR1A |= (1 << COM1B1) | (1 << COM1A1); // set OCR1A\B WGM to set low on compare match.
  TCCR1B = B00000000; // clear TCCR1B register.
  TCCR1B |= (1 << CS11) | (1 << CS12) | (1 << WGM12); // set ext input, CTC mode &  falling trigger.
  TCNT1 = 0; // set TCNT1 counter register to zero.
  OCR1B = 479; // set OCR1B (1st count to value).
  OCR1A = 239; // set OCR1A (2nd count to value).
}
//********* ISR interrupt 1 ********
ISR (INT1_vect) {
  TCCR2B |= (1 << CS21); // start timer 2 & set prescale to 1/8 clock.
  TCNT2 = 0; // reset timer 2 counter to zero.
  OCR2A = 250; // set OCR2A (count to value) about 250uS including interrupt time.
}
//********* ISR timer 2 *********
ISR (TIMER2_COMPA_vect) {
  TCCR2B = B00000000; // stop timer 2.
  TCNT1 = 0; // reset timer 1 counter to zero.
  TCCR1A |= (1 << COM1B0) | (1 << COM1A0); // timer 1 WGM to set high on compare match.
  TCCR1C |= (1 << FOC1A) | (1 << FOC1B); // timer 1 force compare match (set outputs low).
  TCCR1A &= ~(1 << COM1B0) & ~(1 << COM1A0); // timer 1 WGM to set low on compare match.
}
void loop() {}

Appreciate all your help, and this will certainly be a learning exercise for me (because I have no idea what those words mean)

Why not use a blacked-out 5xWS2812 strand as "IC"?

Or if you took a plain strand, Y-ed off the N drop leds every N leds of the strand, and then masked the shift-register-like buffer behind/in something opaque, you'd get the same effect.

1 Like

I think this sketch uses PD5 as input for timer1 to count the pulses for the WS2812.
With the compare match register OC1A its signaling on PB1 to forward the pulses.
(Same for OC1B and PB2)

PD3 is used to start the timer2 for detecting the break(reset) signal.
When timer2 expires the pulse count in timer1 is reset to zero.

I expect some external logic gates here (DOUT1 = PB1 && DIN). Do you have the schematic for it?

It uses space, produces heat and doubles the required hardware...

1 Like

The WS2812 data input is GPIO18 (same signal input twice). 1B is the 1st output, 1C is the 2nd output.

If this project drops down to an ATTiny, I would presume I'd still need similar ports for uploading (bootloader and sketch?), and would still also need an external crystal?

Hmm... where is GPIO18 coming from?
Where are 1B and 1C going to?

I expect some 7402 NOR logic gate for
PD3 = PD5 = !DIN
DOUT1 = !(PB1 || !DIN)
DOUT2 = !(PB2 || !DIN)