Read a Data Series - Control LEDs in a Matrix

@JustPete, you’re almost there. If you’re setting data items explicitly, you don’t need the for loop.
What for (i = 0; i < DATA_SIZE; i++) does,is generate the array index list from 0-499 so I don’t have to type all those numbers when I’m just generating synthetic data anyway.

You’ve got the real data all ready to go. Take out everything to do with the for loop, including the loop itself, the data[i]= and the pair of curly braces that start and end it, just leaving your list of data items, and it should work.

Yeah, @JustPete - If you stared at this all day, I feel for ya and want to give you two related code examples. Try out each of these two patterns on your board and see if you understand what’s going on.

Populating an array's values manually

Let’s just work with the first 5 of your pixels for this one.

export var data = array(5) // Set up a 5-bin container

// Fill it with some data
data[0] = 0
data[1] = 1
data[2] = 0
data[3] = .5
data[4] = 1

// Notice there's no beforeRender(). Doni't need it this time. 

export function render(index) {
  if (index >= 5) {  // All pixels after the fifth are dark
    hsv(0, 0, 0) 
  } else {
    h = data[index] ? 0.0 : 0.66 // Red if this bin is nonzero, Blue if it's zero
    s = 1
    v = data[index] ? 1 :  .3    // Full brightness if this bin is nonzero, 30% otherwise
    hsv(h, s, v)
  }
}

Output:
Pixelblaze 2021-03-10 21-32-59


For loop

If you follow that, here’s how we can use the for loop to populate an array. You don’t really need an array to make this pattern, but let’s try anyway. Make the perfect squares (0, 1, 4, 9, 16, 25, 36, 49, etc) bright.

Pixelblaze 2021-03-10 21-29-11

var brightnesses = array(pixelCount)

// Loop through all the bins, setting the brightness for each pixel
for (i = 0; i < pixelCount; i++) { 
  // If there's no decimal part to the square root, 
  //  it's a perfect square (0,1,4,9,16,25,etc);
  //  set it's brightness to be full
  if (frac(sqrt(i)) == 0) {
    brightnesses[i] = 1
  } else {
    brightnesses[i] = .1
  }
}

export function render(index) {
  h = index/pixelCount // Rainbow
  s = 1
  v = brightnesses[index]
  hsv(h, s, v)
}

Hope this helps demystify arrays.

Blockquote

This is what “stuck” me more or less, thinking I had to “feed” the data in from inside beforeRender… when, after all it apparently is a block of data stored somewhere else. I suppose that is what is meant by “initialize”.

Pattern is now working, slowing it way down has effectively illustrated the visual and where I need to go next.

Thanks for your patience and input!

Thanks so much! I’ve looked at quite a bit of info on arrays and know the applications and forms are many… so, simplifying examples are just SO key at the front end. Really helpful!

Blockquote

Per my comment to @zranger1 above, this was quite a grand ol’ sticking point. Like, I was trying to see in the mind’s eye just how the data are accessed/employed in the code.

So that gets to looping… that example you provide using this tool

And

is just brilliant and solid, a great mechanism/tool in the bag.

As for looping with an explicitly defined set of array values, it seems to loop “automatically”?

By the way those 500 values and their format were pretty easily arranged in excel, and represent about half of the complete dataset I hope to run in the code. Question: Is this a computationally costly method or is there something better/faster? I am getting about 36 FPS on a v2.25 running a 16x16 matrix of 2812B.

Getting and setting values in static arrays are pretty fast. Why don’t you post your full code as it stands now (you can skip the middle 498 values!) and we should be able to speed it up.

Yes, for loops by there very nature will repeat themselves automatically. To expand on what zranger1 was explaining, the loop stuff goes like this:

for (SETUP INITIAL CONDITIONS;
KEEP LOOPING IF THIS IS TRUE;
RUN THIS BETWEEN EVERY ITERATION)
{ code to run repeatedly }

There’s actually very little computation going on – on my nearly identical PB2 16x16 setup, it runs at between 85 and 90 fps.

Check to make sure your data array initialization is outside any function – particularly that it isn’t being done inside your beforeRender() or render() functions. The data array is effectively a big database that lives outside your rendering loop. Once you’ve set an array value, it stays set unless you explicitly change it elsewhere in the pattern. So you don’t need to set the values on every frame.

Also performance is invariant with respect to the number of data items – to use 1000 items, just set the variable DATA_SIZE to 1000 and paste your new dataset into the pattern.

Here’s the code… looks pretty familiar, doesn’t it !!!

var frameTime = 10000;

var pixelInterval = 0; // interval (in pixels) between lit pixels. Calculated in code.

export function hsvPickerBlue(h,s,v) {
hBlue = h; sBlue = s; vBlue = v
}

export function hsvPickerRed(h,s,v) {
hRed = h; sRed = s; vRed = v
}

var DATA_SIZE = 500;

var data = array(DATA_SIZE); // storage for our input data stream FILLS ALL 500 with a number of pixels lit red

// export var data

var data_index = 0; // array index of “active” data item

