I'm running 150 WS2812b's with an ATX/PC power supply.  I'm attempting to add a

(Ed McAndrew) #1

I’m running 150 WS2812b’s with an ATX/PC power supply. I’m attempting to add a block of code that will enumerate values from my arrSerialValues[] array, average them and write them out to the analog pins on my Arduino Uno.

Everything works great within my sketch until I add in the averaging code. Once I do that, all of my WS2812b leds blink red/blue continuously. I don’t believe that it’s power related, they appear to be functioning at full brightness. It seems more like memory, data corruption or a clock issue perhaps? I’ve tested the code with a simulator and the values are all valid. It’s just that the addition of the code causes loss of control to the digital WS2812b side.

I’ve tried several different ways of doing the averaging, each way ends with the same red/blue blinking.

I can’t get to pastebin from here right now, but I can post the entire sketch later if needed.

void loop() {
word tmpR = 0; word tmpG = 0; word tmpB = 0;
int low = 0; int high = 0;
word arraysize = 5;
for (byte i = 0; i < sizeof(arrPrefix); ++i) {
waitLoop: while (!Serial.available()) ;;
if (arrPrefix[i] == Serial.read()) { continue; }
i = 0;
goto waitLoop;
for (byte i = 0; i < NUMLEDS; i++) { // read the transmitted data
for (byte j = 0; j < 3; j++) {
arrSerialValues[i][j] = Serial.read();
leds[i] = CRGB(arrSerialValues[i][0], arrSerialValues[i][1], arrSerialValues[i][2]);
// from here*
for (int i = 0; i < 2; i++) {
tmpR = 0; tmpG = 0; tmpB = 0;
switch (i) {
case 0:
low = 5; high = low + ((arraysize * 2) + 1);
case 1:
low = 91; high = low + ((arraysize * 2) + 1);
for(int k = low; k < high; k = k+2){
tmpR += arrSerialValues[k][0];
tmpG += arrSerialValues[k][1];
tmpB += arrSerialValues[k][2];
tmpR /= (high - low) / 2;
tmpG /= (high - low) / 2;
tmpB /= (high - low) / 2;
tmpR += (tmpR * ANALOGSCALE) / 100; if (tmpR > 255) { tmpR = 255; }
tmpG += (tmpG * ANALOGSCALE) / 100; if (tmpG > 255) { tmpG = 255; }
tmpB += (tmpB * ANALOGSCALE) / 100; if (tmpB > 255) { tmpB = 255; }
switch (i) {
case 0:
writeRightSide(tmpR, tmpG, tmpB);
case 1:
writeLeftSide(tmpR, tmpG, tmpB);
// to here*

(Adam Sharp) #2

Just a query:

You do know that while .Show is running that interrupts are disabled and your Serial functions will miss/loose incoming data don’t you?

This will corrupt your incoming serial data stream if more that one serial (or whatever the hardware buffer size is on your processor) character is received during the time that .Show is executing.

(Daniel Garcia) #3

Adam is right (and this has been a long running frustration of mine). Luckily, I am currently testing a solution for this on arm based devices (teensy 3.1, due, rfduino). Avr devices are proving to be a bit of a bastard with this though.

Also, what device are you doing this on? You’re taking up 450 bytes with the led array, and another 450 bytes with your arrSerialValues arrays.

(Also, what do your writeRightSide/writeLeftSide functions look like?)

What is the relationship between arraysize and the size of arrSerialValues?

I might need to see the whole sketch to track what’s going on better.

(Ed McAndrew) #4

Thank you very much for your time on this.

I’m REALLY new to this (my first project). I’m not certain that I’m following Adam. I know about interrupts and serial i/o (to an extent), but it’s been about a decade since I’ve done any serial coms (or C/C++ for that matter).

This picture hopefully depicts what I’m trying to do. I’m trying to get an average of 5 LEDs (every other one) from either side to write out. I’m using an Arduino Uno…

This first paste is a working script that I’m currently using. It grabs 1 pixel to write out. The problem with this is that in a fast paced sequence, my outter zones (for lack of a better term) are all over the place due to the resolution of 1 pixel.


This second paste is the script that I’m working on.

(Ed McAndrew) #5

Sorry, my reply got truncated.

(second paste)

(Daniel Garcia) #6

For laughs, have you run some simpler test programs to make sure that the hardware and such is all up and running?

(Ed McAndrew) #7

Thanks again Daniel,

I’ve run just about all the demos in both FastLED and FastLED 2.1 libraries and am using the first sketch right now (watching Sons of Anarchy). Everything works perfect until I add in the averaging code.

I’d be glad to run more though if you have anything in mind.

(Ed McAndrew) #8

Here is some video…

Good Sketch (paste 1):

Bad Sketch (paste 2):

(Ed McAndrew) #9

Interestingly enough, after watching the problematic video, the averaging routine seems to be working as the analog strips are blinking at the same rate and color as the WS2812b strip.

(Adam Sharp) #10

Here goes and simplified to your situation:

Interrupts cause an ‘interruption’ to your normal computer program flow. In your case, your program is whizzing around doing whatever you have programmed it to.

These interrupts are already pre-programmed into the Arduino environment (like serial), or ones that you have programmed yourself.

When serial data starts to come into the processor, the special serial hardware carefully decodes the signal to form the serial character. This all happens in the hardware and nothing to do with programming. However, once this character has been decoded and received completely, i.e. one serial character, then something has to be done with it BEFORE the next one starts to arrive or the first character will be overwritten and lost as there is nowhere for it to go.

Now your running program can either keep looking to see if this character is ready, which takes up a lot of time to do this ‘polling’ or, in the case of Arduinos, you can use an interrupt.

When the serial character is ready, the hardware tells the processor it has a serial character ready. This telling is called an ‘interrupt’. When the processor gets an interrupt, it stops whatever it is doing and services the interrupt. In this case, get a character from the serial decoding hardware and put it safely away in memory for later use by your program. Once this is done, the interrupt is complete and the normal program can continue from where it was interrupted. And the hardware can decode the next serial character.

This is a bit like a teacher in a class full of kids, and one raises their hand to ask a question. The teacher stops what they are doing to answer the child’s question and then the teacher resumes whatever they were doing before the interruption.

In computers, interrupts normally have to be handled as quickly as possible and as immediately as possible. You can only handle one interrupt at a time (normally). This is because there are many possible interrupts and each one should be considered very important. So do the ‘interrupt’ as quickly as possible, so that if another interrupt arrives during an interrupt, nothing serious happens and both interrupts are serviced in a timely fashion.

FastLED, during .Show, turns these interrupts off. So it can do its very important stuff with the LEDs. It is so critical that it must not be interrupted until it finishes. The more LEDs you have, the longer this takes FastLED to complete. This means the longer the processor is not ‘seeing’ and ‘doing’ interrupts. In your case, not seeing serial characters arriving and doing something with them.

Bit like the teacher answering their phone and looking away from their class. This kids still have questions that want to be answered, but the teacher is ignoring them. And when the teacher has finished their phone call, the kids have either put their hand down (the interrupt has gone away), or the child has forgotten what they wanted to ask (the interrupt was processed too late).

Might make your understanding of interrupts and FastLED better or worse. Sorry it is very difficult to explain interrupts without seeing your body language and if you are understanding the explanation. There are plenty of interrupt articles on the net that might explain things better for you.

(Ed McAndrew) #11

Thank you very much for the explanation Adam. I do understand interrupts.

What I didn’t/don’t understand is how it applies to this particular sketch.

Unless your saying that the execution time of my additional averaging routine is taking too long, subsequently affecting time needed to address interrupts.

(Adam Sharp) #12

Ed, I’m glad you understand interrupts, many don’t.

No in this case, what I’m saying is that the FastLED.Show disables interrupts before it starts to address the LEDs (so no serial data can be received), then updates the LEDs, and then re-enables the interrupts. Then your averaging resumes, along with receiving serial data, until you call .Show again.

If the time it takes to update all the LEDs takes longer than it takes for the serial hardware to receive two characters, then one or more serial characters will be lost. This is because the serial hardware can not interrupt the processor to ‘receive’ any serial characters.

Without knowing what baud rate, inter-character spacing and character size you are using for the serial, and the number and type of LEDs, it is not possible to calculate this error window.

The higher the baud rate and the more LEDs you have, the worse this will become, and the more serial characters you may loose.

(Daniel Garcia) #13

That said - this is the reason why boblight does the “prefixing” game - effectively discarding any data that may come in while writing led data (interrupts disabled) and waiting until the next frame comes in. Of course, this only works as long as you can guarantee that the sequence of bytes that will come across in the prefix won’t ever show up

(Adam Sharp) #14

I’ve gone down the 2 CPU route with dual port RAM between them. One blasts away at the LEDs, the other handles all the I/O using FreeRTOS and loading the D/P RAM with display data.

(Ed McAndrew) #15

Thanks again guys!

I did notice a few days back (with an earlier averaging routine) that it appeared to be right on the edge of a timing issue.

If I tried to sample/average 10 LEDs from each side, it immediately “lost control” (for lack of a better term). So I moved down to averaging 5, and the loss (originally) got a little better, then to 3 and everything appeared to work. It worked UNTIL Boblight sent all black. At that point, the tail end of the WS2812b string (about the last 20) lost control.

To me, this seemed like I was trying to do too much in too little time.

Currently, my baud is set at 115200 for 150 LEDs. If I’m reading everything correctly, adjusting my baud might possibly give me more time?

Also, what about staying off the interrupts with noInterrupts() ? Might that help?

I REALLY appreciate you guys taking your time on this.

(Daniel Garcia) #16

Luckily, the interrupt issue is about to go away for ARM. (I just had to solve this problem for someone whose hardware had a 2 CPU setup for this very reason and a, well, let’s just say “less than ideal” plan for moving data between the two of them.)

(Ed McAndrew) #17

Guys, thank you very much! With your insight, I was able to adjust for my interrupt issues. I increased my baud and moved my .show() up above my averaging routine. Everything is working well now and I was even able to increase my averaging sample from 5 pixels per side to all 29.

Wouldn’t have figured out a workaround without your help!

(Adam Sharp) #18

Glad the info helped and thank you for reporting back.