updated libraries

This commit is contained in:
stuce-bot 2025-06-30 21:41:49 +02:00
parent d5d5d000b3
commit 3b7e68065a
102 changed files with 3020 additions and 1624 deletions

View file

@ -3,10 +3,31 @@
/// @author Stefan Petrick
/// @author Zach Vorhies (FastLED adaptation)
///
/// This sketch is fully compatible with the FastLED web compiler. To use it do the following:
/// 1. Install Fastled: `pip install fastled`
/// 2. cd into this examples page.
/// 3. Run the FastLED web compiler at root: `fastled`
/*
This demo is best viewed using the FastLED compiler.
Windows/MacOS binaries: https://github.com/FastLED/FastLED/releases
Python
Install: pip install fastled
Run: fastled <this sketch directory>
This will compile and preview the sketch in the browser, and enable
all the UI elements you see below.
OVERVIEW:
This is the famouse Animartrix demo by Stefan Petrick. The effects are generated
using polor polar coordinates. The effects are very complex and powerful.
*/
#if !SKETCH_HAS_LOTS_OF_MEMORY
// Platform does not have enough memory
void setup() {}
void loop() {}
#else
#include <stdio.h>
@ -34,6 +55,10 @@ using namespace fl;
#define FIRST_ANIMATION POLAR_WAVES
// This is purely use for the web compiler to display the animartrix effects.
// This small led was chosen because otherwise the bloom effect is too strong.
#define LED_DIAMETER 0.15 // .15 cm or 1.5mm
CRGB leds[NUM_LEDS];
XYMap xyMap = XYMap::constructRectangularGrid(MATRIX_WIDTH, MATRIX_HEIGHT);
@ -44,19 +69,33 @@ UIDescription description("Demo of the Animatrix effects. @author of fx is Stefa
UISlider brightness("Brightness", 255, 0, 255);
UINumberField fxIndex("Animartrix - index", 0, 0, NUM_ANIMATIONS - 1);
UINumberField colorOrder("Color Order", 0, 0, 5);
UISlider timeSpeed("Time Speed", 1, -10, 10, .1);
Animartrix animartrix(xyMap, FIRST_ANIMATION);
FxEngine fxEngine(NUM_LEDS);
void setup() {
auto screen_map = xyMap.toScreenMap();
screen_map.setDiameter(.1);
screen_map.setDiameter(LED_DIAMETER);
FastLED.addLeds<WS2811, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS)
.setCorrection(TypicalLEDStrip)
.setScreenMap(screen_map);
FastLED.setBrightness(brightness);
fxEngine.addFx(animartrix);
colorOrder.onChanged([](int value) {
switch(value) {
case 0: value = RGB; break;
case 1: value = RBG; break;
case 2: value = GRB; break;
case 3: value = GBR; break;
case 4: value = BRG; break;
case 5: value = BGR; break;
}
animartrix.setColorOrder(static_cast<EOrder>(value));
});
}
void loop() {
@ -72,3 +111,4 @@ void loop() {
}
#endif // __AVR__

View file

@ -2,6 +2,11 @@
/*
This demo is best viewed using the FastLED compiler.
Windows/MacOS binaries: https://github.com/FastLED/FastLED/releases
Python
Install: pip install fastled
Run: fastled <this sketch directory>
This will compile and preview the sketch in the browser, and enable
@ -11,6 +16,14 @@ all the UI elements you see below.
#include <Arduino.h>
#include <FastLED.h>
#if !SKETCH_HAS_LOTS_OF_MEMORY
// Platform does not have enough memory
void setup() {}
void loop() {}
#else
#include "fl/audio.h"
#include "fl/downscale.h"
#include "fl/draw_visitor.h"
@ -21,9 +34,11 @@ all the UI elements you see below.
#include "fl/time_alpha.h"
#include "fl/ui.h"
#include "fl/xypath.h"
#include "fx.h"
#include "fl/unused.h"
#include "fx_audio.h"
#include "fx/time.h"
// Sketch.
#include "fl/function.h"
@ -34,6 +49,7 @@ using namespace fl;
#define NUM_LEDS ((WIDTH) * (HEIGHT))
#define IS_SERPINTINE false
#define TIME_ANIMATION 1000 // ms
#define PIN_DATA 3
UITitle title("Simple control of an xy path");
UIDescription description("This is more of a test for new features.");
@ -106,7 +122,7 @@ void setup() {
audioFadeTracker.setOutputTime(value);
FASTLED_WARN("Output time seconds: " << value);
});
FastLED.addLeds<NEOPIXEL, 2>(leds, ledsXY.getTotal())
FastLED.addLeds<NEOPIXEL, PIN_DATA>(leds, ledsXY.getTotal())
.setScreenMap(screenmap);
}
@ -144,10 +160,6 @@ void loop() {
if (triggered) {
FASTLED_WARN("Triggered");
}
// fl::clear(framebuffer);
// fl::clear(framebuffer);
static uint32_t frame = 0;
// x = pointX.as_int();
y = HEIGHT / 2;
@ -164,6 +176,7 @@ void loop() {
soundLevelMeter.processBlock(sample.pcm());
// FASTLED_WARN("")
auto dbfs = soundLevelMeter.getDBFS();
FASTLED_UNUSED(dbfs);
// FASTLED_WARN("getDBFS: " << dbfs);
int32_t max = 0;
for (int i = 0; i < sample.pcm().size(); ++i) {
@ -187,6 +200,7 @@ void loop() {
if (enableFFT) {
auto max_x = fftOut.bins_raw.size() - 1;
FASTLED_UNUSED(max_x);
for (int i = 0; i < fftOut.bins_raw.size(); ++i) {
auto x = i;
auto v = fftOut.bins_db[i];
@ -232,3 +246,5 @@ void loop() {
FastLED.show();
}
#endif // __AVR__

View file

@ -1,9 +1,7 @@
#include <algorithm>
#include <cstdint>
#include <cmath>
#include <cassert>
#include "fl/time_alpha.h"
#include "fl/math_macros.h"
/// Tracks a smoothed peak with attack, decay, and output-inertia time-constants.
class MaxFadeTracker {
@ -34,8 +32,8 @@ public:
// 1) block peak
float peak = 0.0f;
for (size_t i = 0; i < length; ++i) {
float v = std::abs(samples[i]) * (1.0f/32768.0f);
peak = std::max(peak, v);
float v = ABS(samples[i]) * (1.0f/32768.0f);
peak = MAX(peak, v);
}
// 2) time delta
@ -43,15 +41,15 @@ public:
// 3) update currentLevel_ with attack/decay
if (peak > currentLevel_) {
float riseFactor = 1.0f - std::exp(-attackRate_ * dt);
float riseFactor = 1.0f - exp(-attackRate_ * dt);
currentLevel_ += (peak - currentLevel_) * riseFactor;
} else {
float decayFactor = std::exp(-decayRate_ * dt);
float decayFactor = exp(-decayRate_ * dt);
currentLevel_ *= decayFactor;
}
// 4) output inertia: smooth smoothedOutput_ → currentLevel_
float outFactor = 1.0f - std::exp(-outputRate_ * dt);
float outFactor = 1.0f - exp(-outputRate_ * dt);
smoothedOutput_ += (currentLevel_ - smoothedOutput_) * outFactor;
return smoothedOutput_;

View file

@ -11,7 +11,7 @@
*/
#if defined(__AVR__) || defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_RP2350) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_TEENSYLC)
// Avr is not powerful enough.
// Platform does not have enough memory
// Other platforms have weird issues. Will revisit this later.
void setup() {}
void loop() {}

View file

@ -0,0 +1,18 @@
#include "FastLED.h"
#if defined(__AVR__)
#include "avr_test.h"
#elif defined(ESP32)
#include "esp_test.h"
#endif
void setup() {
Serial.begin(115200);
Serial.println("Setup");
}
void loop() {
Serial.println("Loop");
delay(100);
}

View file

@ -0,0 +1,13 @@
#pragma once
#include "FastLED.h"
void avr_tests() {
#if FASTLED_USE_PROGMEM != 1
#error "FASTLED_USE_PROGMEM is not 1 for AVR"
#endif
#if SKETCH_HAS_LOTS_OF_MEMORY != 0
#error "SKETCH_HAS_LOTS_OF_MEMORY is not 0 for AVR"
#endif
}

View file

@ -0,0 +1,13 @@
#pragma once
#include "FastLED.h"
void esp_8266_tests() {
#if FASTLED_USE_PROGMEM != 1
#error "FASTLED_USE_PROGMEM is not 1 for AVR"
#endif
#if SKETCH_HAS_LOTS_OF_MEMORY != 0
#error "SKETCH_HAS_LOTS_OF_MEMORY is not 0 for AVR"
#endif
}

View file

@ -0,0 +1,13 @@
#pragma once
#include "FastLED.h"
void esp32_s3_tests() {
#if FASTLED_USE_PROGMEM != 0
#error "FASTLED_USE_PROGMEM prog memory is turned on for esap32-s3"
#endif
#if SKETCH_HAS_LOTS_OF_MEMORY == 0
#error "SKETCH_HAS_LOTS_OF_MEMORY should have lots of memory for esp32-s3"
#endif
}

View file

@ -0,0 +1,10 @@
#pragma once
#include "sdkconfig.h"
#if defined(ESP8266)
#include "esp_8266_test.h"
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
#include "esp_s3_test.h"
#endif

View file

@ -0,0 +1,134 @@
/*
Basic cork screw test.
This test is forward mapping, in which we test that
the corkscrew is mapped to cylinder cartesian coordinates.
Most of the time, you'll want the reverse mapping, that is
drawing to a rectangular grid, and then mapping that to a corkscrew.
However, to make sure the above mapping works correctly, we have
to test that the forward mapping works correctly first.
*/
#include "fl/assert.h"
#include "fl/corkscrew.h"
#include "fl/grid.h"
#include "fl/leds.h"
#include "fl/screenmap.h"
#include "fl/sstream.h"
#include "fl/warn.h"
#include "noise.h"
#include <FastLED.h>
// #include "vec3.h"
using namespace fl;
#define PIN_DATA 9
#define NUM_LEDS 288
#define CORKSCREW_TOTAL_LENGTH 100
#define CORKSCREW_TOTAL_HEIGHT 23.25 // when height = 0, it's a circle.
// wrapped up over 19 turns
#define CORKSCREW_TURNS 19 // Default to 19 turns
// #define CM_BETWEEN_LEDS 1.0 // 1cm between LEDs
// #define CM_LED_DIAMETER 0.5 // 0.5cm LED diameter
UITitle festivalStickTitle("Corkscrew");
UIDescription festivalStickDescription(
"Tests the ability to map a cork screw onto a 2D cylindrical surface");
UISlider speed("Speed", 0.1f, 0.01f, 1.0f, 0.01f);
UICheckbox allWhite("All White", false);
UICheckbox splatRendering("Splat Rendering", true);
// CRGB leds[NUM_LEDS];
// Tested on a 288 led (2x 144 max density led strip) with 19 turns
// with 23.25cm height, 19 turns, and ~15.5 LEDs per turn.
Corkscrew::Input corkscrewInput(CORKSCREW_TOTAL_LENGTH, CORKSCREW_TOTAL_HEIGHT,
CORKSCREW_TURNS, // Default to 19 turns
NUM_LEDS, // Default to dense 144 leds.
0 // offset to account for gaps between segments
);
// Corkscrew::State corkscrewMap = fl::Corkscrew::generateMap(corkscrewInput);
Corkscrew corkscrew(corkscrewInput);
// Create a corkscrew with:
// - 30cm total length (300mm)
// - 5cm width (50mm)
// - 2mm LED inner diameter
// - 24 LEDs per turn
// fl::ScreenMap screenMap = makeCorkScrew(NUM_LEDS,
// 300.0f, 50.0f, 2.0f, 24.0f);
// fl::vector<vec3f> mapCorkScrew = makeCorkScrew(args);
fl::ScreenMap screenMap;
fl::Grid<CRGB> frameBuffer;
void setup() {
int width = corkscrew.cylinder_width();
int height = corkscrew.cylinder_height();
frameBuffer.reset(width, height);
XYMap xyMap = XYMap::constructRectangularGrid(width, height, 0);
CRGB *leds = frameBuffer.data();
size_t num_leds = frameBuffer.size();
CLEDController *controller =
&FastLED.addLeds<WS2812, 3, BGR>(leds, num_leds);
fl::ScreenMap screenMap = xyMap.toScreenMap();
screenMap.setDiameter(.2f);
// Set the screen map for the controller
controller->setScreenMap(screenMap);
}
void loop() {
fl::clear(frameBuffer);
static float pos = 0;
pos += speed.value();
if (pos > corkscrew.size() - 1) {
pos = 0; // Reset to the beginning
}
if (allWhite) {
for (size_t i = 0; i < frameBuffer.size(); ++i) {
frameBuffer.data()[i] = CRGB(8, 8, 8);
}
}
if (splatRendering) {
Tile2x2_u8_wrap pos_tile = corkscrew.at_wrap(pos);
const CRGB color = CRGB::Blue;
// Draw each pixel in the 2x2 tile using the new wrapping API
for (int dx = 0; dx < 2; ++dx) {
for (int dy = 0; dy < 2; ++dy) {
auto data = pos_tile.at(dx, dy);
vec2i16 wrapped_pos = data.first; // Already wrapped position
uint8_t alpha = data.second; // Alpha value
if (alpha > 0) { // Only draw if there's some alpha
CRGB c = color;
c.nscale8(alpha); // Scale the color by the alpha value
frameBuffer.at(wrapped_pos.x, wrapped_pos.y) = c;
}
}
}
} else {
// None splat rendering, looks aweful.
vec2f pos_vec2f = corkscrew.at_exact(pos);
vec2i16 pos_i16 = vec2i16(round(pos_vec2f.x), round(pos_vec2f.y));
// Now map the cork screw position to the cylindrical buffer that we
// will draw.
frameBuffer.at(pos_i16.x, pos_i16.y) =
CRGB::Blue; // Draw a blue pixel at (w, h)
}
FastLED.show();
}

View file

@ -0,0 +1,11 @@
#include <FastLED.h> // Main FastLED library for controlling LEDs
#if !SKETCH_HAS_LOTS_OF_MEMORY
// Don't compile this for AVR microcontrollers (like Arduino Uno) because they typically
// don't have enough memory to handle this complex animation.
// Instead, we provide empty setup/loop functions so the sketch will compile but do nothing.
void setup() {}
void loop() {}
#else // For all other platforms with more memory (ESP32, Teensy, etc.)
#include "Corkscrew.h"
#endif // End of the non-AVR code section

View file

@ -2,6 +2,11 @@
/*
This demo is best viewed using the FastLED compiler.
Windows/MacOS binaries: https://github.com/FastLED/FastLED/releases
Python
Install: pip install fastled
Run: fastled <this sketch directory>
This will compile and preview the sketch in the browser, and enable
@ -11,6 +16,7 @@ all the UI elements you see below.
#include <Arduino.h>
#include <FastLED.h>
#include "fl/downscale.h"
#include "fl/draw_visitor.h"
#include "fl/math_macros.h"
#include "fl/raster.h"
@ -18,7 +24,6 @@ all the UI elements you see below.
#include "fl/ui.h"
#include "fl/xypath.h"
#include "fx/time.h"
#include "fl/bilinear_compression.h"
// Sketch.
#include "src/wave.h"
@ -32,10 +37,11 @@ using namespace fl;
#define TIME_ANIMATION 1000 // ms
CRGB leds[NUM_LEDS];
CRGB leds_downscaled[NUM_LEDS / 4]; // Downscaled buffer
CRGB leds_downscaled[NUM_LEDS / 4]; // Downscaled buffer
XYMap xyMap(WIDTH, HEIGHT, false);
XYMap xyMap_Dst(WIDTH / 2, HEIGHT / 2, false); // Framebuffer is regular rectangle LED matrix.
XYMap xyMap_Dst(WIDTH / 2, HEIGHT / 2,
false); // Framebuffer is regular rectangle LED matrix.
// XYPathPtr shape = XYPath::NewRosePath(WIDTH, HEIGHT);
// Speed up writing to the super sampled waveFx by writing
@ -44,7 +50,6 @@ XYMap xyMap_Dst(WIDTH / 2, HEIGHT / 2, false); // Framebuffer is regular rectang
WaveEffect wave_fx; // init in setup().
fl::vector<XYPathPtr> shapes = CreateXYPaths(WIDTH, HEIGHT);
XYRaster raster(WIDTH, HEIGHT);
TimeWarp time_warp;
@ -77,12 +82,12 @@ void setupUiCallbacks() {
maxAnimation.onChanged(
[](float value) { shapeProgress.set_max_clamp(maxAnimation.value()); });
trigger.onChanged([]() {
trigger.onClicked([]() {
// shapeProgress.trigger(millis());
FASTLED_WARN("Trigger pressed");
});
useWaveFx.onChanged([](bool on) {
if (on) {
useWaveFx.onChanged([](fl::UICheckbox &checkbox) {
if (checkbox.value()) {
FASTLED_WARN("WaveFX enabled");
} else {
FASTLED_WARN("WaveFX disabled");
@ -94,7 +99,8 @@ void setup() {
Serial.begin(115200);
auto screenmap = xyMap.toScreenMap();
screenmap.setDiameter(.2);
FastLED.addLeds<NEOPIXEL, 2>(leds, xyMap.getTotal()).setScreenMap(screenmap);
FastLED.addLeds<NEOPIXEL, 2>(leds, xyMap.getTotal())
.setScreenMap(screenmap);
auto screenmap2 = xyMap_Dst.toScreenMap();
screenmap.setDiameter(.5);
screenmap2.addOffsetY(-HEIGHT / 2);
@ -142,9 +148,6 @@ void loop() {
s_prev_alpha = curr_alpha;
}
const bool is_active =
true || curr_alpha < maxAnimation.value() && curr_alpha > 0.0f;
static uint32_t frame = 0;
frame++;
clearLeds();
@ -171,9 +174,6 @@ void loop() {
}
uint8_t alpha =
fl::map_range<uint8_t>(i, 0.0f, number_of_steps - 1, 64, 255);
if (!is_active) {
alpha = 0;
}
Tile2x2_u8 subpixel = shape->at_subpixel(a);
subpixel.scale(alpha);
// subpixels.push_back(subpixel);
@ -182,8 +182,7 @@ void loop() {
s_prev_alpha = curr_alpha;
if (useWaveFx && is_active) {
if (useWaveFx) {
DrawRasterToWaveSimulator draw_wave_fx(&wave_fx);
raster.draw(xyMap, draw_wave_fx);
} else {

View file

@ -0,0 +1,14 @@
#include <Arduino.h>
#include <FastLED.h>
#if !SKETCH_HAS_LOTS_OF_MEMORY
// Platform does not have enough memory
void setup() {}
void loop() {}
#else
#include "Downscale.h"
#endif // SKETCH_HAS_LOTS_OF_MEMORY

View file

@ -22,7 +22,7 @@ struct WaveEffect {
struct DrawRasterToWaveSimulator {
DrawRasterToWaveSimulator(WaveEffect* wave_fx) : mWaveFx(wave_fx) {}
void draw(const vec2<int> &pt, uint32_t index, uint8_t value) {
void draw(const vec2<int16_t> &pt, uint32_t index, uint8_t value) {
float valuef = value / 255.0f;
int xx = pt.x;
int yy = pt.y;

View file

@ -3,60 +3,40 @@
/// The Yves ESP32_S3 I2S driver is a driver that uses the I2S peripheral on the ESP32-S3 to drive leds.
/// Originally from: https://github.com/hpwit/I2SClockLessLedDriveresp32s3
///
///
/// This is an advanced driver. It has certain ramifications.
/// - You probably aren't going to be able to use this in ArduinoIDE, because ArduinoIDE does not allow you to put in the necessary build flags.
/// You will need to use PlatformIO to build this.
/// - These flags enable PSRAM.
/// - Once flashed, the ESP32-S3 might NOT want to be reprogrammed again. To get around
/// this hold the reset button and release when the flash tool is looking for an
/// an upload port.
/// - Put a delay in the setup function. This is to make it easier to flash the device.
/// - Put a delay in the setup function. This is to make it easier to flash the device during developement.
/// - Serial output will mess up the DMA controller. I'm not sure why this is happening
/// but just be aware of it. If your device suddenly stops works, remove the printfs and see if that fixes the problem.
/// - You MUST use all the available PINS specified in this demo. Anything less than that will cause FastLED to crash.
/// - Certain leds will turn white in debug mode. Probably has something to do with timing.
/// but just be aware of it. If your device suddenly stops working, remove the printfs and see if that fixes the problem.
///
/// Is RGBW supported? Yes.
///
/// Is Overclocking supported? No.
/// Is Overclocking supported? Yes. Use this to bend the timeings to support other WS281X variants. Fun fact, just overclock the
/// chipset until the LED starts working.
///
/// What about the new WS2812-5VB leds? Kinda. We put in a hack to add the extra wait time of 300 uS.
/// What about the new WS2812-5VB leds? Yes, they have 250us timing.
///
/// What are the advantages of using the FastLED bindings over the raw driver?
/// - FastLED api is more user friendly since you don't have to combine all your leds into one rectangular block.
/// - FastLED api allows you to have different sized strips which will be upscaled to the largest strip internally.
///
/// What are the advantages of using the raw driver over the FastLED bindings?
/// - The raw driver uses less memory because it doesn't have a frame buffer copy.
/// Why use this?
/// Raw YVes driver needs a perfect parallel rectacngle buffer for operation. In this code we've provided FastLED
/// type bindings.
///
// ArduinoIDE
// Should already be enabled.
//
// PLATFORMIO BUILD FLAGS:
// Define your platformio.ini like so:
//
// PlatformIO
// [env:esp32s3]
// platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.04/platform-espressif32.zip
// platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.20/platform-espressif32.zip
// framework = arduino
// board = seeed_xiao_esp32s3
// build_flags =
// ${env:generic-esp.build_flags}
// -DBOARD_HAS_PSRAM
// -mfix-esp32-psram-cache-issue
// -mfix-esp32-psram-cache-strategy=memw
// board_build.partitions = huge_app.csv
//
// Then in your setup function you are going to want to call psramInit();
//
// Want to get a contributor badge for FastLED? This driver has only been lightly tested.
// There are certain open questions:
// - Can the pins order for the strips be changed? (the pins can be defined arbitrarily,Tested on esp32s3, esp32duino version 3.2.0)
// - Are there some combination of pins that can be ommitted?
// - What other caveats are there?
//
// If you know the answer to these questions then please submit a PR to the FastLED repo and
// we will update the information for the community.
#include <esp_psram.h>
#define FASTLED_USES_ESP32S3_I2S
#define FASTLED_USES_ESP32S3_I2S // Must define this before including FastLED.h
#include "FastLED.h"
#include "fl/assert.h"
@ -66,6 +46,8 @@
#define NUM_LEDS_PER_STRIP 256
#define NUM_LEDS (NUM_LEDS_PER_STRIP * NUMSTRIPS)
// Note that you can use less strips than this.
#define EXAMPLE_PIN_NUM_DATA0 19 // B0
#define EXAMPLE_PIN_NUM_DATA1 45 // B1
#define EXAMPLE_PIN_NUM_DATA2 21 // B2
@ -84,8 +66,9 @@
#define EXAMPLE_PIN_NUM_DATA15 18 // R4
const bool gUseFastLEDApi = true; // Set this to false to use the raw driver.
// Users say you can use a lot less strips. Experiment around and find out!
// Please comment at reddit.com/r/fastled and let us know if you have problems.
// Or send us a picture of your Triumps!
int PINS[] = {
EXAMPLE_PIN_NUM_DATA0,
EXAMPLE_PIN_NUM_DATA1,
@ -105,10 +88,9 @@ int PINS[] = {
EXAMPLE_PIN_NUM_DATA15
};
fl::InternalI2SDriver *driver = nullptr;
CRGB leds[NUM_LEDS];
void setup_i2s_using_fastled_api() {
void setup_i2s() {
// Note, in this case we are using contingious memory for the leds. But this is not required.
// Each strip can be a different size and the FastLED api will upscale the smaller strips to the largest strip.
FastLED.addLeds<WS2812, EXAMPLE_PIN_NUM_DATA0, GRB>(
@ -159,15 +141,13 @@ void setup_i2s_using_fastled_api() {
FastLED.addLeds<WS2812, EXAMPLE_PIN_NUM_DATA15, GRB>(
leds + (15 * NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP
);
FastLED.setBrightness(32);
}
void setup() {
psramInit(); // IMPORTANT: This is required to enable PSRAM. If you don't do this, the driver will not work.
// put your setup code here, to run once:
Serial.begin(115200);
Serial.begin(57600);
// This is used so that you can see if PSRAM is enabled. If not, we will crash in setup() or in loop().
log_d("Total heap: %d", ESP.getHeapSize());
@ -175,15 +155,12 @@ void setup() {
log_d("Total PSRAM: %d", ESP.getPsramSize()); // If this prints out 0, then PSRAM is not enabled.
log_d("Free PSRAM: %d", ESP.getFreePsram());
log_d("waiting 6 second before startup");
log_d("waiting 6 seconds before startup");
delay(6000); // The long reset time here is to make it easier to flash the device during the development process.
if (gUseFastLEDApi) {
setup_i2s_using_fastled_api();
} else {
driver = fl::InternalI2SDriver::create();
driver->initled((uint8_t *)leds, PINS, NUMSTRIPS, NUM_LEDS_PER_STRIP); // Skips extra frame buffer copy.
driver->setBrightness(32);
}
setup_i2s();
FastLED.setBrightness(32);
}
void fill_rainbow(CRGB* all_leds) {
@ -199,10 +176,6 @@ void fill_rainbow(CRGB* all_leds) {
void loop() {
fill_rainbow(leds);
if (gUseFastLEDApi) {
FastLED.show();
} else {
FASTLED_ASSERT(driver != nullptr, "Did not expect driver to be null");
driver->show();
}
FastLED.show();
}

View file

@ -1 +1,11 @@
#include "old.h"
#include "FastLED.h"
#if !SKETCH_HAS_LOTS_OF_MEMORY
// Platform does not have enough memory
void setup() {}
void loop() {}
#else
#include "curr.h"
#endif

View file

@ -1,112 +1,64 @@
/*
Festival Stick is a dense corkscrew of LEDs that is wrapped around one end of
a wooden walking stick commonly found on amazon.A0
Basic cork screw test.
The UI screenmap projects this cork screw into polar coordinates, so that the
LEDs are mapped to a sprial, with the inner portion of the spiral being the top,
the outer most portion being the bottom.
This test is forward mapping, in which we test that
the corkscrew is mapped to cylinder cartesian coordinates.
Most of the time, you'll want the reverse mapping, that is
drawing to a rectangular grid, and then mapping that to a corkscrew.
However, to make sure the above mapping works correctly, we have
to test that the forward mapping works correctly first.
*/
#include "fl/assert.h"
#include "fl/corkscrew.h"
#include "fl/screenmap.h"
#include "fl/warn.h"
#include "fl/sstream.h"
#include "fl/grid.h"
#include "fl/leds.h"
#include "fl/screenmap.h"
#include "fl/sstream.h"
#include "fl/warn.h"
#include "noise.h"
#include <FastLED.h>
// #include "vec3.h"
using namespace fl;
// Power management settings
#define VOLTS 5
#define MAX_AMPS 1
#define PIN_DATA 9
#define PIN_CLOCK 7
#define PIN_DATA 3
#define PIN_CLOCK 4
// Pin could have been tied to ground, instead it's tied to another pin.
#define PIN_BUTTON 1
#define PIN_GRND 2
#define NUM_LEDS 288
#define CORKSCREW_TOTAL_LENGTH 100 // 100 cm
#define CORKSCREW_TOTAL_HEIGHT \
23.25f // Total height of the corkscrew in centimeters for 144 densly
// wrapped up over 19 turns
#define CORKSCREW_TURNS 19 // Default to 19 turns
23.25 // when height = 0, it's a circle.
// wrapped up over 19 turns
#define CORKSCREW_TURNS 20.5 // Default to 19 turns
// #define CM_BETWEEN_LEDS 1.0 // 1cm between LEDs
// #define CM_LED_DIAMETER 0.5 // 0.5cm LED diameter
#define CORKSCREW_WIDTH 16
#define CORKSCREW_HEIGHT 19
UITitle festivalStickTitle("Festival Stick");
UITitle festivalStickTitle("Corkscrew");
UIDescription festivalStickDescription(
"Take a wooden walking stick, wrap dense LEDs around it like a corkscrew. "
"Super simple but very awesome looking."
"This assumes the dense 144 LEDs / meter.");
"Tests the ability to map a cork screw onto a 2D cylindrical surface");
UISlider ledsScale("Leds scale", 0.1f, 0.1f, 1.0f, 0.01f);
UIButton button("Button");
UISlider speed("Speed", 0.1f, 0.01f, 1.0f, 0.01f);
CRGB leds[NUM_LEDS];
UICheckbox allWhite("All White", false);
UICheckbox splatRendering("Splat Rendering", true);
// CRGB leds[NUM_LEDS];
// Tested on a 288 led (2x 144 max density led strip) with 19 turns
// with 23.25cm height, 19 turns, and ~15.5 LEDs per turn.
Corkscrew::Input
corkscrewInput(CORKSCREW_TOTAL_HEIGHT,
CORKSCREW_TURNS * 2.0f * PI, // Default to 19 turns
0, // offset to account for gaps between segments
NUM_LEDS, // Default to dense 144 leds.
);
Corkscrew::Input corkscrewInput(CORKSCREW_TOTAL_LENGTH, CORKSCREW_TOTAL_HEIGHT,
CORKSCREW_TURNS, NUM_LEDS, 0);
// Corkscrew::Output corkscrewMap = fl::Corkscrew::generateMap(corkscrewInput);
// Corkscrew::State corkscrewMap = fl::Corkscrew::generateMap(corkscrewInput);
Corkscrew corkscrew(corkscrewInput);
// Used only for the fl::ScreenMap generation.
struct corkscrew_args {
int num_leds = NUM_LEDS;
float leds_per_turn = 15.5;
float width_cm = 1.0;
};
fl::ScreenMap makeScreenMap(corkscrew_args args = corkscrew_args()) {
// Create a ScreenMap for the corkscrew
fl::vector<vec2f> points(args.num_leds);
int num_leds = args.num_leds;
float leds_per_turn = args.leds_per_turn;
float width_cm = args.width_cm;
const float circumference = leds_per_turn;
const float radius = circumference / (2.0 * PI); // radius in mm
const float angle_per_led = 2.0 * PI / leds_per_turn; // degrees per LED
const float height_per_turn_cm = width_cm; // 10cm height per turn
const float height_per_led = height_per_turn_cm / leds_per_turn *
1.3; // this is the changing height per led.
for (int i = 0; i < num_leds; i++) {
float angle = i * angle_per_led; // angle in radians
float r = radius + 10 + i * height_per_led; // height in cm
// Calculate the x, y coordinates for the corkscrew
float x = r * cos(angle); // x coordinate
float y = r * sin(angle); // y coordinate
// Store the 2D coordinates in the vector
points[i] = vec2f(x, y);
}
FASTLED_WARN("Creating ScreenMap with:\n" << points);
// Create a ScreenMap from the points
fl::ScreenMap screenMap(points.data(), num_leds, .5);
return screenMap;
}
// Create a corkscrew with:
// - 30cm total length (300mm)
// - 5cm width (50mm)
@ -117,96 +69,77 @@ fl::ScreenMap makeScreenMap(corkscrew_args args = corkscrew_args()) {
// fl::vector<vec3f> mapCorkScrew = makeCorkScrew(args);
fl::ScreenMap screenMap;
fl::Grid<CRGB> frameBuffer;
CLEDController *addController() {
CLEDController *controller =
&FastLED.addLeds<APA102HD, PIN_DATA, PIN_CLOCK, BGR>(leds, NUM_LEDS);
return controller;
}
void setup() {
pinMode(PIN_GRND, OUTPUT);
digitalWrite(PIN_GRND, LOW); // Set ground pin to low
button.addRealButton(Button(PIN_BUTTON));
corkscrew_args args = corkscrew_args();
screenMap = makeScreenMap(args);
// screenMap = ScreenMap::Circle(NUM_LEDS, 1.5f, 0.5f, 1.0f);
auto controller = addController();
int width = corkscrew.cylinder_width();
int height = corkscrew.cylinder_height();
frameBuffer.reset(width, height);
XYMap xyMap = XYMap::constructRectangularGrid(width, height, 0);
CRGB *leds = frameBuffer.data();
size_t num_leds = frameBuffer.size();
CLEDController* controller = &FastLED.addLeds<APA102HD, PIN_DATA, PIN_CLOCK, BGR>(leds, NUM_LEDS);
// CLEDController *controller =
// &FastLED.addLeds<WS2812, 3, BGR>(leds, num_leds);
fl::ScreenMap screenMap = xyMap.toScreenMap();
screenMap.setDiameter(.2f);
// Set the screen map for the controller
controller->setScreenMap(screenMap);
// Set power management. This allows this festival stick to conformatable
// run on any USB battery that can output at least 1A at 5V.
// Keep in mind that this sketch is designed to use APA102HD mode, which
// will result in even lowwer run power consumption, since the power mode
// does not take into account the APA102HD gamma correction. However it is
// still a correct upper bound that will match the ledset exactly when the
// display tries to go full white.
FastLED.setMaxPowerInVoltsAndMilliamps(VOLTS, MAX_AMPS * 1000);
button.onChanged([](UIButton &but) {
// This function is called when the button is pressed
// If the button is pressed, show the generative pattern
if (but.isPressed()) {
FASTLED_WARN("Button pressed");
} else {
FASTLED_WARN("NOT Button pressed");
}
});
}
void printOutput(const Corkscrew::Output& output) {
fl::sstream stream;
stream << "Corkscrew Output:\n";
stream << "Width: " << output.width << "\n";
stream << "Height: " << output.height << "\n";
// stream << "Mapping: \n";
// for (const auto &point : output.mapping) {
// stream << point << "\n";
// }
FASTLED_WARN(stream.str());
}
LedsXY<CORKSCREW_WIDTH, CORKSCREW_HEIGHT> frameBuffer;
void loop() {
uint32_t now = millis();
fl::clear(leds);
// fl::clear(lesdds);
fl::clear(frameBuffer);
static int w = 0;
static float pos = 0;
EVERY_N_MILLIS(300) {
// Update the corkscrew mapping every second
w = (w + 1) % CORKSCREW_WIDTH;
// Update the corkscrew mapping every second
// w = (w + 1) % CORKSCREW_WIDTH;
// frameBuffer.
pos += speed.value();
if (pos > corkscrew.size() - 1) {
pos = 0; // Reset to the beginning
}
// draw a blue line down the middle
for (int i = 0; i < CORKSCREW_HEIGHT; ++i) {
frameBuffer.at(w % CORKSCREW_WIDTH, i) = CRGB::Blue;
frameBuffer.at((w + 1) % CORKSCREW_WIDTH, i) = CRGB::Blue;
frameBuffer.at((w - 1 + CORKSCREW_WIDTH) % CORKSCREW_WIDTH, i) = CRGB::Blue;
frameBuffer.at((w + 2) % CORKSCREW_WIDTH, i) = CRGB::Blue;
frameBuffer.at((w - 2 + CORKSCREW_WIDTH) % CORKSCREW_WIDTH, i) = CRGB::Blue;
if (allWhite) {
for (size_t i = 0; i < frameBuffer.size(); ++i) {
frameBuffer.data()[i] = CRGB(8, 8, 8);
}
}
if (splatRendering) {
Tile2x2_u8_wrap pos_tile = corkscrew.at_wrap(pos);
const CRGB color = CRGB::Blue;
// Draw each pixel in the 2x2 tile using the new wrapping API
for (int dx = 0; dx < 2; ++dx) {
for (int dy = 0; dy < 2; ++dy) {
auto data = pos_tile.at(dx, dy);
vec2i16 wrapped_pos = data.first; // Already wrapped position
uint8_t alpha = data.second; // Alpha value
// printOutput(corkscrewMap);
for (int i = 0; i < NUM_LEDS; ++i) {
// Get the position in the frame buffer
vec2<int16_t> pos = corkscrew.at(i);
// Draw the tile to the frame buffer
CRGB c = frameBuffer.at(pos.x, pos.y);
leds[i] = c;
FASTLED_WARN_IF(i < 16, "LED " << i << " at position: "
<< pos.x << ", " << pos.y
<< " with color: " << c);
if (alpha > 0) { // Only draw if there's some alpha
CRGB c = color;
c.nscale8(alpha); // Scale the color by the alpha value
frameBuffer.at(wrapped_pos.x, wrapped_pos.y) = c;
}
}
}
} else {
// None splat rendering, looks aweful.
vec2f pos_vec2f = corkscrew.at_exact(pos);
vec2i16 pos_i16 = vec2i16(round(pos_vec2f.x), round(pos_vec2f.y));
// Now map the cork screw position to the cylindrical buffer that we
// will draw.
frameBuffer.at(pos_i16.x, pos_i16.y) =
CRGB::Blue; // Draw a blue pixel at (w, h)
}
FastLED.show();
}

View file

@ -40,11 +40,11 @@ UIDescription festivalStickDescription(
"Take a wooden walking stick, wrap dense LEDs around it like a corkscrew. Super simple but very awesome looking."
"This assumes the dense 144 LEDs / meter.");
UISlider ledsScale("Leds scale", 0.1f, 0.1f, 1.0f, 0.01f);
UIButton button("Button");
// Adding a brightness slider
UISlider brightness("Brightness", 16, 0, 255, 1); // Brightness from 0 to 255
CRGB leds[NUM_LEDS];
@ -166,6 +166,8 @@ void setup() {
// into account the APA102HD gamma correction. However it is still a correct upper bound
// that will match the ledset exactly when the display tries to go full white.
FastLED.setMaxPowerInVoltsAndMilliamps(VOLTS, MAX_AMPS * 1000);
// set brightness 8
FastLED.setBrightness(brightness.as_int());
button.onChanged([](UIButton& but) {
// This function is called when the button is pressed
// If the button is pressed, show the generative pattern

View file

@ -0,0 +1,214 @@
/*
This demo is best viewed using the FastLED compiler.
Windows/MacOS binaries: https://github.com/FastLED/FastLED/releases
Python
Install: pip install fastled
Run: fastled <this sketch directory>
This will compile and preview the sketch in the browser, and enable
all the UI elements you see below.
OVERVIEW:
This sketch creates a fire effect on a cylindrical LED display using Perlin noise.
Unlike a flat matrix, this cylinder connects the left and right edges (x=0 and x=width-1),
creating a seamless wrap-around effect. The fire appears to rise from the bottom,
with colors transitioning from black to red/yellow/white (or other palettes).
*/
// Perlin noise fire procedure
// 16x16 rgb led cylinder demo
// Exactly the same as the FireMatrix example, but with a cylinder, meaning that the x=0
// and x = len-1 are connected.
// This also showcases the inoise16(x,y,z,t) function which handles 3D+time noise effects.
// Keep in mind that a cylinder is embedded in a 3D space with time being used to add
// additional noise to the effect.
// HOW THE CYLINDRICAL FIRE EFFECT WORKS:
// 1. We use sine and cosine to map the x-coordinate to a circle in 3D space
// 2. This creates a cylindrical mapping where the left and right edges connect seamlessly
// 3. We use 4D Perlin noise (x,y,z,t) to generate natural-looking fire patterns
// 4. The height coordinate controls color fade-out to create the rising fire effect
// 5. The time dimension adds continuous variation to make the fire look dynamic
#include "FastLED.h" // Main FastLED library for controlling LEDs
#include "fl/ui.h" // UI components for the FastLED web compiler (sliders, buttons, etc.)
#include "fl/xymap.h" // Mapping between 1D LED array and 2D coordinates
#include "fx/time.h" // Time manipulation utilities for animations
using namespace fl; // Use the FastLED namespace for convenience
// Cylinder dimensions - this defines the size of our virtual LED grid
#define HEIGHT 100 // Number of rows in the cylinder (vertical dimension)
#define WIDTH 100 // Number of columns in the cylinder (circumference)
#define SERPENTINE true // Whether the LED strip zigzags back and forth (common in matrix layouts)
#define BRIGHTNESS 255 // Maximum brightness level (0-255)
// UI elements that appear in the FastLED web compiler interface:
UITitle title("FireCylinder Demo"); // Title displayed in the UI
UIDescription description("This Fire demo wraps around the cylinder. It uses Perlin noise to create a fire effect.");
// TimeWarp helps control animation speed - it tracks time and allows speed adjustments
TimeWarp timeScale(0, 1.0f); // Initialize with 0 starting time and 1.0 speed multiplier
// UI Controls for adjusting the fire effect:
UISlider scaleXY("Scale", 8, 1, 100, 1); // Controls the overall size of the fire pattern
UISlider speedY("SpeedY", 1.3, 1, 6, .1); // Controls how fast the fire moves upward
UISlider scaleX("ScaleX", .3, 0.1, 3, .01); // Controls the horizontal scale (affects the wrap-around)
UISlider invSpeedZ("Inverse SpeedZ", 20, 1, 100, 1); // Controls how fast the fire pattern changes over time (higher = slower)
UISlider brightness("Brightness", 255, 0, 255, 1); // Controls overall brightness
UINumberField palette("Palette", 0, 0, 2); // Selects which color palette to use (0=fire, 1=green, 2=blue)
// Array to hold all LED color values - one CRGB struct per LED
CRGB leds[HEIGHT * WIDTH];
// Color palettes define the gradient of colors used for the fire effect
// Each entry has the format: position (0-255), R, G, B
DEFINE_GRADIENT_PALETTE(firepal){
// Traditional fire palette - transitions from black to red to yellow to white
0, 0, 0, 0, // black (bottom of fire)
32, 255, 0, 0, // red (base of flames)
190, 255, 255, 0, // yellow (middle of flames)
255, 255, 255, 255 // white (hottest part/tips of flames)
};
DEFINE_GRADIENT_PALETTE(electricGreenFirePal){
// Green fire palette - for a toxic/alien look
0, 0, 0, 0, // black (bottom)
32, 0, 70, 0, // dark green (base)
190, 57, 255, 20, // electric neon green (middle)
255, 255, 255, 255 // white (hottest part)
};
DEFINE_GRADIENT_PALETTE(electricBlueFirePal){
// Blue fire palette - for a cold/ice fire look
0, 0, 0, 0, // Black (bottom)
32, 0, 0, 70, // Dark blue (base)
128, 20, 57, 255, // Electric blue (middle)
255, 255, 255, 255 // White (hottest part)
};
// Create a mapping between 1D array positions and 2D x,y coordinates
XYMap xyMap(WIDTH, HEIGHT, SERPENTINE);
void setup() {
Serial.begin(115200); // Initialize serial communication for debugging
// Initialize the LED strip:
// - NEOPIXEL is the LED type
// - 3 is the data pin number (for real hardware)
// - setScreenMap connects our 2D coordinate system to the 1D LED array
fl::ScreenMap screen_map = xyMap.toScreenMap();
screen_map.setDiameter(0.1f); // Set the diameter for the cylinder (0.2 cm per LED)
FastLED.addLeds<NEOPIXEL, 3>(leds, HEIGHT * WIDTH).setScreenMap(screen_map);
// Apply color correction for more accurate colors on LED strips
FastLED.setCorrection(TypicalLEDStrip);
}
uint8_t getPaletteIndex(uint32_t millis32, int width, int max_width, int height, int max_height,
uint32_t y_speed) {
// This function calculates which color to use from our palette for each LED
// Get the scale factor from the UI slider
uint16_t scale = scaleXY.as<uint16_t>();
// Convert width position to an angle (0-255 represents 0-360 degrees)
// This maps our flat coordinate to a position on a cylinder
float xf = (float)width / (float)max_width; // Normalized position (0.0 to 1.0)
uint8_t x = (uint8_t)(xf * 255); // Convert to 0-255 range for trig functions
// Calculate the sine and cosine of this angle to get 3D coordinates on the cylinder
uint32_t cosx = cos8(x); // cos8 returns a value 0-255 representing cosine
uint32_t sinx = sin8(x); // sin8 returns a value 0-255 representing sine
// Apply scaling to the sine/cosine values
// This controls how "wide" the noise pattern is around the cylinder
float trig_scale = scale * scaleX.value();
cosx *= trig_scale;
sinx *= trig_scale;
// Calculate Y coordinate (vertical position) with speed offset for movement
uint32_t y = height * scale + y_speed;
// Calculate Z coordinate (time dimension) - controls how the pattern changes over time
uint16_t z = millis32 / invSpeedZ.as<uint16_t>();
// Generate 16-bit Perlin noise using our 4D coordinates (x,y,z,t)
// The << 8 shifts values left by 8 bits (multiplies by 256) to use the full 16-bit range
// The last parameter (0) could be replaced with another time variable for more variation
uint16_t noise16 = inoise16(cosx << 8, sinx << 8, y << 8, 0);
// Convert 16-bit noise to 8-bit by taking the high byte
uint8_t noise_val = noise16 >> 8;
// Calculate how much to subtract based on vertical position (height)
// This creates the fade-out effect from bottom to top
// The formula maps height from 0 to max_height-1 to a value from 255 to 0
int8_t subtraction_factor = abs8(height - (max_height - 1)) * 255 /
(max_height - 1);
// Subtract the factor from the noise value (with underflow protection)
// qsub8 is a "saturating subtraction" - it won't go below 0
return qsub8(noise_val, subtraction_factor);
}
CRGBPalette16 getPalette() {
// This function returns the appropriate color palette based on the UI selection
switch (palette) {
case 0:
return firepal; // Traditional orange/red fire
case 1:
return electricGreenFirePal; // Green "toxic" fire
case 2:
return electricBlueFirePal; // Blue "cold" fire
default:
return firepal; // Default to traditional fire if invalid value
}
}
void loop() {
// The main program loop that runs continuously
// Set the overall brightness from the UI slider
FastLED.setBrightness(brightness);
// Get the selected color palette
CRGBPalette16 myPal = getPalette();
// Get the current time in milliseconds
uint32_t now = millis();
// Update the animation speed from the UI slider
timeScale.setSpeed(speedY);
// Calculate the current y-offset for animation (makes the fire move)
uint32_t y_speed = timeScale.update(now);
// Loop through every LED in our cylindrical matrix
for (int width = 0; width < WIDTH; width++) {
for (int height = 0; height < HEIGHT; height++) {
// Calculate which color to use from our palette for this LED
// This function handles the cylindrical mapping using sine/cosine
uint8_t palette_index =
getPaletteIndex(now, width, WIDTH, height, HEIGHT, y_speed);
// Get the actual RGB color from the palette
// BRIGHTNESS ensures we use the full brightness range
CRGB c = ColorFromPalette(myPal, palette_index, BRIGHTNESS);
// Convert our 2D coordinates to the 1D array index
// We use (WIDTH-1)-width and (HEIGHT-1)-height to flip the coordinates
// This makes the fire appear to rise from the bottom
int index = xyMap((WIDTH - 1) - width, (HEIGHT - 1) - height);
// Set the LED color in our array
leds[index] = c;
}
}
// Send the color data to the actual LEDs
FastLED.show();
}

View file

@ -1,207 +1,9 @@
/*
This demo is best viewed using the FastLED compiler.
Install: pip install fastled
Run: fastled <this sketch directory>
This will compile and preview the sketch in the browser, and enable
all the UI elements you see below.
OVERVIEW:
This sketch creates a fire effect on a cylindrical LED display using Perlin noise.
Unlike a flat matrix, this cylinder connects the left and right edges (x=0 and x=width-1),
creating a seamless wrap-around effect. The fire appears to rise from the bottom,
with colors transitioning from black to red/yellow/white (or other palettes).
*/
// Perlin noise fire procedure
// 16x16 rgb led cylinder demo
// Exactly the same as the FireMatrix example, but with a cylinder, meaning that the x=0
// and x = len-1 are connected.
// This also showcases the inoise16(x,y,z,t) function which handles 3D+time noise effects.
// Keep in mind that a cylinder is embedded in a 3D space with time being used to add
// additional noise to the effect.
// HOW THE CYLINDRICAL FIRE EFFECT WORKS:
// 1. We use sine and cosine to map the x-coordinate to a circle in 3D space
// 2. This creates a cylindrical mapping where the left and right edges connect seamlessly
// 3. We use 4D Perlin noise (x,y,z,t) to generate natural-looking fire patterns
// 4. The height coordinate controls color fade-out to create the rising fire effect
// 5. The time dimension adds continuous variation to make the fire look dynamic
#include "FastLED.h" // Main FastLED library for controlling LEDs
#include "fl/ui.h" // UI components for the FastLED web compiler (sliders, buttons, etc.)
#include "fl/xymap.h" // Mapping between 1D LED array and 2D coordinates
#include "fx/time.h" // Time manipulation utilities for animations
using namespace fl; // Use the FastLED namespace for convenience
// Cylinder dimensions - this defines the size of our virtual LED grid
#define HEIGHT 100 // Number of rows in the cylinder (vertical dimension)
#define WIDTH 100 // Number of columns in the cylinder (circumference)
#define SERPENTINE true // Whether the LED strip zigzags back and forth (common in matrix layouts)
#define BRIGHTNESS 255 // Maximum brightness level (0-255)
// UI elements that appear in the FastLED web compiler interface:
UITitle title("FireCylinder Demo"); // Title displayed in the UI
UIDescription description("This Fire demo wraps around the cylinder. It uses Perlin noise to create a fire effect.");
// TimeWarp helps control animation speed - it tracks time and allows speed adjustments
TimeWarp timeScale(0, 1.0f); // Initialize with 0 starting time and 1.0 speed multiplier
// UI Controls for adjusting the fire effect:
UISlider scaleXY("Scale", 8, 1, 100, 1); // Controls the overall size of the fire pattern
UISlider speedY("SpeedY", 1.3, 1, 6, .1); // Controls how fast the fire moves upward
UISlider scaleX("ScaleX", .3, 0.1, 3, .01); // Controls the horizontal scale (affects the wrap-around)
UISlider invSpeedZ("Inverse SpeedZ", 20, 1, 100, 1); // Controls how fast the fire pattern changes over time (higher = slower)
UISlider brightness("Brightness", 255, 0, 255, 1); // Controls overall brightness
UINumberField palette("Palette", 0, 0, 2); // Selects which color palette to use (0=fire, 1=green, 2=blue)
// Array to hold all LED color values - one CRGB struct per LED
CRGB leds[HEIGHT * WIDTH];
// Color palettes define the gradient of colors used for the fire effect
// Each entry has the format: position (0-255), R, G, B
DEFINE_GRADIENT_PALETTE(firepal){
// Traditional fire palette - transitions from black to red to yellow to white
0, 0, 0, 0, // black (bottom of fire)
32, 255, 0, 0, // red (base of flames)
190, 255, 255, 0, // yellow (middle of flames)
255, 255, 255, 255 // white (hottest part/tips of flames)
};
DEFINE_GRADIENT_PALETTE(electricGreenFirePal){
// Green fire palette - for a toxic/alien look
0, 0, 0, 0, // black (bottom)
32, 0, 70, 0, // dark green (base)
190, 57, 255, 20, // electric neon green (middle)
255, 255, 255, 255 // white (hottest part)
};
DEFINE_GRADIENT_PALETTE(electricBlueFirePal){
// Blue fire palette - for a cold/ice fire look
0, 0, 0, 0, // Black (bottom)
32, 0, 0, 70, // Dark blue (base)
128, 20, 57, 255, // Electric blue (middle)
255, 255, 255, 255 // White (hottest part)
};
// Create a mapping between 1D array positions and 2D x,y coordinates
XYMap xyMap(HEIGHT, WIDTH, SERPENTINE);
void setup() {
Serial.begin(115200); // Initialize serial communication for debugging
// Initialize the LED strip:
// - NEOPIXEL is the LED type
// - 3 is the data pin number (for real hardware)
// - setScreenMap connects our 2D coordinate system to the 1D LED array
FastLED.addLeds<NEOPIXEL, 3>(leds, HEIGHT * WIDTH).setScreenMap(xyMap);
// Apply color correction for more accurate colors on LED strips
FastLED.setCorrection(TypicalLEDStrip);
}
uint8_t getPaletteIndex(uint32_t millis32, int width, int max_width, int height, int max_height,
uint32_t y_speed) {
// This function calculates which color to use from our palette for each LED
// Get the scale factor from the UI slider
uint16_t scale = scaleXY.as<uint16_t>();
// Convert width position to an angle (0-255 represents 0-360 degrees)
// This maps our flat coordinate to a position on a cylinder
float xf = (float)width / (float)max_width; // Normalized position (0.0 to 1.0)
uint8_t x = (uint8_t)(xf * 255); // Convert to 0-255 range for trig functions
// Calculate the sine and cosine of this angle to get 3D coordinates on the cylinder
uint32_t cosx = cos8(x); // cos8 returns a value 0-255 representing cosine
uint32_t sinx = sin8(x); // sin8 returns a value 0-255 representing sine
// Apply scaling to the sine/cosine values
// This controls how "wide" the noise pattern is around the cylinder
float trig_scale = scale * scaleX.value();
cosx *= trig_scale;
sinx *= trig_scale;
// Calculate Y coordinate (vertical position) with speed offset for movement
uint32_t y = height * scale + y_speed;
// Calculate Z coordinate (time dimension) - controls how the pattern changes over time
uint16_t z = millis32 / invSpeedZ.as<uint16_t>();
// Generate 16-bit Perlin noise using our 4D coordinates (x,y,z,t)
// The << 8 shifts values left by 8 bits (multiplies by 256) to use the full 16-bit range
// The last parameter (0) could be replaced with another time variable for more variation
uint16_t noise16 = inoise16(cosx << 8, sinx << 8, y << 8, 0);
// Convert 16-bit noise to 8-bit by taking the high byte
uint8_t noise_val = noise16 >> 8;
// Calculate how much to subtract based on vertical position (height)
// This creates the fade-out effect from bottom to top
// The formula maps height from 0 to max_height-1 to a value from 255 to 0
int8_t subtraction_factor = abs8(height - (max_height - 1)) * 255 /
(max_height - 1);
// Subtract the factor from the noise value (with underflow protection)
// qsub8 is a "saturating subtraction" - it won't go below 0
return qsub8(noise_val, subtraction_factor);
}
CRGBPalette16 getPalette() {
// This function returns the appropriate color palette based on the UI selection
switch (palette) {
case 0:
return firepal; // Traditional orange/red fire
case 1:
return electricGreenFirePal; // Green "toxic" fire
case 2:
return electricBlueFirePal; // Blue "cold" fire
default:
return firepal; // Default to traditional fire if invalid value
}
}
void loop() {
// The main program loop that runs continuously
// Set the overall brightness from the UI slider
FastLED.setBrightness(brightness);
// Get the selected color palette
CRGBPalette16 myPal = getPalette();
// Get the current time in milliseconds
uint32_t now = millis();
// Update the animation speed from the UI slider
timeScale.setSpeed(speedY);
// Calculate the current y-offset for animation (makes the fire move)
uint32_t y_speed = timeScale.update(now);
// Loop through every LED in our cylindrical matrix
for (int width = 0; width < WIDTH; width++) {
for (int height = 0; height < HEIGHT; height++) {
// Calculate which color to use from our palette for this LED
// This function handles the cylindrical mapping using sine/cosine
uint8_t palette_index =
getPaletteIndex(now, width, WIDTH, height, HEIGHT, y_speed);
// Get the actual RGB color from the palette
// BRIGHTNESS ensures we use the full brightness range
CRGB c = ColorFromPalette(myPal, palette_index, BRIGHTNESS);
// Convert our 2D coordinates to the 1D array index
// We use (WIDTH-1)-width and (HEIGHT-1)-height to flip the coordinates
// This makes the fire appear to rise from the bottom
int index = xyMap((WIDTH - 1) - width, (HEIGHT - 1) - height);
// Set the LED color in our array
leds[index] = c;
}
}
// Send the color data to the actual LEDs
FastLED.show();
}
#if !SKETCH_HAS_LOTS_OF_MEMORY
// Platform does not have enough memory
void setup() {}
void loop() {}
#else
#include "FireCylinder.h"
#endif

View file

@ -0,0 +1,200 @@
/*
This demo is best viewed using the FastLED compiler.
Windows/MacOS binaries: https://github.com/FastLED/FastLED/releases
Python
Install: pip install fastled
Run: fastled <this sketch directory>
This will compile and preview the sketch in the browser, and enable
all the UI elements you see below.
OVERVIEW:
This sketch creates a fire effect using Perlin noise on a matrix of LEDs.
The fire appears to move upward with colors transitioning from black at the bottom
to white at the top, with red and yellow in between (for the default palette).
*/
// Perlin noise fire procedure
// 16x16 rgb led matrix demo
// Yaroslaw Turbin, 22.06.2020
// https://vk.com/ldirko
// https://www.reddit.com/user/ldirko/
// https://www.reddit.com/r/FastLED/comments/hgu16i/my_fire_effect_implementation_based_on_perlin/
// Based on the code found at: https://editor.soulmatelights.com/gallery/1229-
// HOW THE FIRE EFFECT WORKS:
// 1. We use Perlin noise with time offset for X and Z coordinates
// to create a naturally scrolling fire pattern that changes over time
// 2. We distort the fire noise to make it look more realistic
// 3. We subtract a value based on Y coordinate to shift the fire color in the palette
// (not just brightness). This creates a fade-out effect from the bottom of the matrix to the top
// 4. The palette is carefully designed to give realistic fire colors
#if defined(__AVR__)
// Platform does not have enough memory
void setup() {}
void loop() {}
#else
#include "FastLED.h" // Main FastLED library for controlling LEDs
#include "fl/ui.h" // UI components for the FastLED web compiler (sliders, etc.)
#include "fl/xymap.h" // Mapping between 1D LED array and 2D coordinates
#include "fx/time.h" // Time manipulation utilities
using namespace fl; // Use the FastLED namespace for convenience
// Matrix dimensions - this defines the size of our virtual LED grid
#define HEIGHT 100 // Number of rows in the matrix
#define WIDTH 100 // Number of columns in the matrix
#define SERPENTINE true // Whether the LED strip zigzags back and forth (common in matrix layouts)
#define BRIGHTNESS 255 // Maximum brightness level (0-255)
// TimeWarp helps control animation speed - it tracks time and allows speed adjustments
TimeWarp timeScale(0, 1.0f); // Initialize with 0 starting time and 1.0 speed multiplier
// UI Controls that appear in the FastLED web compiler interface:
UISlider scaleXY("Scale", 20, 1, 100, 1); // Controls the size of the fire pattern
UISlider speedY("SpeedY", 1, 1, 6, .1); // Controls how fast the fire moves upward
UISlider invSpeedZ("Inverse SpeedZ", 20, 1, 100, 1); // Controls how fast the fire pattern changes over time (higher = slower)
UISlider brightness("Brightness", 255, 0, 255, 1); // Controls overall brightness
UINumberField palette("Palette", 0, 0, 2); // Selects which color palette to use (0=fire, 1=green, 2=blue)
// Array to hold all LED color values - one CRGB struct per LED
CRGB leds[HEIGHT * WIDTH];
// Color palettes define the gradient of colors used for the fire effect
// Each entry has the format: position (0-255), R, G, B
DEFINE_GRADIENT_PALETTE(firepal){
// Traditional fire palette - transitions from black to red to yellow to white
0, 0, 0, 0, // black (bottom of fire)
32, 255, 0, 0, // red (base of flames)
190, 255, 255, 0, // yellow (middle of flames)
255, 255, 255, 255 // white (hottest part/tips of flames)
};
DEFINE_GRADIENT_PALETTE(electricGreenFirePal){
// Green fire palette - for a toxic/alien look
0, 0, 0, 0, // black (bottom)
32, 0, 70, 0, // dark green (base)
190, 57, 255, 20, // electric neon green (middle)
255, 255, 255, 255 // white (hottest part)
};
DEFINE_GRADIENT_PALETTE(electricBlueFirePal) {
// Blue fire palette - for a cold/ice fire look
0, 0, 0, 0, // Black (bottom)
32, 0, 0, 70, // Dark blue (base)
128, 20, 57, 255, // Electric blue (middle)
255, 255, 255, 255 // White (hottest part)
};
// Create a mapping between 1D array positions and 2D x,y coordinates
XYMap xyMap(WIDTH, HEIGHT, SERPENTINE);
void setup() {
Serial.begin(115200); // Initialize serial communication for debugging
// Initialize the LED strip:
// - NEOPIXEL is the LED type
// - 3 is the data pin number (for real hardware)
// - setScreenMap connects our 2D coordinate system to the 1D LED array
fl::ScreenMap screen_map = xyMap.toScreenMap();
screen_map.setDiameter(0.1f); // Set the diameter for the cylinder (0.2 cm per LED)
FastLED.addLeds<NEOPIXEL, 3>(leds, HEIGHT * WIDTH).setScreenMap(screen_map);
// Apply color correction for more accurate colors on LED strips
FastLED.setCorrection(TypicalLEDStrip);
}
uint8_t getPaletteIndex(uint32_t millis32, int i, int j, uint32_t y_speed) {
// This function calculates which color to use from our palette for each LED
// Get the scale factor from the UI slider (controls the "size" of the fire)
uint16_t scale = scaleXY.as<uint16_t>();
// Calculate 3D coordinates for the Perlin noise function:
uint16_t x = i * scale; // X position (horizontal in matrix)
uint32_t y = j * scale + y_speed; // Y position (vertical) + movement offset
uint16_t z = millis32 / invSpeedZ.as<uint16_t>(); // Z position (time dimension)
// Generate 16-bit Perlin noise value using these coordinates
// The << 8 shifts values left by 8 bits (multiplies by 256) to use the full 16-bit range
uint16_t noise16 = inoise16(x << 8, y << 8, z << 8);
// Convert 16-bit noise to 8-bit by taking the high byte (>> 8 shifts right by 8 bits)
uint8_t noise_val = noise16 >> 8;
// Calculate how much to subtract based on vertical position (j)
// This creates the fade-out effect from bottom to top
// abs8() ensures we get a positive value
// The formula maps j from 0 to HEIGHT-1 to a value from 255 to 0
int8_t subtraction_factor = abs8(j - (HEIGHT - 1)) * 255 / (HEIGHT - 1);
// Subtract the factor from the noise value (with underflow protection)
// qsub8 is a "saturating subtraction" - it won't go below 0
return qsub8(noise_val, subtraction_factor);
}
CRGBPalette16 getPalette() {
// This function returns the appropriate color palette based on the UI selection
switch (palette) {
case 0:
return firepal; // Traditional orange/red fire
case 1:
return electricGreenFirePal; // Green "toxic" fire
case 2:
return electricBlueFirePal; // Blue "cold" fire
default:
return firepal; // Default to traditional fire if invalid value
}
}
void loop() {
// The main program loop that runs continuously
// Set the overall brightness from the UI slider
FastLED.setBrightness(brightness);
// Get the selected color palette
CRGBPalette16 myPal = getPalette();
// Get the current time in milliseconds
uint32_t now = millis();
// Update the animation speed from the UI slider
timeScale.setSpeed(speedY);
// Calculate the current y-offset for animation (makes the fire move)
uint32_t y_speed = timeScale.update(now);
// Loop through every LED in our matrix
for (int i = 0; i < WIDTH; i++) {
for (int j = 0; j < HEIGHT; j++) {
// Calculate which color to use from our palette for this LED
uint8_t palette_index = getPaletteIndex(now, i, j, y_speed);
// Get the actual RGB color from the palette
// BRIGHTNESS ensures we use the full brightness range
CRGB c = ColorFromPalette(myPal, palette_index, BRIGHTNESS);
// Convert our 2D coordinates (i,j) to the 1D array index
// We use (WIDTH-1)-i and (HEIGHT-1)-j to flip the coordinates
// This makes the fire appear to rise from the bottom
int index = xyMap((WIDTH - 1) - i, (HEIGHT - 1) - j);
// Set the LED color in our array
leds[index] = c;
}
}
// Send the color data to the actual LEDs
FastLED.show();
}
#endif

View file

@ -1,184 +1,9 @@
/*
This demo is best viewed using the FastLED compiler.
Install: pip install fastled
Run: fastled <this sketch directory>
This will compile and preview the sketch in the browser, and enable
all the UI elements you see below.
OVERVIEW:
This sketch creates a fire effect using Perlin noise on a matrix of LEDs.
The fire appears to move upward with colors transitioning from black at the bottom
to white at the top, with red and yellow in between (for the default palette).
*/
// Perlin noise fire procedure
// 16x16 rgb led matrix demo
// Yaroslaw Turbin, 22.06.2020
// https://vk.com/ldirko
// https://www.reddit.com/user/ldirko/
// https://www.reddit.com/r/FastLED/comments/hgu16i/my_fire_effect_implementation_based_on_perlin/
// Based on the code found at: https://editor.soulmatelights.com/gallery/1229-
// HOW THE FIRE EFFECT WORKS:
// 1. We use Perlin noise with time offset for X and Z coordinates
// to create a naturally scrolling fire pattern that changes over time
// 2. We distort the fire noise to make it look more realistic
// 3. We subtract a value based on Y coordinate to shift the fire color in the palette
// (not just brightness). This creates a fade-out effect from the bottom of the matrix to the top
// 4. The palette is carefully designed to give realistic fire colors
#include "FastLED.h" // Main FastLED library for controlling LEDs
#include "fl/ui.h" // UI components for the FastLED web compiler (sliders, etc.)
#include "fl/xymap.h" // Mapping between 1D LED array and 2D coordinates
#include "fx/time.h" // Time manipulation utilities
using namespace fl; // Use the FastLED namespace for convenience
// Matrix dimensions - this defines the size of our virtual LED grid
#define HEIGHT 100 // Number of rows in the matrix
#define WIDTH 100 // Number of columns in the matrix
#define SERPENTINE true // Whether the LED strip zigzags back and forth (common in matrix layouts)
#define BRIGHTNESS 255 // Maximum brightness level (0-255)
// TimeWarp helps control animation speed - it tracks time and allows speed adjustments
TimeWarp timeScale(0, 1.0f); // Initialize with 0 starting time and 1.0 speed multiplier
// UI Controls that appear in the FastLED web compiler interface:
UISlider scaleXY("Scale", 20, 1, 100, 1); // Controls the size of the fire pattern
UISlider speedY("SpeedY", 1, 1, 6, .1); // Controls how fast the fire moves upward
UISlider invSpeedZ("Inverse SpeedZ", 20, 1, 100, 1); // Controls how fast the fire pattern changes over time (higher = slower)
UISlider brightness("Brightness", 255, 0, 255, 1); // Controls overall brightness
UINumberField palette("Palette", 0, 0, 2); // Selects which color palette to use (0=fire, 1=green, 2=blue)
// Array to hold all LED color values - one CRGB struct per LED
CRGB leds[HEIGHT * WIDTH];
// Color palettes define the gradient of colors used for the fire effect
// Each entry has the format: position (0-255), R, G, B
DEFINE_GRADIENT_PALETTE(firepal){
// Traditional fire palette - transitions from black to red to yellow to white
0, 0, 0, 0, // black (bottom of fire)
32, 255, 0, 0, // red (base of flames)
190, 255, 255, 0, // yellow (middle of flames)
255, 255, 255, 255 // white (hottest part/tips of flames)
};
DEFINE_GRADIENT_PALETTE(electricGreenFirePal){
// Green fire palette - for a toxic/alien look
0, 0, 0, 0, // black (bottom)
32, 0, 70, 0, // dark green (base)
190, 57, 255, 20, // electric neon green (middle)
255, 255, 255, 255 // white (hottest part)
};
DEFINE_GRADIENT_PALETTE(electricBlueFirePal) {
// Blue fire palette - for a cold/ice fire look
0, 0, 0, 0, // Black (bottom)
32, 0, 0, 70, // Dark blue (base)
128, 20, 57, 255, // Electric blue (middle)
255, 255, 255, 255 // White (hottest part)
};
// Create a mapping between 1D array positions and 2D x,y coordinates
XYMap xyMap(HEIGHT, WIDTH, SERPENTINE);
void setup() {
Serial.begin(115200); // Initialize serial communication for debugging
// Initialize the LED strip:
// - NEOPIXEL is the LED type
// - 3 is the data pin number (for real hardware)
// - setScreenMap connects our 2D coordinate system to the 1D LED array
FastLED.addLeds<NEOPIXEL, 3>(leds, HEIGHT * WIDTH).setScreenMap(xyMap);
// Apply color correction for more accurate colors on LED strips
FastLED.setCorrection(TypicalLEDStrip);
}
uint8_t getPaletteIndex(uint32_t millis32, int i, int j, uint32_t y_speed) {
// This function calculates which color to use from our palette for each LED
// Get the scale factor from the UI slider (controls the "size" of the fire)
uint16_t scale = scaleXY.as<uint16_t>();
// Calculate 3D coordinates for the Perlin noise function:
uint16_t x = i * scale; // X position (horizontal in matrix)
uint32_t y = j * scale + y_speed; // Y position (vertical) + movement offset
uint16_t z = millis32 / invSpeedZ.as<uint16_t>(); // Z position (time dimension)
// Generate 16-bit Perlin noise value using these coordinates
// The << 8 shifts values left by 8 bits (multiplies by 256) to use the full 16-bit range
uint16_t noise16 = inoise16(x << 8, y << 8, z << 8);
// Convert 16-bit noise to 8-bit by taking the high byte (>> 8 shifts right by 8 bits)
uint8_t noise_val = noise16 >> 8;
// Calculate how much to subtract based on vertical position (j)
// This creates the fade-out effect from bottom to top
// abs8() ensures we get a positive value
// The formula maps j from 0 to WIDTH-1 to a value from 255 to 0
int8_t subtraction_factor = abs8(j - (WIDTH - 1)) * 255 / (WIDTH - 1);
// Subtract the factor from the noise value (with underflow protection)
// qsub8 is a "saturating subtraction" - it won't go below 0
return qsub8(noise_val, subtraction_factor);
}
CRGBPalette16 getPalette() {
// This function returns the appropriate color palette based on the UI selection
switch (palette) {
case 0:
return firepal; // Traditional orange/red fire
case 1:
return electricGreenFirePal; // Green "toxic" fire
case 2:
return electricBlueFirePal; // Blue "cold" fire
default:
return firepal; // Default to traditional fire if invalid value
}
}
void loop() {
// The main program loop that runs continuously
// Set the overall brightness from the UI slider
FastLED.setBrightness(brightness);
// Get the selected color palette
CRGBPalette16 myPal = getPalette();
// Get the current time in milliseconds
uint32_t now = millis();
// Update the animation speed from the UI slider
timeScale.setSpeed(speedY);
// Calculate the current y-offset for animation (makes the fire move)
uint32_t y_speed = timeScale.update(now);
// Loop through every LED in our matrix
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j < WIDTH; j++) {
// Calculate which color to use from our palette for this LED
uint8_t palette_index = getPaletteIndex(now, i, j, y_speed);
// Get the actual RGB color from the palette
// BRIGHTNESS ensures we use the full brightness range
CRGB c = ColorFromPalette(myPal, palette_index, BRIGHTNESS);
// Convert our 2D coordinates (i,j) to the 1D array index
// We use (HEIGHT-1)-i and (WIDTH-1)-j to flip the coordinates
// This makes the fire appear to rise from the bottom
int index = xyMap((HEIGHT - 1) - i, (WIDTH - 1) - j);
// Set the LED color in our array
leds[index] = c;
}
}
// Send the color data to the actual LEDs
FastLED.show();
}
#if !SKETCH_HAS_LOTS_OF_MEMORY
// Platform does not have enough memory
void setup() {}
void loop() {}
#else
#include "FireMatrix.h"
#endif

View file

@ -8,8 +8,9 @@
/// 3. Run the FastLED web compiler at root: `fastled`
/// 4. When the compiler is done a web page will open.
#include "FastLED.h"
#ifdef __AVR__
#if !SKETCH_HAS_LOTS_OF_MEMORY
void setup() {
// put your setup code here, to run once:
}

View file

@ -2,6 +2,11 @@
/*
This demo is best viewed using the FastLED compiler.
Windows/MacOS binaries: https://github.com/FastLED/FastLED/releases
Python
Install: pip install fastled
Run: fastled <this sketch directory>
This will compile and preview the sketch in the browser, and enable

View file

@ -1,11 +1,11 @@
#pragma once
/*
This demo is best viewed using the FastLED compiler.
Windows/MacOS binaries: https://github.com/FastLED/FastLED/releases
Python
Install: pip install fastled
Run: fastled <this sketch directory>
This will compile and preview the sketch in the browser, and enable
@ -40,6 +40,7 @@ CRGB leds[NUM_LEDS];
UITitle title("FxWave2D Demo");
UIDescription description("Advanced layered and blended wave effects.");
UICheckbox xCyclical("X Is Cyclical", false); // If true, waves wrap around the x-axis (like a loop)
// Main control UI elements:
UIButton button("Trigger"); // Button to trigger a single ripple
UIButton buttonFancy("Trigger Fancy"); // Button to trigger a fancy cross-shaped effect
@ -50,7 +51,7 @@ UISlider blurAmount("Global Blur Amount", 0, 0, 172, 1); // Controls overall
UISlider blurPasses("Global Blur Passes", 1, 1, 10, 1); // Controls how many times blur is applied (more = smoother but slower)
UISlider superSample("SuperSampleExponent", 1.f, 0.f, 3.f, 1.f); // Controls anti-aliasing quality (higher = better quality but more CPU)
UICheckbox xCyclical("X Is Cyclical", false); // If true, waves wrap around the x-axis (like a loop)
// Upper wave layer controls:
UISlider speedUpper("Wave Upper: Speed", 0.12f, 0.0f, 1.0f); // How fast the upper wave propagates

View file

@ -5,6 +5,11 @@
/*
This demo is best viewed using the FastLED compiler.
Windows/MacOS binaries: https://github.com/FastLED/FastLED/releases
Python
Install: pip install fastled
Run: fastled <this sketch directory>
This will compile and preview the sketch in the browser, and enable

View file

@ -0,0 +1,83 @@
/// @file NoisePlayground.ino
/// @brief Demonstrates how to use noise generation on a 2D LED matrix
/// @example NoisePlayground.ino
#include <FastLED.h>
// Params for width and height
const uint8_t kMatrixWidth = 16;
const uint8_t kMatrixHeight = 16;
#define NUM_LEDS (kMatrixWidth * kMatrixHeight)
// Param for different pixel layouts
#define kMatrixSerpentineLayout true
// led array
CRGB leds[kMatrixWidth * kMatrixHeight];
// x,y, & time values
uint32_t x,y,v_time,hue_time,hxy;
// Play with the values of the variables below and see what kinds of effects they
// have! More octaves will make things slower.
// how many octaves to use for the brightness and hue functions
uint8_t octaves=1;
uint8_t hue_octaves=3;
// the 'distance' between points on the x and y axis
int xscale=57771;
int yscale=57771;
// the 'distance' between x/y points for the hue noise
int hue_scale=1;
// how fast we move through time & hue noise
int time_speed=1111;
int hue_speed=31;
// adjust these values to move along the x or y axis between frames
int x_speed=331;
int y_speed=1111;
void setup() {
// initialize the x/y and time values
random16_set_seed(8934);
random16_add_entropy(analogRead(3));
Serial.begin(57600);
Serial.println("resetting!");
delay(3000);
FastLED.addLeds<WS2811,2,GRB>(leds,NUM_LEDS);
FastLED.setBrightness(96);
hxy = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16();
x = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16();
y = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16();
v_time = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16();
hue_time = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16();
}
void loop() {
// fill the led array 2/16-bit noise values
fill_2dnoise16(leds, kMatrixWidth, kMatrixHeight, kMatrixSerpentineLayout,
octaves,x,xscale,y,yscale,v_time,
hue_octaves,hxy,hue_scale,hxy,hue_scale,hue_time, false);
FastLED.show();
// adjust the intra-frame time values
x += x_speed;
y += y_speed;
v_time += time_speed;
hue_time += hue_speed;
// delay(50);
}

View file

@ -1,90 +1,11 @@
#include <FastLED.h> // Main FastLED library for controlling LEDs
/// @file NoisePlayground.ino
/// @brief Demonstrates how to use noise generation on a 2D LED matrix
/// @example NoisePlayground.ino
#include <FastLED.h>
#if defined(__AVR__)
// too large for ATtiny85, attiny88, etc.. Just disable it for all avr boards.
void setup() {};
void loop() {};
#else
// Params for width and height
const uint8_t kMatrixWidth = 16;
const uint8_t kMatrixHeight = 16;
#define NUM_LEDS (kMatrixWidth * kMatrixHeight)
// Param for different pixel layouts
#define kMatrixSerpentineLayout true
// led array
CRGB leds[kMatrixWidth * kMatrixHeight];
// x,y, & time values
uint32_t x,y,v_time,hue_time,hxy;
// Play with the values of the variables below and see what kinds of effects they
// have! More octaves will make things slower.
// how many octaves to use for the brightness and hue functions
uint8_t octaves=1;
uint8_t hue_octaves=3;
// the 'distance' between points on the x and y axis
int xscale=57771;
int yscale=57771;
// the 'distance' between x/y points for the hue noise
int hue_scale=1;
// how fast we move through time & hue noise
int time_speed=1111;
int hue_speed=31;
// adjust these values to move along the x or y axis between frames
int x_speed=331;
int y_speed=1111;
void loop() {
// fill the led array 2/16-bit noise values
fill_2dnoise16(leds, kMatrixWidth, kMatrixHeight, kMatrixSerpentineLayout,
octaves,x,xscale,y,yscale,v_time,
hue_octaves,hxy,hue_scale,hxy,hue_scale,hue_time, false);
FastLED.show();
// adjust the intra-frame time values
x += x_speed;
y += y_speed;
v_time += time_speed;
hue_time += hue_speed;
// delay(50);
}
void setup() {
// initialize the x/y and time values
random16_set_seed(8934);
random16_add_entropy(analogRead(3));
Serial.begin(57600);
Serial.println("resetting!");
delay(3000);
FastLED.addLeds<WS2811,2,GRB>(leds,NUM_LEDS);
FastLED.setBrightness(96);
hxy = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16();
x = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16();
y = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16();
v_time = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16();
hue_time = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16();
}
#endif
#if !SKETCH_HAS_LOTS_OF_MEMORY
// Don't compile this for AVR microcontrollers (like Arduino Uno) because they typically
// don't have enough memory to handle this complex animation.
// Instead, we provide empty setup/loop functions so the sketch will compile but do nothing.
void setup() {}
void loop() {}
#else // For all other platforms with more memory (ESP32, Teensy, etc.)
#include "NoisePlayground.h"
#endif // End of the non-AVR code section

View file

@ -0,0 +1,407 @@
/// @file NoisePlusPalette.ino
/// @brief Demonstrates how to mix noise generation with color palettes on a 2D LED matrix
/// @example NoisePlusPalette.ino
///
/// OVERVIEW:
/// This sketch demonstrates combining Perlin noise with color palettes to create
/// dynamic, flowing color patterns on an LED matrix. The noise function creates
/// natural-looking patterns that change over time, while the color palettes
/// determine which colors are used to visualize the noise values.
#include <FastLED.h> // Main FastLED library for controlling LEDs
#if !SKETCH_HAS_LOTS_OF_MEMORY
// Don't compile this for AVR microcontrollers (like Arduino Uno) because they typically
// don't have enough memory to handle this complex animation.
// Instead, we provide empty setup/loop functions so the sketch will compile but do nothing.
void setup() {}
void loop() {}
#else // For all other platforms with more memory (ESP32, Teensy, etc.)
// LED hardware configuration
#define LED_PIN 3 // Data pin connected to the LED strip
#define BRIGHTNESS 96 // Default brightness level (0-255)
#define LED_TYPE WS2811 // Type of LED strip being used
#define COLOR_ORDER GRB // Color order of the LEDs (varies by strip type)
// Matrix dimensions - defines the size of our virtual LED grid
const uint8_t kMatrixWidth = 16; // Number of columns in the matrix
const uint8_t kMatrixHeight = 16; // Number of rows in the matrix
// LED strip layout configuration
const bool kMatrixSerpentineLayout = true; // If true, every other row runs backwards
// This is common in matrix setups to allow
// for easier wiring
// HOW THIS EXAMPLE WORKS:
//
// This example combines two features of FastLED to produce a remarkable range of
// effects from a relatively small amount of code. This example combines FastLED's
// color palette lookup functions with FastLED's Perlin noise generator, and
// the combination is extremely powerful.
//
// You might want to look at the "ColorPalette" and "Noise" examples separately
// if this example code seems daunting.
//
//
// The basic setup here is that for each frame, we generate a new array of
// 'noise' data, and then map it onto the LED matrix through a color palette.
//
// Periodically, the color palette is changed, and new noise-generation parameters
// are chosen at the same time. In this example, specific noise-generation
// values have been selected to match the given color palettes; some are faster,
// or slower, or larger, or smaller than others, but there's no reason these
// parameters can't be freely mixed-and-matched.
//
// In addition, this example includes some fast automatic 'data smoothing' at
// lower noise speeds to help produce smoother animations in those cases.
//
// The FastLED built-in color palettes (Forest, Clouds, Lava, Ocean, Party) are
// used, as well as some 'hand-defined' ones, and some procedurally generated
// palettes.
// Calculate the total number of LEDs in our matrix
#define NUM_LEDS (kMatrixWidth * kMatrixHeight)
// Find the larger dimension (width or height) for our noise array size
#define MAX_DIMENSION ((kMatrixWidth>kMatrixHeight) ? kMatrixWidth : kMatrixHeight)
// Array to hold all LED color values - one CRGB struct per LED
CRGB leds[kMatrixWidth * kMatrixHeight];
// The 16-bit version of our coordinates for the noise function
// Using 16 bits gives us more resolution and smoother animations
static uint16_t x; // x-coordinate in the noise space
static uint16_t y; // y-coordinate in the noise space
static uint16_t z; // z-coordinate (time dimension) in the noise space
// ANIMATION PARAMETERS:
// We're using the x/y dimensions to map to the x/y pixels on the matrix. We'll
// use the z-axis for "time". speed determines how fast time moves forward. Try
// 1 for a very slow moving effect, or 60 for something that ends up looking like
// water.
uint16_t speed = 20; // Speed is set dynamically once we've started up
// Higher values = faster animation
// Scale determines how far apart the pixels in our noise matrix are. Try
// changing these values around to see how it affects the motion of the display. The
// higher the value of scale, the more "zoomed out" the noise will be. A value
// of 1 will be so zoomed in, you'll mostly see solid colors.
uint16_t scale = 30; // Scale is set dynamically once we've started up
// Higher values = more "zoomed out" pattern
// This is the array that we keep our computed noise values in
// Each position stores an 8-bit (0-255) noise value
uint8_t noise[MAX_DIMENSION][MAX_DIMENSION];
// The current color palette we're using to map noise values to colors
CRGBPalette16 currentPalette( PartyColors_p ); // Start with party colors
// If colorLoop is set to 1, we'll cycle through the colors in the palette
// This creates an additional animation effect on top of the noise movement
uint8_t colorLoop = 1; // 0 = no color cycling, 1 = cycle colors
// Forward declare our functions so that we have maximum compatibility
// with other build tools outside of ArduinoIDE. The *.ino files are
// special in that Arduino will generate function prototypes for you.
// For non-Arduino environments, we need these declarations.
void SetupRandomPalette(); // Creates a random color palette
void SetupPurpleAndGreenPalette(); // Creates a purple and green striped palette
void SetupBlackAndWhiteStripedPalette(); // Creates a black and white striped palette
void ChangePaletteAndSettingsPeriodically(); // Changes palettes and settings over time
void mapNoiseToLEDsUsingPalette(); // Maps noise data to LED colors using the palette
uint16_t XY( uint8_t x, uint8_t y); // Converts x,y coordinates to LED array index
void setup() {
delay(3000); // 3 second delay for recovery and to give time for the serial monitor to open
// Initialize the LED strip:
// - LED_TYPE specifies the chipset (WS2811, WS2812B, etc.)
// - LED_PIN is the data pin number
// - COLOR_ORDER specifies the RGB color ordering for your strip
FastLED.addLeds<LED_TYPE,LED_PIN,COLOR_ORDER>(leds,NUM_LEDS);
// NOTE - This does NOT have a ScreenMap (because it's a legacy sketch)
// so it won't look that good on the web-compiler. But adding it is ONE LINE!
// Set the overall brightness level (0-255)
FastLED.setBrightness(BRIGHTNESS);
// Initialize our noise coordinates to random values
// This ensures the pattern starts from a different position each time
x = random16(); // Random x starting position
y = random16(); // Random y starting position
z = random16(); // Random time starting position
}
// Fill the x/y array of 8-bit noise values using the inoise8 function.
void fillnoise8() {
// If we're running at a low "speed", some 8-bit artifacts become visible
// from frame-to-frame. In order to reduce this, we can do some fast data-smoothing.
// The amount of data smoothing we're doing depends on "speed".
uint8_t dataSmoothing = 0;
if( speed < 50) {
// At lower speeds, apply more smoothing
// This formula creates more smoothing at lower speeds:
// speed=10 → smoothing=160, speed=30 → smoothing=80
dataSmoothing = 200 - (speed * 4);
}
// Loop through each pixel in our noise array
for(int i = 0; i < MAX_DIMENSION; i++) {
// Calculate the offset for this pixel in the x dimension
int ioffset = scale * i;
for(int j = 0; j < MAX_DIMENSION; j++) {
// Calculate the offset for this pixel in the y dimension
int joffset = scale * j;
// Generate the noise value for this pixel using 3D Perlin noise
// The noise function takes x, y, and z (time) coordinates
uint8_t data = inoise8(x + ioffset, y + joffset, z);
// The range of the inoise8 function is roughly 16-238.
// These two operations expand those values out to roughly 0..255
// You can comment them out if you want the raw noise data.
data = qsub8(data, 16); // Subtract 16 (with underflow protection)
data = qadd8(data, scale8(data, 39)); // Add a scaled version of the data to itself
// Apply data smoothing if enabled
if( dataSmoothing ) {
uint8_t olddata = noise[i][j]; // Get the previous frame's value
// Blend between old and new data based on smoothing amount
// Higher dataSmoothing = more of the old value is kept
uint8_t newdata = scale8(olddata, dataSmoothing) +
scale8(data, 256 - dataSmoothing);
data = newdata;
}
// Store the final noise value in our array
noise[i][j] = data;
}
}
// Increment z to move through the noise space over time
z += speed;
// Apply slow drift to X and Y, just for visual variation
// This creates a gentle shifting of the entire pattern
x += speed / 8; // X drifts at 1/8 the speed of z
y -= speed / 16; // Y drifts at 1/16 the speed of z in the opposite direction
}
// Map the noise data to LED colors using the current color palette
void mapNoiseToLEDsUsingPalette()
{
// Static variable that slowly increases to cycle through colors when colorLoop is enabled
static uint8_t ihue=0;
// Loop through each pixel in our LED matrix
for(int i = 0; i < kMatrixWidth; i++) {
for(int j = 0; j < kMatrixHeight; j++) {
// We use the value at the (i,j) coordinate in the noise
// array for our brightness, and the flipped value from (j,i)
// for our pixel's index into the color palette.
// This creates interesting patterns with two different noise mappings.
uint8_t index = noise[j][i]; // Color index from the flipped coordinate
uint8_t bri = noise[i][j]; // Brightness from the normal coordinate
// If color cycling is enabled, add a slowly-changing base value to the index
// This makes the colors shift/rotate through the palette over time
if( colorLoop) {
index += ihue; // Add the slowly increasing hue offset
}
// Brighten up the colors, as the color palette itself often contains the
// light/dark dynamic range desired
if( bri > 127 ) {
// If brightness is in the upper half, make it full brightness
bri = 255;
} else {
// Otherwise, scale it to the full range (0-127 becomes 0-254)
bri = dim8_raw( bri * 2);
}
// Get the final color by looking up the palette color at our index
// and applying the brightness value
CRGB color = ColorFromPalette( currentPalette, index, bri);
// Set the LED color in our array, using the XY mapping function
// to convert from x,y coordinates to the 1D array index
leds[XY(i,j)] = color;
}
}
// Increment the hue value for the next frame (for color cycling)
ihue+=1;
}
void loop() {
// The main program loop that runs continuously
// Periodically choose a new palette, speed, and scale
// This creates variety in the animation over time
ChangePaletteAndSettingsPeriodically();
// Generate new noise data for this frame
fillnoise8();
// Convert the noise data to colors in the LED array
// using the current palette
mapNoiseToLEDsUsingPalette();
// Send the color data to the actual LEDs
FastLED.show();
// No delay is needed here as the calculations already take some time
// Adding a delay would slow down the animation
// delay(10);
}
// PALETTE MANAGEMENT:
//
// There are several different palettes of colors demonstrated here.
//
// FastLED provides several 'preset' palettes: RainbowColors_p, RainbowStripeColors_p,
// OceanColors_p, CloudColors_p, LavaColors_p, ForestColors_p, and PartyColors_p.
//
// Additionally, you can manually define your own color palettes, or you can write
// code that creates color palettes on the fly.
// This controls how long each palette is displayed before changing
// 1 = 5 sec per palette
// 2 = 10 sec per palette
// etc.
#define HOLD_PALETTES_X_TIMES_AS_LONG 1 // Multiplier for palette duration
// Periodically change the palette, speed, and scale settings
void ChangePaletteAndSettingsPeriodically()
{
// Calculate which "second hand" we're on (0-59) based on elapsed time
// We divide by HOLD_PALETTES_X_TIMES_AS_LONG to slow down the changes
uint8_t secondHand = ((millis() / 1000) / HOLD_PALETTES_X_TIMES_AS_LONG) % 60;
static uint8_t lastSecond = 99; // Track the last second to detect changes
// Only update when the second hand changes
if( lastSecond != secondHand) {
lastSecond = secondHand;
// Every 5 seconds, change to a different palette and settings
// Each palette has specific speed and scale settings that work well with it
if( secondHand == 0) { currentPalette = RainbowColors_p; speed = 20; scale = 30; colorLoop = 1; }
if( secondHand == 5) { SetupPurpleAndGreenPalette(); speed = 10; scale = 50; colorLoop = 1; }
if( secondHand == 10) { SetupBlackAndWhiteStripedPalette(); speed = 20; scale = 30; colorLoop = 1; }
if( secondHand == 15) { currentPalette = ForestColors_p; speed = 8; scale =120; colorLoop = 0; }
if( secondHand == 20) { currentPalette = CloudColors_p; speed = 4; scale = 30; colorLoop = 0; }
if( secondHand == 25) { currentPalette = LavaColors_p; speed = 8; scale = 50; colorLoop = 0; }
if( secondHand == 30) { currentPalette = OceanColors_p; speed = 20; scale = 90; colorLoop = 0; }
if( secondHand == 35) { currentPalette = PartyColors_p; speed = 20; scale = 30; colorLoop = 1; }
if( secondHand == 40) { SetupRandomPalette(); speed = 20; scale = 20; colorLoop = 1; }
if( secondHand == 45) { SetupRandomPalette(); speed = 50; scale = 50; colorLoop = 1; }
if( secondHand == 50) { SetupRandomPalette(); speed = 90; scale = 90; colorLoop = 1; }
if( secondHand == 55) { currentPalette = RainbowStripeColors_p; speed = 30; scale = 20; colorLoop = 1; }
}
}
// This function generates a random palette that's a gradient
// between four different colors. The first is a dim hue, the second is
// a bright hue, the third is a bright pastel, and the last is
// another bright hue. This gives some visual bright/dark variation
// which is more interesting than just a gradient of different hues.
void SetupRandomPalette()
{
// Create a new palette with 4 random colors that blend together
currentPalette = CRGBPalette16(
CHSV( random8(), 255, 32), // Random dim hue (low value)
CHSV( random8(), 255, 255), // Random bright hue (full saturation & value)
CHSV( random8(), 128, 255), // Random pastel (medium saturation, full value)
CHSV( random8(), 255, 255)); // Another random bright hue
// The CRGBPalette16 constructor automatically creates a 16-color gradient
// between these four colors, evenly distributed
}
// This function sets up a palette of black and white stripes,
// using code. Since the palette is effectively an array of
// sixteen CRGB colors, the various fill_* functions can be used
// to set them up.
void SetupBlackAndWhiteStripedPalette()
{
// 'black out' all 16 palette entries...
fill_solid( currentPalette, 16, CRGB::Black);
// and set every fourth one to white to create stripes
// Positions 0, 4, 8, and 12 in the 16-color palette
currentPalette[0] = CRGB::White;
currentPalette[4] = CRGB::White;
currentPalette[8] = CRGB::White;
currentPalette[12] = CRGB::White;
// The palette interpolation will create smooth transitions between these colors
}
// This function sets up a palette of purple and green stripes.
void SetupPurpleAndGreenPalette()
{
// Define our colors using HSV color space for consistency
CRGB purple = CHSV( HUE_PURPLE, 255, 255); // Bright purple
CRGB green = CHSV( HUE_GREEN, 255, 255); // Bright green
CRGB black = CRGB::Black; // Black
// Create a 16-color palette with a specific pattern:
// green-green-black-black-purple-purple-black-black, repeated twice
// This creates alternating green and purple stripes with black in between
currentPalette = CRGBPalette16(
green, green, black, black, // First 4 colors
purple, purple, black, black, // Next 4 colors
green, green, black, black, // Repeat the pattern
purple, purple, black, black ); // Last 4 colors
}
//
// Mark's xy coordinate mapping code. See the XYMatrix for more information on it.
//
// This function converts x,y coordinates to a single array index
// It handles both regular and serpentine matrix layouts
uint16_t XY( uint8_t x, uint8_t y)
{
uint16_t i;
// For a regular/sequential layout, it's just y * width + x
if( kMatrixSerpentineLayout == false) {
i = (y * kMatrixWidth) + x;
}
// For a serpentine layout (zigzag), odd rows run backwards
if( kMatrixSerpentineLayout == true) {
if( y & 0x01) { // Check if y is odd (bitwise AND with 1)
// Odd rows run backwards
uint8_t reverseX = (kMatrixWidth - 1) - x;
i = (y * kMatrixWidth) + reverseX;
} else {
// Even rows run forwards
i = (y * kMatrixWidth) + x;
}
}
return i;
}
#endif // End of the non-AVR code section

View file

@ -1,407 +1,11 @@
/// @file NoisePlusPalette.ino
/// @brief Demonstrates how to mix noise generation with color palettes on a 2D LED matrix
/// @example NoisePlusPalette.ino
///
/// OVERVIEW:
/// This sketch demonstrates combining Perlin noise with color palettes to create
/// dynamic, flowing color patterns on an LED matrix. The noise function creates
/// natural-looking patterns that change over time, while the color palettes
/// determine which colors are used to visualize the noise values.
#include <FastLED.h> // Main FastLED library for controlling LEDs
#ifdef __AVR__
#if !SKETCH_HAS_LOTS_OF_MEMORY
// Don't compile this for AVR microcontrollers (like Arduino Uno) because they typically
// don't have enough memory to handle this complex animation.
// Instead, we provide empty setup/loop functions so the sketch will compile but do nothing.
void setup() {}
void loop() {}
#else // For all other platforms with more memory (ESP32, Teensy, etc.)
// LED hardware configuration
#define LED_PIN 3 // Data pin connected to the LED strip
#define BRIGHTNESS 96 // Default brightness level (0-255)
#define LED_TYPE WS2811 // Type of LED strip being used
#define COLOR_ORDER GRB // Color order of the LEDs (varies by strip type)
// Matrix dimensions - defines the size of our virtual LED grid
const uint8_t kMatrixWidth = 16; // Number of columns in the matrix
const uint8_t kMatrixHeight = 16; // Number of rows in the matrix
// LED strip layout configuration
const bool kMatrixSerpentineLayout = true; // If true, every other row runs backwards
// This is common in matrix setups to allow
// for easier wiring
// HOW THIS EXAMPLE WORKS:
//
// This example combines two features of FastLED to produce a remarkable range of
// effects from a relatively small amount of code. This example combines FastLED's
// color palette lookup functions with FastLED's Perlin noise generator, and
// the combination is extremely powerful.
//
// You might want to look at the "ColorPalette" and "Noise" examples separately
// if this example code seems daunting.
//
//
// The basic setup here is that for each frame, we generate a new array of
// 'noise' data, and then map it onto the LED matrix through a color palette.
//
// Periodically, the color palette is changed, and new noise-generation parameters
// are chosen at the same time. In this example, specific noise-generation
// values have been selected to match the given color palettes; some are faster,
// or slower, or larger, or smaller than others, but there's no reason these
// parameters can't be freely mixed-and-matched.
//
// In addition, this example includes some fast automatic 'data smoothing' at
// lower noise speeds to help produce smoother animations in those cases.
//
// The FastLED built-in color palettes (Forest, Clouds, Lava, Ocean, Party) are
// used, as well as some 'hand-defined' ones, and some procedurally generated
// palettes.
// Calculate the total number of LEDs in our matrix
#define NUM_LEDS (kMatrixWidth * kMatrixHeight)
// Find the larger dimension (width or height) for our noise array size
#define MAX_DIMENSION ((kMatrixWidth>kMatrixHeight) ? kMatrixWidth : kMatrixHeight)
// Array to hold all LED color values - one CRGB struct per LED
CRGB leds[kMatrixWidth * kMatrixHeight];
// The 16-bit version of our coordinates for the noise function
// Using 16 bits gives us more resolution and smoother animations
static uint16_t x; // x-coordinate in the noise space
static uint16_t y; // y-coordinate in the noise space
static uint16_t z; // z-coordinate (time dimension) in the noise space
// ANIMATION PARAMETERS:
// We're using the x/y dimensions to map to the x/y pixels on the matrix. We'll
// use the z-axis for "time". speed determines how fast time moves forward. Try
// 1 for a very slow moving effect, or 60 for something that ends up looking like
// water.
uint16_t speed = 20; // Speed is set dynamically once we've started up
// Higher values = faster animation
// Scale determines how far apart the pixels in our noise matrix are. Try
// changing these values around to see how it affects the motion of the display. The
// higher the value of scale, the more "zoomed out" the noise will be. A value
// of 1 will be so zoomed in, you'll mostly see solid colors.
uint16_t scale = 30; // Scale is set dynamically once we've started up
// Higher values = more "zoomed out" pattern
// This is the array that we keep our computed noise values in
// Each position stores an 8-bit (0-255) noise value
uint8_t noise[MAX_DIMENSION][MAX_DIMENSION];
// The current color palette we're using to map noise values to colors
CRGBPalette16 currentPalette( PartyColors_p ); // Start with party colors
// If colorLoop is set to 1, we'll cycle through the colors in the palette
// This creates an additional animation effect on top of the noise movement
uint8_t colorLoop = 1; // 0 = no color cycling, 1 = cycle colors
// Forward declare our functions so that we have maximum compatibility
// with other build tools outside of ArduinoIDE. The *.ino files are
// special in that Arduino will generate function prototypes for you.
// For non-Arduino environments, we need these declarations.
void SetupRandomPalette(); // Creates a random color palette
void SetupPurpleAndGreenPalette(); // Creates a purple and green striped palette
void SetupBlackAndWhiteStripedPalette(); // Creates a black and white striped palette
void ChangePaletteAndSettingsPeriodically(); // Changes palettes and settings over time
void mapNoiseToLEDsUsingPalette(); // Maps noise data to LED colors using the palette
uint16_t XY( uint8_t x, uint8_t y); // Converts x,y coordinates to LED array index
void setup() {
delay(3000); // 3 second delay for recovery and to give time for the serial monitor to open
// Initialize the LED strip:
// - LED_TYPE specifies the chipset (WS2811, WS2812B, etc.)
// - LED_PIN is the data pin number
// - COLOR_ORDER specifies the RGB color ordering for your strip
FastLED.addLeds<LED_TYPE,LED_PIN,COLOR_ORDER>(leds,NUM_LEDS);
// NOTE - This does NOT have a ScreenMap (because it's a legacy sketch)
// so it won't look that good on the web-compiler. But adding it is ONE LINE!
// Set the overall brightness level (0-255)
FastLED.setBrightness(BRIGHTNESS);
// Initialize our noise coordinates to random values
// This ensures the pattern starts from a different position each time
x = random16(); // Random x starting position
y = random16(); // Random y starting position
z = random16(); // Random time starting position
}
// Fill the x/y array of 8-bit noise values using the inoise8 function.
void fillnoise8() {
// If we're running at a low "speed", some 8-bit artifacts become visible
// from frame-to-frame. In order to reduce this, we can do some fast data-smoothing.
// The amount of data smoothing we're doing depends on "speed".
uint8_t dataSmoothing = 0;
if( speed < 50) {
// At lower speeds, apply more smoothing
// This formula creates more smoothing at lower speeds:
// speed=10 → smoothing=160, speed=30 → smoothing=80
dataSmoothing = 200 - (speed * 4);
}
// Loop through each pixel in our noise array
for(int i = 0; i < MAX_DIMENSION; i++) {
// Calculate the offset for this pixel in the x dimension
int ioffset = scale * i;
for(int j = 0; j < MAX_DIMENSION; j++) {
// Calculate the offset for this pixel in the y dimension
int joffset = scale * j;
// Generate the noise value for this pixel using 3D Perlin noise
// The noise function takes x, y, and z (time) coordinates
uint8_t data = inoise8(x + ioffset, y + joffset, z);
// The range of the inoise8 function is roughly 16-238.
// These two operations expand those values out to roughly 0..255
// You can comment them out if you want the raw noise data.
data = qsub8(data, 16); // Subtract 16 (with underflow protection)
data = qadd8(data, scale8(data, 39)); // Add a scaled version of the data to itself
// Apply data smoothing if enabled
if( dataSmoothing ) {
uint8_t olddata = noise[i][j]; // Get the previous frame's value
// Blend between old and new data based on smoothing amount
// Higher dataSmoothing = more of the old value is kept
uint8_t newdata = scale8(olddata, dataSmoothing) +
scale8(data, 256 - dataSmoothing);
data = newdata;
}
// Store the final noise value in our array
noise[i][j] = data;
}
}
// Increment z to move through the noise space over time
z += speed;
// Apply slow drift to X and Y, just for visual variation
// This creates a gentle shifting of the entire pattern
x += speed / 8; // X drifts at 1/8 the speed of z
y -= speed / 16; // Y drifts at 1/16 the speed of z in the opposite direction
}
// Map the noise data to LED colors using the current color palette
void mapNoiseToLEDsUsingPalette()
{
// Static variable that slowly increases to cycle through colors when colorLoop is enabled
static uint8_t ihue=0;
// Loop through each pixel in our LED matrix
for(int i = 0; i < kMatrixWidth; i++) {
for(int j = 0; j < kMatrixHeight; j++) {
// We use the value at the (i,j) coordinate in the noise
// array for our brightness, and the flipped value from (j,i)
// for our pixel's index into the color palette.
// This creates interesting patterns with two different noise mappings.
uint8_t index = noise[j][i]; // Color index from the flipped coordinate
uint8_t bri = noise[i][j]; // Brightness from the normal coordinate
// If color cycling is enabled, add a slowly-changing base value to the index
// This makes the colors shift/rotate through the palette over time
if( colorLoop) {
index += ihue; // Add the slowly increasing hue offset
}
// Brighten up the colors, as the color palette itself often contains the
// light/dark dynamic range desired
if( bri > 127 ) {
// If brightness is in the upper half, make it full brightness
bri = 255;
} else {
// Otherwise, scale it to the full range (0-127 becomes 0-254)
bri = dim8_raw( bri * 2);
}
// Get the final color by looking up the palette color at our index
// and applying the brightness value
CRGB color = ColorFromPalette( currentPalette, index, bri);
// Set the LED color in our array, using the XY mapping function
// to convert from x,y coordinates to the 1D array index
leds[XY(i,j)] = color;
}
}
// Increment the hue value for the next frame (for color cycling)
ihue+=1;
}
void loop() {
// The main program loop that runs continuously
// Periodically choose a new palette, speed, and scale
// This creates variety in the animation over time
ChangePaletteAndSettingsPeriodically();
// Generate new noise data for this frame
fillnoise8();
// Convert the noise data to colors in the LED array
// using the current palette
mapNoiseToLEDsUsingPalette();
// Send the color data to the actual LEDs
FastLED.show();
// No delay is needed here as the calculations already take some time
// Adding a delay would slow down the animation
// delay(10);
}
// PALETTE MANAGEMENT:
//
// There are several different palettes of colors demonstrated here.
//
// FastLED provides several 'preset' palettes: RainbowColors_p, RainbowStripeColors_p,
// OceanColors_p, CloudColors_p, LavaColors_p, ForestColors_p, and PartyColors_p.
//
// Additionally, you can manually define your own color palettes, or you can write
// code that creates color palettes on the fly.
// This controls how long each palette is displayed before changing
// 1 = 5 sec per palette
// 2 = 10 sec per palette
// etc.
#define HOLD_PALETTES_X_TIMES_AS_LONG 1 // Multiplier for palette duration
// Periodically change the palette, speed, and scale settings
void ChangePaletteAndSettingsPeriodically()
{
// Calculate which "second hand" we're on (0-59) based on elapsed time
// We divide by HOLD_PALETTES_X_TIMES_AS_LONG to slow down the changes
uint8_t secondHand = ((millis() / 1000) / HOLD_PALETTES_X_TIMES_AS_LONG) % 60;
static uint8_t lastSecond = 99; // Track the last second to detect changes
// Only update when the second hand changes
if( lastSecond != secondHand) {
lastSecond = secondHand;
// Every 5 seconds, change to a different palette and settings
// Each palette has specific speed and scale settings that work well with it
if( secondHand == 0) { currentPalette = RainbowColors_p; speed = 20; scale = 30; colorLoop = 1; }
if( secondHand == 5) { SetupPurpleAndGreenPalette(); speed = 10; scale = 50; colorLoop = 1; }
if( secondHand == 10) { SetupBlackAndWhiteStripedPalette(); speed = 20; scale = 30; colorLoop = 1; }
if( secondHand == 15) { currentPalette = ForestColors_p; speed = 8; scale =120; colorLoop = 0; }
if( secondHand == 20) { currentPalette = CloudColors_p; speed = 4; scale = 30; colorLoop = 0; }
if( secondHand == 25) { currentPalette = LavaColors_p; speed = 8; scale = 50; colorLoop = 0; }
if( secondHand == 30) { currentPalette = OceanColors_p; speed = 20; scale = 90; colorLoop = 0; }
if( secondHand == 35) { currentPalette = PartyColors_p; speed = 20; scale = 30; colorLoop = 1; }
if( secondHand == 40) { SetupRandomPalette(); speed = 20; scale = 20; colorLoop = 1; }
if( secondHand == 45) { SetupRandomPalette(); speed = 50; scale = 50; colorLoop = 1; }
if( secondHand == 50) { SetupRandomPalette(); speed = 90; scale = 90; colorLoop = 1; }
if( secondHand == 55) { currentPalette = RainbowStripeColors_p; speed = 30; scale = 20; colorLoop = 1; }
}
}
// This function generates a random palette that's a gradient
// between four different colors. The first is a dim hue, the second is
// a bright hue, the third is a bright pastel, and the last is
// another bright hue. This gives some visual bright/dark variation
// which is more interesting than just a gradient of different hues.
void SetupRandomPalette()
{
// Create a new palette with 4 random colors that blend together
currentPalette = CRGBPalette16(
CHSV( random8(), 255, 32), // Random dim hue (low value)
CHSV( random8(), 255, 255), // Random bright hue (full saturation & value)
CHSV( random8(), 128, 255), // Random pastel (medium saturation, full value)
CHSV( random8(), 255, 255)); // Another random bright hue
// The CRGBPalette16 constructor automatically creates a 16-color gradient
// between these four colors, evenly distributed
}
// This function sets up a palette of black and white stripes,
// using code. Since the palette is effectively an array of
// sixteen CRGB colors, the various fill_* functions can be used
// to set them up.
void SetupBlackAndWhiteStripedPalette()
{
// 'black out' all 16 palette entries...
fill_solid( currentPalette, 16, CRGB::Black);
// and set every fourth one to white to create stripes
// Positions 0, 4, 8, and 12 in the 16-color palette
currentPalette[0] = CRGB::White;
currentPalette[4] = CRGB::White;
currentPalette[8] = CRGB::White;
currentPalette[12] = CRGB::White;
// The palette interpolation will create smooth transitions between these colors
}
// This function sets up a palette of purple and green stripes.
void SetupPurpleAndGreenPalette()
{
// Define our colors using HSV color space for consistency
CRGB purple = CHSV( HUE_PURPLE, 255, 255); // Bright purple
CRGB green = CHSV( HUE_GREEN, 255, 255); // Bright green
CRGB black = CRGB::Black; // Black
// Create a 16-color palette with a specific pattern:
// green-green-black-black-purple-purple-black-black, repeated twice
// This creates alternating green and purple stripes with black in between
currentPalette = CRGBPalette16(
green, green, black, black, // First 4 colors
purple, purple, black, black, // Next 4 colors
green, green, black, black, // Repeat the pattern
purple, purple, black, black ); // Last 4 colors
}
//
// Mark's xy coordinate mapping code. See the XYMatrix for more information on it.
//
// This function converts x,y coordinates to a single array index
// It handles both regular and serpentine matrix layouts
uint16_t XY( uint8_t x, uint8_t y)
{
uint16_t i;
// For a regular/sequential layout, it's just y * width + x
if( kMatrixSerpentineLayout == false) {
i = (y * kMatrixWidth) + x;
}
// For a serpentine layout (zigzag), odd rows run backwards
if( kMatrixSerpentineLayout == true) {
if( y & 0x01) { // Check if y is odd (bitwise AND with 1)
// Odd rows run backwards
uint8_t reverseX = (kMatrixWidth - 1) - x;
i = (y * kMatrixWidth) + reverseX;
} else {
// Even rows run forwards
i = (y * kMatrixWidth) + x;
}
}
return i;
}
#include "NoisePlusPalette.h"
#endif // End of the non-AVR code section

View file

@ -1,8 +1,9 @@
/// @file Overclock.ino
/// @brief Demonstrates how to overclock a FastLED setup
#include "FastLED.h"
#ifdef __AVR__
#if !SKETCH_HAS_LOTS_OF_MEMORY
// To effectively test the overclock feature we need
// a large enough dataset to test against. Unfortunately
// the avr platforms don't have enough memory so this example

View file

@ -8,9 +8,6 @@
// Animated, ever-changing rainbows.
// by Mark Kriegsman
#if FASTLED_VERSION < 3001000
#error "Requires FastLED 3.1 or later; check github for latest code."
#endif
#define DATA_PIN 3
//#define CLK_PIN 4

View file

@ -4,6 +4,11 @@
This is a 1D wave simluation!
This demo is best viewed using the FastLED compiler.
Windows/MacOS binaries: https://github.com/FastLED/FastLED/releases
Python
Install: pip install fastled
Run: fastled <this sketch directory>
This will compile and preview the sketch in the browser, and enable

View file

@ -2,6 +2,11 @@
/*
This demo is best viewed using the FastLED compiler.
Windows/MacOS binaries: https://github.com/FastLED/FastLED/releases
Python
Install: pip install fastled
Run: fastled <this sketch directory>
This will compile and preview the sketch in the browser, and enable

View file

@ -2,6 +2,11 @@
/*
This demo is best viewed using the FastLED compiler.
Windows/MacOS binaries: https://github.com/FastLED/FastLED/releases
Python
Install: pip install fastled
Run: fastled <this sketch directory>
This will compile and preview the sketch in the browser, and enable

View file

@ -2,6 +2,11 @@
/*
This demo is best viewed using the FastLED compiler.
Windows/MacOS binaries: https://github.com/FastLED/FastLED/releases
Python
Install: pip install fastled
Run: fastled <this sketch directory>
This will compile and preview the sketch in the browser, and enable
@ -27,7 +32,6 @@ all the UI elements you see below.
#include "src/xypaths.h"
#include "fl/function.h"
#include <assert.h>
using namespace fl;
@ -37,8 +41,6 @@ using namespace fl;
#define IS_SERPINTINE true
#define TIME_ANIMATION 1000 // ms
// CRGB leds[NUM_LEDS];
LedsXY<WIDTH, HEIGHT> leds;
XYMap xyMap(WIDTH, HEIGHT, IS_SERPINTINE);
UITitle title("Simple control of an xy path");
@ -49,31 +51,21 @@ UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f);
UISlider steps("Steps", 100.0f, 1.0f, 200.0f, 1.0f);
UISlider length("Length", 1.0f, 0.0f, 1.0f, 0.01f);
XYPathPtr heartPath = XYPath::NewHeartPath(WIDTH, HEIGHT);
void setup() {
Serial.begin(115200);
auto screenmap = xyMap.toScreenMap();
screenmap.setDiameter(.2);
FastLED.addLeds<NEOPIXEL, 2>(leds, NUM_LEDS).setScreenMap(screenmap);
}
void loop() {
fl::clear(leds);
// FASTLED_ASSERT(false, "This is a test");
// leds(x,y) = CRGB(255, 0, 0);
float from = offset;
float to = length.value() + offset.value();
heartPath->drawColor(CRGB(255, 0, 0), from, to, &leds, steps.as_int());
FastLED.show();
}

View file

@ -2,6 +2,11 @@
/*
This demo is best viewed using the FastLED compiler.
Windows/MacOS binaries: https://github.com/FastLED/FastLED/releases
Python
Install: pip install fastled
Run: fastled <this sketch directory>
This will compile and preview the sketch in the browser, and enable