var next_lit_pixel = 0; // position of next pixel to be lit

var holdTime = 8; // how long we display each data array entry // UNSURE OF AFFECT !!!
export var holdTime
// initialize
data [0] = 138 ;
data[1] = 137 ;
data[2] = 136 ;
data[3] = 136 ;

data[497] = 67 ;
data[498] = 68 ;
data[499] = 68 ;

// }

export function sliderSpeed(v) {
// holdTime = (v * 80) / DATA_SIZE;
holdTime = v * 500 // Modified hold time for longer dwell
}

export function beforeRender(delta) {
frameTime += delta;
next_lit_pixel = 0;

if (frameTime > holdTime) {
pixelInterval = pixelCount/data[data_index];
data_index = (data_index+1) % DATA_SIZE;
frameTime = 0;
}
}

export function render(index) {

if (pixelInterval && (index >= next_lit_pixel)) {
next_lit_pixel += pixelInterval;
hsv(hRed,sRed,vRed);
} else {
hsv(hBlue,sBlue,vBlue);
}
}

Examples of array usage I was poring over include sparks, spectomatrix optim, perlin simplex noise (not available say my notes), blinkfade, and spectro kalidastrip. And there may be others.

Syntax such as the following, (from @zranger1’s above (barely modified by me in any practical sense)) gets me thinking as to how it is working… I might have figured it out but might not -

A quick narrative on this use of the % operator would be absolutely great! That is, if I have not gone over time already…

% is the modulo operator. It’s used to get the remainder of a division operation.
10 % 5 returns 0
11 % 5 returns 1
12 % 5 returns 2
13 % 5 returns 3
and so on.

The range of the results will be {0,1,2,3,4} no matter what number you stick on the left side of the operation. So it’s commonly used as a handy and efficient way of restricting a number to a range.

In the array example above, I want to increment data_index to move it to the next item, and make sure it stays within the bounds of the data array – the range 0-DATA_SIZE. Modulo arithmentic is the simplest, fastest way to do this. You’ll see this sort of construct very frequently.

The other common use of % is retrieving the fractional portion of a number n with the expression n % 1.

Also, I ran the code you posted, with the 500 item data set from a few messages up. On my PB2, with a 16x16 WS2812 matrix (256 pixels), it runs at a fairly constant 87 fps. This is about what I’d expect. Is yours still running slowly?

Edit: Oh – just realized, you probably need to change the LED type in your PB’s settings to “Buffered (x2 rate) WS2812/Neopixel”. This greatly helps performance as the number of pixels goes up!

1 Like

@jeff This one is straight-forward. Very simple. Very nice. In this case the array is just a container for data. Thanks.

Quote=“jeff, post:13, topic:1055”]

Populating an array’s values manually

Let’s just work with the first 5 of your pixels for this one.

export var data = array(5) // Set up a 5-bin container

[/quote]

On the other hand,

gave me a “fract” unrecognized error. (Displays a nice rainbow, tho) so I modified the code - But it does not work…

var brightnesses = array(pixelCount)
var saturation = array(pixelCount)
export var brightnesses // Watcher
export var saturation // Watcher

for (i = 0; i < pixelCount; i++) {

if (sqrt((i) -floor (i)) == 0) {    // "if (frac(sqrt(i)) == 0) { > unrecognized, 
                                    // so re-wrote. But it does not work.
  
brightnesses[i] = 0.2 
  saturation[i] = 1  
}

else {

brightnesses[i] = 0
saturation[i] = 0      

}
}

export function render(index) {
h = 0 // Red for easy diagnostics - index/pixelCount = Rainbow
s = saturation[index]
v = brightnesses[index]

hsv(h, s, v)
}
Getting there with arrays. They are a many splendored thing!

Yes! Super nice. Great for defining a range. But, what if, for example, index < DATA_SIZE (which it seems to be).

This sped things up to about 90 FPS.
But then, why and when to use buffering and why not use it always. Perhaps just as you say, “improves performance as the number of pixels goes up.”

Side Note: I had messed with data speed but that just blanked everything! Didn’t think to change the pixel type.

Thanks!

My mistake - frac() is only available on v3.

Something equivalent on v2 is:

if (floor(sqrt(i)) == sqrt(i)) {

No problem, there’s lots to keep track of! Glad we got it sorted-out.

So, for really big arrays could we feed the values into PB from a websocket or some other off-board vehicle? Asking for a friend

Yes, you can feed in as much data as you like through the websockets API. Take a look at this thread for an example that sends about 1k of data per frame at a rate of 20fps :

That IS awesome!

So I am on the right page, a “frame” is one full render of the index, yes?

Question: About how much data would be required to send over one three digit number and one four digit number?

I know… so many questions???

Each digit is an byte (could be less, depends on what you’re doing…)

So call it 7bytes of data? Tiny.

Cool! Tiny is good. I’ve got two pretty long strings of basically parallel data to work with so I’m running up against what I understand is a 20,000 character limit in PB-code space. Not sure if this is carried over to the v3. Thus looking at running it in through a websocket.