diff --git a/pio-scripts/download_fs.py b/pio-scripts/download_fs.py new file mode 100644 index 0000000000..f244dc2cbb --- /dev/null +++ b/pio-scripts/download_fs.py @@ -0,0 +1,285 @@ +# Written by Maximilian Gerhardt +# 29th December 2020 +# and Christian Baars, Johann Obermeier +# 2023 / 2024 +# License: Apache +# Expanded from functionality provided by PlatformIO's espressif32 and espressif8266 platforms, credited below. +# This script provides functions to download the filesystem (LittleFS) from a running ESP32 / ESP8266 +# over the serial bootloader using esptool.py, and mklittlefs for extracting. +# run by either using the VSCode task "Custom" -> "Download Filesystem" +# or by doing 'pio run -t downloadfs' (with optional '-e ') from the commandline. +# output will be saved, by default, in the "unpacked_fs" of the project. +# this folder can be changed by writing 'custom_unpack_dir = some_other_dir' in the corresponding platformio.ini +# environment. +import re +import sys +from os.path import isfile, join +from enum import Enum +import os +import subprocess +import shutil + +Import("env") +platform = env.PioPlatform() +board = env.BoardConfig() +mcu = board.get("build.mcu", "esp32") + + +class FSType(Enum): + LITTLEFS="littlefs" + FATFS="fatfs" + +class FSInfo: + def __init__(self, fs_type, start, length, page_size, block_size): + self.fs_type = fs_type + self.start = start + self.length = length + self.page_size = page_size + self.block_size = block_size + def __repr__(self): + return f"FS type {self.fs_type} Start {hex(self.start)} Len {self.length} Page size {self.page_size} Block size {self.block_size}" + # extract command supposed to be implemented by subclasses + def get_extract_cmd(self, input_file, output_dir): + raise NotImplementedError() + +class FS_Info(FSInfo): + def __init__(self, start, length, page_size, block_size): + self.tool = env["MKFSTOOL"] + self.tool = join(platform.get_package_dir("tool-mklittlefs"), self.tool) + super().__init__(FSType.LITTLEFS, start, length, page_size, block_size) + def __repr__(self): + return f"{self.fs_type} Start {hex(self.start)} Len {hex(self.length)} Page size {hex(self.page_size)} Block size {hex(self.block_size)}" + def get_extract_cmd(self, input_file, output_dir): + return f'"{self.tool}" -b {self.block_size} -s {self.length} -p {self.page_size} --unpack "{output_dir}" "{input_file}"' + +# SPIFFS helpers copied from ESP32, https://github.com/platformio/platform-espressif32/blob/develop/builder/main.py +# Copyright 2014-present PlatformIO +# Licensed under the Apache License, Version 2.0 (the "License"); + +def _parse_size(value): + if isinstance(value, int): + return value + elif value.isdigit(): + return int(value) + elif value.startswith("0x"): + return int(value, 16) + elif value[-1].upper() in ("K", "M"): + base = 1024 if value[-1].upper() == "K" else 1024 * 1024 + return int(value[:-1]) * base + return value + +## FS helpers for ESP8266 +# copied from https://github.com/platformio/platform-espressif8266/blob/develop/builder/main.py +# Copyright 2014-present PlatformIO +# Licensed under the Apache License, Version 2.0 (the "License"); + +def _parse_ld_sizes(ldscript_path): + assert ldscript_path + result = {} + # get flash size from LD script path + match = re.search(r"\.flash\.(\d+[mk]).*\.ld", ldscript_path) + if match: + result['flash_size'] = _parse_size(match.group(1)) + + appsize_re = re.compile( + r"irom0_0_seg\s*:.+len\s*=\s*(0x[\da-f]+)", flags=re.I) + filesystem_re = re.compile( + r"PROVIDE\s*\(\s*_%s_(\w+)\s*=\s*(0x[\da-f]+)\s*\)" % "FS" + if "arduino" in env.subst("$PIOFRAMEWORK") + else "SPIFFS", + flags=re.I, + ) + with open(ldscript_path) as fp: + for line in fp.readlines(): + line = line.strip() + if not line or line.startswith("/*"): + continue + match = appsize_re.search(line) + if match: + result['app_size'] = _parse_size(match.group(1)) + continue + match = filesystem_re.search(line) + if match: + result['fs_%s' % match.group(1)] = _parse_size( + match.group(2)) + return result + +def esp8266_fetch_fs_size(env): + ldsizes = _parse_ld_sizes(env.GetActualLDScript()) + for key in ldsizes: + if key.startswith("fs_"): + env[key.upper()] = ldsizes[key] + + assert all([ + k in env + for k in ["FS_START", "FS_END", "FS_PAGE", "FS_BLOCK"] + ]) + + # esptool flash starts from 0 + for k in ("FS_START", "FS_END"): + _value = 0 + if env[k] < 0x40300000: + _value = env[k] & 0xFFFFF + elif env[k] < 0x411FB000: + _value = env[k] & 0xFFFFFF + _value -= 0x200000 # correction + else: + _value = env[k] & 0xFFFFFF + _value += 0xE00000 # correction + + env[k] = _value + +## Script interface functions +def parse_partition_table(content): + entries = [e for e in content.split(b'\xaaP') if len(e) > 0] + #print("Partition data:") + for entry in entries: + type = entry[1] + if type in [0x82,0x83]: # SPIFFS or LITTLEFS + offset = int.from_bytes(entry[2:5], byteorder='little', signed=False) + size = int.from_bytes(entry[6:9], byteorder='little', signed=False) + print("type:",hex(type)) + print("address:",hex(offset)) + print("size:",hex(size)) + env["FS_START"] = offset + env["FS_SIZE"] = size + env["FS_PAGE"] = int("0x100", 16) + env["FS_BLOCK"] = int("0x1000", 16) + +def get_partition_table(): + esptoolpy = join(platform.get_package_dir("tool-esptoolpy") or "", "esptool.py") + upload_port = join(env.get("UPLOAD_PORT", "none")) + download_speed = join(str(board.get("download.speed", "115200"))) + if "none" in upload_port: + env.AutodetectUploadPort() + upload_port = join(env.get("UPLOAD_PORT", "none")) + fs_file = join(env["PROJECT_DIR"], "partition_table_from_flash.bin") + esptoolpy_flags = [ + "--chip", mcu, + "--port", upload_port, + "--baud", download_speed, + "--before", "default_reset", + "--after", "hard_reset", + "read_flash", + "0x8000", + "0x1000", + fs_file + ] + esptoolpy_cmd = [env["PYTHONEXE"], esptoolpy] + esptoolpy_flags + try: + returncode = subprocess.call(esptoolpy_cmd, shell=False) + except subprocess.CalledProcessError as exc: + print("Downloading failed with " + str(exc)) + with open(fs_file, mode="rb") as file: + content = file.read() + parse_partition_table(content) + +def get_fs_type_start_and_length(): + platform = env["PIOPLATFORM"] + if platform == "espressif32": + print(f"Retrieving filesystem info for {mcu}.") + get_partition_table() + print("FS_START: " + hex(env["FS_START"])) + print("FS_SIZE: " + hex(env["FS_SIZE"])) + print("FS_PAGE: " + hex(env["FS_PAGE"])) + print("FS_BLOCK: " + hex(env["FS_BLOCK"])) + return FS_Info(env["FS_START"], env["FS_SIZE"], env["FS_PAGE"], env["FS_BLOCK"]) + elif platform == "espressif8266": + print("Retrieving filesystem info for ESP8266.") + filesystem = board.get("build.filesystem", "littlefs") + if filesystem not in ("littlefs"): + print("Unrecognized board_build.filesystem option '" + str(filesystem) + "'.") + env.Exit(1) + # fetching sizes is the same for all filesystems + esp8266_fetch_fs_size(env) + #print("FS_START: " + hex(env["FS_START"])) + #print("FS_SIZE: " + hex(env["FS_END"] - env["FS_START"])) + #print("FS_PAGE: " + hex(env["FS_PAGE"])) + #print("FS_BLOCK: " + hex(env["FS_BLOCK"])) + if filesystem == "littlefs": + print("Recognized LittleFS filesystem.") + return FS_Info(env["FS_START"], env["FS_END"] - env["FS_START"], env["FS_PAGE"], env["FS_BLOCK"]) + else: + print("Unrecongized configuration.") + pass + +def download_fs(fs_info: FSInfo): + print(fs_info) + esptoolpy = join(platform.get_package_dir("tool-esptoolpy") or "", "esptool.py") + upload_port = join(env.get("UPLOAD_PORT", "none")) + download_speed = join(str(board.get("download.speed", "115200"))) + if "none" in upload_port: + env.AutodetectUploadPort() + upload_port = join(env.get("UPLOAD_PORT", "none")) + fs_file = join(env.subst("$BUILD_DIR"), f"downloaded_fs_{hex(fs_info.start)}_{hex(fs_info.length)}.bin") + esptoolpy_flags = [ + "--chip", mcu, + "--port", upload_port, + "--baud", download_speed, + "--before", "default_reset", + "--after", "hard_reset", + "read_flash", + hex(fs_info.start), + hex(fs_info.length), + fs_file + ] + esptoolpy_cmd = [env["PYTHONEXE"], esptoolpy] + esptoolpy_flags + print(esptoolpy_cmd) + print("Download filesystem image") + try: + returncode = subprocess.call(esptoolpy_cmd, shell=False) + return (True, fs_file) + except subprocess.CalledProcessError as exc: + print("Downloading failed with " + str(exc)) + return (False, "") + +def unpack_fs(fs_info: FSInfo, downloaded_file: str): + # by writing custom_unpack_dir = some_dir in the platformio.ini, one can + # control the unpack directory + unpack_dir = env.GetProjectOption("custom_unpack_dir", "unpacked_fs") + if not os.path.exists(downloaded_file): + print(f"ERROR: {downloaded_file} with filesystem not found, maybe download failed due to download_speed setting being too high.") + assert(0) + try: + if os.path.exists(unpack_dir): + shutil.rmtree(unpack_dir) + except Exception as exc: + print("Exception while attempting to remove the folder '" + str(unpack_dir) + "': " + str(exc)) + if not os.path.exists(unpack_dir): + os.makedirs(unpack_dir) + + cmd = fs_info.get_extract_cmd(downloaded_file, unpack_dir) + print("Unpack files from filesystem image") + try: + print(env["MKFSTOOL"]) + print(platform.get_package_dir("tool-mklittlefs")) + print(cmd, unpack_dir); + returncode = subprocess.call(cmd, shell=True) + return (True, unpack_dir) + except subprocess.CalledProcessError as exc: + print("Unpacking filesystem failed with " + str(exc)) + return (False, "") + +def display_fs(extracted_dir): + # extract command already nicely lists all extracted files. + # no need to display that ourselves. just display a summary + file_count = sum([len(files) for r, d, files in os.walk(extracted_dir)]) + print("Extracted " + str(file_count) + " file(s) from filesystem.") + +def command_download_fs(*args, **kwargs): + info = get_fs_type_start_and_length() + download_ok, downloaded_file = download_fs(info) + unpack_ok, unpacked_dir = unpack_fs(info, downloaded_file) + if unpack_ok is True: + display_fs(unpacked_dir) + + +env.AddCustomTarget( + name="downloadfs", + dependencies=None, + actions=[ + command_download_fs + ], + title="Download Filesystem", + description="Downloads and displays files stored in the target ESP32/ESP8266" +) diff --git a/platformio.ini b/platformio.ini index 8c714c6a82..4471d1233a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -2851,4 +2851,4 @@ build_flags = ${env:adafruit_matrixportal_esp32s3_legacy.build_flags} [env:adafruit_matrixportal_esp32s3] ;; this buildenv is just an alias for the matrixportal UF2 build, to keep 3rd party build tools happy. -extends = env:adafruit_matrixportal_esp32s3_tinyUF2 +extends = env:adafruit_matrixportal_esp32s3_tinyUF2 \ No newline at end of file diff --git a/tools/ESP32-Chip_info.hpp b/tools/ESP32-Chip_info.hpp index f9c5ebecf4..78925a4e4f 100644 --- a/tools/ESP32-Chip_info.hpp +++ b/tools/ESP32-Chip_info.hpp @@ -474,7 +474,6 @@ void my_verbose_print_reset_reason(int reason) void show_psram_info_part1(void) { -#if defined(BOARD_HAS_PSRAM) || defined(WLED_USE_PSRAM) //if (esp_spiram_is_initialized() == false) esp_spiram_init(); Serial.println(psramFound() ? "ESP32 PSRAM: found.": "ESP32 PSRAM: not found!"); if (!psramFound()) return; @@ -495,12 +494,10 @@ void show_psram_info_part1(void) } #endif #endif -#endif } void show_psram_info_part2(void) { -#if defined(BOARD_HAS_PSRAM) || defined(WLED_USE_PSRAM) if (!psramFound()) return; // usually the next part won't work ... @@ -518,27 +515,6 @@ void show_psram_info_part2(void) case PSRAM_CACHE_F80M_S80M: Serial.println("* PSRAM speed = PSRAM_CACHE_F80M_S80M -> speed=80 esptool_flash=80 (by config)"); break; } #endif - - #if 0 // this test makes the "max used PSRAM" info unusable - // try to allocate PSRAM (one 640KB chunk so we can be sure it will not fit into DRAM) - void * buff2 = ps_malloc(640 * 1024); - uint8_t * buf = (uint8_t*)malloc(620 * 1024); - Serial.print("* PSRAM free after malloc / ps_malloc : "); Serial.print(ESP.getFreePsram() / 1024.0, 2); Serial.println(" KB (1.2MB allocated)"); - if (buff2 == NULL) - Serial.println("* can't allocate big memory with ps_malloc()"); - else { - Serial.println("* Can allocate big memory with ps_malloc()"); - free(buff2); - } - if (buf == NULL) - Serial.println("* can't allocate big memory with malloc()"); - else { - free(buf); - Serial.println("* Can allocate big memory with malloc()"); - } - #endif - -#endif } void showRealSpeed() { @@ -566,7 +542,7 @@ void showRealSpeed() { Serial.print("FLASH CHIP FREQ (magic): "); Serial.print(ESP.getFlashChipSpeed()/1000000.0, 1); Serial.println(" MHz"); Serial.print("FLASH SIZE (magic byte): "); Serial.print(ESP.getFlashChipSize() / (1024.0 * 1024), 2); Serial.println(" MB"); - Serial.print("FLASH MODE (magic byte): "); Serial.print(ESP.getFlashChipMode()); Serial.println(" ; 0=QIO, 1=QOUT, 2=DIO, 3=DOUT or other\n"); + // Serial.print("FLASH MODE (magic byte): "); Serial.print(ESP.getFlashChipMode()); Serial.println(" ; 0=QIO, 1=QOUT, 2=DIO, 3=DOUT or other\n"); Serial.flush(); Serial.print("FLASH CHIP ID: 0x"); Serial.println(my_ESP_getFlashChipId(), HEX); @@ -574,7 +550,7 @@ void showRealSpeed() { //Serial.print("FLASH CHIP FREQ: "); Serial.print(my_ESP_getFlashChipSpeed() / 1000000.0, 1); Serial.println(" MHz"); // this seems to crash on -S2 #endif Serial.print("FLASH REAL SIZE: "); Serial.print(my_ESP_getFlashChipRealSize() / (1024.0 * 1024), 2); Serial.println(" MB"); - Serial.print("FLASH REAL MODE: "); Serial.println(my_ESP_getFlashChipMode()); + // Serial.print("FLASH REAL MODE: "); Serial.println(my_ESP_getFlashChipMode()); for(int aa=0; aa<42; aa++) Serial.print("-"); Serial.println(); Serial.flush(); @@ -582,10 +558,10 @@ void showRealSpeed() { Serial.print( " FREE RAM: "); Serial.print(ESP.getFreeHeap() / 1024.0, 2); Serial.println(" KB"); Serial.print( " MAX RAM alloc: "); Serial.print(ESP.getMaxAllocHeap() / 1024.0, 2); Serial.println(" KB"); -#if defined(BOARD_HAS_PSRAM) || defined(WLED_USE_PSRAM) Serial.println(); - show_psram_info_part1(); + if (psramFound()) { + show_psram_info_part1(); Serial.print(" total PSRAM: "); Serial.print(ESP.getPsramSize() / 1024.0, 0); Serial.println(" KB"); Serial.print(" FREE PSRAM: "); Serial.print(ESP.getFreePsram() / 1024.0, 2); Serial.println(" KB"); Serial.print(" MAX PSRAM alloc:"); Serial.print(ESP.getMaxAllocPsram() / 1024.0, 2); Serial.println(" KB"); @@ -594,7 +570,6 @@ void showRealSpeed() { show_psram_info_part2(); } Serial.flush(); -#endif #if 0 // duplicate - this info is also printed by getCoreResetReason() Serial.println(); diff --git a/usermods/artifx/arti.h b/usermods/artifx/arti.h index 21eb649fbb..f38668fd5a 100644 --- a/usermods/artifx/arti.h +++ b/usermods/artifx/arti.h @@ -2565,7 +2565,11 @@ class ARTI { uint16_t programFileSize; #if ARTI_PLATFORM == ARTI_ARDUINO programFileSize = programFile.size(); + #if ESP32 + programText = (char *) heap_caps_malloc_prefer(programFileSize+1, 2, MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT); + #else programText = (char *)malloc(programFileSize+1); + #endif programFile.read((byte *)programText, programFileSize); programText[programFileSize] = '\0'; #else diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 0be1bd7974..307940fc9e 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -10,9 +10,15 @@ */ - #include "wled.h" - +#ifdef UM_AUDIOREACTIVE_USE_ESPDSP_FFT + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0) + // #include "esp_dsp.h" + #include "dsps_biquad.h" + #include "dsps_fft4r.h" + #include "dsps_wind_blackman_harris.h" + #endif +#endif #ifdef ARDUINO_ARCH_ESP32 #include @@ -51,7 +57,7 @@ #define FFTTASK_PRIORITY 1 // standard: looptask prio //#define FFTTASK_PRIORITY 2 // above looptask, below async_tcp #endif -#endif +#endif #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) // this applies "pink noise scaling" to FFT results before computing the major peak for effects. @@ -284,7 +290,7 @@ static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels, bool static TaskHandle_t FFT_Task = nullptr; // Table of multiplication factors so that we can even out the frequency response. -#define MAX_PINK 10 // 0 = standard, 1= line-in (pink noise only), 2..4 = IMNP441, 5..6 = ICS-43434, ,7=SPM1423, 8..9 = userdef, 10= flat (no pink noise adjustment) +#define MAX_PINK 11 // 0 = standard, 1= line-in (pink noise only), 2..4 = IMNP441, 5..6 = ICS-43434, ,7=SPM1423, 8..9 = userdef, 10= flat (no pink noise adjustment) static const float fftResultPink[MAX_PINK+1][NUM_GEQ_CHANNELS] = { { 1.70f, 1.71f, 1.73f, 1.78f, 1.68f, 1.56f, 1.55f, 1.63f, 1.79f, 1.62f, 1.80f, 2.06f, 2.47f, 3.35f, 6.83f, 9.55f }, // 0 default from SR WLED // { 1.30f, 1.32f, 1.40f, 1.46f, 1.52f, 1.57f, 1.68f, 1.80f, 1.89f, 2.00f, 2.11f, 2.21f, 2.30f, 2.39f, 3.09f, 4.34f }, // - Line-In Generic -> pink noise adjustment only @@ -302,7 +308,8 @@ static const float fftResultPink[MAX_PINK+1][NUM_GEQ_CHANNELS] = { { 2.25f, 1.60f, 1.30f, 1.60f, 2.20f, 3.20f, 3.06f, 2.60f, 2.85f, 3.50f, 4.10f, 4.80f, 5.70f, 6.05f,10.50f,14.85f }, // 8 userdef #1 for ewowi (enhance median/high freqs) { 4.75f, 3.60f, 2.40f, 2.46f, 3.52f, 1.60f, 1.68f, 3.20f, 2.20f, 2.00f, 2.30f, 2.41f, 2.30f, 1.25f, 4.55f, 6.50f }, // 9 userdef #2 for softhack (mic hidden inside mini-shield) - { 2.38f, 2.18f, 2.07f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.95f, 1.70f, 2.13f, 2.47f } // 10 almost FLAT (IMNP441 but no PINK noise adjustments) + { 2.38f, 2.18f, 2.07f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.95f, 1.70f, 2.13f, 2.47f }, // 10 almost FLAT (IMNP441 but no PINK noise adjustments) + { 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f, 1.70f } // DEAD FLAT }; /* how to make your own profile: @@ -388,6 +395,13 @@ constexpr uint16_t samplesFFT_2 = 256; // meaningful part of FFT result static float* vReal = nullptr; // FFT sample inputs / freq output - these are our raw result bins static float* vImag = nullptr; // imaginary parts +// making it easier to use biquad filter calculator from https://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/ +float a0 = 0.0f; +float a1 = 0.0f; +float a2 = 0.0f; +float b1 = 0.0f; +float b2 = 0.0f; + #ifdef FFT_MAJORPEAK_HUMAN_EAR static float* pinkFactors = nullptr; // "pink noise" correction factors constexpr float pinkcenter = 23.66; // sqrt(560) - center freq for scaling is 560 hz. @@ -396,6 +410,7 @@ constexpr float binWidth = SAMPLE_RATE / (float)samplesFFT; // frequency range o // Create FFT object +#if defined(UM_AUDIOREACTIVE_USE_NEW_FFT) || defined(UM_AUDIOREACTIVE_USE_ESPDSP_FFT) // lib_deps += https://github.com/kosme/arduinoFFT#develop @ 1.9.2 #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) // these options actually cause slow-down on -S2 (-S2 doesn't have floating point hardware) @@ -404,7 +419,27 @@ constexpr float binWidth = SAMPLE_RATE / (float)samplesFFT; // frequency range o #endif #define sqrt(x) sqrtf(x) // little hack that reduces FFT time by 10-50% on ESP32 (as alternative to FFT_SQRT_APPROXIMATION) #define sqrt_internal sqrtf // see https://github.com/kosme/arduinoFFT/pull/83 -#include +#else + // around 50% slower on -S2 +// lib_deps += https://github.com/blazoncek/arduinoFFT.git +#endif +#ifndef UM_AUDIOREACTIVE_USE_ESPDSP_FFT + #include + + #if defined(UM_AUDIOREACTIVE_USE_NEW_FFT) + + #if defined(FFT_LIB_REV) && FFT_LIB_REV > 0x19 + // arduinoFFT 2.x has a slightly different API + static ArduinoFFT FFT = ArduinoFFT( vReal, vImag, samplesFFT, SAMPLE_RATE, true); + #else + // recommended version optimized by @softhack007 (API version 1.9) + static float windowWeighingFactors[samplesFFT] = {0.0f}; // cache for FFT windowing factors + static ArduinoFFT FFT = ArduinoFFT( vReal, vImag, samplesFFT, SAMPLE_RATE, windowWeighingFactors); + #endif + #else + static arduinoFFT FFT = arduinoFFT(vReal, vImag, samplesFFT, SAMPLE_RATE); + #endif +#endif // Helper functions @@ -451,11 +486,20 @@ static bool alocateFFTBuffers(void) { if (vReal) free(vReal); // should not happen if (vImag) free(vImag); // should not happen + #ifdef ESP32 + if ((vReal = (float*) heap_caps_calloc_prefer(sizeof(float), samplesFFT, 2, MALLOC_CAP_INTERNAL, MALLOC_CAP_SPIRAM)) == nullptr) return false; // calloc or die + if ((vImag = (float*) heap_caps_calloc_prefer(sizeof(float), samplesFFT, 2, MALLOC_CAP_INTERNAL, MALLOC_CAP_SPIRAM)) == nullptr) return false; + #else if ((vReal = (float*) calloc(sizeof(float), samplesFFT)) == nullptr) return false; // calloc or die if ((vImag = (float*) calloc(sizeof(float), samplesFFT)) == nullptr) return false; + #endif #ifdef FFT_MAJORPEAK_HUMAN_EAR if (pinkFactors) free(pinkFactors); + #if ESP32 + if ((pinkFactors = (float*) heap_caps_calloc_prefer(sizeof(float), samplesFFT, 2, MALLOC_CAP_INTERNAL, MALLOC_CAP_SPIRAM)) == nullptr) return false; + #else if ((pinkFactors = (float*) calloc(sizeof(float), samplesFFT)) == nullptr) return false; + #endif #endif #ifdef SR_DEBUG @@ -488,6 +532,7 @@ static void runDCBlocker(uint_fast16_t numSamples, float *sampleBuffer) { // void FFTcode(void * parameter) { + #ifdef SR_DEBUG USER_FLUSH(); USER_PRINT("AR: "); USER_PRINT(pcTaskGetTaskName(NULL)); @@ -526,7 +571,9 @@ void FFTcode(void * parameter) #else static float windowWeighingFactors[samplesFFT] = {0.0f}; // cache for FFT windowing factors - use global RAM #endif + #ifndef UM_AUDIOREACTIVE_USE_ESPDSP_FFT static ArduinoFFT FFT = ArduinoFFT( vReal, vImag, samplesFFT, SAMPLE_RATE, windowWeighingFactors); + #endif #endif #ifdef FFT_MAJORPEAK_HUMAN_EAR @@ -540,9 +587,79 @@ void FFTcode(void * parameter) pinkFactors[0] *= 0.5; // suppress 0-42hz bin #endif + #if defined(UM_AUDIOREACTIVE_USE_ESPDSP_FFT) && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0) + + esp_err_t myerr = dsps_fft4r_init_fc32(NULL, samplesFFT >> 1); + if (myerr != ESP_OK) { + USER_PRINTF("Not possible to initialize FFT. Error = %i", myerr); + return; + } + __attribute__((aligned(16))) float window[samplesFFT]; + dsps_wind_blackman_harris_f32(window, samplesFFT); + + // lowpass, 22050 Hz, 9963 Hz, 0.734 Q, gain ignored - https://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/ + a0 = 0.8123610015069542; + a1 = 1.6247220030139085; + a2 = 0.8123610015069542; + b1 = 1.5869495720054403; + b2 = 0.6624944340223766; + + float coeffs_lpf[5] = { a0, a1, a2, b1, b2 }; + float w_lpf[5] = {0, 0}; + + // highpass, 22050 Hz, 35 Hz, 0.734 Q, gain ignored - https://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/ + a0 = 0.9932274488431106; + a1 = -1.9864548976862213; + a2 = 0.9932274488431106; + b1 = -1.9864055002334067; + b2 = 0.9865042951390358; + + float coeffs_hpf[5] = { a0, a1, a2, b1, b2 }; + float w_hpf[5] = {0, 0}; + + // // peak, 22050 Hz, 4659 Hz, 0.646 Q, 6 Gain - https://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/ + // a0 = 1.4269358395668883; + // a1 = -0.27502696828149037; + // a2 = -0.2848721507196676; + // b1 = -0.27502696828149037; + // b2 = 0.14206368884722057; + + // // peak, 22050 Hz, 4659 Hz, 0.646 Q, 12 Gain - https://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/ + // a0 = 2.2787848311643; + // a1 = -0.27502696828149037; + // a2 = -1.136721142317079; + // b1 = -0.27502696828149037; + // b2 = 0.14206368884722057; + + // // peak, 22050 Hz, 4659 Hz, 0.646 Q, 18 Gain - https://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/ + // a0 = 3.9784470221428574; + // a1 = -0.27502696828149037; + // a2 = -2.836383333295637; + // b1 = -0.27502696828149037; + // b2 = 0.14206368884722057; + + // // peak, 22050 Hz, 4659 Hz, 0.646 Q, 30 Gain - https://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/ + // a0 = 14.136195997452123; + // a1 = -0.27502696828149037; + // a2 = -12.994132308604902; + // b1 = -0.27502696828149037; + // b2 = 0.14206368884722057; + + // peak, 22050 Hz, 4659 Hz, 0.646 Q, 24 Gain - https://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/ + a0 = 7.369718939979809; + a1 = -0.27502696828149037; + a2 = -6.227655251132589; + b1 = -0.27502696828149037; + b2 = 0.14206368884722057; + + float coeffs_notch[5] = { a0, a1, a2, b1, b2 }; + float w_notch[5] = {0, 0}; + + #endif + TickType_t xLastWakeTime = xTaskGetTickCount(); for(;;) { - delay(1); // DO NOT DELETE THIS LINE! It is needed to give the IDLE(0) task enough time and to keep the watchdog happy. + delay(1); // DO NOT DELETE THIS LINE! It is needed to give the IDLE(0) task enough time and to keep the watchdog happy. // taskYIELD(), yield(), vTaskDelay() and esp_task_wdt_feed() didn't seem to work. // Don't run FFT computing code if we're in Receive mode or in realtime mode @@ -627,9 +744,11 @@ void FFTcode(void * parameter) #if defined(WLEDMM_FASTPATH) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && defined(ARDUINO_ARCH_ESP32) // experimental - be nice to LED update task (trying to avoid flickering) - dual core only -#if FFTTASK_PRIORITY > 1 - if (strip.isServicing()) delay(1); -#endif + #ifndef UM_AUDIOREACTIVE_USE_ESPDSP_FFT + #if FFTTASK_PRIORITY > 1 + if (strip.isServicing()) delay(1); + #endif + #endif #endif // normal mode: filter everything @@ -647,8 +766,10 @@ void FFTcode(void * parameter) bool doDCRemoval = false; // DCRemove is only necessary if we don't use any kind of low-cut filtering if ((useInputFilter > 0) && (useInputFilter < 99)) { switch(useInputFilter) { - case 1: runMicFilter(sampleCount, samplesStart); break; // PDM microphone bandpass - case 2: runDCBlocker(sampleCount, samplesStart); break; // generic Low-Cut + DC blocker (~40hz cut-off) + case 1: runMicFilter(samplesFFT, vReal); break; // PDM microphone bandpass + #ifndef UM_AUDIOREACTIVE_USE_ESPDSP_FFT + case 2: runDCBlocker(samplesFFT, vReal); break; // generic Low-Cut + DC blocker (~40hz cut-off) + #endif default: doDCRemoval = true; break; } } else doDCRemoval = true; @@ -689,6 +810,9 @@ void FFTcode(void * parameter) if (__builtin_signbit(vReal[i]) != __builtin_signbit(vReal[i+1])) // test sign bit: sign changed -> zero crossing newZeroCrossingCount++; } + #if defined(UM_AUDIOREACTIVE_USE_ESPDSP_FFT) && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0) + vReal[i] *= window[i]; // FFT windowing for ESP-DSP + #endif } newZeroCrossingCount = (newZeroCrossingCount*2)/3; // reduce value so it typically stays below 256 zeroCrossingCount = newZeroCrossingCount; // update only once, to avoid that effects pick up an intermediate value @@ -721,6 +845,7 @@ void FFTcode(void * parameter) if (fabsf(volumeSmth) > 0.25f) { // noise gate open if ((skipSecondFFT == false) || (isFirstRun == true)) { // run FFT (takes 2-3ms on ESP32, ~12ms on ESP32-S2, ~30ms on -C3) + #ifndef UM_AUDIOREACTIVE_USE_ESPDSP_FFT if (doDCRemoval) FFT.dcRemoval(); // remove DC offset switch(fftWindow) { // apply FFT window case 1: @@ -754,10 +879,54 @@ void FFTcode(void * parameter) FFT.compute( FFTDirection::Forward ); // Compute FFT FFT.complexToMagnitude(); // Compute magnitudes + #else + wc = 1.0f; // use Blackman_Harris value from ESP-DSP code + #endif vReal[0] = 0; // The remaining DC offset on the signal produces a strong spike on position 0 that should be eliminated to avoid issues. float last_majorpeak = FFT_MajorPeak; float last_magnitude = FFT_Magnitude; + #if defined(UM_AUDIOREACTIVE_USE_ESPDSP_FFT) + if (TROYHACKS_LPF) { + dsps_biquad_f32(vReal, vImag, samplesFFT, coeffs_lpf, w_lpf); // you can't dump this back into itself, needs a destination + memcpy(vReal, vImag, samplesFFT); // dump it back + } + if (TROYHACKS_HPF) { + dsps_biquad_f32(vReal, vImag, samplesFFT, coeffs_hpf, w_hpf); // you can't dump this back into itself, needs a destination + memcpy(vReal, vImag, samplesFFT); // dump it back + } + if (TROYHACKS_NOTCH) { + dsps_biquad_f32(vReal, vImag, samplesFFT, coeffs_notch, w_notch); // you can't dump this back into itself, needs a destination + memcpy(vReal, vImag, samplesFFT); // dump it back + } + + dsps_fft4r_fc32(vReal,samplesFFT >> 1); + dsps_bit_rev4r_fc32(vReal,samplesFFT >> 1); + dsps_cplx2real_fc32(vReal,samplesFFT >> 1); + + FFT_MajorPeak = 0; + FFT_Magnitude = 0; + + int x=0; + for (int i=0; i FFT_Magnitude) { + FFT_Magnitude = vReal[x]; + FFT_MajorPeak = x*(SAMPLE_RATE/samplesFFT); + } + x++; + } + #elif defined(UM_AUDIOREACTIVE_USE_NEW_FFT) + if (doDCRemoval) FFT.dcRemoval(); // remove DC offset + #if !defined(FFT_PREFER_EXACT_PEAKS) + FFT.windowing( FFTWindow::Flat_top, FFTDirection::Forward); // Weigh data using "Flat Top" function - better amplitude accuracy + #else + FFT.windowing(FFTWindow::Blackman_Harris, FFTDirection::Forward); // Weigh data using "Blackman- Harris" window - sharp peaks due to excellent sideband rejection + #endif + FFT.compute( FFTDirection::Forward ); // Compute FFT + FFT.complexToMagnitude(); // Compute magnitudes + vReal[0] = 0; // The remaining DC offset on the signal produces a strong spike on position 0 that should be eliminated to avoid issues. + #endif #ifdef FFT_MAJORPEAK_HUMAN_EAR // scale FFT results @@ -765,12 +934,16 @@ void FFTcode(void * parameter) vReal[binInd] *= pinkFactors[binInd]; #endif - #if defined(FFT_LIB_REV) && FFT_LIB_REV > 0x19 - // arduinoFFT 2.x has a slightly different API - FFT.majorPeak(&FFT_MajorPeak, &FFT_Magnitude); - #else - FFT.majorPeak(FFT_MajorPeak, FFT_Magnitude); // let the effects know which freq was most dominant - #endif + // #else + #ifndef UM_AUDIOREACTIVE_USE_ESPDSP_FFT + #if defined(FFT_LIB_REV) && FFT_LIB_REV > 0x19 + // arduinoFFT 2.x has a slightly different API + FFT.majorPeak(&FFT_MajorPeak, &FFT_Magnitude); + #else + FFT.majorPeak(FFT_MajorPeak, FFT_Magnitude); // let the effects know which freq was most dominant + #endif + #endif + // #endif FFT_Magnitude *= wc; // apply correction factor if (FFT_MajorPeak < (SAMPLE_RATE / samplesFFT)) {FFT_MajorPeak = 1.0f; FFT_Magnitude = 0;} // too low - use zero @@ -2691,13 +2864,23 @@ class AudioReactive : public Usermod { infoArr.add(roundf(sampleTime)/100.0f); infoArr.add(" ms"); + #ifdef UM_AUDIOREACTIVE_USE_ESPDSP_FFT + infoArr = user.createNestedArray(F("FFT time (ESP-DSP)")); + #else + infoArr = user.createNestedArray(F("FFT time")); + #endif + infoArr.add(roundf(fftTime)/100.0f); + if ((fftTime/100) >= FFT_MIN_CYCLE) // FFT time over budget -> I2S buffer will overflow + infoArr.add("! ms"); + else if ((fftTime/80 + sampleTime/80) >= FFT_MIN_CYCLE) // FFT time >75% of budget -> risk of instability + infoArr.add(" ms!"); + else + infoArr.add(" ms"); + infoArr = user.createNestedArray(F("Filtering time")); infoArr.add(roundf(filterTime)/100.0f); infoArr.add(" ms"); - infoArr = user.createNestedArray(F("FFT time")); - infoArr.add(roundf(fftTime)/100.0f); - #ifdef FFT_USE_SLIDING_WINDOW unsigned timeBudget = doSlidingFFT ? (FFT_MIN_CYCLE) : fftTaskCycle / 115; #else diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 04a77701f2..f5a95a698a 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -8272,7 +8272,7 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma. Fla uint8_t nextband = (remaining < 1)? band +1: band; nextband = constrain(nextband, 0, 15); // just to be sure frBand = ((NUM_BANDS < 16) && (NUM_BANDS > 1)) ? map(nextband, 0, NUM_BANDS - 1, 0, 15):nextband; // always use full range. comment out this line to get the previous behaviour. - uint16_t nextBandHeight = fftResult[frBand]; + uint16_t nextBandHeight = fftResult[frBand]; // smooth Band height bandHeight = (7*bandHeight + 3*lastBandHeight + 3*nextBandHeight) / 12; // yeees, its 12 not 13 (10% amplification) bandHeight = constrain(bandHeight, 0, 255); // remove potential over/underflows @@ -8771,6 +8771,12 @@ static const char _data_FX_MODE_2DWAVINGCELL[] PROGMEM = "Waving Cell@!,,Amplitu ///////////////////////// uint16_t mode_GEQLASER(void) { + #ifdef WLED_ENABLE_HUB75MATRIX + uint8_t side_color_scale = 64; + #else + uint8_t side_color_scale = 32; + #endif + // Author: @TroyHacks // @license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 @@ -8820,7 +8826,7 @@ uint16_t mode_GEQLASER(void) { int linex = i*(cols/NUM_BANDS); if (heights[i] > 1) { - ledColorTemp = color_fade(ledColor,32,true); + ledColorTemp = color_fade(ledColor,side_color_scale,true); int pPos = max(0, linex+(cols/NUM_BANDS)-1); for (int y = (i 0) SEGMENT.drawLine(pPos,rows-y-1,*projector,horizon,ledColorTemp,false,depth); // right side perspective @@ -8846,7 +8852,7 @@ uint16_t mode_GEQLASER(void) { int pPos = max(0, linex+(cols/NUM_BANDS)-1); if (heights[i] > 1) { - ledColorTemp = color_fade(ledColor,32,true); + ledColorTemp = color_fade(ledColor,side_color_scale,true); for (uint_fast8_t y = (i>0) ? heights[i-1] : 0; y <= heights[i]; y++) { // don't bother drawing what we'll hide anyway if (rows-y > 0) SEGMENT.drawLine(linex,rows-y-1,*projector,horizon,ledColorTemp,false,depth); // left side perspective } @@ -8888,7 +8894,7 @@ uint16_t mode_GEQLASER(void) { } if (!SEGMENT.check1 && heights[i] > rows-horizon) { - if (SEGMENT.intensity == 0) ledColorTemp = color_fade(ledColor,32,true); // match side fill if we're in blackout mode + if (SEGMENT.intensity == 0) ledColorTemp = color_fade(ledColor,side_color_scale,true); // match side fill if we're in blackout mode SEGMENT.drawLine(linex,rows-heights[i]-1,linex+(cols/NUM_BANDS)-1,rows-heights[i]-1,ledColorTemp); // top line to simulate hidden top fill } @@ -8950,10 +8956,12 @@ uint16_t mode_2DPaintbrush() { bool color_chaos = SEGMENT.check1; CRGB color; - byte numLines = map8(SEGMENT.intensity,1,16); - + // byte numLines = map8(SEGMENT.intensity,1,64); + byte numLines = SEGMENT.intensity; + if (numLines < 2) numLines = 2; + SEGENV.aux0++; // hue - SEGMENT.fadeToBlackBy(map8(SEGENV.custom1,10,128)); + SEGMENT.fadeToBlackBy(map8(SEGENV.custom1,0,128)); um_data_t *um_data = getAudioData(); uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 247f166e6c..2d90378ae4 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -76,10 +76,16 @@ void WS2812FX::setUpMatrix() { } if ((size > 0) && (customMappingTable == nullptr)) { // second try DEBUG_PRINTLN("setUpMatrix: trying to get fresh memory block."); + #ifdef ESP32 + customMappingTable = (uint16_t*) heap_caps_calloc_prefer(size, sizeof(uint16_t),2,MALLOC_CAP_SPIRAM,MALLOC_CAP_DEFAULT); + #else customMappingTable = (uint16_t*) calloc(size, sizeof(uint16_t)); + #endif if (customMappingTable == nullptr) { USER_PRINTLN("setUpMatrix: alloc failed"); errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag + } else { + USER_PRINTLN("setUpMatrix: alloc failed"); } } if (customMappingTable != nullptr) customMappingTableSize = size; @@ -280,7 +286,7 @@ void IRAM_ATTR __attribute__((hot)) Segment::setPixelColorXY_fast(int x, int y, else ledsrgb[i] = fastled_col; } -#if 0 // this is still a dangerous optimization +#if 1 // this is still a dangerous optimization if ((i < UINT_MAX) && sameColor && (call > 0) && (!transitional) && (mode != FX_MODE_2DSCROLLTEXT) && (ledsrgb[i] == CRGB(scaled_col))) return; // WLEDMM looks like nothing to do #endif @@ -342,7 +348,7 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: col = color_fade(col, _bri_t); } -#if 0 // this is a dangerous optimization +#if 1 // this is a dangerous optimization if ((i < UINT_MAX) && sameColor && (call > 0) && (!transitional) && (mode != FX_MODE_2DSCROLLTEXT) && (ledsrgb[i] == CRGB(col))) return; // WLEDMM looks like nothing to do #endif diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index b09136f533..04bb6a4504 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -114,7 +114,11 @@ void Segment::allocLeds() { if ((size > 0) && (!ledsrgb || size > ledsrgbSize)) { //softhack dont allocate zero bytes USER_PRINTF("allocLeds (%d,%d to %d,%d), %u from %u\n", start, startY, stop, stopY, size, ledsrgb?ledsrgbSize:0); if (ledsrgb) free(ledsrgb); // we need a bigger buffer, so free the old one first + #ifdef ESP32 + ledsrgb = (CRGB*)heap_caps_calloc_prefer(size, 1, 2, MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT); + #else ledsrgb = (CRGB*)calloc(size, 1); + #endif ledsrgbSize = ledsrgb?size:0; if (ledsrgb == nullptr) { USER_PRINTLN("allocLeds failed!!"); @@ -227,18 +231,26 @@ bool Segment::allocateData(size_t len) { //DEBUG_PRINTF("allocateData(%u) start %d, stop %d, vlen %d\n", len, start, stop, virtualLength()); deallocateData(); if (len == 0) return false; // nothing to do + #if ESP32 + if (!psramFound()) { + if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) { + //USER_PRINTF("Segment::allocateData: Segment data quota exceeded! used:%u request:%u max:%d\n", Segment::getUsedSegmentData(), len, MAX_SEGMENT_DATA); + if (len > 0) errorFlag = ERR_LOW_SEG_MEM; // WLEDMM raise errorflag + return false; //not enough memory + } + } + #else if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) { //USER_PRINTF("Segment::allocateData: Segment data quota exceeded! used:%u request:%u max:%d\n", Segment::getUsedSegmentData(), len, MAX_SEGMENT_DATA); if (len > 0) errorFlag = ERR_LOW_SEG_MEM; // WLEDMM raise errorflag return false; //not enough memory } - // do not use SPI RAM on ESP32 since it is slow - //#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) - //if (psramFound()) - // data = (byte*) ps_malloc(len); - //else - //#endif - data = (byte*) malloc(len); + #endif + #ifdef ESP32 + data = (byte*) heap_caps_calloc_prefer(len, 1, 2, MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT); + #else + data = (byte*) calloc(len, 1); + #endif if (!data) { _dataLen = 0; // WLEDMM reset dataLen errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag @@ -248,7 +260,7 @@ bool Segment::allocateData(size_t len) { } //allocation failed Segment::addUsedSegmentData(len); _dataLen = len; - memset(data, 0, len); + // memset(data, 0, len); // already done with calloc if (errorFlag == ERR_LOW_SEG_MEM) errorFlag = ERR_NONE; // WLEDMM reset errorflag on success return true; } @@ -301,11 +313,11 @@ void Segment::setUpLeds() { ledsrgbSize = length() * sizeof(CRGB); // also set this when using global leds. #endif } else if (length() > 0) { //WLEDMM we always want a new buffer //softhack007 quickfix - avoid malloc(0) which is undefined behaviour (should not happen, but i've seen it) - //#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) - //if (psramFound()) - // ledsrgb = (CRGB*)ps_malloc(sizeof(CRGB)*length()); // softhack007 disabled; putting leds into psram leads to horrible slowdown on WROVER boards - //else - //#endif + #ifdef ESP32 + ledsrgb = (CRGB*) heap_caps_malloc_prefer(sizeof(CRGB)*length(), 2, MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT); + #else + ledsrgb = (CRGB*)malloc(sizeof(CRGB)*length()); + #endif allocLeds(); //WLEDMM //USER_PRINTF("\nsetUpLeds() local LEDs: startX=%d stopx=%d startY=%d stopy=%d maxwidth=%d; length=%d, size=%d\n\n", start, stop, startY, stopY, Segment::maxWidth, length(), ledsrgbSize/3); } @@ -1833,13 +1845,11 @@ void WS2812FX::finalizeInit(void) } if (useLedsArray && getLengthTotal()>0) { // WLEDMM avoid malloc(0) size_t arrSize = sizeof(CRGB) * getLengthTotal(); - // softhack007 disabled; putting leds into psram leads to horrible slowdown on WROVER boards (see setUpLeds()) - //#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) - //if (psramFound()) - // Segment::_globalLeds = (CRGB*) ps_malloc(arrSize); - //else - //#endif - if (arrSize > 0) Segment::_globalLeds = (CRGB*) malloc(arrSize); // WLEDMM avoid malloc(0) + #ifdef ESP32 + Segment::_globalLeds = (CRGB*) heap_caps_calloc_prefer(arrSize, 2, MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT); + #else + if (arrSize > 0) Segment::_globalLeds = (CRGB*) malloc(arrSize); // WLEDMM avoid malloc(0) + #endif if ((Segment::_globalLeds != nullptr) && (arrSize > 0)) memset(Segment::_globalLeds, 0, arrSize); // WLEDMM avoid dereferencing nullptr if ((Segment::_globalLeds == nullptr) && (arrSize > 0)) errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag } @@ -2647,11 +2657,19 @@ bool WS2812FX::deserializeMap(uint8_t n) { // don't use new / delete if ((size > 0) && (customMappingTable != nullptr)) { + #if ESP32 + customMappingTable = (uint16_t*) heap_caps_realloc_prefer(customMappingTable, sizeof(uint16_t) * size, 2, MALLOC_CAP_SPIRAM, MALLOC_CAP_INTERNAL); // TroyHacks: This should work? We always have tons of PSRA + #else customMappingTable = (uint16_t*) reallocf(customMappingTable, sizeof(uint16_t) * size); // reallocf will free memory if it cannot resize + #endif } if ((size > 0) && (customMappingTable == nullptr)) { // second try DEBUG_PRINTLN("deserializeMap: trying to get fresh memory block."); + #ifdef ESP32 + customMappingTable = (uint16_t*) heap_caps_calloc_prefer(size, sizeof(uint16_t), 2, MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT); + #else customMappingTable = (uint16_t*) calloc(size, sizeof(uint16_t)); + #endif if (customMappingTable == nullptr) { DEBUG_PRINTLN("deserializeMap: alloc failed!"); errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag diff --git a/wled00/asm_mul16x16.S b/wled00/asm_mul16x16.S new file mode 100644 index 0000000000..4aee805a5b --- /dev/null +++ b/wled00/asm_mul16x16.S @@ -0,0 +1,61 @@ +#if defined(ARDUINO_ARCH_ESP32P4) +.text +.align 4 +.global asm_mul16x16 +.type asm_mul16x16,@function +# ESP32-P4 needs -march rv32imafc_zicsr_zifencei_xesppie -mabi ilp32f +# a0 = out_packet, a1 = brightness, a2 = num_loops, a3 = pixelbuffer +asm_mul16x16: + esp.movx.r.cfg t6 # Enable aligned data access + or t6, t6, 2 # Enable aligned data access + esp.movx.w.cfg t6 # Enable aligned data access + li t6, 8 # put 8 (eventually for vmul bitshift) in temp register 6 + esp.movx.w.sar t6 # set the numbers of bits to right-shift from t6 + li t5, 255 # load 255 into t5 for a comparison + esp.vldbc.8.ip q1, a1, 0 # load the "B" value into q1 from a1, broadcasting the same value to all 16 values of q1 + li t1, 0 # start our loop_num counter t1 at 0 + loop: # "loop" label + beq t1, a2, exit # branch to "exit" if loop_num == num_loops + esp.vld.128.ip q0, a3, 16 # load 16 "A" values into q0 from a3, then move the pointer by 16 to get a new batch + beq a1, t5, skip # If brightness (a1) == 255, jump to "skip" + esp.vmul.u8 q2, q0, q1 # C = A*B (q2 = q0 * q1) then >> by esp.movx.w.sar which we set to 8 + esp.vst.128.ip q2, a0, 16 # store the 16 "C" values into a0, then move the pointer by 16 + j end_skip # jump to "end_skip" + skip: # "skip" label + esp.vst.128.ip q0, a0, 16 # just store full brightness (q0 from a3) to packet (a0) + end_skip: # "end_skip" label + addi t1, t1, 1 # increment loop_num counter t1 + j loop # jump to "loop" + exit: # "exit" label + ret # return +#elif defined(ARDUINO_ARCH_ESP32S3) +.text +.align 4 +.global asm_mul16x16 +.type asm_mul16x16,@function +asm_mul16x16: + # a2 = out_packet, a3 = brightness, a4 = num_loops, a5 = pixelbuffer + entry a1, 16 # prepare windowed registers and reserve 16 bytes of stack + movi.n a8, 8 # put 8 (eventually for vmul bitshift) in a8 + wsr.sar a8 # set the numbers of bits to right-shift from a8 + movi.n a6, 255 # load 255 into a6 for a comparison + ee.vldbc.8.ip q1, a3, 0 # load the "B" value into q1 from a1, broadcasting the same value to all 16 values of q1 + movi.n a7, 0 # start our loop_num counter t1 at 0 + loop: # "loop" label + beq a7, a4, exit # branch to "exit" if loop_num == num_loops + ee.ld.128.usar.ip q3, a5, 0 + ee.vld.128.ip q4, a5, 16 # load 16 "A" values into q0 from a3, then move the pointer by 16 to get a new batch + ee.src.q q0, q3, q4 + ee.vld.128.ip q0, a5, 16 # load 16 "A" values into q0 from a3, then move the pointer by 16 to get a new batch + beq a3, a6, skip # If brightness (a1) == 255, jump to "skip" + ee.vmul.u8 q2, q0, q1 # C = A*B (q2 = q0 * q1) then >> by esp.movx.w.sar which we set to 8 + ee.vst.128.ip q2, a2, 16 # store the 16 "C" values into packet (a2), then move the pointer by 16 + j end_skip # jump to "end_skip" + skip: # "skip" label + ee.vst.128.ip q0, a2, 16 # just store full brightness (q0 from a5) to packet (a2) + end_skip: # "end_skip" label + addi a7, a7, 1 # increment loop_num counter t1 + j loop # jump to "loop" + exit: # "exit" label + retw.n # return +#endif \ No newline at end of file diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 231a23859f..6a7f7214c4 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -447,7 +447,6 @@ uint8_t BusOnOff::getPins(uint8_t* pinArray) const { return 1; } - BusNetwork::BusNetwork(BusConfig &bc, const ColorOrderMap &com) : Bus(bc.type, bc.start, bc.autoWhite), _colorOrderMap(com) { _valid = false; USER_PRINT("["); @@ -475,7 +474,7 @@ BusNetwork::BusNetwork(BusConfig &bc, const ColorOrderMap &com) : Bus(bc.type, b } _UDPchannels = _rgbw ? 4 : 3; #ifdef ESP32 - _data = (byte*) heap_caps_calloc_prefer((bc.count * _UDPchannels)+15, sizeof(byte), 3, MALLOC_CAP_DEFAULT, MALLOC_CAP_SPIRAM); + _data = (byte*) heap_caps_calloc_prefer((bc.count * _UDPchannels)+15, sizeof(byte), 2, MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT); #else _data = (byte*) calloc((bc.count * _UDPchannels)+15, sizeof(byte)); #endif @@ -487,7 +486,7 @@ BusNetwork::BusNetwork(BusConfig &bc, const ColorOrderMap &com) : Bus(bc.type, b _valid = true; _artnet_outputs = bc.artnet_outputs; _artnet_leds_per_output = bc.artnet_leds_per_output; - _artnet_fps_limit = max(uint8_t(1), bc.artnet_fps_limit); + _artnet_fps_limit = bc.artnet_fps_limit; USER_PRINTF(" %u.%u.%u.%u]\n", bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]); } @@ -666,8 +665,8 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh #endif #if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(BOARD_HAS_PSRAM) - if(bc.pins[0] > 4) { - USER_PRINTLN("WARNING, chain limited to 4"); + if(bc.pins[0] > 6) { + USER_PRINTLN("WARNING, chain limited to 6"); } # else // Disable this check if you are want to try bigger setups and accept you @@ -755,8 +754,8 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh #else USER_PRINTLN("MatrixPanel_I2S_DMA - S3 with PSRAM"); - mxconfig.gpio.r1 = 1; - mxconfig.gpio.g1 = 2; + mxconfig.gpio.r1 = 1; + mxconfig.gpio.g1 = 2; mxconfig.gpio.b1 = 42; // 4th pin is GND mxconfig.gpio.r2 = 41; @@ -766,10 +765,10 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh mxconfig.gpio.a = 45; mxconfig.gpio.b = 48; mxconfig.gpio.c = 47; - mxconfig.gpio.d = 21; - mxconfig.gpio.clk = 18; - mxconfig.gpio.lat = 8; - mxconfig.gpio.oe = 3; + mxconfig.gpio.d = 21; // this says GND but should be the "D" pin + mxconfig.gpio.clk = 20; + mxconfig.gpio.lat = 19; + mxconfig.gpio.oe = 8; // don't use GPIO0 or it might hold board in download mode // 16th pin is GND #endif @@ -957,7 +956,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh //display->setBrightness8(25); // range is 0-255, 0 - 0%, 255 - 100% // [setBrightness()] Tried to set output brightness before begin() _bri = (last_bri > 0) ? last_bri : 25; // try to restore persistent brightness value - delay(24); // experimental + // delay(24); // experimental DEBUG_PRINT(F("heap usage: ")); DEBUG_PRINTLN(int(lastHeap - ESP.getFreeHeap())); // Allocate memory and start DMA display if (newDisplay && (display->begin() == false)) { @@ -977,21 +976,38 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh display->clearScreen(); // initially clear the screen buffer USER_PRINTLN("MatrixPanel_I2S_DMA clear ok"); - if (_ledBuffer) free(_ledBuffer); // should not happen - if (_ledsDirty) free(_ledsDirty); // should not happen - - _ledsDirty = (byte*) malloc(getBitArrayBytes(_len)); // create LEDs dirty bits - if (_ledsDirty) setBitArray(_ledsDirty, _len, false); // reset dirty bits + if (_ledBuffer) { + free(_ledBuffer); // should not happen + _ledBuffer = nullptr; // should not happen + } + if (_ledsDirty) { + free(_ledsDirty); // should not happen + _ledsDirty = nullptr; // should not happen + } + #if ESP32 + _ledsDirty = (byte*) heap_caps_malloc_prefer(getBitArrayBytes(_len), 3, MALLOC_CAP_INTERNAL, MALLOC_CAP_DEFAULT, MALLOC_CAP_SPIRAM); + USER_PRINTLN("MatrixPanel_I2S_DMA Step 2"); + // _ledBuffer = (CRGB*) heap_caps_calloc_prefer(_len, sizeof(CRGB), 2, MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT); #if defined(CONFIG_IDF_TARGET_ESP32S3) && CONFIG_SPIRAM_MODE_OCT && defined(BOARD_HAS_PSRAM) && (defined(WLED_USE_PSRAM) || defined(WLED_USE_PSRAM_JSON)) if (psramFound()) { - _ledBuffer = (CRGB*) ps_calloc(_len, sizeof(CRGB)); // create LEDs buffer (initialized to BLACK) + USER_PRINTLN("MatrixPanel_I2S_DMA trying PSRAM allocation for LED buffer"); + USER_PRINT("Length: "); USER_PRINT(_len); USER_PRINT(" Size: "); USER_PRINT(sizeof(CRGB)); USER_PRINT(" Bytes: "); USER_PRINT(_len*sizeof(CRGB)); USER_PRINTLN(" bytes"); + _ledBuffer = (CRGB*) ps_malloc(_len*sizeof(CRGB)); // create LEDs buffer (initialized to BLACK) + USER_PRINTLN("MatrixPanel_I2S_DMA trying PSRAM allocation for LED buffer- WORKED!"); } else { + USER_PRINTLN("MatrixPanel_I2S_DMA using heap allocation for LED buffer"); _ledBuffer = (CRGB*) calloc(_len, sizeof(CRGB)); // create LEDs buffer (initialized to BLACK) } #else + USER_PRINTLN("MatrixPanel_I2S_DMA using heap allocation for LED buffer (default)"); _ledBuffer = (CRGB*) calloc(_len, sizeof(CRGB)); // create LEDs buffer (initialized to BLACK) #endif + USER_PRINTLN("MatrixPanel_I2S_DMA Step 3"); + #endif + + if (_ledsDirty) setBitArray(_ledsDirty, _len, false); // reset dirty bits + USER_PRINTLN("MatrixPanel_I2S_DMA Step 4 end block"); } if ((_ledBuffer == nullptr) || (_ledsDirty == nullptr)) { @@ -1031,6 +1047,10 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh break; } + // Set here to match your physical layout for multiple panels in a matrix layout: + // virtualDisp = new VirtualMatrixPanel((*dma_display), NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, VIRTUAL_MATRIX_CHAIN_TYPE); + fourScanPanel = new VirtualMatrixPanel((*display), 2, 3, 64, 64, CHAIN_BOTTOM_RIGHT_UP); + if (_valid) { _panelWidth = fourScanPanel ? fourScanPanel->width() : display->width(); // cache width - it will never change } @@ -1413,4 +1433,4 @@ uint16_t BusManager::getTotalLength() const { // Bus static member definition int16_t Bus::_cct = -1; uint8_t Bus::_cctBlend = 0; -uint8_t Bus::_gAWM = 255; +uint8_t Bus::_gAWM = 255; \ No newline at end of file diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 04062215bf..60be3126ba 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -339,9 +339,9 @@ class BusNetwork : public Bus { public: BusNetwork(BusConfig &bc, const ColorOrderMap &com); - uint16_t getMaxPixels() const override { return 4096; }; - bool hasRGB() const { return true; } - bool hasWhite() const { return _rgbw; } + uint16_t getMaxPixels() const override { return MAX_LEDS_PER_BUS; }; + bool hasRGB() { return true; } + bool hasWhite() { return _rgbw; } void setPixelColor(uint16_t pix, uint32_t c); diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 069b303aa0..1add2cd8df 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -750,6 +750,7 @@ void serializeConfig() { if (ethernetBoards[ethernetType].eth_power>=0) pins.add(ethernetBoards[ethernetType].eth_power); if (ethernetBoards[ethernetType].eth_mdc>=0) pins.add(ethernetBoards[ethernetType].eth_mdc); if (ethernetBoards[ethernetType].eth_mdio>=0) pins.add(ethernetBoards[ethernetType].eth_mdio); + #ifndef CONFIG_IDF_TARGET_ESP32S3 switch (ethernetBoards[ethernetType].eth_clk_mode) { case ETH_CLOCK_GPIO0_IN: case ETH_CLOCK_GPIO0_OUT: @@ -762,6 +763,7 @@ void serializeConfig() { pins.add(17); break; } + #endif } #endif @@ -1108,6 +1110,9 @@ void serializeConfig() { DEBUG_PRINTF("serializeConfig\n"); File f = WLED_FS.open("/cfg.json", "w"); + #if ESP_IDF_VERSION_MAJOR >= 4 + f.setBufferSize(FS_BUFSIZE); + #endif if (f) serializeJson(doc, f); f.close(); releaseJSONBufferLock(); @@ -1196,6 +1201,9 @@ void serializeConfigSec() { ota[F("aota")] = aOtaEnabled; File f = WLED_FS.open("/wsec.json", "w"); + #if ESP_IDF_VERSION_MAJOR >= 4 + f.setBufferSize(FS_BUFSIZE); + #endif if (f) serializeJson(doc, f); f.close(); releaseJSONBufferLock(); diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 37241959f3..01b06262d7 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -44,34 +44,40 @@ IRAM_ATTR_YN __attribute__((hot)) uint32_t color_blend(uint32_t color1, uint32_t /* * color add function that preserves ratio - * idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule + * original idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule + * speed optimisations by @dedehai */ -IRAM_ATTR_YN uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) // WLEDMM added IRAM_ATTR_YN +IRAM_ATTR_YN uint32_t color_add(uint32_t c1, uint32_t c2, bool preserveCR) // WLEDMM added IRAM_ATTR_YN { - if (c2 == 0) return c1; // WLEDMM shortcut - if (c1 == 0) return c2; // WLEDMM shortcut - - if (fast) { - uint8_t r = R(c1); - uint8_t g = G(c1); - uint8_t b = B(c1); - uint8_t w = W(c1); - r = qadd8(r, R(c2)); - g = qadd8(g, G(c2)); - b = qadd8(b, B(c2)); - w = qadd8(w, W(c2)); - return RGBW32(r,g,b,w); + if (c1 == BLACK) return c2; + if (c2 == BLACK) return c1; + uint32_t rb = (c1 & 0x00FF00FF) + (c2 & 0x00FF00FF); // mask and add two colors at once + uint32_t wg = ((c1>>8) & 0x00FF00FF) + ((c2>>8) & 0x00FF00FF); + uint32_t r = rb >> 16; // extract single color values + uint32_t b = rb & 0xFFFF; + uint32_t w = wg >> 16; + uint32_t g = wg & 0xFFFF; + + if (preserveCR) { // preserve color ratios + uint32_t max = std::max(r,g); // check for overflow note + max = std::max(max,b); + max = std::max(max,w); + //unsigned max = r; // check for overflow note + //max = g > max ? g : max; + //max = b > max ? b : max; + //max = w > max ? w : max; + if (max > 255) { + uint32_t scale = (uint32_t(255)<<8) / max; // division of two 8bit (shifted) values does not work -> use bit shifts and multiplaction instead + rb = ((rb * scale) >> 8) & 0x00FF00FF; // + wg = (wg * scale) & 0xFF00FF00; + } else wg = wg << 8; //shift white and green back to correct position + return rb | wg; } else { - uint32_t r = R(c1) + R(c2); - uint32_t g = G(c1) + G(c2); - uint32_t b = B(c1) + B(c2); - uint32_t w = W(c1) + W(c2); - uint32_t max = r; - if (g > max) max = g; - if (b > max) max = b; - if (w > max) max = w; - if (max < 256) return RGBW32(r, g, b, w); - else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max); + r = r > 255 ? 255 : r; + g = g > 255 ? 255 : g; + b = b > 255 ? 255 : b; + w = w > 255 ? 255 : w; + return RGBW32(r,g,b,w); } } @@ -111,6 +117,36 @@ IRAM_ATTR_YN __attribute__((hot)) uint32_t color_fade(uint32_t c1, uint8_t amoun } #endif + +// 1:1 replacement of fastled function optimized for ESP, slightly faster, more accurate and uses less flash (~ -200bytes) +// CRGB ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t brightness, TBlendType blendType) +// { +// if (blendType == LINEARBLEND_NOWRAP) { +// index = (index*240) >> 8; // Blend range is affected by lo4 blend of values, remap to avoid wrapping +// } +// unsigned hi4 = byte(index) >> 4; +// const CRGB* entry = (CRGB*)( (uint8_t*)(&(pal[0])) + (hi4 * sizeof(CRGB))); +// unsigned red1 = entry->r; +// unsigned green1 = entry->g; +// unsigned blue1 = entry->b; +// if (blendType != NOBLEND) { +// if (hi4 == 15) entry = &(pal[0]); +// else ++entry; +// unsigned f2 = ((index & 0x0F) << 4) + 1; // +1 so we scale by 256 as a max value, then result can just be shifted by 8 +// unsigned f1 = (257 - f2); // f2 is 1 minimum, so this is 256 max +// red1 = (red1 * f1 + (unsigned)entry->r * f2) >> 8; +// green1 = (green1 * f1 + (unsigned)entry->g * f2) >> 8; +// blue1 = (blue1 * f1 + (unsigned)entry->b * f2) >> 8; +// } +// if (brightness < 255) { // note: zero checking could be done to return black but that is hardly ever used so it is omitted +// uint32_t scale = brightness + 1; // adjust for rounding (bitshift) +// red1 = (red1 * scale) >> 8; +// green1 = (green1 * scale) >> 8; +// blue1 = (blue1 * scale) >> 8; +// } +// return CRGB(red1,green1,blue1); +// } + void setRandomColor(byte* rgb) { lastRandomIndex = strip.getMainSegment().get_random_wheel_index(lastRandomIndex); diff --git a/wled00/const.h b/wled00/const.h index f224751212..4e8192ad7d 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -91,6 +91,9 @@ #endif #endif +//WLEDMM seems that 256 is indeed the optimal buffer length +#define FS_BUFSIZE 256 + //Usermod IDs #define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present #define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID @@ -252,8 +255,8 @@ //Network types (master broadcast) (80-95) #define TYPE_NET_DDP_RGB 80 //network DDP RGB bus (master broadcast bus) #define TYPE_NET_E131_RGB 81 //network E131 RGB bus (master broadcast bus, unused) -#define TYPE_NET_ARTNET_RGB 82 //network ArtNet RGB bus (master broadcast bus) -#define TYPE_NET_ARTNET_RGBW 83 //network ArtNet RGB bus (master broadcast bus) +#define TYPE_NET_ARTNET_RGB 82 //network Art-Net RGB bus (master broadcast bus) +#define TYPE_NET_ARTNET_RGBW 83 //network Art-Net RGB bus (master broadcast bus) #define TYPE_NET_DDP_RGBW 88 //network DDP RGBW bus (master broadcast bus) #define IS_DIGITAL(t) (((t) & 0x10) || ((t)==TYPE_HUB75MATRIX)) //digital are 16-31 and 48-63 // WLEDMM added HUB75 @@ -285,7 +288,7 @@ #define BTN_TYPE_ANALOG_INVERTED 8 //Ethernet board types -#define WLED_NUM_ETH_TYPES 12 //WLEDMM +1 for Olimex ESP32-Gateway +#define WLED_NUM_ETH_TYPES 14 //WLEDMM +1 for Olimex ESP32-Gateway +2 for W5500 #define WLED_ETH_NONE 0 #define WLED_ETH_WT32_ETH01 1 @@ -299,6 +302,8 @@ #define WLED_ETH_ABCWLEDV43ETH 9 #define WLED_ETH_SERG74 10 #define WLED_ETH_OLIMEX_GTW 11 +#define WLED_ETH_W5500 12 +#define WLED_ETH_W5500_2 13 //Hue error codes #define HUE_ERROR_INACTIVE 0 @@ -435,7 +440,11 @@ #if !defined(USERMOD_AUDIOREACTIVE) #define SETTINGS_STACK_BUF_SIZE 3834 // WLEDMM added 696+32 bytes of margin (was 3096) #else - #define SETTINGS_STACK_BUF_SIZE 4000 // WLEDMM more buffer for audioreactive UI (add '-D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9216' to your build_flags) + #if defined(MAX_LEDS_PER_BUS) && MAX_LEDS_PER_BUS > 2048 + #define SETTINGS_STACK_BUF_SIZE 5500 // WLEDMM more buffer for audioreactive UI (add '-D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9216' to your build_flags) + #else + #define SETTINGS_STACK_BUF_SIZE 4100 + #endif #endif #endif @@ -481,7 +490,7 @@ #ifdef ESP8266 #define JSON_BUFFER_SIZE 10240 #else - #if defined(BOARD_HAS_PSRAM) && (defined(WLED_USE_PSRAM) || defined(WLED_USE_PSRAM_JSON)) + #if defined(BOARD_HAS_PSRAM) #if defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32C3) #if defined(ARDUINO_ARCH_ESP32C3) #define JSON_BUFFER_SIZE 44000 // WLEDMM - max 44KB on -C3 with PSRAM (chip has 400kb RAM) diff --git a/wled00/data/index.js b/wled00/data/index.js index a89958f575..cb700dcd72 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -2290,10 +2290,9 @@ function toggleLiveview() if (isM) { //WLEDMM adding liveview2D support on main ui isLv = !isLv; - gId("colorGFX").style.display = isLv? "inline":"none"; //WLEDMM: set off if explicitly gfx pushed + // gId("colorGFX").style.display = isLv? "inline":"none"; //WLEDMM: set off if explicitly gfx pushed // TroyHacks: Why??? gId("effectGFX").style.display = isLv? "inline":"none"; gId("segGFX").style.display = isLv? "inline":"none"; - canvasPeek = gId("canvasPeek"); if (isLv) peek(canvasPeek); //W } else { diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 1bde6fbc97..0a15d26250 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -5,7 +5,7 @@ LED Settings