Rearrange Sections in a Multi-Pin Single-Array FASTLED-Setup

Hi,

I am building a music visualizer for my synth-setup. Basically I can get where I want to go, but I have a basic understanding problem. I have been researching for days, but I think I am basically on the wrong track. I would be extremely happy to get some input...

I use 3 WS2812 LED strips connected to pins 3, 5 and 6 of my Arduino UNO.
Please have a look at the attached schematic diagram to see how the LED strips are connected.
The reason why I split and connect the strips this way is because it needs so few cables, it is very tidy and I have to disconnect few connectors when I transport the boxes.

I experimented for days with the Multiple Controller Examples.

Finally I decided to use a single LED array for all LEDS. So I can control the Music Visualizer with the FastLED fill_gradient function. In my final project I have a fill_gradient on each "part" where I can define color and animation direction. Then I control the placement and animation by moving the start and end point of the fill_gradient function. This works like a charm.

Now I want to run a fill_gradient over the whole top (or middle) line for serval Songs (Strip1 Part1 to Strip2 Part1 to Strip3 Part1).

Here is a reduced version of my code to visualize the problem. I have visualized the movement with an i++. A video with the current state is attached. Now the LEDs run over the "Vertical Sections" (see schematic drawing). But I want to let them run over the "Horizontal Sections", one after another. And this without changing the cabling...

#include <FastLED.h>

#define NUM_STRIPS 3
#define NUM_LEDS_PER_STRIP  81
#define NUM_LEDS_PER_PART 27
#define NUM_PARTS 8
#define NUM_LEDS NUM_LEDS_PER_PART * NUM_PARTS

#define BRIGHTNESS  255
#define LED_TYPE  WS2812
#define COLOR_ORDER GRB
CRGB leds[NUM_STRIPS * NUM_LEDS_PER_STRIP];

void setup() {
  // LED LIGHTING SETUP
  FastLED.addLeds<LED_TYPE, 3, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 0, NUM_LEDS_PER_PART * 3);
  FastLED.addLeds<LED_TYPE, 5, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 3, NUM_LEDS_PER_PART * 2);
  FastLED.addLeds<LED_TYPE, 6, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 5, NUM_LEDS_PER_PART * 3);
}

void loop() {
  for (int i = 0; i < NUM_LEDS; i++) {
    fill_gradient(leds, 0, CHSV(0, 255, 255), i, CHSV(160, 255, 255));
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 255);
    delay(10);
  }
}

As I understand the problem it would be a rearrangement of the LEDs in the array. One of many failed attempts was the following. It works for the first horizontal section, but when I uncomment the other Lines the second and third horizontal section in the void setup(), then the pins seems to overwrite themselfs...

#include <FastLED.h>

#define NUM_STRIPS 3
#define NUM_LEDS_PER_STRIP  81
#define NUM_LEDS_PER_PART 27
#define NUM_PARTS 8
#define NUM_LEDS NUM_LEDS_PER_PART * NUM_PARTS

#define BRIGHTNESS  255
#define LED_TYPE  WS2812
#define COLOR_ORDER GRB
CRGB leds[NUM_STRIPS * NUM_LEDS_PER_STRIP];

void setup() {
  // LED LIGHTING SETUP
  FastLED.addLeds<LED_TYPE, 3, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 0, NUM_LEDS_PER_PART);
  FastLED.addLeds<LED_TYPE, 5, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 1, NUM_LEDS_PER_PART);
  FastLED.addLeds<LED_TYPE, 6, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 2, NUM_LEDS_PER_PART);
//  FastLED.addLeds<LED_TYPE, 3, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 3, NUM_LEDS_PER_PART);
//  FastLED.addLeds<LED_TYPE, 5, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 4, NUM_LEDS_PER_PART);
//  FastLED.addLeds<LED_TYPE, 6, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 5, NUM_LEDS_PER_PART);
//  FastLED.addLeds<LED_TYPE, 3, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 6, NUM_LEDS_PER_PART);
//  FastLED.addLeds<LED_TYPE, 6, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 7, NUM_LEDS_PER_PART);
}

void loop() {
  for (int i = 0; i < NUM_LEDS; i++) {
    fill_gradient(leds, 0, CHSV(0, 255, 255), i, CHSV(160, 255, 255));
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 255);
    delay(10);
  }
}

I hope I have clearly described the problem and am very happy about any input.

Thank you very much and greetings JPTHA

Current_State.gif

I looked in the FastLED source code. The description for the addLeds method states the 4th argument is "number of leds for this controller object". I believe what is happening is that each time you are adding for each LED strip the 4th argument tells it there are only 27 LEDS for that controller, hence only accessing the first 27 LEDs per controller.

