/* Support of multiple LED layouts. Non-panel version used by SR WLED documentation.
By: Sutaburosu
Adapted and testing by: Andrew Tuline
Original code:
https://gist.github.com/sutaburosu/43d46240dc2b05683d1e98872885f8db#file-stream_serial-ino-L34-L55
https://github.com/sutaburosu/LEDpijp/blob/main/XYmatrix.h#L37-L58 (has a fix)
Other references:
https://github.com/marcmerlin/ArduinoOnPc-FastLED-GFX-LEDMatrix
https://github.com/Jorgen-VikingGod/LEDMatrix
https://macetech.github.io/FastLED-XY-Map-Generator/
https://github.com/macetech/FastLED-XY-Map-Generator
https://github.com/marcmerlin/Framebuffer_GFX#custom-xy-mapping-for-irregular-arrays
// Helper functions for a two-dimensional XY matrix of pixels.
//
// XY(x,y) takes x and y coordinates and returns an LED index number,
// for use like this: leds[ XY(x,y) ] == CRGB::Red;
//
// If pixels are driven horizontally at the same end, like below, it's a (ROWMAJOR) layout:
//
// 0 > 1 > 2 > 3 > 4
// |
// .----<----<----<----'
// |
// 5 > 6 > 7 > 8 > 9
// |
// .----<----<----<----'
// |
// 10 > 11 > 12 > 13 > 14
// |
// .----<----<----<----'
// |
// 15 > 16 > 17 > 18 > 19
//
//
// If pixels are driven horizontally and laid out back and forth, it's a (ROWMAJOR | SERPENTINE) layout:
//
// 0 > 1 > 2 > 3 > 4
// |
// |
// 9 < 8 < 7 < 6 < 5
// |
// |
// 10 > 11 > 12 > 13 > 14
// |
// |
// 19 < 18 < 17 < 16 < 15
//
We have 5 orientation bits. The values are:
XY_LAYOUT = SERPENTINE = 16, ROWMAJOR = 8, TRANSPOSE = 4, FLIPMAJOR = 2, FLIPMINOR = 1
SERPENTINE - Select if your layout is serpentine. Otherwise, it's not.
ROWMAJOR - Select if your layout is horizontal. De-select if it's vertical.
FLIPMAJOR - Flip the major axis, ie top to bottom if it's a horizontal layout.
FLIPMINOR - Flip the minor axis, ie left to right if it's a horizonal layout.
TRANSPOSE - Swap the major and the minor axes (otherwise no swap). Not for general usage.
*/
// Here's a list of definitons to test. --------------------------------------------------------------------------------
#define XY_LAYOUT (ROWMAJOR | SERPENTINE) // Serpentine with rows at top left. Wokwi standard. Good!
// #define XY_LAYOUT (ROWMAJOR) // Non-serpentine with rows at top left. Good!
// #define XY_LAYOUT (ROWMAJOR | SERPENTINE | FLIPMAJOR) // Serpentine, leds[0] is top right. Good!
// #define XY_LAYOUT (ROWMAJOR | FLIPMAJOR) // Non-serpentine, leds[0] is top right. Good!
// #define XY_LAYOUT (ROWMAJOR | SERPENTINE | FLIPMINOR) // Serpentine, leds[0] is bottom right. Good!
// #define XY_LAYOUT (ROWMAJOR | FLIPMINOR) // Non-serpentine, leds[0] is bottom right. Good!
//#define XY_LAYOUT (ROWMAJOR | SERPENTINE | FLIPMAJOR | FLIPMINOR) // Serpentine, leds[0] is bottom left. Good!
//#define XY_LAYOUT (ROWMAJOR | FLIPMAJOR | FLIPMINOR) // Non-serpentine, leds[0] is bottom left. Good!
// #define XY_LAYOUT 0 // NOT ROWMAJOR and NOT SERPENTINE. Good!
// #define XY_LAYOUT SERPENTINE // Not ROWMAJOR. Good!
// Rest are untested.
// Have NOT tested when TRANSPOSE is set. // We'll get there.
#include <FastLED.h>
#define LED_PIN 12
const uint8_t matrixWidth = 16;
const uint8_t matrixHeight = 16;
#define NUM_LEDS matrixWidth * matrixHeight
CRGB leds[NUM_LEDS+1]; // The +1 is for the out of bounds LED for any out of bound coordinates.
//const bool matrixSerpentine = true;
enum XY_Layout {
SERPENTINE = 16, ROWMAJOR = 8, TRANSPOSE = 4, FLIPMAJOR = 2, FLIPMINOR = 1
};
void setup() {
Serial.begin(115200);
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(64);
// dolights();
}
void loop() {
dolights();
FastLED.show();
}
void dolights() {
// Raw LED writing to confirm physical layout.
/* leds[0] = CRGB::Red;
leds[1] = CRGB::Green;
leds[2] = CRGB::Blue;
leds[31] = CRGB::Orange;
*/
// Layout based led's.
leds[XY(0, 0)] = CRGB::Red;
leds[XY(1, 0)] = CRGB::Green;
leds[XY(2, 0)] = CRGB::Blue;
leds[XY(0, 1)] = CRGB::Orange;
FastLED.show();
}
// enum XY_Layout {
// SERPENTINE = 16, ROWMAJOR = 8, TRANSPOSE = 4, FLIPMAJOR = 2, FLIPMINOR = 1
// };
// Advanced XY layout supports multiple orientations.
uint16_t XY(uint8_t x, uint8_t y) { // By: Sutaburosu
uint8_t major, minor, sz_major, sz_minor;
if (x >= matrixWidth || y >= matrixHeight)
return NUM_LEDS;
if (XY_LAYOUT & ROWMAJOR)
major = x, minor = y, sz_major = matrixWidth, sz_minor = matrixHeight;
else
major = y, minor = x, sz_major = matrixHeight, sz_minor = matrixWidth;
if (((XY_LAYOUT & FLIPMAJOR) != 0) ^ (((minor & 1) != 0) && ((XY_LAYOUT & SERPENTINE) != 0))) // A line of magic.
major = sz_major - 1 - major;
if (XY_LAYOUT & FLIPMINOR)
minor = sz_minor - 1 - minor;
if (XY_LAYOUT & TRANSPOSE)
return major * (uint16_t) sz_major + minor;
else
return minor * (uint16_t) sz_major + major;
}
////////////////////////////////////////////
// OLD AND REFERENCE CODE BELOW HERE. //
////////////////////////////////////////////
/*
// Basic XY layout doesn't support multiple orientations.
#define XY_LAYOUT (ROWMAJOR | SERPENTINE)
uint16_t OLD_XY( uint8_t x, uint8_t y)
{
uint16_t i;
if( matrixSerpentine == false) {
i = (y * matrixWidth) + x;
}
if( matrixSerpentine == true) {
if( y & 0x01) {
// Odd rows run backwards
uint8_t reverseX = (matrixWidth - 1) - x;
i = (y * matrixWidth) + reverseX;
} else {
// Even rows run forwards
i = (y * matrixWidth) + x;
}
}
return i;
}
*/
/*
// Advanced XY with debug code.
// enum XY_Layout {
// SERPENTINE = 16, ROWMAJOR = 8, TRANSPOSE = 4, FLIPMAJOR = 2, FLIPMINOR = 1
// };
uint16_t XY(uint8_t x, uint8_t y) {
uint8_t major, minor, sz_major, sz_minor;
if (x >= matrixWidth || y >= matrixHeight)
return NUM_LEDS; // Failsafe maximum value is one beyond the matrix.
if (XY_LAYOUT & ROWMAJOR) {
Serial.println("Rowmajor");
major = x, minor = y, sz_major = matrixWidth, sz_minor = matrixHeight; // The STANDARD layout, leds[0] is top left.
} else {
Serial.println("Not Rowmajor");
major = y, minor = x, sz_major = matrixHeight, sz_minor = matrixWidth; // Not sure how to set this. Maybe just use a '0'.
}
if (((XY_LAYOUT & FLIPMAJOR) != 0) ^ (((minor & 1) != 0) && ((XY_LAYOUT & SERPENTINE) != 0))) { // A line of magic.
Serial.println("Flipmajor and Serpentine");
major = sz_major - 1 - major;
}
if (XY_LAYOUT & FLIPMINOR) {
Serial.println("Flipminor");
minor = sz_minor - 1 - minor;
}
if (XY_LAYOUT & TRANSPOSE) {
Serial.println("Transposed");
Serial.println(major *(uint16_t) sz_major + minor);
return major * (uint16_t) sz_major + minor;
} else {
Serial.println("Not Transposed");
Serial.println(minor * (uint16_t) sz_major + major);
return minor * (uint16_t) sz_major + major;
}
}
*/