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. ![esp32-s3-n16r8v](../firmware/installer/images/esp32-s3-n8r8v.jpg){: 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"} ![esp32-p4-nano](../firmware/installer/images/esp32-p4-nano.jpg){: 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 | ![esp32-d0](../firmware/installer/images/esp32-d0.jpg){: style="width:100px"} | | [Dig Uno](https://quinled.info/pre-assembled-quinled-dig-uno):
![Dig Uno](https://quinled.info/wp-content/uploads/2020/02/QuinLED-Dig-Uno-v3_front.png){: style="width:100px"}
[Dig Quad](https://quinled.info/pre-assembled-quinled-dig-quad):
![Dig Quad](https://quinled.info/wp-content/uploads/2021/11/QuinLED-Dig-Quad-AB_v3r1-2048x1154.png){: style="width:100px"}
[Dig2Go](https://quinled.info/quinled-dig2go):
![Dig2Go](https://shop.allnetchina.cn/cdn/shop/products/Led_4.jpg?v=1680836018&width=1600){: style="width:100px"} | +| esp32-d0 | ![esp32-d0](../firmware/installer/images/esp32-d0.jpg){: style="width:100px"} | | [Dig2Go](https://quinled.info/quinled-dig2go):
![Dig2Go](https://shop.allnetchina.cn/cdn/shop/products/Led_4.jpg?v=1680836018&width=1600){: style="width:100px"}
[Dig Uno](https://quinled.info/pre-assembled-quinled-dig-uno):
![Dig Uno](https://quinled.info/wp-content/uploads/2020/02/QuinLED-Dig-Uno-v3_front.png){: style="width:100px"}
[Dig Quad](https://quinled.info/pre-assembled-quinled-dig-quad):
![Dig Quad](https://quinled.info/wp-content/uploads/2021/11/QuinLED-Dig-Quad-AB_v3r1-2048x1154.png){: style="width:100px"} | | esp32-d0-16mb | ![esp32-d0-16mb](../firmware/installer/images/esp32-d0-16mb.jpg){: style="width:100px"} | | [Dig Octa](https://quinled.info/quinled-dig-octa):
![Dig Octa](https://quinled.info/wp-content/uploads/2024/10/20240924_141857-2048x1444.png){: 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)
![Shield](https://cdn.tindiemedia.com/images/resize/44YE-eNQ9pJQUh_SmtwwfBXFbAE=/p/fit-in/1370x912/filters:fill(fff)/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) | ![esp32-d0-pico2](../firmware/installer/images/esp32-d0-pico2.jpg){: style="width:100px"} | | [DigNext2](https://quinled.info/dig-next-2):
![DigNext2](https://quinled.info/wp-content/uploads/2026/01/P1087754-Enhanced-NR-2560x1358.jpg){: style="width:100px"} | | esp32-s3-n8r8v | ![esp32-s3-n8r8v](../firmware/installer/images/esp32-s3-n8r8v.jpg){: style="width:100px"} | | SE-16p
![SE-16p](../firmware/installer/images/esp32-s3-stephanelec-16p.jpg){: style="width:100px"} | | esp32-s3-n16r8v | ![esp32-s3-n16r8v](../firmware/installer/images/esp32-s3-n8r8v.jpg){: style="width:100px"} | | [Ali*](https://s.click.aliexpress.com/e/_DBAtJ2H){:target="_blank"} | | esp32-s3-atoms3r | ![esp32-s3-atoms3r](../firmware/installer/images/esp32-s3-atoms3r.jpg){: 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 ![Dig2Go](https://shop.allnetchina.cn/cdn/shop/products/Led_4.jpg?v=1680836018&width=1600){: style="width:100px"} +![DigNext2](https://quinled.info/wp-content/uploads/2026/01/P1087754-Enhanced-NR-2560x1358.jpg){: style="width:100px"} ![Dig Uno](https://quinled.info/wp-content/uploads/2020/02/QuinLED-Dig-Uno-v3_front.png){: style="width:100px"} ![Dig Quad](https://quinled.info/wp-content/uploads/2021/11/QuinLED-Dig-Quad-AB_v3r1-2048x1154.png){: style="width:100px"} ![Dig Octa](https://quinled.info/wp-content/uploads/2024/10/20240924_141857-2048x1444.png){: 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 | ![Rotate](https://github.com/user-attachments/assets/c622a9df-318a-4f83-81c0-f5a5c7bafb7b) | Rotate | | | Checkerboard | ![Checkerboard](https://github.com/user-attachments/assets/54970267-35af-406c-9558-c1f4219a71c0) | Checkerboard | | | Pinwheel 🧊 | ![PinWheel](https://github.com/user-attachments/assets/e5dbadbe-eeb1-41e5-b197-ec4bd5366aea) | 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 🧊 | ![RippleYZ](https://github.com/user-attachments/assets/0918efac-6367-420f-b0e3-d796d9551953) | 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 🧊 | ![RippleXZ](https://github.com/user-attachments/assets/0918efac-6367-420f-b0e3-d796d9551953) | 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();