Using FastLED lib trying to do animation (image jumping around)

All,

I'm not new to the forum, I rarely post but I do read the forums a lot, I have found very helpful information here many many times. I've build a large LED matrix, it's 14x150, 2100 LEDs. I have fonts working, add my own, I can update test using USBHost with USB keyboard. I switched to Arduino Due to get more memory. I'm now trying to do very basic animation, basically have a image move across the matrix in 10 LED jumps. The logic works, using printf's to check. Now I've added in the code to dynamically update the matrix. And of course it does not work. I wanted to check that the I'm building the data correct and tried printing out the Array and got 0 "zero's". I have the movement code commented out, first need to be able to display the image. I had to remove some of the Array to fit into size, see attached file.

Any suggestions ?

Thanks :smiley:

/* Arduino 256 RGB LEDs Matrix Animation Frame 
 * Using WS2812 LED Strips
 
Created by Yvan / https://Brainy-Bits.com

This code is in the public domain...

You can: copy it, use it, modify it, share it or just plain ignore it!
Thx!

*/
/*  
 *   4H Inventors  Serial Addressable LED project 2019.  Arduino Due, USB Host
 *   
*/

#include <avr/pgmspace.h>  // Needed to store stuff in Flash using PROGMEM
#include "FastLED.h"       // Fastled library to control the LEDs

// How many leds are connected?
//#define NUM_LEDS 300
#define NUM_LEDS 2100

//int NUM_LEDS = 2100;
// To pad the front and back of the images
int Front_pad = 20;
int Rear_pad = 95;
int Row_count = 0;
int Row_led = 0;
int Size_image = 35;
int Image_jump = 0;
int Orig_front = 20;
int Orig_rear = 95;

// Define the Data Pin
//#define DATA_PIN 3  // Connected to the data pin of the first LED strip
#define DATA_PIN 5

// Define the array of leds
CRGB leds[NUM_LEDS];

const long rabbit2[] PROGMEM =
{
0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000
};

// the setup routine runs once when you press reset:
void setup() {
  FastLED.addLeds<NEOPIXEL,DATA_PIN>(leds, NUM_LEDS);  // Init of the Fastled library
  //FastLED.setBrightness(15); // orig
  FastLED.setBrightness(125); //150
  Serial.begin (9600);
}

// the loop routine runs over and over again forever:
void loop() {
///*
FastLED.clear();
int Row_count = 1;
//for (Image_jump = 0; (Image_jump + 20) < 150; Image_jump +10) {
for(int i = 0; i < NUM_LEDS; i++) {
  Row_count = i /150;  //  get the number of LEDs for a row
  Row_led = i % 150;   //  get LED number for a given row
  Serial.print(" i = ");
  Serial.println(i);
  Serial.print("Row_count = ");
  Serial.println(Row_count);
  if ( (i/150) % 2 == 0){   // even "row" of LEDs
    Serial.println("even row");
    if ( Row_led >= Front_pad && Row_led < (Front_pad + Size_image) ) {
      Serial.println("between 20 and 95");
       leds[i] = pgm_read_dword(&(rabbit2[i]));
    } else if ( Row_led < Front_pad) {
      Serial.println("padding the Front  Even");
      leds[i] = 0x000000;
    } else {
      Serial.println("padding the Rear   Even");
      leds[i] = 0x000000;
    }
  } else {             // odd row
    Serial.println("odd row");
    if ( Row_led >= Front_pad && Row_led < Rear_pad ) {
      Serial.println("Z  between 20 and 95");
       leds[i] = pgm_read_dword(&(rabbit2[i]));
    } else if ( Row_led < Front_pad) {
      Serial.println("Row padding the Front  Odd");
      leds[i] = 0x000000;
    } else {
      Serial.println("Row padding the Rear   Odd");
      leds[i] = 0x000000;
    }
  }
}  // second for loop

//============
 for( int k = 0; k < NUM_LEDS; k++) {
    Serial.println(" dumping Array ");
    Serial.println(leds[k]);
 }
//============================= 
/*
  Serial.println("");
  Serial.print("Image_jump = ");
  Serial.println(Image_jump);
  if ((Image_jump + 20) < 150) {
    Image_jump = Image_jump + 10;
    Front_pad = Front_pad + Image_jump;
    Rear_pad = Rear_pad + Image_jump;
  } else {
      Image_jump = 0;   
      Front_pad = Orig_front;
      Rear_pad = Orig_rear; 
  }
   Serial.print("Front_pad = ");
   Serial.println(Front_pad);
   Serial.print("Rear_pad = ");
   Serial.println(Rear_pad);
   Serial.print("New Image_jump = ");
   Serial.println(Image_jump);
   */  // end of movement code
  FastLED.show();
  delay(4000);
//}  //First for loop
//*/


}