I will study it a little more when I have time but I'm pretty sure that is what is happening. Now we have to find a solution...

Hey ToddL, thanks a lot for your answer! This is a good point, i have tried to increase the number of LEDs every time I add to a strip/pin. Unfortunately this did not work. In the file "Update_01.gif" you can see the result of this code:

#include <FastLED.h>

#define NUM_STRIPS 3
#define NUM_LEDS_PER_STRIP  81
#define NUM_LEDS_PER_PART 27
#define NUM_PARTS 8
#define NUM_LEDS NUM_LEDS_PER_PART * NUM_PARTS

#define BRIGHTNESS  255
#define LED_TYPE  WS2812
#define COLOR_ORDER GRB
CRGB leds[NUM_STRIPS * NUM_LEDS_PER_STRIP];

void setup() {
  // LED LIGHTING SETUP
  FastLED.addLeds<LED_TYPE, 3, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 0, NUM_LEDS_PER_PART * 1);
  FastLED.addLeds<LED_TYPE, 5, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 1, NUM_LEDS_PER_PART * 1);
  FastLED.addLeds<LED_TYPE, 6, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 2, NUM_LEDS_PER_PART * 1);
  FastLED.addLeds<LED_TYPE, 3, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 3, NUM_LEDS_PER_PART * 2);
  FastLED.addLeds<LED_TYPE, 5, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 4, NUM_LEDS_PER_PART * 2);
  FastLED.addLeds<LED_TYPE, 6, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 5, NUM_LEDS_PER_PART * 2);
//  FastLED.addLeds<LED_TYPE, 3, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 6, NUM_LEDS_PER_PART * 3);
//  FastLED.addLeds<LED_TYPE, 6, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 7, NUM_LEDS_PER_PART * 3);
}

void loop() {
  for (int i = 0; i < NUM_LEDS; i++) {
    fill_gradient(leds, 0, CHSV(0, 255, 255), i, CHSV(160, 255, 255));
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 255);
    delay(10);
  }
}

And in the file "Update_02.gif" you see the result of the same code, but including the last two lines under "void setup()" (these would control the Horizontal Section 3). There I also have random colors on the "Strip3 Part3" and the order of the Parts is messed up:

#include <FastLED.h>

#define NUM_STRIPS 3
#define NUM_LEDS_PER_STRIP  81
#define NUM_LEDS_PER_PART 27
#define NUM_PARTS 8
#define NUM_LEDS NUM_LEDS_PER_PART * NUM_PARTS

#define BRIGHTNESS  255
#define LED_TYPE  WS2812
#define COLOR_ORDER GRB
CRGB leds[NUM_STRIPS * NUM_LEDS_PER_STRIP];

void setup() {
  // LED LIGHTING SETUP
  FastLED.addLeds<LED_TYPE, 3, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 0, NUM_LEDS_PER_PART * 1);
  FastLED.addLeds<LED_TYPE, 5, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 1, NUM_LEDS_PER_PART * 1);
  FastLED.addLeds<LED_TYPE, 6, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 2, NUM_LEDS_PER_PART * 1);
  FastLED.addLeds<LED_TYPE, 3, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 3, NUM_LEDS_PER_PART * 2);
  FastLED.addLeds<LED_TYPE, 5, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 4, NUM_LEDS_PER_PART * 2);
  FastLED.addLeds<LED_TYPE, 6, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 5, NUM_LEDS_PER_PART * 2);
  FastLED.addLeds<LED_TYPE, 3, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 6, NUM_LEDS_PER_PART * 3);
  FastLED.addLeds<LED_TYPE, 6, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 7, NUM_LEDS_PER_PART * 3);
}

void loop() {
  for (int i = 0; i < NUM_LEDS; i++) {
    fill_gradient(leds, 0, CHSV(0, 255, 255), i, CHSV(160, 255, 255));
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 255); 
    delay(10);
  }
}

Update_01.gif

Update_02.gif

jptha, I have an idea for a solution that I'm pretty sure will work. Let me look at it and I'll get back with you later.

The FastLED.addLeds() method associates the RGB values in the leds array with the hardware so that when the FastLED.show() method is called the library can assign the color values to the proper LEDS. The constraint (and a very reasonable one) is that all of the color values associated with that hardware must be contiguous in the leds array. To translate the color operations to your "horizontal" arrangement is a matter of remapping the leds array before executing the FastLED.show() method.

I used your original sketch but I added a "map" array to map indices from a vertical arrangement to a horizontal arrangement. Now you can perform manipulations on the leds array as if your LEDs were horizontal and then call the hRemap() function to remap the leds array from horizontal to the vertical configuration so it will match the wiring.

