Update ChainedPanels example, VirtualDisplay Class and Graphic. Thanks Brian and Galaxy-Man for input.

This commit is contained in:
mrfaptastic 2020-08-09 19:13:13 +01:00
parent eab828b837
commit 3ff7155bf0
3 changed files with 259 additions and 108 deletions

View file

@ -2,13 +2,16 @@
#define _ESP32_VIRTUAL_MATRIX_PANEL_I2S_DMA #define _ESP32_VIRTUAL_MATRIX_PANEL_I2S_DMA
/******************************************************************* /*******************************************************************
Contributed by Brian Lough Class contributed by Brian Lough, and expanded by Faptastic.
YouTube: https://www.youtube.com/brianlough YouTube: https://www.youtube.com/brianlough
Tindie: https://www.tindie.com/stores/brianlough/ Tindie: https://www.tindie.com/stores/brianlough/
Twitter: https://twitter.com/witnessmenow Twitter: https://twitter.com/witnessmenow
*******************************************************************/ *******************************************************************/
#include "ESP32-RGB64x32MatrixPanel-I2S-DMA.h" #include "ESP32-RGB64x32MatrixPanel-I2S-DMA.h"
#include <Fonts/FreeSansBold12pt7b.h>
struct VirtualCoords { struct VirtualCoords {
int16_t x; int16_t x;
@ -30,34 +33,40 @@ class VirtualMatrixPanel : public Adafruit_GFX
int16_t module_rows; int16_t module_rows;
int16_t module_cols; int16_t module_cols;
int16_t screenResX; int16_t panelResX;
int16_t screenResY; int16_t panelResY;
RGB64x32MatrixPanel_I2S_DMA *display; RGB64x32MatrixPanel_I2S_DMA *display;
#ifdef USE_GFX_ROOT #ifdef USE_GFX_ROOT
VirtualMatrixPanel(RGB64x32MatrixPanel_I2S_DMA &disp, int vmodule_rows, int vmodule_cols, int screenX, int screenY) VirtualMatrixPanel(RGB64x32MatrixPanel_I2S_DMA &disp, int vmodule_rows, int vmodule_cols, int panelX, int panelY, bool serpentine_chain = true, bool top_down_chain = false)
: GFX(vmodule_cols*screenX, vmodule_rows*screenY) : GFX(vmodule_cols*panelX, vmodule_rows*panelY)
#else #else
VirtualMatrixPanel(RGB64x32MatrixPanel_I2S_DMA &disp, int vmodule_rows, int vmodule_cols, int screenX, int screenY) VirtualMatrixPanel(RGB64x32MatrixPanel_I2S_DMA &disp, int vmodule_rows, int vmodule_cols, int panelX, int panelY, bool serpentine_chain = true, bool top_down_chain = false )
: Adafruit_GFX(vmodule_cols*screenX, vmodule_rows*screenY) : Adafruit_GFX(vmodule_cols*panelX, vmodule_rows*panelY)
#endif #endif
{ {
this->display = &disp; this->display = &disp;
panelResX = panelX;
panelResY = panelY;
module_rows = vmodule_rows; module_rows = vmodule_rows;
module_cols = vmodule_cols; module_cols = vmodule_cols;
screenResX = screenX;
screenResY = screenY;
virtualResX = vmodule_rows*screenY; virtualResY = vmodule_rows*panelY;
virtualResY = vmodule_cols*screenX; virtualResX = vmodule_cols*panelX;
_s_chain_party = serpentine_chain; // serpentine, or 'S' chain?
_chain_top_down= top_down_chain;
} }
VirtualCoords getCoords(int16_t x, int16_t y); VirtualCoords getCoords(int16_t x, int16_t y);
// equivalent methods of the matrix library so it can be just swapped out. // equivalent methods of the matrix library so it can be just swapped out.
virtual void drawPixel(int16_t x, int16_t y, uint16_t color); virtual void drawPixel(int16_t x, int16_t y, uint16_t color);
virtual void fillScreen(uint16_t color); // overwrite adafruit implementation virtual void fillScreen(uint16_t color); // overwrite adafruit implementation
void clearScreen() { void clearScreen() {
fillScreen(0); fillScreen(0);
} }
@ -76,16 +85,54 @@ class VirtualMatrixPanel : public Adafruit_GFX
return display->Color333(r, g, b); return display->Color333(r, g, b);
} }
void drawDisplayTest();
private: private:
VirtualCoords coords; VirtualCoords coords;
bool _s_chain_party = true; // Are we chained? Ain't no party like a...
bool _chain_top_down = false; // is the ESP at the top or bottom of the matrix of devices?
}; // end Class header }; // end Class header
inline VirtualCoords VirtualMatrixPanel::getCoords(int16_t x, int16_t y) { inline VirtualCoords VirtualMatrixPanel::getCoords(int16_t x, int16_t y) {
int16_t xOffset = (y / screenResY) * (module_cols * screenResX);
coords.x = x + xOffset; coords.x = coords.y = 0;
coords.y = y % screenResY;
return coords; if (x < 0 || x > module_cols*panelResX || y < 0 || y > module_rows*panelResY ) {
//Serial.printf("VirtualMatrixPanel::getCoords(): Invalid virtual display coordinate. x,y: %d, %d\r\n", x, y);
return coords;
}
uint8_t row = (y / panelResY) + 1; //a non indexed 0 row number
if( ( _s_chain_party && !_chain_top_down && (row % 2 == 0) ) // serpentine vertically stacked chain starting from bottom row (i.e. ESP closest to ground), upwards
||
( _s_chain_party && _chain_top_down && (row % 2 != 0) ) // serpentine vertically stacked chain starting from the sky downwards
)
{
// First portion gets you to the correct offset for the row you need
// Second portion inverts the x on the row
coords.x = (y / panelResY) * (module_cols * panelResX) + (virtualResX - 1 - x);
// inverts the y the row
coords.y = panelResY - 1 - (y % panelResY);
}
else
{
coords.x = x + (y / panelResY) * (module_cols * panelResX) ;
coords.y = y % panelResY;
}
// Reverse co-ordinates if panel chain from ESP starts from the TOP RIGHT
if (_chain_top_down)
{
coords.x = (this->display->width()-1) - coords.x;
coords.y = (this->display->height()-1) - coords.y;
}
//Serial.print("Mapping to x: "); Serial.print(coords.x, DEC); Serial.print(", y: "); Serial.println(coords.y, DEC);
return coords;
} }
inline void VirtualMatrixPanel::drawPixel(int16_t x, int16_t y, uint16_t color) inline void VirtualMatrixPanel::drawPixel(int16_t x, int16_t y, uint16_t color)
@ -119,6 +166,24 @@ inline void VirtualMatrixPanel::drawPixelRGB24(int16_t x, int16_t y, rgb_24 colo
this->display->drawPixelRGB24(coords.x, coords.y, color); this->display->drawPixelRGB24(coords.x, coords.y, color);
} }
inline void VirtualMatrixPanel::drawDisplayTest()
{
this->display->setFont(&FreeSansBold12pt7b);
this->display->setTextColor(this->display->color565(255, 255, 0));
this->display->setTextSize(1);
for ( int panel = 0; panel < module_cols*module_rows; panel++ ) {
Serial.print("LOOP for module: "); Serial.println(panel, DEC);
int top_left_x = (panel == 0) ? 0:(panel*panelResX);
Serial.print("Stop start x: "); Serial.println(top_left_x, DEC);
this->display->drawRect( top_left_x, 0, panelResX, panelResY, this->display->color565( 0, 255, 0));
this->display->setCursor(panel*panelResX, panelResY-3);
this->display->print((module_cols*module_rows)-panel);
}
}
// need to recreate this one, as it wouldnt work to just map where it starts. // need to recreate this one, as it wouldnt work to just map where it starts.
inline void VirtualMatrixPanel::drawIcon (int *ico, int16_t x, int16_t y, int16_t module_cols, int16_t module_rows) { inline void VirtualMatrixPanel::drawIcon (int *ico, int16_t x, int16_t y, int16_t module_cols, int16_t module_rows) {
int i, j; int i, j;

View file

@ -1,59 +1,40 @@
/******************************************************************* /******************************************************************************
This is the PatternPlasma Demo adopted for use with multiple
displays arranged in a non standard order
What is a non standard order?
When you connect multiple panels together, the library treats the
multiple panels as one big panel arranged horizontally. Arranging
the displays like this would be a standard order.
[ 4 ][ 3 ][ 2 ][ 1 ] (ESP32 is connected to 1)
If you wanted to arrange the displays vertically, or in rows and
columns this example might be able to help.
[ 4 ][ 3 ]
[ 2 ][ 1 ]
It creates a virtual screen that you draw to in the same way you would
the matrix, but it will look after mapping it back to the displays.
----------- -----------
Steps to use Steps to use
----------- -----------
1) In ESP32-RGB64x32MatrixPanel-I2S-DMA.h: 1) In ESP32-RGB64x32MatrixPanel-I2S-DMA.h:
- Set the MATRIX_HEIGHT to be the y resolution of the physical chained panels in a line (if the panels are 32 x 16, set it to be 16) - Set the MATRIX_HEIGHT to be the y resolution of the physical chained
- Set the MATRIX_WIDTH to be the sum of the x resolution of all the physical chained panels (i.e. If you have 4 x (32px w x 16px h) panels, 32x4 = 128) panels in a line (if the panels are 32 x 16, set it to be 16)
- Set the MATRIX_WIDTH to be the sum of the x resolution of all the physical
chained panels (i.e. If you have 4 x (32px w x 16px h) panels, 32x4 = 128)
2) In the sketch: 2) In the sketch (i.e. this example):
- Set values for NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y. There are comments beside them - Set values for NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y.
explaining what they are in more detail. There are comments beside them explaining what they are in more detail.
- Other than where the matrix is defined and matrix.begin in the setup, you should now be using the virtual display - Other than where the matrix is defined and matrix.begin in the setup, you
for everything (drawing pixels, writing text etc). You can do a find and replace of all calls if it's an existing sketch should now be using the virtual display for everything (drawing pixels, writing text etc).
(just make sure you don't replace the definition and the matrix.begin) You can do a find and replace of all calls if it's an existing sketch
- If the sketch makes use of MATRIX_HEIGHT or MATRIX_WIDTH, these will need to be replaced with the width and height (just make sure you don't replace the definition and the matrix.begin)
of your virtual screen. Either make new defines and use that, or you can use virtualDisp.width() or .height() - If the sketch makes use of MATRIX_HEIGHT or MATRIX_WIDTH, these will need to be
replaced with the width and height of your virtual screen.
Either make new defines and use that, or you can use virtualDisp.width() or .height()
Parts: Thanks to:
ESP32 D1 Mini * - https://s.click.aliexpress.com/e/_dSi824B
ESP32 I2S Matrix Shield (From my Tindie) = https://www.tindie.com/products/brianlough/esp32-i2s-matrix-shield/
* * = Affilate * Brian Lough for the original example as raised in this issue:
https://github.com/mrfaptastic/ESP32-RGB64x32MatrixPanel-I2S-DMA/issues/26
If you find what I do useful and would like to support me, YouTube: https://www.youtube.com/brianlough
please consider becoming a sponsor on Github Tindie: https://www.tindie.com/stores/brianlough/
https://github.com/sponsors/witnessmenow/ Twitter: https://twitter.com/witnessmenow
* Galaxy-Man for the kind donation of panels make/test that this is possible:
https://github.com/Galaxy-Man
Written by Brian Lough *****************************************************************************/
YouTube: https://www.youtube.com/brianlough
Tindie: https://www.tindie.com/stores/brianlough/
Twitter: https://twitter.com/witnessmenow
*******************************************************************/
//#define USE_CUSTOM_PINS // uncomment to use custom pins, then provide below //#define USE_CUSTOM_PINS // uncomment to use custom pins, then provide below
@ -75,36 +56,132 @@
#define OE_PIN 13 #define OE_PIN 13
/* Include FastLED, DMA Display (for module driving), and VirtualMatrixPanel /******************************************************************************
* to enable easy painting & graphics for a chain of individual LED modules. * VIRTUAL DISPLAY / MATRIX PANEL CHAINING CONFIGURATION
*/ *
* Note 1: If chaining from the top right to the left, and then S curving down
* then serpentine_chain = true and top_down_chain = true
* (these being the last two parameters of the virtualDisp(...) constructor.
*
* Note 2: If chaining starts from the bottom up, then top_down_chain = false.
*
* Note 3: By default, this library has serpentine_chain = true, that is, every
* second row has the panels 'upside down' (rotated 180), so the output
* pin of the row above is right above the input connector of the next
* row.
Example 1 panel chaining:
+-----------------+-----------------+-------------------+
| 64x32px PANEL 3 | 64x32px PANEL 2 | 64x32px PANEL 1 |
| ------------ <-------- | ------------xx |
| [OUT] | [IN] | [OUT] [IN] | [OUT] [ESP IN] |
+--------|--------+-----------------+-------------------+
| 64x32px|PANEL 4 | 64x32px PANEL 5 | 64x32px PANEL 6 |
| \|/ ----------> | -----> |
| [IN] [OUT] | [IN] [OUT] | [IN] [OUT] |
+-----------------+-----------------+-------------------+
Example 1 configuration:
#define PANEL_RES_X 64 // Number of pixels wide of each INDIVIDUAL panel module.
#define PANEL_RES_Y 32 // Number of pixels tall of each INDIVIDUAL panel module.
#define NUM_ROWS 2 // Number of rows of chained INDIVIDUAL PANELS
#define NUM_COLS 3 // Number of INDIVIDUAL PANELS per ROW
virtualDisp(dma_display, NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, true, true);
= 192x64 px virtual display, with the top left of panel 3 being pixel co-ord (0,0)
==========================================================
Example 2 panel chaining:
+-------------------+
| 64x32px PANEL 1 |
| ----------------- |
| [OUT] [ESP IN] |
+-------------------+
| 64x32px PANEL 2 |
| |
| [IN] [OUT] |
+-------------------+
| 64x32px PANEL 3 |
| |
| [OUT] [IN] |
+-------------------+
| 64x32px PANEL 4 |
| |
| [IN] [OUT] |
+-------------------+
Example 2 configuration:
#define PANEL_RES_X 64 // Number of pixels wide of each INDIVIDUAL panel module.
#define PANEL_RES_Y 32 // Number of pixels tall of each INDIVIDUAL panel module.
#define NUM_ROWS 4 // Number of rows of chained INDIVIDUAL PANELS
#define NUM_COLS 1 // Number of INDIVIDUAL PANELS per ROW
virtualDisp(dma_display, NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, true, true);
virtualDisp(dma_display, NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, true, true);
= 128x64 px virtual display, with the top left of panel 1 being pixel co-ord (0,0)
==========================================================
Example 3 panel chaining (bottom up):
+-----------------+-----------------+
| 64x32px PANEL 4 | 64x32px PANEL 3 |
| <---------- |
| [OUT] [IN] | [OUT] [in] |
+-----------------+-----------------+
| 64x32px PANEL 1 | 64x32px PANEL 2 |
| ----------> |
| [ESP IN] [OUT] | [IN] [OUT] |
+-----------------+-----------------+
Example 1 configuration:
#define PANEL_RES_X 64 // Number of pixels wide of each INDIVIDUAL panel module.
#define PANEL_RES_Y 32 // Number of pixels tall of each INDIVIDUAL panel module.
#define NUM_ROWS 2 // Number of rows of chained INDIVIDUAL PANELS
#define NUM_COLS 2 // Number of INDIVIDUAL PANELS per ROW
virtualDisp(dma_display, NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, true, false);
= 128x64 px virtual display, with the top left of panel 4 being pixel co-ord (0,0)
*/
#define NUM_ROWS 2 // Number of rows panels in your overall display
#define NUM_COLS 1 // number of panels in each row
#define PANEL_RES_X 64 // Number of pixels wide of each INDIVIDUAL panel module. #define PANEL_RES_X 64 // Number of pixels wide of each INDIVIDUAL panel module.
#define PANEL_RES_Y 32 // Number of pixels tall of each INDIVIDUAL panel module. #define PANEL_RES_Y 32 // Number of pixels tall of each INDIVIDUAL panel module.
/* Create physical module driver class AND virtual (chained) display class. */ #define NUM_ROWS 2 // Number of rows of chained INDIVIDUAL PANELS
#define NUM_COLS 2 // Number of INDIVIDUAL PANELS per ROW
/******************************************************************************
* Create physical DMA panel class AND virtual (chained) display class.
******************************************************************************/
#include <ESP32-VirtualMatrixPanel-I2S-DMA.h> #include <ESP32-VirtualMatrixPanel-I2S-DMA.h>
RGB64x32MatrixPanel_I2S_DMA dma_display; RGB64x32MatrixPanel_I2S_DMA dma_display;
VirtualMatrixPanel virtualDisp(dma_display, NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y); VirtualMatrixPanel virtualDisp(dma_display, NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, true);
/* FastLED Global Variables for Pattern */
#include <FastLED.h>
int time_counter = 0;
int cycles = 0;
CRGBPalette16 currentPalette;
CRGB currentColor;
CRGB ColorFromCurrentPalette(uint8_t index = 0, uint8_t brightness = 255, TBlendType blendType = LINEARBLEND) {
return ColorFromPalette(currentPalette, index, brightness, blendType);
}
/******************************************************************************
* Setup!
******************************************************************************/
void setup() { void setup() {
delay(2000);
Serial.begin(115200); Serial.begin(115200);
Serial.println(""); Serial.println(""); Serial.println(""); Serial.println(""); Serial.println(""); Serial.println("");
Serial.println("*****************************************************"); Serial.println("*****************************************************");
Serial.println(" HELLO !"); Serial.println(" HELLO !");
@ -115,44 +192,53 @@ void setup() {
#else #else
dma_display.begin(); dma_display.begin();
#endif #endif
// fill the screen with 'black'
//dma_display.fillScreen(dma_display.color444(0, 0, 0));
// Sanity checks
if (NUM_ROWS <= 1) {
Serial.println(F("There is no reason to use the VirtualDisplay class for a single horizontal chain and row!"));
}
if (dma_display.width() != NUM_ROWS*NUM_COLS*PANEL_RES_X )
{
Serial.println(F("\r\nERROR: MATRIX_WIDTH and/or MATRIX_HEIGHT in 'ESP32-RGB64x32MatrixPanel-I2S-DMA.h'\r\nis not configured correctly for the requested VirtualMatrixPanel dimensions!\r\n"));
Serial.printf("WIDTH according dma_display is %d, but should be %d. Is your NUM_ROWS and NUM_COLS correct?\r\n", dma_display.width(), NUM_ROWS*NUM_COLS*PANEL_RES_X);
return;
}
// So far so good, so continue
virtualDisp.fillScreen(virtualDisp.color444(0, 0, 0)); virtualDisp.fillScreen(virtualDisp.color444(0, 0, 0));
virtualDisp.drawDisplayTest(); // draw text numbering on each screen to check connectivity
delay(3000);
Serial.println("Chain of 64x32 panels for this example:");
Serial.println("+--------+---------+");
Serial.println("| 4 | 3 |");
Serial.println("| | |");
Serial.println("+--------+---------+");
Serial.println("| 1 | 2 |");
Serial.println("| (ESP) | |");
Serial.println("+--------+---------+");
virtualDisp.setFont(&FreeSansBold12pt7b);
virtualDisp.setTextColor(virtualDisp.color565(0, 0, 255));
virtualDisp.setTextSize(2);
virtualDisp.setCursor(10, virtualDisp.height()-20);
// Red text inside red rect (2 pix in from edge)
virtualDisp.print("1234") ;
virtualDisp.drawRect(1,1, virtualDisp.width()-2, virtualDisp.height()-2, virtualDisp.color565(255,0,0));
// White line from top left to bottom right
virtualDisp.drawLine(0,0, virtualDisp.width()-1, virtualDisp.height()-1, virtualDisp.color565(255,255,255));
// Set current FastLED palette
currentPalette = RainbowColors_p;
virtualDisp.drawRect(4, 4, PANEL_RES_X * NUM_COLS - 8, PANEL_RES_Y * NUM_ROWS - 8, virtualDisp.color565(255, 255, 255));
delay(5000);
} }
void loop() { void loop() {
for (int x = 0; x < virtualDisp.width(); x++) {
for (int y = 0; y < virtualDisp.height(); y++) {
int16_t v = 0;
uint8_t wibble = sin8(time_counter);
v += sin16(x * wibble * 3 + time_counter);
v += cos16(y * (128 - wibble) + time_counter);
v += sin16(y * x * cos8(-time_counter) / 8);
currentColor = ColorFromPalette(currentPalette, (v >> 8) + 127); //, brightness, currentBlendType);
virtualDisp.drawPixelRGB888(x, y, currentColor.r, currentColor.g, currentColor.b);
}
}
time_counter += 1;
cycles++;
if (cycles >= 2048) {
time_counter = 0;
cycles = 0;
}
} // end loop } // end loop

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 102 KiB