test-animation.ino (8.21 KB)

leds[i] = pgm_read_dword(&(rabbit2[i]));

You will have some problems with that line, since i is going to exceed the size of the rabbit2 array.

I made considerable changes to your sketch, but see if you can follow the logic for copying the rabbit image into the led matrix:

#include <avr/pgmspace.h>  // Needed to store stuff in Flash using PROGMEM
#include "FastLED.h"       // Fastled library to control the LEDs

// How many leds are connected?
//#define NUM_LEDS 300
#define NUM_LEDS 2100

//int NUM_LEDS = 2100;
// To pad the front and back of the images
int Front_pad = 20;
int Rear_pad = 95;
int Row_count = 0;
int Row_led = 0;
int Size_image = 35;
int Image_jump = 0;
int Orig_front = 20;
int Orig_rear = 95;

// Define the Data Pin
//#define DATA_PIN 3  // Connected to the data pin of the first LED strip
#define DATA_PIN 5

// Define the array of leds
CRGB leds[NUM_LEDS];

const long rabbit2[] PROGMEM = {
  0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
  0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
  0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
  0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
  0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
  0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
  0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
  0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x3f51b5, 0x000000, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
  0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
  0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x000000, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
  0x000000, 0x000000, 0x000000, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
  0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x000000,
  0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x000000, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
  0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x3f51b5, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000
};

// the setup routine runs once when you press reset:
void setup() {
  FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS); // Init of the Fastled library
  //FastLED.setBrightness(15); // orig
  FastLED.setBrightness(125); //150
  Serial.begin (9600);
}

// the loop routine runs over and over again forever:
void loop() {
  byte display_height = 14;
  byte display_width = 150;
  byte image_height = 14;
  byte image_width = (sizeof(rabbit2) / sizeof(rabbit2[0])) / image_height;
  
  for (int image_origin = 0; image_origin < (display_width - image_width); image_origin += 10) {
    FastLED.clear();
    for (byte image_row = 0; image_row < image_height; image_row++) {
      for (byte image_col = 0; image_col < image_width; image_col++) {
        if ((image_row % 2) == 0) {
          leds[(image_row * display_width + image_origin + image_col)] = pgm_read_dword(&(rabbit2[image_row * image_width + image_col]));
        } else {
          leds[(image_row * display_width + display_width - (image_origin + image_col) - 1)] = pgm_read_dword(&(rabbit2[image_row * image_width + image_width - image_col - 1]));
        }
      }
    }
    
    Serial.println(" dumping Array ");
    for (int k = 0; k < display_height; k++) {
      for (int l = 0; l < display_width; l++) {
        if (k % 2 == 0) {
          Serial.print( (leds[k * display_width + l] == (CRGB)0x000000) ? " " : "X");
        } else {
          Serial.print( (leds[k * display_width + display_width - l - 1] == (CRGB)0x000000) ? " " : "X");
        }
      }
      Serial.println();
    }
    
    FastLED.show();
    delay(4000);
  }
}

i agree with david hence the declaration should beconst unsigned long rabbit2[NUM_LEDS] PROGMEM =
and be aware, 0x000000 = 0x0 = 0, save yourself some typing !
and maybe just get the pointer like thisleds[i] = pgm_read_dword(rabbit2+i*4);

i think you are reading dwords from the location of the pointer to the array btw. pgm_read_dword(&(rabbit2[image_row * image_width + image_col])); the & should not be there i think !? (i am just thinking but i got the progmem thing working without it.)

Deva_Rishi:

pgm_read_dword(&(rabbit2[image_row * image_width + image_col]));

the & should not be there i think !? (i am just thinking but i got the progmem thing working without it.)

Can't seem to get it to work here without the &, but I may be doing something wrong.

I notice that I made the print statements much too complicated, didn't realize reading the leds[] array returns a 0 or 1 instead of the actual brightness levels of the LEDs.