The nice thing about this solution is you can run over vertical or horizontal sections! For vertical you just skip calling the hRemap() function.

This code compiles but I can't guarantee it works because I cannot test it. Hopefully, if it doesn't you can understand what I am doing and fix it.

#include <FastLED.h>

#define NUM_STRIPS 3
#define NUM_LEDS_PER_STRIP  81
#define NUM_LEDS_PER_PART 27
#define NUM_PARTS 8
#define NUM_LEDS NUM_LEDS_PER_PART * NUM_PARTS

#define BRIGHTNESS  255
#define LED_TYPE  WS2812
#define COLOR_ORDER GRB
CRGB leds[NUM_STRIPS * NUM_LEDS_PER_STRIP];

int hLedsMap[NUM_LEDS];

void setup() {
  // LED LIGHTING SETUP
  FastLED.addLeds<LED_TYPE, 3, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 0, NUM_LEDS_PER_PART * 3);
  FastLED.addLeds<LED_TYPE, 5, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 3, NUM_LEDS_PER_PART * 2);
  FastLED.addLeds<LED_TYPE, 6, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 5, NUM_LEDS_PER_PART * 3);

  // Create a horizontal index map
  for (int idx = 0; idx < NUM_LEDS_PER_PART; idx++)
  {
    hLedsMap[idx] = idx;
    hLedsMap[NUM_LEDS_PER_PART + idx] = NUM_LEDS_PER_PART*3 + idx;
    hLedsMap[NUM_LEDS_PER_PART*2 + idx] = NUM_LEDS_PER_PART*5 + idx;
    hLedsMap[NUM_LEDS_PER_PART*3 + idx] = NUM_LEDS_PER_PART*1 + idx;
    hLedsMap[NUM_LEDS_PER_PART*4 + idx] = NUM_LEDS_PER_PART*4 + idx;
    hLedsMap[NUM_LEDS_PER_PART*5 + idx] = NUM_LEDS_PER_PART*6 + idx;
    hLedsMap[NUM_LEDS_PER_PART*6 + idx] = NUM_LEDS_PER_PART*2 + idx;
    hLedsMap[NUM_LEDS_PER_PART*7 + idx] = NUM_LEDS_PER_PART*7 + idx;
  } 
}

void loop() {
  for (int i = 0; i < NUM_LEDS; i++) {
    fill_gradient(leds, 0, CHSV(0, 255, 255), i, CHSV(160, 255, 255));
    hRemap(); // Remap so gradient works horizontally
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 255);
    delay(10);
  }
}

void hRemap() {
  CRGB tempLeds[NUM_STRIPS * NUM_LEDS_PER_STRIP];
  // Save off the leds array so we can remap it
  memcpy (tempLeds, leds, sizeof(leds));

  //
  // tempLeds is now the "horizontal" LED color map; however, each
  // strip is mounted vertically.  Therefore we need to translate from 
  // the "horizontal" LED color map to the "vertical" color map so
  // color manipulations (such as gradient) follow a horizontal 
  // pattern
  //

  for (int i = 0; i < NUM_LEDS; i++) {
    leds[hLedsMap[i]] = tempLeds[i];
  }
}

Kind of expensive memory-wise for an Uno. Each CRGB takes 3 bytes. So:

Statically defined:
leds[] -- 27 * 8 * 3 = 648 bytes
hLedsMap[] -- 27 * 8 * 2 = 432 bytes

When hRemap() is running, on the stack:
tempLeds[] -- 27 * 8 * 3 = 648 bytes

Total while hRemap() is running -- 1728 bytes

gfvalvo:
Kind of expensive memory-wise for an Uno. Each CRGB takes 3 bytes. So:

Statically defined:
leds[] -- 27 * 8 * 3 = 648 bytes
hLedsMap[] -- 27 * 8 * 2 = 432 bytes

When hRemap() is running, on the stack:
tempLeds[] -- 27 * 8 * 3 = 648 bytes

Total while hRemap() is running -- 1728 bytes

There is a more efficient way but it is not as readable. I will work that up if I have time.

I'd write a custom version of the fill_gradient() function (it's not that complicated) that works over several non-contiguous sections of the led array.

Here is a much more efficient version and it allows you to use all utility color functions without rewriting them.

#include <FastLED.h>

#define NUM_STRIPS 3
#define NUM_LEDS_PER_STRIP  81
#define NUM_LEDS_PER_PART 27
#define NUM_PARTS 8
#define NUM_LEDS NUM_LEDS_PER_PART * NUM_PARTS

