From 32979e59010831b5011f398dff10777c04c82f2c Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sat, 17 Feb 2024 12:50:20 +0100 Subject: [PATCH] lots of bugfixes --- platformio.ini | 30 +++--- wled00/FX.cpp | 189 ++++++++++++++++++++++++++---------- wled00/FXparticleSystem.cpp | 26 ++--- 3 files changed, 171 insertions(+), 74 deletions(-) diff --git a/platformio.ini b/platformio.ini index 13c4feee42..6c1952134b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,13 +11,12 @@ # CI/release binaries ;default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32dev_audioreactive, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB, esp32s3dev_8MB_PSRAM_opi, esp32_wrover -default_envs = esp32c3dev - +;default_envs = esp32c3dev +;default_envs = esp32dev_audioreactive src_dir = ./wled00 data_dir = ./wled00/data build_cache_dir = ~/.buildcache -extra_configs = - platformio_override.ini +extra_configs = platformio_override.ini [common] # ------------------------------------------------------------------------------ @@ -173,7 +172,7 @@ lib_deps = # SHT85 ;robtillaart/SHT85@~0.3.3 # Audioreactive usermod - ;https://github.com/kosme/arduinoFFT#develop @ ^1.9.2 + https://github.com/kosme/arduinoFFT#develop @ ^1.9.2 extra_scripts = ${scripts_defaults.extra_scripts} @@ -394,7 +393,7 @@ build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME= -DLOLIN_WIFI_FIX ; seems to work much better with this -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB ;-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip -upload_speed = 460800 +upload_speed = 460800 ; 115200 230400 460800 build_unflags = ${common.build_unflags} lib_deps = ${esp32c3.lib_deps} @@ -403,19 +402,22 @@ lib_deps = ${esp32c3.lib_deps} board = esp32-s3-devkitc-1 platform = ${esp32s3.platform} platform_packages = ${esp32s3.platform_packages} -upload_speed = 921600 ; or 460800 +upload_speed = 115200 ; 230400 ;460800 build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_8MB -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 -D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip ;-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") - ;-D WLED_DEBUG -lib_deps = ${esp32s3.lib_deps} -board_build.partitions = tools/WLED_ESP32_8MB.csv -board_build.f_flash = 80000000L -board_build.flash_mode = qio -; board_build.flash_mode = dio ;; try this if you have problems at startup -monitor_filters = esp32_exception_decoder + -D WLED_DEBUG + -D USERMOD_AUDIOREACTIVE -D UM_AUDIOREACTIVE_USE_NEW_FFT # additional build flags for audioreactive +lib_deps = ${esp32s3.lib_deps} + https://github.com/kosme/arduinoFFT#develop @ 1.9.2+sha.419d7b0 ;; used for USERMOD_AUDIOREACTIVE - using "known working" hash +;board_build.partitions = tools/WLED_ESP32_8MB.csv +board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv +;board_build.f_flash = 80000000L +;board_build.flash_mode = qio +board_build.flash_mode = dio ;; try this if you have problems at startup +;monitor_filters = esp32_exception_decoder [env:esp32s3dev_8MB_PSRAM_opi] ;; ESP32-S3 development board, with 8MB FLASH and >= 8MB PSRAM (memory_type: qio_opi) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index b81a43a949..8e07477cce 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -6271,7 +6271,7 @@ static const char _data_FX_MODE_2DPLASMAROTOZOOM[] PROGMEM = "Rotozoomer@!,Scale float *fftBin = nullptr; um_data_t *um_data; if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { - volumeSmth = *(float*) um_data->u_data[0]; + volumeSmth = *(float*) um_data->u_data[0]; volumeRaw = *(float*) um_data->u_data[1]; fftResult = (uint8_t*) um_data->u_data[2]; samplePeak = *(uint8_t*) um_data->u_data[3]; @@ -8050,6 +8050,8 @@ uint16_t mode_particlefireworks(void) } // update particles, create particles + uint8_t circularexplosion = random8(numRockets + 2); //choose a rocket by random (but not every round one will be picked) + uint8_t spiralexplosion = random8(numRockets + 2); // check each rocket's state and emit particles according to its state: moving up = emit exhaust, at top = explode; falling down = standby time uint16_t emitparticles; // number of particles to emit for each rocket's state @@ -8069,36 +8071,54 @@ uint16_t mode_particlefireworks(void) { // speed is zero, explode! emitparticles = random8(SEGMENT.intensity>>1) + 10; // defines the size of the explosion rockets[j].source.vy = -1; // set speed negative so it will emit no more particles after this explosion until relaunch - if (j == 0) // first rocket, do an angle emit + if (j == circularexplosion || j == spiralexplosion) // chosen rocket, do an angle emit (creating a circle) { - emitparticles>>2; //emit less particles for circle-explosion + emitparticles>>3; //emit less particles for circle-explosions rockets[j].maxLife = 150; rockets[j].minLife = 120; - rockets[j].var = 0; // speed variation around vx,vy (+/- var/2) + rockets[j].var = 0; // speed variation around vx,vy (+/- var/2) } } - uint8_t speed = 5; + uint8_t speed = 3; uint8_t angle = 0; + if (j == spiralexplosion) + angle = random(8); for (i; i < numParticles; i++) { if (particles[i].ttl == 0) { // particle is dead - if (j == 0 && emitparticles > 2) // first rocket, do angle emit + if (j == circularexplosion && emitparticles > 2) //do circle emit { Emitter_Angle_emit(&rockets[j], &particles[i],angle,speed); - emitparticles--; - //set angle for next particle - angle += 21; //about 30° - if(angle > 250) //full circle completed, increase speed and reset angle + + if (angle > 242) // full circle completed, increase speed and reset angle { - angle = 0; - speed += 8; - rockets[j].source.hue = random8(); //new color for next row - rockets[j].source.sat = random8(); + angle += 10; + speed += 6; + rockets[j].source.hue = random8(); // new color for next row + rockets[j].source.sat = random8(); + if(emitparticles > 12) + emitparticles-=12; //emitted about 12 particles for one circle, ensures no incomplete circles are done + else + emitparticles = 0; } + + //set angle for next particle + angle += 21; //about 30° + } + else if (j == spiralexplosion && emitparticles > 2) // do spiral emit + { + Emitter_Angle_emit(&rockets[j], &particles[i], angle, speed); + emitparticles--; + emitparticles--;//only emit half as many particles as in circle explosion, it gets too huge otherwise + angle += 15; + speed++; + rockets[j].source.hue++; + rockets[j].source.sat = random8(155)+100; + } else if (emitparticles > 0) { @@ -8138,7 +8158,7 @@ uint16_t mode_particlefireworks(void) rockets[i].source.sat = random8(100)+155; rockets[i].maxLife = 200; rockets[i].minLife = 50; - rockets[i].source.ttl = random8((255 - SEGMENT.speed))+10; // standby time til next launch (in frames at 42fps, max of 265 is about 6 seconds + rockets[i].source.ttl = random8((255 - SEGMENT.speed))+50; // standby time til next launch (in frames at 42fps, max of 300 is about 7 seconds rockets[i].vx = 0; // emitting speed rockets[i].vy = 0; // emitting speed rockets[i].var = (SEGMENT.intensity >> 3) + 10; // speed variation around vx,vy (+/- var/2) @@ -8167,7 +8187,7 @@ uint16_t mode_particlefireworks(void) return FRAMETIME; } -static const char _data_FX_MODE_PARTICLEFIREWORKS[] PROGMEM = "Particle Fireworks@Launches,Explosion Size,Height,Bounce,Rockets,Wrap X,Bounce X,Bounce Y;;!;012;pal=11,sx=100,ix=50,c1=64,c2=128,c3=10,o1=0,o2=0,o3=1"; +static const char _data_FX_MODE_PARTICLEFIREWORKS[] PROGMEM = "Particle Fireworks@Launches,Explosion Size,Height,Bounce,Rockets,Wrap X,Bounce X,Bounce Y;;!;012;pal=11,sx=100,ix=50,c1=64,c2=128,c3=10,o1=0,o2=0,o3=0"; /* * Particle Volcano (gravity spray) @@ -8517,8 +8537,8 @@ uint16_t mode_particlefall(void) // calculate the end of the spray data and assign it as the data pointer for the particles: particles = reinterpret_cast(SEGENV.data); // cast the data array into a particle pointer + uint32_t i = 0; - uint32_t j = 0; if (SEGMENT.call == 0) // initialization { @@ -8527,7 +8547,7 @@ uint16_t mode_particlefall(void) particles[i].ttl = 0; } } - particles[i].sat = ((SEGMENT.custom3) << 3) + 7; // set saturation + if (SEGMENT.call % (64 - (SEGMENT.intensity >> 2)) == 0 && SEGMENT.intensity > 1) // every nth frame emit particles, stop emitting if zero { while (i < numParticles) // emit particles @@ -8535,7 +8555,7 @@ uint16_t mode_particlefall(void) if (particles[i].ttl == 0) // find a dead particle { // emit particle at random position just over the top of the matrix - particles[i].ttl = 3000 - (SEGMENT.speed << 3) + random16(500); // if speed is higher, make them die sooner + particles[i].ttl = 1500 - (SEGMENT.speed << 2) + random16(500); // if speed is higher, make them die sooner if (random8(5) == 0) // 16% of particles apper anywhere particles[i].x = random16(cols * PS_P_RADIUS - 1); @@ -8565,7 +8585,7 @@ uint16_t mode_particlefall(void) { applyFriction(&particles[i], 50 - SEGMENT.speed); } - Particle_Gravity_update(&particles[i], SEGMENT.check1, SEGMENT.check2, SEGMENT.check3, 150); // surface hardness is fixed to 150 + Particle_Gravity_update(&particles[i], SEGMENT.check1, SEGMENT.check2, SEGMENT.check3, min(hardness,(uint8_t)150)); // surface hardness max is 150 } SEGMENT.fill(BLACK); // clear the matrix @@ -8580,17 +8600,69 @@ uint16_t mode_particlefall(void) static const char _data_FX_MODE_PARTICLEFALL[] PROGMEM = "Falling Particles@Speed,Intensity,Randomness,Collision hardness,Saturation,Wrap X,Side bounce,Ground bounce;;!;012;pal=11,sx=100,ix=200,c1=31,c2=0,c3=20,o1=0,o2=0,o3=1"; /* - * Particle pile up test - * Uses palette for particle color + * Particle Waterfall + * Uses palette for particle color, spray source at top emitting particles, many config options * by DedeHai (Damian Schneider) */ +/*Audio Reactive test: + + volumeSmth = *(float*) um_data->u_data[0]; + volumeRaw = *(float*) um_data->u_data[1]; + fftResult = (uint8_t*) um_data->u_data[2]; + samplePeak = *(uint8_t*) um_data->u_data[3]; + FFT_MajorPeak = *(float*) um_data->u_data[4]; + my_magnitude = *(float*) um_data->u_data[5]; + maxVol = (uint8_t*) um_data->u_data[6]; // requires UI element (SEGMENT.customX?), changes source element + binNum = (uint8_t*) um_data->u_data[7]; // requires UI element (SEGMENT.customX?), changes source element + fftBin = (float*) um_data->u_data[8]; //not sure what this pointer does... not used in any examples + */ + uint16_t mode_particlewaterfall(void) { if (SEGLEN == 1) return mode_static(); + +/* + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) + { + um_data = NULL; //no audio + } + //TODO: will crash if no umdata! + uint8_t volumeSmth = (uint8_t)(*(float *)um_data->u_data[0]); //volume, mapped 0-255 + float volumeRaw = *(float *)um_data->u_data[1]; //always zero? + uint8_t* fftResult = (uint8_t *)um_data->u_data[2]; //16 bins with FFT data + uint8_t samplePeak = *(uint8_t *)um_data->u_data[3]; //0 or 1 if a sample peak is detected (not sure what the thresholds are) + float FFT_MajorPeak = *(float *)um_data->u_data[4]; //frequency in Hz of major peak + float my_magnitude = *(float *)um_data->u_data[5]; //current volume, should fit a uint16_t (goes up to 20'000 or even higher?) but unclear what exactly the value is + //maxVol = (uint8_t *)um_data->u_data[6]; // requires UI element (SEGMENT.customX?), changes source element, see Ripple Peak for an example + //binNum = (uint8_t *)um_data->u_data[7]; // requires UI element (SEGMENT.customX?), changes source element + //float* fftBin = (float *)um_data->u_data[8]; //points to what exactly? + */ +/* + //print values as a test: + Serial.println("***"); + Serial.print(volumeSmth); + Serial.print(", "); + Serial.print(volumeRaw); + Serial.print(", "); + Serial.print(samplePeak); + Serial.print(", "); + Serial.print(FFT_MajorPeak); + Serial.print(", "); + Serial.println(my_magnitude); + + for(int i = 0; i<16;i++) + { + Serial.print(fftResult[i]); + Serial.print(" "); + } + + Serial.println("***"); +*/ const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1; const uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength(); @@ -8641,33 +8713,40 @@ uint16_t mode_particlewaterfall(void) { spray[i].source.hue++; //change hue of spray source } - if (SEGMENT.call % (9 - (SEGMENT.intensity>>5)) == 0 && SEGMENT.intensity > 0) // every nth frame, cycle color and emit particles, do not emit if intensity is zero + + uint8_t intensity = SEGMENT.intensity; +/* + if (um_data != NULL) //audio reactive data available { + intensity = map(volumeSmth,0,255,20,255); + }*/ + if (SEGMENT.call % (9 - (intensity >> 5)) == 0 && intensity > 0) // every nth frame, cycle color and emit particles, do not emit if intensity is zero + { - for (i = 0; i < numSprays; i++) - { - spray[i].vy = -SEGMENT.speed >> 3; // emitting speed, down - spray[i].source.x = map(SEGMENT.custom3, 0, 31, 0, (cols - 2) * PS_P_RADIUS) + i * PS_P_RADIUS * 2; // emitter position - spray[i].source.ttl = 255; // source never dies, replenish its lifespan - spray[i].var = SEGMENT.custom1 >> 3; // emiting variation 0-32 - } + for (i = 0; i < numSprays; i++) + { + spray[i].vy = -SEGMENT.speed >> 3; // emitting speed, down + spray[i].source.x = map(SEGMENT.custom3, 0, 31, 0, (cols - 2) * PS_P_RADIUS) + i * PS_P_RADIUS * 2; // emitter position + spray[i].source.ttl = 255; // source never dies, replenish its lifespan + spray[i].var = SEGMENT.custom1 >> 3; // emiting variation 0-32 + } - i = 0; - j = 0; - while (i < numParticles) - { - if (particles[i].ttl == 0) // find a dead particle + i = 0; + j = 0; + while (i < numParticles) { - Emitter_Fountain_emit(&spray[j], &particles[i]); - j = (j + 1) % numSprays; - if (percycle-- == 0) + if (particles[i].ttl == 0) // find a dead particle { - break; // quit loop if all particles of this round emitted + Emitter_Fountain_emit(&spray[j], &particles[i]); + j = (j + 1) % numSprays; + if (percycle-- == 0) + { + break; // quit loop if all particles of this round emitted + } } + i++; } - i++; } - } // detect and handle collisions @@ -8725,6 +8804,8 @@ uint16_t mode_particlebox(void) return mode_static(); // allocation failed; //allocation failed particles = reinterpret_cast(SEGENV.data); // cast the data array into a particle pointer + + uint32_t i = 0; uint32_t j = 0; @@ -8837,7 +8918,7 @@ uint16_t mode_particlebox(void) return FRAMETIME; } -static const char _data_FX_MODE_PARTICLEBOX[] PROGMEM = "Particle Box@Speed,Particles,Tilt strength,Bouncyness,,Rocking Boat,;;!;012;pal=1,sx=120,ix=100,c1=190,c2=210,o1=0"; +static const char _data_FX_MODE_PARTICLEBOX[] PROGMEM = "Particle Box@Speed,Particles,Tilt strength,Hardness,,Rocking Boat,;;!;012;pal=1,sx=120,ix=100,c1=190,c2=210,o1=0"; /* perlin noise 'gravity' mapping as in particles on noise hills viewed from above @@ -8856,7 +8937,12 @@ uint16_t mode_particleperlin(void) const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1; const uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength(); - const uint32_t numParticles = 255; + +#ifdef ESP8266 + const uint32_t numParticles = 200; +#else + const uint32_t numParticles = 350; +#endif PSparticle *particles; @@ -8881,7 +8967,7 @@ uint16_t mode_particleperlin(void) } } - uint32_t displayparticles = SEGMENT.intensity; + uint32_t displayparticles = map(SEGMENT.intensity,0,255,10,numParticles); // apply 'gravity' from a 2D perlin noise map SEGMENT.aux0 += SEGMENT.speed >> 4; // noise z-position; @@ -8928,7 +9014,7 @@ uint16_t mode_particleperlin(void) return FRAMETIME; } static const char _data_FX_MODE_PARTICLEPERLIN[] PROGMEM = "Particle Perlin-Noise@,Particles,;;!;012;pal=1,ix=100"; - +//TODO: add zoom and maybe some other functions /* * Particle smashing down like meteorites and exploding as they hit the ground, like inverted fireworks * has many parameters to play with @@ -9105,7 +9191,12 @@ uint16_t mode_particleattractor(void) const uint16_t PS_MAX_X(cols * PS_P_RADIUS - 1); const uint16_t PS_MAX_Y(rows * PS_P_RADIUS - 1); - const uint32_t numParticles = 265; // maximum number of particles +#ifdef ESP8266 + const uint32_t numParticles = 150; // maximum number of particles +#else + const uint32_t numParticles = 300; // maximum number of particles +#endif + PSparticle *particles; PSparticle *attractor; @@ -9126,8 +9217,8 @@ uint16_t mode_particleattractor(void) spray = reinterpret_cast(attractor + 1); counters = reinterpret_cast(spray + 1); - uint32_t i = 0; - uint32_t j = 0; + uint32_t i; + uint32_t j; if (SEGMENT.call == 0) // initialization { @@ -9156,7 +9247,7 @@ uint16_t mode_particleattractor(void) spray->var = 6; //emitting speed variation } - uint16_t displayparticles = SEGMENT.intensity; //cannot go to 255 particles, it will crash if set above 250, why is unclear... maybe array is too small? at 260 it will still crash, if numparticles are set to 265 it does not crash... + uint32_t displayparticles = map(SEGMENT.intensity, 0, 255, 1, numParticles); uint8_t hardness = SEGMENT.custom2; // how hard the collisions are, 255 = full hard. i = 0; j = 0; diff --git a/wled00/FXparticleSystem.cpp b/wled00/FXparticleSystem.cpp index be6dcae2bd..b62ccb3780 100644 --- a/wled00/FXparticleSystem.cpp +++ b/wled00/FXparticleSystem.cpp @@ -85,31 +85,31 @@ void Particle_attractor(PSparticle *particle, PSparticle *attractor, uint8_t *co particle->ttl = 0; return; } - distanceSquared = PS_P_RADIUS * PS_P_RADIUS + PS_P_RADIUS * PS_P_RADIUS; // limit the distance of particle size to avoid very high forces + distanceSquared = 4 * PS_P_RADIUS * PS_P_RADIUS; // limit the distance of particle size to avoid very high forces } - // check if distance is small enough to even cause a force (for that strength<<10 must be bigger than the distance squared) + int32_t shiftedstrength = (int32_t)strength << 16; int32_t force; int32_t xforce; int32_t yforce; int32_t xforce_abs; // absolute value int32_t yforce_abs; - - if (shiftedstrength < distanceSquared) // if far away, set the force to 1 so it still attracts and does not leave particles just sitting outside its influence radius - { + // check if distance is small enough to even cause a force (for that strength<<10 must be bigger than the distance squared) + //if ((shiftedstrength) < distanceSquared) // if far away, set the force to 2 so it still attracts and does not leave particles just sitting outside its influence radius + //{ // force calculation above is zero //give some force in both directions (x and y) to avoid further calculations as this is just to get things moving a little - xforce_abs = 1; - yforce_abs = 1; - } - else - { + //xforce_abs = 1; + //yforce_abs = 1; + //} + //else + //{ force = shiftedstrength / distanceSquared; xforce = (force * dx) >> 10; // scale to a lower value, found by experimenting yforce = (force * dy) >> 10; xforce_abs = abs(xforce); // absolute value yforce_abs = abs(yforce); - } + //} uint8_t xcounter = (*counter) & 0x0F; // lower four bits uint8_t ycounter = (*counter) >> 4; // upper four bits @@ -133,6 +133,8 @@ void Particle_attractor(PSparticle *particle, PSparticle *attractor, uint8_t *co particle->vx += 1; } } + else //save counter value + *counter |= xcounter & 0x0F; // write lower four bits, make sure not to write more than 4 bits } else { @@ -158,6 +160,8 @@ void Particle_attractor(PSparticle *particle, PSparticle *attractor, uint8_t *co particle->vy += 1; } } + else // save counter value + *counter |= (ycounter << 4) & 0xF0; // write upper four bits } else {