const unsigned long rabbit2[NUM_LEDS] PROGMEM =

That would not work, rabbit2 is much smaller than the complete LED display, 490 pixels if I recall correctly.

Personally, I would store the image as a two-dimensional array of bytes, for large images that would save 25% of the storage space, and easy enough to convert the 6-digit hex numbers to groups of three 2-digit hex numbers with a good text editor.

Thanks for the suggestions. I didn't state the problem clearly. I have the code working using, "pgm_read_dword but it requires that I build the entire array. I've included working code with full array. I build image 14x35. Then I had to manually pad the front and the back so it would fit into 14x150. Also my LEDs are installed using Z pattern so gets fun keeping track of that when doing it manually. I wrote the first program so that I could simply store the array 14x35 then I could pad the array in the code, and I could adjust the amount I pad the front and back to get the image to move around (very primitive animation). I've stepped through the first program and all the boundaries are working, but the Array is not being loaded. Stuck on that part.

Thanks again

animation_full_array.ino (23.1 KB)

I notice that I made the print statements much too complicated, didn't realize reading the leds[] array returns a 0 or 1 instead of the actual brightness levels of the LEDs.

they return the brighness level, but as a 3 byte struct. I do recall there being an issue with comparing, (something about CRGB.r & CRGB.g & CRGB.b ) check out the library and do some tests in a smaller test sketch to confirm. Actually regarding the rows and columns agree, but easiest would be to store the image in a CRGB array as well.
Regardless of all this, i think your matrix is going to have a bit of a wavy display when you move an image 2100 leds take quite some time to address and the timing between the first and the last is going to be visible i think.

for(int i = 0; i < NUM_LEDS; i++) {
    leds[i] = pgm_read_dword(&(rabbit1[i]));
  }

You can't use i for indexing the rabbit1 array, because it doesn't have the same number of elements as the leds array.

You don't need to copy all NUM_LEDS elements anyway, since you are clearing the display each time, just copy over the rabbit1 array to the portion of the leds array that it will occupy.

I plan on moving the image in 10 LED steps at a time, with a buffer/end-stop of 15 on either end. I can use i for indexing. I posted working code that shows this working. It will iterate through NUM_LEDs (2100), the FastLED library takes care of handling the Z pattern (left - right, then right-left etc). I have the image structure built in the Array that I define. The second code has the entire array, where I manually pad the image on the front and back, this is very very tedious. I design the image using, Pixilart - Free online pixel art drawing tool then download the file in png format. I wrote a python program that reads this in and converts to Z pattern or not (flag). This will output the array in the format I need. I build the image array in 14x35 size. The LED matrix has 14 rows, with 150 LED (5m LED strip cut in half). I want to display the image and then after a short delay, ~4sec, move it over 10 LEDs, or show another image. I think Deva_Rishi is on the right path with the CRGB structure. I'm going to try and build a new array and not define it as CRGB

CRGB leds[NUM_LEDS];

Then will dump this new array confirm it's what it should be. If this checks out I will try to copy the new array into "CRGB leds[NUM_LEDS];" array. There is something about the CRGB structure that I do not understand. I will also look at the CRGB Library again.

Thanks

Yeah looks like you'll manage, i did realize after writing the reply that the progmem library doesn't have a command to read a 3-byte variable but i guess you could read 1 byte 3 times (or create that command and add it to the progmem library ) and assign it to CRGB.r CRGB.g & CRGB.b

Deva_Rishi:
Yeah looks like you'll manage, i did realize after writing the reply that the progmem library doesn't have a command to read a 3-byte variable but i guess you could read 1 byte 3 times (or create that command and add it to the progmem library ) and assign it to CRGB.r CRGB.g & CRGB.b

There is a memcpy_P command that will copy as many bytes as you tell it to - if the array is in CRGB format you could even copy a complete row of pixels at once.

All,

I got everything working. The problem was self inflicted. In the end after fixing several things that where wrong with the first code I uploaded, I was able to get this assignment working, " leds = 0x000000;" it was working all along, another problem was keeping this from working. I was also able to get the movement working. This also keeps the size of the array's small, using progmem, but I will probably have several images loaded.
*Wanted to share the working solution. *
Thanks for all the help and suggestions !
test-animation.ino (19.4 KB)