#define BRIGHTNESS  255
#define LED_TYPE  WS2812
#define COLOR_ORDER GRB
CRGB leds[NUM_STRIPS * NUM_LEDS_PER_STRIP];

void setup() {
  // LED LIGHTING SETUP
  FastLED.addLeds<LED_TYPE, 3, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 0, NUM_LEDS_PER_PART * 3);
  FastLED.addLeds<LED_TYPE, 5, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 3, NUM_LEDS_PER_PART * 2);
  FastLED.addLeds<LED_TYPE, 6, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 5, NUM_LEDS_PER_PART * 3);
}

void loop() {
  for (int i = 0; i < NUM_LEDS; i++) {
    fill_gradient(leds, 0, CHSV(0, 255, 255), i, CHSV(160, 255, 255));
    hRemap(); // Remap so gradient works horizontally
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 255);
    delay(10);
  }
}

void hRemap() {
  CRGB tempLeds[NUM_LEDS_PER_PART];

  // Horiz  -> Vert (HW) mapping
  // ------------------------------
  // Part 0 -> Part 0
  // Part 1 -> Part 3
  // Part 2 -> Part 5
  // Part 3 -> Part 1
  // Part 4 -> Part 4
  // Part 5 -> Part 6
  // Part 6 -> Part 2
  // Part 7 -> Part 7
  
  // Part 0 -> Part 0 
  //memcpy(leds, leds, NUM_LEDS_PER_PART*sizeof(CRGB));

  // Part 3 -> Part 1
  memcpy(tempLeds, &leds[NUM_LEDS_PER_PART], NUM_LEDS_PER_PART*sizeof(CRGB));
  memcpy(&leds[NUM_LEDS_PER_PART], &leds[NUM_LEDS_PER_PART*3], NUM_LEDS_PER_PART*sizeof(CRGB));

  // Part 1 -> Part 3
  memcpy(&leds[NUM_LEDS_PER_PART*3], tempLeds, NUM_LEDS_PER_PART*sizeof(CRGB));
  
  // Part 4 -> Part 4
  //memcpy(&leds[NUM_LEDS_PER_PART*4], &leds[NUM_LEDS_PER_PART*4], NUM_LEDS_PER_PART*sizeof(CRGB));

  // Part 6 -> Part 2
  memcpy(tempLeds, &leds[NUM_LEDS_PER_PART*2], NUM_LEDS_PER_PART*sizeof(CRGB));
  memcpy(&leds[NUM_LEDS_PER_PART*2], &leds[NUM_LEDS_PER_PART*6], NUM_LEDS_PER_PART*sizeof(CRGB));

  // Part 5 -> Part 6
  memcpy(&leds[NUM_LEDS_PER_PART*6], &leds[NUM_LEDS_PER_PART*5], NUM_LEDS_PER_PART*sizeof(CRGB));

  // Part 2 -> Part 5
  memcpy(&leds[NUM_LEDS_PER_PART*5], tempLeds, NUM_LEDS_PER_PART*sizeof(CRGB));

  // Part 7 -> Part 7
  //memcpy(&leds[NUM_LEDS_PER_PART*7], &leds[NUM_LEDS_PER_PART*7], NUM_LEDS_PER_PART*sizeof(CRGB));
}

@ToddL - This is exactly what I was looking for and did not know that such possibilities even exist.
Your 2nd, more efficient solution works just perfectly (see GIF). So now I need some time to understand your concept in detail.
And the ability to switch between horizontal and vertical mapping is of course a really cool feature…
Thank you very much for your commitment, I really appreciate it. And especially your detailed explanation!

@gfvalvo thanks also to you for the input! I will have a closer look at the fill_gradient function to understand what exactly happens in it.

Have a nice day, thanks and greetings, JPTHA.

Update_03.gif

jptha:
@ToddL - This is exactly what I was looking for and did not know that such possibilities even exist.
Your 2nd, more efficient solution works just perfectly (see GIF). So now I need some time to understand your concept in detail.
And the ability to switch between horizontal and vertical mapping is of course a really cool feature…
Thank you very much for your commitment, I really appreciate it. And especially your detailed explanation!

@gfvalvo thanks also to you for the input! I will have a closer look at the fill_gradient function to understand what exactly happens in it.

Have a nice day, thanks and greetings, JPTHA.

Glad it worked out! Let me know if you have any questions.

Hello again,

After a few tests I have now noticed that the hRemap() function does not work in all cases - or I am using the function incorrectly...

What if I work with e.g. a Knight Rider Light or any other function that requires FastLED.show() twice?

