From d5a6c82d91cd1d1c066d0ddf895625b7cb2397cb Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 5 Feb 2025 09:47:57 +0100 Subject: [PATCH 1/3] Adding support for chained 64x32 panels --- firmware/lib/cw-cf-0x01 | 1 + firmware/lib/cw-commons/CWPreferences.h | 4 + firmware/lib/cw-commons/CWWebServer.h | 103 +++++++++++++++------- firmware/lib/cw-commons/SettingsWebPage.h | 10 ++- firmware/src/main.cpp | 29 ++++-- 5 files changed, 108 insertions(+), 39 deletions(-) create mode 160000 firmware/lib/cw-cf-0x01 diff --git a/firmware/lib/cw-cf-0x01 b/firmware/lib/cw-cf-0x01 new file mode 160000 index 0000000..a56389f --- /dev/null +++ b/firmware/lib/cw-cf-0x01 @@ -0,0 +1 @@ +Subproject commit a56389f4efb2fb1c531e17ef8a2fde382a32c9a0 diff --git a/firmware/lib/cw-commons/CWPreferences.h b/firmware/lib/cw-commons/CWPreferences.h index 6a464ff..e085ebc 100644 --- a/firmware/lib/cw-commons/CWPreferences.h +++ b/firmware/lib/cw-commons/CWPreferences.h @@ -13,6 +13,7 @@ struct ClockwiseParams const char* const PREF_SWAP_BLUE_GREEN = "swapBlueGreen"; const char* const PREF_USE_24H_FORMAT = "use24hFormat"; + const char* const CHAIN = "chain"; const char* const PREF_DISPLAY_BRIGHT = "displayBright"; const char* const PREF_DISPLAY_ABC_MIN = "autoBrightMin"; const char* const PREF_DISPLAY_ABC_MAX = "autoBrightMax"; @@ -28,6 +29,7 @@ struct ClockwiseParams bool swapBlueGreen; bool use24hFormat; + bool chain; uint8_t displayBright; uint16_t autoBrightMin; uint16_t autoBrightMax; @@ -57,6 +59,7 @@ struct ClockwiseParams { preferences.putBool(PREF_SWAP_BLUE_GREEN, swapBlueGreen); preferences.putBool(PREF_USE_24H_FORMAT, use24hFormat); + preferences.putBool(CHAIN, chain); preferences.putUInt(PREF_DISPLAY_BRIGHT, displayBright); preferences.putUInt(PREF_DISPLAY_ABC_MIN, autoBrightMin); preferences.putUInt(PREF_DISPLAY_ABC_MAX, autoBrightMax); @@ -75,6 +78,7 @@ struct ClockwiseParams { swapBlueGreen = preferences.getBool(PREF_SWAP_BLUE_GREEN, false); use24hFormat = preferences.getBool(PREF_USE_24H_FORMAT, true); + chain = preferences.getBool(CHAIN, false); displayBright = preferences.getUInt(PREF_DISPLAY_BRIGHT, 32); autoBrightMin = preferences.getUInt(PREF_DISPLAY_ABC_MIN, 0); autoBrightMax = preferences.getUInt(PREF_DISPLAY_ABC_MAX, 0); diff --git a/firmware/lib/cw-commons/CWWebServer.h b/firmware/lib/cw-commons/CWWebServer.h index 8eebdee..8df5a8a 100644 --- a/firmware/lib/cw-commons/CWWebServer.h +++ b/firmware/lib/cw-commons/CWWebServer.h @@ -17,8 +17,8 @@ struct ClockwiseWebServer bool force_restart; const char* HEADER_TEMPLATE_D = "X-%s: %d\r\n"; const char* HEADER_TEMPLATE_S = "X-%s: %s\r\n"; - - static ClockwiseWebServer *getInstance() + + static ClockwiseWebServer *getInstance() { static ClockwiseWebServer base; return &base; @@ -40,7 +40,6 @@ struct ClockwiseWebServer if (force_restart) StatusController::getInstance()->forceRestart(); - WiFiClient client = server.available(); if (client) { @@ -83,49 +82,88 @@ struct ClockwiseWebServer void processRequest(WiFiClient client, String method, String path, String key, String value) { - if (method == "GET" && path == "/") { + if (method == "GET" && path == "/") + { client.println("HTTP/1.0 200 OK"); client.println("Content-Type: text/html"); client.println(); client.println(SETTINGS_PAGE); - } else if (method == "GET" && path == "/get") { + } + else if (method == "GET" && path == "/get") + { getCurrentSettings(client); - } else if (method == "GET" && path == "/read") { - if (key == "pin") { + } + else if (method == "GET" && path == "/read") + { + if (key == "pin") + { readPin(client, key, value.toInt()); } - } else if (method == "POST" && path == "/restart") { + } + else if (method == "POST" && path == "/restart") + { client.println("HTTP/1.0 204 No Content"); force_restart = true; - } else if (method == "POST" && path == "/set") { + } + else if (method == "POST" && path == "/set") + { ClockwiseParams::getInstance()->load(); - //a baby seal has died due this ifs - if (key == ClockwiseParams::getInstance()->PREF_DISPLAY_BRIGHT) { + // a baby seal has died due this ifs + if (key == ClockwiseParams::getInstance()->PREF_DISPLAY_BRIGHT) + { ClockwiseParams::getInstance()->displayBright = value.toInt(); - } else if (key == ClockwiseParams::getInstance()->PREF_WIFI_SSID) { + } + else if (key == ClockwiseParams::getInstance()->PREF_WIFI_SSID) + { ClockwiseParams::getInstance()->wifiSsid = value; - } else if (key == ClockwiseParams::getInstance()->PREF_WIFI_PASSWORD) { + } + else if (key == ClockwiseParams::getInstance()->PREF_WIFI_PASSWORD) + { ClockwiseParams::getInstance()->wifiPwd = value; - } else if (key == "autoBright") { //autoBright=0010,0800 - ClockwiseParams::getInstance()->autoBrightMin = value.substring(0,4).toInt(); - ClockwiseParams::getInstance()->autoBrightMax = value.substring(5,9).toInt(); - } else if (key == ClockwiseParams::getInstance()->PREF_SWAP_BLUE_GREEN) { + } + else if (key == "autoBright") + { // autoBright=0010,0800 + ClockwiseParams::getInstance()->autoBrightMin = value.substring(0, 4).toInt(); + ClockwiseParams::getInstance()->autoBrightMax = value.substring(5, 9).toInt(); + } + else if (key == ClockwiseParams::getInstance()->PREF_SWAP_BLUE_GREEN) + { ClockwiseParams::getInstance()->swapBlueGreen = (value == "1"); - } else if (key == ClockwiseParams::getInstance()->PREF_USE_24H_FORMAT) { + } + else if (key == ClockwiseParams::getInstance()->PREF_USE_24H_FORMAT) + { ClockwiseParams::getInstance()->use24hFormat = (value == "1"); - } else if (key == ClockwiseParams::getInstance()->PREF_LDR_PIN) { + } + else if (key == ClockwiseParams::getInstance()->CHAIN) + { + ClockwiseParams::getInstance()->chain = (value == "1"); + } + else if (key == ClockwiseParams::getInstance()->PREF_LDR_PIN) + { ClockwiseParams::getInstance()->ldrPin = value.toInt(); - } else if (key == ClockwiseParams::getInstance()->PREF_TIME_ZONE) { + } + else if (key == ClockwiseParams::getInstance()->PREF_TIME_ZONE) + { ClockwiseParams::getInstance()->timeZone = value; - } else if (key == ClockwiseParams::getInstance()->PREF_NTP_SERVER) { + } + else if (key == ClockwiseParams::getInstance()->PREF_NTP_SERVER) + { ClockwiseParams::getInstance()->ntpServer = value; - } else if (key == ClockwiseParams::getInstance()->PREF_CANVAS_FILE) { + } + else if (key == ClockwiseParams::getInstance()->PREF_CANVAS_FILE) + { ClockwiseParams::getInstance()->canvasFile = value; - } else if (key == ClockwiseParams::getInstance()->PREF_CANVAS_SERVER) { + } + else if (key == ClockwiseParams::getInstance()->PREF_CANVAS_SERVER) + { ClockwiseParams::getInstance()->canvasServer = value; - } else if (key == ClockwiseParams::getInstance()->PREF_MANUAL_POSIX) { + } + else if (key == ClockwiseParams::getInstance()->PREF_MANUAL_POSIX) + { ClockwiseParams::getInstance()->manualPosix = value; - } else if (key == ClockwiseParams::getInstance()->PREF_DISPLAY_ROTATION) { + } + else if (key == ClockwiseParams::getInstance()->PREF_DISPLAY_ROTATION) + { ClockwiseParams::getInstance()->displayRotation = value.toInt(); } ClockwiseParams::getInstance()->save(); @@ -133,19 +171,18 @@ struct ClockwiseWebServer } } - - - void readPin(WiFiClient client, String key, uint16_t pin) { + void readPin(WiFiClient client, String key, uint16_t pin) + { ClockwiseParams::getInstance()->load(); client.println("HTTP/1.0 204 No Content"); client.printf(HEADER_TEMPLATE_D, key, analogRead(pin)); - + client.println(); } - - void getCurrentSettings(WiFiClient client) { + void getCurrentSettings(WiFiClient client) + { ClockwiseParams::getInstance()->load(); client.println("HTTP/1.0 204 No Content"); @@ -155,7 +192,8 @@ struct ClockwiseWebServer client.printf(HEADER_TEMPLATE_D, ClockwiseParams::getInstance()->PREF_DISPLAY_ABC_MAX, ClockwiseParams::getInstance()->autoBrightMax); client.printf(HEADER_TEMPLATE_D, ClockwiseParams::getInstance()->PREF_SWAP_BLUE_GREEN, ClockwiseParams::getInstance()->swapBlueGreen); client.printf(HEADER_TEMPLATE_D, ClockwiseParams::getInstance()->PREF_USE_24H_FORMAT, ClockwiseParams::getInstance()->use24hFormat); - client.printf(HEADER_TEMPLATE_D, ClockwiseParams::getInstance()->PREF_LDR_PIN, ClockwiseParams::getInstance()->ldrPin); + client.printf(HEADER_TEMPLATE_D, ClockwiseParams::getInstance()->CHAIN, ClockwiseParams::getInstance()->chain); + client.printf(HEADER_TEMPLATE_D, ClockwiseParams::getInstance()->PREF_LDR_PIN, ClockwiseParams::getInstance()->ldrPin); client.printf(HEADER_TEMPLATE_S, ClockwiseParams::getInstance()->PREF_TIME_ZONE, ClockwiseParams::getInstance()->timeZone.c_str()); client.printf(HEADER_TEMPLATE_S, ClockwiseParams::getInstance()->PREF_WIFI_SSID, ClockwiseParams::getInstance()->wifiSsid.c_str()); client.printf(HEADER_TEMPLATE_S, ClockwiseParams::getInstance()->PREF_NTP_SERVER, ClockwiseParams::getInstance()->ntpServer.c_str()); @@ -169,5 +207,4 @@ struct ClockwiseWebServer client.printf(HEADER_TEMPLATE_S, "CLOCKFACE_NAME", CLOCKFACE_NAME); client.println(); } - }; diff --git a/firmware/lib/cw-commons/SettingsWebPage.h b/firmware/lib/cw-commons/SettingsWebPage.h index 7a42c3f..3e73d92 100644 --- a/firmware/lib/cw-commons/SettingsWebPage.h +++ b/firmware/lib/cw-commons/SettingsWebPage.h @@ -69,6 +69,14 @@ const char SETTINGS_PAGE[] PROGMEM = R""""( save: "updatePreference('use24hFormat', Number(use24h.checked))", property: "use24hFormat" }, + { + title: "Chain 64x32 panels", + description: "Use two 64x32 LED Panels instead of one single 64x64", + formInput: "", + icon: "fa-clock-o", + save: "updatePreference('chain', Number(chain.checked))", + property: "chain" + }, { title: "Swap Blue/Green pins?", description: "Swap Blue and Green pins because the panel is RBG instead of RGB", @@ -229,7 +237,7 @@ const char SETTINGS_PAGE[] PROGMEM = R""""( } //Local - //createCards({ "displayBright": 30, "swapBlueGreen": 1, "use24hFormat": 0, "timeZone": "Europe/Lisbon", "ntpServer": "pool.ntp.org", "wifiSsid": "test", "autoBrightMin":0, "autoBrightMax":800, "ldrPin":35, "cw_fw_version":"1.2.2", "clockface_name":"cw-cf-0x07", "canvasServer":"raw.githubusercontent.com", "canvasFile":"star-wars.json" }); + //createCards({ "displayBright": 30, "swapBlueGreen": 1, "use24hFormat": 0, , "chain" : 0, "timeZone": "Europe/Lisbon", "ntpServer": "pool.ntp.org", "wifiSsid": "test", "autoBrightMin":0, "autoBrightMax":800, "ldrPin":35, "cw_fw_version":"1.2.2", "clockface_name":"cw-cf-0x07", "canvasServer":"raw.githubusercontent.com", "canvasFile":"star-wars.json" }); //Embedded begin(); diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index 61ca80d..d8cac1c 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -1,5 +1,5 @@ #include -#include +#include // Clockface #include @@ -15,7 +15,20 @@ #define ESP32_LED_BUILTIN 2 +#define CHAINED_PANEL_RES_X 64 // Number of pixels wide of each INDIVIDUAL panel module. +#define CHAINED_PANEL_RES_Y 32 // Number of pixels tall of each INDIVIDUAL panel module. +#define CHAINED_NUM_ROWS 2 // Number of rows of chained INDIVIDUAL PANELS +#define CHAINED_NUM_COLS 1 // Number of rows of chained INDIVIDUAL PANELS + +#define PANEL_RES_X 64 // Number of pixels wide of each INDIVIDUAL panel module. +#define PANEL_RES_Y 64 // Number of pixels tall of each INDIVIDUAL panel module. +#define NUM_COLS 1 // Number of INDIVIDUAL PANELS per ROW +#define NUM_ROWS 1 // Number of rows of chained INDIVIDUAL PANELS + +#define VIRTUAL_MATRIX_CHAIN_TYPE CHAIN_BOTTOM_LEFT_UP + MatrixPanel_I2S_DMA *dma_display = nullptr; +VirtualMatrixPanel *virtualDisp = nullptr; Clockface *clockface; @@ -28,7 +41,11 @@ uint8_t currentBrightSlot = -1; void displaySetup(bool swapBlueGreen, uint8_t displayBright, uint8_t displayRotation) { - HUB75_I2S_CFG mxconfig(64, 64, 1); + HUB75_I2S_CFG mxconfig( + ClockwiseParams::getInstance()->chain ? CHAINED_PANEL_RES_X : PANEL_RES_X, // module width + ClockwiseParams::getInstance()->chain ? CHAINED_PANEL_RES_Y : PANEL_RES_Y, // module height + ClockwiseParams::getInstance()->chain ? (CHAINED_PANEL_RES_X * CHAINED_PANEL_RES_Y) : (NUM_ROWS * NUM_COLS) // chain length +); if (swapBlueGreen) { @@ -46,8 +63,10 @@ void displaySetup(bool swapBlueGreen, uint8_t displayBright, uint8_t displayRota dma_display = new MatrixPanel_I2S_DMA(mxconfig); dma_display->begin(); dma_display->setBrightness8(displayBright); - dma_display->clearScreen(); - dma_display->setRotation(displayRotation); + + virtualDisp = new VirtualMatrixPanel((*dma_display), NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, VIRTUAL_MATRIX_CHAIN_TYPE); + virtualDisp->clearScreen(); + virtualDisp->setRotation(displayRotation); } void automaticBrightControl() @@ -90,7 +109,7 @@ void setup() pinMode(ClockwiseParams::getInstance()->ldrPin, INPUT); displaySetup(ClockwiseParams::getInstance()->swapBlueGreen, ClockwiseParams::getInstance()->displayBright, ClockwiseParams::getInstance()->displayRotation); - clockface = new Clockface(dma_display); + clockface = new Clockface(virtualDisp); autoBrightEnabled = (ClockwiseParams::getInstance()->autoBrightMax > 0); From 2ee5eb5b38629d9d9c8688b35e19ca8189f7d7cf Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 5 Feb 2025 09:49:30 +0100 Subject: [PATCH 2/3] Remove trailing whitespace --- firmware/lib/cw-commons/CWWebServer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/lib/cw-commons/CWWebServer.h b/firmware/lib/cw-commons/CWWebServer.h index 8df5a8a..555804f 100644 --- a/firmware/lib/cw-commons/CWWebServer.h +++ b/firmware/lib/cw-commons/CWWebServer.h @@ -18,7 +18,7 @@ struct ClockwiseWebServer const char* HEADER_TEMPLATE_D = "X-%s: %d\r\n"; const char* HEADER_TEMPLATE_S = "X-%s: %s\r\n"; - static ClockwiseWebServer *getInstance() + static ClockwiseWebServer *getInstance() { static ClockwiseWebServer base; return &base; From e6ba896fa263eff455e85dce51dba2aa5e9c7f78 Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 5 Feb 2025 10:15:58 +0100 Subject: [PATCH 3/3] Avoid definining unnecessary constants --- firmware/src/main.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index d8cac1c..a64ffc3 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -15,17 +15,19 @@ #define ESP32_LED_BUILTIN 2 -#define CHAINED_PANEL_RES_X 64 // Number of pixels wide of each INDIVIDUAL panel module. -#define CHAINED_PANEL_RES_Y 32 // Number of pixels tall of each INDIVIDUAL panel module. -#define CHAINED_NUM_ROWS 2 // Number of rows of chained INDIVIDUAL PANELS -#define CHAINED_NUM_COLS 1 // Number of rows of chained INDIVIDUAL PANELS +// Single 64x64 LED panel use case +#define PANEL_RES_X 64 +#define PANEL_RES_Y 64 +#define NUM_COLS 1 +#define NUM_ROWS 1 -#define PANEL_RES_X 64 // Number of pixels wide of each INDIVIDUAL panel module. -#define PANEL_RES_Y 64 // Number of pixels tall of each INDIVIDUAL panel module. -#define NUM_COLS 1 // Number of INDIVIDUAL PANELS per ROW -#define NUM_ROWS 1 // Number of rows of chained INDIVIDUAL PANELS +// Chained 64x32 panels use case +// #define CHAINED_PANEL_RES_X 64 +#define CHAINED_PANEL_RES_Y 32 +#define CHAINED_NUM_ROWS 2 +// #define CHAINED_NUM_COLS 1 -#define VIRTUAL_MATRIX_CHAIN_TYPE CHAIN_BOTTOM_LEFT_UP +#define VIRTUAL_MATRIX_CHAIN_TYPE CHAIN_BOTTOM_LEFT_UP MatrixPanel_I2S_DMA *dma_display = nullptr; VirtualMatrixPanel *virtualDisp = nullptr; @@ -42,10 +44,10 @@ uint8_t currentBrightSlot = -1; void displaySetup(bool swapBlueGreen, uint8_t displayBright, uint8_t displayRotation) { HUB75_I2S_CFG mxconfig( - ClockwiseParams::getInstance()->chain ? CHAINED_PANEL_RES_X : PANEL_RES_X, // module width - ClockwiseParams::getInstance()->chain ? CHAINED_PANEL_RES_Y : PANEL_RES_Y, // module height - ClockwiseParams::getInstance()->chain ? (CHAINED_PANEL_RES_X * CHAINED_PANEL_RES_Y) : (NUM_ROWS * NUM_COLS) // chain length -); + PANEL_RES_X, + ClockwiseParams::getInstance()->chain ? CHAINED_PANEL_RES_Y : PANEL_RES_Y, + ClockwiseParams::getInstance()->chain ? (CHAINED_NUM_ROWS * NUM_COLS) : (NUM_ROWS * NUM_COLS) + ); if (swapBlueGreen) {