clockwise
clockwise copied to clipboard
Matrix Grid
Hi. I'm trying to make a clock that consists of 2x2+ grid. I see that chaining is available (1x*), but for a 2x2+ grid, VirtualMatrixPanel_T is required. I was able to get the VirtualMatrixPanel example code to work, but I'm struggling to figure out how to draw the clockface to the Virtual Matrix. Could this option be enable by default but set to 1x1. This would allow users to expand if desired.
This is what I've tried in main.cpp
#include <Arduino.h>
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
#include <ESP32-HUB75-VirtualMatrixPanel_T.hpp>
// Clockface
#include <Clockface.h>
// Commons
#include <WiFiController.h>
#include <CWDateTime.h>
#include <CWPreferences.h>
#include <CWWebServer.h>
#include <StatusController.h>
#define MIN_BRIGHT_DISPLAY_ON 4
#define MIN_BRIGHT_DISPLAY_OFF 0
/**
* Configuration of the LED matrix panels number and individual pixel resolution.
**/
#define PANEL_RES_X 32 // Number of pixels wide of each INDIVIDUAL panel module.
#define PANEL_RES_Y 16 // Number of pixels tall of each INDIVIDUAL panel module.
#define VDISP_NUM_ROWS 4 // Number of rows of individual LED panels
#define VDISP_NUM_COLS 2 // Number of individual LED panels per row
#define PANEL_CHAIN_LEN (VDISP_NUM_ROWS*VDISP_NUM_COLS) // Don't change
/**
* Configuration of the approach used to chain all the individual panels together.
* Refer to the documentation or check the enum 'PANEL_CHAIN_TYPE' in VirtualMatrixPanel_T.hpp for options.
**/
#define PANEL_CHAIN_TYPE CHAIN_TOP_LEFT_DOWN_ZZ
/**
* Optional config for the per-panel pixel mapping, for non-standard panels.
* i.e. 1/4 scan panels, or outdoor panels. They're a pain in the a-- and all
* have their own weird pixel mapping that is not linear.
*
* This is used for Examples 2 and 3.
*
**/
#define PANEL_SCAN_TYPE FOUR_SCAN_32PX_HIGH
#define ESP32_LED_BUILTIN 2
/**
* Mandatory declaration of the dma_display. DO NOT CHANGE
**/
MatrixPanel_I2S_DMA *dma_display = nullptr;
/**
* Template instantiation for the VirtualMatrixPanel_T class, depending on use-case.
**/
VirtualMatrixPanel_T<PANEL_CHAIN_TYPE>* virtualDisp = nullptr;
Clockface *clockface;
WiFiController wifi;
CWDateTime cwDateTime;
bool autoBrightEnabled;
long autoBrightMillis = 0;
uint8_t currentBrightSlot = -1;
void displaySetup(bool swapBlueGreen, uint8_t displayBright, uint8_t displayRotation)
{
HUB75_I2S_CFG mxconfig(
PANEL_RES_X,
PANEL_RES_Y,
PANEL_CHAIN_LEN
);
if (swapBlueGreen)
{
// Swap Blue and Green pins because the panel is RBG instead of RGB.
mxconfig.gpio.b1 = 26;
mxconfig.gpio.b2 = 12;
mxconfig.gpio.g1 = 27;
mxconfig.gpio.g2 = 13;
}
mxconfig.gpio.r1 = 25;
mxconfig.gpio.r2 = 14;
mxconfig.gpio.g1 = 26;
mxconfig.gpio.g2 = 12;
mxconfig.gpio.b1 = 27;
mxconfig.gpio.b2 = 13;
mxconfig.gpio.a = 23;
mxconfig.gpio.b = 19;
mxconfig.gpio.c = 5;
mxconfig.gpio.d = -1;
mxconfig.gpio.e = -1;
mxconfig.gpio.clk = 16;
mxconfig.gpio.lat = 4;
mxconfig.gpio.oe = 15;
mxconfig.clkphase = false;
//mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_10M;
//mxconfig.driver = HUB75_I2S_CFG::FM6126A;
// Physical Display Setup
dma_display = new MatrixPanel_I2S_DMA(mxconfig);
dma_display->begin();
dma_display->setBrightness8(displayBright);
dma_display->clearScreen();
dma_display->setRotation(displayRotation);
//Setup the VirtualMatrixPanel_T class to map the virtual pixels to the physical pixels.
virtualDisp = new VirtualMatrixPanel_T<PANEL_CHAIN_TYPE>(VDISP_NUM_ROWS, VDISP_NUM_COLS, PANEL_RES_X, PANEL_RES_Y);
}
void automaticBrightControl()
{
if (autoBrightEnabled) {
if (millis() - autoBrightMillis > 3000)
{
int16_t currentValue = analogRead(ClockwiseParams::getInstance()->ldrPin);
uint16_t ldrMin = ClockwiseParams::getInstance()->autoBrightMin;
uint16_t ldrMax = ClockwiseParams::getInstance()->autoBrightMax;
const uint8_t minBright = (currentValue < ldrMin ? MIN_BRIGHT_DISPLAY_OFF : MIN_BRIGHT_DISPLAY_ON);
uint8_t maxBright = ClockwiseParams::getInstance()->displayBright;
uint8_t slots = 10; //10 slots
uint8_t mapLDR = map(currentValue > ldrMax ? ldrMax : currentValue, ldrMin, ldrMax, 1, slots);
uint8_t mapBright = map(mapLDR, 1, slots, minBright, maxBright);
// Serial.printf("LDR: %d, mapLDR: %d, Bright: %d\n", currentValue, mapLDR, mapBright);
if(abs(currentBrightSlot - mapLDR ) >= 2 || mapBright == 0){
dma_display->setBrightness8(mapBright);
currentBrightSlot=mapLDR;
// Serial.printf("setBrightness: %d , Update currentBrightSlot to %d\n", mapBright, mapLDR);
}
autoBrightMillis = millis();
}
}
}
void setup()
{
Serial.begin(115200);
pinMode(ESP32_LED_BUILTIN, OUTPUT);
StatusController::getInstance()->blink_led(5, 100);
ClockwiseParams::getInstance()->load();
pinMode(ClockwiseParams::getInstance()->ldrPin, INPUT);
displaySetup(ClockwiseParams::getInstance()->swapBlueGreen, ClockwiseParams::getInstance()->displayBright, ClockwiseParams::getInstance()->displayRotation);
clockface = new Clockface(dma_display);
// Pass a reference to the DMA display to the VirtualMatrixPanel_T class
virtualDisp->setDisplay(*dma_display);
autoBrightEnabled = (ClockwiseParams::getInstance()->autoBrightMax > 0);
StatusController::getInstance()->clockwiseLogo();
delay(1000);
StatusController::getInstance()->wifiConnecting();
if (wifi.begin())
{
StatusController::getInstance()->ntpConnecting();
cwDateTime.begin(ClockwiseParams::getInstance()->timeZone.c_str(),
ClockwiseParams::getInstance()->use24hFormat,
ClockwiseParams::getInstance()->ntpServer.c_str(),
ClockwiseParams::getInstance()->manualPosix.c_str());
//clockface->setup(&cwDateTime);
clockface->setup(&cwDateTime);
}
}
void loop()
{
wifi.handleImprovWiFi();
if (wifi.isConnected())
{
ClockwiseWebServer::getInstance()->handleHttpRequest();
ezt::events();
}
if (wifi.connectionSucessfulOnce)
{
//clockface->update();
clockface->update();
}
automaticBrightControl();
}
Hmm. I wouldn't change that because the clockfaces have a fixed size, the sprites won't scale.
I’m still planning on using a total of 64x64 pixels so there shouldn’t be any scaling.
I figured it out with a 1x2 of P5 modules!! Again, no scaling because the P5s are 64x32. 64x64 total pixels.
The above code work with one small modification.
clockface = new Clockface(dma_display); to clockface = new Clockface(virtualDisp);
0 and 180deg rotation works. 90 and 270deg do not. It must be a conflict between how the virtualDisplay and rotation code moves around the pixels.
ESP32-VirtualMatrixPanel-I2S-DMA.h has its own rotate function.
switch (_rotate) {
case 0: //no rotation, do nothing
break;
case (1): //90 degree rotation
{
int16_t temp_x = virt_x;
virt_x = virt_y;
virt_y = virtualResY - 1 - temp_x;
break;
}
case (2): //180 rotation
{
virt_x = virtualResX - 1 - virt_x;
virt_y = virtualResY - 1 - virt_y;
break;
}
case (3): //270 rotation
{
int16_t temp_x = virt_x;
virt_x = virtualResX - 1 - virt_y;
virt_y = temp_x;
break;
}
}
Work with P10 modules too. This is a 2x4 with 32x16px modules.
That's good to know. Thank you @krzimmer! Do you mind to open a pull request with that change of the virtual display? As long as it doesnt break the default one, I would like to have this feature included.
Sure. I’ve never done a pull request, but there is a first for everything. I’ll give it a try.
Like I said, the only issue I’ve found so far is with screen rotation. Would you like for me to figure that out and include it in the same pull request or proceed with just the addition of virtualDisplay?
Cool! That would be a great first PR then. Fixing the screen rotation is a "nice to have", if you can fix it that would be great but open the PR even if you can't, we can figure this out later.