I packed the code versions in functions, they could be uncommented in the loop section. The corresponding animated GIFs of these 3 Functions are attached here.

  • KnightRiderOriginal(); Runs over part 1, 2 & 3 of strip 1.

  • KnightRiderFake(); Is the desired result in ugly Code. Runs over part 1 of strip 1, then part 1 of strip 2, then part 1 of strip 3.

  • KnightRiderhRemap(); Is the failed try to hRemap KnightRider...

The way I understand it, is that memcpy() copies and don't cut out. So in the KnightRiderhRemap() where hRemap() and FastLED.show() occur twice, the wrong values ​​are copied somehow... But I really can't wrap my head around this one...

I would be very happy about suggestions for solutions and food for thought, thank you and greetings, jptha

#include <FastLED.h>

// LED LIGHTING SETUP
#define NUM_STRIPS 3
#define NUM_LEDS_PER_STRIP  81
#define NUM_LEDS_PER_PART 27
#define NUM_PARTS 8
#define NUM_LEDS NUM_LEDS_PER_PART * NUM_PARTS

// STRIP SETUP
#define BRIGHTNESS  255
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB
CRGB leds[NUM_STRIPS * NUM_LEDS_PER_STRIP];

void setup() {

  // LED LIGHTING SETUP
  delay( 300 ); // power-up safety delay

  FastLED.addLeds<LED_TYPE, 1, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 0, NUM_LEDS_PER_PART * 3);
  FastLED.addLeds<LED_TYPE, 8, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 3, NUM_LEDS_PER_PART * 2);
  FastLED.addLeds<LED_TYPE, 10, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 5, NUM_LEDS_PER_PART * 3);
  FastLED.setBrightness(BRIGHTNESS);

  // CLEAR LEDS
  fill_solid(leds, NUM_LEDS, CRGB::Black); // some led strips are all on at power on, so let's power them off at boot
  FastLED.show();
}

void KnightRiderOriginal(){
  for(int c = 0; c < ((NUM_LEDS_PER_PART*3)-1); c++) {
    leds[c] = CHSV(255, 255, 255);
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 64);
    delay(15);
    }
  for(int i = ((NUM_LEDS_PER_PART*3)-1); i >= 0; i--) {
    leds[i] = CHSV(160, 255, 255);
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 64);
    delay(15);
    }
}    

void KnightRiderhRemap(){
  for(int c = 0; c < ((NUM_LEDS_PER_PART*3)-1); c++) {
    leds[c] = CHSV(255, 255, 255);
    hRemap();
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 64);
    delay(15);
    }
  for(int i = ((NUM_LEDS_PER_PART*3)-1); i >= 0; i--) {
    leds[i] = CHSV(160, 255, 255);
    hRemap();
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 64);
    delay(15);
    }
}    

void KnightRiderFake(){
  for(int c = 0; c < ((NUM_LEDS_PER_PART*1)-1); c++) {
    leds[c] = CHSV(255, 255, 255);
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 64);
    delay(15);
    }
  for(int c = NUM_LEDS_PER_PART*3; c < ((NUM_LEDS_PER_PART*4)-1); c++) {
    leds[c] = CHSV(255, 255, 255);
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 64);
    delay(15);
    }
  for(int c = NUM_LEDS_PER_PART*5; c < ((NUM_LEDS_PER_PART*6)-1); c++) {
    leds[c] = CHSV(255, 255, 255);
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 64);
    delay(15);
    }
  for(int i = ((NUM_LEDS_PER_PART*6)-1); i >= NUM_LEDS_PER_PART*5; i--) {
    leds[i] = CHSV(160, 255, 255);
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 64);
    delay(15);
    }
  for(int i = ((NUM_LEDS_PER_PART*4)-1); i >= NUM_LEDS_PER_PART*3; i--) {
    leds[i] = CHSV(160, 255, 255);
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 64);
    delay(15);
    }
  for(int i = ((NUM_LEDS_PER_PART*1)-1); i >= 0; i--) {
    leds[i] = CHSV(160, 255, 255);
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 64);
    delay(15);
    }
}    

