Update ChainedPanels example, VirtualDisplay Class and Graphic. Thanks Brian and Galaxy-Man for input.
This commit is contained in:
parent
eab828b837
commit
3ff7155bf0
3 changed files with 259 additions and 108 deletions
|
@ -2,13 +2,16 @@
|
|||
#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
|
||||
Tindie: https://www.tindie.com/stores/brianlough/
|
||||
Twitter: https://twitter.com/witnessmenow
|
||||
*******************************************************************/
|
||||
|
||||
#include "ESP32-RGB64x32MatrixPanel-I2S-DMA.h"
|
||||
#include <Fonts/FreeSansBold12pt7b.h>
|
||||
|
||||
|
||||
struct VirtualCoords {
|
||||
int16_t x;
|
||||
|
@ -30,27 +33,33 @@ class VirtualMatrixPanel : public Adafruit_GFX
|
|||
int16_t module_rows;
|
||||
int16_t module_cols;
|
||||
|
||||
int16_t screenResX;
|
||||
int16_t screenResY;
|
||||
int16_t panelResX;
|
||||
int16_t panelResY;
|
||||
|
||||
RGB64x32MatrixPanel_I2S_DMA *display;
|
||||
|
||||
#ifdef USE_GFX_ROOT
|
||||
VirtualMatrixPanel(RGB64x32MatrixPanel_I2S_DMA &disp, int vmodule_rows, int vmodule_cols, int screenX, int screenY)
|
||||
: GFX(vmodule_cols*screenX, vmodule_rows*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*panelX, vmodule_rows*panelY)
|
||||
#else
|
||||
VirtualMatrixPanel(RGB64x32MatrixPanel_I2S_DMA &disp, int vmodule_rows, int vmodule_cols, int screenX, int screenY)
|
||||
: Adafruit_GFX(vmodule_cols*screenX, vmodule_rows*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*panelX, vmodule_rows*panelY)
|
||||
#endif
|
||||
{
|
||||
this->display = &disp;
|
||||
|
||||
panelResX = panelX;
|
||||
panelResY = panelY;
|
||||
|
||||
module_rows = vmodule_rows;
|
||||
module_cols = vmodule_cols;
|
||||
screenResX = screenX;
|
||||
screenResY = screenY;
|
||||
|
||||
virtualResX = vmodule_rows*screenY;
|
||||
virtualResY = vmodule_cols*screenX;
|
||||
virtualResY = vmodule_rows*panelY;
|
||||
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);
|
||||
|
@ -76,18 +85,56 @@ class VirtualMatrixPanel : public Adafruit_GFX
|
|||
return display->Color333(r, g, b);
|
||||
}
|
||||
|
||||
void drawDisplayTest();
|
||||
|
||||
private:
|
||||
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
|
||||
|
||||
inline VirtualCoords VirtualMatrixPanel::getCoords(int16_t x, int16_t y) {
|
||||
int16_t xOffset = (y / screenResY) * (module_cols * screenResX);
|
||||
coords.x = x + xOffset;
|
||||
coords.y = y % screenResY;
|
||||
|
||||
coords.x = coords.y = 0;
|
||||
|
||||
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)
|
||||
{
|
||||
VirtualCoords coords = getCoords(x, y);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
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.
|
||||
inline void VirtualMatrixPanel::drawIcon (int *ico, int16_t x, int16_t y, int16_t module_cols, int16_t module_rows) {
|
||||
int i, j;
|
||||
|
|
|
@ -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
|
||||
-----------
|
||||
|
||||
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_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)
|
||||
- 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_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
|
||||
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
|
||||
for everything (drawing pixels, writing text etc). You can do a find and replace of all calls if it's an existing sketch
|
||||
- Set values for NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y.
|
||||
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 for everything (drawing pixels, writing text etc).
|
||||
You can do a find and replace of all calls if it's an existing sketch
|
||||
(just make sure you don't replace the definition and the matrix.begin)
|
||||
- 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()
|
||||
- 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:
|
||||
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/
|
||||
Thanks to:
|
||||
|
||||
* * = 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,
|
||||
please consider becoming a sponsor on Github
|
||||
https://github.com/sponsors/witnessmenow/
|
||||
|
||||
|
||||
Written by Brian Lough
|
||||
YouTube: https://www.youtube.com/brianlough
|
||||
Tindie: https://www.tindie.com/stores/brianlough/
|
||||
Twitter: https://twitter.com/witnessmenow
|
||||
*******************************************************************/
|
||||
|
||||
* Galaxy-Man for the kind donation of panels make/test that this is possible:
|
||||
https://github.com/Galaxy-Man
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
//#define USE_CUSTOM_PINS // uncomment to use custom pins, then provide below
|
||||
|
||||
|
@ -75,36 +56,132 @@
|
|||
#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.
|
||||
|
||||
#define NUM_ROWS 2 // Number of rows panels in your overall display
|
||||
#define NUM_COLS 1 // number of panels in each 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.
|
||||
|
||||
/* Create physical module driver class AND virtual (chained) display class. */
|
||||
#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 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
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Create physical DMA panel class AND virtual (chained) display class.
|
||||
******************************************************************************/
|
||||
#include <ESP32-VirtualMatrixPanel-I2S-DMA.h>
|
||||
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() {
|
||||
|
||||
delay(2000);
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println(""); Serial.println(""); Serial.println("");
|
||||
Serial.println("*****************************************************");
|
||||
Serial.println(" HELLO !");
|
||||
|
@ -116,43 +193,52 @@ void setup() {
|
|||
dma_display.begin();
|
||||
#endif
|
||||
|
||||
// fill the screen with 'black'
|
||||
//dma_display.fillScreen(dma_display.color444(0, 0, 0));
|
||||
virtualDisp.fillScreen(virtualDisp.color444(0, 0, 0));
|
||||
|
||||
// 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);
|
||||
|
||||
// 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.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));
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
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
|
Binary file not shown.
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 102 KiB |
Loading…
Reference in a new issue