diff --git a/docs/gettingstarted/installer.md b/docs/gettingstarted/installer.md
index 27fd3453a..1c57dcaea 100644
--- a/docs/gettingstarted/installer.md
+++ b/docs/gettingstarted/installer.md
@@ -21,7 +21,7 @@ Recommended low-cost DIY board up to 10K LEDs: [ESP32-S3 n16r8](https://s.click.
{: style="width:100px"}
-Recommended state-of-the-art DIY board, up to 16K - 98K LEDs !: [ESP32-P4-Nano](https://www.waveshare.com/esp32-p4-nano.htm){:target="_blank"}
+Recommended state-of-the-art DIY board, up to 16-98K LEDs: [ESP32-P4-Nano](https://www.waveshare.com/esp32-p4-nano.htm){:target="_blank"}
{: style="width:100px"}
@@ -45,8 +45,9 @@ After a successful install, go to **Logs & Console**, press **Reset Device**, an
| Name | Image* | Flash | Shop & Board presets |
|------|--------|-------|----------------------|
-| esp32-d0 | {: style="width:100px"} | | [Dig Uno](https://quinled.info/pre-assembled-quinled-dig-uno):
{: style="width:100px"}
[Dig Quad](https://quinled.info/pre-assembled-quinled-dig-quad):
{: style="width:100px"}
[Dig2Go](https://quinled.info/quinled-dig2go):
{: style="width:100px"} |
+| esp32-d0 | {: style="width:100px"} | | [Dig2Go](https://quinled.info/quinled-dig2go):
{: style="width:100px"}
[Dig Uno](https://quinled.info/pre-assembled-quinled-dig-uno):
{: style="width:100px"}
[Dig Quad](https://quinled.info/pre-assembled-quinled-dig-quad):
{: style="width:100px"} |
| esp32-d0-16mb | {: style="width:100px"} | | [Dig Octa](https://quinled.info/quinled-dig-octa):
{: style="width:100px"}
[Serg ESP32](https://www.tindie.com/products/serg74/esp32-wroom-usb-c-d1-mini32-form-factor-board){:target="_blank"} and [Shield](https://www.tindie.com/products/serg74/wled-shield-board-for-addressable-leds)
/i/93057/products/2021-08-14T14%3A44%3A14.418Z-shield_v3-1.jpg?1628927139){: style="width:100px"} |
+| [esp32-d0-pico2](https://documentation.espressif.com/esp32-pico-mini-02_datasheet_en.pdf) | {: style="width:100px"} | | [DigNext2](https://quinled.info/dig-next-2):
{: style="width:100px"} |
| esp32-s3-n8r8v | {: style="width:100px"} | | SE-16p
{: style="width:100px"} |
| esp32-s3-n16r8v | {: style="width:100px"} | | [Ali*](https://s.click.aliexpress.com/e/_DBAtJ2H){:target="_blank"} |
| esp32-s3-atoms3r | {: style="width:100px"} | | [M5Stack store](https://shop.m5stack.com/products/atoms3r-dev-kit){:target="_blank"} |
diff --git a/docs/moonbase/inputoutput.md b/docs/moonbase/inputoutput.md
index 37e1ddc37..a36e881fa 100644
--- a/docs/moonbase/inputoutput.md
+++ b/docs/moonbase/inputoutput.md
@@ -53,12 +53,14 @@ For each board the following presets are defined:
### QuinLed boards
{: style="width:100px"}
+{: style="width:100px"}
{: style="width:100px"}
{: style="width:100px"}
{: style="width:100px"}
* [Dig2Go](https://quinled.info/quinled-dig2go/), [Dig Uno](https://quinled.info/pre-assembled-quinled-dig-uno/), [Dig Quad](https://quinled.info/pre-assembled-quinled-dig-quad/): Choose the esp32-d0 (4MB) board in the [MoonLight Installer](../../gettingstarted/installer/)
* Dig2Go: Shipped with a 300 LED, GRBW led strip: Choose layout with 300 lights (e.g. Single Column for 1D, Panel 15x20 for 2D). Select Light preset GRBW in the LED Driver.
+* [DigNext2](https://quinled.info/dig-next-2): Choose the esp32-d0-pico2 board in the [MoonLight Installer](../../gettingstarted/installer/)
* [Dig Octa](https://quinled.info/quinled-dig-octa/): Choose the esp32-d0-16mb board in the [MoonLight Installer](../../gettingstarted/installer/)
* On first install, erase flash first (Especially when other firmware like WLED was on it) as MoonLight uses a partition scheme with 3MB of flash (currently no OTA support).
* After install, select the QuinLED board preset to have the pins assigned correctly.
diff --git a/docs/moonlight/modifiers.md b/docs/moonlight/modifiers.md
index 8b1f3e04d..db2bbbcb9 100644
--- a/docs/moonlight/modifiers.md
+++ b/docs/moonlight/modifiers.md
@@ -22,6 +22,6 @@ The following Modifiers are defined in MoonLight. Some found there origin in WLE
| Rotate |  |
| |
| Checkerboard |  |
| |
| Pinwheel 🧊 |  |
| Projects 1D/2D effects onto 2D/3D layouts in a pinwheel pattern.
**Swirl**: bend the pinwheel
**Rotation Symmetry**: rotational symmetry of the pattern
**Petals** Virtual width
**Ztwist** twist the pattern along the z-axis
Height: distance from center to corner |
-| RippleYZ 🧊 |  |
| 1D/2D effect will be rippled to 2D/3D (🚨)
Shrink: shrinks the original size towards Y and Z, towardsY: copies X into Y, towardsZ: copies XY into Z |
+| RippleXZ 🧊 |  |
| 1D/2D effect will be rippled to 2D/3D (🚨)
Shrink: shrinks the original size towards X and Z, towardsX: copies Y into X, towardsZ: copies XY into Z |
🚨: some effects already do this theirselves e.g. FreqMatrix runs on 1D but copies to 2D and 3D if size allows.
diff --git a/firmware/esp32-d0.ini b/firmware/esp32-d0.ini
index 225fc1c58..2e0de27ea 100644
--- a/firmware/esp32-d0.ini
+++ b/firmware/esp32-d0.ini
@@ -37,7 +37,6 @@ lib_deps = ${esp32-d0-base.lib_deps}
[env:esp32-d0-16mb]
-extends = env:esp32dev
board = esp32_16MB
board_build.partitions = boards/ESP32_16MB_3MBFlash.csv ; standard for 16MB flash: 3MB firmware, 10 MB filesystem
build_flags = ${esp32-d0-base.build_flags}
@@ -113,3 +112,11 @@ build_flags = ${env.build_flags}
lib_deps = ${env.lib_deps}
; RAM: [=== ] 26.2% (used 85912 bytes from 327680 bytes)
; Flash: [======= ] 65.9% (used 2073762 bytes from 3145728 bytes)
+
+
+[env:esp32-d0-pico2]
+board = esp32-pico-devkitm-2 ; https://github.com/platformio/platform-espressif32/blob/master/boards/esp32-pico-devkitm-2.json
+board_build.partitions = default_8MB.csv ; boards/ESP32_8MB.csv
+build_flags = ${esp32-d0-base.build_flags}
+ -D HTTPD_STACK_SIZE=6144
+lib_deps = ${esp32-d0-base.lib_deps}
\ No newline at end of file
diff --git a/firmware/installer/images/esp32-d0-pico2.jpg b/firmware/installer/images/esp32-d0-pico2.jpg
new file mode 100644
index 000000000..397466b50
Binary files /dev/null and b/firmware/installer/images/esp32-d0-pico2.jpg differ
diff --git a/firmware/installer/manifest_esp32-d0-pico2.json b/firmware/installer/manifest_esp32-d0-pico2.json
new file mode 100644
index 000000000..2c278fa1a
--- /dev/null
+++ b/firmware/installer/manifest_esp32-d0-pico2.json
@@ -0,0 +1,18 @@
+{
+ "name": "MoonLight on esp32-d0-pico2",
+ "version": "0.7.0",
+ "home_assistant_domain": "esphome",
+ "funding_url": "https://esphome.io/guides/supporters.html",
+ "new_install_prompt_erase": true,
+ "new_install_immediately": false,
+ "builds": [
+ {
+ "chipFamily": "ESP32",
+ "name": "esp32-d0",
+ "image": "./images/esp32-d0-pico2.jpg",
+ "parts": [
+ { "path": "./MoonLight_esp32-d0-pico2_0-7-0_webflash.bin", "offset": 0 }
+ ]
+ }
+ ]
+}
diff --git a/misc/livescripts/E_lines.sc b/misc/livescripts/E_lines.sc
index e1b45fd87..cbfdf4646 100644
--- a/misc/livescripts/E_lines.sc
+++ b/misc/livescripts/E_lines.sc
@@ -1,5 +1,5 @@
void loop() {
- fadeToBlackBy(255);
+ fadeToBlackBy(100);
int x = millis() / 100;
for (int y = 0; y < height; y++) {
setRGB(y*width+x%width, CRGB(255,0,0));
diff --git a/platformio.ini b/platformio.ini
index b4a0ef7d3..df2540490 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -56,7 +56,7 @@ build_flags =
-D BUILD_TARGET=\"$PIOENV\"
-D APP_NAME=\"MoonLight\" ; 🌙 Must only contain characters from [a-zA-Z0-9-_] as this is converted into a filename
-D APP_VERSION=\"0.7.1\" ; semver compatible version string
- -D APP_DATE=\"20260113\" ; 🌙
+ -D APP_DATE=\"20260116\" ; 🌙
-D PLATFORM_VERSION=\"pioarduino-55.03.35\" ; 🌙 make sure it matches with above plaftform
diff --git a/src/MoonBase/Modules/ModuleIO.h b/src/MoonBase/Modules/ModuleIO.h
index 2c335af01..f248545be 100644
--- a/src/MoonBase/Modules/ModuleIO.h
+++ b/src/MoonBase/Modules/ModuleIO.h
@@ -72,10 +72,11 @@ enum IO_PinUsageEnum {
enum IO_BoardsEnum {
board_none, //
+ board_QuinLEDDig2Go,
+ board_QuinLEDDigNext2,
board_QuinLEDDigUnoV3,
board_QuinLEDDigQuadV3,
board_QuinLEDDigOctaV2,
- board_QuinLEDDig2Go,
// board_QuinLEDPenta,
// board_QuinLEDPentaPlus,
board_SergUniShieldV5,
@@ -114,10 +115,11 @@ class ModuleIO : public Module {
control = addControl(controls, "boardPreset", "select");
control["default"] = 0;
addControlValue(control, BUILD_TARGET); // 0 none
+ addControlValue(control, "QuinLED Dig2Go");
+ addControlValue(control, "QuinLED DigNext2");
addControlValue(control, "QuinLED Dig Uno v3");
addControlValue(control, "QuinLED Dig Quad v3");
addControlValue(control, "QuinLED Dig Octa v2");
- addControlValue(control, "QuinLED Dig2Go");
addControlValue(control, "Serg Universal Shield");
addControlValue(control, "Serg Mini Shield");
addControlValue(control, "Mathieu SE16 v1");
@@ -330,42 +332,6 @@ class ModuleIO : public Module {
pinAssigner.assignPin(10, pin_PHY_CS); // WIZ850IO nCS
pinAssigner.assignPin(45, pin_PHY_IRQ); // WIZ850IO nINT
pinAssigner.assignPin(4, pin_Infrared);
- } else if (boardID == board_QuinLEDDigUnoV3) {
- // Dig-Uno-V3
- // esp32-d0 (4MB)
- object["maxPower"] = 50; // max 75, but 10A fuse
- pinAssigner.assignPin(16, pin_LED);
- pinAssigner.assignPin(3, pin_LED);
- pinAssigner.assignPin(0, pin_ButtonPush);
- pinAssigner.assignPin(15, pin_Relay);
- // pinAssigner.assignPin(2, pin_I2S_SD);
- // pinAssigner.assignPin(12, pin_I2S_WS);
- // pinAssigner.assignPin(13, pin_Temperature);
- // pinAssigner.assignPin(15, pin_I2S_SCK);
- // pinAssigner.assignPin(16, pin_LED_03);
- // pinAssigner.assignPin(32, pin_Exposed);
- } else if (boardID == board_QuinLEDDigQuadV3) {
- // Dig-Quad-V3
- // esp32-d0 (4MB)
- object["maxPower"] = 150;
- uint8_t ledPins[] = {16, 3, 1, 4}; // LED_PINS
- for (uint8_t gpio : ledPins) pinAssigner.assignPin(gpio, pin_LED);
- pinAssigner.assignPin(0, pin_ButtonPush);
-
- pinAssigner.assignPin(15, pin_Relay);
-
- // pinAssigner.assignPin(2, pin_I2S_SD;
- // pinAssigner.assignPin(12, pin_I2S_WS;
- // pinAssigner.assignPin(13, pin_Temperature;
- // pinAssigner.assignPin(15, pin_I2S_SCK;
- // pinAssigner.assignPin(32, pin_Exposed;
- } else if (boardID == board_QuinLEDDigOctaV2) {
- // Dig-Octa-32-8L
- object["maxPower"] = 400; // 10A Fuse * 8 ... 400 W
- uint8_t ledPins[] = {0, 1, 2, 3, 4, 5, 12, 13}; // LED_PINS
- for (uint8_t gpio : ledPins) pinAssigner.assignPin(gpio, pin_LED);
- pinAssigner.assignPin(33, pin_Relay);
- pinAssigner.assignPin(34, pin_ButtonPush);
} else if (boardID == board_QuinLEDDig2Go) {
// dig2go
object["maxPower"] = 10; // USB powered: 2A / 10W
@@ -402,6 +368,62 @@ class ModuleIO : public Module {
// pinAssigner.assignPin(16, pin_I2C_SCL);
// pinAssigner.assignPin(13, pin_Relay_LightsOn);
// pinAssigner.assignPin(5, pin_LED);
+ } else if (boardID == board_QuinLEDDigNext2) {
+ // digNext2
+ object["maxPower"] = 65;
+ pinAssigner.assignPin(2, pin_LED);
+ pinAssigner.assignPin(4, pin_LED);
+ pinAssigner.assignPin(5, pin_Relay_LightsOn);
+ pinAssigner.assignPin(20, pin_Relay_LightsOn);
+ pinAssigner.assignPin(21, pin_Relay_LightsOn);
+ pinAssigner.assignPin(22, pin_Relay_LightsOn);
+ pinAssigner.assignPin(7, pin_I2S_SD);
+ pinAssigner.assignPin(8, pin_I2S_WS);
+ // pinAssigner.assignPin(?, pin_I2S_SCK);
+ pinAssigner.assignPin(15, pin_I2C_SDA);
+ pinAssigner.assignPin(14, pin_I2C_SCL);
+ pinAssigner.assignPin(34, pin_Button_Push_LightsOn);
+ pinAssigner.assignPin(35, pin_ButtonPush);
+ pinAssigner.assignPin(0, pin_Exposed);
+ pinAssigner.assignPin(25, pin_Exposed);
+ pinAssigner.assignPin(32, pin_Exposed);
+ pinAssigner.assignPin(33, pin_Exposed);
+ } else if (boardID == board_QuinLEDDigUnoV3) {
+ // Dig-Uno-V3
+ // esp32-d0 (4MB)
+ object["maxPower"] = 50; // max 75, but 10A fuse
+ pinAssigner.assignPin(16, pin_LED);
+ pinAssigner.assignPin(3, pin_LED);
+ pinAssigner.assignPin(0, pin_ButtonPush);
+ pinAssigner.assignPin(15, pin_Relay);
+ // pinAssigner.assignPin(2, pin_I2S_SD);
+ // pinAssigner.assignPin(12, pin_I2S_WS);
+ // pinAssigner.assignPin(13, pin_Temperature);
+ // pinAssigner.assignPin(15, pin_I2S_SCK);
+ // pinAssigner.assignPin(16, pin_LED_03);
+ // pinAssigner.assignPin(32, pin_Exposed);
+ } else if (boardID == board_QuinLEDDigQuadV3) {
+ // Dig-Quad-V3
+ // esp32-d0 (4MB)
+ object["maxPower"] = 150;
+ uint8_t ledPins[] = {16, 3, 1, 4}; // LED_PINS
+ for (uint8_t gpio : ledPins) pinAssigner.assignPin(gpio, pin_LED);
+ pinAssigner.assignPin(0, pin_ButtonPush);
+
+ pinAssigner.assignPin(15, pin_Relay);
+
+ // pinAssigner.assignPin(2, pin_I2S_SD;
+ // pinAssigner.assignPin(12, pin_I2S_WS;
+ // pinAssigner.assignPin(13, pin_Temperature;
+ // pinAssigner.assignPin(15, pin_I2S_SCK;
+ // pinAssigner.assignPin(32, pin_Exposed;
+ } else if (boardID == board_QuinLEDDigOctaV2) {
+ // Dig-Octa-32-8L
+ object["maxPower"] = 400; // 10A Fuse * 8 ... 400 W
+ uint8_t ledPins[] = {0, 1, 2, 3, 4, 5, 12, 13}; // LED_PINS
+ for (uint8_t gpio : ledPins) pinAssigner.assignPin(gpio, pin_LED);
+ pinAssigner.assignPin(33, pin_Relay);
+ pinAssigner.assignPin(34, pin_ButtonPush);
} else if (boardID == board_SergMiniShield) {
object["maxPower"] = 50; // 10A Fuse ...
pinAssigner.assignPin(16, pin_LED);
@@ -605,6 +627,11 @@ class ModuleIO : public Module {
}
void readPins() {
+ if (safeModeMB) {
+ EXT_LOGW(ML_TAG, "Safe mode enabled, not adding pins");
+ return;
+ }
+
uint8_t pinRS485TX = UINT8_MAX;
uint8_t pinRS485RX = UINT8_MAX;
uint8_t pinRS485DE = UINT8_MAX;
diff --git a/src/MoonBase/Nodes.cpp b/src/MoonBase/Nodes.cpp
index 417f33d48..e4335c362 100644
--- a/src/MoonBase/Nodes.cpp
+++ b/src/MoonBase/Nodes.cpp
@@ -19,7 +19,7 @@ void Node::updateControl(const JsonObject& control) {
// if (oldValue == "") return; // newControl, value already set
if (!control["name"].isNull() && !control["type"].isNull() && !control["p"].isNull()) { // name and type can be null if control is removed in compareRecursive
int pointer = control["p"];
- // EXT_LOGD(ML_TAG, "%s = %s t:%s p:%p", control["name"].as(), control["value"].as().c_str(), control["type"].as(), (void*)pointer);
+ EXT_LOGD(ML_TAG, "%s = %s t:%s p:%p", control["name"].as(), control["value"].as().c_str(), control["type"].as(), (void*)pointer);
if (pointer) {
if (control["type"] == "slider" || control["type"] == "select" || control["type"] == "pin" || control["type"] == "number") {
diff --git a/src/MoonBase/Utilities.cpp b/src/MoonBase/Utilities.cpp
index 9004315b9..a59a1e8b4 100644
--- a/src/MoonBase/Utilities.cpp
+++ b/src/MoonBase/Utilities.cpp
@@ -88,7 +88,7 @@ bool copyFile(const char* srcPath, const char* dstPath) {
size_t n;
while ((n = src.read(buf, sizeof(buf))) > 0) {
if (dst.write(buf, n) != n) {
- Serial.println("Write failed!");
+ EXT_LOGE(MB_TAG, "Write failed!");
src.close();
dst.close();
return false;
diff --git a/src/MoonBase/Utilities.h b/src/MoonBase/Utilities.h
index fb3712202..c2e6841b7 100644
--- a/src/MoonBase/Utilities.h
+++ b/src/MoonBase/Utilities.h
@@ -192,9 +192,9 @@ T* allocMB(size_t n, const char* name = nullptr) {
T* res = (T*)heap_caps_calloc_prefer(n, sizeof(T), 2, MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT); // calloc is malloc + memset(0);
if (res) {
totalAllocatedMB += heap_caps_get_allocated_size(res);
- // EXT_LOGD(MB_TAG, "Allocated %s: %d x %d bytes in %s s:%d (tot:%d)", name?name:"", n, sizeof(T), isInPSRAM(res)?"PSRAM":"RAM", heap_caps_get_allocated_size(res), totalAllocatedMB);
+ // EXT_LOGD(MB_TAG, "Allocated %s: %d x %d bytes in %s s:%d (tot:%d)", name?name:"x", n, sizeof(T), isInPSRAM(res)?"PSRAM":"RAM", heap_caps_get_allocated_size(res), totalAllocatedMB);
} else
- EXT_LOGE(MB_TAG, "heap_caps_malloc of %d x %d not succeeded", n, sizeof(T));
+ EXT_LOGE(MB_TAG, "heap_caps_malloc for %s of %d x %d not succeeded", name?name:"x", n, sizeof(T));
return res;
}
@@ -202,22 +202,34 @@ template
T* reallocMB(T* p, size_t n, const char* name = nullptr) {
T* res = (T*)heap_caps_realloc_prefer(p, n * sizeof(T), 2, MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT); // calloc is malloc + memset(0);
if (res) {
- // EXT_LOGD(MB_TAG, "Re-Allocated %s: %d x %d bytes in %s s:%d", name?name:"", n, sizeof(T), isInPSRAM(res)?"PSRAM":"RAM", heap_caps_get_allocated_size(res));
+ // EXT_LOGD(MB_TAG, "Re-Allocated %s: %d x %d bytes in %s s:%d", name?name:"x", n, sizeof(T), isInPSRAM(res)?"PSRAM":"RAM", heap_caps_get_allocated_size(res));
} else
- EXT_LOGE(MB_TAG, "heap_caps_malloc of %d x %d not succeeded", n, sizeof(T));
+ EXT_LOGE(MB_TAG, "heap_caps_malloc for %s of %d x %d not succeeded", name?name:"x", n, sizeof(T));
return res;
}
+template
+void reallocMB2(T* &p, size_t &pSize, size_t n, const char* name = nullptr) {
+ T* res = (T*)heap_caps_realloc_prefer(p, n * sizeof(T), 2, MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT); // calloc is malloc + memset(0);
+ if (res) {
+ // EXT_LOGD(MB_TAG, "Re-Allocated %s: %d x %d bytes in %s s:%d", name?name:"x", n, sizeof(T), isInPSRAM(res)?"PSRAM":"RAM", heap_caps_get_allocated_size(res));
+ p = res;
+ pSize = n;
+ } else {
+ EXT_LOGE(MB_TAG, "heap_caps_malloc for %s of %d x %d not succeeded, keeping old %d", name?name:"x", n, sizeof(T), pSize);
+ }
+}
+
// free memory
template
void freeMB(T*& p, const char* name = nullptr) {
if (p) {
totalAllocatedMB -= heap_caps_get_allocated_size(p);
- // EXT_LOGD(MB_TAG, "free %s: x x %d bytes in %s, s:%d (tot:%d)", name?name:"", sizeof(T), isInPSRAM(p)?"PSRAM":"RAM", heap_caps_get_allocated_size(p), totalAllocatedMB);
+ // EXT_LOGD(MB_TAG, "free %s: x x %d bytes in %s, s:%d (tot:%d)", name?name:"x", sizeof(T), isInPSRAM(p)?"PSRAM":"RAM", heap_caps_get_allocated_size(p), totalAllocatedMB);
heap_caps_free(p);
p = nullptr;
} else
- EXT_LOGW(MB_TAG, "Nothing to free: pointer is null");
+ EXT_LOGW(MB_TAG, "Nothing to free for %s: pointer is null", name?name:"x");
}
// allocate vector
diff --git a/src/MoonLight/Layers/PhysicalLayer.cpp b/src/MoonLight/Layers/PhysicalLayer.cpp
index 957779100..474f6652f 100644
--- a/src/MoonLight/Layers/PhysicalLayer.cpp
+++ b/src/MoonLight/Layers/PhysicalLayer.cpp
@@ -83,6 +83,7 @@ void PhysicalLayer::setup() {
}
void PhysicalLayer::loop() {
+ if (lights.header.nrOfChannels >= lights.maxChannels) return; // in case alloc mem is not successful
// runs the loop of all effects / nodes in the layer
for (VirtualLayer* layer : layers) {
if (layer) {
@@ -119,6 +120,7 @@ void PhysicalLayer::loopDrivers() {
requestMapVirtual = false;
}
+ // for physical layer nodes
if (prevSize != lights.header.size) EXT_LOGD(ML_TAG, "onSizeChanged P %d,%d,%d -> %d,%d,%d", prevSize.x, prevSize.y, prevSize.z, lights.header.size.x, lights.header.size.y, lights.header.size.z);
for (Node* node : nodes) {
@@ -154,18 +156,24 @@ void PhysicalLayer::onLayoutPre() {
EXT_LOGD(ML_TAG, "pass %d mp:%d", pass, monitorPass);
if (pass == 1) {
+ // Hold mutex while modifying shared state!
+ if (layerP.lights.useDoubleBuffer) xSemaphoreTake(swapMutex, portMAX_DELAY);
+
lights.header.nrOfLights = 0; // for pass1 and pass2 as in pass2 virtual layer needs it
lights.header.size = {0, 0, 0};
- if (layerP.lights.useDoubleBuffer) xSemaphoreTake(swapMutex, portMAX_DELAY);
EXT_LOGD(ML_TAG, "positions in progress (%d -> 1)", lights.header.isPositions);
- lights.header.isPositions = 1; // in progress...
+ lights.header.isPositions = 1; // Stops effectTask from starting NEW frames
+
if (layerP.lights.useDoubleBuffer) xSemaphoreGive(swapMutex);
- delay(100); // wait to stop effects
+ delay(100); // Wait for any in-progress frame to complete
+
+ // Now safe to zero the buffer - effectTask won't start new frames while isPositions == 1
+ if (layerP.lights.useDoubleBuffer) xSemaphoreTake(swapMutex, portMAX_DELAY);
+ memset(lights.channelsE, 0, lights.maxChannels);
+ if (layerP.lights.useDoubleBuffer) xSemaphoreGive(swapMutex);
- // set all channels to 0 (e.g for multichannel to not activate unused channels, e.g. fancy modes on MHs)
- memset(lights.channelsE, 0, lights.maxChannels); // set all the channels to 0, positions in channelsE
- // dealloc pins
+ // dealloc pins (non-critical, can be outside mutex)
if (!monitorPass) {
memset(ledsPerPin, 0xFF, sizeof(ledsPerPin)); // UINT16_MAX is 2 * 0xFF
memset(ledPinsAssigned, 0, sizeof(ledPinsAssigned));
diff --git a/src/MoonLight/Layers/VirtualLayer.cpp b/src/MoonLight/Layers/VirtualLayer.cpp
index f640948a7..e97b08bd7 100644
--- a/src/MoonLight/Layers/VirtualLayer.cpp
+++ b/src/MoonLight/Layers/VirtualLayer.cpp
@@ -57,6 +57,7 @@ void VirtualLayer::loop() {
}
}
+ // for virtual nodes
if (prevSize != size) EXT_LOGD(ML_TAG, "onSizeChanged V %d,%d,%d -> %d,%d,%d", prevSize.x, prevSize.y, prevSize.z, size.x, size.y, size.z);
for (Node* node : nodes) {
if (prevSize != size) {
@@ -167,13 +168,13 @@ void VirtualLayer::setLight(const nrOfLights_t indexV, const uint8_t* channels,
}
break;
}
- } else {
+ } else { // no mapping
uint32_t index = indexV * layerP->lights.header.channelsPerLight + offset;
- // if (index + length <= layerP->lights.maxChannels) { // no mapping
- memcpy(&layerP->lights.channelsE[index], channels, length);
- // } else {
- // EXT_LOGW(ML_TAG, "%d + %d >= %d", indexV, length, layerP->lights.maxChannels);
- // }
+ if (index + length <= layerP->lights.maxChannels) {
+ memcpy(&layerP->lights.channelsE[index], channels, length);
+ } else {
+ EXT_LOGW(ML_TAG, "%d + %d >= %d (%d %d)", indexV, length, layerP->lights.maxChannels, index, offset);
+ }
}
}
@@ -215,15 +216,15 @@ T VirtualLayer::getLight(const nrOfLights_t indexV, uint8_t offset) const {
return T(); // not implemented yet
break;
}
- } else {
+ } else { // no mapping
uint32_t index = indexV * layerP->lights.header.channelsPerLight + offset;
- // if (index + sizeof(T) <= layerP->lights.maxChannels) { // no mapping
- return *(T*)&layerP->lights.channelsE[index];
- // } else {
- // // some operations will go out of bounds e.g. VUMeter, uncomment below lines if you wanna test on a specific effect
- // EXT_LOGW(ML_TAG, "%d + %d >= %d", indexV, sizeof(T), layerP->lights.maxChannels);
- // return T();
- // }
+ if (index + sizeof(T) <= layerP->lights.maxChannels) {
+ return *(T*)&layerP->lights.channelsE[index];
+ } else {
+ // some operations will go out of bounds e.g. VUMeter, uncomment below lines if you wanna test on a specific effect
+ EXT_LOGW(ML_TAG, "%d + %d >= %d", indexV, sizeof(T), layerP->lights.maxChannels);
+ return T();
+ }
}
}
@@ -322,16 +323,8 @@ void VirtualLayer::fill_rainbow(const uint8_t initialhue, const uint8_t deltahue
void VirtualLayer::createMappingTableAndAddOneToOne() {
if (mappingTableSize != size.x * size.y * size.z) {
- EXT_LOGD(ML_TAG, "Allocating mappingTable: nrOfLights=%d, sizeof(PhysMap)=%d, total bytes=%d",
- size.x * size.y * size.z, sizeof(PhysMap), size.x * size.y * size.z * sizeof(PhysMap));
- PhysMap* newTable = reallocMB(mappingTable, size.x * size.y * size.z, "mappingTable");
- if (newTable) {
- mappingTable = newTable;
- EXT_LOGD(ML_TAG, "realloc mappingTable %d -> %dx%dx%d", mappingTableSize, size.x, size.y, size.z);
- mappingTableSize = size.x * size.y * size.z;
- } else {
- EXT_LOGW(ML_TAG, "realloc mappingTable failed keeping oldSize %d", mappingTableSize);
- }
+ EXT_LOGD(ML_TAG, "Allocating mappingTable: nrOfLights=%d, sizeof(PhysMap)=%d, total bytes=%d", size.x * size.y * size.z, sizeof(PhysMap), size.x * size.y * size.z * sizeof(PhysMap));
+ reallocMB2(mappingTable, mappingTableSize, size.x * size.y * size.z, "mappingTable");
}
if (mappingTable && mappingTableSize) memset(mappingTable, 0, mappingTableSize * sizeof(PhysMap)); // on layout, set mappingTable to default PhysMap
diff --git a/src/MoonLight/Layers/VirtualLayer.h b/src/MoonLight/Layers/VirtualLayer.h
index 4854a023c..0048438ff 100644
--- a/src/MoonLight/Layers/VirtualLayer.h
+++ b/src/MoonLight/Layers/VirtualLayer.h
@@ -78,7 +78,7 @@ class VirtualLayer {
// they will be reused to avoid fragmentation
PhysMap* mappingTable = nullptr;
- nrOfLights_t mappingTableSize = 0;
+ size_t mappingTableSize = 0;
std::vector, VectorRAMAllocator > > mappingTableIndexes;
nrOfLights_t mappingTableIndexesSizeUsed = 0;
diff --git a/src/MoonLight/Modules/ModuleDrivers.h b/src/MoonLight/Modules/ModuleDrivers.h
index 3a1ea8846..d2cdcf494 100644
--- a/src/MoonLight/Modules/ModuleDrivers.h
+++ b/src/MoonLight/Modules/ModuleDrivers.h
@@ -32,6 +32,11 @@ class ModuleDrivers : public NodeManager {
}
void readPins() {
+ if (safeModeMB) {
+ EXT_LOGW(ML_TAG, "Safe mode enabled, not adding pins");
+ return;
+ }
+
_moduleIO->read(
[&](ModuleState& state) {
// find the pins in board definitions
@@ -163,7 +168,7 @@ class ModuleDrivers : public NodeManager {
node->moduleIO = _moduleIO; // to get pin allocations
node->moduleNodes = (Module*)this; // to request UI update
node->setup(); // run the setup of the effect
- node->onSizeChanged(Coord3D());
+ node->onSizeChanged(Coord3D()); // to init memory allocations
// layers[0]->nodes.reserve(index+1);
// from here it runs concurrently in the drivers task
diff --git a/src/MoonLight/Modules/ModuleEffects.h b/src/MoonLight/Modules/ModuleEffects.h
index af5b75022..449d90a0b 100644
--- a/src/MoonLight/Modules/ModuleEffects.h
+++ b/src/MoonLight/Modules/ModuleEffects.h
@@ -156,7 +156,7 @@ class ModuleEffects : public NodeManager {
addControlValue(control, getNameAndTags());
addControlValue(control, getNameAndTags());
addControlValue(control, getNameAndTags());
- addControlValue(control, getNameAndTags());
+ addControlValue(control, getNameAndTags());
// find all the .sc files on FS
File rootFolder = ESPFS.open("/");
@@ -252,7 +252,7 @@ class ModuleEffects : public NodeManager {
if (!node) node = checkAndAlloc(name);
if (!node) node = checkAndAlloc(name);
if (!node) node = checkAndAlloc(name);
- if (!node) node = checkAndAlloc(name);
+ if (!node) node = checkAndAlloc(name);
#if FT_LIVESCRIPT
if (!node) {
@@ -270,7 +270,7 @@ class ModuleEffects : public NodeManager {
// node->moduleIO = _moduleIO; // to get pin allocations
node->moduleNodes = (Module*)this; // to request UI update
node->setup(); // run the setup of the effect
- node->onSizeChanged(Coord3D());
+ node->onSizeChanged(Coord3D()); // to init memory allocations
// layers[0]->nodes.reserve(index+1);
// from here it runs concurrently in the effects task
diff --git a/src/MoonLight/Modules/ModuleLightsControl.h b/src/MoonLight/Modules/ModuleLightsControl.h
index 40e4bd0ab..d50e27dd8 100644
--- a/src/MoonLight/Modules/ModuleLightsControl.h
+++ b/src/MoonLight/Modules/ModuleLightsControl.h
@@ -215,6 +215,11 @@ class ModuleLightsControl : public Module {
}
void readPins() {
+ if (safeModeMB) {
+ EXT_LOGW(ML_TAG, "Safe mode enabled, not adding pins");
+ return;
+ }
+
moduleIO.read(
[&](ModuleState& state) {
pinRelayLightsOn = UINT8_MAX;
diff --git a/src/MoonLight/Nodes/Drivers/D_Infrared.h b/src/MoonLight/Nodes/Drivers/D_Infrared.h
index 83602ed77..c34ffb741 100644
--- a/src/MoonLight/Nodes/Drivers/D_Infrared.h
+++ b/src/MoonLight/Nodes/Drivers/D_Infrared.h
@@ -38,6 +38,11 @@ class IRDriver : public Node {
uint8_t irPreset = 1;
void readPins() {
+ if (safeModeMB) {
+ EXT_LOGW(ML_TAG, "Safe mode enabled, not adding pins");
+ return;
+ }
+
moduleIO->read(
[&](ModuleState& state) {
pinInfrared = UINT8_MAX;
diff --git a/src/MoonLight/Nodes/Effects/E_MoonLight.h b/src/MoonLight/Nodes/Effects/E_MoonLight.h
index bcefa71cb..8f322dbd9 100644
--- a/src/MoonLight/Nodes/Effects/E_MoonLight.h
+++ b/src/MoonLight/Nodes/Effects/E_MoonLight.h
@@ -69,10 +69,10 @@ class StarSkyEffect : public Node {
~StarSkyEffect() { resetStars(); }
void resetStars() {
- if (stars_indexes) freeMB(stars_indexes);
- if (stars_fade_dir) freeMB(stars_fade_dir);
- if (stars_brightness) freeMB(stars_brightness);
- if (stars_colors) freeMB(stars_colors);
+ if (stars_indexes) freeMB(stars_indexes, "indexes");
+ if (stars_fade_dir) freeMB(stars_fade_dir, "fade");
+ if (stars_brightness) freeMB(stars_brightness, "brightness");
+ if (stars_colors) freeMB(stars_colors, "colors");
stars_indexes = nullptr;
stars_fade_dir = nullptr;
stars_brightness = nullptr;
@@ -327,7 +327,7 @@ class ScrollingTextEffect : public Node {
}
void loop() override {
- layer->fadeToBlackBy();
+ layer->fadeToBlackBy(100);
#define nrOfChoices 7
uint8_t choice;
@@ -1667,8 +1667,9 @@ class MarioTestEffect : public Node {
class RingEffect : public Node {
protected:
- void setRing(int ring, CRGB colour) { // so britisch ;-)
- layer->setRGB(Coord3D(0, ring), colour); // 1D effect on y-axis (default)
+ void setRing(int ring, CRGB colour) { // so britisch ;-)
+ for (int x = 0; x < layer->size.x; x++)
+ for (int z = 0; z < layer->size.z; z++) layer->setRGB(Coord3D(x, ring, z), colour); // 1D effect on y-axis (default)
}
};
@@ -1681,24 +1682,21 @@ class RingRandomFlowEffect : public RingEffect {
// void setup() override {} //so no palette control is created
uint8_t* hue = nullptr;
+ size_t hueSize = 0;
- ~RingRandomFlowEffect() { freeMB(hue); }
-
- void onSizeChanged(const Coord3D& prevSize) override {
- freeMB(hue);
- hue = allocMB(layer->size.y);
- if (!hue) {
- EXT_LOGE(ML_TAG, "allocate hue failed");
- }
+ ~RingRandomFlowEffect() {
+ if (hue) freeMB(hue, "hue");
}
+ void onSizeChanged(const Coord3D& prevSize) override { reallocMB2(hue, hueSize, layer->size.y, "hue"); }
+
void loop() override {
if (hue) {
hue[0] = random(0, 255);
- for (int r = 0; r < layer->size.y; r++) {
+ for (int r = 0; r < hueSize; r++) {
setRing(r, CHSV(hue[r], 255, 255));
}
- for (int r = (layer->size.y - 1); r >= 1; r--) {
+ for (int r = (hueSize - 1); r >= 1; r--) {
hue[r] = hue[(r - 1)]; // set this ruing based on the inner
}
// FastLED.delay(SPEED);
diff --git a/src/MoonLight/Nodes/Effects/E_MoonModules.h b/src/MoonLight/Nodes/Effects/E_MoonModules.h
index 1abb4014a..9fea467a6 100644
--- a/src/MoonLight/Nodes/Effects/E_MoonModules.h
+++ b/src/MoonLight/Nodes/Effects/E_MoonModules.h
@@ -130,7 +130,7 @@ class GameOfLifeEffect : public Node {
uint16_t spaceshipCRC;
uint16_t cubeGliderCRC;
bool soloGlider;
- uint16_t generation;
+ uint16_t generation = 0;
bool birthNumbers[9];
bool surviveNumbers[9];
CRGB prevPalette;
@@ -147,7 +147,7 @@ class GameOfLifeEffect : public Node {
if (!cells || !futureCells || !cellColors) return;
// Setup Grid
- memset(cells, 0, dataSize);
+ memset(cells, 0, cellsSize);
memset(cellColors, 0, layer->size.x * layer->size.y * layer->size.z);
for (int x = 0; x < layer->size.x; x++)
@@ -162,38 +162,38 @@ class GameOfLifeEffect : public Node {
// layer->setRGB(Coord3D(x,y,z), bgColor); // Color set in redraw loop
}
}
- memcpy(futureCells, cells, dataSize);
+ memcpy(futureCells, cells, cellsSize);
soloGlider = false;
// Change CRCs
- uint16_t crc = crc16((const unsigned char*)cells, dataSize);
+ uint16_t crc = crc16((const unsigned char*)cells, cellsSize);
oscillatorCRC = crc, spaceshipCRC = crc, cubeGliderCRC = crc;
gliderLength = lcm(layer->size.y, layer->size.x) * 4;
cubeGliderLength = gliderLength * 6; // Change later for rectangular cuboid
}
- int dataSize = 0;
+ size_t cellsSize = 0;
+ size_t cellColorsSize = 0;
~GameOfLifeEffect() override {
- freeMB(cells);
- freeMB(futureCells);
- freeMB(cellColors);
+ if (cells) freeMB(cells, "cells");
+ if (futureCells) freeMB(futureCells, "futureCells");
+ if (cellColors) freeMB(cellColors, "cellColors");
}
void onSizeChanged(const Coord3D& prevSize) override {
- dataSize = ((layer->size.x * layer->size.y * layer->size.z + 7) / 8);
+ // EXT_LOGW(ML_TAG, "GameOfLife onSizeChanged %d,%d,%d -> %d,%d,%d", prevSize.x, prevSize.y, prevSize.z, layer->size.x, layer->size.y, layer->size.z);
- freeMB(cells);
- freeMB(futureCells);
- freeMB(cellColors);
-
- cells = allocMB(dataSize);
- futureCells = allocMB(dataSize);
- cellColors = allocMB(layer->size.x * layer->size.y * layer->size.z);
+ reallocMB2(cells, cellsSize, (layer->size.x * layer->size.y * layer->size.z + 7) / 8, "cells");
+ reallocMB2(futureCells, cellsSize, cellsSize, "futureCells"); // take the cellsSize of cells (can be smaller then asked for if not enough memory)
+ reallocMB2(cellColors, cellColorsSize, layer->size.x * layer->size.y * layer->size.z, "cellColors");
if (!cells || !futureCells || !cellColors) {
EXT_LOGE(ML_TAG, "allocation of cells || !futureCells || !cellColors failed");
+ // freeMB will be done when GOL is deleted
+ return;
}
+ EXT_LOGD(ML_TAG, "allocation of cells futureCells cellColors successful %d %d", cellsSize, layer->nrOfLights);
startNewGameOfLife();
}
@@ -218,30 +218,34 @@ class GameOfLifeEffect : public Node {
bool blurDead = step > millis() && !fadedBackground;
// Redraw Loop
if (generation <= 1 || blurDead) { // Readd overlay support when implemented
- for (int x = 0; x < layer->size.x; x++)
- for (int y = 0; y < layer->size.y; y++)
+ for (int x = 0; x < layer->size.x; x++) {
+ for (int y = 0; y < layer->size.y; y++) {
for (int z = 0; z < layer->size.z; z++) {
uint16_t cIndex = layer->XYZUnModified(Coord3D(x, y, z)); // Current cell index (bit grid lookup)
- Coord3D cLocPos = Coord3D(x, y, z);
- uint16_t cLoc = layer->XYZ(cLocPos); // Current cell location (led index)
- if (!layer->isMapped(cIndex)) continue;
- bool alive = getBitValue(cells, cIndex);
- bool recolor = (alive && generation == 1 && cellColors[cIndex] == 0 && !random(16)); // Palette change or Initial Color
- // Redraw alive if palette changed, spawn initial colors randomly, age alive cells while paused
- if (alive && recolor) {
- cellColors[cIndex] = random8(1, 255);
- layer->setRGB(cLoc, colorByAge ? CRGB::Green : ColorFromPalette(layerP.palette, cellColors[cIndex]));
- } else if (alive && colorByAge && !generation)
- layer->blendColor(cLoc, CRGB::Red, 248); // Age alive cells while paused
- else if (alive && cellColors[cIndex] != 0)
- layer->setRGB(cLoc, colorByAge ? CRGB::Green : ColorFromPalette(layerP.palette, cellColors[cIndex]));
- // Redraw dead if palette changed, blur paused game, fade on newgame
- // if (!alive && (paletteChanged || disablePause)) layer->setRGB(cLoc, bgColor); // Remove blended dead cells
- else if (!alive && blurDead)
- layer->blendColor(cLoc, bgColor, blur); // Blend dead cells while paused
- else if (!alive && generation == 1)
- layer->blendColor(cLoc, bgColor, 248); // Fade dead on new game
+ if (cIndex < cellColorsSize) {
+ Coord3D cLocPos = Coord3D(x, y, z);
+ uint16_t cLoc = layer->XYZ(cLocPos); // Current cell location (led index)
+ if (!layer->isMapped(cIndex)) continue;
+ bool alive = getBitValue(cells, cIndex);
+ bool recolor = (alive && generation == 1 && cellColors[cIndex] == 0 && !random(16)); // Palette change or Initial Color
+ // Redraw alive if palette changed, spawn initial colors randomly, age alive cells while paused
+ if (alive && recolor) {
+ cellColors[cIndex] = random8(1, 255);
+ layer->setRGB(cLoc, colorByAge ? CRGB::Green : ColorFromPalette(layerP.palette, cellColors[cIndex]));
+ } else if (alive && colorByAge && !generation)
+ layer->blendColor(cLoc, CRGB::Red, 248); // Age alive cells while paused
+ else if (alive && cellColors[cIndex] != 0)
+ layer->setRGB(cLoc, colorByAge ? CRGB::Green : ColorFromPalette(layerP.palette, cellColors[cIndex]));
+ // Redraw dead if palette changed, blur paused game, fade on newgame
+ // if (!alive && (paletteChanged || disablePause)) layer->setRGB(cLoc, bgColor); // Remove blended dead cells
+ else if (!alive && blurDead)
+ layer->blendColor(cLoc, bgColor, blur); // Blend dead cells while paused
+ else if (!alive && generation == 1)
+ layer->blendColor(cLoc, bgColor, 248); // Fade dead on new game
+ }
}
+ }
+ }
}
// if (!speed || step > millis() || millis() - step < 1000 / speed) return; // Check if enough time has passed for updating
@@ -252,8 +256,8 @@ class GameOfLifeEffect : public Node {
const int zAxis = (layer->layerDimension == _3D) ? 1 : 0; // Avoids looping through z axis neighbors if 2D
bool disableWrap = !wrap || soloGlider || generation % 1500 == 0 || zAxis;
// Loop through all cells. Count neighbors, apply rules, setPixel
- for (int x = 0; x < layer->size.x; x++)
- for (int y = 0; y < layer->size.y; y++)
+ for (int x = 0; x < layer->size.x; x++) {
+ for (int y = 0; y < layer->size.y; y++) {
for (int z = 0; z < layer->size.z; z++) {
Coord3D cPos = Coord3D(x, y, z);
uint16_t cIndex = layer->XYZUnModified(cPos);
@@ -294,7 +298,7 @@ class GameOfLifeEffect : public Node {
} else if (!cellValue && birthNumbers[neighbors]) {
// Reproduction
setBitValue(futureCells, cIndex, true);
- uint8_t colorIndex = nColors[random8(colorCount)];
+ uint8_t colorIndex = (colorCount > 0) ? nColors[random8(colorCount)] : random8();
if (random8(100) < mutation) colorIndex = random8();
cellColors[cIndex] = colorIndex;
layer->setRGB(cPos, colorByAge ? CRGB::Green : ColorFromPalette(layerP.palette, colorIndex));
@@ -314,19 +318,21 @@ class GameOfLifeEffect : public Node {
}
}
}
+ }
+ }
if (aliveCount == 5)
soloGlider = true;
else
soloGlider = false;
- memcpy(cells, futureCells, dataSize);
- uint16_t crc = crc16((const unsigned char*)cells, dataSize);
+ memcpy(cells, futureCells, cellsSize);
+ uint16_t crc = crc16((const unsigned char*)cells, cellsSize);
bool repetition = false;
if (!aliveCount || crc == oscillatorCRC || crc == spaceshipCRC || crc == cubeGliderCRC) repetition = true;
if ((repetition && infinite) || (infinite && !random8(50)) || (infinite && float(aliveCount) / (aliveCount + deadCount) < 0.05)) {
placePentomino(futureCells, colorByAge); // Place R-pentomino/Glider if infinite mode is enabled
- memcpy(cells, futureCells, dataSize);
+ memcpy(cells, futureCells, cellsSize);
repetition = false;
}
if (repetition) {
@@ -338,7 +344,7 @@ class GameOfLifeEffect : public Node {
if (generation % 16 == 0) oscillatorCRC = crc;
if (gliderLength && generation % gliderLength == 0) spaceshipCRC = crc;
if (cubeGliderLength && generation % cubeGliderLength == 0) cubeGliderCRC = crc;
- (generation)++;
+ generation++;
step = millis();
}
}; // GameOfLife
diff --git a/src/MoonLight/Nodes/Effects/E_WLED.h b/src/MoonLight/Nodes/Effects/E_WLED.h
index ab8162bbd..cca5b1e08 100644
--- a/src/MoonLight/Nodes/Effects/E_WLED.h
+++ b/src/MoonLight/Nodes/Effects/E_WLED.h
@@ -26,21 +26,14 @@ class BouncingBallsEffect : public Node {
}
Ball (*balls)[maxNumBalls] = nullptr; //[maxColumns][maxNumBalls];
- uint16_t ballsSize = 0;
+ size_t ballsSize = 0;
- ~BouncingBallsEffect() override { freeMB(balls); }
-
- void onSizeChanged(const Coord3D& prevSize) override {
- Ball(*newAlloc)[maxNumBalls] = reallocMB(balls, layer->size.x);
-
- if (newAlloc) {
- balls = newAlloc;
- ballsSize = layer->size.x;
- } else {
- EXT_LOGE(ML_TAG, "(re)allocate balls failed");
- }
+ ~BouncingBallsEffect() override {
+ if (balls) freeMB(balls, "balls");
}
+ void onSizeChanged(const Coord3D& prevSize) override { reallocMB2(balls, ballsSize, layer->size.x, "balls"); }
+
void loop() override {
if (!balls) return;
@@ -56,7 +49,7 @@ class BouncingBallsEffect : public Node {
// for (size_t i = 0; i < maxNumBalls; i++) balls[i].lastBounceTime = time;
// }
- for (int x = 0; x < MIN(layer->size.x, ballsSize); x++) {
+ for (int x = 0; x < ballsSize; x++) {
for (size_t i = 0; i < MIN(numBalls, maxNumBalls); i++) {
float timeSinceLastBounce = (time - balls[x][i].lastBounceTime) / ((255 - grav) / 64 + 1);
float timeSec = timeSinceLastBounce / 1000.0f;
@@ -330,20 +323,14 @@ class GEQEffect : public Node {
}
uint16_t* previousBarHeight = nullptr;
- uint8_t previousBarHeightSize = 0;
-
- ~GEQEffect() { freeMB(previousBarHeight); }
+ size_t previousBarHeightSize = 0;
- void onSizeChanged(const Coord3D& prevSize) override {
- uint16_t* newAlloc = reallocMB(previousBarHeight, layer->size.x);
- if (newAlloc) {
- previousBarHeight = newAlloc;
- previousBarHeightSize = layer->size.x;
- } else {
- EXT_LOGE(ML_TAG, "(re)allocate previousBarHeight failed");
- }
+ ~GEQEffect() {
+ if (previousBarHeight) freeMB(previousBarHeight, "previousBarHeight");
}
+ void onSizeChanged(const Coord3D& prevSize) override { reallocMB2(previousBarHeight, previousBarHeightSize, layer->size.x, "previousBarHeight"); }
+
void loop() override {
const int NUM_BANDS = NUM_GEQ_CHANNELS; // ::map(layer->custom1, 0, 255, 1, 16);
@@ -564,9 +551,11 @@ class PacManEffect : public Node {
}
pacmancharacters_t* character = nullptr;
- uint8_t nrOfCharacters = 0;
+ size_t nrOfCharacters = 0;
- ~PacManEffect() { freeMB(character); }
+ ~PacManEffect() {
+ if (character) freeMB(character, "character");
+ }
void onSizeChanged(const Coord3D& prevSize) override { initializePacMan(); }
@@ -591,13 +580,7 @@ class PacManEffect : public Node {
EXT_LOGD(ML_TAG, "#l:%d #pd:%d #g:%d #pd:%d", layer->nrOfLights, numPowerDotsControl, numGhosts, numPowerDots);
- pacmancharacters_t* newAlloc = reallocMB(character, numGhosts + numPowerDots + 1); // +1 is the PacMan character
- if (newAlloc) {
- character = newAlloc;
- nrOfCharacters = numGhosts + numPowerDots + 1;
- } else {
- EXT_LOGE(ML_TAG, "(re)allocate character failed"); // keep old (if existed)
- }
+ reallocMB2(character, nrOfCharacters, numGhosts + numPowerDots + 1, "character"); // +1 is the PacMan character
if (nrOfCharacters > 0) {
character[PACMAN].color = CRGB::Yellow;
@@ -988,16 +971,11 @@ class TetrixEffect : public Node {
}
Tetris* drops = nullptr;
- uint16_t nrOfDrops = 0;
+ size_t nrOfDrops = 0;
void onSizeChanged(const Coord3D& prevSize) override {
- Tetris* newAlloc = reallocMB(drops, layer->size.x);
- if (newAlloc) {
- drops = newAlloc;
- nrOfDrops = layer->size.x;
- } else {
- EXT_LOGE(ML_TAG, "(re)allocate drops failed"); // keep old (if existed)
- }
+ reallocMB2(drops, nrOfDrops, layer->size.x, "drops");
+
for (int i = 0; i < nrOfDrops; i++) {
drops[i].stack = 0; // reset brick stack size
drops[i].step = millis() + 2000; // start by fading out strip
@@ -1005,7 +983,9 @@ class TetrixEffect : public Node {
}
}
- ~TetrixEffect() override { freeMB(drops); };
+ ~TetrixEffect() override {
+ if (drops) freeMB(drops, "drops");
+ };
void loop() override {
if (!drops) return;
@@ -1143,7 +1123,7 @@ class PopCornEffect : public Node {
uint16_t ledIndex = popcorn[i].pos;
CRGB col = ColorFromPalette(layerP.palette, popcorn[i].colIndex * (256 / maxNumPopcorn));
if (ledIndex < layer->size.y) {
- layer->setRGB(ledIndex, col);
+ // layer->setRGB(Coord3D(0, ledIndex), col);
for (int x = 0; x < layer->size.x; x++)
for (int z = 0; z < layer->size.z; z++) layer->setRGB(Coord3D(x, ledIndex, z), col);
}
@@ -1332,10 +1312,12 @@ class OctopusEffect : public Node {
Coord3D prevLedSize;
Map_t* rMap = nullptr;
- uint16_t rMapSize = 0;
+ size_t rMapSize = 0;
uint32_t step;
- ~OctopusEffect() { freeMB(rMap); }
+ ~OctopusEffect() {
+ if (rMap) freeMB(rMap, "rMap");
+ }
void setRMap() {
const uint8_t C_X = layer->size.x / 2 + (offset.x - 50) * layer->size.x / 100;
@@ -1353,16 +1335,7 @@ class OctopusEffect : public Node {
}
}
- void onSizeChanged(const Coord3D& prevSize) override {
- Map_t* newAlloc = reallocMB(rMap, layer->size.x * layer->size.y);
- if (newAlloc) {
- rMap = newAlloc;
- rMapSize = layer->size.x * layer->size.y;
- setRMap();
- } else {
- EXT_LOGE(ML_TAG, "(re)allocate rMap failed");
- }
- }
+ void onSizeChanged(const Coord3D& prevSize) override { reallocMB2(rMap, rMapSize, layer->size.x * layer->size.y, "rMap"); }
void loop() override {
if (rMap) { // check if rMap allocation successful
@@ -1647,12 +1620,14 @@ class FlowEffect : public Node {
layer->fill_solid(ColorFromPalette(layerP.palette, -counter));
- for (int z = 0; z < zones; z++) {
- uint16_t pos = offset + z * zoneLen;
+ for (int zone = 0; zone < zones; zone++) {
+ uint16_t pos = offset + zone * zoneLen;
for (int i = 0; i < zoneLen; i++) {
uint8_t colorIndex = (i * 255 / zoneLen) - counter;
- uint16_t led = (z & 0x01) ? i : (zoneLen - 1) - i;
- layer->setRGB(Coord3D(0, pos + led), ColorFromPalette(layerP.palette, colorIndex));
+ uint16_t led = (zone & 0x01) ? i : (zoneLen - 1) - i;
+ CRGB color = ColorFromPalette(layerP.palette, colorIndex);
+ for (int x = 0; x < layer->size.x; x++)
+ for (int z = 0; z < layer->size.z; z++) layer->setRGB(Coord3D(x, pos + led, z), color);
}
}
}
@@ -1733,23 +1708,19 @@ class RainEffect : public Node {
}
Spark* drops = nullptr;
- uint16_t nrOfDrops = 0;
+ size_t nrOfDrops = 0;
- ~RainEffect() override { freeMB(drops); }
+ ~RainEffect() override {
+ if (drops) freeMB(drops, "drops");
+ }
void onSizeChanged(const Coord3D& prevSize) override {
- Spark* newAlloc = reallocMB(drops, layer->size.x);
+ reallocMB2(drops, nrOfDrops, layer->size.x, "drops");
- if (newAlloc) {
- drops = newAlloc;
- nrOfDrops = layer->size.x;
- for (int x = 0; x < layer->size.x; x++) {
- drops[x].pos = 0;
- drops[x].col = 0;
- drops[x].vel = 0;
- }
- } else {
- EXT_LOGE(ML_TAG, "(re)allocate drops failed");
+ for (int x = 0; x < nrOfDrops; x++) {
+ drops[x].pos = 0;
+ drops[x].col = 0;
+ drops[x].vel = 0;
}
}
@@ -1809,23 +1780,19 @@ class DripEffect : public Node {
}
Spark (*drops)[maxNumDrops] = nullptr; //[maxColumns][maxNumBalls];
- uint16_t nrOfDrops = 0;
+ size_t nrOfDrops = 0;
- ~DripEffect() override { freeMB(drops); }
+ ~DripEffect() override {
+ if (drops) freeMB(drops, "drops");
+ }
void onSizeChanged(const Coord3D& prevSize) override {
- Spark(*newAlloc)[maxNumDrops] = reallocMB(drops, layer->size.x);
+ reallocMB2(drops, nrOfDrops, layer->size.x, "drops");
- if (newAlloc) {
- drops = newAlloc;
- nrOfDrops = layer->size.x;
- for (int x = 0; x < layer->size.x; x++) {
- for (int j = 0; j < maxNumDrops; j++) {
- drops[x][j].colIndex = init; // Set to init so loop() will initialize properly
- }
+ for (int x = 0; x < nrOfDrops; x++) {
+ for (int j = 0; j < maxNumDrops; j++) {
+ drops[x][j].colIndex = init; // Set to init so loop() will initialize properly
}
- } else {
- EXT_LOGE(ML_TAG, "(re)allocate drops failed");
}
}
diff --git a/src/MoonLight/Nodes/Modifiers/M_MoonLight.h b/src/MoonLight/Nodes/Modifiers/M_MoonLight.h
index ac1fa393c..b155d033d 100644
--- a/src/MoonLight/Nodes/Modifiers/M_MoonLight.h
+++ b/src/MoonLight/Nodes/Modifiers/M_MoonLight.h
@@ -55,8 +55,8 @@ class MirrorModifier : public Node {
static const char* tags() { return "💎🐙"; }
bool mirrorX = true;
- bool mirrorY = false;
- bool mirrorZ = false;
+ bool mirrorY = true;
+ bool mirrorZ = true;
void setup() override {
addControl(mirrorX, "mirrorX", "checkbox");
@@ -195,19 +195,19 @@ class PinwheelModifier : public Node {
};
// Idea and first implementation (WLEDMM Art-Net) by @Troy
-class RippleYZModifier : public Node {
+class RippleXZModifier : public Node {
public:
- static const char* name() { return "RippleYZ"; }
+ static const char* name() { return "RippleXZ"; }
static uint8_t dim() { return _3D; }
static const char* tags() { return "💎💫"; }
bool shrink = true;
- bool towardsY = true;
+ bool towardsX = true;
bool towardsZ = false;
void setup() override {
addControl(shrink, "shrink", "checkbox");
- addControl(towardsY, "towardsY", "checkbox");
+ addControl(towardsX, "towardsX", "checkbox");
addControl(towardsZ, "towardsZ", "checkbox");
}
@@ -220,21 +220,21 @@ class RippleYZModifier : public Node {
// layer->size.y++;
// layer->size.z++;
if (shrink) {
- if (towardsY) layer->size.y = 1;
+ if (towardsX) layer->size.x = 1;
if (towardsZ) layer->size.z = 1;
}
}
void modifyPosition(Coord3D& position) override {
if (shrink) {
- if (towardsY) position.y = 0;
+ if (towardsX) position.x = 0;
if (towardsZ) position.z = 0;
}
}
void loop() override {
- // 1D->2D: each Y is rippled through the X-axis
- if (towardsY) {
+ // 1D->2D: ripple effect propagates along the X-axis
+ if (towardsX) {
if (layer->effectDimension == _1D && layer->layerDimension > _1D) {
for (int x = layer->size.x - 1; x >= 1; x--) {
for (int y = 0; y < layer->size.y; y++) {
@@ -244,7 +244,7 @@ class RippleYZModifier : public Node {
}
}
- // 2D->3D: each XY plane is rippled through the z-axis
+ // 2D->3D: each XY plane propagates along the Z-axis
if (towardsZ) { // not relevant for 2D fixtures
if (layer->effectDimension < _3D && layer->layerDimension == _3D) {
for (int z = layer->size.z - 1; z >= 1; z--) {
@@ -257,7 +257,7 @@ class RippleYZModifier : public Node {
}
}
}
-}; // RippleYZ
+}; // RippleXZ
// RotateModifier rotates the light position around the center of the layout.
// It can flip the x and y coordinates, reverse the rotation direction, and alternate the rotation
diff --git a/src/main.cpp b/src/main.cpp
index 77ce13848..cd8b54809 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -132,8 +132,8 @@ void effectTask(void* pvParameters) {
if (layerP.lights.header.isPositions == 0 && !newFrameReady) { // within mutex as driver task can change this
if (layerP.lights.useDoubleBuffer) {
- xSemaphoreGive(swapMutex);
memcpy(layerP.lights.channelsE, layerP.lights.channelsD, layerP.lights.header.nrOfChannels); // Copy previous frame (channelsD) to working buffer (channelsE)
+ xSemaphoreGive(swapMutex);
}
layerP.loop();