void hRemap() {
  CRGB tempLeds[NUM_LEDS_PER_PART];

  // Horiz  -> Vert (HW) mapping
  // ------------------------------
  // Part 0 -> Part 0
  // Part 1 -> Part 3
  // Part 2 -> Part 5
  // Part 3 -> Part 1
  // Part 4 -> Part 4
  // Part 5 -> Part 6
  // Part 6 -> Part 2
  // Part 7 -> Part 7

  // Part 0 -> Part 0
  //memcpy(leds, leds, NUM_LEDS_PER_PART*sizeof(CRGB));

  // Part 3 -> Part 1
  memcpy(tempLeds, &leds[NUM_LEDS_PER_PART], NUM_LEDS_PER_PART * sizeof(CRGB));
  memcpy(&leds[NUM_LEDS_PER_PART], &leds[NUM_LEDS_PER_PART * 3], NUM_LEDS_PER_PART * sizeof(CRGB));

  // Part 1 -> Part 3
  memcpy(&leds[NUM_LEDS_PER_PART * 3], tempLeds, NUM_LEDS_PER_PART * sizeof(CRGB));

  // Part 4 -> Part 4
  //memcpy(&leds[NUM_LEDS_PER_PART*4], &leds[NUM_LEDS_PER_PART*4], NUM_LEDS_PER_PART*sizeof(CRGB));

  // Part 6 -> Part 2
  memcpy(tempLeds, &leds[NUM_LEDS_PER_PART * 2], NUM_LEDS_PER_PART * sizeof(CRGB));
  memcpy(&leds[NUM_LEDS_PER_PART * 2], &leds[NUM_LEDS_PER_PART * 6], NUM_LEDS_PER_PART * sizeof(CRGB));

  // Part 5 -> Part 6
  memcpy(&leds[NUM_LEDS_PER_PART * 6], &leds[NUM_LEDS_PER_PART * 5], NUM_LEDS_PER_PART * sizeof(CRGB));

  // Part 2 -> Part 5
  memcpy(&leds[NUM_LEDS_PER_PART * 5], tempLeds, NUM_LEDS_PER_PART * sizeof(CRGB));

  // Part 7 -> Part 7
  //memcpy(&leds[NUM_LEDS_PER_PART*7], &leds[NUM_LEDS_PER_PART*7], NUM_LEDS_PER_PART*sizeof(CRGB));
}

void loop() {

//KnightRiderOriginal();
KnightRiderhRemap();
//KnightRiderFake();

}

KnightRiderhRemap.gif

KnightRiderOriginal.gif

KnightRiderFake.gif

Update:

It has nothing to do with the fact that the FastLED.show() function is needed twice. I think it's because the fadeToBlackBy() stands after the FastLED.show(). With fadeToBlackBy(leds, NUM_LEDS, 64) I have the memcpy() errors. With fadeToBlackBy(leds, NUM_LEDS, 255) I have no memcpy() errors problems, but also no tail in the Animation.
Putting fadeToBlackBy() at the beginning of the for loop doesn't work either.

How do you solve such problems?

void KnightRiderhRemap(){
  for(int c = 0; c < ((NUM_LEDS_PER_PART*3)-1); c++) {
    leds[c] = CHSV(255, 255, 255);
    hRemap();
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 255);
    delay(15);
    }
  for(int i = ((NUM_LEDS_PER_PART*3)-1); i >= 0; i--) {
    leds[i] = CHSV(160, 255, 255);
    hRemap();
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 255);
    delay(15);
    }
}

KnightRiderhRemapFadeToBlackBy255.gif

OK, all over again. I have an array with 8 parts of 27 LEDs. So 1 array with 216 LEDs.
I want to map the vertical layout to a horizontal layout.

Horiz -> Vert mapping

Part 0 -> Part 0
Part 1 -> Part 3
Part 2 -> Part 6
Part 3 -> Part 1
Part 4 -> Part 4
Part 5 -> Part 2
Part 6 -> Part 5
Part 7 -> Part 7

Please look at the drawing in the attachment. -> Layout.jpg

Toddl came up with the idea of memcpy. That would actually be an ingenious solution. Only I don't make them work.

I'm currently testing the hRemap with the KnightRiderLight. hRemap must be in front of the FastLed.show. Since I use fadeToBlackBy, an hRemap must also be inserted after fadeToBlackBy. Did I interpret that correctly?

This code works as long as fadeToBlackBy(leds, NUM_LEDS, 255); is set. But so you have no trail at the moving light point.
As soon as I turn on the trail fadeToBlackBy(leds, NUM_LEDS, 16); I have memcpy errors on part 2, 5 & 6. Pls. check the attached GIF -> KnightRiderhRemapRightButFailed.gif

#include <FastLED.h>

// LED LIGHTING SETUP
#define NUM_STRIPS 3
#define NUM_LEDS_PER_STRIP  81
#define NUM_LEDS_PER_PART 27
#define NUM_PARTS 8
#define NUM_LEDS NUM_LEDS_PER_PART * NUM_PARTS

// STRIP SETUP
#define BRIGHTNESS  255
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB
CRGB leds[NUM_STRIPS * NUM_LEDS_PER_STRIP];

void setup() {

  // LED LIGHTING SETUP
  delay( 300 ); // power-up safety delay

  FastLED.addLeds<LED_TYPE, 1, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 0, NUM_LEDS_PER_PART * 3);
  FastLED.addLeds<LED_TYPE, 8, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 3, NUM_LEDS_PER_PART * 2);
  FastLED.addLeds<LED_TYPE, 10, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 5, NUM_LEDS_PER_PART * 3);
  FastLED.setBrightness(BRIGHTNESS);

  // CLEAR LEDS
  fill_solid(leds, NUM_LEDS, CRGB::Black);
  FastLED.show();
 
}

void KnightRiderhRemap(){
  for(int c = 0; c < NUM_LEDS; c++) {
    leds[c] = CHSV(255, 255, 255);
    hRemap();
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 16);
    hRemap();
    delay(30);
    }
  for(int i = NUM_LEDS; i >= 0; i--) {
    leds[i] = CHSV(160, 255, 255);
    hRemap();
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 16);
    hRemap();
    delay(30);
    }
}

void hRemap() {
  CRGB tempLeds[NUM_LEDS_PER_PART];

  // Horiz  -> Vert (HW) mapping
  // ------------------------------
  // Part 0 -> Part 0
  // Part 1 -> Part 3
  // Part 2 -> Part 6
  // Part 3 -> Part 1
  // Part 4 -> Part 4
  // Part 5 -> Part 2
  // Part 6 -> Part 5
  // Part 7 -> Part 7

  //  memcpy (destination, source, size);

  // Part 1 -> Temp 
  memcpy(tempLeds, &leds[NUM_LEDS_PER_PART], NUM_LEDS_PER_PART*sizeof(CRGB));
  // Part 3 -> Part 1 
  memcpy(&leds[NUM_LEDS_PER_PART], &leds[NUM_LEDS_PER_PART*3], NUM_LEDS_PER_PART*sizeof(CRGB));
  // Temp -> Part 3 
  memcpy(&leds[NUM_LEDS_PER_PART*3], tempLeds, NUM_LEDS_PER_PART*sizeof(CRGB));

  // Part 2 -> Temp 
  memcpy(tempLeds, &leds[NUM_LEDS_PER_PART*2], NUM_LEDS_PER_PART*sizeof(CRGB));
  // Part 6 -> Part 2 
  memcpy(&leds[NUM_LEDS_PER_PART*2], &leds[NUM_LEDS_PER_PART*6], NUM_LEDS_PER_PART*sizeof(CRGB));
  // Part 5 -> Part 6 
  memcpy(&leds[NUM_LEDS_PER_PART*6], &leds[NUM_LEDS_PER_PART*5], NUM_LEDS_PER_PART*sizeof(CRGB));
  // Temp -> Part 5 
  memcpy(&leds[NUM_LEDS_PER_PART*5], tempLeds, NUM_LEDS_PER_PART*sizeof(CRGB));
}

void loop() {
  KnightRiderhRemap();
}

I did a lot of tests and one Version that works is the following hRemap adjustment.
I mean part 5 & 6 are interchanged, so it is no Solution, but there are no memcpy errors.
Check the GIF -> KnightRiderhRemapKindof.gif
As soon as I switch parts 5 & 6 with the memcpy method, I have the same memcpy errors again.

void hRemap() {
  CRGB tempLeds[NUM_LEDS_PER_PART];

  // Horiz  -> Vert (HW) mapping
  // ------------------------------
  // Part 0 -> Part 0
  // Part 1 -> Part 3
  // Part 2 -> Part 6
  // Part 3 -> Part 1
  // Part 4 -> Part 4
  // Part 5 -> Part 2
  // Part 6 -> Part 5
  // Part 7 -> Part 7

  //  memcpy (destination, source, size);

  // Part 1 -> Temp 
  memcpy(tempLeds, &leds[NUM_LEDS_PER_PART], NUM_LEDS_PER_PART * sizeof(CRGB));
  // Part 3 -> Part 1 
  memcpy(&leds[NUM_LEDS_PER_PART], &leds[NUM_LEDS_PER_PART * 3], NUM_LEDS_PER_PART * sizeof(CRGB));
  // Temp -> Part 3 
  memcpy(&leds[NUM_LEDS_PER_PART * 3], tempLeds, NUM_LEDS_PER_PART * sizeof(CRGB));


  // Part 2 -> Temp 
  memcpy(tempLeds, &leds[NUM_LEDS_PER_PART * 2], NUM_LEDS_PER_PART * sizeof(CRGB));
  // Part 6 -> Part 2 
  memcpy(&leds[NUM_LEDS_PER_PART * 2], &leds[NUM_LEDS_PER_PART * 6], NUM_LEDS_PER_PART * sizeof(CRGB));
  // Temp -> Part 6 
  memcpy(&leds[NUM_LEDS_PER_PART * 6], tempLeds, NUM_LEDS_PER_PART * sizeof(CRGB));
}

Can anyone explain to me what i'm doing wrong? And why the 2nd version of the hRemap does not give an ivalid Result and the first, actually correct version of the hRemap have this strange dublicated behavior on Part 2, 5 & 6? This just doesn't make sense to me...

And if someone knows another way, i would be happy for suggestions... I mean, I don't need to be able to switch between mappings. Actually I just need a simple remap of CRGB leds [];

Layout.jpg

KnightRiderhRemapRightButFailed.gif

KnightRiderhRemapKindof.gif

To end this: It works now... I can't really explain what the problem was, but I did the following:

I had 3 parts on pin 1, 2 parts on pin 8 and 3 parts on pin 10. Now I have expanded the setup with another "virtual" part on pin 8 and expanded the array with these 27 LEDs.
This works with my visualizer and I noticed that it actually works better with the gap than without. And especially: All calculations are a lot easier this way.

If someone found out where my memcpy errors came from, I would like to know.

Here is my final code, maybe that will help someone in the future...

#include <FastLED.h>

// LED LIGHTING SETUP
#define NUM_STRIPS 3
#define NUM_LEDS_PER_STRIP  81
#define NUM_LEDS_PER_PART 27
#define NUM_PARTS 9
#define NUM_LEDS NUM_LEDS_PER_PART * NUM_PARTS

// STRIP SETUP
#define BRIGHTNESS  255
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB
CRGB leds[NUM_STRIPS * NUM_LEDS_PER_STRIP];

void setup() {
  // LED LIGHTING SETUP
  delay( 300 ); 
  FastLED.addLeds<LED_TYPE, 1, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 0, NUM_LEDS_PER_PART * 3);
  FastLED.addLeds<LED_TYPE, 8, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 3, NUM_LEDS_PER_PART * 3);
  FastLED.addLeds<LED_TYPE, 10, COLOR_ORDER>(leds, NUM_LEDS_PER_PART * 6, NUM_LEDS_PER_PART * 3);
  FastLED.setBrightness(BRIGHTNESS);

  // CLEAR LEDS
  fill_solid(leds, NUM_LEDS, CRGB::Black);
  FastLED.show();
}

void KnightRiderhRemap(){
  for(int c = 0; c < ((NUM_LEDS_PER_PART*9)-1); c++) {
    leds[c] = CHSV(255, 255, 255);
    hRemap();
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 16);
    hRemap();
    delay(0);
    }
  for(int i = ((NUM_LEDS_PER_PART*9)-1); i >= 0; i--) {
    leds[i] = CHSV(160, 255, 255);
    hRemap();
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 16);
    hRemap();
    delay(0);
    }
}

void hRemap() {
  CRGB tempLeds[NUM_LEDS_PER_PART];

  //  memcpy (destination, source, size);

  memmove(tempLeds, &leds[NUM_LEDS_PER_PART], NUM_LEDS_PER_PART*sizeof(CRGB));
  memmove(&leds[NUM_LEDS_PER_PART], &leds[NUM_LEDS_PER_PART*3], NUM_LEDS_PER_PART*sizeof(CRGB));
  memmove(&leds[NUM_LEDS_PER_PART*3], tempLeds, NUM_LEDS_PER_PART*sizeof(CRGB));

  memmove(tempLeds, &leds[NUM_LEDS_PER_PART*6], NUM_LEDS_PER_PART*sizeof(CRGB));
  memmove(&leds[NUM_LEDS_PER_PART*6], &leds[NUM_LEDS_PER_PART*2], NUM_LEDS_PER_PART*sizeof(CRGB));
  memmove(&leds[NUM_LEDS_PER_PART*2], tempLeds, NUM_LEDS_PER_PART*sizeof(CRGB));

  memmove(tempLeds, &leds[NUM_LEDS_PER_PART*7], NUM_LEDS_PER_PART*sizeof(CRGB));
  memmove(&leds[NUM_LEDS_PER_PART*7], &leds[NUM_LEDS_PER_PART*5], NUM_LEDS_PER_PART*sizeof(CRGB));
  memmove(&leds[NUM_LEDS_PER_PART*5], tempLeds, NUM_LEDS_PER_PART*sizeof(CRGB));
}

void loop() {
  KnightRiderhRemap();  
}